| Index: tests/compiler/dart2js/sourcemaps/html_parts.dart
|
| diff --git a/tests/compiler/dart2js/sourcemaps/html_parts.dart b/tests/compiler/dart2js/sourcemaps/html_parts.dart
|
| index 6ddf95e6bcb7e4649aeb61baff1aebd5006502ce..4d06f98855ad1598c3837aa9c3c6b11294049d07 100644
|
| --- a/tests/compiler/dart2js/sourcemaps/html_parts.dart
|
| +++ b/tests/compiler/dart2js/sourcemaps/html_parts.dart
|
| @@ -6,13 +6,130 @@ library sourcemap.html_parts;
|
|
|
| import 'sourcemap_html_helper.dart';
|
|
|
| +class Annotation {
|
| + final id;
|
| + final int codeOffset;
|
| + final String title;
|
| + final data;
|
| +
|
| + Annotation(this.id, this.codeOffset, this.title, {this.data});
|
| +}
|
| +
|
| +typedef bool AnnotationFilter(Annotation annotation);
|
| +typedef AnnotationData AnnotationDataFunction(
|
| + Iterable<Annotation> annotations,
|
| + {bool forSpan});
|
| +typedef LineData LineDataFunction(lineAnnotation);
|
| +
|
| +bool includeAllAnnotation(Annotation annotation) => true;
|
| +
|
| +class LineData {
|
| + final String lineClass;
|
| + final String lineNumberClass;
|
| +
|
| + const LineData({
|
| + this.lineClass: 'line',
|
| + this.lineNumberClass: 'lineNumber'});
|
| +}
|
| +
|
| +class AnnotationData {
|
| + final String tag;
|
| + final Map<String, String> properties;
|
| +
|
| + const AnnotationData({
|
| + this.tag: 'a',
|
| + this.properties: const <String, String>{}});
|
| +
|
| + int get hashCode => tag.hashCode * 13 + properties.hashCode * 19;
|
| +
|
| + bool operator ==(other) {
|
| + if (identical(this, other)) return true;
|
| + if (other is! AnnotationData) return false;
|
| + return tag == other.tag &&
|
| + properties.length == other.properties.length &&
|
| + properties.keys.every((k) => properties[k] == other.properties[k]);
|
| + }
|
| +}
|
| +
|
| +AnnotationDataFunction createAnnotationDataFunction(
|
| + {CssColorScheme colorScheme: const SingleColorScheme(),
|
| + ElementScheme elementScheme: const ElementScheme()}) {
|
| + return (Iterable<Annotation> annotations, {bool forSpan}) {
|
| + return getAnnotationDataFromSchemes(
|
| + annotations,
|
| + forSpan: forSpan,
|
| + colorScheme: colorScheme,
|
| + elementScheme: elementScheme);
|
| + };
|
| +}
|
| +
|
| +LineData getDefaultLineData(data) => const LineData();
|
| +
|
| +AnnotationData getAnnotationDataFromSchemes(
|
| + Iterable<Annotation> annotations,
|
| + {bool forSpan,
|
| + CssColorScheme colorScheme: const SingleColorScheme(),
|
| + ElementScheme elementScheme: const ElementScheme()}) {
|
| + if (colorScheme.showLocationAsSpan != forSpan) return null;
|
| + Map<String, String> data = <String, String>{};
|
| + var id;
|
| + if (annotations.length == 1) {
|
| + Annotation annotation = annotations.single;
|
| + if (annotation != null) {
|
| + id = annotation.id;
|
| + data['style'] = colorScheme.singleLocationToCssColor(id);
|
| + data['title'] = annotation.title;
|
| + }
|
| + } else {
|
| + id = annotations.first.id;
|
| + List ids = [];
|
| + for (Annotation annotation in annotations) {
|
| + ids.add(annotation.id);
|
| + }
|
| + data['style'] = colorScheme.multiLocationToCssColor(ids);
|
| + data['title'] = annotations.map((l) => l.title).join(',');
|
| + }
|
| + if (id != null) {
|
| + Set ids = annotations.map((l) => l.id).toSet();
|
| + data['tag'] = 'a';
|
| + data['name'] = elementScheme.getName(id, ids);
|
| + data['href'] = elementScheme.getHref(id, ids);
|
| + data['onclick'] = elementScheme.onClick(id, ids);
|
| + data['onmouseover'] = elementScheme.onMouseOver(id, ids);
|
| + data['onmouseout'] = elementScheme.onMouseOut(id, ids);
|
| + return new AnnotationData(
|
| + properties: data);
|
| + }
|
| + return null;
|
| +}
|
| +
|
| class HtmlPrintContext {
|
| final int lineNoWidth;
|
| final bool usePre;
|
| + final AnnotationFilter includeAnnotation;
|
| + final AnnotationDataFunction getAnnotationData;
|
| + final LineDataFunction getLineData;
|
|
|
| HtmlPrintContext({
|
| this.lineNoWidth,
|
| - this.usePre: true});
|
| + this.usePre: true,
|
| + this.includeAnnotation: includeAllAnnotation,
|
| + this.getAnnotationData: getAnnotationDataFromSchemes,
|
| + this.getLineData: getDefaultLineData});
|
| +
|
| + HtmlPrintContext from({
|
| + int lineNoWidth,
|
| + bool usePre,
|
| + AnnotationFilter includeAnnotation,
|
| + AnnotationDataFunction getAnnotationData,
|
| + LineDataFunction getLineData}) {
|
| + return new HtmlPrintContext(
|
| + lineNoWidth: lineNoWidth ?? this.lineNoWidth,
|
| + usePre: usePre ?? this.usePre,
|
| + includeAnnotation: includeAnnotation ?? this.includeAnnotation,
|
| + getAnnotationData: getAnnotationData ?? this.getAnnotationData,
|
| + getLineData: getLineData ?? this.getLineData);
|
| + }
|
| }
|
|
|
| enum HtmlPartKind {
|
| @@ -21,31 +138,36 @@ enum HtmlPartKind {
|
| CONST,
|
| NEWLINE,
|
| TEXT,
|
| - ANCHOR,
|
| + TAG,
|
| + LINE_NUMBER,
|
| }
|
|
|
| abstract class HtmlPart {
|
| - void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {}
|
| + void printHtmlOn(StringBuffer buffer, HtmlPrintContext context);
|
|
|
| - toJson();
|
| + HtmlPartKind get kind;
|
|
|
| - static HtmlPart fromJson(json) {
|
| + toJson(JsonStrategy strategy);
|
| +
|
| + static HtmlPart fromJson(json, JsonStrategy strategy) {
|
| if (json is String) {
|
| return new ConstHtmlPart(json);
|
| } else {
|
| switch (HtmlPartKind.values[json['kind']]) {
|
| case HtmlPartKind.LINE:
|
| - return HtmlLine.fromJson(json);
|
| + return HtmlLine.fromJson(json, strategy);
|
| case HtmlPartKind.CODE:
|
| - return CodeLine.fromJson(json);
|
| + return CodeLine.fromJson(json, strategy);
|
| case HtmlPartKind.CONST:
|
| - return ConstHtmlPart.fromJson(json);
|
| + return ConstHtmlPart.fromJson(json, strategy);
|
| case HtmlPartKind.NEWLINE:
|
| return const NewLine();
|
| case HtmlPartKind.TEXT:
|
| - return HtmlText.fromJson(json);
|
| - case HtmlPartKind.ANCHOR:
|
| - return AnchorHtmlPart.fromJson(json);
|
| + return HtmlText.fromJson(json, strategy);
|
| + case HtmlPartKind.TAG:
|
| + return TagPart.fromJson(json, strategy);
|
| + case HtmlPartKind.LINE_NUMBER:
|
| + return LineNumber.fromJson(json, strategy);
|
| }
|
| }
|
| }
|
| @@ -56,16 +178,18 @@ class ConstHtmlPart implements HtmlPart {
|
|
|
| const ConstHtmlPart(this.html);
|
|
|
| + HtmlPartKind get kind => HtmlPartKind.CONST;
|
| +
|
| @override
|
| void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| buffer.write(html);
|
| }
|
|
|
| - toJson() {
|
| - return {'kind': HtmlPartKind.CONST.index, 'html': html};
|
| + toJson(JsonStrategy strategy) {
|
| + return {'kind': kind.index, 'html': html};
|
| }
|
|
|
| - static ConstHtmlPart fromJson(Map json) {
|
| + static ConstHtmlPart fromJson(Map json, JsonStrategy strategy) {
|
| return new ConstHtmlPart(json['html']);
|
| }
|
| }
|
| @@ -73,6 +197,8 @@ class ConstHtmlPart implements HtmlPart {
|
| class NewLine implements HtmlPart {
|
| const NewLine();
|
|
|
| + HtmlPartKind get kind => HtmlPartKind.NEWLINE;
|
| +
|
| void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| if (context.usePre) {
|
| buffer.write('\n');
|
| @@ -81,8 +207,8 @@ class NewLine implements HtmlPart {
|
| }
|
| }
|
|
|
| - toJson() {
|
| - return {'kind': HtmlPartKind.NEWLINE.index};
|
| + toJson(JsonStrategy strategy) {
|
| + return {'kind': kind.index};
|
| }
|
| }
|
|
|
| @@ -91,90 +217,70 @@ class HtmlText implements HtmlPart {
|
|
|
| const HtmlText(this.text);
|
|
|
| + HtmlPartKind get kind => HtmlPartKind.TEXT;
|
| +
|
| void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| String escaped = escape(text);
|
| buffer.write(escaped);
|
| }
|
|
|
| - toJson() {
|
| - return {'kind': HtmlPartKind.TEXT.index, 'text': text};
|
| + toJson(JsonStrategy strategy) {
|
| + return {'kind': kind.index, 'text': text};
|
| }
|
|
|
| - static HtmlText fromJson(Map json) {
|
| + static HtmlText fromJson(Map json, JsonStrategy strategy) {
|
| return new HtmlText(json['text']);
|
| }
|
| }
|
|
|
| -class AnchorHtmlPart implements HtmlPart {
|
| - final String color;
|
| - final String name;
|
| - final String href;
|
| - final String title;
|
| - final String onclick;
|
| - final String onmouseover;
|
| - final String onmouseout;
|
| -
|
| - AnchorHtmlPart({
|
| - this.color,
|
| - this.name,
|
| - this.href,
|
| - this.title,
|
| - this.onclick,
|
| - this.onmouseover,
|
| - this.onmouseout});
|
| +class TagPart implements HtmlPart {
|
| + final String tag;
|
| + final Map<String, String> properties;
|
| + final List<HtmlPart> content;
|
| +
|
| + TagPart(
|
| + this.tag,
|
| + {this.properties: const <String, String>{},
|
| + this.content: const <HtmlPart>[]});
|
| +
|
| + HtmlPartKind get kind => HtmlPartKind.TAG;
|
|
|
| @override
|
| void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| - buffer.write('<a');
|
| - if (href != null) {
|
| - buffer.write(' href="${href}"');
|
| - }
|
| - if (name != null) {
|
| - buffer.write(' name="${name}"');
|
| - }
|
| - if (title != null) {
|
| - buffer.write(' title="${escape(title)}"');
|
| - }
|
| - buffer.write(' style="${color}"');
|
| - if (onclick != null) {
|
| - buffer.write(' onclick="${onclick}"');
|
| - }
|
| - if (onmouseover != null) {
|
| - buffer.write(' onmouseover="${onmouseover}"');
|
| - }
|
| - if (onmouseout != null) {
|
| - buffer.write(' onmouseout="${onmouseout}"');
|
| - }
|
| + buffer.write('<$tag');
|
| + properties.forEach((String key, String value) {
|
| + if (value != null) {
|
| + buffer.write(' $key="${value}"');
|
| + }
|
| + });
|
| buffer.write('>');
|
| + for (HtmlPart child in content) {
|
| + child.printHtmlOn(buffer, context);
|
| + }
|
| + buffer.write('</$tag>');
|
| }
|
|
|
| - toJson() {
|
| + toJson(JsonStrategy strategy) {
|
| return {
|
| - 'kind': HtmlPartKind.ANCHOR.index,
|
| - 'color': color,
|
| - 'name': name,
|
| - 'href': href,
|
| - 'title': title,
|
| - 'onclick': onclick,
|
| - 'onmouseover': onmouseover,
|
| - 'onmouseout': onmouseout};
|
| + 'kind': kind.index,
|
| + 'tag': tag,
|
| + 'properties': properties,
|
| + 'content': content.map((p) => p.toJson(strategy)).toList()};
|
| }
|
|
|
| - static AnchorHtmlPart fromJson(Map json) {
|
| - return new AnchorHtmlPart(
|
| - color: json['color'],
|
| - name: json['name'],
|
| - href: json['href'],
|
| - title: json['title'],
|
| - onclick: json['onclick'],
|
| - onmouseover: json['onmouseover'],
|
| - onmouseout: json['onmouseout']);
|
| + static TagPart fromJson(Map json, JsonStrategy strategy) {
|
| + return new TagPart(
|
| + json['tag'],
|
| + properties: json['properties'],
|
| + content: json['content'].map(HtmlPart.fromJson).toList());
|
| }
|
| }
|
|
|
| class HtmlLine implements HtmlPart {
|
| final List<HtmlPart> htmlParts = <HtmlPart>[];
|
|
|
| + HtmlPartKind get kind => HtmlPartKind.LINE;
|
| +
|
| @override
|
| void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) {
|
| for (HtmlPart part in htmlParts) {
|
| @@ -182,30 +288,148 @@ class HtmlLine implements HtmlPart {
|
| }
|
| }
|
|
|
| - Map toJson() {
|
| + Map toJson(JsonStrategy strategy) {
|
| return {
|
| - 'kind': HtmlPartKind.LINE.index,
|
| - 'html': htmlParts.map((p) => p.toJson()).toList(),
|
| + 'kind': kind.index,
|
| + 'html': htmlParts.map((p) => p.toJson(strategy)).toList(),
|
| };
|
| }
|
|
|
| - static CodeLine fromJson(Map json) {
|
| + static HtmlLine fromJson(Map json, JsonStrategy strategy) {
|
| HtmlLine line = new HtmlLine();
|
| - json['html'].forEach((part) => line.htmlParts.add(HtmlPart.fromJson(part)));
|
| + json['html']
|
| + .forEach((part) => line.htmlParts
|
| + .add(HtmlPart.fromJson(part, strategy)));
|
| return line;
|
| }
|
| }
|
|
|
| -class CodeLine extends HtmlLine {
|
| +class CodePart {
|
| + final List<Annotation> annotations;
|
| + final String subsequentCode;
|
| +
|
| + CodePart(this.annotations, this.subsequentCode);
|
| +
|
| + void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| + Iterable<Annotation> included =
|
| + annotations.where(context.includeAnnotation);
|
| +
|
| + List<HtmlPart> htmlParts = <HtmlPart>[];
|
| + if (included.isNotEmpty) {
|
| + AnnotationData annotationData =
|
| + context.getAnnotationData(included, forSpan: false);
|
| + AnnotationData annotationDataForSpan =
|
| + context.getAnnotationData(included, forSpan: true);
|
| +
|
| + String head = subsequentCode;
|
| + String tail = '';
|
| + if (subsequentCode.length > 1) {
|
| + head = subsequentCode.substring(0, 1);
|
| + tail = subsequentCode.substring(1);
|
| + }
|
| +
|
| + void addForSpan(AnnotationData data) {
|
| + htmlParts.add(new TagPart(
|
| + data.tag,
|
| + properties: data.properties,
|
| + content: [new HtmlText(subsequentCode)]));
|
| + }
|
| +
|
| + if (annotationData != null &&
|
| + annotationDataForSpan != null) {
|
| + htmlParts.add(new TagPart(
|
| + annotationDataForSpan.tag,
|
| + properties: annotationDataForSpan.properties,
|
| + content: [
|
| + new TagPart(
|
| + annotationData.tag,
|
| + properties: annotationData.properties,
|
| + content: [new HtmlText(head)]),
|
| + new HtmlText(tail)]));
|
| + } else if (annotationDataForSpan != null) {
|
| + htmlParts.add(new TagPart(
|
| + annotationDataForSpan.tag,
|
| + properties: annotationDataForSpan.properties,
|
| + content: [new HtmlText(subsequentCode)]));
|
| + } else if (annotationData != null) {
|
| + htmlParts.add(new TagPart(
|
| + annotationData.tag,
|
| + properties: annotationData.properties,
|
| + content: [new HtmlText(head)]));
|
| + htmlParts.add(new HtmlText(tail));
|
| + } else {
|
| + htmlParts.add(new HtmlText(subsequentCode));
|
| + }
|
| + } else {
|
| + htmlParts.add(new HtmlText(subsequentCode));
|
| + }
|
| +
|
| + for (HtmlPart part in htmlParts) {
|
| + part.printHtmlOn(buffer, context);
|
| + }
|
| + }
|
| +
|
| + Map toJson(JsonStrategy strategy) {
|
| + return {
|
| + 'annotations':
|
| + annotations.map((a) => strategy.encodeAnnotation(a)).toList(),
|
| + 'subsequentCode': subsequentCode,
|
| + };
|
| + }
|
| +
|
| + static CodePart fromJson(Map json, JsonStrategy strategy) {
|
| + return new CodePart(
|
| + json['annotations'].map((j) => strategy.decodeAnnotation(j)).toList(),
|
| + json['subsequentCode']);
|
| + }
|
| +}
|
| +
|
| +class LineNumber extends HtmlPart {
|
| + final int lineNo;
|
| + final lineAnnotation;
|
| +
|
| + LineNumber(this.lineNo, this.lineAnnotation);
|
| +
|
| + HtmlPartKind get kind => HtmlPartKind.LINE_NUMBER;
|
| +
|
| + @override
|
| + toJson(JsonStrategy strategy) {
|
| + return {
|
| + 'kind': kind.index,
|
| + 'lineNo': lineNo,
|
| + 'lineAnnotation': strategy.encodeLineAnnotation(lineAnnotation),
|
| + };
|
| + }
|
| +
|
| + static LineNumber fromJson(Map json, JsonStrategy strategy) {
|
| + return new LineNumber(
|
| + json['lineNo'],
|
| + strategy.decodeLineAnnotation(json['lineAnnotation']));
|
| + }
|
| +
|
| + @override
|
| + void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {
|
| + buffer.write(lineNumber(
|
| + lineNo,
|
| + width: context.lineNoWidth,
|
| + useNbsp: !context.usePre,
|
| + className: context.getLineData(lineAnnotation).lineNumberClass));
|
| + }
|
| +}
|
| +
|
| +class CodeLine extends HtmlPart {
|
| + final Uri uri;
|
| final int lineNo;
|
| final int offset;
|
| final StringBuffer codeBuffer = new StringBuffer();
|
| - final List<HtmlPart> htmlParts = <HtmlPart>[];
|
| - // TODO(johnniwinther): Make annotations serializable.
|
| + final List<CodePart> codeParts = <CodePart>[];
|
| final List<Annotation> annotations = <Annotation>[];
|
| + var lineAnnotation;
|
| String _code;
|
|
|
| - CodeLine(this.lineNo, this.offset);
|
| + CodeLine(this.lineNo, this.offset, {this.uri});
|
| +
|
| + HtmlPartKind get kind => HtmlPartKind.CODE;
|
|
|
| String get code {
|
| if (_code == null) {
|
| @@ -216,28 +440,74 @@ class CodeLine extends HtmlLine {
|
|
|
| @override
|
| void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) {
|
| - htmlBuffer.write(lineNumber(
|
| - lineNo, width: context.lineNoWidth, useNbsp: !context.usePre));
|
| - for (HtmlPart part in htmlParts) {
|
| + if (context.usePre) {
|
| + LineData lineData = context.getLineData(lineAnnotation);
|
| + htmlBuffer.write('<p class="${lineData.lineClass}">');
|
| + }
|
| + new LineNumber(lineNo, lineAnnotation).printHtmlOn(htmlBuffer, context);
|
| + for (CodePart part in codeParts) {
|
| part.printHtmlOn(htmlBuffer, context);
|
| }
|
| + const NewLine().printHtmlOn(htmlBuffer, context);
|
| + if (context.usePre) {
|
| + htmlBuffer.write('</p>');
|
| + }
|
| }
|
|
|
| - Map toJson() {
|
| + Map toJson(JsonStrategy strategy) {
|
| return {
|
| - 'kind': HtmlPartKind.CODE.index,
|
| + 'kind': kind.index,
|
| 'lineNo': lineNo,
|
| 'offset': offset,
|
| 'code': code,
|
| - 'html': htmlParts.map((p) => p.toJson()).toList(),
|
| + 'parts': codeParts.map((p) => p.toJson(strategy)).toList(),
|
| + 'annotations':
|
| + annotations.map((a) => strategy.encodeAnnotation(a)).toList(),
|
| + 'lineAnnotation': lineAnnotation != null
|
| + ? strategy.encodeLineAnnotation(lineAnnotation) : null,
|
| };
|
| }
|
|
|
| - static CodeLine fromJson(Map json) {
|
| - CodeLine line = new CodeLine(json['lineNo'], json['offset']);
|
| + static CodeLine fromJson(Map json, JsonStrategy strategy) {
|
| + CodeLine line = new CodeLine(
|
| + json['lineNo'],
|
| + json['offset'],
|
| + uri: json['uri'] != null ? Uri.parse(json['uri']) : null);
|
| line.codeBuffer.write(json['code']);
|
| - json['html'].forEach((part) => line.htmlParts.add(HtmlPart.fromJson(part)));
|
| + json['parts']
|
| + .forEach((part) => line.codeParts
|
| + .add(CodePart.fromJson(part, strategy)));
|
| + json['annotations']
|
| + .forEach((a) => line.annotations
|
| + .add(strategy.decodeAnnotation(a)));
|
| + line.lineAnnotation = json['lineAnnotation'] != null
|
| + ? strategy.decodeLineAnnotation(json['lineAnnotation']) : null;
|
| return line;
|
| }
|
| }
|
|
|
| +class JsonStrategy {
|
| + const JsonStrategy();
|
| +
|
| + Map encodeAnnotation(Annotation annotation) {
|
| + return {
|
| + 'id': annotation.id,
|
| + 'codeOffset': annotation.codeOffset,
|
| + 'title': annotation.title,
|
| + 'data': annotation.data,
|
| + };
|
| + }
|
| +
|
| + Annotation decodeAnnotation(Map json) {
|
| + return new Annotation(
|
| + json['id'],
|
| + json['codeOffset'],
|
| + json['title'],
|
| + data: json['data']);
|
| + }
|
| +
|
| +
|
| + encodeLineAnnotation(lineAnnotation) => lineAnnotation;
|
| +
|
| + decodeLineAnnotation(json) => json;
|
| +}
|
|
|