Index: lib/src/string_edit_buffer.dart |
diff --git a/lib/src/string_edit_buffer.dart b/lib/src/string_edit_buffer.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..dc314f71a7050c3ed30d7d1885f8f373283d5454 |
--- /dev/null |
+++ b/lib/src/string_edit_buffer.dart |
@@ -0,0 +1,113 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/// Defines [StringEditBuffer], a buffer that can be used to apply edits on a |
+/// string. |
+// TODO(sigmund): this should move to a separate package. |
+library dart2js.src.string_edit_buffer; |
+ |
+/// A buffer meant to apply edits on a string (rather than building a string |
+/// from scratch). Each change is described using the location information on |
+/// the original string. Internally this buffer keeps track of how a |
+/// modification in one portion can offset a modification further down the |
+/// string. |
+class StringEditBuffer { |
+ final String original; |
+ final _edits = <_StringEdit>[]; |
+ |
+ StringEditBuffer(this.original); |
+ |
+ bool get hasEdits => _edits.length > 0; |
+ |
+ /// Edit the original text, replacing text on the range [begin] and |
+ /// exclusive [end] with the [replacement] string. |
+ void replace(int begin, int end, String replacement, [int sortId]) { |
+ _edits.add(new _StringEdit(begin, end, replacement, sortId)); |
+ } |
+ |
+ /// Insert [string] at [offset]. |
+ /// Equivalent to `replace(offset, offset, string)`. |
+ void insert(int offset, String string, [sortId]) => |
+ replace(offset, offset, string, sortId); |
+ |
+ /// Remove text from the range [begin] to exclusive [end]. |
+ /// Equivalent to `replace(begin, end, '')`. |
+ void remove(int begin, int end) => replace(begin, end, ''); |
+ |
+ /// Applies all pending [edit]s and returns a new string. |
+ /// |
+ /// This method is non-destructive: it does not discard existing edits or |
+ /// change the [original] string. Further edits can be added and this method |
+ /// can be called again. |
+ /// |
+ /// Throws [UnsupportedError] if the edits were overlapping. If no edits were |
+ /// made, the original string will be returned. |
+ String toString() { |
+ var sb = new StringBuffer(); |
+ if (_edits.length == 0) return original; |
+ |
+ // Sort edits by start location. |
+ _edits.sort(); |
+ |
+ int consumed = 0; |
+ for (var edit in _edits) { |
+ if (consumed > edit.begin) { |
+ sb = new StringBuffer(); |
+ sb.write('overlapping edits. Insert at offset '); |
+ sb.write(edit.begin); |
+ sb.write(' but have consumed '); |
+ sb.write(consumed); |
+ sb.write(' input characters. List of edits:'); |
+ for (var e in _edits) { |
+ sb.write('\n '); |
+ sb.write(e); |
+ } |
+ throw new UnsupportedError(sb.toString()); |
+ } |
+ |
+ // Add characters from the original string between this edit and the last |
+ // one, if any. |
+ var betweenEdits = original.substring(consumed, edit.begin); |
+ sb.write(betweenEdits); |
+ sb.write(edit.string); |
+ consumed = edit.end; |
+ } |
+ |
+ // Add any text from the end of the original string that was not replaced. |
+ sb.write(original.substring(consumed)); |
+ return sb.toString(); |
+ } |
+} |
+ |
+/// A single edit in a [StringEditBuffer]. |
+class _StringEdit implements Comparable<_StringEdit> { |
+ // Offset where edit begins |
+ final int begin; |
+ |
+ // Offset where edit ends |
+ final int end; |
+ |
+ // Sort index as a tie-breaker for edits that have the same location. |
+ final int sortId; |
+ |
+ // String to insert |
+ final String string; |
+ |
+ _StringEdit(int begin, this.end, this.string, [int sortId]) |
+ : begin = begin, |
+ sortId = sortId == null ? begin : sortId; |
+ |
+ int get length => end - begin; |
+ |
+ String toString() => '(Edit @ $begin,$end: "$string")'; |
+ |
+ int compareTo(_StringEdit other) { |
+ int diff = begin - other.begin; |
+ if (diff != 0) return diff; |
+ diff = end - other.end; |
+ if (diff != 0) return diff; |
+ // use edit order as a tie breaker |
+ return sortId - other.sortId; |
+ } |
+} |