Skip to content

dartdoc comment references

Janice Collins edited this page Jun 30, 2021 · 10 revisions

Dartdoc comment references

A "comment reference" in dartdoc is an expression wrapped by brackets within a documentation comment. They have a syntax similar to Dart expressions and are interpreted by dartdoc to create links to different parts of the documentation. The behavior is largely unspecified in the Dart language and so, this document describes Dartdoc's interpretation.

Syntax

The syntax of a documentation comment often looks like a simplified version of a Dart expression. Dartdoc's grammar for them is as follows:

<rawCommentReference> ::= <prefix>?<commentReference><suffix>?

<prefix> ::= <constructorPrefixHint>
  | <leadingJunk>

<suffix> ::= <callablePostfixHint>
  | <trailingJunk>

<leadingJunk> ::= ('const' | 'final' | 'var')(<whitespace>+)

<trailingJunk> ::= '('<notClosedParenthesis>*')'
  | '<'<notGreaterThan>*'>'
  | '?'
  | '!'
  | <whitespace>*

<constructorPrefixHint> ::= 'new '

<callablePostfixHint> ::= '()'

<commentReference> ::= (<globalScopeReference> '.')? <dartdocIdentifier> ('.' <dartdocIdentifier>)*

<globalScopeReference> ::= <packageName> '.' <libraryName>
  | <libraryName>

<dartdocIdentifier> ::= <dartIdentifier>
  | ('operator' <whitespace>*)?<operator>

A rawCommentReference includes all text between the two brackets, passed into dartdoc via the Markdown parser which interprets documentation comments generally.

The prefix and suffix include extra, optional characters that are allowed to either make the documentation comment more readable in prose, or to pass information to Dartdoc on what kind of identifier should be resolved.

The constructorPrefix, if present, signals to Dartdoc that we should resolve the comment reference exclusively to a constructor, to disambiguate a reference from a type of the same name.

The callableSuffix, if present, signals to Dartdoc to resolve the comment reference to callable items, including constructors. This can be used to disambiguate constructors, or simply to restrict resolution to a function or method.

leadingJunk and trailingJunk are accepted by the parser, but ignored, if present.

The commentReference isolated from the prefix/suffix, consists of a period-separated list of dartdoc identifiers. It can optionally include globalScopeReference, with a package name and library name, or just a library name, to further specify what the reference refers to. Some examples: foo, ClassName.foo, libName.ClassName.foo, and even packageName.libName.ClassName.foo.

libraryName is the name of the library a symbol is defined in or exposed in via reexporting. Because library names can contain periods, using them can create ambiguity in parsing, with multiple possible parses for a single commentReference. See below for how this is handled.

packageName is the name of a Dart "package", which can be the Dart SDK, a pub or pub-like package defined by a pubspec.yaml file, or another package type if dartdoc is extended to support it. Like libraryName, this can create ambiguities in parsing. See below for how this is handled.

A dartdocIdentifier is different from a Dart identifier in that it can include the word operator, some intervening whitespace, and a Dart operator, itself.

Basic Syntax

Here are some rawCommentReference examples (with the enclosing brackets for clarity) along with descriptions of how Dartdoc will parse them.

  • [anIdentifier] : This is just a single dartdocIdentifier with nothing special about it.
  • [AThing.AnotherThing] : This is parsed as two dartdocIdentifiers chained together.
  • [Foo.operator +] : Two dartdocIdentifiers chained together again.
  • [var libName.topLevelName.memberName] : The var is treated as leadingJunk, and this is three identifiers together.
  • [packageName.topLevelName.functionName()] : Three identifiers chained together, but the parser notes that we have a callablePostfixHint.
  • [new ClassName.ConstructorName()] : Two identifiers chained together, the parser noting the presence of callablePostFixHint and constructorPrefixHint.

Ambiguous Syntax Handling

Some aspects that really should be handled in the parser are instead handled at evaluation time due to the ambiguity introduced by library and package names allowing periods. There is no way of knowing up front whether a reference might eventually be evaluated as a library name containing multiple names.

A library can therefore be ambiguous with its own members in terms of naming. Does foo.Thing.AnotherThing refer to a library literally named foo.Thing.AnotherThing, a top level variable named AnotherThing in the library named foo.Thing, or a member of class Thing named AnotherThing in library Foo, and so on. Dartdoc answers this question by not answering it, and searching every possibility in sequence until it finds a match. While convenient for users who therefore don't have to worry about these ambiguities unless there is a problem, this is likely to change in the future as ambiguous parsing is confusing when there is a problem.

Dartdoc always tries interpreting as though package names and library names do not contain periods, first. So, it is recommended to avoid creating package names and libraries with periods in it until this ambiguity issue can be addressed with a syntax change in a future version of dartdoc.

Evaluation

Dartdoc evaluates comment references after parsing to attempt to match the comment reference to part of the public or private API surface -- or a library or package. If found and public, that will result in a hyperlink being generated to the documentation page for the requested symbol, library, or package.

Simple evaluation

Member variables

Parameters and Type Parameters

Callables

Constructors

Borrowing documentation via superchains

Exports, reexports, and scope

Ambiguous Evaluation

Disambiguation

Resolving errors

Unresolved doc references

Examples

  • Default constructor: [MyClass.MyClass]
  • Named constructors: [MyClass.namedConstructor]
library myLib;
final String myString = "hi";

String param;

class A {
  final String myString = "there";

  /// The lovely [A] class's [foo] method.
  /// [myString] - refers to A.myString
  /// [myLib.myString] - refers to the top level myString
  /// [param] - refers to the parameter of foo.
  /// [myLib.param] - refers to the top level param.
  void foo(int param) {}
}
Clone this wiki locally