6
6
library dartdoc.markdown_processor;
7
7
8
8
import 'dart:convert' ;
9
+ import 'dart:math' ;
9
10
10
11
import 'package:analyzer/dart/ast/ast.dart' ;
11
12
import 'package:analyzer/dart/element/element.dart'
@@ -14,8 +15,23 @@ import 'package:html/parser.dart' show parse;
14
15
import 'package:markdown/markdown.dart' as md;
15
16
16
17
import 'model.dart' ;
17
-
18
- const bool _emitWarning = false ;
18
+ import 'reporting.dart' ;
19
+
20
+ const validHtmlTags = const [
21
+ "a" , "abbr" , "address" , "area" , "article" , "aside" , "audio" , "b" ,
22
+ "bdi" , "bdo" , "blockquote" , "br" , "button" , "canvas" , "caption" ,
23
+ "cite" , "code" , "col" , "colgroup" , "data" , "datalist" , "dd" , "del" , "dfn" ,
24
+ "div" , "dl" , "dt" , "em" , "fieldset" , "figcaption" , "figure" ,
25
+ "footer" , "form" , "h1" , "h2" , "h3" , "h4" , "h5" , "h6" , "header" , "hr" ,
26
+ "i" , "iframe" , "img" , "input" , "ins" , "kbd" , "keygen" , "label" ,
27
+ "legend" , "li" , "link" , "main" , "map" , "mark" , "meta" , "meter" , "nav" ,
28
+ "noscript" , "object" , "ol" , "optgroup" , "option" , "output" , "p" , "param" ,
29
+ "pre" , "progress" , "q" , "s" , "samp" ,
30
+ "script" , "section" , "select" , "small" , "source" , "span" , "strong" , "style" ,
31
+ "sub" , "sup" , "table" , "tbody" , "td" , "template" , "textarea" , "tfoot" , "th" ,
32
+ "thead" , "time" , "title" , "tr" , "track" , "u" , "ul" , "var" , "video" , "wbr"
33
+ ];
34
+ final nonHTMLRegexp = new RegExp ("</?(?!(${validHtmlTags .join ("|" )})[> ])\\ w+[> ]" );
19
35
20
36
// We don't emit warnings currently: #572.
21
37
const List <String > _oneLinerSkipTags = const ["code" , "pre" ];
@@ -137,9 +153,8 @@ MatchingLinkResult _findRefElementInLibrary(String codeRef, ModelElement element
137
153
} else if (result.length == 1 ) {
138
154
return new MatchingLinkResult (result.values.first, result.values.first.name);
139
155
} else {
140
- // TODO: add --fatal-warning, which would make the app crash in case of ambiguous references
141
- print (
142
- "Ambiguous reference to [${codeRef }] in '${element .fullyQualifiedName }' (${element .sourceFileName }:${element .lineNumber }). " +
156
+ warning (
157
+ "Ambiguous reference to [${codeRef }] in ${_elementLocation (element )}. " +
143
158
"We found matches to the following elements: ${result .keys .map ((k ) => "'${k }'" ).join (", " )}" );
144
159
return new MatchingLinkResult (null , null );
145
160
}
@@ -165,23 +180,75 @@ String _linkDocReference(String reference, ModelElement element, NodeList<Commen
165
180
// different for doc references. sigh.
166
181
return '<a ${classContent }href="${linkedElement .href }">$label </a>' ;
167
182
} else {
168
- if (_emitWarning) {
169
- // TODO: add --fatal-warning, which would make the app crash in case of ambiguous references
170
- print (" warning: unresolved doc reference '$reference ' (in $element )" );
171
- }
183
+ warning ("unresolved doc reference '$reference ' (in ${_elementLocation (element )}" );
172
184
return '<code>${HTML_ESCAPE .convert (label )}</code>' ;
173
185
}
174
186
}
175
187
188
+ String _elementLocation (ModelElement element) {
189
+ while ((element.element.documentationComment == null || element.element.documentationComment == "" )
190
+ && element.overriddenElement != null ) {
191
+ element = element.overriddenElement;
192
+ }
193
+ return "'${element .fullyQualifiedName }' (${element .sourceFileName }:${element .lineNumber })" ;
194
+ }
195
+
176
196
String _renderMarkdownToHtml (String text, [ModelElement element]) {
177
197
md.Node _linkResolver (String name) {
178
198
NodeList <CommentReference > commentRefs = _getCommentRefs (element);
179
199
return new md.Text (_linkDocReference (name, element, commentRefs));
180
200
}
181
201
202
+ _showWarningsForGenericsOutsideSquareBracketsBlocks (text, element);
182
203
return md.markdownToHtml (text, inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver);
183
204
}
184
205
206
+ // Generics should be wrapped into `[]` blocks, to avoid handling them as HTML tags
207
+ // (like, [Apple<int>]). @Hixie asked for a warning when there's something, that looks
208
+ // like a non HTML tag (a generic?) outside of a `[]` block.
209
+ // https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942
210
+ void _showWarningsForGenericsOutsideSquareBracketsBlocks (String text, [ModelElement element]) {
211
+ List <int > tagPositions = findFreeHangingGenericsPositions (text);
212
+ if (tagPositions.isNotEmpty) {
213
+ tagPositions.forEach ((int position) {
214
+ String errorMessage = "Generic type handled as HTML" ;
215
+ if (element != null ) {
216
+ errorMessage += " in ${_elementLocation (element )}" ;
217
+ }
218
+ errorMessage += " - '${text .substring (max (position - 20 , 0 ), min (position + 20 , text .length ))}'" ;
219
+ warning (errorMessage);
220
+ });
221
+ }
222
+ }
223
+
224
+ List <int > findFreeHangingGenericsPositions (String string) {
225
+ int currentPosition = 0 ;
226
+ int squareBracketsDepth = 0 ;
227
+ List <int > results = [];
228
+ while (true ) {
229
+ final int nextOpenBracket = string.indexOf ("[" , currentPosition);
230
+ final int nextCloseBracket = string.indexOf ("]" , currentPosition);
231
+ final int nextNonHTMLTag = string.indexOf (nonHTMLRegexp, currentPosition);
232
+ final Iterable <int > nextPositions = [nextOpenBracket, nextCloseBracket, nextNonHTMLTag].where ((p) => p != - 1 );
233
+ if (nextPositions.isNotEmpty) {
234
+ final minPos = nextPositions.reduce (min);
235
+ if (nextOpenBracket == minPos) {
236
+ squareBracketsDepth += 1 ;
237
+ } else if (nextCloseBracket == minPos) {
238
+ squareBracketsDepth = max (squareBracketsDepth - 1 , 0 );
239
+ } else if (nextNonHTMLTag == minPos) {
240
+ if (squareBracketsDepth == 0 ) {
241
+ results.add (minPos);
242
+ }
243
+ }
244
+ currentPosition = minPos + 1 ;
245
+ } else {
246
+ break ;
247
+ }
248
+ }
249
+ return results;
250
+ }
251
+
185
252
class Documentation {
186
253
final String raw;
187
254
final String asHtml;
0 commit comments