| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 |  | 
| 3 // BSD-style license that can be found in the LICENSE file. |  | 
| 4 |  | 
| 5 /// Tools to help implement refactoring like transformations to Dart code. |  | 
| 6 /// |  | 
| 7 /// [TextEditTransaction] supports making a series of changes to a text buffer. |  | 
| 8 /// [guessIndent] helps to guess the appropriate indentiation for the new code. |  | 
| 9 library source_maps.refactor; |  | 
| 10 |  | 
| 11 import 'package:source_span/source_span.dart'; |  | 
| 12 |  | 
| 13 import 'printer.dart'; |  | 
| 14 |  | 
| 15 /// Editable text transaction. |  | 
| 16 /// |  | 
| 17 /// Applies a series of edits using original location |  | 
| 18 /// information, and composes them into the edited string. |  | 
| 19 class TextEditTransaction { |  | 
| 20   final SourceFile file; |  | 
| 21   final String original; |  | 
| 22   final _edits = <_TextEdit>[]; |  | 
| 23 |  | 
| 24   /// Creates a new transaction. |  | 
| 25   TextEditTransaction(this.original, this.file); |  | 
| 26 |  | 
| 27   bool get hasEdits => _edits.length > 0; |  | 
| 28 |  | 
| 29   /// Edit the original text, replacing text on the range [begin] and [end] |  | 
| 30   /// with the [replacement]. [replacement] can be either a string or a |  | 
| 31   /// [NestedPrinter]. |  | 
| 32   void edit(int begin, int end, replacement) { |  | 
| 33     _edits.add(new _TextEdit(begin, end, replacement)); |  | 
| 34   } |  | 
| 35 |  | 
| 36   /// Create a source map [SourceLocation] for [offset]. |  | 
| 37   SourceLocation _loc(int offset) => |  | 
| 38       file != null ? file.location(offset) : null; |  | 
| 39 |  | 
| 40   /// Applies all pending [edit]s and returns a [NestedPrinter] containing the |  | 
| 41   /// rewritten string and source map information. [filename] is given to the |  | 
| 42   /// underlying printer to indicate the name of the generated file that will |  | 
| 43   /// contains the source map information. |  | 
| 44   /// |  | 
| 45   /// Throws [UnsupportedError] if the edits were overlapping. If no edits were |  | 
| 46   /// made, the printer simply contains the original string. |  | 
| 47   NestedPrinter commit() { |  | 
| 48     var printer = new NestedPrinter(); |  | 
| 49     if (_edits.length == 0) { |  | 
| 50       return printer..add(original, location: _loc(0), isOriginal: true); |  | 
| 51     } |  | 
| 52 |  | 
| 53     // Sort edits by start location. |  | 
| 54     _edits.sort(); |  | 
| 55 |  | 
| 56     int consumed = 0; |  | 
| 57     for (var edit in _edits) { |  | 
| 58       if (consumed > edit.begin) { |  | 
| 59         var sb = new StringBuffer(); |  | 
| 60         sb..write(file.location(edit.begin).toolString) |  | 
| 61             ..write(': overlapping edits. Insert at offset ') |  | 
| 62             ..write(edit.begin) |  | 
| 63             ..write(' but have consumed ') |  | 
| 64             ..write(consumed) |  | 
| 65             ..write(' input characters. List of edits:'); |  | 
| 66         for (var e in _edits) sb..write('\n    ')..write(e); |  | 
| 67         throw new UnsupportedError(sb.toString()); |  | 
| 68       } |  | 
| 69 |  | 
| 70       // Add characters from the original string between this edit and the last |  | 
| 71       // one, if any. |  | 
| 72       var betweenEdits = original.substring(consumed, edit.begin); |  | 
| 73       printer..add(betweenEdits, location: _loc(consumed), isOriginal: true) |  | 
| 74              ..add(edit.replace, location: _loc(edit.begin)); |  | 
| 75       consumed = edit.end; |  | 
| 76     } |  | 
| 77 |  | 
| 78     // Add any text from the end of the original string that was not replaced. |  | 
| 79     printer.add(original.substring(consumed), |  | 
| 80         location: _loc(consumed), isOriginal: true); |  | 
| 81     return printer; |  | 
| 82   } |  | 
| 83 } |  | 
| 84 |  | 
| 85 class _TextEdit implements Comparable<_TextEdit> { |  | 
| 86   final int begin; |  | 
| 87   final int end; |  | 
| 88 |  | 
| 89   /// The replacement used by the edit, can be a string or a [NestedPrinter]. |  | 
| 90   final replace; |  | 
| 91 |  | 
| 92   _TextEdit(this.begin, this.end, this.replace); |  | 
| 93 |  | 
| 94   int get length => end - begin; |  | 
| 95 |  | 
| 96   String toString() => '(Edit @ $begin,$end: "$replace")'; |  | 
| 97 |  | 
| 98   int compareTo(_TextEdit other) { |  | 
| 99     int diff = begin - other.begin; |  | 
| 100     if (diff != 0) return diff; |  | 
| 101     return end - other.end; |  | 
| 102   } |  | 
| 103 } |  | 
| 104 |  | 
| 105 /// Returns all whitespace characters at the start of [charOffset]'s line. |  | 
| 106 String guessIndent(String code, int charOffset) { |  | 
| 107   // Find the beginning of the line |  | 
| 108   int lineStart = 0; |  | 
| 109   for (int i = charOffset - 1; i >= 0; i--) { |  | 
| 110     var c = code.codeUnitAt(i); |  | 
| 111     if (c == _LF || c == _CR) { |  | 
| 112       lineStart = i + 1; |  | 
| 113       break; |  | 
| 114     } |  | 
| 115   } |  | 
| 116 |  | 
| 117   // Grab all the whitespace |  | 
| 118   int whitespaceEnd = code.length; |  | 
| 119   for (int i = lineStart; i < code.length; i++) { |  | 
| 120     var c = code.codeUnitAt(i); |  | 
| 121     if (c != _SPACE && c != _TAB) { |  | 
| 122       whitespaceEnd = i; |  | 
| 123       break; |  | 
| 124     } |  | 
| 125   } |  | 
| 126 |  | 
| 127   return code.substring(lineStart, whitespaceEnd); |  | 
| 128 } |  | 
| 129 |  | 
| 130 const int _CR = 13; |  | 
| 131 const int _LF = 10; |  | 
| 132 const int _TAB = 9; |  | 
| 133 const int _SPACE = 32; |  | 
| OLD | NEW | 
|---|