Chromium Code Reviews| 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 |