| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library analysis_server.src.get_handler; | 5 library analysis_server.src.get_handler; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:convert'; | 9 import 'dart:convert'; |
| 10 import 'dart:io'; | 10 import 'dart:io'; |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 239 AnalysisContext context) { | 239 AnalysisContext context) { |
| 240 for (Folder folder in folderMap.keys) { | 240 for (Folder folder in folderMap.keys) { |
| 241 if (folderMap[folder] == context) { | 241 if (folderMap[folder] == context) { |
| 242 return folder; | 242 return folder; |
| 243 } | 243 } |
| 244 } | 244 } |
| 245 return null; | 245 return null; |
| 246 } | 246 } |
| 247 | 247 |
| 248 /** | 248 /** |
| 249 * Create a link to [path] with query parameters [params], with inner HTML | |
| 250 * [innerHtml]. If [hasError] is `true`, then the link will have the class | |
| 251 * 'error'. | |
| 252 */ | |
| 253 String _makeLink(String path, Map<String, String> params, String innerHtml, | |
| 254 [bool hasError = false]) { | |
| 255 Uri uri = new Uri(path: path, queryParameters: params); | |
| 256 String href = HTML_ESCAPE.convert(uri.toString()); | |
| 257 String classAttribute = hasError ? ' class="error"' : ''; | |
| 258 return '<a href="$href"$classAttribute>$innerHtml</a>'; | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * Return a response containing information about an AST structure. | 249 * Return a response containing information about an AST structure. |
| 263 */ | 250 */ |
| 264 void _returnAst(HttpRequest request) { | 251 void _returnAst(HttpRequest request) { |
| 265 AnalysisServer analysisServer = _server.analysisServer; | 252 AnalysisServer analysisServer = _server.analysisServer; |
| 266 if (analysisServer == null) { | 253 if (analysisServer == null) { |
| 267 return _returnFailure(request, 'Analysis server not running'); | 254 return _returnFailure(request, 'Analysis server not running'); |
| 268 } | 255 } |
| 269 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; | 256 String contextFilter = request.uri.queryParameters[CONTEXT_QUERY_PARAM]; |
| 270 if (contextFilter == null) { | 257 if (contextFilter == null) { |
| 271 return _returnFailure( | 258 return _returnFailure( |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 if (first) { | 361 if (first) { |
| 375 first = false; | 362 first = false; |
| 376 } else { | 363 } else { |
| 377 buffer.write('<br>'); | 364 buffer.write('<br>'); |
| 378 } | 365 } |
| 379 AnalysisContextImpl analyzingContext = | 366 AnalysisContextImpl analyzingContext = |
| 380 analysisServer.folderMap[folder]; | 367 analysisServer.folderMap[folder]; |
| 381 if (analyzingContext == context) { | 368 if (analyzingContext == context) { |
| 382 buffer.write(folder.path); | 369 buffer.write(folder.path); |
| 383 } else { | 370 } else { |
| 384 buffer.write(_makeLink(CACHE_ENTRY_PATH, { | 371 buffer.write(makeLink(CACHE_ENTRY_PATH, { |
| 385 CONTEXT_QUERY_PARAM: folder.path, | 372 CONTEXT_QUERY_PARAM: folder.path, |
| 386 SOURCE_QUERY_PARAM: sourceUri | 373 SOURCE_QUERY_PARAM: sourceUri |
| 387 }, HTML_ESCAPE.convert(folder.path))); | 374 }, HTML_ESCAPE.convert(folder.path))); |
| 388 } | 375 } |
| 389 if (entryMap[folder].explicitlyAdded) { | 376 if (entryMap[folder].explicitlyAdded) { |
| 390 buffer.write(' (explicit)'); | 377 buffer.write(' (explicit)'); |
| 391 } else { | 378 } else { |
| 392 buffer.write(' (implicit)'); | 379 buffer.write(' (implicit)'); |
| 393 } | 380 } |
| 394 }); | 381 }); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 478 | 465 |
| 479 Folder folder = _findFolder(analysisServer, contextFilter); | 466 Folder folder = _findFolder(analysisServer, contextFilter); |
| 480 AnalysisContextImpl context = analysisServer.folderMap[folder]; | 467 AnalysisContextImpl context = analysisServer.folderMap[folder]; |
| 481 List<String> links = <String>[]; | 468 List<String> links = <String>[]; |
| 482 context.visitCacheItems( | 469 context.visitCacheItems( |
| 483 (Source source, SourceEntry dartEntry, DataDescriptor rowDesc, CacheStat
e state) | 470 (Source source, SourceEntry dartEntry, DataDescriptor rowDesc, CacheStat
e state) |
| 484 { | 471 { |
| 485 if (state != stateFilter || rowDesc.toString() != descriptorFilter) { | 472 if (state != stateFilter || rowDesc.toString() != descriptorFilter) { |
| 486 return; | 473 return; |
| 487 } | 474 } |
| 488 String link = _makeLink(CACHE_ENTRY_PATH, { | 475 String link = makeLink(CACHE_ENTRY_PATH, { |
| 489 CONTEXT_QUERY_PARAM: folder.path, | 476 CONTEXT_QUERY_PARAM: folder.path, |
| 490 SOURCE_QUERY_PARAM: source.uri.toString() | 477 SOURCE_QUERY_PARAM: source.uri.toString() |
| 491 }, HTML_ESCAPE.convert(source.fullName)); | 478 }, HTML_ESCAPE.convert(source.fullName)); |
| 492 links.add(link); | 479 links.add(link); |
| 493 }); | 480 }); |
| 494 | 481 |
| 495 _writeResponse(request, (StringBuffer buffer) { | 482 _writeResponse(request, (StringBuffer buffer) { |
| 496 _writePage( | 483 _writePage( |
| 497 buffer, | 484 buffer, |
| 498 'Analysis Server - Cache Search', | 485 'Analysis Server - Cache Search', |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 context.prioritySources.map((Source source) => source.fullName).toList()
; | 547 context.prioritySources.map((Source source) => source.fullName).toList()
; |
| 561 context.visitCacheItems( | 548 context.visitCacheItems( |
| 562 (Source source, SourceEntry sourceEntry, DataDescriptor rowDesc, | 549 (Source source, SourceEntry sourceEntry, DataDescriptor rowDesc, |
| 563 CacheState state) { | 550 CacheState state) { |
| 564 String sourceName = source.fullName; | 551 String sourceName = source.fullName; |
| 565 if (!links.containsKey(sourceName)) { | 552 if (!links.containsKey(sourceName)) { |
| 566 CaughtException exception = sourceEntry.exception; | 553 CaughtException exception = sourceEntry.exception; |
| 567 if (exception != null) { | 554 if (exception != null) { |
| 568 exceptions.add(exception); | 555 exceptions.add(exception); |
| 569 } | 556 } |
| 570 String link = _makeLink(CACHE_ENTRY_PATH, { | 557 String link = makeLink(CACHE_ENTRY_PATH, { |
| 571 CONTEXT_QUERY_PARAM: folder.path, | 558 CONTEXT_QUERY_PARAM: folder.path, |
| 572 SOURCE_QUERY_PARAM: source.uri.toString() | 559 SOURCE_QUERY_PARAM: source.uri.toString() |
| 573 }, sourceName, exception != null); | 560 }, sourceName, exception != null); |
| 574 if (sourceEntry.explicitlyAdded) { | 561 if (sourceEntry.explicitlyAdded) { |
| 575 explicitNames.add(sourceName); | 562 explicitNames.add(sourceName); |
| 576 } else { | 563 } else { |
| 577 implicitNames.add(sourceName); | 564 implicitNames.add(sourceName); |
| 578 } | 565 } |
| 579 links[sourceName] = link; | 566 links[sourceName] = link; |
| 580 } | 567 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 594 buffer.write('<h3>$title</h3>'); | 581 buffer.write('<h3>$title</h3>'); |
| 595 if (fileNames == null || fileNames.isEmpty) { | 582 if (fileNames == null || fileNames.isEmpty) { |
| 596 buffer.write('<p>None</p>'); | 583 buffer.write('<p>None</p>'); |
| 597 } else { | 584 } else { |
| 598 buffer.write('<table style="width: 100%">'); | 585 buffer.write('<table style="width: 100%">'); |
| 599 for (String fileName in fileNames) { | 586 for (String fileName in fileNames) { |
| 600 buffer.write('<tr><td>'); | 587 buffer.write('<tr><td>'); |
| 601 buffer.write(links[fileName]); | 588 buffer.write(links[fileName]); |
| 602 buffer.write('</td><td>'); | 589 buffer.write('</td><td>'); |
| 603 if (overlayMap.containsKey(fileName)) { | 590 if (overlayMap.containsKey(fileName)) { |
| 604 buffer.write(_makeLink(OVERLAY_PATH, { | 591 buffer.write(makeLink(OVERLAY_PATH, { |
| 605 ID_PARAM: overlayMap[fileName].toString() | 592 ID_PARAM: overlayMap[fileName].toString() |
| 606 }, 'overlay')); | 593 }, 'overlay')); |
| 607 } | 594 } |
| 608 buffer.write('</td></tr>'); | 595 buffer.write('</td></tr>'); |
| 609 } | 596 } |
| 610 buffer.write('</table>'); | 597 buffer.write('</table>'); |
| 611 } | 598 } |
| 612 } | 599 } |
| 613 | 600 |
| 614 _writeResponse(request, (StringBuffer buffer) { | 601 _writeResponse(request, (StringBuffer buffer) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 625 AnalysisContextStatistics statistics = context.statistics; | 612 AnalysisContextStatistics statistics = context.statistics; |
| 626 statistics.cacheRows.forEach((AnalysisContextStatistics_CacheRow row) { | 613 statistics.cacheRows.forEach((AnalysisContextStatistics_CacheRow row) { |
| 627 List rowText = [row.name]; | 614 List rowText = [row.name]; |
| 628 for (CacheState state in CacheState.values) { | 615 for (CacheState state in CacheState.values) { |
| 629 String text = row.getCount(state).toString(); | 616 String text = row.getCount(state).toString(); |
| 630 Map<String, String> params = <String, String>{ | 617 Map<String, String> params = <String, String>{ |
| 631 STATE_QUERY_PARAM: state.toString(), | 618 STATE_QUERY_PARAM: state.toString(), |
| 632 CONTEXT_QUERY_PARAM: folder.path, | 619 CONTEXT_QUERY_PARAM: folder.path, |
| 633 DESCRIPTOR_QUERY_PARAM: row.name | 620 DESCRIPTOR_QUERY_PARAM: row.name |
| 634 }; | 621 }; |
| 635 rowText.add(_makeLink(CACHE_STATE_PATH, params, text)); | 622 rowText.add(makeLink(CACHE_STATE_PATH, params, text)); |
| 636 } | 623 } |
| 637 _writeRow(buffer, rowText, classes: [null, "right"]); | 624 _writeRow(buffer, rowText, classes: [null, "right"]); |
| 638 }); | 625 }); |
| 639 buffer.write('</table>'); | 626 buffer.write('</table>'); |
| 640 | 627 |
| 641 _writeFiles(buffer, 'Priority Files', priorityNames); | 628 _writeFiles(buffer, 'Priority Files', priorityNames); |
| 642 _writeFiles(buffer, 'Explicitly Analyzed Files', explicitNames); | 629 _writeFiles(buffer, 'Explicitly Analyzed Files', explicitNames); |
| 643 _writeFiles(buffer, 'Implicitly Analyzed Files', implicitNames); | 630 _writeFiles(buffer, 'Implicitly Analyzed Files', implicitNames); |
| 644 | 631 |
| 645 buffer.write('<h3>Exceptions</h3>'); | 632 buffer.write('<h3>Exceptions</h3>'); |
| (...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 928 | 915 |
| 929 /** | 916 /** |
| 930 * Return an error in response to an unrecognized request received by the HTTP | 917 * Return an error in response to an unrecognized request received by the HTTP |
| 931 * server. | 918 * server. |
| 932 */ | 919 */ |
| 933 void _returnUnknownRequest(HttpRequest request) { | 920 void _returnUnknownRequest(HttpRequest request) { |
| 934 _writeResponse(request, (StringBuffer buffer) { | 921 _writeResponse(request, (StringBuffer buffer) { |
| 935 _writePage(buffer, 'Analysis Server', [], (StringBuffer buffer) { | 922 _writePage(buffer, 'Analysis Server', [], (StringBuffer buffer) { |
| 936 buffer.write('<h3>Pages</h3>'); | 923 buffer.write('<h3>Pages</h3>'); |
| 937 buffer.write('<p>'); | 924 buffer.write('<p>'); |
| 938 buffer.write(_makeLink(COMPLETION_PATH, {}, 'Completion data')); | 925 buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data')); |
| 939 buffer.write('</p>'); | 926 buffer.write('</p>'); |
| 940 buffer.write('<p>'); | 927 buffer.write('<p>'); |
| 941 buffer.write(_makeLink(PERFORMANCE_PATH, {}, 'Performance')); | 928 buffer.write(makeLink(PERFORMANCE_PATH, {}, 'Performance')); |
| 942 buffer.write('</p>'); | 929 buffer.write('</p>'); |
| 943 buffer.write('<p>'); | 930 buffer.write('<p>'); |
| 944 buffer.write(_makeLink(STATUS_PATH, {}, 'Server status')); | 931 buffer.write(makeLink(STATUS_PATH, {}, 'Server status')); |
| 945 buffer.write('</p>'); | 932 buffer.write('</p>'); |
| 946 }); | 933 }); |
| 947 }); | 934 }); |
| 948 } | 935 } |
| 949 | 936 |
| 950 /** | 937 /** |
| 951 * Return a two digit decimal representation of the given non-negative integer | 938 * Return a two digit decimal representation of the given non-negative integer |
| 952 * [value]. | 939 * [value]. |
| 953 */ | 940 */ |
| 954 String _twoDigit(int value) { | 941 String _twoDigit(int value) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 993 buffer.write('<p><b>Analysis Contexts</b></p>'); | 980 buffer.write('<p><b>Analysis Contexts</b></p>'); |
| 994 buffer.write('<p>'); | 981 buffer.write('<p>'); |
| 995 bool first = true; | 982 bool first = true; |
| 996 folders.forEach((Folder folder) { | 983 folders.forEach((Folder folder) { |
| 997 if (first) { | 984 if (first) { |
| 998 first = false; | 985 first = false; |
| 999 } else { | 986 } else { |
| 1000 buffer.write('<br>'); | 987 buffer.write('<br>'); |
| 1001 } | 988 } |
| 1002 String key = folder.shortName; | 989 String key = folder.shortName; |
| 1003 buffer.write(_makeLink(CONTEXT_PATH, { | 990 buffer.write(makeLink(CONTEXT_PATH, { |
| 1004 CONTEXT_QUERY_PARAM: folder.path | 991 CONTEXT_QUERY_PARAM: folder.path |
| 1005 }, key, _hasException(folderMap[folder]))); | 992 }, key, _hasException(folderMap[folder]))); |
| 1006 }); | 993 }); |
| 1007 buffer.write('</p>'); | 994 buffer.write('</p>'); |
| 1008 | 995 |
| 1009 buffer.write('<p><b>Options</b></p>'); | 996 buffer.write('<p><b>Options</b></p>'); |
| 1010 buffer.write('<p>'); | 997 buffer.write('<p>'); |
| 1011 _writeOption( | 998 _writeOption( |
| 1012 buffer, | 999 buffer, |
| 1013 'Analyze functon bodies', | 1000 'Analyze functon bodies', |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1097 'Complete (ms)', | 1084 'Complete (ms)', |
| 1098 '', | 1085 '', |
| 1099 '# Notifications', | 1086 '# Notifications', |
| 1100 '', | 1087 '', |
| 1101 '# Suggestions', | 1088 '# Suggestions', |
| 1102 '', | 1089 '', |
| 1103 'Snippet'], | 1090 'Snippet'], |
| 1104 header: true); | 1091 header: true); |
| 1105 int index = 0; | 1092 int index = 0; |
| 1106 for (CompletionPerformance performance in handler.performanceList) { | 1093 for (CompletionPerformance performance in handler.performanceList) { |
| 1107 String link = _makeLink(COMPLETION_PATH, { | 1094 String link = makeLink(COMPLETION_PATH, { |
| 1108 'index': '$index' | 1095 'index': '$index' |
| 1109 }, '${performance.startTimeAndMs}'); | 1096 }, '${performance.startTimeAndMs}'); |
| 1110 _writeRow( | 1097 _writeRow( |
| 1111 buffer, | 1098 buffer, |
| 1112 [ | 1099 [ |
| 1113 link, | 1100 link, |
| 1114 ' ', | 1101 ' ', |
| 1115 performance.firstNotificationInMilliseconds, | 1102 performance.firstNotificationInMilliseconds, |
| 1116 ' ', | 1103 ' ', |
| 1117 performance.elapsedInMilliseconds, | 1104 performance.elapsedInMilliseconds, |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1170 | 1157 |
| 1171 /** | 1158 /** |
| 1172 * Write the status of the edit domain (on the main status page) to the given | 1159 * Write the status of the edit domain (on the main status page) to the given |
| 1173 * [buffer]. | 1160 * [buffer]. |
| 1174 */ | 1161 */ |
| 1175 void _writeEditStatus(StringBuffer buffer) { | 1162 void _writeEditStatus(StringBuffer buffer) { |
| 1176 buffer.write('<h3>Edit Domain</h3>'); | 1163 buffer.write('<h3>Edit Domain</h3>'); |
| 1177 _writeTwoColumns(buffer, (StringBuffer buffer) { | 1164 _writeTwoColumns(buffer, (StringBuffer buffer) { |
| 1178 buffer.write('<p><b>Performance Data</b></p>'); | 1165 buffer.write('<p><b>Performance Data</b></p>'); |
| 1179 buffer.write('<p>'); | 1166 buffer.write('<p>'); |
| 1180 buffer.write(_makeLink(COMPLETION_PATH, {}, 'Completion data')); | 1167 buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data')); |
| 1181 buffer.write('</p>'); | 1168 buffer.write('</p>'); |
| 1182 }, (StringBuffer buffer) { | 1169 }, (StringBuffer buffer) { |
| 1183 }); | 1170 }); |
| 1184 } | 1171 } |
| 1185 | 1172 |
| 1186 /** | 1173 /** |
| 1187 * Write a representation of the given [caughtException] to the given | 1174 * Write a representation of the given [caughtException] to the given |
| 1188 * [buffer]. If [isCause] is `true`, then the exception was a cause for | 1175 * [buffer]. If [isCause] is `true`, then the exception was a cause for |
| 1189 * another exception. | 1176 * another exception. |
| 1190 */ | 1177 */ |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1410 } else { | 1397 } else { |
| 1411 buffer.write('Inactive'); | 1398 buffer.write('Inactive'); |
| 1412 } | 1399 } |
| 1413 buffer.write('<br>'); | 1400 buffer.write('<br>'); |
| 1414 buffer.write('Version: '); | 1401 buffer.write('Version: '); |
| 1415 buffer.write(AnalysisServer.VERSION); | 1402 buffer.write(AnalysisServer.VERSION); |
| 1416 buffer.write('</p>'); | 1403 buffer.write('</p>'); |
| 1417 | 1404 |
| 1418 buffer.write('<p><b>Performance Data</b></p>'); | 1405 buffer.write('<p><b>Performance Data</b></p>'); |
| 1419 buffer.write('<p>'); | 1406 buffer.write('<p>'); |
| 1420 buffer.write( | 1407 buffer.write(makeLink(PERFORMANCE_PATH, {}, 'Communication performance')); |
| 1421 _makeLink(PERFORMANCE_PATH, {}, 'Communication performance')); | |
| 1422 buffer.write('</p>'); | 1408 buffer.write('</p>'); |
| 1423 }, (StringBuffer buffer) { | 1409 }, (StringBuffer buffer) { |
| 1424 _writeSubscriptionList(buffer, ServerService.VALUES, services); | 1410 _writeSubscriptionList(buffer, ServerService.VALUES, services); |
| 1425 }); | 1411 }); |
| 1426 return true; | 1412 return true; |
| 1427 } | 1413 } |
| 1428 | 1414 |
| 1429 /** | 1415 /** |
| 1430 * Write a representation of the given [stackTrace] to the given [buffer]. | 1416 * Write a representation of the given [stackTrace] to the given [buffer]. |
| 1431 */ | 1417 */ |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1540 buffer.write('List containing ${value.length} entries'); | 1526 buffer.write('List containing ${value.length} entries'); |
| 1541 buffer.write('<ul>'); | 1527 buffer.write('<ul>'); |
| 1542 for (var entry in value) { | 1528 for (var entry in value) { |
| 1543 buffer.write('<li>'); | 1529 buffer.write('<li>'); |
| 1544 _writeValueAsHtml(buffer, entry, linkParameters); | 1530 _writeValueAsHtml(buffer, entry, linkParameters); |
| 1545 buffer.write('</li>'); | 1531 buffer.write('</li>'); |
| 1546 } | 1532 } |
| 1547 buffer.write('</ul>'); | 1533 buffer.write('</ul>'); |
| 1548 } else if (value is AstNode) { | 1534 } else if (value is AstNode) { |
| 1549 String link = | 1535 String link = |
| 1550 _makeLink(AST_PATH, linkParameters, value.runtimeType.toString()); | 1536 makeLink(AST_PATH, linkParameters, value.runtimeType.toString()); |
| 1551 buffer.write('<i>$link</i>'); | 1537 buffer.write('<i>$link</i>'); |
| 1552 } else if (value is Element) { | 1538 } else if (value is Element) { |
| 1553 String link = | 1539 String link = |
| 1554 _makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString()); | 1540 makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString()); |
| 1555 buffer.write('<i>$link</i>'); | 1541 buffer.write('<i>$link</i>'); |
| 1556 } else { | 1542 } else { |
| 1557 buffer.write(HTML_ESCAPE.convert(value.toString())); | 1543 buffer.write(HTML_ESCAPE.convert(value.toString())); |
| 1558 buffer.write(' <i>(${value.runtimeType.toString()})</i>'); | 1544 buffer.write(' <i>(${value.runtimeType.toString()})</i>'); |
| 1559 } | 1545 } |
| 1560 } | 1546 } |
| 1547 |
| 1548 /** |
| 1549 * Create a link to [path] with query parameters [params], with inner HTML |
| 1550 * [innerHtml]. If [hasError] is `true`, then the link will have the class |
| 1551 * 'error'. |
| 1552 */ |
| 1553 static String makeLink(String path, Map<String, String> params, |
| 1554 String innerHtml, [bool hasError = false]) { |
| 1555 Uri uri = new Uri(path: path, queryParameters: params); |
| 1556 String href = HTML_ESCAPE.convert(uri.toString()); |
| 1557 String classAttribute = hasError ? ' class="error"' : ''; |
| 1558 return '<a href="$href"$classAttribute>$innerHtml</a>'; |
| 1559 } |
| 1561 } | 1560 } |
| OLD | NEW |