dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.25k stars 1.58k forks source link

Doc comment syntax for copying a passage of documentation from elsewhere #56793

Open srawlins opened 1 month ago

srawlins commented 1 month ago

Dartdoc currently has a system that uses two doc comment directives, @template and @macro. A block of doc comment text can be wrapped in {@template a.b.c}...{@endtemplate} in order to store it away as an insertable block of text keyed with "a.b.c". The text can be inserted elsewhere then via {@macro a.b.c}. This system has a major drawback: in order to perform a single insertion, e.g. inserting the text for "a.b.c", dartdoc must parse the doc comments of every member in the packages that may be documented. This is hundreds of thousands of elements in the case of Flutter. This prevents any kind of incremental doc generation, hurts discoverability, makes typos easy. Additionally, there is no substitution performed, so the text is not really a "template", and it's insertion is not really a "macro" application. It's just a block of static text that can be inserted here and there.

OK, so I'd like to design a new system from the ground up, supported in analyzer, and in dartdoc via analyzer API.

Requirements

(I'm not married to "passage," but I do like it. I'm really not a fan of "template". Other choices include snippet, chunk, excerpt, ...)

API mockup

With these requirements, we have some options of how to refer to a passage via a fully qualified name. But I imagine it contains the library's URI (package:flutter/widgets.dart) (note to take the unquoted forms into account), the passage's immediate "name" (like "foo"), and possibly the name(s) of the library elements, like class name / method name. Does a passage's name have to be unique across a library, or only within the doc comment in which it is declared? What delimiters to use between the library URI and other names?

To go lightweight, we can say that a passage's name has to be unique within it's library, and we can use a hashmark as a delimiter, so that a passage named "foo" in the Flutter widgets library is referable via "package:flutter/widgets.dart#foo". I like the hashmark as it is reminiscent of an HTML fragment delimiter. (With the new unquoted syntax, it would look something like flutter/widgets#foo.

As one possible option for how to define a passage, and how to insert a passage:

Features

These requirements can enable the following features:

srawlins commented 1 month ago

CC @gspencergoog @goderbauer

gspencergoog commented 1 month ago

The name cannot contain whitespace (to be consistent with other doc directive restrictions), although maybe within quotes??? Maybe not.

I'd say not. Programmers are pretty familiar with snake case or camel case names, and allowing spaces adds quoting, which adds escaping and unescaping, which makes it all a lot more complex.

gspencergoog commented 1 month ago

(I'm not married to "passage," but I do like it. I'm really not a fan of "template". Other choices include snippet, chunk, excerpt, ...)

It's a reasonable name. I think I'd prefer excerpt, but passage works. I'd say no to snippet, because of confusion with code snippets.

lrhn commented 1 month ago

Would it be enough to have a qualified template reference? So instead of {@macro foo.bar} you can write {@macro ../file.dart#foo.bar}, treating everything up to the next } as a relative URI if it has a fragment, and as a normal name of not (no whitespace in either case). If you use the qualified reference, then the lookup only has to check one file, and won't find templates anywhere else. It still has to check the entire file, but I think it would have to fit snippets too.

Maybe allow templates to be named {@template #foo.bar} in which case it only works with qualified references. Inside the same file, it would be just {@macro #foo.bar}

srawlins commented 1 month ago

I like those ideas! I'd like to look up some stats on Flutter's use of {@macro}, like

bwilkerson commented 1 week ago

I have a slightly different suggestion for the reference. Given a passage/excerpt named 'name', write the reference as {@insert name}, but require that there is a docImport for the library that defines the passage to be inserted.

On a different note we have just removed most of DeclarationTracker (the class that was co-opted to support 'template's in the analyzer). This arc of work would allow us to remove the rest of that class, which would simplify the code and might improve performance.