5
5
import Char from 'typescript-char' ;
6
6
import { BraceCounter } from '../language/braceCounter' ;
7
7
import { TextBuilder } from '../language/textBuilder' ;
8
+ import { TextRangeCollection } from '../language/textRangeCollection' ;
8
9
import { Tokenizer } from '../language/tokenizer' ;
9
10
import { ITextRangeCollection , IToken , TokenType } from '../language/types' ;
10
11
11
12
export 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 = '' ;
16
17
17
18
// tslint:disable-next-line:cyclomatic-complexity
18
19
public formatLine ( text : string ) : string {
@@ -27,7 +28,7 @@ export class LineFormatter {
27
28
28
29
const ws = this . text . substr ( 0 , this . tokens . getItemAt ( 0 ) . start ) ;
29
30
if ( ws . length > 0 ) {
30
- this . builder . append ( ws ) ; // Preserve leading indentation
31
+ this . builder . append ( ws ) ; // Preserve leading indentation.
31
32
}
32
33
33
34
for ( let i = 0 ; i < this . tokens . count ; i += 1 ) {
@@ -55,24 +56,28 @@ export class LineFormatter {
55
56
break ;
56
57
57
58
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.
59
60
this . builder . append ( ':' ) ;
60
61
if ( ! this . braceCounter . isOpened ( TokenType . OpenBracket ) && ( next && next . type !== TokenType . Colon ) ) {
61
- // Not inside opened [[ ... ] sequence
62
+ // Not inside opened [[ ... ] sequence.
62
63
this . builder . softAppendSpace ( ) ;
63
64
}
64
65
break ;
65
66
66
67
case TokenType . Comment :
67
- // add space before in-line comment
68
+ // Add space before in-line comment.
68
69
if ( prev ) {
69
70
this . builder . softAppendSpace ( ) ;
70
71
}
71
72
this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
72
73
break ;
73
74
75
+ case TokenType . Semicolon :
76
+ this . builder . append ( ';' ) ;
77
+ break ;
78
+
74
79
default :
75
- this . handleOther ( t ) ;
80
+ this . handleOther ( t , i ) ;
76
81
break ;
77
82
}
78
83
}
@@ -85,7 +90,7 @@ export class LineFormatter {
85
90
const opCode = this . text . charCodeAt ( t . start ) ;
86
91
switch ( opCode ) {
87
92
case Char . Equal :
88
- if ( index >= 2 && this . handleEqual ( t , index ) ) {
93
+ if ( this . handleEqual ( t , index ) ) {
89
94
return ;
90
95
}
91
96
break ;
@@ -105,27 +110,66 @@ export class LineFormatter {
105
110
}
106
111
107
112
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 ;
118
120
}
119
121
return false ;
120
122
}
121
123
122
- private handleOther ( t : IToken ) : void {
124
+ private handleOther ( t : IToken , index : number ) : void {
123
125
if ( this . isBraceType ( t . type ) ) {
124
126
this . braceCounter . countBrace ( t ) ;
127
+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
128
+ return ;
125
129
}
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 ( ) ;
126
148
this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
127
149
}
128
150
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
+
129
173
private isOpenBraceType ( type : TokenType ) : boolean {
130
174
return type === TokenType . OpenBrace || type === TokenType . OpenBracket || type === TokenType . OpenCurly ;
131
175
}
@@ -135,4 +179,16 @@ export class LineFormatter {
135
179
private isBraceType ( type : TokenType ) : boolean {
136
180
return this . isOpenBraceType ( type ) || this . isCloseBraceType ( type ) ;
137
181
}
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
+ }
138
194
}
0 commit comments