OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /// Contains a code printer that generates code by recording the source maps. | 5 /// Contains a code printer that generates code by recording the source maps. |
6 library source_maps.printer; | 6 library source_maps.printer; |
7 | 7 |
8 import 'package:source_span/source_span.dart' as source_span; | 8 import 'package:source_span/source_span.dart'; |
9 | 9 |
10 import 'builder.dart'; | 10 import 'builder.dart'; |
11 import 'span.dart'; | 11 import 'src/source_map_span.dart'; |
12 import 'src/span_wrapper.dart'; | |
13 | 12 |
14 const int _LF = 10; | 13 const int _LF = 10; |
15 const int _CR = 13; | 14 const int _CR = 13; |
16 | 15 |
17 /// A simple printer that keeps track of offset locations and records source | 16 /// A simple printer that keeps track of offset locations and records source |
18 /// maps locations. | 17 /// maps locations. |
19 class Printer { | 18 class Printer { |
20 final String filename; | 19 final String filename; |
21 final StringBuffer _buff = new StringBuffer(); | 20 final StringBuffer _buff = new StringBuffer(); |
22 final SourceMapBuilder _maps = new SourceMapBuilder(); | 21 final SourceMapBuilder _maps = new SourceMapBuilder(); |
23 String get text => _buff.toString(); | 22 String get text => _buff.toString(); |
24 String get map => _maps.toJson(filename); | 23 String get map => _maps.toJson(filename); |
25 | 24 |
26 /// Current source location mapping. | 25 /// Current source location mapping. |
27 Location _loc; | 26 SourceLocation _loc; |
28 | 27 |
29 /// Current line in the buffer; | 28 /// Current line in the buffer; |
30 int _line = 0; | 29 int _line = 0; |
31 | 30 |
32 /// Current column in the buffer. | 31 /// Current column in the buffer. |
33 int _column = 0; | 32 int _column = 0; |
34 | 33 |
35 Printer(this.filename); | 34 Printer(this.filename); |
36 | 35 |
37 /// Add [str] contents to the output, tracking new lines to track correct | 36 /// Add [str] contents to the output, tracking new lines to track correct |
38 /// positions for span locations. When [projectMarks] is true, this method | 37 /// positions for span locations. When [projectMarks] is true, this method |
39 /// adds a source map location on each new line, projecting that every new | 38 /// adds a source map location on each new line, projecting that every new |
40 /// line in the target file (printed here) corresponds to a new line in the | 39 /// line in the target file (printed here) corresponds to a new line in the |
41 /// source file. | 40 /// source file. |
42 void add(String str, {projectMarks: false}) { | 41 void add(String str, {projectMarks: false}) { |
43 var chars = str.runes.toList(); | 42 var chars = str.runes.toList(); |
44 var length = chars.length; | 43 var length = chars.length; |
45 for (int i = 0; i < length; i++) { | 44 for (int i = 0; i < length; i++) { |
46 var c = chars[i]; | 45 var c = chars[i]; |
47 if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) { | 46 if (c == _LF || (c == _CR && (i + 1 == length || chars[i + 1] != _LF))) { |
48 // Return not followed by line-feed is treated as a new line. | 47 // Return not followed by line-feed is treated as a new line. |
49 _line++; | 48 _line++; |
50 _column = 0; | 49 _column = 0; |
51 if (projectMarks && _loc != null) { | 50 if (projectMarks && _loc != null) { |
52 if (_loc is FixedLocation) { | 51 if (_loc is FileLocation) { |
53 mark(new FixedLocation(0, _loc.sourceUrl, _loc.line + 1, 0)); | |
54 } else if (_loc is FileLocation) { | |
55 var file = (_loc as FileLocation).file; | 52 var file = (_loc as FileLocation).file; |
56 mark(new FileLocation(file, file.getOffset(_loc.line + 1, 0))); | 53 mark(file.location(file.getOffset(_loc.line + 1))); |
| 54 } else { |
| 55 mark(new SourceLocation(0, |
| 56 sourceUrl: _loc.sourceUrl, line: _loc.line + 1, column: 0)); |
57 } | 57 } |
58 } | 58 } |
59 } else { | 59 } else { |
60 _column++; | 60 _column++; |
61 } | 61 } |
62 } | 62 } |
63 _buff.write(str); | 63 _buff.write(str); |
64 } | 64 } |
65 | 65 |
66 | 66 |
67 /// Append a [total] number of spaces in the target file. Typically used for | 67 /// Append a [total] number of spaces in the target file. Typically used for |
68 /// formatting indentation. | 68 /// formatting indentation. |
69 void addSpaces(int total) { | 69 void addSpaces(int total) { |
70 for (int i = 0; i < total; i++) _buff.write(' '); | 70 for (int i = 0; i < total; i++) _buff.write(' '); |
71 _column += total; | 71 _column += total; |
72 } | 72 } |
73 | 73 |
74 /// Marks that the current point in the target file corresponds to the [mark] | 74 /// Marks that the current point in the target file corresponds to the [mark] |
75 /// in the source file, which can be either a [Location] or a [Span]. When the | 75 /// in the source file, which can be either a [SourceLocation] or a |
76 /// mark is an identifier's Span, this also records the name of the identifier | 76 /// [SourceSpan]. When the mark is a [SourceMapSpan] with `isIdentifier` set, |
77 /// in the source map information. | 77 /// this also records the name of the identifier in the source map |
| 78 /// information. |
78 void mark(mark) { | 79 void mark(mark) { |
79 var loc; | 80 var loc; |
80 var identifier = null; | 81 var identifier = null; |
81 if (mark is Location || mark is source_span.SourceLocation) { | 82 if (mark is SourceLocation) { |
82 loc = LocationWrapper.wrap(mark); | 83 loc = mark; |
83 } else if (mark is Span || mark is source_span.SourceSpan) { | 84 } else if (mark is SourceSpan) { |
84 mark = SpanWrapper.wrap(mark); | |
85 loc = mark.start; | 85 loc = mark.start; |
86 if (mark.isIdentifier) identifier = mark.text; | 86 if (mark is SourceMapSpan && mark.isIdentifier) identifier = mark.text; |
87 } | 87 } |
88 _maps.addLocation(loc, | 88 _maps.addLocation( |
89 new FixedLocation(_buff.length, null, _line, _column), identifier); | 89 loc, |
| 90 new SourceLocation(_buff.length, line: _line, column: _column), |
| 91 identifier); |
90 _loc = loc; | 92 _loc = loc; |
91 } | 93 } |
92 } | 94 } |
93 | 95 |
94 /// A more advanced printer that keeps track of offset locations to record | 96 /// A more advanced printer that keeps track of offset locations to record |
95 /// source maps, but additionally allows nesting of different kind of items, | 97 /// source maps, but additionally allows nesting of different kind of items, |
96 /// including [NestedPrinter]s, and it let's you automatically indent text. | 98 /// including [NestedPrinter]s, and it let's you automatically indent text. |
97 /// | 99 /// |
98 /// This class is especially useful when doing code generation, where different | 100 /// This class is especially useful when doing code generation, where different |
99 /// peices of the code are generated independently on separate printers, and are | 101 /// peices of the code are generated independently on separate printers, and are |
100 /// finally put together in the end. | 102 /// finally put together in the end. |
101 class NestedPrinter implements NestedItem { | 103 class NestedPrinter implements NestedItem { |
102 | 104 |
103 /// Items recoded by this printer, which can be [String] literals, | 105 /// Items recoded by this printer, which can be [String] literals, |
104 /// [NestedItem]s, and source map information like [Location] and [Span]. | 106 /// [NestedItem]s, and source map information like [SourceLocation] and |
| 107 /// [SourceSpan]. |
105 List _items = []; | 108 List _items = []; |
106 | 109 |
107 /// Internal buffer to merge consecutive strings added to this printer. | 110 /// Internal buffer to merge consecutive strings added to this printer. |
108 StringBuffer _buff; | 111 StringBuffer _buff; |
109 | 112 |
110 /// Current indentation, which can be updated from outside this class. | 113 /// Current indentation, which can be updated from outside this class. |
111 int indent; | 114 int indent; |
112 | 115 |
113 /// Item used to indicate that the following item is copied from the original | 116 /// Item used to indicate that the following item is copied from the original |
114 /// source code, and hence we should preserve source-maps on every new line. | 117 /// source code, and hence we should preserve source-maps on every new line. |
115 static final _ORIGINAL = new Object(); | 118 static final _ORIGINAL = new Object(); |
116 | 119 |
117 NestedPrinter([this.indent = 0]); | 120 NestedPrinter([this.indent = 0]); |
118 | 121 |
119 /// Adds [object] to this printer. [object] can be a [String], | 122 /// Adds [object] to this printer. [object] can be a [String], |
120 /// [NestedPrinter], or anything implementing [NestedItem]. If [object] is a | 123 /// [NestedPrinter], or anything implementing [NestedItem]. If [object] is a |
121 /// [String], the value is appended directly, without doing any formatting | 124 /// [String], the value is appended directly, without doing any formatting |
122 /// changes. If you wish to add a line of code with automatic indentation, use | 125 /// changes. If you wish to add a line of code with automatic indentation, use |
123 /// [addLine] instead. [NestedPrinter]s and [NestedItem]s are not processed | 126 /// [addLine] instead. [NestedPrinter]s and [NestedItem]s are not processed |
124 /// until [build] gets called later on. We ensure that [build] emits every | 127 /// until [build] gets called later on. We ensure that [build] emits every |
125 /// object in the order that they were added to this printer. | 128 /// object in the order that they were added to this printer. |
126 /// | 129 /// |
127 /// The [location] and [span] parameters indicate the corresponding source map | 130 /// The [location] and [span] parameters indicate the corresponding source map |
128 /// location of [object] in the original input. Only one, [location] or | 131 /// location of [object] in the original input. Only one, [location] or |
129 /// [span], should be provided at a time. | 132 /// [span], should be provided at a time. |
130 /// | 133 /// |
131 /// [location] can be either a [Location] or a [SourceLocation]. [span] can be | |
132 /// either a [Span] or a [SourceSpan]. Using a [Location] or a [Span] is | |
133 /// deprecated and will be unsupported in version 0.10.0. | |
134 /// | |
135 /// Indicate [isOriginal] when [object] is copied directly from the user code. | 134 /// Indicate [isOriginal] when [object] is copied directly from the user code. |
136 /// Setting [isOriginal] will make this printer propagate source map locations | 135 /// Setting [isOriginal] will make this printer propagate source map locations |
137 /// on every line-break. | 136 /// on every line-break. |
138 void add(object, {location, span, bool isOriginal: false}) { | 137 void add(object, {SourceLocation location, SourceSpan span, |
| 138 bool isOriginal: false}) { |
139 if (object is! String || location != null || span != null || isOriginal) { | 139 if (object is! String || location != null || span != null || isOriginal) { |
140 _flush(); | 140 _flush(); |
141 assert(location == null || span == null); | 141 assert(location == null || span == null); |
142 if (location != null) _items.add(LocationWrapper.wrap(location)); | 142 if (location != null) _items.add(location); |
143 if (span != null) _items.add(SpanWrapper.wrap(span)); | 143 if (span != null) _items.add(span); |
144 if (isOriginal) _items.add(_ORIGINAL); | 144 if (isOriginal) _items.add(_ORIGINAL); |
145 } | 145 } |
146 | 146 |
147 if (object is String) { | 147 if (object is String) { |
148 _appendString(object); | 148 _appendString(object); |
149 } else { | 149 } else { |
150 _items.add(object); | 150 _items.add(object); |
151 } | 151 } |
152 } | 152 } |
153 | 153 |
154 /// Append `2 * indent` spaces to this printer. | 154 /// Append `2 * indent` spaces to this printer. |
155 void insertIndent() => _indent(indent); | 155 void insertIndent() => _indent(indent); |
156 | 156 |
157 /// Add a [line], autoindenting to the current value of [indent]. Note, | 157 /// Add a [line], autoindenting to the current value of [indent]. Note, |
158 /// indentation is not inferred from the contents added to this printer. If a | 158 /// indentation is not inferred from the contents added to this printer. If a |
159 /// line starts or ends an indentation block, you need to also update [indent] | 159 /// line starts or ends an indentation block, you need to also update [indent] |
160 /// accordingly. Also, indentation is not adapted for nested printers. If | 160 /// accordingly. Also, indentation is not adapted for nested printers. If |
161 /// you add a [NestedPrinter] to this printer, its indentation is set | 161 /// you add a [NestedPrinter] to this printer, its indentation is set |
162 /// separately and will not include any the indentation set here. | 162 /// separately and will not include any the indentation set here. |
163 /// | 163 /// |
164 /// The [location] and [span] parameters indicate the corresponding source map | 164 /// The [location] and [span] parameters indicate the corresponding source map |
165 /// location of [object] in the original input. Only one, [location] or | 165 /// location of [object] in the original input. Only one, [location] or |
166 /// [span], should be provided at a time. | 166 /// [span], should be provided at a time. |
167 /// | 167 void addLine(String line, {SourceLocation location, SourceSpan span}) { |
168 /// [location] can be either a [Location] or a [SourceLocation]. [span] can be | |
169 /// either a [Span] or a [SourceSpan]. Using a [Location] or a [Span] is | |
170 /// deprecated and will be unsupported in version 0.10.0. | |
171 void addLine(String line, {location, span}) { | |
172 if (location != null || span != null) { | 168 if (location != null || span != null) { |
173 _flush(); | 169 _flush(); |
174 assert(location == null || span == null); | 170 assert(location == null || span == null); |
175 if (location != null) _items.add(LocationWrapper.wrap(location)); | 171 if (location != null) _items.add(location); |
176 if (span != null) _items.add(SpanWrapper.wrap(span)); | 172 if (span != null) _items.add(span); |
177 } | 173 } |
178 if (line == null) return; | 174 if (line == null) return; |
179 if (line != '') { | 175 if (line != '') { |
180 // We don't indent empty lines. | 176 // We don't indent empty lines. |
181 _indent(indent); | 177 _indent(indent); |
182 _appendString(line); | 178 _appendString(line); |
183 } | 179 } |
184 _appendString('\n'); | 180 _appendString('\n'); |
185 } | 181 } |
186 | 182 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 /// Implements the [NestedItem] interface. | 224 /// Implements the [NestedItem] interface. |
229 void writeTo(Printer printer) { | 225 void writeTo(Printer printer) { |
230 _flush(); | 226 _flush(); |
231 bool propagate = false; | 227 bool propagate = false; |
232 for (var item in _items) { | 228 for (var item in _items) { |
233 if (item is NestedItem) { | 229 if (item is NestedItem) { |
234 item.writeTo(printer); | 230 item.writeTo(printer); |
235 } else if (item is String) { | 231 } else if (item is String) { |
236 printer.add(item, projectMarks: propagate); | 232 printer.add(item, projectMarks: propagate); |
237 propagate = false; | 233 propagate = false; |
238 } else if (item is Location || item is Span) { | 234 } else if (item is SourceLocation || item is SourceSpan) { |
239 printer.mark(item); | 235 printer.mark(item); |
240 } else if (item == _ORIGINAL) { | 236 } else if (item == _ORIGINAL) { |
241 // we insert booleans when we are about to quote text that was copied | 237 // we insert booleans when we are about to quote text that was copied |
242 // from the original source. In such case, we will propagate marks on | 238 // from the original source. In such case, we will propagate marks on |
243 // every new-line. | 239 // every new-line. |
244 propagate = true; | 240 propagate = true; |
245 } else { | 241 } else { |
246 throw new UnsupportedError('Unknown item type: $item'); | 242 throw new UnsupportedError('Unknown item type: $item'); |
247 } | 243 } |
248 } | 244 } |
249 } | 245 } |
250 } | 246 } |
251 | 247 |
252 /// An item added to a [NestedPrinter]. | 248 /// An item added to a [NestedPrinter]. |
253 abstract class NestedItem { | 249 abstract class NestedItem { |
254 /// Write the contents of this item into [printer]. | 250 /// Write the contents of this item into [printer]. |
255 void writeTo(Printer printer); | 251 void writeTo(Printer printer); |
256 } | 252 } |
OLD | NEW |