@@ -97,11 +97,23 @@ namespace ts.formatting {
9797 }
9898
9999 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 ) ;
101112 }
102113
103114 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 ) ;
105117 }
106118
107119 export function formatDocument ( sourceFile : SourceFile , rulesProvider : RulesProvider , options : FormatCodeSettings ) : TextChange [ ] {
@@ -121,44 +133,55 @@ namespace ts.formatting {
121133 return formatSpan ( span , sourceFile , options , rulesProvider , FormattingRequestKind . FormatSelection ) ;
122134 }
123135
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 [ ] {
126137 if ( ! parent ) {
127138 return [ ] ;
128139 }
140+
129141 const span = {
130142 pos : getLineStartPositionForPosition ( parent . getStart ( sourceFile ) , sourceFile ) ,
131143 end : parent . end
132144 } ;
145+
133146 return formatSpan ( span , sourceFile , options , rulesProvider , requestKind ) ;
134147 }
135148
136- function findOutermostParent ( position : number , expectedTokenKind : SyntaxKind , sourceFile : SourceFile ) : Node {
149+ function findPrecedingTokenOfKind ( position : number , expectedTokenKind : SyntaxKind , sourceFile : SourceFile ) : Node | undefined {
137150 const precedingToken = findPrecedingToken ( position , sourceFile ) ;
138151
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+ }
148156
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 ;
159182 while ( current &&
160183 current . parent &&
161- current . parent . end === precedingToken . end &&
184+ current . parent . end === token . end &&
162185 ! isListElement ( current . parent , current ) ) {
163186 current = current . parent ;
164187 }
0 commit comments