Skip to content

Commit c476d64

Browse files
authored
Refactor more documentation comment generation into DocumentationComment (#2817)
* Straight move * Partial * delete experiment * rebuild * remove ??= from a cut and paste
1 parent a4142c4 commit c476d64

File tree

4 files changed

+195
-190
lines changed

4 files changed

+195
-190
lines changed

lib/src/generator/templates.runtime_renderers.dart

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3928,6 +3928,26 @@ class _Renderer_DocumentationComment
39283928
parent: r);
39293929
},
39303930
),
3931+
'documentationLocal': Property(
3932+
getValue: (CT_ c) => c.documentationLocal,
3933+
renderVariable:
3934+
(CT_ c, Property<CT_> self, List<String> remainingNames) {
3935+
if (remainingNames.isEmpty) {
3936+
return self.getValue(c).toString();
3937+
}
3938+
var name = remainingNames.first;
3939+
var nextProperty =
3940+
_Renderer_String.propertyMap().getValue(name);
3941+
return nextProperty.renderVariable(self.getValue(c),
3942+
nextProperty, [...remainingNames.skip(1)]);
3943+
},
3944+
isNullValue: (CT_ c) => c.documentationLocal == null,
3945+
renderValue: (CT_ c, RendererBase<CT_> r,
3946+
List<MustachioNode> ast, StringSink sink) {
3947+
_render_String(c.documentationLocal, ast, r.template, sink,
3948+
parent: r);
3949+
},
3950+
),
39313951
'fullyQualifiedNameWithoutLibrary': Property(
39323952
getValue: (CT_ c) => c.fullyQualifiedNameWithoutLibrary,
39333953
renderVariable:
@@ -3971,6 +3991,13 @@ class _Renderer_DocumentationComment
39713991
getters: _invisibleGetters['ModelElementRenderer']);
39723992
},
39733993
),
3994+
'needsPrecache': Property(
3995+
getValue: (CT_ c) => c.needsPrecache,
3996+
renderVariable: (CT_ c, Property<CT_> self,
3997+
List<String> remainingNames) =>
3998+
self.renderSimpleVariable(c, remainingNames, 'bool'),
3999+
getBool: (CT_ c) => c.needsPrecache == true,
4000+
),
39744001
'pathContext': Property(
39754002
getValue: (CT_ c) => c.pathContext,
39764003
renderVariable: (CT_ c, Property<CT_> self,
@@ -4229,6 +4256,7 @@ class _Renderer_Enum extends RendererBase<Enum> {
42294256
CT_,
42304257
() => {
42314258
..._Renderer_InheritingContainer.propertyMap<CT_>(),
4259+
..._Renderer_TypeImplementing.propertyMap<CT_>(),
42324260
'inheritanceChain': Property(
42334261
getValue: (CT_ c) => c.inheritanceChain,
42344262
renderVariable: (CT_ c, Property<CT_> self,
@@ -9481,26 +9509,6 @@ class _Renderer_ModelElement extends RendererBase<ModelElement> {
94819509
parent: r));
94829510
},
94839511
),
9484-
'documentationLocal': Property(
9485-
getValue: (CT_ c) => c.documentationLocal,
9486-
renderVariable:
9487-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
9488-
if (remainingNames.isEmpty) {
9489-
return self.getValue(c).toString();
9490-
}
9491-
var name = remainingNames.first;
9492-
var nextProperty =
9493-
_Renderer_String.propertyMap().getValue(name);
9494-
return nextProperty.renderVariable(self.getValue(c),
9495-
nextProperty, [...remainingNames.skip(1)]);
9496-
},
9497-
isNullValue: (CT_ c) => c.documentationLocal == null,
9498-
renderValue: (CT_ c, RendererBase<CT_> r,
9499-
List<MustachioNode> ast, StringSink sink) {
9500-
_render_String(c.documentationLocal, ast, r.template, sink,
9501-
parent: r);
9502-
},
9503-
),
95049512
'element': Property(
95059513
getValue: (CT_ c) => c.element,
95069514
renderVariable: (CT_ c, Property<CT_> self,

lib/src/model/documentation_comment.dart

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'package:args/args.dart';
22
import 'package:crypto/crypto.dart' as crypto;
3-
import 'package:dartdoc/src/model/model.dart';
3+
import 'package:dartdoc/src/model/documentable.dart';
4+
import 'package:dartdoc/src/model/locatable.dart';
5+
import 'package:dartdoc/src/model/source_code_mixin.dart';
46
import 'package:dartdoc/src/render/model_element_renderer.dart';
57
import 'package:dartdoc/src/utils.dart';
68
import 'package:dartdoc/src/warnings.dart';
@@ -23,9 +25,15 @@ final _basicToolPattern = RegExp(
2325

2426
final _examplePattern = RegExp(r'{@example\s+([^}]+)}');
2527

28+
final _macroRegExp = RegExp(r'{@macro\s+([^}]+)}');
29+
30+
final _htmlInjectRegExp = RegExp(r'<dartdoc-html>([a-f0-9]+)</dartdoc-html>');
31+
32+
final RegExp _needsPrecacheRegExp = RegExp(r'{@(template|tool|inject-html)');
33+
2634
/// Features for processing directives in a documentation comment.
2735
///
28-
/// [processCommentWithoutTools] and [processComment] are the primary
36+
/// [_processCommentWithoutTools] and [processComment] are the primary
2937
/// entrypoints.
3038
mixin DocumentationComment
3139
on Documentable, Warnable, Locatable, SourceCodeMixin {
@@ -59,7 +67,7 @@ mixin DocumentationComment
5967

6068
/// Process a [documentationComment], performing various actions based on
6169
/// `{@}`-style directives, except `{@tool}`, returning the processed result.
62-
String processCommentWithoutTools(String documentationComment) {
70+
String _processCommentWithoutTools(String documentationComment) {
6371
var docs = stripComments(documentationComment);
6472
if (!docs.contains('{@')) {
6573
_analyzeCodeBlocks(docs);
@@ -79,6 +87,7 @@ mixin DocumentationComment
7987

8088
/// Process [documentationComment], performing various actions based on
8189
/// `{@}`-style directives, returning the processed result.
90+
@visibleForTesting
8291
Future<String> processComment(String documentationComment) async {
8392
var docs = stripComments(documentationComment);
8493
// Must evaluate tools first, in case they insert any other directives.
@@ -707,4 +716,145 @@ mixin DocumentationComment
707716
}
708717
});
709718
}
719+
720+
/// Returns the documentation for this literal element unless
721+
/// [config.dropTextFrom] indicates it should not be returned. Macro
722+
/// definitions are stripped, but macros themselves are not injected. This
723+
/// is a two stage process to avoid ordering problems.
724+
String _documentationLocal;
725+
726+
String get documentationLocal =>
727+
_documentationLocal ??= _buildDocumentationLocal();
728+
729+
/// Unconditionally precache local documentation.
730+
///
731+
/// Use only in factory for [PackageGraph].
732+
Future<void> precacheLocalDocs() async {
733+
_documentationLocal = await _buildDocumentationBase();
734+
}
735+
736+
bool _needsPrecache;
737+
bool get needsPrecache => _needsPrecache ??=
738+
_needsPrecacheRegExp.hasMatch(documentationComment ?? '');
739+
740+
String _rawDocs;
741+
742+
String _buildDocumentationLocal() => _buildDocumentationBaseSync();
743+
744+
/// Override this to add more features to the documentation builder in a
745+
/// subclass.
746+
String buildDocumentationAddition(String docs) => docs ?? '';
747+
748+
/// Separate from _buildDocumentationLocal for overriding.
749+
String _buildDocumentationBaseSync() {
750+
assert(_rawDocs == null,
751+
'reentrant calls to _buildDocumentation* not allowed');
752+
// Do not use the sync method if we need to evaluate tools or templates.
753+
assert(!isCanonical || !needsPrecache);
754+
if (config.dropTextFrom.contains(element.library.name)) {
755+
_rawDocs = '';
756+
} else {
757+
_rawDocs = _processCommentWithoutTools(documentationComment ?? '');
758+
}
759+
_rawDocs = buildDocumentationAddition(_rawDocs);
760+
return _rawDocs;
761+
}
762+
763+
/// Separate from _buildDocumentationLocal for overriding. Can only be
764+
/// used as part of [PackageGraph.setUpPackageGraph].
765+
Future<String> _buildDocumentationBase() async {
766+
assert(_rawDocs == null,
767+
'reentrant calls to _buildDocumentation* not allowed');
768+
// Do not use the sync method if we need to evaluate tools or templates.
769+
if (config.dropTextFrom.contains(element.library.name)) {
770+
_rawDocs = '';
771+
} else {
772+
_rawDocs = await processComment(documentationComment ?? '');
773+
}
774+
_rawDocs = buildDocumentationAddition(_rawDocs);
775+
return _rawDocs;
776+
}
777+
778+
/// Replace &lt;<dartdoc-html>[digest]</dartdoc-html>&gt; in API comments with
779+
/// the contents of the HTML fragment earlier defined by the
780+
/// &#123;@inject-html&#125; directive. The [digest] is a SHA1 of the contents
781+
/// of the HTML fragment, automatically generated upon parsing the
782+
/// &#123;@inject-html&#125; directive.
783+
///
784+
/// This markup is generated and inserted by [_stripHtmlAndAddToIndex] when it
785+
/// removes the HTML fragment in preparation for markdown processing. It isn't
786+
/// meant to be used at a user level.
787+
///
788+
/// Example:
789+
///
790+
/// You place the fragment in a dartdoc comment:
791+
///
792+
/// Some comments
793+
/// &#123;@inject-html&#125;
794+
/// &lt;p&gt;[HTML contents!]&lt;/p&gt;
795+
/// &#123;@endtemplate&#125;
796+
/// More comments
797+
///
798+
/// and [_stripHtmlAndAddToIndex] will replace your HTML fragment with this:
799+
///
800+
/// Some comments
801+
/// &lt;dartdoc-html&gt;4cc02f877240bf69855b4c7291aba8a16e5acce0&lt;/dartdoc-html&gt;
802+
/// More comments
803+
///
804+
/// Which will render in the final HTML output as:
805+
///
806+
/// Some comments
807+
/// &lt;p&gt;[HTML contents!]&lt;/p&gt;
808+
/// More comments
809+
///
810+
/// And the HTML fragment will not have been processed or changed by Markdown,
811+
/// but just injected verbatim.
812+
String injectHtmlFragments(String rawDocs) {
813+
if (!config.injectHtml) return rawDocs;
814+
815+
return rawDocs.replaceAllMapped(_htmlInjectRegExp, (match) {
816+
var fragment = packageGraph.getHtmlFragment(match[1]);
817+
if (fragment == null) {
818+
warn(PackageWarning.unknownHtmlFragment, message: match[1]);
819+
}
820+
return fragment;
821+
});
822+
}
823+
824+
/// Replace &#123;@macro ...&#125; in API comments with the contents of the macro
825+
///
826+
/// Syntax:
827+
///
828+
/// &#123;@macro NAME&#125;
829+
///
830+
/// Example:
831+
///
832+
/// You define the template in any comment for a documentable entity like:
833+
///
834+
/// &#123;@template foo&#125;
835+
/// Foo contents!
836+
/// &#123;@endtemplate&#125;
837+
///
838+
/// and them somewhere use it like this:
839+
///
840+
/// Some comments
841+
/// &#123;@macro foo&#125;
842+
/// More comments
843+
///
844+
/// Which will render
845+
///
846+
/// Some comments
847+
/// Foo contents!
848+
/// More comments
849+
///
850+
String injectMacros(String rawDocs) {
851+
return rawDocs.replaceAllMapped(_macroRegExp, (match) {
852+
var macro = packageGraph.getMacro(match[1]);
853+
if (macro == null) {
854+
warn(PackageWarning.unknownMacro, message: match[1]);
855+
}
856+
macro = processCommentDirectives(macro ?? '');
857+
return macro;
858+
});
859+
}
710860
}

0 commit comments

Comments
 (0)