format on open curly

This commit is contained in:
Arthur Ozga
2017-06-19 20:30:24 -07:00
committed by Arthur Ozga
parent 47c1563649
commit 4c40c42f56
5 changed files with 54 additions and 36 deletions

View File

@@ -97,11 +97,23 @@ namespace ts.formatting {
}
export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
return formatOutermostParent(position, SyntaxKind.SemicolonToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon);
const outermostParent = findOutermostParentWithinListLevelFromPosition(position, SyntaxKind.SemicolonToken, sourceFile);
return formatOutermostParent(outermostParent, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon);
}
export function formatOnOpeningCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
const openingCurly = findPrecedingTokenOfKind(position, SyntaxKind.FirstPunctuation, sourceFile);
const block = openingCurly && openingCurly.parent;
if (!(block && isBlock(block))) {
return [];
}
const outermostParent = findOutermostParentWithinListLevel(block);
return formatOutermostParent(outermostParent, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnOpeningCurlyBrace);
}
export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
return formatOutermostParent(position, SyntaxKind.CloseBraceToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace);
const outermostParent = findOutermostParentWithinListLevelFromPosition(position, SyntaxKind.CloseBraceToken, sourceFile);
return formatOutermostParent(outermostParent, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace);
}
export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] {
@@ -121,44 +133,55 @@ namespace ts.formatting {
return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection);
}
function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
const parent = findOutermostParent(position, expectedLastToken, sourceFile);
function formatOutermostParent(parent: Node | undefined, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] {
if (!parent) {
return [];
}
const span = {
pos: getLineStartPositionForPosition(parent.getStart(sourceFile), sourceFile),
end: parent.end
};
return formatSpan(span, sourceFile, options, rulesProvider, requestKind);
}
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
function findPrecedingTokenOfKind(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node | undefined {
const precedingToken = findPrecedingToken(position, sourceFile);
// when it is claimed that trigger character was typed at given position
// we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
// If this condition is not hold - then trigger character was typed in some other context,
// i.e.in comment and thus should not trigger autoformatting
if (!precedingToken ||
precedingToken.kind !== expectedTokenKind ||
position !== precedingToken.getEnd()) {
return undefined;
}
return precedingToken && precedingToken.kind === expectedTokenKind && position === precedingToken.getEnd() ?
precedingToken :
undefined;
}
// walk up and search for the parent node that ends at the same position with precedingToken.
// for cases like this
//
// let x = 1;
// while (true) {
// }
// after typing close curly in while statement we want to reformat just the while statement.
// However if we just walk upwards searching for the parent that has the same end value -
// we'll end up with the whole source file. isListElement allows to stop on the list element level
let current = precedingToken;
/**
* Validating `expectedLastToken` ensures the token was typed in the context we expect (eg: not a comment).
* @param expectedLastToken The last token constituting the desired parent node.
*/
function findOutermostParentWithinListLevelFromPosition(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile) {
const precedingToken = findPrecedingTokenOfKind(position, expectedLastToken, sourceFile);
return precedingToken && findOutermostParentWithinListLevel(precedingToken);
}
/**
* Finds the outermost parent within the same list level as the token at position.
*
* Consider typing the following
* ```
* let x = 1;
* while (true) {
* }
* ```
* Upon typing the closing curly, we want to format the entire `while`-statement, but not the preceding
* variable declaration.
*/
function findOutermostParentWithinListLevel(token: Node): Node {
// If we walk upwards searching for the parent that has the same end value, we'll end up with the whole source file.
// `isListElement` allows to stop on the list element level.
let current = token;
while (current &&
current.parent &&
current.parent.end === precedingToken.end &&
current.parent.end === token.end &&
!isListElement(current.parent, current)) {
current = current.parent;
}

View File

@@ -47,14 +47,6 @@ namespace ts.formatting {
return this.contextNodeAllOnSameLine;
}
public NextNodeAllOnSameLine(): boolean {
if (this.nextNodeAllOnSameLine === undefined) {
this.nextNodeAllOnSameLine = this.NodeIsOnOneLine(this.nextTokenParent);
}
return this.nextNodeAllOnSameLine;
}
public TokensAreOnSameLine(): boolean {
if (this.tokensAreOnSameLine === undefined) {
const startLine = this.sourceFile.getLineAndCharacterOfPosition(this.currentTokenSpan.pos).line;

View File

@@ -7,6 +7,7 @@ namespace ts.formatting {
FormatSelection,
FormatOnEnter,
FormatOnSemicolon,
FormatOnOpeningCurlyBrace,
FormatOnClosingCurlyBrace
}
}

View File

@@ -671,7 +671,7 @@ namespace ts.formatting {
// This check is done before an open brace in a control construct, a function, or a typescript block declaration
static IsBeforeMultilineBlockContext(context: FormattingContext): boolean {
return Rules.IsBeforeBlockContext(context) && !(context.NextNodeAllOnSameLine() || context.NextNodeBlockIsOnOneLine());
return Rules.IsBeforeBlockContext(context) && !(context.NextNodeBlockIsOnOneLine());
}
static IsMultilineBlockContext(context: FormattingContext): boolean {

View File

@@ -1763,8 +1763,10 @@ namespace ts {
function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[] {
const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName);
const settings = toEditorSettings(options);
if (key === "}") {
if (key === "{") {
return formatting.formatOnOpeningCurly(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === "}") {
return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(settings), settings);
}
else if (key === ";") {