OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 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. |
| 4 |
| 5 /// Defines [StringEditBuffer], a buffer that can be used to apply edits on a |
| 6 /// string. |
| 7 // TODO(sigmund): this should move to a separate package. |
| 8 library dart2js.src.string_edit_buffer; |
| 9 |
| 10 /// A buffer meant to apply edits on a string (rather than building a string |
| 11 /// from scratch). Each change is described using the location information on |
| 12 /// the original string. Internally this buffer keeps track of how a |
| 13 /// modification in one portion can offset a modification further down the |
| 14 /// string. |
| 15 class StringEditBuffer { |
| 16 final String original; |
| 17 final _edits = <_StringEdit>[]; |
| 18 |
| 19 StringEditBuffer(this.original); |
| 20 |
| 21 bool get hasEdits => _edits.length > 0; |
| 22 |
| 23 /// Edit the original text, replacing text on the range [begin] and |
| 24 /// exclusive [end] with the [replacement] string. |
| 25 void replace(int begin, int end, String replacement, [int sortId]) { |
| 26 _edits.add(new _StringEdit(begin, end, replacement, sortId)); |
| 27 } |
| 28 |
| 29 /// Insert [string] at [offset]. |
| 30 /// Equivalent to `replace(offset, offset, string)`. |
| 31 void insert(int offset, String string, [sortId]) => |
| 32 replace(offset, offset, string, sortId); |
| 33 |
| 34 /// Remove text from the range [begin] to exclusive [end]. |
| 35 /// Equivalent to `replace(begin, end, '')`. |
| 36 void remove(int begin, int end) => replace(begin, end, ''); |
| 37 |
| 38 /// Applies all pending [edit]s and returns a new string. |
| 39 /// |
| 40 /// This method is non-destructive: it does not discard existing edits or |
| 41 /// change the [original] string. Further edits can be added and this method |
| 42 /// can be called again. |
| 43 /// |
| 44 /// Throws [UnsupportedError] if the edits were overlapping. If no edits were |
| 45 /// made, the original string will be returned. |
| 46 String toString() { |
| 47 var sb = new StringBuffer(); |
| 48 if (_edits.length == 0) return original; |
| 49 |
| 50 // Sort edits by start location. |
| 51 _edits.sort(); |
| 52 |
| 53 int consumed = 0; |
| 54 for (var edit in _edits) { |
| 55 if (consumed > edit.begin) { |
| 56 sb = new StringBuffer(); |
| 57 sb.write('overlapping edits. Insert at offset '); |
| 58 sb.write(edit.begin); |
| 59 sb.write(' but have consumed '); |
| 60 sb.write(consumed); |
| 61 sb.write(' input characters. List of edits:'); |
| 62 for (var e in _edits) { |
| 63 sb.write('\n '); |
| 64 sb.write(e); |
| 65 } |
| 66 throw new UnsupportedError(sb.toString()); |
| 67 } |
| 68 |
| 69 // Add characters from the original string between this edit and the last |
| 70 // one, if any. |
| 71 var betweenEdits = original.substring(consumed, edit.begin); |
| 72 sb.write(betweenEdits); |
| 73 sb.write(edit.string); |
| 74 consumed = edit.end; |
| 75 } |
| 76 |
| 77 // Add any text from the end of the original string that was not replaced. |
| 78 sb.write(original.substring(consumed)); |
| 79 return sb.toString(); |
| 80 } |
| 81 } |
| 82 |
| 83 /// A single edit in a [StringEditBuffer]. |
| 84 class _StringEdit implements Comparable<_StringEdit> { |
| 85 // Offset where edit begins |
| 86 final int begin; |
| 87 |
| 88 // Offset where edit ends |
| 89 final int end; |
| 90 |
| 91 // Sort index as a tie-breaker for edits that have the same location. |
| 92 final int sortId; |
| 93 |
| 94 // String to insert |
| 95 final String string; |
| 96 |
| 97 _StringEdit(int begin, this.end, this.string, [int sortId]) |
| 98 : begin = begin, |
| 99 sortId = sortId == null ? begin : sortId; |
| 100 |
| 101 int get length => end - begin; |
| 102 |
| 103 String toString() => '(Edit @ $begin,$end: "$string")'; |
| 104 |
| 105 int compareTo(_StringEdit other) { |
| 106 int diff = begin - other.begin; |
| 107 if (diff != 0) return diff; |
| 108 diff = end - other.end; |
| 109 if (diff != 0) return diff; |
| 110 // use edit order as a tie breaker |
| 111 return sortId - other.sortId; |
| 112 } |
| 113 } |
OLD | NEW |