6
6
library dartdoc.markdown_processor;
7
7
8
8
import 'dart:convert' ;
9
+ import 'dart:io' ;
9
10
import 'dart:math' ;
10
11
11
12
import 'package:analyzer/dart/ast/ast.dart' ;
12
13
import 'package:analyzer/dart/element/element.dart' ;
13
14
import 'package:analyzer/src/dart/element/member.dart' show Member;
14
15
import 'package:html/parser.dart' show parse;
15
16
import 'package:markdown/markdown.dart' as md;
17
+ import 'package:tuple/tuple.dart' ;
16
18
17
19
import 'model.dart' ;
18
20
@@ -134,9 +136,6 @@ final RegExp isConstructor = new RegExp(r'^new[\s]+', multiLine: true);
134
136
// Covers anything with leading digits/symbols, empty string, weird punctuation, spaces.
135
137
final RegExp notARealDocReference = new RegExp (r'''(^[^\w]|^[\d]|[,"'/]|^$)''' );
136
138
137
- // We don't emit warnings currently: #572.
138
- const List <String > _oneLinerSkipTags = const ["code" , "pre" ];
139
-
140
139
final List <md.InlineSyntax > _markdown_syntaxes = [
141
140
new _InlineCodeSyntax (),
142
141
new _AutolinkWithoutScheme ()
@@ -152,6 +151,22 @@ class MatchingLinkResult {
152
151
MatchingLinkResult (this .element, this .label, {this .warn: true });
153
152
}
154
153
154
+ class IterableBlockParser extends md.BlockParser {
155
+ IterableBlockParser (lines, document) : super (lines, document);
156
+
157
+ Iterable <md.Node > parseLinesGenerator () sync * {
158
+ while (! isDone) {
159
+ for (var syntax in blockSyntaxes) {
160
+ if (syntax.canParse (this )) {
161
+ md.Node block = syntax.parse (this );
162
+ if (block != null ) yield (block);
163
+ break ;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
155
170
// Calculate a class hint for findCanonicalModelElementFor.
156
171
ModelElement _getPreferredClass (ModelElement modelElement) {
157
172
if (modelElement is EnclosedElement &&
@@ -667,23 +682,14 @@ String _linkDocReference(String codeRef, Documentable documentable,
667
682
}
668
683
}
669
684
670
- String _renderMarkdownToHtml (Documentable element) {
671
- NodeList <CommentReference > commentRefs = _getCommentRefs (element);
672
- md.Node _linkResolver (String name) {
673
- return new md.Text (_linkDocReference (name, element, commentRefs));
674
- }
675
-
676
- String text = element.documentation;
677
- _showWarningsForGenericsOutsideSquareBracketsBlocks (text, element);
678
- return md.markdownToHtml (text,
679
- inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver);
680
- }
681
-
682
685
// Maximum number of characters to display before a suspected generic.
683
686
const maxPriorContext = 20 ;
684
687
// Maximum number of characters to display after the beginning of a suspected generic.
685
688
const maxPostContext = 30 ;
686
689
690
+ final RegExp allBeforeFirstNewline = new RegExp (r'^.*\n' , multiLine: true );
691
+ final RegExp allAfterLastNewline = new RegExp (r'\n.*$' , multiLine: true );
692
+
687
693
// Generics should be wrapped into `[]` blocks, to avoid handling them as HTML tags
688
694
// (like, [Apple<int>]). @Hixie asked for a warning when there's something, that looks
689
695
// like a non HTML tag (a generic?) outside of a `[]` block.
@@ -697,10 +703,8 @@ void _showWarningsForGenericsOutsideSquareBracketsBlocks(
697
703
"${text .substring (max (position - maxPriorContext , 0 ), position )}" ;
698
704
String postContext =
699
705
"${text .substring (position , min (position + maxPostContext , text .length ))}" ;
700
- priorContext =
701
- priorContext.replaceAll (new RegExp (r'^.*\n' , multiLine: true ), '' );
702
- postContext =
703
- postContext.replaceAll (new RegExp (r'\n.*$' , multiLine: true ), '' );
706
+ priorContext = priorContext.replaceAll (allBeforeFirstNewline, '' );
707
+ postContext = postContext.replaceAll (allAfterLastNewline, '' );
704
708
String errorMessage = "$priorContext $postContext " ;
705
709
// TODO(jcollins-g): allow for more specific error location inside comments
706
710
element.warn (PackageWarning .typeAsHtml, message: errorMessage);
@@ -740,19 +744,24 @@ List<int> findFreeHangingGenericsPositions(String string) {
740
744
return results;
741
745
}
742
746
743
- class Documentation {
744
- final String raw;
745
- final String asHtml;
746
- final String asOneLiner;
747
-
748
- factory Documentation .forElement (Documentable element) {
749
- String tempHtml = _renderMarkdownToHtml (element);
750
- return new Documentation ._internal (element.documentation, tempHtml);
751
- }
752
-
753
- Documentation ._(this .raw, this .asHtml, this .asOneLiner);
754
-
755
- factory Documentation ._internal (String markdown, String rawHtml) {
747
+ class MarkdownDocument extends md.Document {
748
+ MarkdownDocument (
749
+ {Iterable <md.BlockSyntax > blockSyntaxes,
750
+ Iterable <md.InlineSyntax > inlineSyntaxes,
751
+ md.ExtensionSet extensionSet,
752
+ linkResolver,
753
+ imageLinkResolver})
754
+ : super (
755
+ blockSyntaxes: blockSyntaxes,
756
+ inlineSyntaxes: inlineSyntaxes,
757
+ extensionSet: extensionSet,
758
+ linkResolver: linkResolver,
759
+ imageLinkResolver: imageLinkResolver);
760
+
761
+ /// Returns a tuple of longHtml, shortHtml. longHtml is NULL if [processFullDocs] is true.
762
+ static Tuple2 <String , String > _renderNodesToHtml (
763
+ List <md.Node > nodes, bool processFullDocs) {
764
+ var rawHtml = new md.HtmlRenderer ().render (nodes);
756
765
var asHtmlDocument = parse (rawHtml);
757
766
for (var s in asHtmlDocument.querySelectorAll ('script' )) {
758
767
s.remove ();
@@ -775,16 +784,139 @@ class Documentation {
775
784
// Assume the user intended Dart if there are no other classes present.
776
785
if (! specifiesLanguage) pre.classes.add ('language-dart' );
777
786
}
787
+ String asHtml;
788
+ String asOneLiner;
778
789
779
- // `trim` fixes issue with line ending differences between mac and windows.
780
- var asHtml = asHtmlDocument.body.innerHtml? .trim ();
781
- var asOneLiner = asHtmlDocument.body.children.isEmpty
790
+ if (processFullDocs) {
791
+ // `trim` fixes issue with line ending differences between mac and windows.
792
+ asHtml = asHtmlDocument.body.innerHtml? .trim ();
793
+ }
794
+ asOneLiner = asHtmlDocument.body.children.isEmpty
782
795
? ''
783
796
: asHtmlDocument.body.children.first.innerHtml;
784
- if (! asOneLiner.startsWith ('<p>' )) {
785
- asOneLiner = '<p>$asOneLiner </p>' ;
797
+
798
+ return new Tuple2 (asHtml, asOneLiner);
799
+ }
800
+
801
+ // From package:markdown/src/document.dart
802
+ // TODO(jcollins-g): consider making this a public method in markdown package
803
+ void _parseInlineContent (List <md.Node > nodes) {
804
+ for (int i = 0 ; i < nodes.length; i++ ) {
805
+ var node = nodes[i];
806
+ if (node is md.UnparsedContent ) {
807
+ List <md.Node > inlineNodes =
808
+ new md.InlineParser (node.textContent, this ).parse ();
809
+ nodes.removeAt (i);
810
+ nodes.insertAll (i, inlineNodes);
811
+ i += inlineNodes.length - 1 ;
812
+ } else if (node is md.Element && node.children != null ) {
813
+ _parseInlineContent (node.children);
814
+ }
815
+ }
816
+ }
817
+
818
+ /// Returns a tuple of longHtml, shortHtml (longHtml is NULL if !processFullDocs)
819
+ Tuple3 <String , String , bool > renderLinesToHtml (
820
+ List <String > lines, bool processFullDocs) {
821
+ bool hasExtendedDocs = false ;
822
+ md.Node firstNode;
823
+ List <md.Node > nodes = [];
824
+ for (md.Node node
825
+ in new IterableBlockParser (lines, this ).parseLinesGenerator ()) {
826
+ if (firstNode != null ) {
827
+ hasExtendedDocs = true ;
828
+ if (! processFullDocs) break ;
829
+ }
830
+ firstNode ?? = node;
831
+ nodes.add (node);
786
832
}
787
- return new Documentation ._(markdown, asHtml, asOneLiner);
833
+ _parseInlineContent (nodes);
834
+
835
+ String shortHtml;
836
+ String longHtml;
837
+ if (processFullDocs) {
838
+ Tuple2 htmls = _renderNodesToHtml (nodes, processFullDocs);
839
+ longHtml = htmls.item1;
840
+ shortHtml = htmls.item2;
841
+ } else {
842
+ if (firstNode != null ) {
843
+ Tuple2 htmls = _renderNodesToHtml ([firstNode], processFullDocs);
844
+ shortHtml = htmls.item2;
845
+ } else {
846
+ shortHtml = '' ;
847
+ }
848
+ }
849
+ return new Tuple3 <String , String , bool >(
850
+ longHtml, shortHtml, hasExtendedDocs);
851
+ }
852
+ }
853
+
854
+ class Documentation {
855
+ final Documentable _element;
856
+ Documentation .forElement (this ._element) {}
857
+
858
+ bool _hasExtendedDocs;
859
+ bool get hasExtendedDocs {
860
+ if (_hasExtendedDocs == null ) {
861
+ _renderHtmlForDartdoc (_element.isCanonical && _asHtml == null );
862
+ }
863
+ return _hasExtendedDocs;
864
+ }
865
+
866
+ String _asHtml;
867
+ String get asHtml {
868
+ if (_asHtml == null ) {
869
+ assert (_asOneLiner == null || _element.isCanonical);
870
+ _renderHtmlForDartdoc (true );
871
+ }
872
+ return _asHtml;
873
+ }
874
+
875
+ String _asOneLiner;
876
+ String get asOneLiner {
877
+ if (_asOneLiner == null ) {
878
+ assert (_asHtml == null );
879
+ _renderHtmlForDartdoc (_element.isCanonical);
880
+ }
881
+ return _asOneLiner;
882
+ }
883
+
884
+ NodeList <CommentReference > _commentRefs;
885
+ NodeList <CommentReference > get commentRefs {
886
+ if (_commentRefs == null ) _commentRefs = _getCommentRefs (_element);
887
+ return _commentRefs;
888
+ }
889
+
890
+ String get raw => _element.documentation;
891
+
892
+ void _renderHtmlForDartdoc (bool processAllDocs) {
893
+ Tuple3 <String , String , bool > renderResults =
894
+ _renderMarkdownToHtml (processAllDocs);
895
+ if (processAllDocs) {
896
+ _asHtml = renderResults.item1;
897
+ }
898
+ if (_asOneLiner == null ) {
899
+ _asOneLiner = renderResults.item2;
900
+ }
901
+ if (_hasExtendedDocs != null ) {
902
+ assert (_hasExtendedDocs == renderResults.item3);
903
+ }
904
+ _hasExtendedDocs = renderResults.item3;
905
+ }
906
+
907
+ /// Returns a tuple of longHtml, shortHtml, hasExtendedDocs
908
+ /// (longHtml is NULL if !processFullDocs)
909
+ Tuple3 <String , String , bool > _renderMarkdownToHtml (bool processFullDocs) {
910
+ md.Node _linkResolver (String name) {
911
+ return new md.Text (_linkDocReference (name, _element, commentRefs));
912
+ }
913
+
914
+ String text = _element.documentation;
915
+ _showWarningsForGenericsOutsideSquareBracketsBlocks (text, _element);
916
+ MarkdownDocument document = new MarkdownDocument (
917
+ inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver);
918
+ List <String > lines = text.replaceAll ('\r\n ' , '\n ' ).split ('\n ' );
919
+ return document.renderLinesToHtml (lines, processFullDocs);
788
920
}
789
921
}
790
922
0 commit comments