| OLD | NEW |
| 1 #library("postProcess"); | 1 #library("postProcess"); |
| 2 | 2 |
| 3 #import("../../../frog/lib/node/node.dart"); | 3 #import("../../../frog/lib/node/node.dart"); |
| 4 #import("dart:json"); | 4 #import("dart:json"); |
| 5 #import("util.dart"); |
| 5 | 6 |
| 6 // TODO(jacobr): this file conflates pretty printing of the JSON database with | 7 void main() { |
| 7 // filtering the database to select the best matches per file. | 8 // Database of code documentation. |
| 8 // Separate out the two tasks as quick and dirty coding is correct for pretty | 9 Map<String, List> database = JSON.parse( |
| 9 // printing but more carefully documented code is required for the code | 10 fs.readFileSync('output/database.json', 'utf8')); |
| 10 // filtering the database. | 11 final filteredDb = {}; |
| 11 Map<String, List> database; | 12 final obsolete = []; |
| 12 Map<String, Map> allProps; | 13 for (String type in database.getKeys()) { |
| 13 Set<String> matchedTypes; | 14 final entry = pickBestEntry(database[type], type); |
| 14 | 15 filteredDb[type] = entry; |
| 15 String orEmpty(String str) { | 16 if (entry.containsKey("members")) { |
| 16 return str == null ? "" : str; | 17 Map members = getMembersMap(entry); |
| 17 } | 18 for (String name in members.getKeys()) { |
| 18 | 19 Map memberData = members[name]; |
| 19 /** Returns whether the type has any member matching the specified name. */ | 20 if (memberData['obsolete'] == true) { |
| 20 bool hasAny(String type, String prop) { | 21 obsolete.add({'type': type, 'member' : name}); |
| 21 final data = allProps[type]; | |
| 22 return data['properties'].containsKey(prop) || | |
| 23 data['methods'].containsKey(prop) || | |
| 24 data['constants'].containsKey(prop); | |
| 25 } | |
| 26 | |
| 27 List<String> sortStringCollection(Collection<String> collection) { | |
| 28 final out = <String>[]; | |
| 29 out.addAll(collection); | |
| 30 out.sort((String a, String b) => a.compareTo(b)); | |
| 31 return out; | |
| 32 } | |
| 33 | |
| 34 /** | |
| 35 * Return the members from an [entry] as Map of member names to member | |
| 36 * objects. | |
| 37 */ | |
| 38 Map getMembersMap(Map entry) { | |
| 39 List<Map> rawMembers = entry["members"]; | |
| 40 final members = {}; | |
| 41 for (final entry in rawMembers) { | |
| 42 members[entry['name']] = entry; | |
| 43 } | |
| 44 return members; | |
| 45 } | |
| 46 | |
| 47 int addMissing(StringBuffer sb, String type, Map members) { | |
| 48 int total = 0; | |
| 49 /** | |
| 50 * Add all missing members to the string output and return the number of | |
| 51 * missing members. | |
| 52 */ | |
| 53 void addMissingHelper(String propType) { | |
| 54 Map expected = allProps[type][propType]; | |
| 55 if (expected != null) { | |
| 56 for(final name in sortStringCollection(expected.getKeys())) { | |
| 57 if (!members.containsKey(name)) { | |
| 58 total++; | |
| 59 sb.add(""" | |
| 60 <tr class="missing"> | |
| 61 <td>$name</td> | |
| 62 <td></td> | |
| 63 <td>Could not find documentation for $propType</td> | |
| 64 </tr> | |
| 65 """); | |
| 66 } | 22 } |
| 67 } | 23 } |
| 68 } | 24 } |
| 69 } | 25 } |
| 70 | |
| 71 addMissingHelper('properties'); | |
| 72 addMissingHelper('methods'); | |
| 73 addMissingHelper('constants'); | |
| 74 return total; | |
| 75 } | |
| 76 | |
| 77 /** | |
| 78 * Score entries using similarity heuristics calculated from the observed and | |
| 79 * expected list of members. We could be much less naive and penalize spurious | |
| 80 * methods, prefer entries with class level comments, etc. This method is | |
| 81 * needed becase we extract entries for each of the top search results for | |
| 82 * each class name and rely on these scores to determine which entry was | |
| 83 * best. Typically all scores but one will be zero. Multiple pages have | |
| 84 * non-zero scores when MDN has multiple pages on the same class or pages on | |
| 85 * similar classes (e.g. HTMLElement and Element), or pages on Mozilla | |
| 86 * specific classes that are similar to DOM classes (Console). | |
| 87 */ | |
| 88 num scoreEntry(Map entry, String type) { | |
| 89 num score = 0; | |
| 90 // TODO(jacobr): consider removing skipped entries completely instead of | |
| 91 // just giving them lower scores. | |
| 92 if (!entry.containsKey('skipped')) { | |
| 93 score++; | |
| 94 } | |
| 95 if (entry.containsKey("members")) { | |
| 96 Map members = getMembersMap(entry); | |
| 97 for (String name in members.getKeys()) { | |
| 98 if (hasAny(type, name)) { | |
| 99 score++; | |
| 100 } | |
| 101 } | |
| 102 } | |
| 103 return score; | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * Given a list of candidates for the documentation for a type, find the one | |
| 108 * that is the best. | |
| 109 */ | |
| 110 Map pickBestEntry(List entries, String type) { | |
| 111 num bestScore = -1; | |
| 112 Map bestEntry; | |
| 113 for (Map entry in entries) { | |
| 114 if (entry != null) { | |
| 115 num score = scoreEntry(entry, type); | |
| 116 if (score > bestScore) { | |
| 117 bestScore = score; | |
| 118 bestEntry = entry; | |
| 119 } | |
| 120 } | |
| 121 } | |
| 122 return bestEntry; | |
| 123 } | |
| 124 | |
| 125 void main() { | |
| 126 // Database of code documentation. | |
| 127 database = JSON.parse(fs.readFileSync('output/database.json', 'utf8')); | |
| 128 // Database of expected property names for each type in WebKit. | |
| 129 allProps = JSON.parse(fs.readFileSync('data/dartIdl.json', 'utf8')); | |
| 130 // Types we have documentation for. | |
| 131 matchedTypes = new Set<String>(); | |
| 132 int numMissingMethods = 0; | |
| 133 int numFoundMethods = 0; | |
| 134 int numExtraMethods = 0; | |
| 135 int numGen = 0; | |
| 136 int numSkipped = 0; | |
| 137 final sbSkipped = new StringBuffer(); | |
| 138 final sbAllExamples = new StringBuffer(); | |
| 139 final filteredDb = {}; | |
| 140 | |
| 141 // Table rows for all obsolete members. | |
| 142 final sbObsolete = new StringBuffer(); | |
| 143 // Main documentation file. | |
| 144 final sb = new StringBuffer(); | |
| 145 | |
| 146 // TODO(jacobr): switch to using a real template system instead of string | |
| 147 // interpolation combined with StringBuffers. | |
| 148 sb.add(""" | |
| 149 <html> | |
| 150 <head> | |
| 151 <style type="text/css"> | |
| 152 body { | |
| 153 background-color: #eee; | |
| 154 margin: 10px; | |
| 155 font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, | |
| 156 Arial, Helvetica, sans-serif; | |
| 157 } | |
| 158 | |
| 159 .debug { | |
| 160 color: #888; | |
| 161 } | |
| 162 | |
| 163 .compatibility, .links, .see-also, .summary, .members, .example { | |
| 164 border: 1px solid #CCC; | |
| 165 margin: 5px; | |
| 166 padding: 5px; | |
| 167 } | |
| 168 | |
| 169 .type, #dart_summary { | |
| 170 border: 1px solid; | |
| 171 margin-top: 10px; | |
| 172 margin-bottom: 10px; | |
| 173 padding: 10px; | |
| 174 overflow: hidden; | |
| 175 background-color: white; | |
| 176 -moz-box-shadow: 5px 5px 5px #888; | |
| 177 -webkit-box-shadow: 5px 5px 5px #888; | |
| 178 box-shadow: 5px 5px 5px #888; | |
| 179 } | |
| 180 | |
| 181 #dart_summary { | |
| 182 border: 2px solid #00F; | |
| 183 margin: 5px; | |
| 184 padding: 5px; | |
| 185 } | |
| 186 | |
| 187 th { | |
| 188 background-color:#ccc; | |
| 189 font-weight: bold; | |
| 190 } | |
| 191 | |
| 192 tr:nth-child(odd) { | |
| 193 background-color:#eee; | |
| 194 } | |
| 195 tr:nth-child(even) { | |
| 196 background-color:#fff; | |
| 197 } | |
| 198 | |
| 199 tr:nth-child(odd).unknown { | |
| 200 background-color:#dd0; | |
| 201 } | |
| 202 tr:nth-child(even).unknown { | |
| 203 background-color:#ff0; | |
| 204 } | |
| 205 | |
| 206 tr:nth-child(odd).missing { | |
| 207 background-color:#d88; | |
| 208 } | |
| 209 tr:nth-child(even).missing { | |
| 210 background-color:#faa; | |
| 211 } | |
| 212 | |
| 213 li.unknown { | |
| 214 color: #f00; | |
| 215 } | |
| 216 | |
| 217 td, th { | |
| 218 vertical-align: top; | |
| 219 } | |
| 220 </style> | |
| 221 <title>Doc Dump</title> | |
| 222 </head> | |
| 223 <body> | |
| 224 <h1>Doc Dump</h1> | |
| 225 <ul> | |
| 226 <li><a href="#dart_summary">Summary</a></li> | |
| 227 </li> | |
| 228 """); | |
| 229 | |
| 230 for (String type in sortStringCollection(database.getKeys())) { | |
| 231 Map entry = pickBestEntry(database[type], type); | |
| 232 filteredDb[type] = entry; | |
| 233 if (entry == null || entry.containsKey('skipped')) { | |
| 234 numSkipped++; | |
| 235 sbSkipped.add(""" | |
| 236 <li id="$type"> | |
| 237 <a target="_blank" href="http://www.google.com/cse?cx=01719397256594783026
6%3Awpqsk6dy6ee&ie=UTF-8&q=$type"> | |
| 238 $type | |
| 239 </a> | |
| 240 -- | |
| 241 Title: ${entry == null ? "???" : entry["title"]} -- Issue: | |
| 242 ${entry == null ? "???" : entry['cause']} | |
| 243 -- | |
| 244 <a target="_blank" href="${entry == null ? "???" : entry["srcUrl"]}"> | |
| 245 scraped url | |
| 246 </a> | |
| 247 </li>"""); | |
| 248 continue; | |
| 249 } | |
| 250 matchedTypes.add(type); | |
| 251 numGen++; | |
| 252 StringBuffer sbSections = new StringBuffer(); | |
| 253 StringBuffer sbMembers = new StringBuffer(); | |
| 254 StringBuffer sbExamples = new StringBuffer(); | |
| 255 if (entry.containsKey("members")) { | |
| 256 Map members = getMembersMap(entry); | |
| 257 sbMembers.add(""" | |
| 258 <div class="members"> | |
| 259 <h3><span class="debug">[dart]</span> Members</h3> | |
| 260 <table> | |
| 261 <tbody> | |
| 262 <tr> | |
| 263 <th>Name</th><th>Description</th><th>IDL</th><th>Status</th> | |
| 264 </tr> | |
| 265 """); | |
| 266 for (String name in sortStringCollection(members.getKeys())) { | |
| 267 Map memberData = members[name]; | |
| 268 bool unknown = !hasAny(type, name); | |
| 269 StringBuffer classes = new StringBuffer(); | |
| 270 if (unknown) classes.add("unknown "); | |
| 271 if (unknown) { | |
| 272 numExtraMethods++; | |
| 273 } else { | |
| 274 numFoundMethods++; | |
| 275 } | |
| 276 | |
| 277 final sbMember = new StringBuffer(); | |
| 278 | |
| 279 if (memberData.containsKey('url')) { | |
| 280 sbMember.add(""" | |
| 281 <td><a href="${memberData['url']}">$name</a></td> | |
| 282 """); | |
| 283 } else { | |
| 284 sbMember.add(""" | |
| 285 <td>$name</td> | |
| 286 """); | |
| 287 } | |
| 288 sbMember.add(""" | |
| 289 <td>${memberData['help']}</td> | |
| 290 <td> | |
| 291 <pre>${orEmpty(memberData['idl'])}</pre> | |
| 292 </td> | |
| 293 <td>${memberData['obsolete'] == true ? "Obsolete" : ""}</td> | |
| 294 """); | |
| 295 if (memberData['obsolete'] == true) { | |
| 296 sbObsolete.add("<tr class='$classes'><td>$type</td>$sbMember</tr>"); | |
| 297 } | |
| 298 sbMembers.add("<tr class='$classes'>$sbMember</tr>"); | |
| 299 } | |
| 300 | |
| 301 numMissingMethods += addMissing(sbMembers, type, members); | |
| 302 | |
| 303 sbMembers.add(""" | |
| 304 </tbody> | |
| 305 </table> | |
| 306 </div> | |
| 307 """); | |
| 308 } | |
| 309 for (String sectionName in | |
| 310 ["summary", "constructor", "compatibility", "specification", | |
| 311 "seeAlso"]) { | |
| 312 if (entry.containsKey(sectionName)) { | |
| 313 sbSections.add(""" | |
| 314 <div class="$sectionName"> | |
| 315 <h3><span class="debug">[Dart]</span> $sectionName</h3> | |
| 316 ${entry[sectionName]} | |
| 317 </div> | |
| 318 """); | |
| 319 } | |
| 320 } | |
| 321 if (entry.containsKey("links")) { | |
| 322 sbSections.add(""" | |
| 323 <div class="links"> | |
| 324 <h3><span class="debug">[Dart]</span> Specification</h3> | |
| 325 <ul> | |
| 326 """); | |
| 327 List links = entry["links"]; | |
| 328 for (Map link in links) { | |
| 329 sbSections.add(""" | |
| 330 <li><a href="${link['href']}">${link['title']}</a></li> | |
| 331 """); | |
| 332 } | |
| 333 sbSections.add(""" | |
| 334 </ul> | |
| 335 </div> | |
| 336 """); | |
| 337 } | |
| 338 if (entry.containsKey("examples")) { | |
| 339 for (String example in entry["examples"]) { | |
| 340 sbExamples.add(""" | |
| 341 <div class="example"> | |
| 342 <h3><span class="debug">[Dart]</span> Example</h3> | |
| 343 $example | |
| 344 </div> | |
| 345 """); | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 String title = entry['title']; | |
| 350 if (title != type) { | |
| 351 title = '<h4>Dart type: $type</h4><h2>$title</h2>'; | |
| 352 } else { | |
| 353 title = '<h2>$title</h2>'; | |
| 354 } | |
| 355 sb.add(""" | |
| 356 <div class='type' id="$type"> | |
| 357 <a href='${entry['srcUrl']}'>$title</a> | |
| 358 $sbSections | |
| 359 $sbExamples | |
| 360 $sbMembers | |
| 361 </div> | |
| 362 """); | |
| 363 if (sbExamples.length > 0) { | |
| 364 sbAllExamples.add(""" | |
| 365 <div class='type' id="$type"> | |
| 366 <a href='${entry['srcUrl']}'>$title</a> | |
| 367 $sbExamples | |
| 368 </div> | |
| 369 """); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 for (String type in sortStringCollection(allProps.getKeys())) { | |
| 374 if (!matchedTypes.contains(type) && | |
| 375 !database.containsKey(type)) { | |
| 376 numSkipped++; | |
| 377 sbSkipped.add(""" | |
| 378 <li class="unknown" id="$type"> | |
| 379 <a target="_blank" href="http://www.google.com/cse?cx=01719397256594783026
6%3Awpqsk6dy6ee&ie=UTF-8&q=$type"> | |
| 380 $type | |
| 381 </a> | |
| 382 </li> | |
| 383 """); | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 sb.add(""" | |
| 388 <div id="#dart_summary"> | |
| 389 <h2>Summary</h2> | |
| 390 <h3> | |
| 391 Generated docs for $numGen classes out of a possible | |
| 392 ${allProps.getKeys().length} | |
| 393 </h3> | |
| 394 <h3>Found documentation for $numFoundMethods methods listed in WebKit</h3> | |
| 395 <h3> | |
| 396 Found documentation for $numExtraMethods methods not listed in WebKit | |
| 397 </h3> | |
| 398 <h3> | |
| 399 Unable to find documentation for $numMissingMethods methods present in | |
| 400 WebKit | |
| 401 </h3> | |
| 402 <h3> | |
| 403 Skipped generating documentation for $numSkipped classes due to no | |
| 404 plausible matching files | |
| 405 </h3> | |
| 406 <ul> | |
| 407 $sbSkipped | |
| 408 </ul> | |
| 409 </div> | |
| 410 """); | |
| 411 sb.add(""" | |
| 412 </body> | |
| 413 </html> | |
| 414 """); | |
| 415 | |
| 416 fs.writeFileSync("output/database.html", sb.toString()); | |
| 417 | |
| 418 fs.writeFileSync("output/examples.html", """ | |
| 419 <html> | |
| 420 <head> | |
| 421 <style type="text/css"> | |
| 422 body { | |
| 423 background-color: #eee; | |
| 424 margin: 10px; | |
| 425 font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, Arial, | |
| 426 Helvetica, sans-serif; | |
| 427 } | |
| 428 | |
| 429 .debug { | |
| 430 color: #888; | |
| 431 } | |
| 432 | |
| 433 .example { | |
| 434 border: 1px solid #CCC; | |
| 435 margin: 5px; | |
| 436 padding: 5px; | |
| 437 } | |
| 438 | |
| 439 .type { | |
| 440 border: 1px solid; | |
| 441 margin-top: 10px; | |
| 442 margin-bottom: 10px; | |
| 443 padding: 10px; | |
| 444 overflow: hidden; | |
| 445 background-color: white; | |
| 446 -moz-box-shadow: 5px 5px 5px #888; | |
| 447 -webkit-box-shadow: 5px 5px 5px #888; | |
| 448 box-shadow: 5px 5px 5px #888; | |
| 449 } | |
| 450 </style> | |
| 451 <title>All examples</title> | |
| 452 </head> | |
| 453 <body> | |
| 454 <h1>All examples</h1> | |
| 455 $sbAllExamples | |
| 456 </body> | |
| 457 </html> | |
| 458 """); | |
| 459 | |
| 460 fs.writeFileSync("output/obsolete.html", """ | |
| 461 <html> | |
| 462 <head> | |
| 463 <style type="text/css"> | |
| 464 body { | |
| 465 background-color: #eee; | |
| 466 margin: 10px; | |
| 467 font: 14px/1.428 "Lucida Grande", "Lucida Sans Unicode", Lucida, | |
| 468 Arial, Helvetica, sans-serif; | |
| 469 } | |
| 470 | |
| 471 .debug { | |
| 472 color: #888; | |
| 473 } | |
| 474 | |
| 475 .type { | |
| 476 border: 1px solid; | |
| 477 margin-top: 10px; | |
| 478 margin-bottom: 10px; | |
| 479 padding: 10px; | |
| 480 overflow: hidden; | |
| 481 background-color: white; | |
| 482 -moz-box-shadow: 5px 5px 5px #888; | |
| 483 -webkit-box-shadow: 5px 5px 5px #888; | |
| 484 box-shadow: 5px 5px 5px #888; | |
| 485 } | |
| 486 </style> | |
| 487 <title>Methods marked as obsolete</title> | |
| 488 </head> | |
| 489 <body> | |
| 490 <h1>Methods marked as obsolete</h1> | |
| 491 <table> | |
| 492 <tbody> | |
| 493 <tr> | |
| 494 <th>Type</th> | |
| 495 <th>Name</th> | |
| 496 <th>Description</th> | |
| 497 <th>IDL</th> | |
| 498 <th>Status</th> | |
| 499 </tr> | |
| 500 $sbObsolete | |
| 501 </tbody> | |
| 502 </table> | |
| 503 </body> | |
| 504 </html> | |
| 505 """); | |
| 506 | |
| 507 fs.writeFileSync("output/database.filtered.json", | 26 fs.writeFileSync("output/database.filtered.json", |
| 508 JSON.stringify(filteredDb)); | 27 JSON.stringify(filteredDb)); |
| 28 fs.writeFileSync("output/obsolete.json", JSON.stringify(obsolete)); |
| 509 } | 29 } |
| OLD | NEW |