| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 sourcemap.html_parts; | 5 library sourcemap.html_parts; |
| 6 | 6 |
| 7 import 'sourcemap_html_helper.dart'; | 7 import 'sourcemap_html_helper.dart'; |
| 8 | 8 |
| 9 class Annotation { |
| 10 final id; |
| 11 final int codeOffset; |
| 12 final String title; |
| 13 final data; |
| 14 |
| 15 Annotation(this.id, this.codeOffset, this.title, {this.data}); |
| 16 } |
| 17 |
| 18 typedef bool AnnotationFilter(Annotation annotation); |
| 19 typedef AnnotationData AnnotationDataFunction( |
| 20 Iterable<Annotation> annotations, |
| 21 {bool forSpan}); |
| 22 typedef LineData LineDataFunction(lineAnnotation); |
| 23 |
| 24 bool includeAllAnnotation(Annotation annotation) => true; |
| 25 |
| 26 class LineData { |
| 27 final String lineClass; |
| 28 final String lineNumberClass; |
| 29 |
| 30 const LineData({ |
| 31 this.lineClass: 'line', |
| 32 this.lineNumberClass: 'lineNumber'}); |
| 33 } |
| 34 |
| 35 class AnnotationData { |
| 36 final String tag; |
| 37 final Map<String, String> properties; |
| 38 |
| 39 const AnnotationData({ |
| 40 this.tag: 'a', |
| 41 this.properties: const <String, String>{}}); |
| 42 |
| 43 int get hashCode => tag.hashCode * 13 + properties.hashCode * 19; |
| 44 |
| 45 bool operator ==(other) { |
| 46 if (identical(this, other)) return true; |
| 47 if (other is! AnnotationData) return false; |
| 48 return tag == other.tag && |
| 49 properties.length == other.properties.length && |
| 50 properties.keys.every((k) => properties[k] == other.properties[k]); |
| 51 } |
| 52 } |
| 53 |
| 54 AnnotationDataFunction createAnnotationDataFunction( |
| 55 {CssColorScheme colorScheme: const SingleColorScheme(), |
| 56 ElementScheme elementScheme: const ElementScheme()}) { |
| 57 return (Iterable<Annotation> annotations, {bool forSpan}) { |
| 58 return getAnnotationDataFromSchemes( |
| 59 annotations, |
| 60 forSpan: forSpan, |
| 61 colorScheme: colorScheme, |
| 62 elementScheme: elementScheme); |
| 63 }; |
| 64 } |
| 65 |
| 66 LineData getDefaultLineData(data) => const LineData(); |
| 67 |
| 68 AnnotationData getAnnotationDataFromSchemes( |
| 69 Iterable<Annotation> annotations, |
| 70 {bool forSpan, |
| 71 CssColorScheme colorScheme: const SingleColorScheme(), |
| 72 ElementScheme elementScheme: const ElementScheme()}) { |
| 73 if (colorScheme.showLocationAsSpan != forSpan) return null; |
| 74 Map<String, String> data = <String, String>{}; |
| 75 var id; |
| 76 if (annotations.length == 1) { |
| 77 Annotation annotation = annotations.single; |
| 78 if (annotation != null) { |
| 79 id = annotation.id; |
| 80 data['style'] = colorScheme.singleLocationToCssColor(id); |
| 81 data['title'] = annotation.title; |
| 82 } |
| 83 } else { |
| 84 id = annotations.first.id; |
| 85 List ids = []; |
| 86 for (Annotation annotation in annotations) { |
| 87 ids.add(annotation.id); |
| 88 } |
| 89 data['style'] = colorScheme.multiLocationToCssColor(ids); |
| 90 data['title'] = annotations.map((l) => l.title).join(','); |
| 91 } |
| 92 if (id != null) { |
| 93 Set ids = annotations.map((l) => l.id).toSet(); |
| 94 data['tag'] = 'a'; |
| 95 data['name'] = elementScheme.getName(id, ids); |
| 96 data['href'] = elementScheme.getHref(id, ids); |
| 97 data['onclick'] = elementScheme.onClick(id, ids); |
| 98 data['onmouseover'] = elementScheme.onMouseOver(id, ids); |
| 99 data['onmouseout'] = elementScheme.onMouseOut(id, ids); |
| 100 return new AnnotationData( |
| 101 properties: data); |
| 102 } |
| 103 return null; |
| 104 } |
| 105 |
| 9 class HtmlPrintContext { | 106 class HtmlPrintContext { |
| 10 final int lineNoWidth; | 107 final int lineNoWidth; |
| 11 final bool usePre; | 108 final bool usePre; |
| 109 final AnnotationFilter includeAnnotation; |
| 110 final AnnotationDataFunction getAnnotationData; |
| 111 final LineDataFunction getLineData; |
| 12 | 112 |
| 13 HtmlPrintContext({ | 113 HtmlPrintContext({ |
| 14 this.lineNoWidth, | 114 this.lineNoWidth, |
| 15 this.usePre: true}); | 115 this.usePre: true, |
| 116 this.includeAnnotation: includeAllAnnotation, |
| 117 this.getAnnotationData: getAnnotationDataFromSchemes, |
| 118 this.getLineData: getDefaultLineData}); |
| 119 |
| 120 HtmlPrintContext from({ |
| 121 int lineNoWidth, |
| 122 bool usePre, |
| 123 AnnotationFilter includeAnnotation, |
| 124 AnnotationDataFunction getAnnotationData, |
| 125 LineDataFunction getLineData}) { |
| 126 return new HtmlPrintContext( |
| 127 lineNoWidth: lineNoWidth ?? this.lineNoWidth, |
| 128 usePre: usePre ?? this.usePre, |
| 129 includeAnnotation: includeAnnotation ?? this.includeAnnotation, |
| 130 getAnnotationData: getAnnotationData ?? this.getAnnotationData, |
| 131 getLineData: getLineData ?? this.getLineData); |
| 132 } |
| 16 } | 133 } |
| 17 | 134 |
| 18 enum HtmlPartKind { | 135 enum HtmlPartKind { |
| 19 CODE, | 136 CODE, |
| 20 LINE, | 137 LINE, |
| 21 CONST, | 138 CONST, |
| 22 NEWLINE, | 139 NEWLINE, |
| 23 TEXT, | 140 TEXT, |
| 24 ANCHOR, | 141 TAG, |
| 142 LINE_NUMBER, |
| 25 } | 143 } |
| 26 | 144 |
| 27 abstract class HtmlPart { | 145 abstract class HtmlPart { |
| 28 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) {} | 146 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context); |
| 29 | 147 |
| 30 toJson(); | 148 HtmlPartKind get kind; |
| 31 | 149 |
| 32 static HtmlPart fromJson(json) { | 150 toJson(JsonStrategy strategy); |
| 151 |
| 152 static HtmlPart fromJson(json, JsonStrategy strategy) { |
| 33 if (json is String) { | 153 if (json is String) { |
| 34 return new ConstHtmlPart(json); | 154 return new ConstHtmlPart(json); |
| 35 } else { | 155 } else { |
| 36 switch (HtmlPartKind.values[json['kind']]) { | 156 switch (HtmlPartKind.values[json['kind']]) { |
| 37 case HtmlPartKind.LINE: | 157 case HtmlPartKind.LINE: |
| 38 return HtmlLine.fromJson(json); | 158 return HtmlLine.fromJson(json, strategy); |
| 39 case HtmlPartKind.CODE: | 159 case HtmlPartKind.CODE: |
| 40 return CodeLine.fromJson(json); | 160 return CodeLine.fromJson(json, strategy); |
| 41 case HtmlPartKind.CONST: | 161 case HtmlPartKind.CONST: |
| 42 return ConstHtmlPart.fromJson(json); | 162 return ConstHtmlPart.fromJson(json, strategy); |
| 43 case HtmlPartKind.NEWLINE: | 163 case HtmlPartKind.NEWLINE: |
| 44 return const NewLine(); | 164 return const NewLine(); |
| 45 case HtmlPartKind.TEXT: | 165 case HtmlPartKind.TEXT: |
| 46 return HtmlText.fromJson(json); | 166 return HtmlText.fromJson(json, strategy); |
| 47 case HtmlPartKind.ANCHOR: | 167 case HtmlPartKind.TAG: |
| 48 return AnchorHtmlPart.fromJson(json); | 168 return TagPart.fromJson(json, strategy); |
| 169 case HtmlPartKind.LINE_NUMBER: |
| 170 return LineNumber.fromJson(json, strategy); |
| 49 } | 171 } |
| 50 } | 172 } |
| 51 } | 173 } |
| 52 } | 174 } |
| 53 | 175 |
| 54 class ConstHtmlPart implements HtmlPart { | 176 class ConstHtmlPart implements HtmlPart { |
| 55 final String html; | 177 final String html; |
| 56 | 178 |
| 57 const ConstHtmlPart(this.html); | 179 const ConstHtmlPart(this.html); |
| 58 | 180 |
| 181 HtmlPartKind get kind => HtmlPartKind.CONST; |
| 182 |
| 59 @override | 183 @override |
| 60 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { | 184 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 61 buffer.write(html); | 185 buffer.write(html); |
| 62 } | 186 } |
| 63 | 187 |
| 64 toJson() { | 188 toJson(JsonStrategy strategy) { |
| 65 return {'kind': HtmlPartKind.CONST.index, 'html': html}; | 189 return {'kind': kind.index, 'html': html}; |
| 66 } | 190 } |
| 67 | 191 |
| 68 static ConstHtmlPart fromJson(Map json) { | 192 static ConstHtmlPart fromJson(Map json, JsonStrategy strategy) { |
| 69 return new ConstHtmlPart(json['html']); | 193 return new ConstHtmlPart(json['html']); |
| 70 } | 194 } |
| 71 } | 195 } |
| 72 | 196 |
| 73 class NewLine implements HtmlPart { | 197 class NewLine implements HtmlPart { |
| 74 const NewLine(); | 198 const NewLine(); |
| 75 | 199 |
| 200 HtmlPartKind get kind => HtmlPartKind.NEWLINE; |
| 201 |
| 76 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { | 202 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 77 if (context.usePre) { | 203 if (context.usePre) { |
| 78 buffer.write('\n'); | 204 buffer.write('\n'); |
| 79 } else { | 205 } else { |
| 80 buffer.write('<br/>'); | 206 buffer.write('<br/>'); |
| 81 } | 207 } |
| 82 } | 208 } |
| 83 | 209 |
| 84 toJson() { | 210 toJson(JsonStrategy strategy) { |
| 85 return {'kind': HtmlPartKind.NEWLINE.index}; | 211 return {'kind': kind.index}; |
| 86 } | 212 } |
| 87 } | 213 } |
| 88 | 214 |
| 89 class HtmlText implements HtmlPart { | 215 class HtmlText implements HtmlPart { |
| 90 final String text; | 216 final String text; |
| 91 | 217 |
| 92 const HtmlText(this.text); | 218 const HtmlText(this.text); |
| 93 | 219 |
| 220 HtmlPartKind get kind => HtmlPartKind.TEXT; |
| 221 |
| 94 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { | 222 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 95 String escaped = escape(text); | 223 String escaped = escape(text); |
| 96 buffer.write(escaped); | 224 buffer.write(escaped); |
| 97 } | 225 } |
| 98 | 226 |
| 99 toJson() { | 227 toJson(JsonStrategy strategy) { |
| 100 return {'kind': HtmlPartKind.TEXT.index, 'text': text}; | 228 return {'kind': kind.index, 'text': text}; |
| 101 } | 229 } |
| 102 | 230 |
| 103 static HtmlText fromJson(Map json) { | 231 static HtmlText fromJson(Map json, JsonStrategy strategy) { |
| 104 return new HtmlText(json['text']); | 232 return new HtmlText(json['text']); |
| 105 } | 233 } |
| 106 } | 234 } |
| 107 | 235 |
| 108 class AnchorHtmlPart implements HtmlPart { | 236 class TagPart implements HtmlPart { |
| 109 final String color; | 237 final String tag; |
| 110 final String name; | 238 final Map<String, String> properties; |
| 111 final String href; | 239 final List<HtmlPart> content; |
| 112 final String title; | |
| 113 final String onclick; | |
| 114 final String onmouseover; | |
| 115 final String onmouseout; | |
| 116 | 240 |
| 117 AnchorHtmlPart({ | 241 TagPart( |
| 118 this.color, | 242 this.tag, |
| 119 this.name, | 243 {this.properties: const <String, String>{}, |
| 120 this.href, | 244 this.content: const <HtmlPart>[]}); |
| 121 this.title, | 245 |
| 122 this.onclick, | 246 HtmlPartKind get kind => HtmlPartKind.TAG; |
| 123 this.onmouseover, | |
| 124 this.onmouseout}); | |
| 125 | 247 |
| 126 @override | 248 @override |
| 127 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { | 249 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 128 buffer.write('<a'); | 250 buffer.write('<$tag'); |
| 129 if (href != null) { | 251 properties.forEach((String key, String value) { |
| 130 buffer.write(' href="${href}"'); | 252 if (value != null) { |
| 253 buffer.write(' $key="${value}"'); |
| 254 } |
| 255 }); |
| 256 buffer.write('>'); |
| 257 for (HtmlPart child in content) { |
| 258 child.printHtmlOn(buffer, context); |
| 131 } | 259 } |
| 132 if (name != null) { | 260 buffer.write('</$tag>'); |
| 133 buffer.write(' name="${name}"'); | |
| 134 } | |
| 135 if (title != null) { | |
| 136 buffer.write(' title="${escape(title)}"'); | |
| 137 } | |
| 138 buffer.write(' style="${color}"'); | |
| 139 if (onclick != null) { | |
| 140 buffer.write(' onclick="${onclick}"'); | |
| 141 } | |
| 142 if (onmouseover != null) { | |
| 143 buffer.write(' onmouseover="${onmouseover}"'); | |
| 144 } | |
| 145 if (onmouseout != null) { | |
| 146 buffer.write(' onmouseout="${onmouseout}"'); | |
| 147 } | |
| 148 buffer.write('>'); | |
| 149 } | 261 } |
| 150 | 262 |
| 151 toJson() { | 263 toJson(JsonStrategy strategy) { |
| 152 return { | 264 return { |
| 153 'kind': HtmlPartKind.ANCHOR.index, | 265 'kind': kind.index, |
| 154 'color': color, | 266 'tag': tag, |
| 155 'name': name, | 267 'properties': properties, |
| 156 'href': href, | 268 'content': content.map((p) => p.toJson(strategy)).toList()}; |
| 157 'title': title, | |
| 158 'onclick': onclick, | |
| 159 'onmouseover': onmouseover, | |
| 160 'onmouseout': onmouseout}; | |
| 161 } | 269 } |
| 162 | 270 |
| 163 static AnchorHtmlPart fromJson(Map json) { | 271 static TagPart fromJson(Map json, JsonStrategy strategy) { |
| 164 return new AnchorHtmlPart( | 272 return new TagPart( |
| 165 color: json['color'], | 273 json['tag'], |
| 166 name: json['name'], | 274 properties: json['properties'], |
| 167 href: json['href'], | 275 content: json['content'].map(HtmlPart.fromJson).toList()); |
| 168 title: json['title'], | |
| 169 onclick: json['onclick'], | |
| 170 onmouseover: json['onmouseover'], | |
| 171 onmouseout: json['onmouseout']); | |
| 172 } | 276 } |
| 173 } | 277 } |
| 174 | 278 |
| 175 class HtmlLine implements HtmlPart { | 279 class HtmlLine implements HtmlPart { |
| 176 final List<HtmlPart> htmlParts = <HtmlPart>[]; | 280 final List<HtmlPart> htmlParts = <HtmlPart>[]; |
| 177 | 281 |
| 282 HtmlPartKind get kind => HtmlPartKind.LINE; |
| 283 |
| 178 @override | 284 @override |
| 179 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { | 285 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 180 for (HtmlPart part in htmlParts) { | 286 for (HtmlPart part in htmlParts) { |
| 181 part.printHtmlOn(htmlBuffer, context); | 287 part.printHtmlOn(htmlBuffer, context); |
| 182 } | 288 } |
| 183 } | 289 } |
| 184 | 290 |
| 185 Map toJson() { | 291 Map toJson(JsonStrategy strategy) { |
| 186 return { | 292 return { |
| 187 'kind': HtmlPartKind.LINE.index, | 293 'kind': kind.index, |
| 188 'html': htmlParts.map((p) => p.toJson()).toList(), | 294 'html': htmlParts.map((p) => p.toJson(strategy)).toList(), |
| 189 }; | 295 }; |
| 190 } | 296 } |
| 191 | 297 |
| 192 static CodeLine fromJson(Map json) { | 298 static HtmlLine fromJson(Map json, JsonStrategy strategy) { |
| 193 HtmlLine line = new HtmlLine(); | 299 HtmlLine line = new HtmlLine(); |
| 194 json['html'].forEach((part) => line.htmlParts.add(HtmlPart.fromJson(part))); | 300 json['html'] |
| 301 .forEach((part) => line.htmlParts |
| 302 .add(HtmlPart.fromJson(part, strategy))); |
| 195 return line; | 303 return line; |
| 196 } | 304 } |
| 197 } | 305 } |
| 198 | 306 |
| 199 class CodeLine extends HtmlLine { | 307 class CodePart { |
| 308 final List<Annotation> annotations; |
| 309 final String subsequentCode; |
| 310 |
| 311 CodePart(this.annotations, this.subsequentCode); |
| 312 |
| 313 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 314 Iterable<Annotation> included = |
| 315 annotations.where(context.includeAnnotation); |
| 316 |
| 317 List<HtmlPart> htmlParts = <HtmlPart>[]; |
| 318 if (included.isNotEmpty) { |
| 319 AnnotationData annotationData = |
| 320 context.getAnnotationData(included, forSpan: false); |
| 321 AnnotationData annotationDataForSpan = |
| 322 context.getAnnotationData(included, forSpan: true); |
| 323 |
| 324 String head = subsequentCode; |
| 325 String tail = ''; |
| 326 if (subsequentCode.length > 1) { |
| 327 head = subsequentCode.substring(0, 1); |
| 328 tail = subsequentCode.substring(1); |
| 329 } |
| 330 |
| 331 void addForSpan(AnnotationData data) { |
| 332 htmlParts.add(new TagPart( |
| 333 data.tag, |
| 334 properties: data.properties, |
| 335 content: [new HtmlText(subsequentCode)])); |
| 336 } |
| 337 |
| 338 if (annotationData != null && |
| 339 annotationDataForSpan != null) { |
| 340 htmlParts.add(new TagPart( |
| 341 annotationDataForSpan.tag, |
| 342 properties: annotationDataForSpan.properties, |
| 343 content: [ |
| 344 new TagPart( |
| 345 annotationData.tag, |
| 346 properties: annotationData.properties, |
| 347 content: [new HtmlText(head)]), |
| 348 new HtmlText(tail)])); |
| 349 } else if (annotationDataForSpan != null) { |
| 350 htmlParts.add(new TagPart( |
| 351 annotationDataForSpan.tag, |
| 352 properties: annotationDataForSpan.properties, |
| 353 content: [new HtmlText(subsequentCode)])); |
| 354 } else if (annotationData != null) { |
| 355 htmlParts.add(new TagPart( |
| 356 annotationData.tag, |
| 357 properties: annotationData.properties, |
| 358 content: [new HtmlText(head)])); |
| 359 htmlParts.add(new HtmlText(tail)); |
| 360 } else { |
| 361 htmlParts.add(new HtmlText(subsequentCode)); |
| 362 } |
| 363 } else { |
| 364 htmlParts.add(new HtmlText(subsequentCode)); |
| 365 } |
| 366 |
| 367 for (HtmlPart part in htmlParts) { |
| 368 part.printHtmlOn(buffer, context); |
| 369 } |
| 370 } |
| 371 |
| 372 Map toJson(JsonStrategy strategy) { |
| 373 return { |
| 374 'annotations': |
| 375 annotations.map((a) => strategy.encodeAnnotation(a)).toList(), |
| 376 'subsequentCode': subsequentCode, |
| 377 }; |
| 378 } |
| 379 |
| 380 static CodePart fromJson(Map json, JsonStrategy strategy) { |
| 381 return new CodePart( |
| 382 json['annotations'].map((j) => strategy.decodeAnnotation(j)).toList(), |
| 383 json['subsequentCode']); |
| 384 } |
| 385 } |
| 386 |
| 387 class LineNumber extends HtmlPart { |
| 388 final int lineNo; |
| 389 final lineAnnotation; |
| 390 |
| 391 LineNumber(this.lineNo, this.lineAnnotation); |
| 392 |
| 393 HtmlPartKind get kind => HtmlPartKind.LINE_NUMBER; |
| 394 |
| 395 @override |
| 396 toJson(JsonStrategy strategy) { |
| 397 return { |
| 398 'kind': kind.index, |
| 399 'lineNo': lineNo, |
| 400 'lineAnnotation': strategy.encodeLineAnnotation(lineAnnotation), |
| 401 }; |
| 402 } |
| 403 |
| 404 static LineNumber fromJson(Map json, JsonStrategy strategy) { |
| 405 return new LineNumber( |
| 406 json['lineNo'], |
| 407 strategy.decodeLineAnnotation(json['lineAnnotation'])); |
| 408 } |
| 409 |
| 410 @override |
| 411 void printHtmlOn(StringBuffer buffer, HtmlPrintContext context) { |
| 412 buffer.write(lineNumber( |
| 413 lineNo, |
| 414 width: context.lineNoWidth, |
| 415 useNbsp: !context.usePre, |
| 416 className: context.getLineData(lineAnnotation).lineNumberClass)); |
| 417 } |
| 418 } |
| 419 |
| 420 class CodeLine extends HtmlPart { |
| 421 final Uri uri; |
| 200 final int lineNo; | 422 final int lineNo; |
| 201 final int offset; | 423 final int offset; |
| 202 final StringBuffer codeBuffer = new StringBuffer(); | 424 final StringBuffer codeBuffer = new StringBuffer(); |
| 203 final List<HtmlPart> htmlParts = <HtmlPart>[]; | 425 final List<CodePart> codeParts = <CodePart>[]; |
| 204 // TODO(johnniwinther): Make annotations serializable. | |
| 205 final List<Annotation> annotations = <Annotation>[]; | 426 final List<Annotation> annotations = <Annotation>[]; |
| 427 var lineAnnotation; |
| 206 String _code; | 428 String _code; |
| 207 | 429 |
| 208 CodeLine(this.lineNo, this.offset); | 430 CodeLine(this.lineNo, this.offset, {this.uri}); |
| 431 |
| 432 HtmlPartKind get kind => HtmlPartKind.CODE; |
| 209 | 433 |
| 210 String get code { | 434 String get code { |
| 211 if (_code == null) { | 435 if (_code == null) { |
| 212 _code = codeBuffer.toString(); | 436 _code = codeBuffer.toString(); |
| 213 } | 437 } |
| 214 return _code; | 438 return _code; |
| 215 } | 439 } |
| 216 | 440 |
| 217 @override | 441 @override |
| 218 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { | 442 void printHtmlOn(StringBuffer htmlBuffer, HtmlPrintContext context) { |
| 219 htmlBuffer.write(lineNumber( | 443 if (context.usePre) { |
| 220 lineNo, width: context.lineNoWidth, useNbsp: !context.usePre)); | 444 LineData lineData = context.getLineData(lineAnnotation); |
| 221 for (HtmlPart part in htmlParts) { | 445 htmlBuffer.write('<p class="${lineData.lineClass}">'); |
| 446 } |
| 447 new LineNumber(lineNo, lineAnnotation).printHtmlOn(htmlBuffer, context); |
| 448 for (CodePart part in codeParts) { |
| 222 part.printHtmlOn(htmlBuffer, context); | 449 part.printHtmlOn(htmlBuffer, context); |
| 223 } | 450 } |
| 451 const NewLine().printHtmlOn(htmlBuffer, context); |
| 452 if (context.usePre) { |
| 453 htmlBuffer.write('</p>'); |
| 454 } |
| 224 } | 455 } |
| 225 | 456 |
| 226 Map toJson() { | 457 Map toJson(JsonStrategy strategy) { |
| 227 return { | 458 return { |
| 228 'kind': HtmlPartKind.CODE.index, | 459 'kind': kind.index, |
| 229 'lineNo': lineNo, | 460 'lineNo': lineNo, |
| 230 'offset': offset, | 461 'offset': offset, |
| 231 'code': code, | 462 'code': code, |
| 232 'html': htmlParts.map((p) => p.toJson()).toList(), | 463 'parts': codeParts.map((p) => p.toJson(strategy)).toList(), |
| 464 'annotations': |
| 465 annotations.map((a) => strategy.encodeAnnotation(a)).toList(), |
| 466 'lineAnnotation': lineAnnotation != null |
| 467 ? strategy.encodeLineAnnotation(lineAnnotation) : null, |
| 233 }; | 468 }; |
| 234 } | 469 } |
| 235 | 470 |
| 236 static CodeLine fromJson(Map json) { | 471 static CodeLine fromJson(Map json, JsonStrategy strategy) { |
| 237 CodeLine line = new CodeLine(json['lineNo'], json['offset']); | 472 CodeLine line = new CodeLine( |
| 473 json['lineNo'], |
| 474 json['offset'], |
| 475 uri: json['uri'] != null ? Uri.parse(json['uri']) : null); |
| 238 line.codeBuffer.write(json['code']); | 476 line.codeBuffer.write(json['code']); |
| 239 json['html'].forEach((part) => line.htmlParts.add(HtmlPart.fromJson(part))); | 477 json['parts'] |
| 478 .forEach((part) => line.codeParts |
| 479 .add(CodePart.fromJson(part, strategy))); |
| 480 json['annotations'] |
| 481 .forEach((a) => line.annotations |
| 482 .add(strategy.decodeAnnotation(a))); |
| 483 line.lineAnnotation = json['lineAnnotation'] != null |
| 484 ? strategy.decodeLineAnnotation(json['lineAnnotation']) : null; |
| 240 return line; | 485 return line; |
| 241 } | 486 } |
| 242 } | 487 } |
| 243 | 488 |
| 489 class JsonStrategy { |
| 490 const JsonStrategy(); |
| 491 |
| 492 Map encodeAnnotation(Annotation annotation) { |
| 493 return { |
| 494 'id': annotation.id, |
| 495 'codeOffset': annotation.codeOffset, |
| 496 'title': annotation.title, |
| 497 'data': annotation.data, |
| 498 }; |
| 499 } |
| 500 |
| 501 Annotation decodeAnnotation(Map json) { |
| 502 return new Annotation( |
| 503 json['id'], |
| 504 json['codeOffset'], |
| 505 json['title'], |
| 506 data: json['data']); |
| 507 } |
| 508 |
| 509 |
| 510 encodeLineAnnotation(lineAnnotation) => lineAnnotation; |
| 511 |
| 512 decodeLineAnnotation(json) => json; |
| 513 } |
| OLD | NEW |