Skip to content

Commit 4c40c42

Browse files
Arthur Ozgaaozgaa
authored andcommitted
format on open curly
1 parent 47c1563 commit 4c40c42

File tree

5 files changed

+54
-36
lines changed

5 files changed

+54
-36
lines changed

src/services/formatting/formatting.ts

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/services/formatting/formattingContext.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,6 @@ namespace ts.formatting {
4747
return this.contextNodeAllOnSameLine;
4848
}
4949

50-
public NextNodeAllOnSameLine(): boolean {
51-
if (this.nextNodeAllOnSameLine === undefined) {
52-
this.nextNodeAllOnSameLine = this.NodeIsOnOneLine(this.nextTokenParent);
53-
}
54-
55-
return this.nextNodeAllOnSameLine;
56-
}
57-
5850
public TokensAreOnSameLine(): boolean {
5951
if (this.tokensAreOnSameLine === undefined) {
6052
const startLine = this.sourceFile.getLineAndCharacterOfPosition(this.currentTokenSpan.pos).line;

src/services/formatting/formattingRequestKind.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace ts.formatting {
77
FormatSelection,
88
FormatOnEnter,
99
FormatOnSemicolon,
10+
FormatOnOpeningCurlyBrace,
1011
FormatOnClosingCurlyBrace
1112
}
1213
}

src/services/formatting/rules.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ namespace ts.formatting {
671671

672672
// This check is done before an open brace in a control construct, a function, or a typescript block declaration
673673
static IsBeforeMultilineBlockContext(context: FormattingContext): boolean {
674-
return Rules.IsBeforeBlockContext(context) && !(context.NextNodeAllOnSameLine() || context.NextNodeBlockIsOnOneLine());
674+
return Rules.IsBeforeBlockContext(context) && !(context.NextNodeBlockIsOnOneLine());
675675
}
676676

677677
static IsMultilineBlockContext(context: FormattingContext): boolean {

src/services/services.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,8 +1763,10 @@ namespace ts {
17631763
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
17641764
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
17651765
const settings = toEditorSettings(options);
1766-
1767-
if (key === "}") {
1766+
if (key === "{") {
1767+
return formatting.formatOnOpeningCurly(position, sourceFile, getRuleProvider(settings), settings);
1768+
}
1769+
else if (key === "}") {
17681770
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(settings), settings);
17691771
}
17701772
else if (key === ";") {

0 commit comments

Comments
 (0)