Citing source

cite shows a definition that already exists in this build. The macro records the symbol. The site reads that symbol's current source when it renders, so a body edit shows up without the page being recompiled. The permalink is the symbol anchor. A footer link, when the build knows a GitHub revision, points at the whole definition in that revision.

A member

Pin the type, then the member. The lambda is checked against that type. _.m names a field or a method. When the method is overloaded, name the overload with its argument type: _.m(_: Arg).

cite[MountKey.type](_.from)
cite[ExampleRunner](_.run(_: Example))
specular.MountKey$package.MountKey.from
def from(raw: String): Either[MountKeyError, MountKey] =
    if raw.isEmpty then Left(MountKeyError.Empty)
    else if Allowed.matches(raw) then Right(raw)
    else Left(MountKeyError.IllegalCharacters(raw))

View source

specular.ExampleRunner.run
def run(example: Example): UIO[ascent.ast.UI[Any]]

View source

A stable term

When you have a stable path, cite it directly. An eta-expanded method (obj.m(_)) names that overload. A call (obj.m("x")) does not: cite names a definition.

cite(MountPoint.Attr)
cite(MountKey.from(_))
specular.MountPoint.Attr
  /** Marks an element as an example mount point; its value is the mount key. */
  val Attr: String = "data-specular-mount"

View source

The whole definition

cite[A].definition is the whole definition, including its scaladoc, up to the size cap. An opaque type and a type alias cite themselves, not the expanded right-hand side.

cite[MountKey].definition
specular.MountKey$package.MountKey
/** The browser-side handle of a mounted example: an HTML attribute value and a client-side map key.
  *
  * Restricted to `[A-Za-z0-9._-]+`, an unambiguous, injection-proof alphabet. A literal is checked at compile time
  * (`MountKey("counter")`, or a string literal passed to `exampleDom` / `illustrationDom` / `withMountKey`). Runtime
  * text goes through [[MountKey.from]].
  */
opaque type MountKey = String

View source

The header

.signature keeps the scaladoc, the annotations, and the header, and drops the body. A val, a type alias, and an opaque type are already a header, so .signature is the whole definition there. The anchor gains -signature, so the header and the body can share a page.

cite(MountKey.from(_)).signature
cite[MountKeyError].signature
cite(MountPoint.Attr).signature
specular.MountKey$package.MountKey.from
def from(raw: String): Either[MountKeyError, MountKey]

View source

specular.MountKeyError
enum MountKeyError

View source

specular.MountPoint.Attr
  /** Marks an element as an example mount point; its value is the mount key. */
  val Attr: String = "data-specular-mount"

View source

A window

.elided(n) keeps the first n lines of the text signature and formatting already produced. n below 1 fails the build. When the slice is shorter than n, the panel shows the whole slice and no marker. When lines were dropped, the marker sits under the code. It is not part of the copy. The anchor and the footer still name the whole definition: the panel is a window onto it.

cite[MountPoint.type].elided(6)
cite(MountPoint.Selector).elided(20)
specular.MountPoint
/** The SSR-to-browser mount contract, shared by both platforms so the two halves cannot drift.
  *
  * `SiteBuilder` (JVM) stamps [[Attr]] onto the wrapper of every interactive example; `SpecularClient` (Scala.js) scans
  * for exactly that attribute and mounts whatever is registered under the key it carries. Both sides read these names
  * from here, because a rename in one place would otherwise silently stop every example mounting, with green tests
  * either side.

…

View source

specular.MountPoint.Selector
  /** CSS selector for the scan. */
  val Selector: String = s"[$Attr]"

View source

Formatting

The default is the file text. .formatted runs the definition through scalafmt, using the repo's .scalafmt.conf. .asWritten forces the file text. specularCiteFormat (as-written or formatted) is the default for a cite that calls neither method, passed as -Dspecular.cite.format. The method wins over the setting. A scalafmt diagnostic fails that cite. It does not fall back to the raw slice.

cite(MountKey.from(_)).formatted
cite[MountKey.type](_.from).asWritten
specular.MountKey$package.MountKey.from
def from(raw: String): Either[MountKeyError, MountKey] =
  if raw.isEmpty then Left(MountKeyError.Empty)
  else if Allowed.matches(raw) then Right(raw)
  else Left(MountKeyError.IllegalCharacters(raw))

View source

specular.MountKey$package.MountKey.from
def from(raw: String): Either[MountKeyError, MountKey] =
    if raw.isEmpty then Left(MountKeyError.Empty)
    else if Allowed.matches(raw) then Right(raw)
    else Left(MountKeyError.IllegalCharacters(raw))

View source

The heading is an in-page link: #cite- plus the symbol name. A module-class $ (MountPoint$.Attr) is dropped, so the link reads like the source (#cite-specular.MountPoint.Attr). A file with several top-level definitions is wrapped as File$package. That segment stays, so two files in one package do not share a permalink (#cite-specular.MountKey$package.MountKey.from). Format and elision are part of the anchor (-signature, -elided-6, -formatted, -as-written), so two views of one symbol can share a page. The same symbol on two pages is fine. The same anchor twice on one page fails the build.

The footer is not the permalink. Line numbers move. It is blob/<revision>/<path>#Lstart-Lend for the revision this site was built from, and only when that URL is an http(s) link the site will accept. No repository metadata means no footer. The anchor still works. The line range is the whole definition, including when the panel is a signature or a window.

What a cite will not do

A call is not a definition. cite[Foo[Int]] is rejected: cite the type constructor. A synthetic (copy, a generated apply, an anonymous given) has no source of its own. A symbol with no source file, or one whose source is inside a jar, does not compile. The compiler can see those.

A symbol that compiled from source can still be unreadable when the site is built: the file moved, the span is gone, or the slice is empty. That cite does not become a blank panel. The build fails, and the page shows the error where the source would have been. Two members are two cites. Elision is a window on one definition, not a splice of two of them.

exampleDom stays for a Scala.js sample the JVM page must not typecheck. A string path is the wrong tool once the compiler can see the symbol.