11import 'package:args/args.dart' ;
22import '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' ;
46import 'package:dartdoc/src/render/model_element_renderer.dart' ;
57import 'package:dartdoc/src/utils.dart' ;
68import 'package:dartdoc/src/warnings.dart' ;
@@ -23,9 +25,15 @@ final _basicToolPattern = RegExp(
2325
2426final _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.
3038mixin 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 <<dartdoc-html>[digest] </dartdoc-html>> in API comments with
779+ /// the contents of the HTML fragment earlier defined by the
780+ /// {@inject-html} directive. The [digest] is a SHA1 of the contents
781+ /// of the HTML fragment, automatically generated upon parsing the
782+ /// {@inject-html} 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+ /// {@inject-html}
794+ /// <p>[HTML contents!]</p>
795+ /// {@endtemplate}
796+ /// More comments
797+ ///
798+ /// and [_stripHtmlAndAddToIndex] will replace your HTML fragment with this:
799+ ///
800+ /// Some comments
801+ /// <dartdoc-html>4cc02f877240bf69855b4c7291aba8a16e5acce0</dartdoc-html>
802+ /// More comments
803+ ///
804+ /// Which will render in the final HTML output as:
805+ ///
806+ /// Some comments
807+ /// <p>[HTML contents!]</p>
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 {@macro ...} in API comments with the contents of the macro
825+ ///
826+ /// Syntax:
827+ ///
828+ /// {@macro NAME}
829+ ///
830+ /// Example:
831+ ///
832+ /// You define the template in any comment for a documentable entity like:
833+ ///
834+ /// {@template foo}
835+ /// Foo contents!
836+ /// {@endtemplate}
837+ ///
838+ /// and them somewhere use it like this:
839+ ///
840+ /// Some comments
841+ /// {@macro foo}
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