Index: pkg/source_maps/lib/printer.dart |
diff --git a/pkg/source_maps/lib/printer.dart b/pkg/source_maps/lib/printer.dart |
index 333aadcd429c4e951c0aef2c78a2f47db287e442..95539d2b77bf549a41d81bc90208581a71aa1260 100644 |
--- a/pkg/source_maps/lib/printer.dart |
+++ b/pkg/source_maps/lib/printer.dart |
@@ -12,8 +12,8 @@ import 'span.dart'; |
const int _LF = 10; |
const int _CR = 13; |
-/// A printer that keeps track of offset locations and records source maps |
-/// locations. |
+/// A simple printer that keeps track of offset locations and records source |
+/// maps locations. |
class Printer { |
final String filename; |
final StringBuffer _buff = new StringBuffer(); |
@@ -87,3 +87,159 @@ class Printer { |
_loc = loc; |
} |
} |
+ |
+/// A more advanced printer that keeps track of offset locations to record |
+/// source maps, but additionally allows nesting of different kind of items, |
+/// including [NestedPrinter]s, and it let's you automatically indent text. |
+/// |
+/// This class is especially useful when doing code generation, where different |
+/// peices of the code are generated independently on separate printers, and are |
+/// finally put together in the end. |
+class NestedPrinter implements NestedItem { |
+ |
+ /// Items recoded by this printer, which can be [String] literals, |
+ /// [NestedItem]s, and source map information like [Location] and [Span]. |
+ List _items = []; |
+ |
+ /// Internal buffer to merge consecutive strings added to this printer. |
+ StringBuffer _buff; |
+ |
+ /// Current indentation, which can be updated from outside this class. |
+ int indent; |
+ |
+ /// Item used to indicate that the following item is copied from the original |
+ /// source code, and hence we should preserve source-maps on every new line. |
+ static final _ORIGINAL = new Object(); |
+ |
+ NestedPrinter([this.indent = 0]); |
+ |
+ /// Adds [object] to this printer. [object] can be a [String], |
+ /// [NestedPrinter], or anything implementing [NestedItem]. If [object] is a |
+ /// [String], the value is appended directly, without doing any formatting |
+ /// changes. If you wish to add a line of code with automatic indentation, use |
+ /// [addLine] instead. [NestedPrinter]s and [NestedItem]s are not processed |
+ /// until [build] gets called later on. We ensure that [build] emits every |
+ /// object in the order that they were added to this printer. |
+ /// |
+ /// The [location] and [span] parameters indicate the corresponding source map |
+ /// location of [object] in the original input. Only one, [location] or |
+ /// [span], should be provided at a time. |
+ /// |
+ /// Indicate [isOriginal] when [object] is copied directly from the user code. |
+ /// Setting [isOriginal] will make this printer propagate source map locations |
+ /// on every line-break. |
+ void add(object, {Location location, Span span, bool isOriginal: false}) { |
+ if (object is! String || location != null || span != null || isOriginal) { |
+ _flush(); |
+ assert(location == null || span == null); |
+ if (location != null) _items.add(location); |
+ if (span != null) _items.add(span); |
+ if (isOriginal) _items.add(_ORIGINAL); |
+ } |
+ |
+ if (object is String) { |
+ _appendString(object); |
+ } else { |
+ _items.add(object); |
+ } |
+ } |
+ |
+ /// Append `2 * indent` spaces to this printer. |
+ void insertIndent() => _indent(indent); |
+ |
+ /// Add a [line], autoindenting to the current value of [indent]. Note, |
+ /// indentation is not inferred from the contents added to this printer. If a |
+ /// line starts or ends an indentation block, you need to also update [indent] |
+ /// accordingly. Also, indentation is not adapted for nested printers. If |
+ /// you add a [NestedPrinter] to this printer, its indentation is set |
+ /// separately and will not include any the indentation set here. |
+ /// |
+ /// The [location] and [span] parameters indicate the corresponding source map |
+ /// location of [object] in the original input. Only one, [location] or |
+ /// [span], should be provided at a time. |
+ void addLine(String line, {Location location, Span span}) { |
+ if (location != null || span != null) { |
+ _flush(); |
+ assert(location == null || span == null); |
+ if (location != null) _items.add(location); |
+ if (span != null) _items.add(span); |
+ } |
+ if (line == null) return; |
+ if (line != '') { |
+ // We don't indent empty lines. |
+ _indent(indent); |
+ _appendString(line); |
+ } |
+ _appendString('\n'); |
+ } |
+ |
+ /// Appends a string merging it with any previous strings, if possible. |
+ void _appendString(String s) { |
+ if (_buff == null) _buff = new StringBuffer(); |
+ _buff.write(s); |
+ } |
+ |
+ /// Adds all of the current [_buff] contents as a string item. |
+ void _flush() { |
+ if (_buff != null) { |
+ _items.add(_buff.toString()); |
+ _buff = null; |
+ } |
+ } |
+ |
+ void _indent(int indent) { |
+ for (int i = 0; i < indent; i++) _appendString(' '); |
+ } |
+ |
+ /// Returns a string representation of all the contents appended to this |
+ /// printer, including source map location tokens. |
+ String toString() { |
+ _flush(); |
+ return (new StringBuffer()..writeAll(_items)).toString(); |
+ } |
+ |
+ /// [Printer] used during the last call to [build], if any. |
+ Printer printer; |
+ |
+ /// Returns the text produced after calling [build]. |
+ String get text => printer.text; |
+ |
+ /// Returns the source-map information produced after calling [build]. |
+ String get map => printer.map; |
+ |
+ /// Builds the output of this printer and source map information. After |
+ /// calling this function, you can use [text] and [map] to retrieve the |
+ /// geenrated code and source map information, respectively. |
+ void build(String filename) { |
+ writeTo(printer = new Printer(filename)); |
+ } |
+ |
+ /// Implements the [NestedItem] interface. |
+ void writeTo(Printer printer) { |
+ _flush(); |
+ bool propagate = false; |
+ for (var item in _items) { |
+ if (item is NestedItem) { |
+ item.writeTo(printer); |
+ } else if (item is String) { |
+ printer.add(item, projectMarks: propagate); |
+ propagate = false; |
+ } else if (item is Location || item is Span) { |
+ printer.mark(item); |
+ } else if (item == _ORIGINAL) { |
+ // we insert booleans when we are about to quote text that was copied |
+ // from the original source. In such case, we will propagate marks on |
+ // every new-line. |
+ propagate = true; |
+ } else { |
+ throw new UnsupportedError('Unknown item type: $item'); |
+ } |
+ } |
+ } |
+} |
+ |
+/// An item added to a [NestedPrinter]. |
+abstract class NestedItem { |
+ /// Write the contents of this item into [printer]. |
+ void writeTo(Printer printer); |
+} |