| Index: tests/compiler/dart2js/sourcemaps/diff_view.dart
|
| diff --git a/tests/compiler/dart2js/sourcemaps/diff_view.dart b/tests/compiler/dart2js/sourcemaps/diff_view.dart
|
| index b689360c5271bba122aab5981182e099295287e4..c58a21edaddaa94eb75f99cb5963b52b54bce5c8 100644
|
| --- a/tests/compiler/dart2js/sourcemaps/diff_view.dart
|
| +++ b/tests/compiler/dart2js/sourcemaps/diff_view.dart
|
| @@ -1,40 +1,61 @@
|
| -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| library sourcemap.diff_view;
|
|
|
| import 'dart:async';
|
| +import 'dart:convert';
|
| import 'dart:io';
|
| +
|
| import 'package:compiler/src/commandline_options.dart';
|
| import 'package:compiler/src/diagnostics/invariant.dart';
|
| +import 'package:compiler/src/elements/elements.dart';
|
| import 'package:compiler/src/io/position_information.dart';
|
| +import 'package:compiler/src/io/source_information.dart';
|
| +import 'package:compiler/src/io/source_file.dart';
|
| import 'package:compiler/src/js/js.dart' as js;
|
| +import 'package:compiler/src/js/js_debug.dart';
|
| +
|
| +import 'diff.dart';
|
| +import 'html_parts.dart';
|
| +import 'js_tracer.dart';
|
| +import 'output_structure.dart';
|
| import 'sourcemap_helper.dart';
|
| import 'sourcemap_html_helper.dart';
|
| import 'trace_graph.dart';
|
| -import 'js_tracer.dart';
|
|
|
| -const String WITH_SOURCE_INFO_STYLE = 'background-color:#FF8080;';
|
| -const String WITHOUT_SOURCE_INFO_STYLE = 'border: solid 1px #FF8080;';
|
| -const String ADDITIONAL_SOURCE_INFO_STYLE = 'border: solid 1px #8080FF;';
|
| +const String WITH_SOURCE_INFO_STYLE = 'border: solid 1px #FF8080;';
|
| +const String WITHOUT_SOURCE_INFO_STYLE = 'background-color: #8080FF;';
|
| +const String ADDITIONAL_SOURCE_INFO_STYLE = 'border: solid 1px #80FF80;';
|
| +const String UNUSED_SOURCE_INFO_STYLE = 'border: solid 1px #8080FF;';
|
|
|
| main(List<String> args) async {
|
| DEBUG_MODE = true;
|
| String out = 'out.js.diff_view.html';
|
| String filename;
|
| List<String> currentOptions = [];
|
| - List<List<String>> options = [currentOptions];
|
| + List<List<String>> optionSegments = [currentOptions];
|
| + Map<int, String> loadFrom = {};
|
| + Map<int, String> saveTo = {};
|
| int argGroup = 0;
|
| bool addAnnotations = true;
|
| for (String arg in args) {
|
| if (arg == '--') {
|
| currentOptions = [];
|
| - options.add(currentOptions);
|
| + optionSegments.add(currentOptions);
|
| argGroup++;
|
| } else if (arg == '-h') {
|
| addAnnotations = false;
|
| print('Hiding annotations');
|
| + } else if (arg == '-l') {
|
| + loadFrom[argGroup] = 'out.js.diff$argGroup.json';
|
| + } else if (arg.startsWith('--load=')) {
|
| + loadFrom[argGroup] = arg.substring('--load='.length);
|
| + } else if (arg == '-s') {
|
| + saveTo[argGroup] = 'out.js.diff$argGroup.json';
|
| + } else if (arg.startsWith('--save=')) {
|
| + saveTo[argGroup] = arg.substring('--save='.length);
|
| } else if (arg.startsWith('-o')) {
|
| out = arg.substring('-o'.length);
|
| } else if (arg.startsWith('--out=')) {
|
| @@ -45,54 +66,184 @@ main(List<String> args) async {
|
| filename = arg;
|
| }
|
| }
|
| - List<String> commonArguments = options[0];
|
| - List<String> options1;
|
| - List<String> options2;
|
| - if (options.length == 1) {
|
| + List<String> commonArguments = optionSegments[0];
|
| + List<List<String>> options = <List<String>>[];
|
| + if (optionSegments.length == 1) {
|
| // Use default options; comparing SSA and CPS output using the new
|
| // source information strategy.
|
| - options1 = [USE_NEW_SOURCE_INFO]..addAll(commonArguments);
|
| - options2 = [USE_NEW_SOURCE_INFO, Flags.useCpsIr]..addAll(commonArguments);
|
| - } else if (options.length == 2) {
|
| + options.add([USE_NEW_SOURCE_INFO]..addAll(commonArguments));
|
| + options.add([USE_NEW_SOURCE_INFO, Flags.useCpsIr]..addAll(commonArguments));
|
| + } else if (optionSegments.length == 2) {
|
| // Use alternative options for the second output column.
|
| - options1 = commonArguments;
|
| - options2 = options[1]..addAll(commonArguments);
|
| + options.add(commonArguments);
|
| + options.add(optionSegments[1]..addAll(commonArguments));
|
| } else {
|
| // Use specific options for both output columns.
|
| - options1 = options[1]..addAll(commonArguments);
|
| - options2 = options[2]..addAll(commonArguments);
|
| + options.add(optionSegments[1]..addAll(commonArguments));
|
| + options.add(optionSegments[2]..addAll(commonArguments));
|
| + }
|
| +
|
| + SourceFileManager sourceFileManager = new IOSourceFileManager(Uri.base);
|
| + List<AnnotatedOutput> outputs = <AnnotatedOutput>[];
|
| + for (int i = 0; i < 2; i++) {
|
| + AnnotatedOutput output;
|
| + if (loadFrom.containsKey(i)) {
|
| + output = AnnotatedOutput.loadOutput(loadFrom[i]);
|
| + } else {
|
| + print('Compiling ${options[i].join(' ')} $filename');
|
| + CodeLinesResult result = await computeCodeLines(
|
| + options[i], filename, addAnnotations: addAnnotations);
|
| + OutputStructure structure = OutputStructure.parse(result.codeLines);
|
| + computeEntityCodeSources(result, structure);
|
| + output = new AnnotatedOutput(
|
| + filename,
|
| + options[i],
|
| + structure,
|
| + result.coverage.getCoverageReport());
|
| + }
|
| + if (saveTo.containsKey(i)) {
|
| + AnnotatedOutput.saveOutput(output, saveTo[i]);
|
| + }
|
| + outputs.add(output);
|
| + }
|
| +
|
| + List<DiffBlock> blocks = createDiffBlocks(
|
| + outputs.map((o) => o.structure).toList(),
|
| + sourceFileManager);
|
| +
|
| + outputDiffView(
|
| + out, outputs, blocks, addAnnotations: addAnnotations);
|
| +}
|
| +
|
| +/// Attaches [CodeSource]s to the entities in [structure] using the
|
| +/// element-to-offset in [result].
|
| +void computeEntityCodeSources(
|
| + CodeLinesResult result, OutputStructure structure) {
|
| + result.elementMap.forEach((int line, Element element) {
|
| + OutputEntity entity = structure.getEntityForLine(line);
|
| + if (entity != null) {
|
| + entity.codeSource = codeSourceFromElement(element);
|
| + }
|
| + });
|
| +}
|
| +
|
| +/// The structured output of a compilation.
|
| +class AnnotatedOutput {
|
| + final String filename;
|
| + final List<String> options;
|
| + final OutputStructure structure;
|
| + final String coverage;
|
| +
|
| + AnnotatedOutput(this.filename, this.options, this.structure, this.coverage);
|
| +
|
| + List<CodeLine> get codeLines => structure.lines;
|
| +
|
| + Map toJson() {
|
| + return {
|
| + 'filename': filename,
|
| + 'options': options,
|
| + 'structure': structure.toJson(),
|
| + 'coverage': coverage,
|
| + };
|
| + }
|
| +
|
| + static AnnotatedOutput fromJson(Map json) {
|
| + String filename = json['filename'];
|
| + List<String> options = json['options'];
|
| + OutputStructure structure = OutputStructure.fromJson(json['structure']);
|
| + String coverage = json['coverage'];
|
| + return new AnnotatedOutput(filename, options, structure, coverage);
|
| + }
|
| +
|
| + static AnnotatedOutput loadOutput(filename) {
|
| + AnnotatedOutput output = AnnotatedOutput.fromJson(
|
| + JSON.decode(new File(filename).readAsStringSync()));
|
| + print('Output loaded from $filename');
|
| + return output;
|
| }
|
|
|
| - print('Compiling ${options1.join(' ')} $filename');
|
| - CodeLinesResult result1 = await computeCodeLines(
|
| - options1, filename, addAnnotations: addAnnotations);
|
| - print('Compiling ${options2.join(' ')} $filename');
|
| - CodeLinesResult result2 = await computeCodeLines(
|
| - options2, filename, addAnnotations: addAnnotations);
|
| + static void saveOutput(AnnotatedOutput output, String filename) {
|
| + if (filename != null) {
|
| + new File(filename).writeAsStringSync(
|
| + const JsonEncoder.withIndent(' ').convert(output.toJson()));
|
| + print('Output saved in $filename');
|
| + }
|
| + }
|
| +}
|
| +
|
| +void outputDiffView(
|
| + String out,
|
| + List<AnnotatedOutput> outputs,
|
| + List<DiffBlock> blocks,
|
| + {bool addAnnotations: true}) {
|
| + assert(outputs[0].filename == outputs[1].filename);
|
| + bool usePre = true;
|
|
|
| StringBuffer sb = new StringBuffer();
|
| sb.write('''
|
| <html>
|
| <head>
|
| -<title>Diff for $filename</title>
|
| +<title>Diff for ${outputs[0].filename}</title>
|
| <style>
|
| .lineNumber {
|
| font-size: smaller;
|
| color: #888;
|
| }
|
| +.comment {
|
| + font-size: smaller;
|
| + color: #888;
|
| + font-family: initial;
|
| +}
|
| .header {
|
| position: fixed;
|
| - width: 50%;
|
| - background-color: #400000;
|
| - color: #FFFFFF;
|
| - height: 20px;
|
| + width: 100%;
|
| + background-color: #FFFFFF;
|
| + left: 0px;
|
| top: 0px;
|
| + height: 42px;
|
| z-index: 1000;
|
| }
|
| +.header-table {
|
| + width: 100%;
|
| + background-color: #400000;
|
| + color: #FFFFFF;
|
| + border-spacing: 0px;
|
| +}
|
| +.header-column {
|
| + width: 34%;
|
| +}
|
| +.legend {
|
| + padding: 2px;
|
| +}
|
| +.table {
|
| + position: absolute;
|
| + left: 0px;
|
| + top: 42px;
|
| + width: 100%;
|
| + border-spacing: 0px;
|
| +}
|
| .cell {
|
| - max-width:500px;
|
| - overflow-x:auto;
|
| - vertical-align:top;
|
| + max-width: 500px;
|
| + overflow-y: hidden;
|
| + vertical-align: top;
|
| + border-top: 1px solid #F0F0F0;
|
| + border-left: 1px solid #F0F0F0;
|
| +''');
|
| + if (usePre) {
|
| + sb.write('''
|
| + overflow-x: hidden;
|
| + white-space: pre-wrap;
|
| +''');
|
| + } else {
|
| + sb.write('''
|
| + overflow-x: hidden;
|
| + padding-left: 100px;
|
| + text-indent: -100px;
|
| +''');
|
| + }
|
| + sb.write('''
|
| + font-family: monospace;
|
| + padding: 0px;
|
| }
|
| .corresponding1 {
|
| background-color: #FFFFE0;
|
| @@ -106,14 +257,29 @@ main(List<String> args) async {
|
| .identical2 {
|
| background-color: #C0E0C0;
|
| }
|
| +.line {
|
| + padding-left: 7em;
|
| + text-indent: -7em;
|
| + margin: 0px;
|
| +}
|
| +.column0 {
|
| +}
|
| +.column1 {
|
| +}
|
| +.column2 {
|
| +}
|
| </style>
|
| </head>
|
| <body>''');
|
|
|
| sb.write('''
|
| -<div class="header" style="left: 0px;">[${options1.join(',')}]</div>
|
| -<div class="header" style="right: 0px;">[${options2.join(',')}]</div>
|
| -<div style="position:absolute;left:0px;top:22px;width:100%;height:18px;">
|
| +<div class="header">
|
| +<table class="header-table"><tr>
|
| +<td class="header-column">[${outputs[0].options.join(',')}]</td>
|
| +<td class="header-column">[${outputs[1].options.join(',')}]</td>
|
| +<td class="header-column">Dart code</td>
|
| +</tr></table>
|
| +<div class="legend">
|
| <span class="identical1"> </span>
|
| <span class="identical2"> </span>
|
| identical blocks
|
| @@ -121,19 +287,36 @@ main(List<String> args) async {
|
| <span class="corresponding2"> </span>
|
| corresponding blocks
|
| ''');
|
| +
|
| if (addAnnotations) {
|
| sb.write('''
|
| <span style="$WITH_SOURCE_INFO_STYLE"> </span>
|
| - offset with source information
|
| + <span title="'offset with source information' means that source information
|
| +is available for an offset which is expected to have a source location
|
| +attached. This offset has source information as intended.">
|
| + offset with source information</span>
|
| <span style="$WITHOUT_SOURCE_INFO_STYLE"> </span>
|
| - offset without source information
|
| + <span title="'offset without source information' means that _no_ source
|
| +information is available for an offset which was expected to have a source
|
| +location attached. Source information must be found for this offset.">
|
| + offset without source information</span>
|
| <span style="$ADDITIONAL_SOURCE_INFO_STYLE"> </span>
|
| - offset with unneeded source information
|
| + <span title="'offset with unneeded source information' means that a source
|
| +location was attached to an offset which was _not_ expected to have a source
|
| +location attached. The source location should be removed from this offset.">
|
| + offset with unneeded source information</span>
|
| + <span style="$UNUSED_SOURCE_INFO_STYLE"> </span>
|
| + <span title="'offset with unused source information' means that source
|
| +information is available for an offset which is _not_ expected to have a source
|
| +location attached. This source information _could_ be used by a parent AST node
|
| +offset that is an 'offset without source information'.">
|
| + offset with unused source information</span>
|
| ''');
|
| }
|
| +
|
| sb.write('''
|
| -</div>
|
| -<table style="position:absolute;left:0px;top:40px;width:100%;"><tr>
|
| +</div></div>
|
| +<table class="table">
|
| ''');
|
|
|
| void addCell(String content) {
|
| @@ -146,225 +329,60 @@ main(List<String> args) async {
|
| ''');
|
| }
|
|
|
| - List<OutputStructure> structures = [
|
| - OutputStructure.parse(result1.codeLines),
|
| - OutputStructure.parse(result2.codeLines)];
|
| - List<List<CodeLine>> inputLines = [result1.codeLines, result2.codeLines];
|
| - List<List<HtmlPart>> outputLines = [<HtmlPart>[], <HtmlPart>[]];
|
| -
|
| /// Marker to alternate output colors.
|
| bool alternating = false;
|
|
|
| - /// Enable 'corresponding' background colors for [f].
|
| - void withMatching(f()) {
|
| - HtmlPart start = new ConstHtmlPart(
|
| - '<div class="corresponding${alternating ? '1' : '2'}">');
|
| - HtmlPart end = new ConstHtmlPart('</div>');
|
| - alternating = !alternating;
|
| - outputLines[0].add(start);
|
| - outputLines[1].add(start);
|
| - f();
|
| - outputLines[0].add(end);
|
| - outputLines[1].add(end);
|
| - }
|
| -
|
| - /// Enable 'identical' background colors for [f].
|
| - void withIdentical(f()) {
|
| - HtmlPart start = new ConstHtmlPart(
|
| - '<div class="identical${alternating ? '1' : '2'}">');
|
| - HtmlPart end = new ConstHtmlPart('</div>');
|
| - alternating = !alternating;
|
| - outputLines[0].add(start);
|
| - outputLines[1].add(start);
|
| - f();
|
| - outputLines[0].add(end);
|
| - outputLines[1].add(end);
|
| - }
|
| -
|
| - /// Output code lines in [range] from input number [index], padding the other
|
| - /// column with empty lines.
|
| - void handleSkew(int index, Interval range) {
|
| - int from = range.from;
|
| - while (from < range.to) {
|
| - outputLines[1 - index].add(const ConstHtmlPart('\n'));
|
| - outputLines[index].add(
|
| - new CodeLineHtmlPart(inputLines[index][from++]));
|
| + List<HtmlPrintContext> printContexts = <HtmlPrintContext>[];
|
| + for (int i = 0; i < 2; i++) {
|
| + int lineNoWidth;
|
| + if (outputs[i].codeLines.isNotEmpty) {
|
| + lineNoWidth = '${outputs[i].codeLines.last.lineNo + 1}'.length;
|
| }
|
| + printContexts.add(new HtmlPrintContext(lineNoWidth: lineNoWidth));
|
| }
|
|
|
| - /// Output code lines of the [indices] from the corresponding inputs.
|
| - void addBoth(List<int> indices) {
|
| - outputLines[0].add(new CodeLineHtmlPart(inputLines[0][indices[0]]));
|
| - outputLines[1].add(new CodeLineHtmlPart(inputLines[1][indices[1]]));
|
| - }
|
| -
|
| - /// Output code lines of the [ranges] from the corresponding inputs.
|
| - void addBothLines(List<Interval> ranges) {
|
| - Interval range1 = ranges[0];
|
| - Interval range2 = ranges[1];
|
| - int offset = 0;
|
| - while (range1.from + offset < range1.to &&
|
| - range2.from + offset < range2.to) {
|
| - addBoth([range1.from + offset, range2.from + offset]);
|
| - offset++;
|
| - }
|
| - if (range1.from + offset < range1.to) {
|
| - handleSkew(0, new Interval(range1.from + offset, range1.to));
|
| + for (DiffBlock block in blocks) {
|
| + String className;
|
| + switch (block.kind) {
|
| + case DiffKind.UNMATCHED:
|
| + className = 'cell';
|
| + break;
|
| + case DiffKind.MATCHING:
|
| + className = 'cell corresponding${alternating ? '1' : '2'}';
|
| + alternating = !alternating;
|
| + break;
|
| + case DiffKind.IDENTICAL:
|
| + className = 'cell identical${alternating ? '1' : '2'}';
|
| + alternating = !alternating;
|
| + break;
|
| }
|
| - if (range2.from + offset < range2.to) {
|
| - handleSkew(1, new Interval(range2.from + offset, range2.to));
|
| - }
|
| - }
|
| -
|
| - /// Merge the code lines in [range1] and [range2] of the corresponding input.
|
| - void addRaw(Interval range1, Interval range2) {
|
| - match(a, b) => a.code == b.code;
|
| -
|
| - List<Interval> currentMatchedIntervals;
|
| -
|
| - void flushMatching() {
|
| - if (currentMatchedIntervals != null) {
|
| - withIdentical(() {
|
| - addBothLines(currentMatchedIntervals);
|
| - });
|
| - }
|
| - currentMatchedIntervals = null;
|
| - }
|
| -
|
| - align(
|
| - inputLines[0],
|
| - inputLines[1],
|
| - range1: range1,
|
| - range2: range2,
|
| - match: match,
|
| - handleSkew: (int listIndex, Interval range) {
|
| - flushMatching();
|
| - handleSkew(listIndex, range);
|
| - },
|
| - handleMatched: (List<int> indices) {
|
| - if (currentMatchedIntervals == null) {
|
| - currentMatchedIntervals = [
|
| - new Interval(indices[0], indices[0] + 1),
|
| - new Interval(indices[1], indices[1] + 1)];
|
| - } else {
|
| - currentMatchedIntervals[0] =
|
| - new Interval(currentMatchedIntervals[0].from, indices[0] + 1);
|
| - currentMatchedIntervals[1] =
|
| - new Interval(currentMatchedIntervals[1].from, indices[1] + 1);
|
| - }
|
| - },
|
| - handleUnmatched: (List<int> indices) {
|
| - flushMatching();
|
| - addBoth(indices);
|
| - });
|
| -
|
| - flushMatching();
|
| - }
|
| -
|
| - /// Output the lines of the library blocks in [childRange] in
|
| - /// `structures[index]`, padding the other column with empty lines.
|
| - void addBlock(int index, Interval childRange) {
|
| - handleSkew(index, structures[index].getChildInterval(childRange));
|
| - }
|
| -
|
| - /// Output the members of the [classes] aligned.
|
| - void addMatchingClasses(List<LibraryClass> classes) {
|
| - withMatching(() {
|
| - addBothLines(classes.map((c) => c.header).toList());
|
| - });
|
| - align(classes[0].children, classes[1].children,
|
| - match: (a, b) => a.name == b.name,
|
| - handleSkew: (int listIndex, Interval childRange) {
|
| - handleSkew(listIndex,
|
| - classes[listIndex].getChildInterval(childRange));
|
| - },
|
| - handleMatched: (List<int> indices) {
|
| - List<Interval> intervals = [
|
| - classes[0].getChild(indices[0]).interval,
|
| - classes[1].getChild(indices[1]).interval];
|
| - withMatching(() {
|
| - addBothLines(intervals);
|
| - });
|
| - },
|
| - handleUnmatched: (List<int> indices) {
|
| - List<Interval> intervals = [
|
| - classes[0].getChild(indices[0]).interval,
|
| - classes[1].getChild(indices[1]).interval];
|
| - addBothLines(intervals);
|
| - });
|
| - withMatching(() {
|
| - addBothLines(classes.map((c) => c.footer).toList());
|
| - });
|
| - }
|
| -
|
| - /// Output the library blocks in [indices] from the corresponding
|
| - /// [OutputStructure]s, aligning their content.
|
| - void addMatchingBlocks(List<int> indices) {
|
| - List<LibraryBlock> blocks = [
|
| - structures[0].getChild(indices[0]),
|
| - structures[1].getChild(indices[1])];
|
| -
|
| - withMatching(() {
|
| - addBothLines(blocks.map((b) => b.header).toList());
|
| - });
|
| - align(blocks[0].children, blocks[1].children,
|
| - match: (a, b) => a.name == b.name,
|
| - handleSkew: (int listIndex, Interval childRange) {
|
| - handleSkew(listIndex, blocks[listIndex].getChildInterval(childRange));
|
| - },
|
| - handleMatched: (List<int> indices) {
|
| - List<BasicEntity> entities = [
|
| - blocks[0].getChild(indices[0]),
|
| - blocks[1].getChild(indices[1])];
|
| - if (entities.every((e) => e is LibraryClass)) {
|
| - addMatchingClasses(entities);
|
| + sb.write('<tr>');
|
| + for (int index = 0; index < 3; index++) {
|
| + sb.write('''<td class="$className column$index">''');
|
| + List<HtmlPart> lines = block.getColumn(index);
|
| + if (lines.isNotEmpty) {
|
| + for (HtmlPart line in lines) {
|
| + sb.write('<p class="line">');
|
| + if (index < printContexts.length) {
|
| + line.printHtmlOn(sb, printContexts[index]);
|
| } else {
|
| - withMatching(() {
|
| - addBothLines(entities.map((e) => e.interval).toList());
|
| - });
|
| + line.printHtmlOn(sb, new HtmlPrintContext());
|
| }
|
| - },
|
| - handleUnmatched: (List<int> indices) {
|
| - List<Interval> intervals = [
|
| - blocks[0].getChild(indices[0]).interval,
|
| - blocks[1].getChild(indices[1]).interval];
|
| - addBothLines(intervals);
|
| - });
|
| - withMatching(() {
|
| - addBothLines(blocks.map((b) => b.footer).toList());
|
| - });
|
| - }
|
| -
|
| - /// Output the lines of the blocks in [indices] from the corresponding
|
| - /// [OutputStructure]s.
|
| - void addUnmatchedBlocks(List<int> indices) {
|
| - List<LibraryBlock> blocks = [
|
| - structures[0].getChild(indices[0]),
|
| - structures[1].getChild(indices[1])];
|
| - addBothLines([blocks[0].interval, blocks[1].interval]);
|
| + sb.write('</p>');
|
| + }
|
| + }
|
| + sb.write('''</td>''');
|
| + }
|
| + sb.write('</tr>');
|
| }
|
|
|
| -
|
| - addRaw(structures[0].header, structures[1].header);
|
| -
|
| - align(structures[0].children,
|
| - structures[1].children,
|
| - match: (a, b) => a.name == b.name,
|
| - handleSkew: addBlock,
|
| - handleMatched: addMatchingBlocks,
|
| - handleUnmatched: addUnmatchedBlocks);
|
| -
|
| - addRaw(structures[0].footer, structures[1].footer);
|
| -
|
| - addCell(htmlPartsToString(outputLines[0], inputLines[0]));
|
| - addCell(htmlPartsToString(outputLines[1], inputLines[1]));
|
| -
|
| sb.write('''</tr><tr>''');
|
| - addCell(result1.coverage.getCoverageReport());
|
| - addCell(result2.coverage.getCoverageReport());
|
| +
|
| + addCell(outputs[0].coverage);
|
| + addCell(outputs[1].coverage);
|
|
|
| sb.write('''
|
| -</tr></table>
|
| +</table>
|
| </body>
|
| </html>
|
| ''');
|
| @@ -373,515 +391,178 @@ main(List<String> args) async {
|
| print('Diff generated in $out');
|
| }
|
|
|
| -/// Align the content of [list1] and [list2].
|
| -///
|
| -/// If provided, [range1] and [range2] aligned the subranges of [list1] and
|
| -/// [list2], otherwise the whole lists are aligned.
|
| -///
|
| -/// If provided, [match] determines the equality between members of [list1] and
|
| -/// [list2], otherwise `==` is used.
|
| -///
|
| -/// [handleSkew] is called when a subrange of one list is not found in the
|
| -/// other.
|
| -///
|
| -/// [handleMatched] is called when two indices match up.
|
| -///
|
| -/// [handleUnmatched] is called when two indices don't match up (none are found
|
| -/// in the other list).
|
| -void align(List list1,
|
| - List list2,
|
| - {Interval range1,
|
| - Interval range2,
|
| - bool match(a, b),
|
| - void handleSkew(int listIndex, Interval range),
|
| - void handleMatched(List<int> indices),
|
| - void handleUnmatched(List<int> indices)}) {
|
| - if (match == null) {
|
| - match = (a, b) => a == b;
|
| - }
|
| -
|
| - if (range1 == null) {
|
| - range1 = new Interval(0, list1.length);
|
| - }
|
| - if (range2 == null) {
|
| - range2 = new Interval(0, list2.length);
|
| - }
|
| -
|
| - Interval findInOther(
|
| - List thisLines, Interval thisRange,
|
| - List otherLines, Interval otherRange) {
|
| - for (int index = otherRange.from; index < otherRange.to; index++) {
|
| - if (match(thisLines[thisRange.from], otherLines[index])) {
|
| - int offset = 1;
|
| - while (thisRange.from + offset < thisRange.to &&
|
| - otherRange.from + offset < otherRange.to &&
|
| - match(thisLines[thisRange.from + offset],
|
| - otherLines[otherRange.from + offset])) {
|
| - offset++;
|
| - }
|
| - return new Interval(index, index + offset);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - int start1 = range1.from;
|
| - int end1 = range1.to;
|
| - int start2 = range2.from;
|
| - int end2 = range2.to;
|
| -
|
| - const int ALIGN1 = -1;
|
| - const int UNMATCHED = 0;
|
| - const int ALIGN2 = 1;
|
| +class CodeLinesResult {
|
| + final List<CodeLine> codeLines;
|
| + final Coverage coverage;
|
| + final Map<int, Element> elementMap;
|
| + final SourceFileManager sourceFileManager;
|
|
|
| - while (start1 < end1 && start2 < end2) {
|
| - if (match(list1[start1], list2[start2])) {
|
| - handleMatched([start1++, start2++]);
|
| - } else {
|
| - Interval subrange1 = new Interval(start1, end1);
|
| - Interval subrange2 = new Interval(start2, end2);
|
| - Interval element2inList1 =
|
| - findInOther(list1, subrange1, list2, subrange2);
|
| - Interval element1inList2 =
|
| - findInOther(list2, subrange2, list1, subrange1);
|
| - int choice = 0;
|
| - if (element2inList1 != null) {
|
| - if (element1inList2 != null) {
|
| - if (element1inList2.length > 1 && element2inList1.length > 1) {
|
| - choice =
|
| - element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
|
| - } else if (element2inList1.length > 1) {
|
| - choice = ALIGN2;
|
| - } else if (element1inList2.length > 1) {
|
| - choice = ALIGN1;
|
| - } else {
|
| - choice =
|
| - element2inList1.from < element1inList2.from ? ALIGN2 : ALIGN1;
|
| - }
|
| - } else {
|
| - choice = ALIGN2;
|
| - }
|
| - } else if (element1inList2 != null) {
|
| - choice = ALIGN1;
|
| - }
|
| - switch (choice) {
|
| - case ALIGN1:
|
| - handleSkew(0, new Interval(start1, element1inList2.from));
|
| - start1 = element1inList2.from;
|
| - break;
|
| - case ALIGN2:
|
| - handleSkew(1, new Interval(start2, element2inList1.from));
|
| - start2 = element2inList1.from;
|
| - break;
|
| - case UNMATCHED:
|
| - handleUnmatched([start1++, start2++]);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - if (start1 < end1) {
|
| - handleSkew(0, new Interval(start1, end1));
|
| - }
|
| - if (start2 < end2) {
|
| - handleSkew(1, new Interval(start2, end2));
|
| - }
|
| + CodeLinesResult(this.codeLines, this.coverage,
|
| + this.elementMap, this.sourceFileManager);
|
| }
|
|
|
| -// Constants used to identify the subsection of the JavaScript output. These
|
| -// are specifically for the unminified full_emitter output.
|
| -const String HEAD = ' var dart = [';
|
| -const String TAIL = ' }], ';
|
| -const String END = ' setupProgram(dart';
|
| -
|
| -final RegExp TOP_LEVEL_VALUE = new RegExp(r'^ (".+?"):');
|
| -final RegExp TOP_LEVEL_FUNCTION =
|
| - new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
|
| -final RegExp TOP_LEVEL_CLASS = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
|
| -
|
| -final RegExp MEMBER_VALUE = new RegExp(r'^ (".+?"):');
|
| -final RegExp MEMBER_FUNCTION =
|
| - new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?function');
|
| -final RegExp MEMBER_OBJECT = new RegExp(r'^ ([a-zA-Z0-9_$]+): \[?\{');
|
| -
|
| -/// Subrange of the JavaScript output.
|
| -abstract class OutputEntity {
|
| - Interval get interval;
|
| - Interval get header;
|
| - Interval get footer;
|
| +/// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
|
| +Future<CodeLinesResult> computeCodeLines(
|
| + List<String> options,
|
| + String filename,
|
| + {bool addAnnotations: true}) async {
|
| + SourceMapProcessor processor = new SourceMapProcessor(filename);
|
| + SourceMaps sourceMaps =
|
| + await processor.process(options, perElement: true, forMain: true);
|
|
|
| - List<OutputEntity> get children;
|
| + const int WITH_SOURCE_INFO = 0;
|
| + const int WITHOUT_SOURCE_INFO = 1;
|
| + const int ADDITIONAL_SOURCE_INFO = 2;
|
| + const int UNUSED_SOURCE_INFO = 3;
|
|
|
| - Interval getChildInterval(Interval childIndex) {
|
| - return new Interval(
|
| - children[childIndex.from].interval.from,
|
| - children[childIndex.to - 1].interval.to);
|
| + SourceMapInfo info = sourceMaps.mainSourceMapInfo;
|
|
|
| - }
|
| + List<CodeLine> codeLines;
|
| + Coverage coverage = new Coverage();
|
| + List<Annotation> annotations = <Annotation>[];
|
|
|
| - OutputEntity getChild(int index) {
|
| - return children[index];
|
| + void addAnnotation(int id, int offset, String title) {
|
| + annotations.add(new Annotation(id, offset, title));
|
| }
|
| -}
|
| -
|
| -/// The whole JavaScript output.
|
| -class OutputStructure extends OutputEntity {
|
| - final List<CodeLine> lines;
|
| - final int headerEnd;
|
| - final int footerStart;
|
| - final List<LibraryBlock> children;
|
| -
|
| - OutputStructure(
|
| - this.lines,
|
| - this.headerEnd,
|
| - this.footerStart,
|
| - this.children);
|
|
|
| - Interval get interval => new Interval(0, lines.length);
|
| -
|
| - Interval get header => new Interval(0, headerEnd);
|
| -
|
| - Interval get footer => new Interval(footerStart, lines.length);
|
| -
|
| - /// Compute the structure of the JavaScript [lines].
|
| - static OutputStructure parse(List<CodeLine> lines) {
|
| -
|
| - int findHeaderStart(List<CodeLine> lines) {
|
| - int index = 0;
|
| - for (CodeLine line in lines) {
|
| - if (line.code.startsWith(HEAD)) {
|
| - return index;
|
| - }
|
| - index++;
|
| - }
|
| - return lines.length;
|
| - }
|
| + String code = info.code;
|
| + TraceGraph graph = createTraceGraph(info, coverage);
|
| + if (addAnnotations) {
|
| + Set<js.Node> mappedNodes = new Set<js.Node>();
|
|
|
| - int findHeaderEnd(int start, List<CodeLine> lines) {
|
| - int index = start;
|
| - for (CodeLine line in lines.skip(start)) {
|
| - if (line.code.startsWith(END)) {
|
| - return index;
|
| - }
|
| - index++;
|
| - }
|
| - return lines.length;
|
| - }
|
| + void addSourceLocations(
|
| + int kind, int offset, List<SourceLocation> locations, String prefix) {
|
|
|
| - String readHeader(CodeLine line) {
|
| - String code = line.code;
|
| - String ssaLineHeader;
|
| - if (code.startsWith(HEAD)) {
|
| - return code.substring(HEAD.length);
|
| - } else if (code.startsWith(TAIL)) {
|
| - return code.substring(TAIL.length);
|
| - }
|
| - return null;
|
| + addAnnotation(kind, offset,
|
| + '${prefix}${locations
|
| + .where((l) => l != null)
|
| + .map((l) => l.shortText)
|
| + .join('\n')}');
|
| }
|
|
|
| - List<LibraryBlock> computeHeaderMap(
|
| - List<CodeLine> lines, int start, int end) {
|
| - List<LibraryBlock> libraryBlocks = <LibraryBlock>[];
|
| - LibraryBlock current;
|
| - for (int index = start; index < end; index++) {
|
| - String header = readHeader(lines[index]);
|
| - if (header != null) {
|
| - if (current != null) {
|
| - current.to = index;
|
| - }
|
| - libraryBlocks.add(current = new LibraryBlock(header, index));
|
| - }
|
| + bool addSourceLocationsForNode(int kind, js.Node node, String prefix) {
|
| + Map<int, List<SourceLocation>> locations = info.nodeMap[node];
|
| + if (locations == null || locations.isEmpty) {
|
| + return false;
|
| }
|
| - if (current != null) {
|
| - current.to = end;
|
| - }
|
| - return libraryBlocks;
|
| + locations.forEach(
|
| + (int offset, List<SourceLocation> locations) {
|
| + addSourceLocations(kind, offset, locations,
|
| + '${prefix}\n${truncate(nodeToString(node), 80)}\n');
|
| + });
|
| + mappedNodes.add(node);
|
| + return true;
|
| }
|
|
|
| - int headerEnd = findHeaderStart(lines);
|
| - int footerStart = findHeaderEnd(headerEnd, lines);
|
| - List<LibraryBlock> libraryBlocks =
|
| - computeHeaderMap(lines, headerEnd, footerStart);
|
| - for (LibraryBlock block in libraryBlocks) {
|
| - block.preprocess(lines);
|
| - }
|
|
|
| - return new OutputStructure(
|
| - lines, headerEnd, footerStart, libraryBlocks);
|
| - }
|
| -}
|
| -
|
| -abstract class AbstractEntity extends OutputEntity {
|
| - final String name;
|
| - final int from;
|
| - int to;
|
| -
|
| - AbstractEntity(this.name, this.from);
|
| -
|
| - Interval get interval => new Interval(from, to);
|
| -}
|
| -
|
| -/// A block defining the content of a Dart library.
|
| -class LibraryBlock extends AbstractEntity {
|
| - List<BasicEntity> children = <BasicEntity>[];
|
| - int get headerEnd => from + 2;
|
| - int get footerStart => to - 1;
|
| -
|
| - LibraryBlock(String name, int from) : super(name, from);
|
| -
|
| - Interval get header => new Interval(from, headerEnd);
|
| -
|
| - Interval get footer => new Interval(footerStart, to);
|
| -
|
| - void preprocess(List<CodeLine> lines) {
|
| - int index = headerEnd;
|
| - BasicEntity current;
|
| - while (index < footerStart) {
|
| - String line = lines[index].code;
|
| - BasicEntity next;
|
| - Match matchFunction = TOP_LEVEL_FUNCTION.firstMatch(line);
|
| - if (matchFunction != null) {
|
| - next = new BasicEntity(matchFunction.group(1), index);
|
| - } else {
|
| - Match matchClass = TOP_LEVEL_CLASS.firstMatch(line);
|
| - if (matchClass != null) {
|
| - next = new LibraryClass(matchClass.group(1), index);
|
| + for (TraceStep step in graph.steps) {
|
| + String title = '${step.id}:${step.kind}:${step.offset}';
|
| + if (!addSourceLocationsForNode(WITH_SOURCE_INFO, step.node, title)) {
|
| + int offset;
|
| + if (options.contains(USE_NEW_SOURCE_INFO)) {
|
| + offset = step.offset.subexpressionOffset;
|
| } else {
|
| - Match matchValue = TOP_LEVEL_VALUE.firstMatch(line);
|
| - if (matchValue != null) {
|
| - next = new BasicEntity(matchValue.group(1), index);
|
| - }
|
| + offset = info.jsCodePositions[step.node].startPosition;
|
| }
|
| - }
|
| - if (next != null) {
|
| - if (current != null) {
|
| - current.to = index;
|
| + if (offset != null) {
|
| + addAnnotation(WITHOUT_SOURCE_INFO, offset, title);
|
| }
|
| - children.add(current = next);
|
| - } else if (index == headerEnd) {
|
| - throw 'Failed to match first library block line:\n$line';
|
| }
|
| -
|
| - index++;
|
| - }
|
| - if (current != null) {
|
| - current.to = footerStart;
|
| }
|
| -
|
| - for (BasicEntity entity in children) {
|
| - entity.preprocess(lines);
|
| + for (js.Node node in info.nodeMap.nodes) {
|
| + if (!mappedNodes.contains(node)) {
|
| + addSourceLocationsForNode(ADDITIONAL_SOURCE_INFO, node, '');
|
| + }
|
| }
|
| + SourceLocationCollector collector = new SourceLocationCollector();
|
| + info.node.accept(collector);
|
| + collector.sourceLocations.forEach(
|
| + (js.Node node, List<SourceLocation> locations) {
|
| + if (!mappedNodes.contains(node)) {
|
| + int offset = info.jsCodePositions[node].startPosition;
|
| + addSourceLocations(UNUSED_SOURCE_INFO, offset, locations, '');
|
| + }
|
| + });
|
| }
|
| -}
|
| -
|
| -/// A simple member of a library or class.
|
| -class BasicEntity extends AbstractEntity {
|
| - BasicEntity(String name, int from) : super(name, from);
|
| -
|
| - Interval get header => new Interval(from, to);
|
| -
|
| - Interval get footer => new Interval(to, to);
|
|
|
| - List<OutputEntity> get children => const <OutputEntity>[];
|
| -
|
| - void preprocess(List<CodeLine> lines) {}
|
| -}
|
| -
|
| -/// A block defining a Dart class.
|
| -class LibraryClass extends BasicEntity {
|
| - List<BasicEntity> children = <BasicEntity>[];
|
| - int get headerEnd => from + 1;
|
| - int get footerStart => to - 1;
|
| -
|
| - LibraryClass(String name, int from) : super(name, from);
|
| -
|
| - Interval get header => new Interval(from, headerEnd);
|
| -
|
| - Interval get footer => new Interval(footerStart, to);
|
| -
|
| - void preprocess(List<CodeLine> lines) {
|
| - int index = headerEnd;
|
| - BasicEntity current;
|
| - while (index < footerStart) {
|
| - String line = lines[index].code;
|
| - BasicEntity next;
|
| - Match matchFunction = MEMBER_FUNCTION.firstMatch(line);
|
| - if (matchFunction != null) {
|
| - next = new BasicEntity(matchFunction.group(1), index);
|
| - } else {
|
| - Match matchClass = MEMBER_OBJECT.firstMatch(line);
|
| - if (matchClass != null) {
|
| - next = new BasicEntity(matchClass.group(1), index);
|
| - } else {
|
| - Match matchValue = MEMBER_VALUE.firstMatch(line);
|
| - if (matchValue != null) {
|
| - next = new BasicEntity(matchValue.group(1), index);
|
| + StringSourceFile sourceFile = new StringSourceFile.fromName(filename, code);
|
| + Map<int, Element> elementMap = <int, Element>{};
|
| + sourceMaps.elementSourceMapInfos.forEach(
|
| + (Element element, SourceMapInfo info) {
|
| + CodePosition position = info.jsCodePositions[info.node];
|
| + elementMap[sourceFile.getLine(position.startPosition)] = element;
|
| + });
|
| +
|
| + codeLines = convertAnnotatedCodeToCodeLines(
|
| + code,
|
| + annotations,
|
| + colorScheme: new CustomColorScheme(
|
| + single: (int id) {
|
| + if (id == WITH_SOURCE_INFO) {
|
| + return WITH_SOURCE_INFO_STYLE;
|
| + } else if (id == ADDITIONAL_SOURCE_INFO) {
|
| + return ADDITIONAL_SOURCE_INFO_STYLE;
|
| + } else if (id == UNUSED_SOURCE_INFO) {
|
| + return UNUSED_SOURCE_INFO_STYLE;
|
| }
|
| + return WITHOUT_SOURCE_INFO_STYLE;
|
| + },
|
| + multi: (List ids) {
|
| + if (ids.contains(WITH_SOURCE_INFO)) {
|
| + return WITH_SOURCE_INFO_STYLE;
|
| + } else if (ids.contains(ADDITIONAL_SOURCE_INFO)) {
|
| + return ADDITIONAL_SOURCE_INFO_STYLE;
|
| + } else if (ids.contains(UNUSED_SOURCE_INFO)) {
|
| + return UNUSED_SOURCE_INFO_STYLE;
|
| + }
|
| + return WITHOUT_SOURCE_INFO_STYLE;
|
| }
|
| - }
|
| - if (next != null) {
|
| - if (current != null) {
|
| - current.to = index;
|
| - }
|
| - children.add(current = next);
|
| - } else if (index == headerEnd) {
|
| - throw 'Failed to match first library block line:\n$line';
|
| - }
|
| -
|
| - index++;
|
| - }
|
| - if (current != null) {
|
| - current.to = footerStart;
|
| - }
|
| - }
|
| -}
|
| -
|
| -class Interval {
|
| - final int from;
|
| - final int to;
|
| -
|
| - const Interval(this.from, this.to);
|
| -
|
| - int get length => to - from;
|
| -}
|
| -
|
| -class HtmlPart {
|
| - void printHtmlOn(StringBuffer buffer) {}
|
| -}
|
| -
|
| -class ConstHtmlPart implements HtmlPart {
|
| - final String html;
|
| -
|
| - const ConstHtmlPart(this.html);
|
| -
|
| - @override
|
| - void printHtmlOn(StringBuffer buffer) {
|
| - buffer.write(html);
|
| - }
|
| + ));
|
| + return new CodeLinesResult(codeLines, coverage, elementMap,
|
| + sourceMaps.sourceFileManager);
|
| }
|
|
|
| -class CodeLineHtmlPart implements HtmlPart {
|
| - final CodeLine line;
|
| -
|
| - CodeLineHtmlPart(this.line);
|
| +/// Visitor that computes a map from [js.Node]s to all attached source
|
| +/// locations.
|
| +class SourceLocationCollector extends js.BaseVisitor {
|
| + Map<js.Node, List<SourceLocation>> sourceLocations =
|
| + <js.Node, List<SourceLocation>>{};
|
|
|
| @override
|
| - void printHtmlOn(StringBuffer buffer, [int lineNoWidth]) {
|
| - line.printHtmlOn(buffer, lineNoWidth);
|
| - }
|
| -}
|
| -
|
| -/// Convert [parts] to an HTML string while checking invariants for [lines].
|
| -String htmlPartsToString(List<HtmlPart> parts, List<CodeLine> lines) {
|
| - int lineNoWidth;
|
| - if (lines.isNotEmpty) {
|
| - lineNoWidth = '${lines.last.lineNo + 1}'.length;
|
| - }
|
| - StringBuffer buffer = new StringBuffer();
|
| - int expectedLineNo = 0;
|
| - for (HtmlPart part in parts) {
|
| - if (part is CodeLineHtmlPart) {
|
| - if (part.line.lineNo != expectedLineNo) {
|
| - print('Expected line no $expectedLineNo, found ${part.line.lineNo}');
|
| - if (part.line.lineNo < expectedLineNo) {
|
| - print('Duplicate lines:');
|
| - int index = part.line.lineNo;
|
| - while (index <= expectedLineNo) {
|
| - print(lines[index++].code);
|
| - }
|
| - } else {
|
| - print('Missing lines:');
|
| - int index = expectedLineNo;
|
| - while (index <= part.line.lineNo) {
|
| - print(lines[index++].code);
|
| - }
|
| - }
|
| - expectedLineNo = part.line.lineNo;
|
| - }
|
| - expectedLineNo++;
|
| - part.printHtmlOn(buffer, lineNoWidth);
|
| - } else {
|
| - part.printHtmlOn(buffer);
|
| + visitNode(js.Node node) {
|
| + SourceInformation sourceInformation = node.sourceInformation;
|
| + if (sourceInformation != null) {
|
| + sourceLocations[node] = sourceInformation.sourceLocations;
|
| }
|
| + node.visitChildren(this);
|
| }
|
| - return buffer.toString();
|
| }
|
|
|
| -class CodeLinesResult {
|
| - final List<CodeLine> codeLines;
|
| - final Coverage coverage;
|
| -
|
| - CodeLinesResult(this.codeLines, this.coverage);
|
| -}
|
| -
|
| -/// Compute [CodeLine]s and [Coverage] for [filename] using the given [options].
|
| -Future<CodeLinesResult> computeCodeLines(
|
| - List<String> options,
|
| - String filename,
|
| - {bool addAnnotations: true}) async {
|
| - SourceMapProcessor processor = new SourceMapProcessor(filename);
|
| - List<SourceMapInfo> sourceMapInfoList =
|
| - await processor.process(options, perElement: false);
|
| -
|
| - const int WITH_SOURCE_INFO = 0;
|
| - const int WITHOUT_SOURCE_INFO = 1;
|
| - const int ADDITIONAL_SOURCE_INFO = 2;
|
| -
|
| - for (SourceMapInfo info in sourceMapInfoList) {
|
| - if (info.element != null) continue;
|
| -
|
| - List<CodeLine> codeLines;
|
| - Coverage coverage = new Coverage();
|
| - List<Annotation> annotations = <Annotation>[];
|
| -
|
| - String code = info.code;
|
| - TraceGraph graph = createTraceGraph(info, coverage);
|
| - if (addAnnotations) {
|
| - Set<js.Node> mappedNodes = new Set<js.Node>();
|
| - for (TraceStep step in graph.steps) {
|
| - int offset;
|
| - if (options.contains(USE_NEW_SOURCE_INFO)) {
|
| - offset = step.offset.subexpressionOffset;
|
| - } else {
|
| - offset = info.jsCodePositions[step.node].startPosition;
|
| - }
|
| - if (offset != null) {
|
| - int id = step.sourceLocation != null
|
| - ? WITH_SOURCE_INFO : WITHOUT_SOURCE_INFO;
|
| - annotations.add(
|
| - new Annotation(id, offset, null));
|
| - }
|
| - }
|
| - if (!options.contains(USE_NEW_SOURCE_INFO)) {
|
| - for (js.Node node in info.nodeMap.nodes) {
|
| - if (!mappedNodes.contains(node)) {
|
| - int offset = info.jsCodePositions[node].startPosition;
|
| - annotations.add(
|
| - new Annotation(ADDITIONAL_SOURCE_INFO, offset, null));
|
| - }
|
| - }
|
| - }
|
| +/// Compute a [CodeSource] for source span of [element].
|
| +CodeSource codeSourceFromElement(Element element) {
|
| + CodeKind kind;
|
| + Uri uri;
|
| + String name;
|
| + int begin;
|
| + int end;
|
| + if (element.isLibrary) {
|
| + LibraryElement library = element;
|
| + kind = CodeKind.LIBRARY;
|
| + name = library.libraryOrScriptName;
|
| + uri = library.entryCompilationUnit.script.resourceUri;
|
| + } else if (element.isClass) {
|
| + kind = CodeKind.CLASS;
|
| + name = element.name;
|
| + uri = element.compilationUnit.script.resourceUri;
|
| + } else {
|
| + AstElement astElement = element.implementation;
|
| + kind = CodeKind.MEMBER;
|
| + uri = astElement.compilationUnit.script.resourceUri;
|
| + name = computeElementNameForSourceMaps(astElement);
|
| + if (astElement.hasNode) {
|
| + begin = astElement.node.getBeginToken().charOffset;
|
| + end = astElement.node.getEndToken().charEnd;
|
| }
|
| - codeLines = convertAnnotatedCodeToCodeLines(
|
| - code,
|
| - annotations,
|
| - colorScheme: new CustomColorScheme(
|
| - single: (int id) {
|
| - if (id == WITH_SOURCE_INFO) {
|
| - return WITH_SOURCE_INFO_STYLE;
|
| - } else if (id == ADDITIONAL_SOURCE_INFO) {
|
| - return ADDITIONAL_SOURCE_INFO_STYLE;
|
| - }
|
| - return WITHOUT_SOURCE_INFO_STYLE;
|
| - },
|
| - multi: (List ids) {
|
| - if (ids.contains(WITH_SOURCE_INFO)) {
|
| - return WITH_SOURCE_INFO_STYLE;
|
| - } else if (ids.contains(ADDITIONAL_SOURCE_INFO)) {
|
| - return ADDITIONAL_SOURCE_INFO_STYLE;
|
| - }
|
| - return WITHOUT_SOURCE_INFO_STYLE;
|
| - }
|
| - ));
|
| - return new CodeLinesResult(codeLines, coverage);
|
| }
|
| -}
|
| + return new CodeSource(kind, uri, name, begin, end);
|
| +}
|
|
|