Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(585)

Side by Side Diff: utils/dartdoc/dartdoc.dart

Issue 9256002: Add a mode to dartdoc to generate the navigation on the client. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Respond to review. Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utils/dartdoc/dartdoc ('k') | utils/dartdoc/interact.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 /** 5 /**
6 * To use it, from this directory, run: 6 * To use it, from this directory, run:
7 * 7 *
8 * $ ./dartdoc <path to .dart file> 8 * $ ./dartdoc <path to .dart file>
9 * 9 *
10 * This will create a "docs" directory with the docs for your libraries. To 10 * This will create a "docs" directory with the docs for your libraries. To
11 * create these beautiful docs, dartdoc parses your library and every library 11 * create these beautiful docs, dartdoc parses your library and every library
12 * it imports (recursively). From each library, it parses all classes and 12 * it imports (recursively). From each library, it parses all classes and
13 * members, finds the associated doc comments and builds crosslinked docs from 13 * members, finds the associated doc comments and builds crosslinked docs from
14 * them. 14 * them.
15 */ 15 */
16 #library('dartdoc'); 16 #library('dartdoc');
17 17
18 #import('dart:json');
18 #import('../../frog/lang.dart'); 19 #import('../../frog/lang.dart');
19 #import('../../frog/file_system.dart'); 20 #import('../../frog/file_system.dart');
20 #import('../../frog/file_system_node.dart'); 21 #import('../../frog/file_system_node.dart');
21 #import('../../frog/lib/node/node.dart'); 22 #import('../../frog/lib/node/node.dart');
23 #import('classify.dart');
22 #import('markdown.dart', prefix: 'md'); 24 #import('markdown.dart', prefix: 'md');
23 25
24 #source('classify.dart');
25 #source('comment_map.dart'); 26 #source('comment_map.dart');
26 #source('files.dart'); 27 #source('files.dart');
27 #source('utils.dart'); 28 #source('utils.dart');
28 29
29 /** 30 /**
31 * Generates completely static HTML containing everything you need to browse
32 * the docs. The only client side behavior is trivial stuff like syntax
33 * highlighting code.
34 */
35 final MODE_STATIC = 0;
36
37 /**
38 * Generated docs do not include baked HTML navigation. Instead, a single
39 * `nav.json` file is created and the appropriate navigation is generated
40 * client-side by parsing that and building HTML.
41 *
42 * This dramatically reduces the generated size of the HTML since a large
43 * fraction of each static page is just redundant navigation links.
44 *
45 * In this mode, the browser will do a XHR for nav.json which means that to
46 * preview docs locally, you will need to enable requesting file:// links in
47 * your browser or run a little local server like `python -m SimpleHTTPServer`.
48 */
49 final MODE_LIVE_NAV = 1;
50
51 /**
30 * Run this from the `utils/dartdoc` directory. 52 * Run this from the `utils/dartdoc` directory.
31 */ 53 */
32 void main() { 54 void main() {
33 // The entrypoint of the library to generate docs for. 55 // The entrypoint of the library to generate docs for.
34 final entrypoint = process.argv[process.argv.length - 1]; 56 final entrypoint = process.argv[process.argv.length - 1];
35 57
36 // Parse the dartdoc options. 58 // Parse the dartdoc options.
37 bool includeSource = true; 59 bool includeSource = true;
60 var mode = MODE_LIVE_NAV;
38 61
39 for (int i = 2; i < process.argv.length - 1; i++) { 62 for (int i = 2; i < process.argv.length - 1; i++) {
40 final arg = process.argv[i]; 63 final arg = process.argv[i];
41 switch (arg) { 64 switch (arg) {
42 case '--no-code': 65 case '--no-code':
43 includeSource = false; 66 includeSource = false;
44 break; 67 break;
45 68
69 case '--mode=static':
70 mode = MODE_STATIC;
71 break;
72
73 case '--mode=live-nav':
74 mode = MODE_LIVE_NAV;
75 break;
76
46 default: 77 default:
47 print('Unknown option: $arg'); 78 print('Unknown option: $arg');
48 } 79 }
49 } 80 }
50 81
51 final files = new NodeFileSystem(); 82 final files = new NodeFileSystem();
52 parseOptions('../../frog', [] /* args */, files); 83 parseOptions('../../frog', [] /* args */, files);
53 initializeWorld(files); 84 initializeWorld(files);
54 85
55 var dartdoc; 86 var dartdoc;
56 final elapsed = time(() { 87 final elapsed = time(() {
57 dartdoc = new Dartdoc(); 88 dartdoc = new Dartdoc();
58 dartdoc.includeSource = includeSource; 89 dartdoc.includeSource = includeSource;
90 dartdoc.mode = mode;
91
59 dartdoc.document(entrypoint); 92 dartdoc.document(entrypoint);
60 }); 93 });
61 94
62 print('Documented ${dartdoc._totalLibraries} libraries, ' + 95 print('Documented ${dartdoc._totalLibraries} libraries, ' +
63 '${dartdoc._totalTypes} types, and ' + 96 '${dartdoc._totalTypes} types, and ' +
64 '${dartdoc._totalMembers} members in ${elapsed}msec.'); 97 '${dartdoc._totalMembers} members in ${elapsed}msec.');
65 } 98 }
66 99
67 class Dartdoc { 100 class Dartdoc {
68 /** Set to `false` to not include the source code in the generated docs. */ 101 /** Set to `false` to not include the source code in the generated docs. */
69 bool includeSource = true; 102 bool includeSource = true;
70 103
71 /** 104 /**
105 * Dartdoc can generate docs in a few different ways based on how dynamic you
106 * want the client-side behavior to be. The value for this should be one of
107 * the `MODE_` constants.
108 */
109 int mode = MODE_LIVE_NAV;
110
111 /**
72 * The title used for the overall generated output. Set this to change it. 112 * The title used for the overall generated output. Set this to change it.
73 */ 113 */
74 String mainTitle = 'Dart Documentation'; 114 String mainTitle = 'Dart Documentation';
75 115
76 /** 116 /**
77 * The URL that the Dart logo links to. Defaults "index.html", the main 117 * The URL that the Dart logo links to. Defaults "index.html", the main
78 * page for the generated docs, but can be anything. 118 * page for the generated docs, but can be anything.
79 */ 119 */
80 String mainUrl = 'index.html'; 120 String mainUrl = 'index.html';
81 121
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 break; 181 break;
142 182
143 default: 183 default:
144 // Normal entrypoint script. 184 // Normal entrypoint script.
145 world.processDartScript(entrypoint); 185 world.processDartScript(entrypoint);
146 } 186 }
147 187
148 world.resolveAll(); 188 world.resolveAll();
149 189
150 // Generate the docs. 190 // Generate the docs.
191 if (mode == MODE_LIVE_NAV) docNavigationJson();
192
151 docIndex(); 193 docIndex();
152 for (final library in world.libraries.getValues()) { 194 for (final library in world.libraries.getValues()) {
153 docLibrary(library); 195 docLibrary(library);
154 } 196 }
155 } finally { 197 } finally {
156 options.dietParse = oldDietParse; 198 options.dietParse = oldDietParse;
157 } 199 }
158 } 200 }
159 201
160 /** 202 /**
161 * Writes the page header with the given [title] and [breadcrumbs]. The 203 * Writes the page header with the given [title] and [breadcrumbs]. The
162 * breadcrumbs are an interleaved list of links and titles. If a link is null, 204 * breadcrumbs are an interleaved list of links and titles. If a link is null,
163 * then no link will be generated. For example, given: 205 * then no link will be generated. For example, given:
164 * 206 *
165 * ['foo', 'foo.html', 'bar', null] 207 * ['foo', 'foo.html', 'bar', null]
166 * 208 *
167 * It will output: 209 * It will output:
168 * 210 *
169 * <a href="foo.html">foo</a> &rsaquo; bar 211 * <a href="foo.html">foo</a> &rsaquo; bar
170 */ 212 */
171 writeHeader(String title, List<String> breadcrumbs) { 213 writeHeader(String title, List<String> breadcrumbs) {
172 write( 214 write(
173 ''' 215 '''
174 <!DOCTYPE html> 216 <!DOCTYPE html>
175 <html> 217 <html>
176 <head> 218 <head>
177 '''); 219 ''');
178 writeHeadContents(title); 220 writeHeadContents(title);
221
222 // Add data attributes describing what the page documents.
223 var data = '';
224 if (_currentLibrary != null) {
225 data += ' data-library="${md.escapeHtml(_currentLibrary.name)}"';
226 }
227
228 if (_currentType != null) {
229 data += ' data-type="${md.escapeHtml(typeName(_currentType))}"';
230 }
231
179 write( 232 write(
180 ''' 233 '''
181 </head> 234 </head>
182 <body> 235 <body$data>
183 <div class="page"> 236 <div class="page">
184 <div class="header"> 237 <div class="header">
185 ${a(mainUrl, '<div class="logo"></div>')} 238 ${a(mainUrl, '<div class="logo"></div>')}
186 ${a('index.html', mainTitle)} 239 ${a('index.html', mainTitle)}
187 '''); 240 ''');
188 241
189 // Write the breadcrumb trail. 242 // Write the breadcrumb trail.
190 for (int i = 0; i < breadcrumbs.length; i += 2) { 243 for (int i = 0; i < breadcrumbs.length; i += 2) {
191 if (breadcrumbs[i + 1] == null) { 244 if (breadcrumbs[i + 1] == null) {
192 write(' &rsaquo; ${breadcrumbs[i]}'); 245 write(' &rsaquo; ${breadcrumbs[i]}');
193 } else { 246 } else {
194 write(' &rsaquo; ${a(breadcrumbs[i + 1], breadcrumbs[i])}'); 247 write(' &rsaquo; ${a(breadcrumbs[i + 1], breadcrumbs[i])}');
195 } 248 }
196 } 249 }
197 writeln('</div>'); 250 writeln('</div>');
198 251
199 docNavigation(); 252 docNavigation();
200 writeln('<div class="content">'); 253 writeln('<div class="content">');
201 } 254 }
202 255
256 String get clientScript() {
257 switch (mode) {
258 case MODE_STATIC: return 'client-static';
259 case MODE_LIVE_NAV: return 'client-live-nav';
260 default: throw 'Unknown mode $mode.';
261 }
262 }
263
203 writeHeadContents(String title) { 264 writeHeadContents(String title) {
204 writeln( 265 writeln(
205 ''' 266 '''
206 <meta charset="utf-8"> 267 <meta charset="utf-8">
207 <title>$title</title> 268 <title>$title</title>
208 <link rel="stylesheet" type="text/css" 269 <link rel="stylesheet" type="text/css"
209 href="${relativePath('styles.css')}" /> 270 href="${relativePath('styles.css')}" />
210 <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700 ,800" rel="stylesheet" type="text/css"> 271 <link href="http://fonts.googleapis.com/css?family=Open+Sans:400,600,700 ,800" rel="stylesheet" type="text/css">
211 <link rel="shortcut icon" href="${relativePath('favicon.ico')}" /> 272 <link rel="shortcut icon" href="${relativePath('favicon.ico')}" />
212 <script src="${relativePath('interact.js')}"></script> 273 <script src="${relativePath('$clientScript.js')}"></script>
213 '''); 274 ''');
214 } 275 }
215 276
216 writeFooter() { 277 writeFooter() {
217 writeln( 278 writeln(
218 ''' 279 '''
219 </div> 280 </div>
220 <div class="clear"></div> 281 <div class="clear"></div>
221 </div> 282 </div>
222 <div class="footer">$footerText</div> 283 <div class="footer">$footerText</div>
(...skipping 13 matching lines...) Expand all
236 writeln( 297 writeln(
237 ''' 298 '''
238 <h4>${a(libraryUrl(library), library.name)}</h4> 299 <h4>${a(libraryUrl(library), library.name)}</h4>
239 '''); 300 ''');
240 } 301 }
241 302
242 writeFooter(); 303 writeFooter();
243 endFile(); 304 endFile();
244 } 305 }
245 306
307 /**
308 * Walks the libraries and creates a JSON object containing the data needed
309 * to generate navigation for them.
310 */
311 docNavigationJson() {
312 startFile('nav.json');
313
314 final libraries = {};
315
316 for (final library in orderByName(world.libraries)) {
317 final types = [];
318
319 for (final type in orderByName(library.types)) {
320 if (type.isTop) continue;
321 if (type.name.startsWith('_')) continue;
322
323 final kind = type.isClass ? 'class' : 'interface';
324 final url = typeUrl(type);
325 types.add({ 'name': typeName(type), 'kind': kind, 'url': url });
326 }
327
328 libraries[library.name] = types;
329 }
330
331 writeln(JSON.stringify(libraries));
332 endFile();
333 }
334
246 docNavigation() { 335 docNavigation() {
247 writeln( 336 writeln(
248 ''' 337 '''
249 <div class="nav"> 338 <div class="nav">
250 '''); 339 ''');
251 340
252 for (final library in orderByName(world.libraries)) { 341 if (mode == MODE_STATIC) {
253 write('<h2><div class="icon-library"></div>'); 342 for (final library in orderByName(world.libraries)) {
343 write('<h2><div class="icon-library"></div>');
254 344
255 if ((_currentLibrary == library) && (_currentType == null)) { 345 if ((_currentLibrary == library) && (_currentType == null)) {
256 write('<strong>${library.name}</strong>'); 346 write('<strong>${library.name}</strong>');
257 } else { 347 } else {
258 write('${a(libraryUrl(library), library.name)}'); 348 write('${a(libraryUrl(library), library.name)}');
349 }
350 write('</h2>');
351
352 // Only expand classes in navigation for current library.
353 if (_currentLibrary == library) docLibraryNavigation(library);
259 } 354 }
260 write('</h2>');
261
262 // Only expand classes in navigation for current library.
263 if (_currentLibrary == library) docLibraryNavigation(library);
264 } 355 }
265 356
266 writeln('</div>'); 357 writeln('</div>');
267 } 358 }
268 359
269 /** Writes the navigation for the types contained by the given library. */ 360 /** Writes the navigation for the types contained by the given library. */
270 docLibraryNavigation(Library library) { 361 docLibraryNavigation(Library library) {
271 // Show the exception types separately. 362 // Show the exception types separately.
272 final types = <Type>[]; 363 final types = <Type>[];
273 final exceptions = <Type>[]; 364 final exceptions = <Type>[];
(...skipping 599 matching lines...) Expand 10 before | Expand all | Expand 10 after
873 964
874 return new md.Element.text('code', name); 965 return new md.Element.text('code', name);
875 } 966 }
876 967
877 // TODO(rnystrom): Move into SourceSpan? 968 // TODO(rnystrom): Move into SourceSpan?
878 int getSpanColumn(SourceSpan span) { 969 int getSpanColumn(SourceSpan span) {
879 final line = span.file.getLine(span.start); 970 final line = span.file.getLine(span.start);
880 return span.file.getColumn(line, span.start); 971 return span.file.getColumn(line, span.start);
881 } 972 }
882 } 973 }
OLDNEW
« no previous file with comments | « utils/dartdoc/dartdoc ('k') | utils/dartdoc/interact.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698