mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 21:41:19 +00:00
Add syntax highlighting to instrumentation_renderer
In this change, we also push HTML escaping to mustache. Changes can be seen in x20 pages emailed to the team. Change-Id: I239668a0844fb5bd7c90848f25a3cbf29e50363d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118460 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
parent
0b7a78d0a6
commit
87a369f1d2
|
@ -15,31 +15,45 @@ class InstrumentationRenderer {
|
|||
Map<String, dynamic> mustacheContext = {'units': <Map<String, dynamic>>[]};
|
||||
for (var compilationUnit in info.units) {
|
||||
StringBuffer buffer = StringBuffer();
|
||||
// List of Mustache context for both unmodified and modified regions:
|
||||
//
|
||||
// * 'modified': Whether this region represents modified source, or
|
||||
// unmodified.
|
||||
// * 'content': The textual content of this region.
|
||||
// * 'explanation': The textual explanation of why the content in this
|
||||
// region was modified. It will appear in a "tooltip" on hover.
|
||||
// TODO(srawlins): Support some sort of HTML explanation, with
|
||||
// hyperlinks to anchors in other source code.
|
||||
List<Map> regions = [];
|
||||
for (var region in compilationUnit.regions) {
|
||||
if (region.offset > previousIndex) {
|
||||
// Display a region of unmodified content.
|
||||
buffer.write(
|
||||
compilationUnit.content.substring(previousIndex, region.offset));
|
||||
regions.add({
|
||||
'modified': false,
|
||||
'content':
|
||||
compilationUnit.content.substring(previousIndex, region.offset)
|
||||
});
|
||||
previousIndex = region.offset + region.length;
|
||||
}
|
||||
buffer.write(_regionWithTooltip(region, compilationUnit.content));
|
||||
regions.add({
|
||||
'modified': true,
|
||||
'content': compilationUnit.content
|
||||
.substring(region.offset, region.offset + region.length),
|
||||
'explanation': region.explanation,
|
||||
});
|
||||
}
|
||||
if (previousIndex < compilationUnit.content.length) {
|
||||
// Last region of unmodified content.
|
||||
buffer.write(compilationUnit.content.substring(previousIndex));
|
||||
regions.add({
|
||||
'modified': false,
|
||||
'content': compilationUnit.content.substring(previousIndex)
|
||||
});
|
||||
}
|
||||
mustacheContext['units']
|
||||
.add({'path': compilationUnit.path, 'content': buffer.toString()});
|
||||
.add({'path': compilationUnit.path, 'regions': regions});
|
||||
}
|
||||
return _template.renderString(mustacheContext);
|
||||
}
|
||||
|
||||
String _regionWithTooltip(RegionInfo region, String content) {
|
||||
String regionContent =
|
||||
content.substring(region.offset, region.offset + region.length);
|
||||
return '<span class="region">$regionContent'
|
||||
'<span class="tooltip">${region.explanation}</span></span>';
|
||||
}
|
||||
}
|
||||
|
||||
/// A mustache template for one library's instrumentation output.
|
||||
|
@ -47,35 +61,58 @@ mustache.Template _template = mustache.Template(r'''
|
|||
<html>
|
||||
<head>
|
||||
<title>Non-nullable fix instrumentation report</title>
|
||||
<script src="highlight.pack.js"></script>
|
||||
<link rel="stylesheet" href="styles/androidstudio.css">
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.content {
|
||||
.content {
|
||||
font-family: monospace;
|
||||
whitespace: pre;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.content.highlighting {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.regions {
|
||||
position: absolute;
|
||||
top: 0.5em;
|
||||
/* The content of the regions is not visible; the user instead will see the
|
||||
* highlighted copy of the content. */
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.region {
|
||||
/* Green means this region was added. */
|
||||
color: green;
|
||||
background-color: #ccffcc;
|
||||
color: #003300;
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.region .tooltip {
|
||||
background-color: #EEE;
|
||||
border: solid 2px #999;
|
||||
color: #333;
|
||||
cursor: auto;
|
||||
left: 50%;
|
||||
margin-left: -50px;
|
||||
margin-left: -100px;
|
||||
padding: 1px;
|
||||
position: absolute;
|
||||
top: 120%;
|
||||
top: 100%;
|
||||
visibility: hidden;
|
||||
width: 100px;
|
||||
width: 200px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
@ -86,13 +123,31 @@ div.content {
|
|||
</head>
|
||||
<body>
|
||||
<h1>Non-nullable fix instrumentation report</h1>
|
||||
<p><em>Well-written introduction to this report.</em></p>
|
||||
{{# units }}
|
||||
<h2>{{ path }}</h2>
|
||||
<div class="content">
|
||||
{{{ content }}}
|
||||
</div> {{! content }}
|
||||
{{/ units }}
|
||||
</body>
|
||||
</html>
|
||||
''');
|
||||
<p><em>Well-written introduction to this report.</em></p>'''
|
||||
' {{# units }}'
|
||||
' <h2>{{{ path }}}</h2>'
|
||||
' <div class="content highlighting">'
|
||||
'{{! These regions are written out, unmodified, as they need to be found }}'
|
||||
'{{! in one simple text string for highlight.js to hightlight them. }}'
|
||||
'{{# regions }}'
|
||||
'{{ content }}'
|
||||
'{{/ regions }}'
|
||||
' <div class="regions">'
|
||||
'{{! The regions are then printed again, overlaying the first copy of the }}'
|
||||
'{{! content, to provide tooltips for modified regions. }}'
|
||||
'{{# regions }}'
|
||||
'{{^ modified }}{{ content }}{{/ modified }}'
|
||||
'{{# modified }}<span class="region">{{ content }}'
|
||||
'<span class="tooltip">{{explanation}}</span></span>{{/ modified }}'
|
||||
'{{/ regions }}'
|
||||
'</div></div>'
|
||||
' {{/ units }}'
|
||||
' <script lang="javascript">'
|
||||
'document.addEventListener("DOMContentLoaded", (event) => {'
|
||||
' document.querySelectorAll(".highlighting").forEach((block) => {'
|
||||
' hljs.highlightBlock(block);'
|
||||
' });'
|
||||
'});'
|
||||
' </script>'
|
||||
' </body>'
|
||||
'</html>');
|
||||
|
|
|
@ -17,6 +17,41 @@ main() {
|
|||
|
||||
@reflectiveTest
|
||||
class InstrumentationRendererTest extends AbstractAnalysisTest {
|
||||
test_outputContainsEachPath() async {
|
||||
LibraryInfo info = LibraryInfo([
|
||||
UnitInfo('/lib/a.dart', 'int? a = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
UnitInfo('/lib/part1.dart', 'int? b = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
UnitInfo('/lib/part2.dart', 'int? c = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
]);
|
||||
String output = InstrumentationRenderer(info).render();
|
||||
expect(output, contains('<h2>/lib/a.dart</h2>'));
|
||||
expect(output, contains('<h2>/lib/part1.dart</h2>'));
|
||||
expect(output, contains('<h2>/lib/part2.dart</h2>'));
|
||||
}
|
||||
|
||||
test_outputContainsEscapedHtml() async {
|
||||
LibraryInfo info = LibraryInfo([
|
||||
UnitInfo('/lib/a.dart', 'List<String>? a = null;',
|
||||
[RegionInfo(12, 1, 'null was assigned')]),
|
||||
]);
|
||||
String output = InstrumentationRenderer(info).render();
|
||||
expect(
|
||||
output,
|
||||
contains('List<String><span class="region">?'
|
||||
'<span class="tooltip">null was assigned</span></span> a = null;'));
|
||||
}
|
||||
|
||||
test_outputContainsEscapedHtml_ampersand() async {
|
||||
LibraryInfo info = LibraryInfo([
|
||||
UnitInfo('/lib/a.dart', 'bool a = true && false;', []),
|
||||
]);
|
||||
String output = InstrumentationRenderer(info).render();
|
||||
expect(output, contains('bool a = true && false;'));
|
||||
}
|
||||
|
||||
test_outputContainsModifiedAndUnmodifiedRegions() async {
|
||||
LibraryInfo info = LibraryInfo([
|
||||
UnitInfo('/lib/a.dart', 'int? a = null;',
|
||||
|
@ -28,19 +63,4 @@ class InstrumentationRendererTest extends AbstractAnalysisTest {
|
|||
contains('int<span class="region">?'
|
||||
'<span class="tooltip">null was assigned</span></span> a = null;'));
|
||||
}
|
||||
|
||||
test_outputContainsEachPath() async {
|
||||
LibraryInfo info = LibraryInfo([
|
||||
UnitInfo('/lib/a.dart', 'int? a = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
UnitInfo('/lib/part1.dart', 'int? b = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
UnitInfo('/lib/part2.dart', 'int? c = null;',
|
||||
[RegionInfo(3, 1, 'null was assigned')]),
|
||||
]);
|
||||
String output = InstrumentationRenderer(info).render();
|
||||
expect(output, contains('<h2>/lib/a.dart</h2>'));
|
||||
expect(output, contains('<h2>/lib/part1.dart</h2>'));
|
||||
expect(output, contains('<h2>/lib/part2.dart</h2>'));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue