OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** An awesome documentation generator. */ | 5 /** An awesome documentation generator. */ |
6 #library('dartdoc'); | 6 #library('dartdoc'); |
7 | 7 |
8 #import('../../frog/lang.dart'); | 8 #import('../../frog/lang.dart'); |
| 9 #import('../../frog/file_system.dart'); |
9 #import('../../frog/file_system_node.dart'); | 10 #import('../../frog/file_system_node.dart'); |
10 | 11 |
11 #source('classify.dart'); | 12 #source('classify.dart'); |
12 | 13 |
13 /** Path to corePath library. */ | 14 /** Path to corePath library. */ |
14 final corePath = 'lib'; | 15 final corePath = 'lib'; |
15 | 16 |
16 /** Path to generate html files into. */ | 17 /** Path to generate html files into. */ |
17 final outdir = 'docs'; | 18 final outdir = 'docs'; |
18 | 19 |
19 /** Special comment position used to store the library-level doc comment. */ | 20 /** Special comment position used to store the library-level doc comment. */ |
20 final _libraryDoc = -1; | 21 final _libraryDoc = -1; |
21 | 22 |
22 /** The file currently being written to. */ | 23 /** The file currently being written to. */ |
23 StringBuffer _file; | 24 StringBuffer _file; |
24 | 25 |
25 /** | 26 /** |
26 * The cached lookup-table to associate doc comments with spans. The outer map | 27 * The cached lookup-table to associate doc comments with spans. The outer map |
27 * is from filenames to doc comments in that file. The inner map maps from the | 28 * is from filenames to doc comments in that file. The inner map maps from the |
28 * token positions to doc comments. Each position is the starting offset of the | 29 * token positions to doc comments. Each position is the starting offset of the |
29 * next non-comment token *following* the doc comment. For example, the position | 30 * next non-comment token *following* the doc comment. For example, the position |
30 * for this comment would be the position of the "Map" token below. | 31 * for this comment would be the position of the "Map" token below. |
31 */ | 32 */ |
32 Map<String, Map<int, String>> _comments; | 33 Map<String, Map<int, String>> _comments; |
33 | 34 |
34 int _totalLibraries = 0; | 35 int _totalLibraries = 0; |
35 int _totalTypes = 0; | 36 int _totalTypes = 0; |
36 int _totalMembers = 0; | 37 int _totalMembers = 0; |
37 | 38 |
| 39 FileSystem files; |
| 40 |
38 /** | 41 /** |
39 * Run this from the frog/samples directory. Before running, you need | 42 * Run this from the frog/samples directory. Before running, you need |
40 * to create a docs dir with 'mkdir docs' - since Dart currently doesn't | 43 * to create a docs dir with 'mkdir docs' - since Dart currently doesn't |
41 * support creating new directories. | 44 * support creating new directories. |
42 */ | 45 */ |
43 void main() { | 46 void main() { |
44 // The entrypoint of the library to generate docs for. | 47 // The entrypoint of the library to generate docs for. |
45 final libPath = process.argv[2]; | 48 final libPath = process.argv[2]; |
46 | 49 |
47 // TODO(rnystrom): Get options and homedir like frog.dart does. | 50 files = new NodeFileSystem(); |
48 final files = new NodeFileSystem(); | |
49 parseOptions('../../frog', [] /* args */, files); | 51 parseOptions('../../frog', [] /* args */, files); |
50 | 52 |
51 final elapsed = time(() { | 53 final elapsed = time(() { |
| 54 _comments = <String, Map<int, String>>{}; |
| 55 |
52 initializeWorld(files); | 56 initializeWorld(files); |
53 | 57 |
54 world.processScript(libPath); | 58 world.processScript(libPath); |
55 world.resolveAll(); | 59 world.resolveAll(); |
56 | 60 |
57 _comments = <String, Map<int, String>>{}; | 61 // Clean the output directory. |
| 62 if (files.fileExists(outdir)) { |
| 63 files.removeDirectory(outdir, recursive: true); |
| 64 } |
| 65 files.createDirectory(outdir, recursive: true); |
58 | 66 |
59 for (var library in world.libraries.getValues()) { | 67 // Copy over the static files. |
| 68 for (final file in ['interact.js', 'styles.css']) { |
| 69 copyStatic(file); |
| 70 } |
| 71 |
| 72 // Generate the docs. |
| 73 for (final library in world.libraries.getValues()) { |
60 docLibrary(library); | 74 docLibrary(library); |
61 } | 75 } |
62 | 76 |
63 docIndex(world.libraries.getValues()); | 77 docIndex(world.libraries.getValues()); |
64 }); | 78 }); |
65 | 79 |
66 print('Documented $_totalLibraries libraries, $_totalTypes types, and ' + | 80 print('Documented $_totalLibraries libraries, $_totalTypes types, and ' + |
67 '$_totalMembers members in ${elapsed}msec.'); | 81 '$_totalMembers members in ${elapsed}msec.'); |
68 } | 82 } |
69 | 83 |
| 84 /** Copies the static file at 'static/file' to the output directory. */ |
| 85 copyStatic(String file) { |
| 86 var contents = files.readAll(joinPaths('static', file)); |
| 87 files.writeString(joinPaths(outdir, file), contents); |
| 88 } |
| 89 |
70 num time(callback()) { | 90 num time(callback()) { |
71 // Unlike world.withTiming, returns the elapsed time. | 91 // Unlike world.withTiming, returns the elapsed time. |
72 final watch = new Stopwatch(); | 92 final watch = new Stopwatch(); |
73 watch.start(); | 93 watch.start(); |
74 callback(); | 94 callback(); |
75 watch.stop(); | 95 watch.stop(); |
76 return watch.elapsedInMs(); | 96 return watch.elapsedInMs(); |
77 } | 97 } |
78 | 98 |
79 startFile() { | 99 startFile() { |
(...skipping 24 matching lines...) Expand all Loading... |
104 ''' | 124 ''' |
105 <html><head> | 125 <html><head> |
106 <title>Index</title> | 126 <title>Index</title> |
107 <link rel="stylesheet" type="text/css" href="styles.css" /> | 127 <link rel="stylesheet" type="text/css" href="styles.css" /> |
108 </head> | 128 </head> |
109 <body> | 129 <body> |
110 <div class="content"> | 130 <div class="content"> |
111 <ul> | 131 <ul> |
112 '''); | 132 '''); |
113 | 133 |
114 var sorted = new List<Library>.from(libraries); | 134 final sorted = new List<Library>.from(libraries); |
115 sorted.sort((a, b) => a.name.compareTo(b.name)); | 135 sorted.sort((a, b) => a.name.compareTo(b.name)); |
116 | 136 |
117 for (var library in sorted) { | 137 for (final library in sorted) { |
118 writeln( | 138 writeln( |
119 ''' | 139 ''' |
120 <li><a href="${sanitize(library.name)}.html"> | 140 <li><a href="${sanitize(library.name)}.html"> |
121 Library ${library.name}</a> | 141 Library ${library.name}</a> |
122 </li> | 142 </li> |
123 '''); | 143 '''); |
124 } | 144 } |
125 | 145 |
126 writeln( | 146 writeln( |
127 ''' | 147 ''' |
(...skipping 25 matching lines...) Expand all Loading... |
153 | 173 |
154 bool needsSeparator = false; | 174 bool needsSeparator = false; |
155 | 175 |
156 // Look for a comment for the entire library. | 176 // Look for a comment for the entire library. |
157 final comment = findCommentInFile(library.baseSource, _libraryDoc); | 177 final comment = findCommentInFile(library.baseSource, _libraryDoc); |
158 if (comment != null) { | 178 if (comment != null) { |
159 writeln('<div class="doc"><p>$comment</p></div>'); | 179 writeln('<div class="doc"><p>$comment</p></div>'); |
160 needsSeparator = true; | 180 needsSeparator = true; |
161 } | 181 } |
162 | 182 |
163 for (var type in library.types.getValues()) { | 183 for (final type in library.types.getValues()) { |
164 if (needsSeparator) writeln('<hr/>'); | 184 if (needsSeparator) writeln('<hr/>'); |
165 if (docType(type)) needsSeparator = true; | 185 if (docType(type)) needsSeparator = true; |
166 } | 186 } |
167 | 187 |
168 writeln( | 188 writeln( |
169 ''' | 189 ''' |
170 </div> | 190 </div> |
171 </body></html> | 191 </body></html> |
172 '''); | 192 '''); |
173 | 193 |
(...skipping 20 matching lines...) Expand all Loading... |
194 '''); | 214 '''); |
195 | 215 |
196 docInheritance(type); | 216 docInheritance(type); |
197 docCode(type.span); | 217 docCode(type.span); |
198 docConstructors(type); | 218 docConstructors(type); |
199 | 219 |
200 wroteSomething = true; | 220 wroteSomething = true; |
201 } | 221 } |
202 | 222 |
203 // Collect the different kinds of members. | 223 // Collect the different kinds of members. |
204 var methods = []; | 224 final methods = []; |
205 var fields = []; | 225 final fields = []; |
206 | 226 |
207 for (var member in orderValuesByKeys(type.members)) { | 227 for (final member in orderValuesByKeys(type.members)) { |
208 if (member.isMethod && | 228 if (member.isMethod && |
209 (member.definition != null) && | 229 (member.definition != null) && |
210 !member.name.startsWith('_')) { | 230 !member.name.startsWith('_')) { |
211 methods.add(member); | 231 methods.add(member); |
212 } else if (member.isProperty) { | 232 } else if (member.isProperty) { |
213 if (member.canGet) methods.add(member.getter); | 233 if (member.canGet) methods.add(member.getter); |
214 if (member.canSet) methods.add(member.setter); | 234 if (member.canSet) methods.add(member.setter); |
215 } else if (member.isField && !member.name.startsWith('_')) { | 235 } else if (member.isField && !member.name.startsWith('_')) { |
216 fields.add(member); | 236 fields.add(member); |
217 } | 237 } |
218 } | 238 } |
219 | 239 |
220 if (methods.length > 0) { | 240 if (methods.length > 0) { |
221 writeln('<h3>Methods</h3>'); | 241 writeln('<h3>Methods</h3>'); |
222 for (var method in methods) docMethod(type.name, method); | 242 for (final method in methods) docMethod(type.name, method); |
223 } | 243 } |
224 | 244 |
225 if (fields.length > 0) { | 245 if (fields.length > 0) { |
226 writeln('<h3>Fields</h3>'); | 246 writeln('<h3>Fields</h3>'); |
227 for (var field in fields) docField(type.name, field); | 247 for (final field in fields) docField(type.name, field); |
228 } | 248 } |
229 | 249 |
230 return wroteSomething || methods.length > 0 || fields.length > 0; | 250 return wroteSomething || methods.length > 0 || fields.length > 0; |
231 } | 251 } |
232 | 252 |
233 /** Document the superclass and superinterfaces of [Type]. */ | 253 /** Document the superclass and superinterfaces of [Type]. */ |
234 docInheritance(Type type) { | 254 docInheritance(Type type) { |
235 // Show the superclass and superinterface(s). | 255 // Show the superclass and superinterface(s). |
236 if ((type.parent != null) && (type.parent.isObject) || | 256 if ((type.parent != null) && (type.parent.isObject) || |
237 (type.interfaces != null && type.interfaces.length > 0)) { | 257 (type.interfaces != null && type.interfaces.length > 0)) { |
238 writeln('<p>'); | 258 writeln('<p>'); |
239 | 259 |
240 if (type.parent != null) { | 260 if (type.parent != null) { |
241 write('Extends ${typeRef(type.parent)}. '); | 261 write('Extends ${typeRef(type.parent)}. '); |
242 } | 262 } |
243 | 263 |
244 if (type.interfaces != null) { | 264 if (type.interfaces != null) { |
245 var interfaces = []; | 265 final interfaces = []; |
246 switch (type.interfaces.length) { | 266 switch (type.interfaces.length) { |
247 case 0: | 267 case 0: |
248 // Do nothing. | 268 // Do nothing. |
249 break; | 269 break; |
250 | 270 |
251 case 1: | 271 case 1: |
252 write('Implements ${typeRef(type.interfaces[0])}.'); | 272 write('Implements ${typeRef(type.interfaces[0])}.'); |
253 break; | 273 break; |
254 | 274 |
255 case 2: | 275 case 2: |
256 write('''Implements ${typeRef(type.interfaces[0])} and | 276 write('''Implements ${typeRef(type.interfaces[0])} and |
257 ${typeRef(type.interfaces[1])}.'''); | 277 ${typeRef(type.interfaces[1])}.'''); |
258 break; | 278 break; |
259 | 279 |
260 default: | 280 default: |
261 write('Implements '); | 281 write('Implements '); |
262 for (var i = 0; i < type.interfaces.length; i++) { | 282 for (final i = 0; i < type.interfaces.length; i++) { |
263 write('${typeRef(type.interfaces[i])}'); | 283 write('${typeRef(type.interfaces[i])}'); |
264 if (i < type.interfaces.length - 1) { | 284 if (i < type.interfaces.length - 1) { |
265 write(', '); | 285 write(', '); |
266 } else { | 286 } else { |
267 write(' and '); | 287 write(' and '); |
268 } | 288 } |
269 } | 289 } |
270 write('.'); | 290 write('.'); |
271 break; | 291 break; |
272 } | 292 } |
273 } | 293 } |
274 } | 294 } |
275 } | 295 } |
276 | 296 |
277 /** Document the constructors for [Type], if any. */ | 297 /** Document the constructors for [Type], if any. */ |
278 docConstructors(Type type) { | 298 docConstructors(Type type) { |
279 if (type.constructors.length > 0) { | 299 if (type.constructors.length > 0) { |
280 writeln('<h3>Constructors</h3>'); | 300 writeln('<h3>Constructors</h3>'); |
281 for (var name in type.constructors.getKeys()) { | 301 for (final name in type.constructors.getKeys()) { |
282 var constructor = type.constructors[name]; | 302 final constructor = type.constructors[name]; |
283 docMethod(type.name, constructor, namedConstructor: name); | 303 docMethod(type.name, constructor, namedConstructor: name); |
284 } | 304 } |
285 } | 305 } |
286 } | 306 } |
287 | 307 |
288 /** | 308 /** |
289 * Documents the [method] in a type named [typeName]. Handles all kinds of | 309 * Documents the [method] in a type named [typeName]. Handles all kinds of |
290 * methods including getters, setters, and constructors. | 310 * methods including getters, setters, and constructors. |
291 */ | 311 */ |
292 docMethod(String typeName, MethodMember method, | 312 docMethod(String typeName, MethodMember method, |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 | 353 |
334 write('<strong>$name</strong>'); | 354 write('<strong>$name</strong>'); |
335 | 355 |
336 // Named constructors. | 356 // Named constructors. |
337 if (namedConstructor != null && namedConstructor != '') { | 357 if (namedConstructor != null && namedConstructor != '') { |
338 write('.'); | 358 write('.'); |
339 write(namedConstructor); | 359 write(namedConstructor); |
340 } | 360 } |
341 | 361 |
342 write('('); | 362 write('('); |
343 var paramList = []; | 363 final paramList = []; |
344 if (method.parameters == null) print(method.name); | 364 if (method.parameters == null) print(method.name); |
345 for (var p in method.parameters) { | 365 for (final p in method.parameters) { |
346 paramList.add('${optionalTypeRef(p.type)}${p.name}'); | 366 paramList.add('${optionalTypeRef(p.type)}${p.name}'); |
347 } | 367 } |
348 write(Strings.join(paramList, ", ")); | 368 write(Strings.join(paramList, ", ")); |
349 write(')'); | 369 write(')'); |
350 | 370 |
351 write(''' <a class="anchor-link" href="#$typeName.${method.name}" | 371 write(''' <a class="anchor-link" href="#$typeName.${method.name}" |
352 title="Permalink to $typeName.$name">#</a>'''); | 372 title="Permalink to $typeName.$name">#</a>'''); |
353 writeln('</h4>'); | 373 writeln('</h4>'); |
354 | 374 |
355 docCode(method.span, showCode: true); | 375 docCode(method.span, showCode: true); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 docCode(field.span, showCode: true); | 411 docCode(field.span, showCode: true); |
392 writeln('</div>'); | 412 writeln('</div>'); |
393 } | 413 } |
394 | 414 |
395 /** | 415 /** |
396 * Writes a type annotation for [type]. Will hyperlink it to that type's | 416 * Writes a type annotation for [type]. Will hyperlink it to that type's |
397 * documentation if possible. | 417 * documentation if possible. |
398 */ | 418 */ |
399 typeRef(Type type) { | 419 typeRef(Type type) { |
400 if (type.library != null) { | 420 if (type.library != null) { |
401 var library = sanitize(type.library.name); | 421 final library = sanitize(type.library.name); |
402 return '<a href="${library}.html#${type.name}">${type.name}</a>'; | 422 return '<a href="${library}.html#${type.name}">${type.name}</a>'; |
403 } else { | 423 } else { |
404 return type.name; | 424 return type.name; |
405 } | 425 } |
406 } | 426 } |
407 | 427 |
408 /** | 428 /** |
409 * Creates a linked string for an optional type annotation. Returns an empty | 429 * Creates a linked string for an optional type annotation. Returns an empty |
410 * string if the type is Dynamic. | 430 * string if the type is Dynamic. |
411 */ | 431 */ |
412 optionalTypeRef(Type type) { | 432 optionalTypeRef(Type type) { |
413 if (type.name == 'Dynamic') { | 433 if (type.name == 'Dynamic') { |
414 return ''; | 434 return ''; |
415 } else { | 435 } else { |
416 return typeRef(type) + ' '; | 436 return typeRef(type) + ' '; |
417 } | 437 } |
418 } | 438 } |
419 | 439 |
420 /** | 440 /** |
421 * Documents the code contained within [span]. Will include the previous | 441 * Documents the code contained within [span]. Will include the previous |
422 * Dartdoc associated with that span if found, and will include the syntax | 442 * Dartdoc associated with that span if found, and will include the syntax |
423 * highlighted code itself if desired. | 443 * highlighted code itself if desired. |
424 */ | 444 */ |
425 docCode(SourceSpan span, [bool showCode = false]) { | 445 docCode(SourceSpan span, [bool showCode = false]) { |
426 if (span == null) return; | 446 if (span == null) return; |
427 | 447 |
428 writeln('<div class="doc">'); | 448 writeln('<div class="doc">'); |
429 var comment = findComment(span); | 449 final comment = findComment(span); |
430 if (comment != null) { | 450 if (comment != null) { |
431 writeln('<p>$comment</p>'); | 451 writeln('<p>$comment</p>'); |
432 } | 452 } |
433 | 453 |
434 if (showCode) { | 454 if (showCode) { |
435 writeln('<pre class="source">'); | 455 writeln('<pre class="source">'); |
436 write(formatCode(span)); | 456 write(formatCode(span)); |
437 writeln('</pre>'); | 457 writeln('</pre>'); |
438 } | 458 } |
439 | 459 |
440 writeln('</div>'); | 460 writeln('</div>'); |
441 } | 461 } |
442 | 462 |
443 /** Finds the doc comment preceding the given source span, if there is one. */ | 463 /** Finds the doc comment preceding the given source span, if there is one. */ |
444 findComment(SourceSpan span) => findCommentInFile(span.file, span.start); | 464 findComment(SourceSpan span) => findCommentInFile(span.file, span.start); |
445 | 465 |
446 /** Finds the doc comment preceding the given source span, if there is one. */ | 466 /** Finds the doc comment preceding the given source span, if there is one. */ |
447 findCommentInFile(SourceFile file, int position) { | 467 findCommentInFile(SourceFile file, int position) { |
448 // Get the doc comments for this file. | 468 // Get the doc comments for this file. |
449 var fileComments = _comments.putIfAbsent(file.filename, | 469 final fileComments = _comments.putIfAbsent(file.filename, |
450 () => parseDocComments(file)); | 470 () => parseDocComments(file)); |
451 | 471 |
452 return fileComments[position]; | 472 return fileComments[position]; |
453 } | 473 } |
454 | 474 |
455 parseDocComments(SourceFile file) { | 475 parseDocComments(SourceFile file) { |
456 var comments = <int, String>{}; | 476 final comments = <int, String>{}; |
457 | 477 |
458 var tokenizer = new Tokenizer(file, false); | 478 final tokenizer = new Tokenizer(file, false); |
459 var lastComment = null; | 479 var lastComment = null; |
460 | 480 |
461 while (true) { | 481 while (true) { |
462 var token = tokenizer.next(); | 482 final token = tokenizer.next(); |
463 if (token.kind == TokenKind.END_OF_FILE) break; | 483 if (token.kind == TokenKind.END_OF_FILE) break; |
464 | 484 |
465 if (token.kind == TokenKind.COMMENT) { | 485 if (token.kind == TokenKind.COMMENT) { |
466 var text = token.text; | 486 final text = token.text; |
467 if (text.startsWith('/**')) { | 487 if (text.startsWith('/**')) { |
468 // Remember that we've encountered a doc comment. | 488 // Remember that we've encountered a doc comment. |
469 lastComment = stripComment(token.text); | 489 lastComment = stripComment(token.text); |
470 } | 490 } |
471 } else if (token.kind == TokenKind.WHITESPACE) { | 491 } else if (token.kind == TokenKind.WHITESPACE) { |
472 // Ignore whitespace tokens. | 492 // Ignore whitespace tokens. |
473 } else if (token.kind == TokenKind.HASH) { | 493 } else if (token.kind == TokenKind.HASH) { |
474 // Look for #library() to find the library comment. | 494 // Look for #library() to find the library comment. |
475 var next = tokenizer.next(); | 495 final next = tokenizer.next(); |
476 if ((lastComment != null) && (next.kind == TokenKind.LIBRARY)) { | 496 if ((lastComment != null) && (next.kind == TokenKind.LIBRARY)) { |
477 comments[_libraryDoc] = lastComment; | 497 comments[_libraryDoc] = lastComment; |
478 lastComment = null; | 498 lastComment = null; |
479 } | 499 } |
480 } else { | 500 } else { |
481 if (lastComment != null) { | 501 if (lastComment != null) { |
482 // We haven't attached the last doc comment to something yet, so stick | 502 // We haven't attached the last doc comment to something yet, so stick |
483 // it to this token. | 503 // it to this token. |
484 comments[token.start] = lastComment; | 504 comments[token.start] = lastComment; |
485 lastComment = null; | 505 lastComment = null; |
486 } | 506 } |
487 } | 507 } |
488 } | 508 } |
489 | 509 |
490 return comments; | 510 return comments; |
491 } | 511 } |
492 | 512 |
493 /** | 513 /** |
494 * Takes a string of Dart code and turns it into sanitized HTML. | 514 * Takes a string of Dart code and turns it into sanitized HTML. |
495 */ | 515 */ |
496 formatCode(SourceSpan span) { | 516 formatCode(SourceSpan span) { |
497 // Remove leading indentation to line up with first line. | 517 // Remove leading indentation to line up with first line. |
498 var column = getSpanColumn(span); | 518 final column = getSpanColumn(span); |
499 var lines = span.text.split('\n'); | 519 final lines = span.text.split('\n'); |
500 // TODO(rnystrom): Dirty hack. | 520 // TODO(rnystrom): Dirty hack. |
501 for (int i = 1; i < lines.length; i++) { | 521 for (final i = 1; i < lines.length; i++) { |
502 lines[i] = unindent(lines[i], column); | 522 lines[i] = unindent(lines[i], column); |
503 } | 523 } |
504 | 524 |
505 var code = Strings.join(lines, '\n'); | 525 final code = Strings.join(lines, '\n'); |
506 | 526 |
507 // Syntax highlight. | 527 // Syntax highlight. |
508 return classifySource(new SourceFile('', code)); | 528 return classifySource(new SourceFile('', code)); |
509 } | 529 } |
510 | 530 |
511 // TODO(rnystrom): Move into SourceSpan? | 531 // TODO(rnystrom): Move into SourceSpan? |
512 int getSpanColumn(SourceSpan span) { | 532 int getSpanColumn(SourceSpan span) { |
513 var line = span.file.getLine(span.start); | 533 final line = span.file.getLine(span.start); |
514 return span.file.getColumn(line, span.start); | 534 return span.file.getColumn(line, span.start); |
515 } | 535 } |
516 | 536 |
517 /** Removes up to [indentation] leading whitespace characters from [text]. */ | 537 /** Removes up to [indentation] leading whitespace characters from [text]. */ |
518 unindent(String text, int indentation) { | 538 unindent(String text, int indentation) { |
519 var start; | 539 var start; |
520 for (start = 0; start < Math.min(indentation, text.length); start++) { | 540 for (start = 0; start < Math.min(indentation, text.length); start++) { |
521 // Stop if we hit a non-whitespace character. | 541 // Stop if we hit a non-whitespace character. |
522 if (text[start] != ' ') break; | 542 if (text[start] != ' ') break; |
523 } | 543 } |
524 | 544 |
525 return text.substring(start); | 545 return text.substring(start); |
526 } | 546 } |
527 | 547 |
528 /** | 548 /** |
529 * Pulls the raw text out of a doc comment (i.e. removes the comment | 549 * Pulls the raw text out of a doc comment (i.e. removes the comment |
530 * characters. | 550 * characters. |
531 */ | 551 */ |
532 // TODO(rnystrom): Should handle [name] and [:code:] in comments. Should also | 552 // TODO(rnystrom): Should handle [name] and [:code:] in comments. Should also |
533 // break empty lines into multiple paragraphs. Other formatting? | 553 // break empty lines into multiple paragraphs. Other formatting? |
534 // See dart/compiler/java/com/google/dart/compiler/backend/doc for ideas. | 554 // See dart/compiler/java/com/google/dart/compiler/backend/doc for ideas. |
535 // (/DartDocumentationVisitor.java#180) | 555 // (/DartDocumentationVisitor.java#180) |
536 stripComment(comment) { | 556 stripComment(comment) { |
537 StringBuffer buf = new StringBuffer(); | 557 StringBuffer buf = new StringBuffer(); |
538 | 558 |
539 for (var line in comment.split('\n')) { | 559 for (final line in comment.split('\n')) { |
540 line = line.trim(); | 560 line = line.trim(); |
541 if (line.startsWith('/**')) line = line.substring(3, line.length); | 561 if (line.startsWith('/**')) line = line.substring(3, line.length); |
542 if (line.endsWith('*/')) line = line.substring(0, line.length-2); | 562 if (line.endsWith('*/')) line = line.substring(0, line.length-2); |
543 line = line.trim(); | 563 line = line.trim(); |
544 while (line.startsWith('*')) line = line.substring(1, line.length); | 564 while (line.startsWith('*')) line = line.substring(1, line.length); |
545 line = line.trim(); | 565 line = line.trim(); |
546 buf.add(line); | 566 buf.add(line); |
547 buf.add(' '); | 567 buf.add(' '); |
548 } | 568 } |
549 | 569 |
550 return buf.toString(); | 570 return buf.toString(); |
551 } | 571 } |
OLD | NEW |