1
1
import 'package:args/args.dart' ;
2
2
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' ;
4
6
import 'package:dartdoc/src/render/model_element_renderer.dart' ;
5
7
import 'package:dartdoc/src/utils.dart' ;
6
8
import 'package:dartdoc/src/warnings.dart' ;
@@ -23,9 +25,15 @@ final _basicToolPattern = RegExp(
23
25
24
26
final _examplePattern = RegExp (r'{@example\s+([^}]+)}' );
25
27
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
+
26
34
/// Features for processing directives in a documentation comment.
27
35
///
28
- /// [processCommentWithoutTools ] and [processComment] are the primary
36
+ /// [_processCommentWithoutTools ] and [processComment] are the primary
29
37
/// entrypoints.
30
38
mixin DocumentationComment
31
39
on Documentable , Warnable , Locatable , SourceCodeMixin {
@@ -59,7 +67,7 @@ mixin DocumentationComment
59
67
60
68
/// Process a [documentationComment] , performing various actions based on
61
69
/// `{@}` -style directives, except `{@tool}` , returning the processed result.
62
- String processCommentWithoutTools (String documentationComment) {
70
+ String _processCommentWithoutTools (String documentationComment) {
63
71
var docs = stripComments (documentationComment);
64
72
if (! docs.contains ('{@' )) {
65
73
_analyzeCodeBlocks (docs);
@@ -79,6 +87,7 @@ mixin DocumentationComment
79
87
80
88
/// Process [documentationComment] , performing various actions based on
81
89
/// `{@}` -style directives, returning the processed result.
90
+ @visibleForTesting
82
91
Future <String > processComment (String documentationComment) async {
83
92
var docs = stripComments (documentationComment);
84
93
// Must evaluate tools first, in case they insert any other directives.
@@ -707,4 +716,145 @@ mixin DocumentationComment
707
716
}
708
717
});
709
718
}
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
+ }
710
860
}
0 commit comments