| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2014, 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 library engine.incremental_scanner; |
| 6 |
| 7 import "dart:math" as math; |
| 8 |
| 9 import 'error.dart'; |
| 10 import 'scanner.dart'; |
| 11 import 'source.dart'; |
| 12 import 'utilities_collection.dart' show TokenMap; |
| 13 |
| 14 /** |
| 15 * Instances of the class `IncrementalScanner` implement a scanner that scans a
subset of a |
| 16 * string and inserts the resulting tokens into the middle of an existing token
stream. |
| 17 */ |
| 18 class IncrementalScanner extends Scanner { |
| 19 /** |
| 20 * The reader used to access the characters in the source. |
| 21 */ |
| 22 CharacterReader _reader; |
| 23 |
| 24 /** |
| 25 * A map from tokens that were copied to the copies of the tokens. |
| 26 */ |
| 27 TokenMap _tokenMap = new TokenMap(); |
| 28 |
| 29 /** |
| 30 * The token in the new token stream immediately to the left of the range of t
okens that were |
| 31 * inserted, or the token immediately to the left of the modified region if th
ere were no new |
| 32 * tokens. |
| 33 */ |
| 34 Token _leftToken; |
| 35 |
| 36 /** |
| 37 * The token in the new token stream immediately to the right of the range of
tokens that were |
| 38 * inserted, or the token immediately to the right of the modified region if t
here were no new |
| 39 * tokens. |
| 40 */ |
| 41 Token _rightToken; |
| 42 |
| 43 /** |
| 44 * A flag indicating whether there were any tokens changed as a result of the
modification. |
| 45 */ |
| 46 bool _hasNonWhitespaceChange = false; |
| 47 |
| 48 /** |
| 49 * Initialize a newly created scanner. |
| 50 * |
| 51 * @param source the source being scanned |
| 52 * @param reader the character reader used to read the characters in the sourc
e |
| 53 * @param errorListener the error listener that will be informed of any errors
that are found |
| 54 */ |
| 55 IncrementalScanner(Source source, CharacterReader reader, |
| 56 AnalysisErrorListener errorListener) |
| 57 : super(source, reader, errorListener) { |
| 58 this._reader = reader; |
| 59 } |
| 60 |
| 61 /** |
| 62 * Return `true` if there were any tokens either added or removed (or both) as
a result of |
| 63 * the modification. |
| 64 * |
| 65 * @return `true` if there were any tokens changed as a result of the modifica
tion |
| 66 */ |
| 67 bool get hasNonWhitespaceChange => _hasNonWhitespaceChange; |
| 68 |
| 69 /** |
| 70 * Return the token in the new token stream immediately to the left of the ran
ge of tokens that |
| 71 * were inserted, or the token immediately to the left of the modified region
if there were no new |
| 72 * tokens. |
| 73 * |
| 74 * @return the token to the left of the inserted tokens |
| 75 */ |
| 76 Token get leftToken => _leftToken; |
| 77 |
| 78 /** |
| 79 * Return the token in the new token stream immediately to the right of the ra
nge of tokens that |
| 80 * were inserted, or the token immediately to the right of the modified region
if there were no |
| 81 * new tokens. |
| 82 * |
| 83 * @return the token to the right of the inserted tokens |
| 84 */ |
| 85 Token get rightToken => _rightToken; |
| 86 |
| 87 /** |
| 88 * Return a map from tokens that were copied to the copies of the tokens. |
| 89 * |
| 90 * @return a map from tokens that were copied to the copies of the tokens |
| 91 */ |
| 92 TokenMap get tokenMap => _tokenMap; |
| 93 |
| 94 /** |
| 95 * Given the stream of tokens scanned from the original source, the modified s
ource (the result of |
| 96 * replacing one contiguous range of characters with another string of charact
ers), and a |
| 97 * specification of the modification that was made, return a stream of tokens
scanned from the |
| 98 * modified source. The original stream of tokens will not be modified. |
| 99 * |
| 100 * @param originalStream the stream of tokens scanned from the original source |
| 101 * @param index the index of the first character in both the original and modi
fied source that was |
| 102 * affected by the modification |
| 103 * @param removedLength the number of characters removed from the original sou
rce |
| 104 * @param insertedLength the number of characters added to the modified source |
| 105 */ |
| 106 Token rescan(Token originalStream, int index, int removedLength, |
| 107 int insertedLength) { |
| 108 // |
| 109 // Copy all of the tokens in the originalStream whose end is less than the |
| 110 // replacement start. (If the replacement start is equal to the end of an |
| 111 // existing token, then it means that the existing token might have been |
| 112 // modified, so we need to rescan it.) |
| 113 // |
| 114 while (originalStream.type != TokenType.EOF && originalStream.end < index) { |
| 115 originalStream = _copyAndAdvance(originalStream, 0); |
| 116 } |
| 117 Token oldFirst = originalStream; |
| 118 Token oldLeftToken = originalStream.previous; |
| 119 _leftToken = tail; |
| 120 // |
| 121 // Skip tokens in the original stream until we find a token whose offset is |
| 122 // greater than the end of the removed region. (If the end of the removed |
| 123 // region is equal to the beginning of an existing token, then it means that |
| 124 // the existing token might have been modified, so we need to rescan it.) |
| 125 // |
| 126 int removedEnd = index + (removedLength == 0 ? 0 : removedLength - 1); |
| 127 while (originalStream.type != TokenType.EOF && |
| 128 originalStream.offset <= removedEnd) { |
| 129 originalStream = originalStream.next; |
| 130 } |
| 131 Token oldLast; |
| 132 Token oldRightToken; |
| 133 if (originalStream.type != TokenType.EOF && |
| 134 removedEnd + 1 == originalStream.offset) { |
| 135 oldLast = originalStream; |
| 136 originalStream = originalStream.next; |
| 137 oldRightToken = originalStream; |
| 138 } else { |
| 139 oldLast = originalStream.previous; |
| 140 oldRightToken = originalStream; |
| 141 } |
| 142 // |
| 143 // Compute the delta between the character index of characters after the |
| 144 // modified region in the original source and the index of the corresponding |
| 145 // character in the modified source. |
| 146 // |
| 147 int delta = insertedLength - removedLength; |
| 148 // |
| 149 // Compute the range of characters that are known to need to be rescanned. |
| 150 // If the index is within an existing token, then we need to start at the |
| 151 // beginning of the token. |
| 152 // |
| 153 int scanStart = math.min(oldFirst.offset, index); |
| 154 int oldEnd = oldLast.end + delta - 1; |
| 155 int newEnd = index + insertedLength - 1; |
| 156 int scanEnd = math.max(newEnd, oldEnd); |
| 157 // |
| 158 // Starting at the start of the scan region, scan tokens from the |
| 159 // modifiedSource until the end of the just scanned token is greater than or |
| 160 // equal to end of the scan region in the modified source. Include trailing |
| 161 // characters of any token that was split as a result of inserted text, |
| 162 // as in "ab" --> "a.b". |
| 163 // |
| 164 _reader.offset = scanStart - 1; |
| 165 int next = _reader.advance(); |
| 166 while (next != -1 && _reader.offset <= scanEnd) { |
| 167 next = bigSwitch(next); |
| 168 } |
| 169 // |
| 170 // Copy the remaining tokens in the original stream, but apply the delta to |
| 171 // the token's offset. |
| 172 // |
| 173 if (originalStream.type == TokenType.EOF) { |
| 174 _copyAndAdvance(originalStream, delta); |
| 175 _rightToken = tail; |
| 176 _rightToken.setNextWithoutSettingPrevious(_rightToken); |
| 177 } else { |
| 178 originalStream = _copyAndAdvance(originalStream, delta); |
| 179 _rightToken = tail; |
| 180 while (originalStream.type != TokenType.EOF) { |
| 181 originalStream = _copyAndAdvance(originalStream, delta); |
| 182 } |
| 183 Token eof = _copyAndAdvance(originalStream, delta); |
| 184 eof.setNextWithoutSettingPrevious(eof); |
| 185 } |
| 186 // |
| 187 // If the index is immediately after an existing token and the inserted |
| 188 // characters did not change that original token, then adjust the leftToken |
| 189 // to be the next token. For example, in "a; c;" --> "a;b c;", the leftToken |
| 190 // was ";", but this code advances it to "b" since "b" is the first new |
| 191 // token. |
| 192 // |
| 193 Token newFirst = _leftToken.next; |
| 194 while (!identical(newFirst, _rightToken) && |
| 195 !identical(oldFirst, oldRightToken) && |
| 196 newFirst.type != TokenType.EOF && |
| 197 _equalTokens(oldFirst, newFirst)) { |
| 198 _tokenMap.put(oldFirst, newFirst); |
| 199 oldLeftToken = oldFirst; |
| 200 oldFirst = oldFirst.next; |
| 201 _leftToken = newFirst; |
| 202 newFirst = newFirst.next; |
| 203 } |
| 204 Token newLast = _rightToken.previous; |
| 205 while (!identical(newLast, _leftToken) && |
| 206 !identical(oldLast, oldLeftToken) && |
| 207 newLast.type != TokenType.EOF && |
| 208 _equalTokens(oldLast, newLast)) { |
| 209 _tokenMap.put(oldLast, newLast); |
| 210 oldRightToken = oldLast; |
| 211 oldLast = oldLast.previous; |
| 212 _rightToken = newLast; |
| 213 newLast = newLast.previous; |
| 214 } |
| 215 _hasNonWhitespaceChange = !identical(_leftToken.next, _rightToken) || |
| 216 !identical(oldLeftToken.next, oldRightToken); |
| 217 // |
| 218 // TODO(brianwilkerson) Begin tokens are not getting associated with the |
| 219 // corresponding end tokens (because the end tokens have not been copied |
| 220 // when we're copying the begin tokens). This could have implications for |
| 221 // parsing. |
| 222 // TODO(brianwilkerson) Update the lineInfo. |
| 223 // |
| 224 return firstToken; |
| 225 } |
| 226 |
| 227 Token _copyAndAdvance(Token originalToken, int delta) { |
| 228 Token copiedToken = originalToken.copy(); |
| 229 _tokenMap.put(originalToken, copiedToken); |
| 230 copiedToken.offset += delta; |
| 231 appendToken(copiedToken); |
| 232 Token originalComment = originalToken.precedingComments; |
| 233 Token copiedComment = originalToken.precedingComments; |
| 234 while (originalComment != null) { |
| 235 _tokenMap.put(originalComment, copiedComment); |
| 236 originalComment = originalComment.next; |
| 237 copiedComment = copiedComment.next; |
| 238 } |
| 239 return originalToken.next; |
| 240 } |
| 241 |
| 242 /** |
| 243 * Return `true` if the two tokens are equal to each other. For the purposes o
f the |
| 244 * incremental scanner, two tokens are equal if they have the same type and le
xeme. |
| 245 * |
| 246 * @param oldToken the token from the old stream that is being compared |
| 247 * @param newToken the token from the new stream that is being compared |
| 248 * @return `true` if the two tokens are equal to each other |
| 249 */ |
| 250 bool _equalTokens(Token oldToken, Token newToken) => |
| 251 oldToken.type == newToken.type && |
| 252 oldToken.length == newToken.length && |
| 253 oldToken.lexeme == newToken.lexeme; |
| 254 } |
| OLD | NEW |