@@ -97,11 +97,23 @@ namespace ts.formatting {
97
97
}
98
98
99
99
export function formatOnSemicolon ( position : number , sourceFile : SourceFile , rulesProvider : RulesProvider , options : FormatCodeSettings ) : TextChange [ ] {
100
- return formatOutermostParent ( position , SyntaxKind . SemicolonToken , sourceFile , options , rulesProvider , FormattingRequestKind . FormatOnSemicolon ) ;
100
+ const outermostParent = findOutermostParentWithinListLevelFromPosition ( position , SyntaxKind . SemicolonToken , sourceFile ) ;
101
+ return formatOutermostParent ( outermostParent , sourceFile , options , rulesProvider , FormattingRequestKind . FormatOnSemicolon ) ;
102
+ }
103
+
104
+ export function formatOnOpeningCurly ( position : number , sourceFile : SourceFile , rulesProvider : RulesProvider , options : FormatCodeSettings ) : TextChange [ ] {
105
+ const openingCurly = findPrecedingTokenOfKind ( position , SyntaxKind . FirstPunctuation , sourceFile ) ;
106
+ const block = openingCurly && openingCurly . parent ;
107
+ if ( ! ( block && isBlock ( block ) ) ) {
108
+ return [ ] ;
109
+ }
110
+ const outermostParent = findOutermostParentWithinListLevel ( block ) ;
111
+ return formatOutermostParent ( outermostParent , sourceFile , options , rulesProvider , FormattingRequestKind . FormatOnOpeningCurlyBrace ) ;
101
112
}
102
113
103
114
export function formatOnClosingCurly ( position : number , sourceFile : SourceFile , rulesProvider : RulesProvider , options : FormatCodeSettings ) : TextChange [ ] {
104
- return formatOutermostParent ( position , SyntaxKind . CloseBraceToken , sourceFile , options , rulesProvider , FormattingRequestKind . FormatOnClosingCurlyBrace ) ;
115
+ const outermostParent = findOutermostParentWithinListLevelFromPosition ( position , SyntaxKind . CloseBraceToken , sourceFile ) ;
116
+ return formatOutermostParent ( outermostParent , sourceFile , options , rulesProvider , FormattingRequestKind . FormatOnClosingCurlyBrace ) ;
105
117
}
106
118
107
119
export function formatDocument ( sourceFile : SourceFile , rulesProvider : RulesProvider , options : FormatCodeSettings ) : TextChange [ ] {
@@ -121,44 +133,55 @@ namespace ts.formatting {
121
133
return formatSpan ( span , sourceFile , options , rulesProvider , FormattingRequestKind . FormatSelection ) ;
122
134
}
123
135
124
- function formatOutermostParent ( position : number , expectedLastToken : SyntaxKind , sourceFile : SourceFile , options : FormatCodeSettings , rulesProvider : RulesProvider , requestKind : FormattingRequestKind ) : TextChange [ ] {
125
- const parent = findOutermostParent ( position , expectedLastToken , sourceFile ) ;
136
+ function formatOutermostParent ( parent : Node | undefined , sourceFile : SourceFile , options : FormatCodeSettings , rulesProvider : RulesProvider , requestKind : FormattingRequestKind ) : TextChange [ ] {
126
137
if ( ! parent ) {
127
138
return [ ] ;
128
139
}
140
+
129
141
const span = {
130
142
pos : getLineStartPositionForPosition ( parent . getStart ( sourceFile ) , sourceFile ) ,
131
143
end : parent . end
132
144
} ;
145
+
133
146
return formatSpan ( span , sourceFile , options , rulesProvider , requestKind ) ;
134
147
}
135
148
136
- function findOutermostParent ( position : number , expectedTokenKind : SyntaxKind , sourceFile : SourceFile ) : Node {
149
+ function findPrecedingTokenOfKind ( position : number , expectedTokenKind : SyntaxKind , sourceFile : SourceFile ) : Node | undefined {
137
150
const precedingToken = findPrecedingToken ( position , sourceFile ) ;
138
151
139
- // when it is claimed that trigger character was typed at given position
140
- // we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
141
- // If this condition is not hold - then trigger character was typed in some other context,
142
- // i.e.in comment and thus should not trigger autoformatting
143
- if ( ! precedingToken ||
144
- precedingToken . kind !== expectedTokenKind ||
145
- position !== precedingToken . getEnd ( ) ) {
146
- return undefined ;
147
- }
152
+ return precedingToken && precedingToken . kind === expectedTokenKind && position === precedingToken . getEnd ( ) ?
153
+ precedingToken :
154
+ undefined ;
155
+ }
148
156
149
- // walk up and search for the parent node that ends at the same position with precedingToken.
150
- // for cases like this
151
- //
152
- // let x = 1;
153
- // while (true) {
154
- // }
155
- // after typing close curly in while statement we want to reformat just the while statement.
156
- // However if we just walk upwards searching for the parent that has the same end value -
157
- // we'll end up with the whole source file. isListElement allows to stop on the list element level
158
- let current = precedingToken ;
157
+ /**
158
+ * Validating `expectedLastToken` ensures the token was typed in the context we expect (eg: not a comment).
159
+ * @param expectedLastToken The last token constituting the desired parent node.
160
+ */
161
+ function findOutermostParentWithinListLevelFromPosition ( position : number , expectedLastToken : SyntaxKind , sourceFile : SourceFile ) {
162
+ const precedingToken = findPrecedingTokenOfKind ( position , expectedLastToken , sourceFile ) ;
163
+ return precedingToken && findOutermostParentWithinListLevel ( precedingToken ) ;
164
+ }
165
+
166
+ /**
167
+ * Finds the outermost parent within the same list level as the token at position.
168
+ *
169
+ * Consider typing the following
170
+ * ```
171
+ * let x = 1;
172
+ * while (true) {
173
+ * }
174
+ * ```
175
+ * Upon typing the closing curly, we want to format the entire `while`-statement, but not the preceding
176
+ * variable declaration.
177
+ */
178
+ function findOutermostParentWithinListLevel ( token : Node ) : Node {
179
+ // If we walk upwards searching for the parent that has the same end value, we'll end up with the whole source file.
180
+ // `isListElement` allows to stop on the list element level.
181
+ let current = token ;
159
182
while ( current &&
160
183
current . parent &&
161
- current . parent . end === precedingToken . end &&
184
+ current . parent . end === token . end &&
162
185
! isListElement ( current . parent , current ) ) {
163
186
current = current . parent ;
164
187
}
0 commit comments