@@ -77,30 +77,20 @@ private void AddToken(TokenExt token) {
7777 /// <param name="includeToken">A function which returns true if the token should be added to the final list. If null, all tokens will be added.</param>
7878 /// <returns>A non-null list of tokens on that line.</returns>
7979 private List < TokenExt > TokenizeLine ( int line , Func < TokenExt , bool > includeToken = null ) {
80- Check . Argument ( nameof ( line ) , ( ) => line > 0 ) ;
80+ Check . Argument ( nameof ( line ) , ( ) => line >= 0 ) ;
8181
8282 var extraToken = true ;
83-
84- var peeked = _tokenizer . Peek ( ) ;
85- while ( peeked != null && ( peeked . Line <= line || extraToken ) ) {
86- var token = _tokenizer . Next ( ) ;
87-
88- if ( includeToken == null || includeToken ( token ) ) {
89- AddToken ( token ) ;
83+ for ( var current = _tokenizer . Current ?? _tokenizer . Next ( ) ; current != null && ( current . Line <= line || extraToken ) ; current = _tokenizer . Next ( ) ) {
84+ if ( includeToken == null || includeToken ( current ) ) {
85+ AddToken ( current ) ;
9086 }
9187
92- peeked = _tokenizer . Peek ( ) ;
93-
94- if ( token . Line > line && ! token . IsIgnored ) {
88+ if ( current . Line > line && ! current . IsIgnored ) {
9589 extraToken = false ;
9690 }
9791 }
9892
99- if ( ! _lineTokens . TryGetValue ( line , out List < TokenExt > tokens ) ) {
100- return new List < TokenExt > ( ) ;
101- }
102-
103- return tokens ;
93+ return _lineTokens . TryGetValue ( line , out var tokens ) ? tokens : new List < TokenExt > ( ) ;
10494 }
10595
10696 /// <summary>
@@ -109,11 +99,10 @@ private List<TokenExt> TokenizeLine(int line, Func<TokenExt, bool> includeToken
10999 /// <param name="line">One-indexed line number.</param>
110100 /// <returns>A list of TextEdits needed to format the line.</returns>
111101 public TextEdit [ ] FormatLine ( int line ) {
112- if ( line < 1 ) {
102+ if ( line < 0 ) {
113103 return NoEdits ;
114104 }
115-
116- // Keep ExplictLineJoin because it has text associated with it.
105+ // Keep ExplicitLineJoin because it has text associated with it.
117106 var tokens = TokenizeLine ( line , t => ! t . IsIgnored || t . Kind == TokenKind . ExplicitLineJoin ) ;
118107
119108 if ( tokens . Count == 0 ) {
@@ -122,14 +111,14 @@ public TextEdit[] FormatLine(int line) {
122111
123112 var builder = new StringBuilder ( ) ;
124113 var first = tokens [ 0 ] ;
125- var beginCol = first . Span . Start . Column ;
114+ var startIndex = first . Span . Start ;
126115 var startIdx = 0 ;
127116
128117 if ( first . IsMultilineString ) {
129118 // If the first token is a multiline string, start the edit afterward,
130119 // skip looking at the first token, and ensure that there's a space
131120 // after it if needed (i.e. in the case of a following comment).
132- beginCol = first . Span . End . Column ;
121+ startIndex = first . Span . End ;
133122 startIdx = 1 ;
134123 if ( builder . Length == 0 ) {
135124 builder . Append ( ' ' ) ;
@@ -357,7 +346,7 @@ public TextEdit[] FormatLine(int line) {
357346 }
358347 }
359348
360- var endCol = _tokenizer . EndOfLineCol ( line ) ;
349+ var endIndex = _tokenizer . GetLineEndIndex ( line ) ;
361350
362351 var afterLast = tokens . Last ( ) . Next ;
363352 if ( afterLast != null && afterLast . IsMultilineString ) {
@@ -368,16 +357,17 @@ public TextEdit[] FormatLine(int line) {
368357 }
369358
370359 builder . TrimEnd ( ) ;
371- var newText = builder . ToString ( ) ;
372-
373- if ( newText . Length == 0 ) {
360+ if ( builder . Length == 0 ) {
374361 return NoEdits ;
375362 }
376363
364+ var newText = builder . ToString ( ) ;
365+ var lineStartIndex = _tokenizer . GetLineStartIndex ( line ) ;
366+
377367 var edit = new TextEdit {
378368 range = new Range {
379- start = new SourceLocation ( line , beginCol ) ,
380- end = new SourceLocation ( line , endCol )
369+ start = new Position { line = line , character = startIndex - lineStartIndex } ,
370+ end = new Position { line = line , character = endIndex - lineStartIndex }
381371 } ,
382372 newText = newText
383373 } ;
@@ -391,13 +381,25 @@ private static void AppendTokenEnsureWhiteSpacesAround(StringBuilder builder, To
391381 . EnsureEndsWithWhiteSpace ( ) ;
392382
393383 private class TokenExt {
394- public Token Token { get ; set ; }
395- public SourceSpan Span { get ; set ; }
396- public int Line => Span . End . Line ;
384+ public TokenExt ( Token token , string precedingWhitespace , IndexSpan span , int line , bool isMultiLine ,
385+ TokenExt prev ) {
386+ Token = token ;
387+ PrecedingWhitespace = precedingWhitespace ;
388+ Span = span ;
389+ Line = line ;
390+ Prev = prev ;
391+ IsMultilineString = IsString && isMultiLine ;
392+ }
393+
394+ public Token Token { get ; }
395+ public IndexSpan Span { get ; }
396+ public int Line { get ; }
397397 public TokenExt Inside { get ; set ; }
398- public TokenExt Prev { get ; set ; }
398+ public TokenExt Prev { get ; }
399399 public TokenExt Next { get ; set ; }
400- public string PrecedingWhitespace { get ; set ; }
400+ public string PrecedingWhitespace { get ; }
401+ public bool IsMultilineString { get ; }
402+
401403 public TokenKind Kind => Token . Kind ;
402404
403405 public override string ToString ( ) => Token . VerbatimImage ;
@@ -434,9 +436,7 @@ public bool MatchesClose(TokenExt other) {
434436 public bool IsKeyword => ( Kind >= TokenKind . FirstKeyword && Kind <= TokenKind . LastKeyword ) || Kind == TokenKind . KeywordAsync || Kind == TokenKind . KeywordAwait ;
435437
436438 public bool IsString => Kind == TokenKind . Constant && Token != Tokens . NoneToken && ( Token . Value is string || Token . Value is AsciiString ) ;
437-
438- public bool IsMultilineString => Span . Start . Line != Span . End . Line && IsString ;
439-
439+
440440 public bool IsSimpleSliceToLeft {
441441 get {
442442 if ( Kind != TokenKind . Colon ) {
@@ -540,26 +540,17 @@ public TokenExt NextNonIgnored {
540540 private class TokenizerWrapper {
541541 private readonly Tokenizer _tokenizer ;
542542 private readonly Stack < TokenExt > _insides = new Stack < TokenExt > ( ) ;
543- private TokenExt _peeked = null ;
544- private TokenExt _prev = null ;
543+ public TokenExt Current { get ; private set ; }
545544
546545 public TokenizerWrapper ( Tokenizer tokenizer ) {
547546 _tokenizer = tokenizer ;
548547 }
549548
550549 /// <summary>
551- /// Returns the next token, and advances the tokenizer. Note that
552- /// the returned token's Next will not be set until the tokenizer
553- /// actually reads that next token.
550+ /// Returns the next token, and advances the tokenizer.
554551 /// </summary>
555552 /// <returns>The next token</returns>
556553 public TokenExt Next ( ) {
557- if ( _peeked != null ) {
558- var tmp = _peeked ;
559- _peeked = null ;
560- return tmp ;
561- }
562-
563554 if ( _tokenizer . IsEndOfFile ) {
564555 return null ;
565556 }
@@ -571,14 +562,18 @@ public TokenExt Next() {
571562 }
572563
573564 var tokenSpan = _tokenizer . TokenSpan ;
574- var sourceSpan = new SourceSpan ( _tokenizer . IndexToLocation ( tokenSpan . Start ) , _tokenizer . IndexToLocation ( tokenSpan . End ) ) ;
575-
576- var tokenExt = new TokenExt {
577- Token = token ,
578- PrecedingWhitespace = _tokenizer . PreceedingWhiteSpace ,
579- Span = sourceSpan ,
580- Prev = _prev
581- } ;
565+ var line = _tokenizer . CurrentLine ;
566+ var lineStart = GetLineStartIndex ( line ) ;
567+ var isMultiLine = tokenSpan . Start < lineStart ;
568+
569+ var tokenExt = new TokenExt (
570+ token ,
571+ _tokenizer . PreceedingWhiteSpace ,
572+ tokenSpan ,
573+ line ,
574+ isMultiLine ,
575+ Current
576+ ) ;
582577
583578 if ( tokenExt . IsClose ) {
584579 if ( _insides . Count == 0 || ! _insides . Peek ( ) . MatchesClose ( tokenExt ) ) {
@@ -597,52 +592,27 @@ public TokenExt Next() {
597592 _insides . Push ( tokenExt ) ;
598593 }
599594
600- if ( _prev != null ) {
601- _prev . Next = tokenExt ;
595+ if ( Current != null ) {
596+ Current . Next = tokenExt ;
602597 }
603598
604- _prev = tokenExt ;
599+ Current = tokenExt ;
605600 return tokenExt ;
606601 }
607602
608603 /// <summary>
609- /// Returns the next token without advancing the tokenizer. Note that
610- /// the returned token's Next will not be set until the tokenizer
611- /// actually reads that next token.
604+ /// Gets the index of the start of the line
612605 /// </summary>
613- /// <returns>The next token</returns>
614- public TokenExt Peek ( ) {
615- if ( _peeked != null ) {
616- return _peeked ;
617- }
618-
619- _peeked = Next ( ) ;
620- return _peeked ;
621- }
606+ /// <param name="line">Line number.</param>
607+ public int GetLineStartIndex ( int line ) => line > 0 ? _tokenizer . GetNewLineLocation ( line - 1 ) . EndIndex : 0 ;
622608
623609 /// <summary>
624- /// Gets the one-indexed column number of the end of a line. The
625- /// tokenizer must be past the line's newline (or at EOF) in order
626- /// for this function to work.
610+ /// Gets the index of the end of the line, excluding line break
627611 /// </summary>
628- /// <param name="line">A one-indexed line number.</param>
629- /// <returns>One-indexed column number for the end of the line</returns>
630- public int EndOfLineCol ( int line ) {
631- if ( line > _tokenizer . CurrentPosition . Line || ( line == _tokenizer . CurrentPosition . Line && ! _tokenizer . IsEndOfFile ) ) {
632- throw new ArgumentException ( "tokenizer must be at EOF or past line's newline" , nameof ( line ) ) ;
633- }
634-
635- var idx = line - 1 ;
636- var lines = _tokenizer . GetLineLocations ( ) ;
637-
638- if ( idx < lines . Length ) {
639- var nlLoc = lines [ idx ] ;
640-
641- var sourceLocation = _tokenizer . IndexToLocation ( nlLoc . EndIndex - 1 ) ;
642- return sourceLocation . Column ;
643- }
644-
645- return _tokenizer . CurrentPosition . Column ;
612+ /// <param name="line">Line number.</param>
613+ public int GetLineEndIndex ( int line ) {
614+ var newLineLocation = _tokenizer . GetNewLineLocation ( line ) ;
615+ return newLineLocation . EndIndex - newLineLocation . Kind . GetSize ( ) ;
646616 }
647617 }
648618
0 commit comments