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 |