Line data Source code
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 : import 'package:charcode/ascii.dart'; 6 : 7 : import 'line_scanner.dart'; 8 : import 'span_scanner.dart'; 9 : 10 : // TODO(nweiz): Currently this duplicates code in line_scanner.dart. Once 11 : // sdk#23770 is fully complete, we should move the shared code into a mixin. 12 : 13 : /// A regular expression matching newlines across platforms. 14 0 : final _newlineRegExp = RegExp(r'\r\n?|\n'); 15 : 16 : /// A [SpanScanner] that tracks the line and column eagerly, like [LineScanner]. 17 : class EagerSpanScanner extends SpanScanner { 18 0 : @override 19 0 : int get line => _line; 20 : int _line = 0; 21 : 22 0 : @override 23 0 : int get column => _column; 24 : int _column = 0; 25 : 26 0 : @override 27 : LineScannerState get state => 28 0 : _EagerSpanScannerState(this, position, line, column); 29 : 30 0 : bool get _betweenCRLF => peekChar(-1) == $cr && peekChar() == $lf; 31 : 32 0 : @override 33 : set state(LineScannerState state) { 34 0 : if (state is! _EagerSpanScannerState || !identical(state._scanner, this)) { 35 0 : throw ArgumentError('The given LineScannerState was not returned by ' 36 : 'this LineScanner.'); 37 : } 38 : 39 0 : super.position = state.position; 40 0 : _line = state.line; 41 0 : _column = state.column; 42 : } 43 : 44 0 : @override 45 : set position(int newPosition) { 46 0 : final oldPosition = position; 47 0 : super.position = newPosition; 48 : 49 0 : if (newPosition > oldPosition) { 50 0 : final newlines = _newlinesIn(string.substring(oldPosition, newPosition)); 51 0 : _line += newlines.length; 52 0 : if (newlines.isEmpty) { 53 0 : _column += newPosition - oldPosition; 54 : } else { 55 0 : _column = newPosition - newlines.last.end; 56 : } 57 : } else { 58 0 : final newlines = _newlinesIn(string.substring(newPosition, oldPosition)); 59 0 : if (_betweenCRLF) newlines.removeLast(); 60 : 61 0 : _line -= newlines.length; 62 0 : if (newlines.isEmpty) { 63 0 : _column -= oldPosition - newPosition; 64 : } else { 65 0 : _column = 66 0 : newPosition - string.lastIndexOf(_newlineRegExp, newPosition) - 1; 67 : } 68 : } 69 : } 70 : 71 0 : EagerSpanScanner(String string, {sourceUrl, int? position}) 72 0 : : super(string, sourceUrl: sourceUrl, position: position); 73 : 74 0 : @override 75 : bool scanChar(int character) { 76 0 : if (!super.scanChar(character)) return false; 77 0 : _adjustLineAndColumn(character); 78 : return true; 79 : } 80 : 81 0 : @override 82 : int readChar() { 83 0 : final character = super.readChar(); 84 0 : _adjustLineAndColumn(character); 85 : return character; 86 : } 87 : 88 : /// Adjusts [_line] and [_column] after having consumed [character]. 89 0 : void _adjustLineAndColumn(int character) { 90 0 : if (character == $lf || (character == $cr && peekChar() != $lf)) { 91 0 : _line += 1; 92 0 : _column = 0; 93 : } else { 94 0 : _column += 1; 95 : } 96 : } 97 : 98 0 : @override 99 : bool scan(Pattern pattern) { 100 0 : if (!super.scan(pattern)) return false; 101 0 : final firstMatch = (lastMatch![0])!; 102 : 103 0 : final newlines = _newlinesIn(firstMatch); 104 0 : _line += newlines.length; 105 0 : if (newlines.isEmpty) { 106 0 : _column += firstMatch.length; 107 : } else { 108 0 : _column = firstMatch.length - newlines.last.end; 109 : } 110 : 111 : return true; 112 : } 113 : 114 : /// Returns a list of [Match]es describing all the newlines in [text], which 115 : /// is assumed to end at [position]. 116 0 : List<Match> _newlinesIn(String text) { 117 0 : final newlines = _newlineRegExp.allMatches(text).toList(); 118 0 : if (_betweenCRLF) newlines.removeLast(); 119 : return newlines; 120 : } 121 : } 122 : 123 : /// A class representing the state of an [EagerSpanScanner]. 124 : class _EagerSpanScannerState implements LineScannerState { 125 : final EagerSpanScanner _scanner; 126 : @override 127 : final int position; 128 : @override 129 : final int line; 130 : @override 131 : final int column; 132 : 133 0 : _EagerSpanScannerState(this._scanner, this.position, this.line, this.column); 134 : }