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

Side by Side Diff: tools/html_json_doc/lib/html_to_json.dart

Issue 12220056: Delete the old html_json docs stuff. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 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 | « tools/html_json_doc/bin/html_json_doc.dart ('k') | tools/html_json_doc/lib/json_to_html.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Library for extracting the documentation comments from files generated by
7 * the HTML library. The comments are stored in a JSON file.
8 *
9 * Comments must be in either the block style with leading *s:
10 *
11 * /**
12 * * Comment here.
13 * */
14 *
15 * Or the triple-slash style:
16 *
17 * /// Docs go here.
18 * /// And here.
19 *
20 * Each member that is to be documented should be preceeded by a meta-comment
21 * containing the string `@docsEditable` such as:
22 *
23 * /// @docsEditable
24 */
25 library html_to_json;
26
27 import 'dart:json';
28 import 'dart:io';
29 import 'dart:async';
30
31
32 /// True if any errors were triggered through the conversion.
33 bool _anyErrors = false;
34
35
36 /**
37 * Convert files on [htmlPath] and write JSON to [jsonPath].
38 */
39 Future<bool> convert(Path htmlPath, Path jsonPath) {
40 var completer = new Completer();
41
42 // TODO(amouravski): make this transform once I know what I want this file to
43 // return.
44 _convertFiles(htmlPath).then((convertedJson) {
45 final jsonFile = new File.fromPath(jsonPath);
46 var writeJson = convertedJson;
47
48 if (jsonFile.existsSync()) {
49 writeJson = _mergeJsonAndFile(convertedJson, jsonFile);
50 }
51
52 var outputStream = jsonFile.openOutputStream();
53 outputStream.writeString(prettyPrintJson(writeJson));
54
55 outputStream.onNoPendingWrites = () {
56 completer.complete(_anyErrors);
57 };
58
59 outputStream.onClosed = () {
60 completer.complete(_anyErrors);
61 };
62
63 outputStream.onError = completer.completeError;
64 });
65
66 return completer.future;
67 }
68
69
70 /**
71 * Convert all files on [htmlPath].
72 *
73 * Returns a future that completes to the converted JSON object.
74 */
75 Future<Object> _convertFiles(Path htmlPath) {
76 var completer = new Completer();
77
78 List<Future> fileFutures = [];
79
80 // Get a list of all HTML dart files.
81 // TODO(amouravski): discriminate .dart files.
82 final htmlDir = new Directory.fromPath(htmlPath);
83 final lister = htmlDir.list(recursive: false);
84
85 lister.onFile = (String path) {
86 final name = new Path(path).filename;
87
88 // Ignore private classes.
89 if (name.startsWith('_')) return;
90
91 // Ignore non-dart files.
92 if (!name.endsWith('.dart')) return;
93
94 File file = new File(path);
95
96 // TODO(amouravski): Handle missing file.
97 if (!file.existsSync()) {
98 print('ERROR: cannot find file $path');
99 _anyErrors = true;
100 return;
101 }
102
103 fileFutures.add(_convertFile(file));
104 };
105
106
107 // Combine all JSON objects
108 lister.onDone = (_) {
109 Futures.wait(fileFutures).then((jsonList) {
110 var convertedJson = {};
111 jsonList.forEach((json) {
112 final k = json.keys[0];
113 convertedJson.putIfAbsent(k, () => json[k]);
114 });
115 completer.complete(convertedJson);
116 });
117 };
118
119 // TODO(amouravski): add more error handling.
120
121 return completer.future;
122 }
123
124
125 /**
126 * Convert a single file to JSON docs.
127 *
128 * Returns a map with one entry whose key is the file name and whose value is
129 * the list of comment lines.
130 */
131 Future<Map> _convertFile(File file) {
132 var completer = new Completer();
133
134 var comments = {};
135
136 // Find all /// @docsEditable annotations.
137 InputStream file_stream = file.openInputStream();
138 StringInputStream inputLines = new StringInputStream(file_stream);
139
140 // TODO(amouravski): Re-write as file.readAsLine().thin((lines) {...}
141 inputLines.onLine = () {
142 var comment = <String>[];
143
144 var docCommentFound = false;
145 String line;
146 while ((line = inputLines.readLine()) != null) {
147 var trimmedLine = line.trim();
148
149 // Sentinel found. Process the comment block.
150 if (trimmedLine.startsWith('///') &&
151 trimmedLine.contains('@docsEditable')) {
152 if (docCommentFound == true) {
153 var nextLine = inputLines.readLine();
154
155 if (nextLine == null) return false;
156
157 var lineObject = {};
158
159 if (comments[nextLine] != null) {
160 print('WARNING: duplicate line ${nextLine} found in'
161 '${new Path(file.fullPathSync()).filename}');
162 }
163 comments.putIfAbsent(nextLine, () => comment);
164 }
165
166 // Reset.
167 docCommentFound = false;
168 comment = <String>[];
169 } else if ( // Start a comment block.
170 trimmedLine.startsWith('/**') ||
171 trimmedLine.startsWith('///')) {
172 docCommentFound = true;
173 comment.add(line);
174 } else if (docCommentFound &&
175 // TODO(amouravski): This will barf on:
176 // /// blah
177 // *
178 (trimmedLine.startsWith('*') || trimmedLine.startsWith('///'))) {
179 comment.add(line);
180 } else {
181 // Reset if we're not in a comment.
182 docCommentFound = false;
183 comment = <String>[];
184 }
185 }
186 };
187
188 inputLines.onClosed = () {
189 var jsonObject = {};
190 jsonObject[new Path(file.fullPathSync()).filename] = comments;
191 completer.complete(jsonObject);
192 };
193
194 // TODO(amouravski): better error handling.
195
196 return completer.future;
197 }
198
199
200 /**
201 * Merge the new JSON object and the existing file.
202 */
203 Object _mergeJsonAndFile(Object json, File file) {
204 var completer = new Completer();
205
206 var fileJson = {};
207 var jsonRead = file.readAsStringSync();
208
209 if (jsonRead == '') {
210 print('WARNING: no data read from '
211 '${new Path(file.fullPathSync()).filename}');
212 _anyErrors = true;
213 } else {
214 fileJson = JSON.parse(jsonRead);
215 }
216 return _mergeJson(json, fileJson);
217 }
218
219
220 /**
221 * Merge two JSON objects, such that the returned JSON object is the
222 * union of both.
223 *
224 * Each JSON must be a map, with each value being a map.
225 */
226 Object _mergeJson(Object json1, Object json2) {
227 if (json1 is Map && json2 is Map) {
228 // Then check if [json2] contains any key form [json1], in which case
229 // add all of the values from [json2] to the values of [json1].
230 json2.forEach((k, v) {
231 if (json1.containsKey(k)) {
232 v.forEach((vk, vv) {
233 if (json1[k].containsKey(vk) &&
234 !_listsEqual(json1[k][vk],vv)) {
235 // Assume that json1 is more current and take its data as opposed
236 // to json2's.
237 // TODO(amouravski): add better warning message and only if there's
238 // a conflict.
239 print('INFO: duplicate keys.');
240 _anyErrors = false;
241 } else {
242 json1[k].putIfAbsent(vk, () => vv);
243 }
244 });
245 } else {
246 json1.putIfAbsent(k, () => v);
247 }
248 });
249 } else {
250 throw new ArgumentError('JSON objects must both be Maps');
251 }
252
253 // TODO(amouravski): more error handling.
254
255 return json1;
256 }
257
258
259 /**
260 * Tests for equality between two lists.
261 *
262 * This checks the first level of depth, so does not work for nested lists.
263 */
264 bool _listsEqual(List list1, List list2) {
265 return list1.every((e) => list2.contains(e)) &&
266 list2.every((e) => list1.contains(e));
267 }
268
269
270 /**
271 * Print JSON in a much nicer format.
272 *
273 * For example:
274 *
275 * {"foo":["bar","baz"],"boo":{"far:"faz"}}
276 *
277 * becomes:
278 *
279 * {
280 * "foo":
281 * [
282 * "bar",
283 * "baz"
284 * ],
285 * "boo":
286 * {
287 * "far":
288 * "faz"
289 * }
290 * }
291 */
292 String prettyPrintJson(Object json, [String indentation = '']) {
293 var output;
294
295 if (json is List) {
296 var recursiveOutput =
297 Strings.join(json.map((e) =>
298 prettyPrintJson(e, '$indentation ')), ',\n');
299 output = '$indentation[\n'
300 '$recursiveOutput'
301 '\n$indentation]';
302 } else if (json is Map) {
303 var keys = json.keys
304 ..sort();
305
306 // TODO(amouravski): No newline after :
307 var mapList = keys.map((key) =>
308 '$indentation${JSON.stringify(key)}:\n'
309 '${prettyPrintJson(json[key], '$indentation ')}');
310 var recursiveOutput = Strings.join(mapList, ',\n');
311 output = '$indentation{\n'
312 '$recursiveOutput'
313 '\n$indentation}';
314 } else {
315 output = '$indentation${JSON.stringify(json)}';
316 }
317 return output;
318 }
OLDNEW
« no previous file with comments | « tools/html_json_doc/bin/html_json_doc.dart ('k') | tools/html_json_doc/lib/json_to_html.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698