55import Char from 'typescript-char' ;
66import { BraceCounter } from '../language/braceCounter' ;
77import { TextBuilder } from '../language/textBuilder' ;
8+ import { TextRangeCollection } from '../language/textRangeCollection' ;
89import { Tokenizer } from '../language/tokenizer' ;
910import { ITextRangeCollection , IToken , TokenType } from '../language/types' ;
1011
1112export class LineFormatter {
12- private builder : TextBuilder ;
13- private tokens : ITextRangeCollection < IToken > ;
14- private braceCounter : BraceCounter ;
15- private text : string ;
13+ private builder = new TextBuilder ( ) ;
14+ private tokens : ITextRangeCollection < IToken > = new TextRangeCollection < IToken > ( [ ] ) ;
15+ private braceCounter = new BraceCounter ( ) ;
16+ private text = '' ;
1617
1718 // tslint:disable-next-line:cyclomatic-complexity
1819 public formatLine ( text : string ) : string {
@@ -27,7 +28,7 @@ export class LineFormatter {
2728
2829 const ws = this . text . substr ( 0 , this . tokens . getItemAt ( 0 ) . start ) ;
2930 if ( ws . length > 0 ) {
30- this . builder . append ( ws ) ; // Preserve leading indentation
31+ this . builder . append ( ws ) ; // Preserve leading indentation.
3132 }
3233
3334 for ( let i = 0 ; i < this . tokens . count ; i += 1 ) {
@@ -55,24 +56,28 @@ export class LineFormatter {
5556 break ;
5657
5758 case TokenType . Colon :
58- // x: 1 if not in slice, x[1:y] if inside the slice
59+ // x: 1 if not in slice, x[1:y] if inside the slice.
5960 this . builder . append ( ':' ) ;
6061 if ( ! this . braceCounter . isOpened ( TokenType . OpenBracket ) && ( next && next . type !== TokenType . Colon ) ) {
61- // Not inside opened [[ ... ] sequence
62+ // Not inside opened [[ ... ] sequence.
6263 this . builder . softAppendSpace ( ) ;
6364 }
6465 break ;
6566
6667 case TokenType . Comment :
67- // add space before in-line comment
68+ // Add space before in-line comment.
6869 if ( prev ) {
6970 this . builder . softAppendSpace ( ) ;
7071 }
7172 this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
7273 break ;
7374
75+ case TokenType . Semicolon :
76+ this . builder . append ( ';' ) ;
77+ break ;
78+
7479 default :
75- this . handleOther ( t ) ;
80+ this . handleOther ( t , i ) ;
7681 break ;
7782 }
7883 }
@@ -85,7 +90,7 @@ export class LineFormatter {
8590 const opCode = this . text . charCodeAt ( t . start ) ;
8691 switch ( opCode ) {
8792 case Char . Equal :
88- if ( index >= 2 && this . handleEqual ( t , index ) ) {
93+ if ( this . handleEqual ( t , index ) ) {
8994 return ;
9095 }
9196 break ;
@@ -105,27 +110,66 @@ export class LineFormatter {
105110 }
106111
107112 private handleEqual ( t : IToken , index : number ) : boolean {
108- if ( this . braceCounter . isOpened ( TokenType . OpenBrace ) ) {
109- // Check if this is = in function arguments. If so, do not
110- // add spaces around it.
111- const prev = this . tokens . getItemAt ( index - 1 ) ;
112- const prevPrev = this . tokens . getItemAt ( index - 2 ) ;
113- if ( prev . type === TokenType . Identifier &&
114- ( prevPrev . type === TokenType . Comma || prevPrev . type === TokenType . OpenBrace ) ) {
115- this . builder . append ( '=' ) ;
116- return true ;
117- }
113+ if ( this . isMultipleStatements ( index ) && ! this . braceCounter . isOpened ( TokenType . OpenBrace ) ) {
114+ return false ; // x = 1; x, y = y, x
115+ }
116+ // Check if this is = in function arguments. If so, do not add spaces around it.
117+ if ( this . isEqualsInsideArguments ( index ) ) {
118+ this . builder . append ( '=' ) ;
119+ return true ;
118120 }
119121 return false ;
120122 }
121123
122- private handleOther ( t : IToken ) : void {
124+ private handleOther ( t : IToken , index : number ) : void {
123125 if ( this . isBraceType ( t . type ) ) {
124126 this . braceCounter . countBrace ( t ) ;
127+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
128+ return ;
125129 }
130+
131+ if ( this . isEqualsInsideArguments ( index - 1 ) ) {
132+ // Don't add space around = inside function arguments.
133+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
134+ return ;
135+ }
136+
137+ if ( index > 0 ) {
138+ const prev = this . tokens . getItemAt ( index - 1 ) ;
139+ if ( this . isOpenBraceType ( prev . type ) || prev . type === TokenType . Colon ) {
140+ // Don't insert space after (, [ or { .
141+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
142+ return ;
143+ }
144+ }
145+
146+ // In general, keep tokens separated.
147+ this . builder . softAppendSpace ( ) ;
126148 this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
127149 }
128150
151+ private isEqualsInsideArguments ( index : number ) : boolean {
152+ if ( index < 1 ) {
153+ return false ;
154+ }
155+ const prev = this . tokens . getItemAt ( index - 1 ) ;
156+ if ( prev . type === TokenType . Identifier ) {
157+ if ( index >= 2 ) {
158+ // (x=1 or ,x=1
159+ const prevPrev = this . tokens . getItemAt ( index - 2 ) ;
160+ return prevPrev . type === TokenType . Comma || prevPrev . type === TokenType . OpenBrace ;
161+ } else if ( index < this . tokens . count - 2 ) {
162+ const next = this . tokens . getItemAt ( index + 1 ) ;
163+ const nextNext = this . tokens . getItemAt ( index + 2 ) ;
164+ // x=1, or x=1)
165+ if ( this . isValueType ( next . type ) ) {
166+ return nextNext . type === TokenType . Comma || nextNext . type === TokenType . CloseBrace ;
167+ }
168+ }
169+ }
170+ return false ;
171+ }
172+
129173 private isOpenBraceType ( type : TokenType ) : boolean {
130174 return type === TokenType . OpenBrace || type === TokenType . OpenBracket || type === TokenType . OpenCurly ;
131175 }
@@ -135,4 +179,16 @@ export class LineFormatter {
135179 private isBraceType ( type : TokenType ) : boolean {
136180 return this . isOpenBraceType ( type ) || this . isCloseBraceType ( type ) ;
137181 }
182+ private isValueType ( type : TokenType ) : boolean {
183+ return type === TokenType . Identifier || type === TokenType . Unknown ||
184+ type === TokenType . Number || type === TokenType . String ;
185+ }
186+ private isMultipleStatements ( index : number ) : boolean {
187+ for ( let i = index ; i >= 0 ; i -= 1 ) {
188+ if ( this . tokens . getItemAt ( i ) . type === TokenType . Semicolon ) {
189+ return true ;
190+ }
191+ }
192+ return false ;
193+ }
138194}
0 commit comments