@@ -43,7 +43,7 @@ export class LineFormatter {
4343
4444 case TokenType . Comma :
4545 this . builder . append ( ',' ) ;
46- if ( next && ! this . isCloseBraceType ( next . type ) ) {
46+ if ( next && ! this . isCloseBraceType ( next . type ) && next . type !== TokenType . Colon ) {
4747 this . builder . softAppendSpace ( ) ;
4848 }
4949 break ;
@@ -52,7 +52,12 @@ export class LineFormatter {
5252 if ( prev && ! this . isOpenBraceType ( prev . type ) && prev . type !== TokenType . Colon && prev . type !== TokenType . Operator ) {
5353 this . builder . softAppendSpace ( ) ;
5454 }
55- this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
55+ const id = this . text . substring ( t . start , t . end ) ;
56+ this . builder . append ( id ) ;
57+ if ( this . keywordWithSpaceAfter ( id ) && next && this . isOpenBraceType ( next . type ) ) {
58+ // for x in ()
59+ this . builder . softAppendSpace ( ) ;
60+ }
5661 break ;
5762
5863 case TokenType . Colon :
@@ -84,8 +89,10 @@ export class LineFormatter {
8489 return this . builder . getText ( ) ;
8590 }
8691
92+ // tslint:disable-next-line:cyclomatic-complexity
8793 private handleOperator ( index : number ) : void {
8894 const t = this . tokens . getItemAt ( index ) ;
95+ const prev = index > 0 ? this . tokens . getItemAt ( index - 1 ) : undefined ;
8996 if ( t . length === 1 ) {
9097 const opCode = this . text . charCodeAt ( t . start ) ;
9198 switch ( opCode ) {
@@ -99,18 +106,36 @@ export class LineFormatter {
99106 case Char . ExclamationMark :
100107 this . builder . append ( this . text [ t . start ] ) ;
101108 return ;
109+ case Char . Asterisk :
110+ if ( prev && prev . type === TokenType . Identifier && prev . length === 6 && this . text . substr ( prev . start , prev . length ) === 'lambda' ) {
111+ this . builder . softAppendSpace ( ) ;
112+ this . builder . append ( '*' ) ;
113+ return ;
114+ }
115+ break ;
102116 default :
103117 break ;
104118 }
119+ } else if ( t . length === 2 ) {
120+ if ( this . text . charCodeAt ( t . start ) === Char . Asterisk && this . text . charCodeAt ( t . start + 1 ) === Char . Asterisk ) {
121+ if ( ! prev || ( prev . type !== TokenType . Identifier && prev . type !== TokenType . Number ) ) {
122+ this . builder . append ( '**' ) ;
123+ return ;
124+ }
125+ if ( prev && prev . type === TokenType . Identifier && prev . length === 6 && this . text . substr ( prev . start , prev . length ) === 'lambda' ) {
126+ this . builder . softAppendSpace ( ) ;
127+ this . builder . append ( '**' ) ;
128+ return ;
129+ }
130+ }
105131 }
132+
106133 // Do not append space if operator is preceded by '(' or ',' as in foo(**kwarg)
107- if ( index > 0 ) {
108- const prev = this . tokens . getItemAt ( index - 1 ) ;
109- if ( this . isOpenBraceType ( prev . type ) || prev . type === TokenType . Comma ) {
110- this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
111- return ;
112- }
134+ if ( prev && ( this . isOpenBraceType ( prev . type ) || prev . type === TokenType . Comma ) ) {
135+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
136+ return ;
113137 }
138+
114139 this . builder . softAppendSpace ( ) ;
115140 this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
116141 this . builder . softAppendSpace ( ) ;
@@ -135,43 +160,82 @@ export class LineFormatter {
135160 return ;
136161 }
137162
138- if ( this . isEqualsInsideArguments ( index - 1 ) ) {
163+ const prev = index > 0 ? this . tokens . getItemAt ( index - 1 ) : undefined ;
164+ if ( prev && prev . length === 1 && this . text . charCodeAt ( prev . start ) === Char . Equal && this . isEqualsInsideArguments ( index - 1 ) ) {
139165 // Don't add space around = inside function arguments.
140166 this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
141167 return ;
142168 }
143169
144- if ( index > 0 ) {
145- const prev = this . tokens . getItemAt ( index - 1 ) ;
146- if ( this . isOpenBraceType ( prev . type ) || prev . type === TokenType . Colon ) {
147- // Don't insert space after (, [ or { .
148- this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
149- return ;
150- }
170+ if ( prev && ( this . isOpenBraceType ( prev . type ) || prev . type === TokenType . Colon ) ) {
171+ // Don't insert space after (, [ or { .
172+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
173+ return ;
151174 }
152175
153- // In general, keep tokens separated.
154- this . builder . softAppendSpace ( ) ;
155- this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
176+ if ( t . type === TokenType . Unknown ) {
177+ this . handleUnknown ( t ) ;
178+ } else {
179+ // In general, keep tokens separated.
180+ this . builder . softAppendSpace ( ) ;
181+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
182+ }
156183 }
157184
185+ private handleUnknown ( t : IToken ) : void {
186+ const prevChar = t . start > 0 ? this . text . charCodeAt ( t . start - 1 ) : 0 ;
187+ if ( prevChar === Char . Space || prevChar === Char . Tab ) {
188+ this . builder . softAppendSpace ( ) ;
189+ }
190+ this . builder . append ( this . text . substring ( t . start , t . end ) ) ;
191+
192+ const nextChar = t . end < this . text . length - 1 ? this . text . charCodeAt ( t . end ) : 0 ;
193+ if ( nextChar === Char . Space || nextChar === Char . Tab ) {
194+ this . builder . softAppendSpace ( ) ;
195+ }
196+ }
158197 private isEqualsInsideArguments ( index : number ) : boolean {
198+ // Since we don't have complete statement, this is mostly heuristics.
199+ // Therefore the code may not be handling all possible ways of the
200+ // argument list continuation.
159201 if ( index < 1 ) {
160202 return false ;
161203 }
204+
162205 const prev = this . tokens . getItemAt ( index - 1 ) ;
163- if ( prev . type === TokenType . Identifier ) {
164- if ( index >= 2 ) {
165- // (x=1 or ,x=1
166- const prevPrev = this . tokens . getItemAt ( index - 2 ) ;
167- return prevPrev . type === TokenType . Comma || prevPrev . type === TokenType . OpenBrace ;
168- } else if ( index < this . tokens . count - 2 ) {
169- const next = this . tokens . getItemAt ( index + 1 ) ;
170- const nextNext = this . tokens . getItemAt ( index + 2 ) ;
171- // x=1, or x=1)
172- if ( this . isValueType ( next . type ) ) {
173- return nextNext . type === TokenType . Comma || nextNext . type === TokenType . CloseBrace ;
174- }
206+ if ( prev . type !== TokenType . Identifier ) {
207+ return false ;
208+ }
209+
210+ const first = this . tokens . getItemAt ( 0 ) ;
211+ if ( first . type === TokenType . Comma ) {
212+ return true ; // Line starts with commma
213+ }
214+
215+ const last = this . tokens . getItemAt ( this . tokens . count - 1 ) ;
216+ if ( last . type === TokenType . Comma ) {
217+ return true ; // Line ends in comma
218+ }
219+
220+ if ( index >= 2 ) {
221+ // (x=1 or ,x=1
222+ const prevPrev = this . tokens . getItemAt ( index - 2 ) ;
223+ return prevPrev . type === TokenType . Comma || prevPrev . type === TokenType . OpenBrace ;
224+ }
225+
226+ if ( index >= this . tokens . count - 2 ) {
227+ return false ;
228+ }
229+
230+ const next = this . tokens . getItemAt ( index + 1 ) ;
231+ const nextNext = this . tokens . getItemAt ( index + 2 ) ;
232+ // x=1, or x=1)
233+ if ( this . isValueType ( next . type ) ) {
234+ if ( nextNext . type === TokenType . CloseBrace ) {
235+ return true ;
236+ }
237+ if ( nextNext . type === TokenType . Comma ) {
238+ return last . type === TokenType . CloseBrace ;
175239 }
176240 }
177241 return false ;
@@ -198,4 +262,10 @@ export class LineFormatter {
198262 }
199263 return false ;
200264 }
265+ private keywordWithSpaceAfter ( s : string ) : boolean {
266+ return s === 'in' || s === 'return' || s === 'and' ||
267+ s === 'or' || s === 'not' || s === 'from' ||
268+ s === 'import' || s === 'except' || s === 'for' ||
269+ s === 'as' || s === 'is' ;
270+ }
201271}
0 commit comments