OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 * Library for extracting the documentation comments from files generated by | 6 * Library for extracting the documentation comments from files generated by |
7 * the HTML library. The comments are stored in a JSON file. | 7 * the HTML library. The comments are stored in a JSON file. |
8 * | 8 * |
9 * Comments must be in either the block style with leading *s: | 9 * Comments must be in either the block style with leading *s: |
10 * | 10 * |
(...skipping 30 matching lines...) Expand all Loading... | |
41 // TODO(amouravski): make this transform once I know what I want this file to | 41 // TODO(amouravski): make this transform once I know what I want this file to |
42 // return. | 42 // return. |
43 _convertFiles(htmlPath).then((convertedJson) { | 43 _convertFiles(htmlPath).then((convertedJson) { |
44 final jsonFile = new File.fromPath(jsonPath); | 44 final jsonFile = new File.fromPath(jsonPath); |
45 var writeJson = convertedJson; | 45 var writeJson = convertedJson; |
46 | 46 |
47 if (jsonFile.existsSync()) { | 47 if (jsonFile.existsSync()) { |
48 writeJson = _mergeJsonAndFile(convertedJson, jsonFile); | 48 writeJson = _mergeJsonAndFile(convertedJson, jsonFile); |
49 } | 49 } |
50 | 50 |
51 var outputStream = jsonFile.openOutputStream(); | 51 jsonFile.writeAsStringSync(prettyPrintJson(writeJson)); |
52 outputStream.writeString(prettyPrintJson(writeJson)); | 52 completer.complete(_anyErrors); |
53 | |
54 outputStream.onNoPendingWrites = () { | |
55 completer.complete(_anyErrors); | |
56 }; | |
57 | |
58 outputStream.onClosed = () { | |
59 completer.complete(_anyErrors); | |
60 }; | |
61 | |
62 outputStream.onError = completer.completeException; | |
63 }); | 53 }); |
64 | 54 |
65 return completer.future; | 55 return completer.future; |
66 } | 56 } |
67 | 57 |
68 | 58 |
69 /** | 59 /** |
70 * Convert all files on [htmlPath]. | 60 * Convert all files on [htmlPath]. |
71 * | 61 * |
72 * Returns a future that completes to the converted JSON object. | 62 * Returns a future that completes to the converted JSON object. |
73 */ | 63 */ |
74 Future<Object> _convertFiles(Path htmlPath) { | 64 Future<Object> _convertFiles(Path htmlPath) { |
75 var completer = new Completer(); | 65 //List<Future> fileFutures = []; |
Emily Fortuna
2012/11/28 17:17:30
probably just delete this line if it's going to be
| |
76 | 66 var group = new FutureGroup(); |
77 List<Future> fileFutures = []; | |
78 | 67 |
79 // Get a list of all HTML dart files. | 68 // Get a list of all HTML dart files. |
80 // TODO(amouravski): discriminate .dart files. | 69 // TODO(amouravski): discriminate .dart files. |
81 final htmlDir = new Directory.fromPath(htmlPath); | 70 final htmlDir = new Directory.fromPath(htmlPath); |
82 final lister = htmlDir.list(recursive: false); | 71 final lister = htmlDir.list(recursive: false); |
83 | 72 |
73 var listCompleter = new Completer(); | |
74 group.add(listCompleter.future); | |
75 | |
84 lister.onFile = (String path) { | 76 lister.onFile = (String path) { |
85 final name = new Path.fromNative(path).filename; | 77 final name = new Path.fromNative(path).filename; |
86 | 78 |
87 // Ignore private classes. | 79 // Ignore private classes. |
88 if (name.startsWith('_')) return; | 80 if (name.startsWith('_')) return; |
89 | 81 |
90 // Ignore non-dart files. | 82 // Ignore non-dart files. |
91 if (!name.endsWith('.dart')) return; | 83 if (!name.endsWith('.dart')) return; |
92 | 84 |
93 File file = new File(path); | 85 group.add(_convertFile(path)); |
94 | 86 //fileFutures.add(_convertFile(file)); |
Emily Fortuna
2012/11/28 17:17:30
ditto here.
| |
95 // TODO(amouravski): Handle missing file. | |
96 if (!file.existsSync()) { | |
97 print('ERROR: cannot find file $path'); | |
98 _anyErrors = true; | |
99 return; | |
100 } | |
101 | |
102 fileFutures.add(_convertFile(file)); | |
103 }; | 87 }; |
104 | 88 |
105 | 89 |
106 // Combine all JSON objects | 90 // Combine all JSON objects |
107 lister.onDone = (_) { | 91 lister.onDone = (done) { |
108 Futures.wait(fileFutures).then((jsonList) { | 92 if (done) listCompleter.complete(null); |
109 var convertedJson = {}; | 93 // Futures.wait(fileFutures).then((jsonList) { |
Emily Fortuna
2012/11/28 17:17:30
and here
| |
110 jsonList.forEach((json) { | 94 // var convertedJson = {}; |
111 final k = json.keys[0]; | 95 // jsonList.forEach((json) { |
112 convertedJson.putIfAbsent(k, () => json[k]); | 96 // final k = json.keys[0]; |
113 }); | 97 // convertedJson.putIfAbsent(k, () => json[k]); |
114 completer.complete(convertedJson); | 98 // }); |
115 }); | 99 // completer.complete(convertedJson); |
100 // }); | |
116 }; | 101 }; |
117 | 102 |
118 // TODO(amouravski): add more error handling. | 103 // TODO(amouravski): add more error handling. |
119 | 104 |
120 return completer.future; | 105 return group.future.transform((values) { |
106 var convertedJson = {}; | |
107 values.forEach((json) { | |
108 final k = json.keys[0]; | |
109 convertedJson.putIfAbsent(k, () => json[k]); | |
110 }); | |
111 | |
112 return convertedJson; | |
113 }); | |
114 | |
115 //return completer.future; | |
Emily Fortuna
2012/11/28 17:17:30
same
| |
121 } | 116 } |
122 | 117 |
123 | 118 |
124 /** | 119 /** |
125 * Convert a single file to JSON docs. | 120 * Convert a single file to JSON docs. |
126 * | 121 * |
127 * Returns a map with one entry whose key is the file name and whose value is | 122 * Returns a map with one entry whose key is the file name and whose value is |
128 * the list of comment lines. | 123 * the list of comment lines. |
129 */ | 124 */ |
130 Future<Map> _convertFile(File file) { | 125 Future<Map> _convertFile(String path) { |
126 File file = new File(path); | |
127 | |
128 // TODO(amouravski): Handle missing file. | |
129 if (!file.existsSync()) { | |
130 print('ERROR: cannot find file $path'); | |
131 _anyErrors = true; | |
132 return; | |
133 } | |
134 | |
131 var completer = new Completer(); | 135 var completer = new Completer(); |
132 | 136 |
133 var comments = {}; | 137 var comments = {}; |
134 | 138 |
135 // Find all /// @docsEditable annotations. | 139 file.readAsLines().then((lines) { |
136 InputStream file_stream = file.openInputStream(); | |
137 StringInputStream inputLines = new StringInputStream(file_stream); | |
138 | |
139 // TODO(amouravski): Re-write as file.readAsLine().thin((lines) {...} | |
140 inputLines.onLine = () { | |
141 var comment = <String>[]; | 140 var comment = <String>[]; |
142 | 141 |
143 var docCommentFound = false; | 142 var docCommentFound = false; |
144 String line; | 143 String line; |
145 while ((line = inputLines.readLine()) != null) { | 144 while (!lines.isEmpty && (line = lines.removeAt(0)) != null) { |
146 var trimmedLine = line.trim(); | 145 var trimmedLine = line.trim(); |
147 | 146 |
148 // Sentinel found. Process the comment block. | 147 // Sentinel found. Process the comment block. |
149 if (trimmedLine.startsWith('///') && | 148 if (trimmedLine.startsWith('///') && |
150 trimmedLine.contains('@docsEditable')) { | 149 trimmedLine.contains('@docsEditable')) { |
151 if (docCommentFound == true) { | 150 if (docCommentFound == true) { |
152 var nextLine = inputLines.readLine(); | 151 var nextLine = lines.removeAt(0); |
153 | 152 |
154 if (nextLine == null) return false; | 153 if (nextLine == null) return false; |
155 | 154 |
156 var lineObject = {}; | 155 var lineObject = {}; |
157 | 156 |
158 if (comments[nextLine] != null) { | 157 if (comments[nextLine] != null) { |
159 print('WARNING: duplicate line ${nextLine} found in' | 158 print('WARNING: duplicate line ${nextLine} found in' |
160 '${new Path(file.fullPathSync()).filename}'); | 159 '${new Path(file.fullPathSync()).filename}'); |
161 } | 160 } |
162 comments.putIfAbsent(nextLine, () => comment); | 161 comments.putIfAbsent(nextLine, () => comment); |
(...skipping 12 matching lines...) Expand all Loading... | |
175 // /// blah | 174 // /// blah |
176 // * | 175 // * |
177 (trimmedLine.startsWith('*') || trimmedLine.startsWith('///'))) { | 176 (trimmedLine.startsWith('*') || trimmedLine.startsWith('///'))) { |
178 comment.add(line); | 177 comment.add(line); |
179 } else { | 178 } else { |
180 // Reset if we're not in a comment. | 179 // Reset if we're not in a comment. |
181 docCommentFound = false; | 180 docCommentFound = false; |
182 comment = <String>[]; | 181 comment = <String>[]; |
183 } | 182 } |
184 } | 183 } |
185 }; | |
186 | 184 |
187 inputLines.onClosed = () { | |
188 var jsonObject = {}; | 185 var jsonObject = {}; |
189 jsonObject[new Path(file.fullPathSync()).filename] = comments; | 186 jsonObject[new Path(file.fullPathSync()).filename] = comments; |
190 completer.complete(jsonObject); | 187 completer.complete(jsonObject); |
191 }; | 188 }); |
192 | 189 |
193 // TODO(amouravski): better error handling. | 190 // TODO(amouravski): better error handling. |
194 | 191 |
195 return completer.future; | 192 return completer.future; |
196 } | 193 } |
197 | 194 |
198 | 195 |
199 /** | 196 /** |
200 * Merge the new JSON object and the existing file. | 197 * Merge the new JSON object and the existing file. |
201 */ | 198 */ |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
304 '$indentation${JSON.stringify(key)}:\n' | 301 '$indentation${JSON.stringify(key)}:\n' |
305 '${prettyPrintJson(json[key], '$indentation ')}'); | 302 '${prettyPrintJson(json[key], '$indentation ')}'); |
306 var recursiveOutput = Strings.join(mapList, ',\n'); | 303 var recursiveOutput = Strings.join(mapList, ',\n'); |
307 output = '$indentation{\n' | 304 output = '$indentation{\n' |
308 '$recursiveOutput' | 305 '$recursiveOutput' |
309 '\n$indentation}'; | 306 '\n$indentation}'; |
310 } else { | 307 } else { |
311 output = '$indentation${JSON.stringify(json)}'; | 308 output = '$indentation${JSON.stringify(json)}'; |
312 } | 309 } |
313 return output; | 310 return output; |
314 } | 311 } |
312 /** A completer that waits until all added [Future]s complete. */ | |
Emily Fortuna
2012/11/28 17:17:30
add a blank newline before this line
| |
313 class FutureGroup { | |
314 const _FINISHED = -1; | |
315 int _pending = 0; | |
316 Completer<List> _completer = new Completer<List>(); | |
317 final List<Future> futures = <Future>[]; | |
318 final List output = []; | |
319 | |
320 /** | |
321 * Wait for [task] to complete (assuming this barrier has not already been | |
322 * marked as completed, otherwise you'll get an exception indicating | |
323 * that a | |
324 * future has already been completed). | |
325 */ | |
326 void add(Future task) { | |
327 if (_pending == _FINISHED) { | |
328 throw new FutureAlreadyCompleteException(); | |
329 } | |
330 _pending++; | |
331 futures.add(task); | |
332 task.handleException( | |
333 (e) => _completer.completeException(e, task.stackTrace)); | |
334 task.then((value) { | |
335 if (value != null) output.add(value); | |
336 _pending--; | |
337 if (_pending == 0) { | |
338 _pending = _FINISHED; | |
339 _completer.complete(output); | |
340 } | |
341 }); | |
342 } | |
343 | |
344 Future<List> get future => _completer.future; | |
345 } | |
OLD | NEW |