diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index f08f8c96585..827378e3eed 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -317,63 +317,6 @@ namespace ts.formatting { rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; - function isSemicolonDeletionContext(context: FormattingContext): boolean { - if (context.TokensAreOnSameLine()) { - return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken - || context.nextTokenSpan.kind === SyntaxKind.EndOfFileToken; - } - - if (context.nextTokenSpan.kind === SyntaxKind.SemicolonClassElement || - context.nextTokenSpan.kind === SyntaxKind.SemicolonToken - ) { - return false; - } - - if (context.contextNode.kind === SyntaxKind.InterfaceDeclaration || - context.contextNode.kind === SyntaxKind.TypeAliasDeclaration - ) { - // Can’t remove semicolon after `foo`; it would parse as a - // method declaration: - // interface I { - // foo; - // (): void - // } - return !(isPropertySignature(context.currentTokenParent) - && !context.currentTokenParent.type - && context.nextTokenSpan.kind === SyntaxKind.OpenParenToken); - } - - if (isPropertyDeclaration(context.currentTokenParent)) { - return !context.currentTokenParent.initializer; - } - - return context.currentTokenParent.kind !== SyntaxKind.ForStatement - && context.currentTokenParent.kind !== SyntaxKind.EmptyStatement - && context.currentTokenParent.kind !== SyntaxKind.SemicolonClassElement - && context.nextTokenSpan.kind !== SyntaxKind.OpenBracketToken - && context.nextTokenSpan.kind !== SyntaxKind.OpenParenToken - && context.nextTokenSpan.kind !== SyntaxKind.PlusToken - && context.nextTokenSpan.kind !== SyntaxKind.MinusToken - && context.nextTokenSpan.kind !== SyntaxKind.SlashToken - && context.nextTokenSpan.kind !== SyntaxKind.RegularExpressionLiteral - && context.nextTokenSpan.kind !== SyntaxKind.CommaToken - && context.nextTokenSpan.kind !== SyntaxKind.TemplateExpression - && context.nextTokenSpan.kind !== SyntaxKind.TemplateHead - && context.nextTokenSpan.kind !== SyntaxKind.NoSubstitutionTemplateLiteral - && context.nextTokenSpan.kind !== SyntaxKind.DotToken; - } - - function isSemicolonInsertionContext(context: FormattingContext): boolean { - const contextAncestor = findAncestor(context.currentTokenParent, ancestor => { - if (ancestor.end !== context.currentTokenSpan.end) { - return "quit"; - } - return syntaxMayBeASICandidate(ancestor.kind); - }); - - return !!contextAncestor && isASICandidate(contextAncestor); - } - // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list. const lowPriorityCommonRules = [ // Space after keyword but not before ; or : or ? @@ -833,4 +776,65 @@ namespace ts.formatting { function isNonNullAssertionContext(context: FormattingContext): boolean { return context.contextNode.kind === SyntaxKind.NonNullExpression; } + + function isSemicolonDeletionContext(context: FormattingContext): boolean { + if (context.TokensAreOnSameLine()) { + return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken + || context.nextTokenSpan.kind === SyntaxKind.EndOfFileToken; + } + + const nextToken = isTrivia(context.nextTokenSpan.kind) + ? context.nextTokenParent.getChildAt(0) + : context.nextTokenSpan; + + if (nextToken.kind === SyntaxKind.SemicolonClassElement || + nextToken.kind === SyntaxKind.SemicolonToken + ) { + return false; + } + + if (context.contextNode.kind === SyntaxKind.InterfaceDeclaration || + context.contextNode.kind === SyntaxKind.TypeAliasDeclaration + ) { + // Can’t remove semicolon after `foo`; it would parse as a method declaration: + // + // interface I { + // foo; + // (): void + // } + return !(isPropertySignature(context.currentTokenParent) + && !context.currentTokenParent.type + && nextToken.kind === SyntaxKind.OpenParenToken); + } + + if (isPropertyDeclaration(context.currentTokenParent)) { + return !context.currentTokenParent.initializer; + } + + return context.currentTokenParent.kind !== SyntaxKind.ForStatement + && context.currentTokenParent.kind !== SyntaxKind.EmptyStatement + && context.currentTokenParent.kind !== SyntaxKind.SemicolonClassElement + && nextToken.kind !== SyntaxKind.OpenBracketToken + && nextToken.kind !== SyntaxKind.OpenParenToken + && nextToken.kind !== SyntaxKind.PlusToken + && nextToken.kind !== SyntaxKind.MinusToken + && nextToken.kind !== SyntaxKind.SlashToken + && nextToken.kind !== SyntaxKind.RegularExpressionLiteral + && nextToken.kind !== SyntaxKind.CommaToken + && nextToken.kind !== SyntaxKind.TemplateExpression + && nextToken.kind !== SyntaxKind.TemplateHead + && nextToken.kind !== SyntaxKind.NoSubstitutionTemplateLiteral + && nextToken.kind !== SyntaxKind.DotToken; + } + + function isSemicolonInsertionContext(context: FormattingContext): boolean { + const contextAncestor = findAncestor(context.currentTokenParent, ancestor => { + if (ancestor.end !== context.currentTokenSpan.end) { + return "quit"; + } + return syntaxMayBeASICandidate(ancestor.kind); + }); + + return !!contextAncestor && isASICandidate(contextAncestor); + } } diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index 8851313c8bd..634cb74f8a7 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -16,7 +16,7 @@ namespace ts.formatting { function getRuleActionExclusion(ruleAction: RuleAction): RuleAction { let mask: RuleAction = 0; if (ruleAction & RuleAction.Ignore) { - return -1; + mask |= RuleAction.TriviaAction; } if (ruleAction & RuleAction.TriviaAction) { mask |= RuleAction.TriviaAction; diff --git a/src/services/types.ts b/src/services/types.ts index 722539af150..1e6da950567 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -758,7 +758,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, - // insertTrailingSemicolon: true, + // insertTrailingSemicolon: false, }; } diff --git a/tests/cases/fourslash/formatRemoveSemicolons2.ts b/tests/cases/fourslash/formatRemoveSemicolons2.ts new file mode 100644 index 00000000000..9760cc4f206 --- /dev/null +++ b/tests/cases/fourslash/formatRemoveSemicolons2.ts @@ -0,0 +1,15 @@ +/// + +////interface I { +//// a: string; +//// /** @internal */ +//// b: string; +////} + +format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.document(); +verify.currentFileContentIs(`interface I { + a: string + /** @internal */ + b: string +}`);