From df151ac8b3b9bebc249509bd6efa4fe51fc4ee24 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 28 Aug 2019 17:27:02 -0700 Subject: [PATCH 01/25] Add UserPreferences for semicolons --- src/compiler/types.ts | 1 + src/services/codefixes/importFixes.ts | 2 +- src/services/getEditsForFileRename.ts | 4 +- src/services/organizeImports.ts | 4 +- src/services/textChanges.ts | 39 ++++++++++--------- .../unittests/services/textChanges.ts | 2 +- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 999a922041201..29fb9ca71d84c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6166,6 +6166,7 @@ namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; + readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index a7aabce8ac362..1bb99d31f830e 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -169,7 +169,7 @@ namespace ts.codefix { // We sort the best codefixes first, so taking `first` is best for completions. const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, exportInfos, host, preferences)).moduleSpecifier; const fix = first(getFixForImport(exportInfos, symbolName, position, program, sourceFile, host, preferences)); - return { moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) }; + return { moduleSpecifier, codeAction: codeFixActionToCodeAction(codeActionForFix({ host, formatContext, preferences }, sourceFile, symbolName, fix, getQuotePreference(sourceFile, preferences))) }; } function codeFixActionToCodeAction({ description, changes, commands }: CodeFixAction): CodeAction { diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index 66cc88c2677ef..ea3a3a4dbe427 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -6,14 +6,14 @@ namespace ts { newFileOrDirPath: string, host: LanguageServiceHost, formatContext: formatting.FormatContext, - _preferences: UserPreferences, + preferences: UserPreferences, sourceMapper: SourceMapper, ): ReadonlyArray { const useCaseSensitiveFileNames = hostUsesCaseSensitiveFileNames(host); const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); const oldToNew = getPathUpdater(oldFileOrDirPath, newFileOrDirPath, getCanonicalFileName, sourceMapper); const newToOld = getPathUpdater(newFileOrDirPath, oldFileOrDirPath, getCanonicalFileName, sourceMapper); - return textChanges.ChangeTracker.with({ host, formatContext }, changeTracker => { + return textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => { updateTsconfigFiles(program, changeTracker, oldToNew, oldFileOrDirPath, newFileOrDirPath, host.getCurrentDirectory(), useCaseSensitiveFileNames); updateImports(program, changeTracker, oldToNew, newToOld, host, getCanonicalFileName); }); diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index 81c9108749f2e..e9b9b15d746e0 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -12,10 +12,10 @@ namespace ts.OrganizeImports { formatContext: formatting.FormatContext, host: LanguageServiceHost, program: Program, - _preferences: UserPreferences, + preferences: UserPreferences, ) { - const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext }); + const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext, preferences }); const coalesceAndOrganizeImports = (importGroup: ReadonlyArray) => coalesceImports(removeUnusedImports(importGroup, sourceFile, program)); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 31c9f9f26717b..788c2f152f540 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -218,6 +218,7 @@ namespace ts.textChanges { export interface TextChangesContext { host: LanguageServiceHost; formatContext: formatting.FormatContext; + preferences: UserPreferences; } export type TypeAnnotatable = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature; @@ -235,7 +236,7 @@ namespace ts.textChanges { private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray }[] = []; public static fromContext(context: TextChangesContext): ChangeTracker { - return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext); + return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext, context.preferences); } public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] { @@ -245,7 +246,7 @@ namespace ts.textChanges { } /** Public for tests only. Other callers should use `ChangeTracker.with`. */ - constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {} + constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext, private preferences: UserPreferences) {} public deleteRange(sourceFile: SourceFile, range: TextRange): void { this.changes.push({ kind: ChangeKind.Remove, sourceFile, range }); @@ -752,9 +753,9 @@ namespace ts.textChanges { public getChanges(validate?: ValidateNonFormattedText): FileTextChanges[] { this.finishDeleteDeclarations(); this.finishClassesWithNodesInsertedAtStart(); - const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, validate); + const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, this.preferences, validate); for (const { oldFile, fileName, statements } of this.newFiles) { - changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext)); + changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext, this.preferences)); } return changes; } @@ -778,12 +779,12 @@ namespace ts.textChanges { export type ValidateNonFormattedText = (node: Node, text: string) => void; - export function getNewFileText(statements: ReadonlyArray, scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext): string { - return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext); + export function getNewFileText(statements: ReadonlyArray, scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): string { + return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext, preferences); } namespace changesToText { - export function getTextChangesFromChanges(changes: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { + export function getTextChangesFromChanges(changes: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { return group(changes, c => c.sourceFile.path).map(changesInFile => { const sourceFile = changesInFile[0].sourceFile; // order changes by start position @@ -795,25 +796,25 @@ namespace ts.textChanges { `${JSON.stringify(normalized[i].range)} and ${JSON.stringify(normalized[i + 1].range)}`); } const textChanges = normalized.map(c => - createTextChange(createTextSpanFromRange(c.range), computeNewText(c, sourceFile, newLineCharacter, formatContext, validate))); + createTextChange(createTextSpanFromRange(c.range), computeNewText(c, sourceFile, newLineCharacter, formatContext, preferences, validate))); return { fileName: sourceFile.fileName, textChanges }; }); } - export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges { - const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext); + export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): FileTextChanges { + const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext, preferences); return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true }; } - export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext): string { + export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): string { // TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this - const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter).text).join(newLineCharacter); + const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter, preferences).text).join(newLineCharacter); const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind); const changes = formatting.formatDocument(sourceFile, formatContext); return applyChanges(nonFormattedText, changes) + newLineCharacter; } - function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { + function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): string { if (change.kind === ChangeKind.Remove) { return ""; } @@ -822,7 +823,7 @@ namespace ts.textChanges { } const { options = {}, range: { pos } } = change; - const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, validate); + const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, preferences, validate); const text = change.kind === ChangeKind.ReplaceWithMultipleNodes ? change.nodes.map(n => removeSuffix(format(n), newLineCharacter)).join(change.options!.joiner || newLineCharacter) // TODO: GH#18217 : format(change.node); @@ -832,8 +833,8 @@ namespace ts.textChanges { } /** Note: this may mutate `nodeIn`. */ - function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { - const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter); + function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): string { + const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter, preferences); if (validate) validate(node, text); const { options: formatOptions } = formatContext; const initialIndentation = @@ -850,8 +851,10 @@ namespace ts.textChanges { } /** Note: output node may be mutated input node. */ - export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } { - const omitTrailingSemicolon = !!sourceFile && !probablyUsesSemicolons(sourceFile); + export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string, { semicolonPreference }: UserPreferences): { text: string, node: Node } { + const omitTrailingSemicolon = semicolonPreference === "no semicolons" ? true : + semicolonPreference === "semicolons" ? false : + !!sourceFile && !probablyUsesSemicolons(sourceFile); const writer = createWriter(newLineCharacter, omitTrailingSemicolon); const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed; createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer); diff --git a/src/testRunner/unittests/services/textChanges.ts b/src/testRunner/unittests/services/textChanges.ts index 27195c300348a..17844d6b3ef8e 100644 --- a/src/testRunner/unittests/services/textChanges.ts +++ b/src/testRunner/unittests/services/textChanges.ts @@ -49,7 +49,7 @@ namespace ts { it(caption, () => { const sourceFile = createSourceFile("source.ts", text, ScriptTarget.ES2015, /*setParentNodes*/ true); const rulesProvider = getRuleProvider(placeOpenBraceOnNewLineForFunctions); - const changeTracker = new textChanges.ChangeTracker(newLineCharacter, rulesProvider); + const changeTracker = new textChanges.ChangeTracker(newLineCharacter, rulesProvider, {}); testBlock(sourceFile, changeTracker); const changes = changeTracker.getChanges(validateNodes ? verifyPositions : undefined); assert.equal(changes.length, 1); From 7cca9f47bc21d86661bbbed02e039569f50b38c1 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 29 Aug 2019 21:24:44 -0700 Subject: [PATCH 02/25] Prototype formatter semicolon removal --- src/services/formatting/formatting.ts | 79 ++++++----- src/services/formatting/formattingContext.ts | 2 +- src/services/formatting/formattingScanner.ts | 22 +-- src/services/formatting/rule.ts | 14 +- src/services/formatting/rules.ts | 137 +++++++++++-------- src/services/formatting/rulesMap.ts | 38 ++++- 6 files changed, 178 insertions(+), 114 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index ed6b71efb7523..905f63355966f 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -2,16 +2,14 @@ namespace ts.formatting { export interface FormatContext { readonly options: FormatCodeSettings; - readonly getRule: RulesMap; + readonly getRules: RulesMap; } - export interface TextRangeWithKind extends TextRange { - kind: SyntaxKind; + export interface TextRangeWithKind extends TextRange { + kind: T; } - export interface TextRangeWithTriviaKind extends TextRange { - kind: TriviaKind; - } + export type TextRangeWithTriviaKind = TextRangeWithKind; export interface TokenInfo { leadingTrivia: TextRangeWithTriviaKind[] | undefined; @@ -19,6 +17,16 @@ namespace ts.formatting { trailingTrivia: TextRangeWithTriviaKind[] | undefined; } + export function createTextRangeWithKind(pos: number, end: number, kind: T): TextRangeWithKind { + const textRangeWithKind: TextRangeWithKind = { pos, end, kind }; + if (Debug.isDebugging) { + Object.defineProperty(textRangeWithKind, "__debugKind", { + get: () => Debug.formatSyntaxKind(kind), + }); + } + return textRangeWithKind; + } + const enum Constants { Unknown = -1 } @@ -386,7 +394,7 @@ namespace ts.formatting { initialIndentation: number, delta: number, formattingScanner: FormattingScanner, - { options, getRule }: FormatContext, + { options, getRules }: FormatContext, requestKind: FormattingRequestKind, rangeContainsError: (r: TextRange) => boolean, sourceFile: SourceFileLike): TextChange[] { @@ -937,34 +945,36 @@ namespace ts.formatting { formattingContext.updateContext(previousItem, previousParent, currentItem, currentParent, contextNode); - const rule = getRule(formattingContext); + const rules = getRules(formattingContext); - let trimTrailingWhitespaces: boolean; + let trimTrailingWhitespaces = false; let lineAction = LineAction.None; - if (rule) { - lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine); - switch (lineAction) { - case LineAction.LineRemoved: - // Handle the case where the next line is moved to be the end of this line. - // In this case we don't indent the next line in the next pass. - if (currentParent.getStart(sourceFile) === currentItem.pos) { - dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false); - } - break; - case LineAction.LineAdded: - // Handle the case where token2 is moved to the new line. - // In this case we indent token2 in the next pass but we set - // sameLineIndent flag to notify the indenter that the indentation is within the line. - if (currentParent.getStart(sourceFile) === currentItem.pos) { - dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true); - } - break; - default: - Debug.assert(lineAction === LineAction.None); - } + if (rules) { + for (const rule of rules) { + lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine); + switch (lineAction) { + case LineAction.LineRemoved: + // Handle the case where the next line is moved to be the end of this line. + // In this case we don't indent the next line in the next pass. + if (currentParent.getStart(sourceFile) === currentItem.pos) { + dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ false); + } + break; + case LineAction.LineAdded: + // Handle the case where token2 is moved to the new line. + // In this case we indent token2 in the next pass but we set + // sameLineIndent flag to notify the indenter that the indentation is within the line. + if (currentParent.getStart(sourceFile) === currentItem.pos) { + dynamicIndentation.recomputeIndentation(/*lineAddedByFormatting*/ true); + } + break; + default: + Debug.assert(lineAction === LineAction.None); + } - // We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line - trimTrailingWhitespaces = !(rule.action & RuleAction.Delete) && rule.flags !== RuleFlags.CanDeleteNewLines; + // We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line + trimTrailingWhitespaces = !(rule.action & RuleAction.DeleteTrivia) && rule.flags !== RuleFlags.CanDeleteNewLines; + } } else { trimTrailingWhitespaces = true; @@ -1140,13 +1150,16 @@ namespace ts.formatting { case RuleAction.Ignore: // no action required return LineAction.None; - case RuleAction.Delete: + case RuleAction.DeleteTrivia: if (previousRange.end !== currentRange.pos) { // delete characters starting from t1.end up to t2.pos exclusive recordDelete(previousRange.end, currentRange.pos - previousRange.end); return onLaterLine ? LineAction.LineRemoved : LineAction.None; } break; + case RuleAction.DeleteToken: + recordDelete(previousRange.pos, previousRange.end - previousRange.pos); + return onLaterLine ? LineAction.LineRemoved : LineAction.None; case RuleAction.NewLine: // exit early if we on different lines and rule cannot change number of newlines // if line1 and line2 are on subsequent lines then no edits are required - ok to exit diff --git a/src/services/formatting/formattingContext.ts b/src/services/formatting/formattingContext.ts index e5a1ff5d4ac8f..df479acdf4e35 100644 --- a/src/services/formatting/formattingContext.ts +++ b/src/services/formatting/formattingContext.ts @@ -99,4 +99,4 @@ namespace ts.formatting { return false; } } -} \ No newline at end of file +} diff --git a/src/services/formatting/formattingScanner.ts b/src/services/formatting/formattingScanner.ts index 2787f90af03a5..a54f353e3475d 100644 --- a/src/services/formatting/formattingScanner.ts +++ b/src/services/formatting/formattingScanner.ts @@ -170,11 +170,11 @@ namespace ts.formatting { let currentToken = getNextToken(n, expectedScanAction); - const token: TextRangeWithKind = { - pos: scanner.getStartPos(), - end: scanner.getTextPos(), - kind: currentToken - }; + const token = createTextRangeWithKind( + scanner.getStartPos(), + scanner.getTextPos(), + currentToken, + ); // consume trailing trivia if (trailingTrivia) { @@ -185,11 +185,11 @@ namespace ts.formatting { if (!isTrivia(currentToken)) { break; } - const trivia: TextRangeWithTriviaKind = { - pos: scanner.getStartPos(), - end: scanner.getTextPos(), - kind: currentToken - }; + const trivia = createTextRangeWithKind( + scanner.getStartPos(), + scanner.getTextPos(), + currentToken, + ); if (!trailingTrivia) { trailingTrivia = []; @@ -276,4 +276,4 @@ namespace ts.formatting { trailingTrivia = undefined; } } -} \ No newline at end of file +} diff --git a/src/services/formatting/rule.ts b/src/services/formatting/rule.ts index aa1bb6b43e930..60d53e32210fa 100644 --- a/src/services/formatting/rule.ts +++ b/src/services/formatting/rule.ts @@ -12,10 +12,14 @@ namespace ts.formatting { export const anyContext: ReadonlyArray = emptyArray; export const enum RuleAction { - Ignore = 1 << 0, - Space = 1 << 1, - NewLine = 1 << 2, - Delete = 1 << 3, + Ignore = 1 << 0, + Space = 1 << 1, + NewLine = 1 << 2, + DeleteTrivia = 1 << 3, + DeleteToken = 1 << 4, + + TriviaAction = Space | NewLine | DeleteTrivia, + TokenAction = DeleteToken } export const enum RuleFlags { @@ -27,4 +31,4 @@ namespace ts.formatting { readonly tokens: ReadonlyArray; readonly isSpecific: boolean; } -} \ No newline at end of file +} diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 84ee38f0f306d..82fa18a839c3b 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -47,28 +47,28 @@ namespace ts.formatting { rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.Ignore), rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.Ignore), - rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.Delete), + rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteTrivia), rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Space), - rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete), + rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), // insert space after '?' only when it is used in conditional operator rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.Space), // in other cases there should be no space between '?' and next token - rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.Delete), + rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteTrivia), // Special handling of unary operators. // Prefix operators generally shouldn't have a space between // them and their target unary expression. - rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete), - rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // More unary operator special-casing. // DevDiv 181814: Be careful when removing leading whitespace @@ -82,7 +82,7 @@ namespace ts.formatting { rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // For functions and control block place } on a new line [multi-line rule] rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.NewLine), @@ -92,12 +92,12 @@ namespace ts.formatting { // Also should not apply to }) rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.Delete), + rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteTrivia), // Add a space after control dec context if the next character is an open bracket ex: 'if (false)[a, b] = [1, 2];' -> 'if (false) [a, b] = [1, 2];' rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.Space), - rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Delete), + rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.DeleteTrivia), rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Space), rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.Space), @@ -110,13 +110,13 @@ namespace ts.formatting { // set x(val) {} rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.Space), - rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.Delete), + rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteTrivia), rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.Space), - rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.Space), - rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.Delete), + rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.DeleteTrivia), // Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options. rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), @@ -129,18 +129,18 @@ namespace ts.formatting { rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), // Template string - rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // JSX opening elements rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // TypeScript-specific rules // Use of module as a function call. e.g.: import m2 = module("m2"); - rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // Add a space around certain TypeScript keywords rule( "SpaceAfterCertainTypeScriptKeywords", @@ -186,26 +186,26 @@ namespace ts.formatting { rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), // Optional parameters and let args - rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete), + rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), // Remove spaces in empty interface literals. e.g.: x: {} - rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.Delete), + rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.DeleteTrivia), // generics and type assertions - rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete), - rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete), - rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete), - rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.Delete), + rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), + rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), rule("NoSpaceAfterCloseAngularBracket", SyntaxKind.GreaterThanToken, [SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext, isNotFunctionDeclContext /*To prevent an interference with the SpaceBeforeOpenParenInFuncDecl rule*/], - RuleAction.Delete), + RuleAction.DeleteTrivia), // decorators rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // Insert space after @ in decorator rule("SpaceAfterDecorator", anyToken, @@ -227,8 +227,8 @@ namespace ts.formatting { [isEndOfDecoratorContextOnSameLine], RuleAction.Space), - rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.Delete), - rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.Delete), + rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.DeleteTrivia), rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space), ]; @@ -236,65 +236,65 @@ namespace ts.formatting { const userConfigurableRules = [ // Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.Space), - rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.Delete), + rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.DeleteTrivia), // Insert space after function keyword for anonymous functions rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Space), - rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Delete), + rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.DeleteTrivia), // Insert space after keywords in control flow statements rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.Space), - rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.Delete), + rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.DeleteTrivia), // Insert space after opening and before closing nonempty parenthesis rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // Insert space after opening and before closing nonempty brackets rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}. rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space), rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space), - rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.Delete), - rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // Insert space after opening and before closing template string braces rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space), rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // No space after { and before } in JSX expression rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space), rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space), - rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Delete), - rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Delete), + rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteTrivia), // Insert space after semicolon in for statement rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.Space), - rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.Delete), + rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.DeleteTrivia), // Insert space before and after binary operators rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Delete), - rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Delete), + rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteTrivia), rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.Space), - rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.Delete), + rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.DeleteTrivia), // Open Brace braces after control block rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines), @@ -306,26 +306,43 @@ namespace ts.formatting { rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines), rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Space), - rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Delete), + rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.DeleteTrivia), rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), - rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Delete), + rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), + rule("NoDiscretionarySemicolon", SyntaxKind.SemicolonToken, anyToken, [isSemicolonDeletionContext], RuleAction.DeleteToken), ]; + function isSemicolonDeletionContext(context: FormattingContext): boolean { + if (context.TokensAreOnSameLine()) { + return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken; + } + 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.CommaToken + && context.nextTokenSpan.kind !== SyntaxKind.DotToken; + } + // 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 ? - rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), - rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), + rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), // No space before and after indexer `x[]` - rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.Delete), - rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete), + rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.DeleteTrivia), rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), // Remove extra space between for and await diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index b10d163343e6d..8851313c8bd66 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -1,7 +1,7 @@ /* @internal */ namespace ts.formatting { export function getFormatContext(options: FormatCodeSettings): FormatContext { - return { options, getRule: getRulesMap() }; + return { options, getRules: getRulesMap() }; } let rulesMapCache: RulesMap | undefined; @@ -13,12 +13,42 @@ namespace ts.formatting { return rulesMapCache; } - export type RulesMap = (context: FormattingContext) => Rule | undefined; + function getRuleActionExclusion(ruleAction: RuleAction): RuleAction { + let mask: RuleAction = 0; + if (ruleAction & RuleAction.Ignore) { + return -1; + } + if (ruleAction & RuleAction.TriviaAction) { + mask |= RuleAction.TriviaAction; + } + if (ruleAction & RuleAction.TokenAction) { + mask |= RuleAction.TokenAction; + } + return mask; + } + + export type RulesMap = (context: FormattingContext) => readonly Rule[] | undefined; function createRulesMap(rules: ReadonlyArray): RulesMap { const map = buildMap(rules); return context => { const bucket = map[getRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind)]; - return bucket && find(bucket, rule => every(rule.context, c => c(context))); + if (bucket) { + const rules: Rule[] = []; + let ruleActionMask: RuleAction = 0; + for (const rule of bucket) { + const acceptRuleActions = ~getRuleActionExclusion(ruleActionMask); + if (!(rule.action & acceptRuleActions)) { + continue; + } + if (every(rule.context, c => c(context))) { + rules.push(rule); + ruleActionMask |= rule.action; + } + } + if (rules.length) { + return rules; + } + } }; } @@ -102,4 +132,4 @@ namespace ts.formatting { Debug.assert((value & mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules."); return (indexBitmap & ~(mask << maskPosition)) | (value << maskPosition); } -} \ No newline at end of file +} From 461e6ad8ea6231a13017ce7b65ca1b65ab217b1c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 5 Sep 2019 11:13:06 -0700 Subject: [PATCH 03/25] Implement semicolon insertion --- src/compiler/core.ts | 15 +++++- src/services/formatting/formatting.ts | 3 ++ src/services/formatting/rule.ts | 13 ++--- src/services/formatting/rules.ts | 14 ++++- src/services/types.ts | 2 + src/services/utilities.ts | 78 +++++++++++++++++++++++++-- 6 files changed, 112 insertions(+), 13 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 12fbd6fb4ffa6..71afec908ab87 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2158,8 +2158,19 @@ namespace ts { return (arg: T) => f(arg) && g(arg); } - export function or(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean { - return arg => f(arg) || g(arg); + export function or(...fs: ((arg: T) => boolean)[]): (arg: T) => boolean { + return arg => { + for (const f of fs) { + if (f(arg)) { + return true; + } + } + return false; + }; + } + + export function not(fn: (...args: T) => boolean): (...args: T) => boolean { + return (...args) => !fn(...args); } export function assertType(_: T): void { } // tslint:disable-line no-empty diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 905f63355966f..f158aff7e5c9a 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1186,6 +1186,9 @@ namespace ts.formatting { recordReplace(previousRange.end, currentRange.pos - previousRange.end, " "); return onLaterLine ? LineAction.LineRemoved : LineAction.None; } + break; + case RuleAction.TrailingSemicolon: + recordReplace(previousRange.end, 0, ";"); } return LineAction.None; } diff --git a/src/services/formatting/rule.ts b/src/services/formatting/rule.ts index 60d53e32210fa..cd8aa4fc00422 100644 --- a/src/services/formatting/rule.ts +++ b/src/services/formatting/rule.ts @@ -12,14 +12,15 @@ namespace ts.formatting { export const anyContext: ReadonlyArray = emptyArray; export const enum RuleAction { - Ignore = 1 << 0, - Space = 1 << 1, - NewLine = 1 << 2, - DeleteTrivia = 1 << 3, - DeleteToken = 1 << 4, + Ignore = 1 << 0, + Space = 1 << 1, + NewLine = 1 << 2, + DeleteTrivia = 1 << 3, + DeleteToken = 1 << 4, + TrailingSemicolon = 1 << 5, TriviaAction = Space | NewLine | DeleteTrivia, - TokenAction = DeleteToken + TokenAction = DeleteToken | TrailingSemicolon } export const enum RuleFlags { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 82fa18a839c3b..8af57d9319039 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -310,7 +310,8 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), - rule("NoDiscretionarySemicolon", SyntaxKind.SemicolonToken, anyToken, [isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabled("insertTrailingSemicolon"), isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("OptionalSemicolon", anyToken, anyToken, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; function isSemicolonDeletionContext(context: FormattingContext): boolean { @@ -329,6 +330,17 @@ namespace ts.formatting { && 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 ? diff --git a/src/services/types.ts b/src/services/types.ts index 53d552394e0df..fa2cdc1c82168 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -734,6 +734,7 @@ namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; + readonly insertTrailingSemicolon?: boolean; } export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings { @@ -757,6 +758,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, + insertTrailingSemicolon: true, }; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 2e6e6875a8159..cf93f126f9bc1 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -743,7 +743,7 @@ namespace ts { return findPrecedingToken(position, file); } - export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFile): Node | undefined { + export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFileLike): Node | undefined { return find(parent); function find(n: Node): Node | undefined { @@ -1981,7 +1981,27 @@ namespace ts { return typeIsAccessible ? res : undefined; } - export function syntaxUsuallyHasTrailingSemicolon(kind: SyntaxKind) { + export function syntaxRequiresTrailingCommaOrSemicolonOrASI(kind: SyntaxKind) { + return kind === SyntaxKind.CallSignature + || kind === SyntaxKind.ConstructSignature + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.PropertySignature + || kind === SyntaxKind.MethodSignature; + } + + export function syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(kind: SyntaxKind) { + return kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor; + } + + export function syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(kind: SyntaxKind) { + return kind === SyntaxKind.ModuleDeclaration; + } + + export function syntaxRequiresTrailingSemicolonOrASI(kind: SyntaxKind) { return kind === SyntaxKind.VariableStatement || kind === SyntaxKind.ExpressionStatement || kind === SyntaxKind.DoStatement @@ -1994,7 +2014,57 @@ namespace ts { || kind === SyntaxKind.TypeAliasDeclaration || kind === SyntaxKind.ImportDeclaration || kind === SyntaxKind.ImportEqualsDeclaration - || kind === SyntaxKind.ExportDeclaration; + || kind === SyntaxKind.ExportDeclaration + || kind === SyntaxKind.NamespaceExportDeclaration + || kind === SyntaxKind.ExportAssignment; + } + + export const syntaxMayBeASICandidate = or( + syntaxRequiresTrailingCommaOrSemicolonOrASI, + syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI, + syntaxRequiresTrailingModuleBlockOrSemicolonOrASI, + syntaxRequiresTrailingSemicolonOrASI); + + export function isASICandidate(node: Node): boolean { + const lastToken = node.getLastToken(); + if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) { + return false; + } + + if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) { + if (lastToken && lastToken.kind === SyntaxKind.CommaToken) { + return false; + } + } + if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) { + const children = node.parent.getChildren(); + const nextChild = children[children.indexOf(node) + 1]; + if (nextChild && isModuleBlock(nextChild)) { + return false; + } + } + else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) { + const children = node.parent.getChildren(); + const nextChild = children[children.indexOf(node) + 1]; + if (nextChild && isFunctionBlock(nextChild)) { + return false; + } + } + else if (!syntaxRequiresTrailingSemicolonOrASI(node.kind)) { + return false; + } + + const sourceFile = node.getSourceFile(); + const nextToken = findNextToken(node, sourceFile, sourceFile); + if (!nextToken) { + return false; + } + + if (nextToken.kind === SyntaxKind.CloseBraceToken) { + return true; + } + + return !positionsAreOnSameLine(node.getEnd(), nextToken.getStart(sourceFile), sourceFile); } export function probablyUsesSemicolons(sourceFile: SourceFile): boolean { @@ -2002,7 +2072,7 @@ namespace ts { let withoutSemicolon = 0; const nStatementsToObserve = 5; forEachChild(sourceFile, function visit(node): boolean | undefined { - if (syntaxUsuallyHasTrailingSemicolon(node.kind)) { + if (syntaxRequiresTrailingSemicolonOrASI(node.kind)) { const lastToken = node.getLastToken(sourceFile); if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) { withSemicolon++; From 0d96d40cfd6cef5d95297eee5c16581a2c7d076a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 5 Sep 2019 14:58:04 -0700 Subject: [PATCH 04/25] Fix existing tests --- src/compiler/core.ts | 15 ++++++++++++ src/services/formatting/formatting.ts | 24 ++++++++++++++++--- src/services/formatting/formattingScanner.ts | 14 +++++++++++ src/services/formatting/rules.ts | 9 ++++--- src/services/types.ts | 2 +- src/services/utilities.ts | 11 +++++---- .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ 8 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 71afec908ab87..59d76e1a3883f 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -329,6 +329,21 @@ namespace ts { return undefined; } + /** + * Like `forEach`, but iterates in reverse order. + */ + export function forEachRight(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + if (array) { + for (let i = array.length - 1; i >= 0; i--) { + const result = callback(array[i], i); + if (result) { + return result; + } + } + } + return undefined; + } + /** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). */ export function firstDefined(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array === undefined) { diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index f158aff7e5c9a..a23801a31534e 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -651,6 +651,21 @@ namespace ts.formatting { consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation, node); } + if (formattingScanner.isOnEOF()) { + const token = formattingScanner.readEOFTokenRange(); + if (token.end <= node.end && previousRange) { + processPair( + token, + sourceFile.getLineAndCharacterOfPosition(token.pos).line, + node, + previousRange, + previousRangeStartLine, + previousParent, + contextNode, + nodeDynamicIndentation); + } + } + function processChildNode( child: Node, inheritedIndentation: number, @@ -950,7 +965,10 @@ namespace ts.formatting { let trimTrailingWhitespaces = false; let lineAction = LineAction.None; if (rules) { - for (const rule of rules) { + // Apply rules in reverse order so that higher when higher priority rules (which are first in the array) + // create text changes at the same position as lower priority rules, the higher priority text changes + // are evaluated last. + forEachRight(rules, rule => { lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine); switch (lineAction) { case LineAction.LineRemoved: @@ -974,10 +992,10 @@ namespace ts.formatting { // We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line trimTrailingWhitespaces = !(rule.action & RuleAction.DeleteTrivia) && rule.flags !== RuleFlags.CanDeleteNewLines; - } + }); } else { - trimTrailingWhitespaces = true; + trimTrailingWhitespaces = currentItem.kind !== SyntaxKind.EndOfFileToken; } if (currentStartLine !== previousStartLine && trimTrailingWhitespaces) { diff --git a/src/services/formatting/formattingScanner.ts b/src/services/formatting/formattingScanner.ts index a54f353e3475d..66aa4397a0f1c 100644 --- a/src/services/formatting/formattingScanner.ts +++ b/src/services/formatting/formattingScanner.ts @@ -6,7 +6,9 @@ namespace ts.formatting { export interface FormattingScanner { advance(): void; isOnToken(): boolean; + isOnEOF(): boolean; readTokenInfo(n: Node): TokenInfo; + readEOFTokenRange(): TextRangeWithKind; getCurrentLeadingTrivia(): TextRangeWithKind[] | undefined; lastTrailingTriviaWasNewLine(): boolean; skipToEndOf(node: Node): void; @@ -38,7 +40,9 @@ namespace ts.formatting { const res = cb({ advance, readTokenInfo, + readEOFTokenRange, isOnToken, + isOnEOF, getCurrentLeadingTrivia: () => leadingTrivia, lastTrailingTriviaWasNewLine: () => wasNewLine, skipToEndOf, @@ -249,12 +253,22 @@ namespace ts.formatting { return token; } + function readEOFTokenRange(): TextRangeWithKind { + Debug.assert(isOnEOF()); + return createTextRangeWithKind(scanner.getStartPos(), scanner.getTextPos(), SyntaxKind.EndOfFileToken); + } + function isOnToken(): boolean { const current = lastTokenInfo ? lastTokenInfo.token.kind : scanner.getToken(); const startPos = lastTokenInfo ? lastTokenInfo.token.pos : scanner.getStartPos(); return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current); } + function isOnEOF(): boolean { + const current = lastTokenInfo ? lastTokenInfo.token.kind : scanner.getToken(); + return current === SyntaxKind.EndOfFileToken; + } + // when containing node in the tree is token // but its kind differs from the kind that was returned by the scanner, // then kind needs to be fixed. This might happen in cases diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 8af57d9319039..cc49a129e8190 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -9,7 +9,9 @@ namespace ts.formatting { export function getAllRules(): RuleSpec[] { const allTokens: SyntaxKind[] = []; for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) { - allTokens.push(token); + if (token !== SyntaxKind.EndOfFileToken) { + allTokens.push(token); + } } function anyTokenExcept(...tokens: SyntaxKind[]): TokenRange { return { tokens: allTokens.filter(t => !tokens.some(t2 => t2 === t)), isSpecific: false }; @@ -17,6 +19,7 @@ namespace ts.formatting { const anyToken: TokenRange = { tokens: allTokens, isSpecific: false }; const anyTokenIncludingMultilineComments = tokenRangeFrom([...allTokens, SyntaxKind.MultiLineCommentTrivia]); + const anyTokenIncludingEOF = tokenRangeFrom([...allTokens, SyntaxKind.EndOfFileToken]); const keywords = tokenRangeFromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword); const binaryOperators = tokenRangeFromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator); const binaryKeywordOperators = [SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]; @@ -310,8 +313,8 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), - rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabled("insertTrailingSemicolon"), isSemicolonDeletionContext], RuleAction.DeleteToken), - rule("OptionalSemicolon", anyToken, anyToken, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), + rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [isOptionDisabled("insertTrailingSemicolon"), isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; function isSemicolonDeletionContext(context: FormattingContext): boolean { diff --git a/src/services/types.ts b/src/services/types.ts index fa2cdc1c82168..722539af150bb 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: true, }; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index cf93f126f9bc1..f14b181874676 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2054,13 +2054,14 @@ namespace ts { return false; } - const sourceFile = node.getSourceFile(); - const nextToken = findNextToken(node, sourceFile, sourceFile); - if (!nextToken) { - return false; + // See comment in parser’s `parseDoStatement` + if (node.kind === SyntaxKind.DoStatement) { + return true; } - if (nextToken.kind === SyntaxKind.CloseBraceToken) { + const sourceFile = node.getSourceFile(); + const nextToken = findNextToken(node, sourceFile, sourceFile); + if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) { return true; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 67d6a98e11ec5..3e2573d5f7a7e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3098,6 +3098,7 @@ declare namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; + readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; @@ -5305,6 +5306,7 @@ declare namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; + readonly insertTrailingSemicolon?: boolean; } function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings; interface DefinitionInfo extends DocumentSpan { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 279357eddf752..0de3e8e9cb462 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3098,6 +3098,7 @@ declare namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; + readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; @@ -5305,6 +5306,7 @@ declare namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; + readonly insertTrailingSemicolon?: boolean; } function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings; interface DefinitionInfo extends DocumentSpan { From 7cae3cdbd136b275d2560617fd546bace6ad9f8f Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 10 Sep 2019 14:53:27 -0700 Subject: [PATCH 05/25] Start adding tests --- src/harness/fourslash.ts | 34 +++++-------- src/services/formatting/formatting.ts | 12 ++++- src/services/utilities.ts | 12 ++--- tests/cases/fourslash/formatAddSemicolons1.ts | 49 +++++++++++++++++++ .../fourslash/formatRemoveSemicolons1.ts | 48 ++++++++++++++++++ tests/cases/fourslash/fourslash.ts | 35 ++++++++++++- 6 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 tests/cases/fourslash/formatAddSemicolons1.ts create mode 100644 tests/cases/fourslash/formatRemoveSemicolons1.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ae16159bc9d88..738676c5429b2 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1683,7 +1683,7 @@ namespace FourSlash { if (this.enableFormatting) { const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings); if (edits.length) { - offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + offset += this.applyEdits(this.activeFile.fileName, edits); } } } @@ -1756,7 +1756,7 @@ namespace FourSlash { if (this.enableFormatting) { const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings); if (edits.length) { - offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + offset += this.applyEdits(this.activeFile.fileName, edits); } } } @@ -1775,7 +1775,7 @@ namespace FourSlash { if (this.enableFormatting) { const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings); if (edits.length) { - this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + this.applyEdits(this.activeFile.fileName, edits); } } @@ -1810,9 +1810,7 @@ namespace FourSlash { * @returns The number of characters added to the file as a result of the edits. * May be negative. */ - private applyEdits(fileName: string, edits: ReadonlyArray, isFormattingEdit: boolean): number { - // Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters - const oldContent = this.getFileContent(fileName); + private applyEdits(fileName: string, edits: ReadonlyArray): number { let runningOffset = 0; forEachTextChange(edits, edit => { @@ -1833,14 +1831,6 @@ namespace FourSlash { runningOffset += editDelta; }); - if (isFormattingEdit) { - const newContent = this.getFileContent(fileName); - - if (this.removeWhitespace(newContent) !== this.removeWhitespace(oldContent)) { - this.raiseError("Formatting operation destroyed non-whitespace content"); - } - } - return runningOffset; } @@ -1856,17 +1846,17 @@ namespace FourSlash { public formatDocument() { const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings); - this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + this.applyEdits(this.activeFile.fileName, edits); } public formatSelection(start: number, end: number) { const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings); - this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + this.applyEdits(this.activeFile.fileName, edits); } public formatOnType(pos: number, key: string) { const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings); - this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + this.applyEdits(this.activeFile.fileName, edits); } private editScriptAndUpdateMarkers(fileName: string, editStart: number, editEnd: number, newText: string) { @@ -2414,7 +2404,7 @@ namespace FourSlash { if (options.applyChanges) { for (const change of action.changes) { - this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false); + this.applyEdits(change.fileName, change.textChanges); } this.verifyNewContentAfterChange(options, action.changes.map(c => c.fileName)); } @@ -2497,7 +2487,7 @@ namespace FourSlash { private applyChanges(changes: ReadonlyArray): void { for (const change of changes) { - this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false); + this.applyEdits(change.fileName, change.textChanges); } } @@ -2525,7 +2515,7 @@ namespace FourSlash { ts.Debug.assert(codeFix.changes.length === 1); const change = ts.first(codeFix.changes); ts.Debug.assert(change.fileName === fileName); - this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false); + this.applyEdits(change.fileName, change.textChanges); const text = range ? this.rangeText(range) : this.getFileContent(this.activeFile.fileName); actualTextArray.push(text); scriptInfo.updateContent(originalContent); @@ -2929,7 +2919,7 @@ namespace FourSlash { const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName, ts.emptyOptions)!; for (const edit of editInfo.edits) { - this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); + this.applyEdits(edit.fileName, edit.textChanges); } let renameFilename: string | undefined; @@ -3045,7 +3035,7 @@ namespace FourSlash { const editInfo = this.languageService.getEditsForRefactor(marker.fileName, formattingOptions, marker.position, refactorNameToApply, actionName, ts.emptyOptions)!; for (const edit of editInfo.edits) { - this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); + this.applyEdits(edit.fileName, edit.textChanges); } const actualContent = this.getFileContent(marker.fileName); diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index a23801a31534e..6469605c8eb7e 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1157,6 +1157,16 @@ namespace ts.formatting { } } + function recordInsertionOnce(start: number, text: string) { + if (text) { + const newEdit = createTextChangeFromStartLength(start, 0, text); + const lastEdit = lastOrUndefined(edits); + if (!lastEdit || !textSpansEqual(lastEdit.span, newEdit.span) || lastEdit.newText !== newEdit.newText) { + edits.push(newEdit); + } + } + } + function applyRuleEdits(rule: Rule, previousRange: TextRangeWithKind, previousStartLine: number, @@ -1206,7 +1216,7 @@ namespace ts.formatting { } break; case RuleAction.TrailingSemicolon: - recordReplace(previousRange.end, 0, ";"); + recordInsertionOnce(previousRange.end, ";"); } return LineAction.None; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index f14b181874676..4393f52eec44a 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2036,17 +2036,15 @@ namespace ts { return false; } } - if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) { - const children = node.parent.getChildren(); - const nextChild = children[children.indexOf(node) + 1]; - if (nextChild && isModuleBlock(nextChild)) { + else if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) { + const lastChild = last(node.getChildren()); + if (lastChild && isModuleBlock(lastChild)) { return false; } } else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) { - const children = node.parent.getChildren(); - const nextChild = children[children.indexOf(node) + 1]; - if (nextChild && isFunctionBlock(nextChild)) { + const lastChild = last(node.getChildren()); + if (lastChild && isFunctionBlock(lastChild)) { return false; } } diff --git a/tests/cases/fourslash/formatAddSemicolons1.ts b/tests/cases/fourslash/formatAddSemicolons1.ts new file mode 100644 index 0000000000000..409524dfc6074 --- /dev/null +++ b/tests/cases/fourslash/formatAddSemicolons1.ts @@ -0,0 +1,49 @@ +/// + +////console.log(1) +////console.log(2) +////const x = function() { } +////for (let i = 0; i < 1; i++) { +//// 1 +//// 2 +////} +////do { } while (false) console.log(3) +////function f() { } +////class C { +//// ["one"] = {} +//// ["two"] +//// three: string +//// m() { } +////} +////enum E { +//// C +////} +////type M = { [K in keyof T]: any } +////declare module 'foo' { } +////declare module 'bar' +////type T = { x: string, y: number } + +format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: true }); +format.document(); +verify.currentFileContentIs(`console.log(1); +console.log(2); +const x = function() { }; +for (let i = 0; i < 1; i++) { + 1; + 2; +} +do { } while (false); console.log(3); +function f() { } +class C { + ["one"] = {} + ["two"]; + three: string; + m() { } +} +enum E { + C +} +type M = { [K in keyof T]: any }; +declare module 'foo' { } +declare module 'bar'; +type T = { x: string, y: number; };`); diff --git a/tests/cases/fourslash/formatRemoveSemicolons1.ts b/tests/cases/fourslash/formatRemoveSemicolons1.ts new file mode 100644 index 0000000000000..d5084df814792 --- /dev/null +++ b/tests/cases/fourslash/formatRemoveSemicolons1.ts @@ -0,0 +1,48 @@ +/// + +////;(function f() { })(); +////const a = 3; +////+ 4; +////const b = 3 +////+ 4; +////const c = 3 + +////4; +////class C { +//// ["one"] = {}; +//// ["two"]; +//// ; +////} +////a; +////`b`; +////b; +////(3); +////4; +//// / regex /; +////; +////[]; +/////** blah */[0]; + +format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.document(); +verify.currentFileContentIs(`;(function f() { })() +const a = 3; ++ 4 +const b = 3 + + 4 +const c = 3 + + 4 +class C { + zero: void + ["one"] = {}; + ["two"]; + ; +} +a; +\`b\`; +b; +(3); +4; + / regex /; +; +[]; +/** blah */[0]`); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index d64bc0a86617f..f32057af08e23 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -96,6 +96,11 @@ declare namespace FourSlashInterface { position: number; data?: any; } + enum IndentStyle { + None = 0, + Block = 1, + Smart = 2, + } interface EditorOptions { BaseIndentSize?: number, IndentSize: number; @@ -103,6 +108,14 @@ declare namespace FourSlashInterface { NewLineCharacter: string; ConvertTabsToSpaces: boolean; } + interface EditorSettings { + baseIndentSize?: number; + indentSize?: number; + tabSize?: number; + newLineCharacter?: string; + convertTabsToSpaces?: boolean; + indentStyle?: IndentStyle; + } interface FormatCodeOptions extends EditorOptions { InsertSpaceAfterCommaDelimiter: boolean; InsertSpaceAfterSemicolonInForStatements: boolean; @@ -119,6 +132,26 @@ declare namespace FourSlashInterface { insertSpaceBeforeTypeAnnotation: boolean; [s: string]: boolean | number | string | undefined; } + interface FormatCodeSettings extends EditorSettings { + readonly insertSpaceAfterCommaDelimiter?: boolean; + readonly insertSpaceAfterSemicolonInForStatements?: boolean; + readonly insertSpaceBeforeAndAfterBinaryOperators?: boolean; + readonly insertSpaceAfterConstructor?: boolean; + readonly insertSpaceAfterKeywordsInControlFlowStatements?: boolean; + readonly insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean; + readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean; + readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean; + readonly insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean; + readonly insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean; + readonly insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean; + readonly insertSpaceAfterTypeAssertion?: boolean; + readonly insertSpaceBeforeFunctionParenthesis?: boolean; + readonly placeOpenBraceOnNewLineForFunctions?: boolean; + readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; + readonly insertSpaceBeforeTypeAnnotation?: boolean; + readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; + readonly insertTrailingSemicolon?: boolean; + } interface Range { fileName: string; pos: number; @@ -375,7 +408,7 @@ declare namespace FourSlashInterface { class format { document(): void; copyFormatOptions(): FormatCodeOptions; - setFormatOptions(options: FormatCodeOptions): any; + setFormatOptions(options: FormatCodeOptions | FormatCodeSettings): any; selection(startMarker: string, endMarker: string): void; onType(posMarker: string, key: string): void; setOption(name: keyof FormatCodeOptions, value: number | string | boolean): void; From e27bc9646a3f6cec82650b6f7243cd1cbabae6e3 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 10:35:52 -0700 Subject: [PATCH 06/25] Fix some edge cases of semicolon deletion --- src/services/formatting/formatting.ts | 10 +++-- src/services/formatting/rules.ts | 32 ++++++++++++- .../fourslash/formatRemoveSemicolons1.ts | 45 +++++++++++++++---- .../TypeScript-Node-Starter | 2 +- tests/cases/user/axios-src/axios-src | 2 +- .../user/create-react-app/create-react-app | 2 +- tests/cases/user/prettier/prettier | 2 +- tests/cases/user/puppeteer/puppeteer | 2 +- tests/cases/user/webpack/webpack | 2 +- 9 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 6469605c8eb7e..47cf646ba69d6 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1147,7 +1147,11 @@ namespace ts.formatting { function recordDelete(start: number, len: number) { if (len) { - edits.push(createTextChangeFromStartLength(start, len, "")); + const newEdit = createTextChangeFromStartLength(start, len, ""); + const lastEdit = lastOrUndefined(edits); + if (!lastEdit || !textSpansEqual(lastEdit.span, newEdit.span) || lastEdit.newText !== newEdit.newText) { + edits.push(newEdit); + } } } @@ -1157,7 +1161,7 @@ namespace ts.formatting { } } - function recordInsertionOnce(start: number, text: string) { + function recordInsert(start: number, text: string) { if (text) { const newEdit = createTextChangeFromStartLength(start, 0, text); const lastEdit = lastOrUndefined(edits); @@ -1216,7 +1220,7 @@ namespace ts.formatting { } break; case RuleAction.TrailingSemicolon: - recordInsertionOnce(previousRange.end, ";"); + recordInsert(previousRange.end, ";"); } return LineAction.None; } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index cc49a129e8190..f08f8c965856b 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -319,8 +319,34 @@ namespace ts.formatting { function isSemicolonDeletionContext(context: FormattingContext): boolean { if (context.TokensAreOnSameLine()) { - return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken; + 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 @@ -329,7 +355,11 @@ namespace ts.formatting { && 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; } diff --git a/tests/cases/fourslash/formatRemoveSemicolons1.ts b/tests/cases/fourslash/formatRemoveSemicolons1.ts index d5084df814792..8b4150e42297b 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons1.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons1.ts @@ -1,6 +1,6 @@ /// -////;(function f() { })(); +////; (function f() { })(); ////const a = 3; ////+ 4; ////const b = 3 @@ -8,6 +8,9 @@ ////const c = 3 + ////4; ////class C { +//// prop; +//// ["p"]; +//// zero: void; //// ["one"] = {}; //// ["two"]; //// ; @@ -21,28 +24,54 @@ ////; ////[]; /////** blah */[0]; +////interface I { +//// new; +//// (); +//// foo; +//// (); +////} +////type T = { +//// new; +//// (); +//// foo; +//// (); +////} format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); format.document(); -verify.currentFileContentIs(`;(function f() { })() +verify.currentFileContentIs(`; (function f() { })() const a = 3; + 4 const b = 3 - + 4 ++ 4 const c = 3 + - 4 +4 class C { + prop + ["p"] zero: void ["one"] = {}; ["two"]; ; } a; -\`b\`; +\`b\` b; -(3); +(3) 4; - / regex /; +/ regex /; ; []; -/** blah */[0]`); +/** blah */[0] +interface I { + new; + () + foo; + () +} +type T = { + new; + () + foo; + () +}`); \ No newline at end of file diff --git a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter index 722ebf8053d2b..1bf5836cae524 160000 --- a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter +++ b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter @@ -1 +1 @@ -Subproject commit 722ebf8053d2bf82bb66134b21c7e291ccae35c4 +Subproject commit 1bf5836cae5246b89bbf7063c3e84e110222fcdf diff --git a/tests/cases/user/axios-src/axios-src b/tests/cases/user/axios-src/axios-src index 98e4acd893fe0..2ee3b482456cd 160000 --- a/tests/cases/user/axios-src/axios-src +++ b/tests/cases/user/axios-src/axios-src @@ -1 +1 @@ -Subproject commit 98e4acd893fe024ae9e6074894c6164802b3af63 +Subproject commit 2ee3b482456cd2a09ccbd3a4b0c20f3d0c5a5644 diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app index 6dec056de3c64..6560858398ddc 160000 --- a/tests/cases/user/create-react-app/create-react-app +++ b/tests/cases/user/create-react-app/create-react-app @@ -1 +1 @@ -Subproject commit 6dec056de3c646fa1bce41acadc31641237863a0 +Subproject commit 6560858398ddc8d1c5b8d7f51929fcb3d9c3055c diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 223443c057e64..2f40dba3177c6 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 223443c057e64ca04cda5c0f37f5d15daaf69337 +Subproject commit 2f40dba3177c6edd3ceb88b26cdf4718e892a3e5 diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer index c2651c2b5cc88..cba0f98a2ac7e 160000 --- a/tests/cases/user/puppeteer/puppeteer +++ b/tests/cases/user/puppeteer/puppeteer @@ -1 +1 @@ -Subproject commit c2651c2b5cc888ebd0ca6be87063d9f98b9eb59c +Subproject commit cba0f98a2ac7edd3c2bffd0ac53185877403da6b diff --git a/tests/cases/user/webpack/webpack b/tests/cases/user/webpack/webpack index b2b5b278ddfa4..b16ca509d12fa 160000 --- a/tests/cases/user/webpack/webpack +++ b/tests/cases/user/webpack/webpack @@ -1 +1 @@ -Subproject commit b2b5b278ddfa49cea5a2a74b42917633e2d58237 +Subproject commit b16ca509d12faf36573b65fffcbae50c5b3e7ee3 From ae9a234de0e8fde9557cfdd97c08ed89caac0785 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 14:19:12 -0700 Subject: [PATCH 07/25] Fix semicolon removal before comments --- src/services/formatting/rules.ts | 118 +++++++++--------- src/services/formatting/rulesMap.ts | 2 +- src/services/types.ts | 2 +- .../fourslash/formatRemoveSemicolons2.ts | 15 +++ 4 files changed, 78 insertions(+), 59 deletions(-) create mode 100644 tests/cases/fourslash/formatRemoveSemicolons2.ts diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index f08f8c965856b..827378e3eed5b 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 8851313c8bd66..634cb74f8a74b 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 722539af150bb..1e6da9505676d 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 0000000000000..9760cc4f2063c --- /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 +}`); From 0ac2807216c4d149ca56c71a40bf6b7ba7de96dd Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 14:58:48 -0700 Subject: [PATCH 08/25] Fix indentation --- src/services/formatting/formatting.ts | 2 +- src/services/formatting/rules.ts | 13 ++++++----- src/services/types.ts | 2 +- .../fourslash/formatRemoveSemicolons1.ts | 4 ++-- .../fourslash/formatRemoveSemicolons2.ts | 22 ++++++++++++------- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 47cf646ba69d6..08792f7636800 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1191,7 +1191,7 @@ namespace ts.formatting { break; case RuleAction.DeleteToken: recordDelete(previousRange.pos, previousRange.end - previousRange.pos); - return onLaterLine ? LineAction.LineRemoved : LineAction.None; + break; case RuleAction.NewLine: // exit early if we on different lines and rule cannot change number of newlines // if line1 and line2 are on subsequent lines then no edits are required - ok to exit diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 827378e3eed5b..0ddc304e2f9c4 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -778,15 +778,18 @@ namespace ts.formatting { } 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; + const startLine = context.sourceFile.getLineAndCharacterOfPosition(context.currentTokenSpan.pos).line; + const endLine = context.sourceFile.getLineAndCharacterOfPosition(nextToken.pos).line; + if (startLine === endLine) { + return nextToken.kind === SyntaxKind.CloseBraceToken + || nextToken.kind === SyntaxKind.EndOfFileToken; + } + + if (nextToken.kind === SyntaxKind.SemicolonClassElement || nextToken.kind === SyntaxKind.SemicolonToken ) { diff --git a/src/services/types.ts b/src/services/types.ts index 1e6da9505676d..475594dfa5c2f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -758,7 +758,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, - // insertTrailingSemicolon: false, + insertTrailingSemicolon: false, }; } diff --git a/tests/cases/fourslash/formatRemoveSemicolons1.ts b/tests/cases/fourslash/formatRemoveSemicolons1.ts index 8b4150e42297b..fe87970baa4aa 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons1.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons1.ts @@ -43,9 +43,9 @@ verify.currentFileContentIs(`; (function f() { })() const a = 3; + 4 const b = 3 -+ 4 + + 4 const c = 3 + -4 + 4 class C { prop ["p"] diff --git a/tests/cases/fourslash/formatRemoveSemicolons2.ts b/tests/cases/fourslash/formatRemoveSemicolons2.ts index 9760cc4f2063c..1371b94151fc5 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons2.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons2.ts @@ -1,15 +1,21 @@ /// -////interface I { -//// a: string; -//// /** @internal */ -//// b: string; +////namespace ts { +//// let x = 0; +//// interface I { +//// a: string; +//// /** @internal */ +//// b: string; +//// } ////} format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); format.document(); -verify.currentFileContentIs(`interface I { - a: string - /** @internal */ - b: string +verify.currentFileContentIs(`namespace ts { + let x = 0 + interface I { + a: string + /** @internal */ + b: string + } }`); From 4027433e53e4168bb9b2f1d506479ce6401bef41 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 15:52:51 -0700 Subject: [PATCH 09/25] Test on checker --- src/services/formatting/rules.ts | 51 +++++++++++-------- src/services/types.ts | 2 +- .../fourslash/formatRemoveSemicolons2.ts | 8 ++- .../fourslash/formatRemoveSemicolons3.ts | 11 ++++ 4 files changed, 49 insertions(+), 23 deletions(-) create mode 100644 tests/cases/fourslash/formatRemoveSemicolons3.ts diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 0ddc304e2f9c4..d6db8cfd3770f 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -778,20 +778,29 @@ namespace ts.formatting { } function isSemicolonDeletionContext(context: FormattingContext): boolean { - const nextToken = isTrivia(context.nextTokenSpan.kind) - ? context.nextTokenParent.getChildAt(0) - : context.nextTokenSpan; + let nextTokenKind = context.nextTokenSpan.kind; + let nextTokenStart = context.nextTokenSpan.pos; + if (isTrivia(nextTokenKind)) { + const sourceFile = context.currentTokenParent.getSourceFile(); + const nextRealToken = context.nextTokenParent === context.currentTokenParent + ? findNextToken(context.currentTokenParent, sourceFile, sourceFile) + : context.nextTokenParent.getFirstToken(); + if (!nextRealToken) { + return true; + } + nextTokenKind = nextRealToken.kind; + nextTokenStart = nextRealToken.getStart(); + } const startLine = context.sourceFile.getLineAndCharacterOfPosition(context.currentTokenSpan.pos).line; - const endLine = context.sourceFile.getLineAndCharacterOfPosition(nextToken.pos).line; + const endLine = context.sourceFile.getLineAndCharacterOfPosition(nextTokenStart).line; if (startLine === endLine) { - return nextToken.kind === SyntaxKind.CloseBraceToken - || nextToken.kind === SyntaxKind.EndOfFileToken; + return nextTokenKind === SyntaxKind.CloseBraceToken + || nextTokenKind === SyntaxKind.EndOfFileToken; } - - if (nextToken.kind === SyntaxKind.SemicolonClassElement || - nextToken.kind === SyntaxKind.SemicolonToken + if (nextTokenKind === SyntaxKind.SemicolonClassElement || + nextTokenKind === SyntaxKind.SemicolonToken ) { return false; } @@ -807,7 +816,7 @@ namespace ts.formatting { // } return !(isPropertySignature(context.currentTokenParent) && !context.currentTokenParent.type - && nextToken.kind === SyntaxKind.OpenParenToken); + && nextTokenKind === SyntaxKind.OpenParenToken); } if (isPropertyDeclaration(context.currentTokenParent)) { @@ -817,17 +826,17 @@ namespace ts.formatting { 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; + && nextTokenKind !== SyntaxKind.OpenBracketToken + && nextTokenKind !== SyntaxKind.OpenParenToken + && nextTokenKind !== SyntaxKind.PlusToken + && nextTokenKind !== SyntaxKind.MinusToken + && nextTokenKind !== SyntaxKind.SlashToken + && nextTokenKind !== SyntaxKind.RegularExpressionLiteral + && nextTokenKind !== SyntaxKind.CommaToken + && nextTokenKind !== SyntaxKind.TemplateExpression + && nextTokenKind !== SyntaxKind.TemplateHead + && nextTokenKind !== SyntaxKind.NoSubstitutionTemplateLiteral + && nextTokenKind !== SyntaxKind.DotToken; } function isSemicolonInsertionContext(context: FormattingContext): boolean { diff --git a/src/services/types.ts b/src/services/types.ts index 475594dfa5c2f..fa2cdc1c82168 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -758,7 +758,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, - insertTrailingSemicolon: false, + insertTrailingSemicolon: true, }; } diff --git a/tests/cases/fourslash/formatRemoveSemicolons2.ts b/tests/cases/fourslash/formatRemoveSemicolons2.ts index 1371b94151fc5..742a28a88d8af 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons2.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons2.ts @@ -2,20 +2,26 @@ ////namespace ts { //// let x = 0; +//// // //// interface I { //// a: string; //// /** @internal */ //// b: string; //// } +//// let y = 0; // ////} +////let z = 0; // format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); format.document(); verify.currentFileContentIs(`namespace ts { let x = 0 + // interface I { a: string /** @internal */ b: string } -}`); + let y = 0 // +} +let z = 0 //`); diff --git a/tests/cases/fourslash/formatRemoveSemicolons3.ts b/tests/cases/fourslash/formatRemoveSemicolons3.ts new file mode 100644 index 0000000000000..9b2266214e1f0 --- /dev/null +++ b/tests/cases/fourslash/formatRemoveSemicolons3.ts @@ -0,0 +1,11 @@ +/// + +////(type).declaredProperties = getNamedMembers(members); +////// Start with signatures at empty array in case of recursive types +////(type).declaredCallSignatures = emptyArray; + +format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.document(); +verify.currentFileContentIs(`(type).declaredProperties = getNamedMembers(members); +// Start with signatures at empty array in case of recursive types +(type).declaredCallSignatures = emptyArray`); \ No newline at end of file From 149eaf67eed8aeff434236a770bce6594b45c1d2 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 16:42:55 -0700 Subject: [PATCH 10/25] Replace semicolon-omitting writer with formatter preference --- src/compiler/types.ts | 1 - src/compiler/utilities.ts | 20 +------ src/services/formatting/rules.ts | 8 ++- src/services/textChanges.ts | 59 +++++++++++-------- src/services/types.ts | 10 +++- .../unittests/services/textChanges.ts | 2 +- 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 29fb9ca71d84c..999a922041201 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6166,7 +6166,6 @@ namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; - readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index facc13ada282f..2afa3c21a17c1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3374,11 +3374,7 @@ namespace ts { }; } - export interface TrailingSemicolonDeferringWriter extends EmitTextWriter { - resetPendingTrailingSemicolon(): void; - } - - export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): TrailingSemicolonDeferringWriter { + export function getTrailingSemicolonDeferringWriter(writer: EmitTextWriter): EmitTextWriter { let pendingTrailingSemicolon = false; function commitPendingTrailingSemicolon() { @@ -3445,20 +3441,6 @@ namespace ts { commitPendingTrailingSemicolon(); writer.decreaseIndent(); }, - resetPendingTrailingSemicolon() { - pendingTrailingSemicolon = false; - } - }; - } - - export function getTrailingSemicolonOmittingWriter(writer: EmitTextWriter): EmitTextWriter { - const deferringWriter = getTrailingSemicolonDeferringWriter(writer); - return { - ...deferringWriter, - writeLine() { - deferringWriter.resetPendingTrailingSemicolon(); - writer.writeLine(); - }, }; } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index d6db8cfd3770f..a1ffc4659ead9 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -313,8 +313,8 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), - rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [isOptionDisabled("insertTrailingSemicolon"), isSemicolonDeletionContext], RuleAction.DeleteToken), - rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [isOptionEnabled("insertTrailingSemicolon"), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), + rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolonPreference", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolonPreference", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list. @@ -400,6 +400,10 @@ namespace ts.formatting { /// Contexts /// + function optionEquals(optionName: K, optionValue: FormatCodeSettings[K]): (context: FormattingContext) => boolean { + return (context) => context.options && context.options[optionName] === optionValue; + } + function isOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean { return (context) => context.options && context.options.hasOwnProperty(optionName) && !!context.options[optionName]; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 788c2f152f540..f4ea350dd8b2f 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -236,7 +236,7 @@ namespace ts.textChanges { private readonly deletedNodes: { readonly sourceFile: SourceFile, readonly node: Node | NodeArray }[] = []; public static fromContext(context: TextChangesContext): ChangeTracker { - return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext, context.preferences); + return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options), context.formatContext); } public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] { @@ -246,7 +246,7 @@ namespace ts.textChanges { } /** Public for tests only. Other callers should use `ChangeTracker.with`. */ - constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext, private preferences: UserPreferences) {} + constructor(private readonly newLineCharacter: string, private readonly formatContext: formatting.FormatContext) {} public deleteRange(sourceFile: SourceFile, range: TextRange): void { this.changes.push({ kind: ChangeKind.Remove, sourceFile, range }); @@ -753,9 +753,9 @@ namespace ts.textChanges { public getChanges(validate?: ValidateNonFormattedText): FileTextChanges[] { this.finishDeleteDeclarations(); this.finishClassesWithNodesInsertedAtStart(); - const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, this.preferences, validate); + const changes = changesToText.getTextChangesFromChanges(this.changes, this.newLineCharacter, this.formatContext, validate); for (const { oldFile, fileName, statements } of this.newFiles) { - changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext, this.preferences)); + changes.push(changesToText.newFileChanges(oldFile, fileName, statements, this.newLineCharacter, this.formatContext)); } return changes; } @@ -779,12 +779,12 @@ namespace ts.textChanges { export type ValidateNonFormattedText = (node: Node, text: string) => void; - export function getNewFileText(statements: ReadonlyArray, scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): string { - return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext, preferences); + export function getNewFileText(statements: ReadonlyArray, scriptKind: ScriptKind, newLineCharacter: string, formatContext: formatting.FormatContext): string { + return changesToText.newFileChangesWorker(/*oldFile*/ undefined, scriptKind, statements, newLineCharacter, formatContext); } namespace changesToText { - export function getTextChangesFromChanges(changes: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { + export function getTextChangesFromChanges(changes: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): FileTextChanges[] { return group(changes, c => c.sourceFile.path).map(changesInFile => { const sourceFile = changesInFile[0].sourceFile; // order changes by start position @@ -796,25 +796,25 @@ namespace ts.textChanges { `${JSON.stringify(normalized[i].range)} and ${JSON.stringify(normalized[i + 1].range)}`); } const textChanges = normalized.map(c => - createTextChange(createTextSpanFromRange(c.range), computeNewText(c, sourceFile, newLineCharacter, formatContext, preferences, validate))); + createTextChange(createTextSpanFromRange(c.range), computeNewText(c, sourceFile, newLineCharacter, formatContext, validate))); return { fileName: sourceFile.fileName, textChanges }; }); } - export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): FileTextChanges { - const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext, preferences); + export function newFileChanges(oldFile: SourceFile | undefined, fileName: string, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext): FileTextChanges { + const text = newFileChangesWorker(oldFile, getScriptKindFromFileName(fileName), statements, newLineCharacter, formatContext); return { fileName, textChanges: [createTextChange(createTextSpan(0, 0), text)], isNewFile: true }; } - export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences): string { + export function newFileChangesWorker(oldFile: SourceFile | undefined, scriptKind: ScriptKind, statements: ReadonlyArray, newLineCharacter: string, formatContext: formatting.FormatContext): string { // TODO: this emits the file, parses it back, then formats it that -- may be a less roundabout way to do this - const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter, preferences).text).join(newLineCharacter); + const nonFormattedText = statements.map(s => getNonformattedText(s, oldFile, newLineCharacter).text).join(newLineCharacter); const sourceFile = createSourceFile("any file name", nonFormattedText, ScriptTarget.ESNext, /*setParentNodes*/ true, scriptKind); const changes = formatting.formatDocument(sourceFile, formatContext); return applyChanges(nonFormattedText, changes) + newLineCharacter; } - function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): string { + function computeNewText(change: Change, sourceFile: SourceFile, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { if (change.kind === ChangeKind.Remove) { return ""; } @@ -823,7 +823,7 @@ namespace ts.textChanges { } const { options = {}, range: { pos } } = change; - const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, preferences, validate); + const format = (n: Node) => getFormattedTextOfNode(n, sourceFile, pos, options, newLineCharacter, formatContext, validate); const text = change.kind === ChangeKind.ReplaceWithMultipleNodes ? change.nodes.map(n => removeSuffix(format(n), newLineCharacter)).join(change.options!.joiner || newLineCharacter) // TODO: GH#18217 : format(change.node); @@ -832,30 +832,37 @@ namespace ts.textChanges { return (options.prefix || "") + noIndent + (options.suffix || ""); } + function getFormatCodeSettingsForWriting(context: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings { + return { + ...context.options, + // If the user has no semicolon preference defined, try to match whatever’s in the source file + semicolonPreference: !context.options.semicolonPreference || context.options.semicolonPreference === SemicolonPreference.Ignore + ? probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Ignore : SemicolonPreference.Remove + : context.options.semicolonPreference, + }; + } + /** Note: this may mutate `nodeIn`. */ - function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, preferences: UserPreferences, validate: ValidateNonFormattedText | undefined): string { - const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter, preferences); + function getFormattedTextOfNode(nodeIn: Node, sourceFile: SourceFile, pos: number, { indentation, prefix, delta }: InsertNodeOptions, newLineCharacter: string, formatContext: formatting.FormatContext, validate: ValidateNonFormattedText | undefined): string { + const { node, text } = getNonformattedText(nodeIn, sourceFile, newLineCharacter); if (validate) validate(node, text); - const { options: formatOptions } = formatContext; + const formatOptions = getFormatCodeSettingsForWriting(formatContext, sourceFile); const initialIndentation = indentation !== undefined ? indentation : formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos); if (delta === undefined) { - delta = formatting.SmartIndenter.shouldIndentChildNode(formatContext.options, nodeIn) ? (formatOptions.indentSize || 0) : 0; + delta = formatting.SmartIndenter.shouldIndentChildNode(formatOptions, nodeIn) ? (formatOptions.indentSize || 0) : 0; } const file: SourceFileLike = { text, getLineAndCharacterOfPosition(pos) { return getLineAndCharacterOfPosition(this, pos); } }; - const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, formatContext); + const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, { ...formatContext, options: formatOptions }); return applyChanges(text, changes); } /** Note: output node may be mutated input node. */ - export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string, { semicolonPreference }: UserPreferences): { text: string, node: Node } { - const omitTrailingSemicolon = semicolonPreference === "no semicolons" ? true : - semicolonPreference === "semicolons" ? false : - !!sourceFile && !probablyUsesSemicolons(sourceFile); - const writer = createWriter(newLineCharacter, omitTrailingSemicolon); + export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLineCharacter: string): { text: string, node: Node } { + const writer = createWriter(newLineCharacter); const newLine = newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed; createPrinter({ newLine, neverAsciiEscape: true }, writer).writeNode(EmitHint.Unspecified, node, sourceFile, writer); return { text: writer.getText(), node: assignPositionsToNode(node) }; @@ -897,11 +904,11 @@ namespace ts.textChanges { interface TextChangesWriter extends EmitTextWriter, PrintHandlers {} - function createWriter(newLine: string, omitTrailingSemicolon?: boolean): TextChangesWriter { + function createWriter(newLine: string): TextChangesWriter { let lastNonTriviaPosition = 0; - const writer = omitTrailingSemicolon ? getTrailingSemicolonOmittingWriter(createTextWriter(newLine)) : createTextWriter(newLine); + const writer = createTextWriter(newLine); const onEmitNode: PrintHandlers["onEmitNode"] = (hint, node, printCallback) => { if (node) { setPos(node, lastNonTriviaPosition); diff --git a/src/services/types.ts b/src/services/types.ts index fa2cdc1c82168..eb6ffd2871e0c 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -676,6 +676,12 @@ namespace ts { Smart = 2, } + export enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove", + } + /* @deprecated - consider using EditorSettings instead */ export interface EditorOptions { BaseIndentSize?: number; @@ -734,7 +740,7 @@ namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; - readonly insertTrailingSemicolon?: boolean; + readonly semicolonPreference?: SemicolonPreference; } export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings { @@ -758,7 +764,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, - insertTrailingSemicolon: true, + semicolonPreference: SemicolonPreference.Ignore, }; } diff --git a/src/testRunner/unittests/services/textChanges.ts b/src/testRunner/unittests/services/textChanges.ts index 17844d6b3ef8e..27195c300348a 100644 --- a/src/testRunner/unittests/services/textChanges.ts +++ b/src/testRunner/unittests/services/textChanges.ts @@ -49,7 +49,7 @@ namespace ts { it(caption, () => { const sourceFile = createSourceFile("source.ts", text, ScriptTarget.ES2015, /*setParentNodes*/ true); const rulesProvider = getRuleProvider(placeOpenBraceOnNewLineForFunctions); - const changeTracker = new textChanges.ChangeTracker(newLineCharacter, rulesProvider, {}); + const changeTracker = new textChanges.ChangeTracker(newLineCharacter, rulesProvider); testBlock(sourceFile, changeTracker); const changes = changeTracker.getChanges(validateNodes ? verifyPositions : undefined); assert.equal(changes.length, 1); From 4555bb815b69981bcb8d1da1c0553c5815e30cda Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 18:26:26 -0700 Subject: [PATCH 11/25] Fix writing new nodes, update protocol --- src/server/protocol.ts | 7 +++++++ src/services/formatting/formatting.ts | 8 ++++---- src/services/textChanges.ts | 10 ++++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d4ad6e7f0f1e8..c791eebf7d2ad 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2946,6 +2946,12 @@ namespace ts.server.protocol { Smart = "Smart", } + export enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove", + } + export interface EditorSettings { baseIndentSize?: number; indentSize?: number; @@ -2972,6 +2978,7 @@ namespace ts.server.protocol { placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; + semicolonPreference?: SemicolonPreference } export interface UserPreferences { diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 08792f7636800..8cd9cc9c5422a 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -426,7 +426,7 @@ namespace ts.formatting { const leadingTrivia = formattingScanner.getCurrentLeadingTrivia(); if (leadingTrivia) { indentTriviaItems(leadingTrivia, initialIndentation, /*indentNextTokenOrTrivia*/ false, - item => processRange(item, sourceFile.getLineAndCharacterOfPosition(item.pos), enclosingNode, enclosingNode, /*dynamicIndentation*/ undefined!)); + item => processRange(item, sourceFile.getLineAndCharacterOfPosition(item.pos), enclosingNode, enclosingNode, /*dynamicIndentation*/ undefined!)); trimTrailingWhitespacesForRemainingRange(); } } @@ -477,7 +477,7 @@ namespace ts.formatting { parent: Node, parentDynamicIndentation: DynamicIndentation, effectiveParentStartLine: number - ): { indentation: number, delta: number } { + ): { indentation: number, delta: number; } { const delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize! : 0; if (effectiveParentStartLine === startLine) { @@ -867,7 +867,7 @@ namespace ts.formatting { if (currentTokenInfo.leadingTrivia) { const commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container); indentNextTokenOrTrivia = indentTriviaItems(currentTokenInfo.leadingTrivia, commentIndentation, indentNextTokenOrTrivia, - item => insertIndentation(item.pos, commentIndentation, /*lineAdded*/ false)); + item => insertIndentation(item.pos, commentIndentation, /*lineAdded*/ false)); } // indent token only if is it is in target range and does not overlap with any error ranges @@ -1317,7 +1317,7 @@ namespace ts.formatting { return SyntaxKind.Unknown; } - let internedSizes: { tabSize: number; indentSize: number }; + let internedSizes: { tabSize: number; indentSize: number; }; let internedTabsIndentation: string[] | undefined; let internedSpacesIndentation: string[] | undefined; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index f4ea350dd8b2f..71d5f289e246c 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -835,10 +835,12 @@ namespace ts.textChanges { function getFormatCodeSettingsForWriting(context: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings { return { ...context.options, - // If the user has no semicolon preference defined, try to match whatever’s in the source file - semicolonPreference: !context.options.semicolonPreference || context.options.semicolonPreference === SemicolonPreference.Ignore - ? probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Ignore : SemicolonPreference.Remove - : context.options.semicolonPreference, + // If the user has no semicolon preference defined and the file doesn’t use semicolons, + // make the formatter remove them. Otherwise, ignore semicolons in the formatter because + // the writer will insert them by default. + semicolonPreference: context.options.semicolonPreference === SemicolonPreference.Remove || (!context.options.semicolonPreference || context.options.semicolonPreference === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) + ? SemicolonPreference.Remove + : SemicolonPreference.Ignore, }; } From ac30127317a2df3d1a28e66fe6cdbb95b068b65e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 12 Sep 2019 09:47:22 -0700 Subject: [PATCH 12/25] Rename option --- src/server/protocol.ts | 2 +- src/services/formatting/rules.ts | 4 ++-- src/services/textChanges.ts | 2 +- src/services/types.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index c791eebf7d2ad..63e0ca1744ff7 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2978,7 +2978,7 @@ namespace ts.server.protocol { placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; - semicolonPreference?: SemicolonPreference + semicolons?: SemicolonPreference; } export interface UserPreferences { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index a1ffc4659ead9..0672fd5609f38 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -313,8 +313,8 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), - rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolonPreference", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), - rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolonPreference", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), + rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list. diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 71d5f289e246c..cf10d01b8d620 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -838,7 +838,7 @@ namespace ts.textChanges { // If the user has no semicolon preference defined and the file doesn’t use semicolons, // make the formatter remove them. Otherwise, ignore semicolons in the formatter because // the writer will insert them by default. - semicolonPreference: context.options.semicolonPreference === SemicolonPreference.Remove || (!context.options.semicolonPreference || context.options.semicolonPreference === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) + semicolons: context.options.semicolons === SemicolonPreference.Remove || (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Remove : SemicolonPreference.Ignore, }; diff --git a/src/services/types.ts b/src/services/types.ts index eb6ffd2871e0c..a73b21db69290 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -740,7 +740,7 @@ namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; - readonly semicolonPreference?: SemicolonPreference; + readonly semicolons?: SemicolonPreference; } export function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings { @@ -764,7 +764,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, - semicolonPreference: SemicolonPreference.Ignore, + semicolons: SemicolonPreference.Ignore, }; } From ca8b8f06386094d395ffcaf9e9d6e1c9a959d37b Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 12 Sep 2019 13:06:54 -0700 Subject: [PATCH 13/25] Really fix formatting synthetic nodes --- src/services/formatting/rules.ts | 12 +++++++----- src/services/services.ts | 4 ++-- src/services/textChanges.ts | 3 ++- src/services/types.ts | 6 ++++++ src/services/utilities.ts | 18 ++++++++++-------- tests/cases/fourslash/extract-method28.ts | 20 ++++++++++++++++++++ tests/cases/fourslash/extract-method29.ts | 20 ++++++++++++++++++++ tests/cases/fourslash/extract-method30.ts | 20 ++++++++++++++++++++ tests/cases/fourslash/fourslash.ts | 8 +++++++- 9 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/extract-method28.ts create mode 100644 tests/cases/fourslash/extract-method29.ts create mode 100644 tests/cases/fourslash/extract-method30.ts diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 0672fd5609f38..dac301c7a2a94 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -785,15 +785,17 @@ namespace ts.formatting { let nextTokenKind = context.nextTokenSpan.kind; let nextTokenStart = context.nextTokenSpan.pos; if (isTrivia(nextTokenKind)) { - const sourceFile = context.currentTokenParent.getSourceFile(); const nextRealToken = context.nextTokenParent === context.currentTokenParent - ? findNextToken(context.currentTokenParent, sourceFile, sourceFile) - : context.nextTokenParent.getFirstToken(); + ? findNextToken( + context.currentTokenParent, + findAncestor(context.currentTokenParent, a => !a.parent)!, + context.sourceFile) + : context.nextTokenParent.getFirstToken(context.sourceFile); if (!nextRealToken) { return true; } nextTokenKind = nextRealToken.kind; - nextTokenStart = nextRealToken.getStart(); + nextTokenStart = nextRealToken.getStart(context.sourceFile); } const startLine = context.sourceFile.getLineAndCharacterOfPosition(context.currentTokenSpan.pos).line; @@ -851,6 +853,6 @@ namespace ts.formatting { return syntaxMayBeASICandidate(ancestor.kind); }); - return !!contextAncestor && isASICandidate(contextAncestor); + return !!contextAncestor && isASICandidate(contextAncestor, context.sourceFile); } } diff --git a/src/services/services.ts b/src/services/services.ts index ad8528b65af65..e32a58dd1bc07 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -99,7 +99,7 @@ namespace ts { return this._children || (this._children = createChildren(this, sourceFile)); } - public getFirstToken(sourceFile?: SourceFile): Node | undefined { + public getFirstToken(sourceFile?: SourceFileLike): Node | undefined { this.assertHasRealPosition(); const children = this.getChildren(sourceFile); if (!children.length) { @@ -112,7 +112,7 @@ namespace ts { child.getFirstToken(sourceFile); } - public getLastToken(sourceFile?: SourceFile): Node | undefined { + public getLastToken(sourceFile?: SourceFileLike): Node | undefined { this.assertHasRealPosition(); const children = this.getChildren(sourceFile); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index cf10d01b8d620..57b266a26e2fe 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -838,7 +838,8 @@ namespace ts.textChanges { // If the user has no semicolon preference defined and the file doesn’t use semicolons, // make the formatter remove them. Otherwise, ignore semicolons in the formatter because // the writer will insert them by default. - semicolons: context.options.semicolons === SemicolonPreference.Remove || (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) + semicolons: context.options.semicolons === SemicolonPreference.Remove || + (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Remove : SemicolonPreference.Ignore, }; diff --git a/src/services/types.ts b/src/services/types.ts index a73b21db69290..f17fa477cc326 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -19,7 +19,13 @@ namespace ts { getFullText(sourceFile?: SourceFile): string; getText(sourceFile?: SourceFile): string; getFirstToken(sourceFile?: SourceFile): Node | undefined; + /* @internal */ + // tslint:disable-next-line unified-signatures + getFirstToken(sourceFile?: SourceFileLike): Node | undefined; getLastToken(sourceFile?: SourceFile): Node | undefined; + /* @internal */ + // tslint:disable-next-line unified-signatures + getLastToken(sourceFile?: SourceFileLike): Node | undefined; // See ts.forEachChild for documentation. forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4393f52eec44a..4a2389a53b7d2 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -751,7 +751,7 @@ namespace ts { // this is token that starts at the end of previous token - return it return n; } - return firstDefined(n.getChildren(), child => { + return firstDefined(n.getChildren(sourceFile), child => { const shouldDiveInChildNode = // previous token is enclosed somewhere in the child (child.pos <= previousToken.pos && child.end > previousToken.end) || @@ -2025,8 +2025,8 @@ namespace ts { syntaxRequiresTrailingModuleBlockOrSemicolonOrASI, syntaxRequiresTrailingSemicolonOrASI); - export function isASICandidate(node: Node): boolean { - const lastToken = node.getLastToken(); + export function isASICandidate(node: Node, sourceFile: SourceFileLike): boolean { + const lastToken = node.getLastToken(sourceFile); if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) { return false; } @@ -2037,13 +2037,13 @@ namespace ts { } } else if (syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind)) { - const lastChild = last(node.getChildren()); + const lastChild = last(node.getChildren(sourceFile)); if (lastChild && isModuleBlock(lastChild)) { return false; } } else if (syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind)) { - const lastChild = last(node.getChildren()); + const lastChild = last(node.getChildren(sourceFile)); if (lastChild && isFunctionBlock(lastChild)) { return false; } @@ -2057,13 +2057,15 @@ namespace ts { return true; } - const sourceFile = node.getSourceFile(); - const nextToken = findNextToken(node, sourceFile, sourceFile); + const topNode = findAncestor(node, ancestor => !ancestor.parent)!; + const nextToken = findNextToken(node, topNode, sourceFile); if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) { return true; } - return !positionsAreOnSameLine(node.getEnd(), nextToken.getStart(sourceFile), sourceFile); + const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; + const endLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line; + return startLine !== endLine; } export function probablyUsesSemicolons(sourceFile: SourceFile): boolean { diff --git a/tests/cases/fourslash/extract-method28.ts b/tests/cases/fourslash/extract-method28.ts new file mode 100644 index 0000000000000..a1e1189102c3f --- /dev/null +++ b/tests/cases/fourslash/extract-method28.ts @@ -0,0 +1,20 @@ +/// + +/////*a*/console.log(0) // +////console.log(0)/*b*/ + +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Insert }); +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "function_scope_0", + actionDescription: "Extract to function in global scope", + newContent: +`/*RENAME*/newFunction(); + +function newFunction() { + console.log(0); // + console.log(0); +} +` +}); diff --git a/tests/cases/fourslash/extract-method29.ts b/tests/cases/fourslash/extract-method29.ts new file mode 100644 index 0000000000000..a7f83e65340be --- /dev/null +++ b/tests/cases/fourslash/extract-method29.ts @@ -0,0 +1,20 @@ +/// + +/////*a*/console.log(0); // +////console.log(0);/*b*/ + +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove }); +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "function_scope_0", + actionDescription: "Extract to function in global scope", + newContent: +`/*RENAME*/newFunction() + +function newFunction() { + console.log(0) // + console.log(0) +} +` +}); diff --git a/tests/cases/fourslash/extract-method30.ts b/tests/cases/fourslash/extract-method30.ts new file mode 100644 index 0000000000000..cf2c87e5c3f88 --- /dev/null +++ b/tests/cases/fourslash/extract-method30.ts @@ -0,0 +1,20 @@ +/// + +/////*a*/console.log(0) // +////console.log(0)/*b*/ + +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Ignore }); +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract Symbol", + actionName: "function_scope_0", + actionDescription: "Extract to function in global scope", + newContent: +`/*RENAME*/newFunction() + +function newFunction() { + console.log(0) // + console.log(0) +} +` +}); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index f32057af08e23..a1d4cab5a4da5 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -66,6 +66,12 @@ declare module ts { Smart = 2, } + enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove", + } + interface OutputFile { name: string; writeByteOrderMark: boolean; @@ -150,7 +156,7 @@ declare namespace FourSlashInterface { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; - readonly insertTrailingSemicolon?: boolean; + readonly semicolons?: ts.SemicolonPreference; } interface Range { fileName: string; From 9c335909652608ecbe447e4dbd449320eab53f0a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 12 Sep 2019 13:29:14 -0700 Subject: [PATCH 14/25] Fix refactoring misses --- src/services/utilities.ts | 2 +- tests/cases/fourslash/codeFixInferFromFunctionUsage.ts | 2 +- tests/cases/fourslash/formatAddSemicolons1.ts | 2 +- tests/cases/fourslash/formatRemoveSemicolons1.ts | 2 +- tests/cases/fourslash/formatRemoveSemicolons2.ts | 2 +- tests/cases/fourslash/formatRemoveSemicolons3.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4a2389a53b7d2..a7eaa8d143745 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2064,7 +2064,7 @@ namespace ts { } const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; - const endLine = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line; + const endLine = sourceFile.getLineAndCharacterOfPosition(nextToken.getStart(sourceFile)).line; return startLine !== endLine; } diff --git a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts index 27a039878ba36..99bbb35d0b7b9 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts @@ -2,7 +2,7 @@ // @noImplicitAny: true ////function wrap( [| arr |] ) { -//// arr.sort(function (a: number, b: number) { return a < b ? -1 : 1 }) +//// arr.sort(function (a: number, b: number) { return a < b ? -1 : 1 }); //// } // https://github.com/Microsoft/TypeScript/issues/29330 diff --git a/tests/cases/fourslash/formatAddSemicolons1.ts b/tests/cases/fourslash/formatAddSemicolons1.ts index 409524dfc6074..a24c86732549d 100644 --- a/tests/cases/fourslash/formatAddSemicolons1.ts +++ b/tests/cases/fourslash/formatAddSemicolons1.ts @@ -23,7 +23,7 @@ ////declare module 'bar' ////type T = { x: string, y: number } -format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: true }); +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Insert }); format.document(); verify.currentFileContentIs(`console.log(1); console.log(2); diff --git a/tests/cases/fourslash/formatRemoveSemicolons1.ts b/tests/cases/fourslash/formatRemoveSemicolons1.ts index fe87970baa4aa..b80ecee7edb2d 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons1.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons1.ts @@ -37,7 +37,7 @@ //// (); ////} -format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove }); format.document(); verify.currentFileContentIs(`; (function f() { })() const a = 3; diff --git a/tests/cases/fourslash/formatRemoveSemicolons2.ts b/tests/cases/fourslash/formatRemoveSemicolons2.ts index 742a28a88d8af..268a217e03231 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons2.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons2.ts @@ -12,7 +12,7 @@ ////} ////let z = 0; // -format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove }); format.document(); verify.currentFileContentIs(`namespace ts { let x = 0 diff --git a/tests/cases/fourslash/formatRemoveSemicolons3.ts b/tests/cases/fourslash/formatRemoveSemicolons3.ts index 9b2266214e1f0..0987e088d5dda 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons3.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons3.ts @@ -4,7 +4,7 @@ ////// Start with signatures at empty array in case of recursive types ////(type).declaredCallSignatures = emptyArray; -format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); +format.setFormatOptions({ ...format.copyFormatOptions(), semicolons: ts.SemicolonPreference.Remove }); format.document(); verify.currentFileContentIs(`(type).declaredProperties = getNamedMembers(members); // Start with signatures at empty array in case of recursive types From 5743d761e7c9036852002e95288501ddd8e58433 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 12 Sep 2019 13:52:03 -0700 Subject: [PATCH 15/25] Un-update submodules gahhhh --- .../cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter | 2 +- tests/cases/user/axios-src/axios-src | 2 +- tests/cases/user/create-react-app/create-react-app | 2 +- tests/cases/user/prettier/prettier | 2 +- tests/cases/user/puppeteer/puppeteer | 2 +- tests/cases/user/webpack/webpack | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter index 40bdb4eadabc9..ca14e7ccbca8a 160000 --- a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter +++ b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter @@ -1 +1 @@ -Subproject commit 40bdb4eadabc9fbed7d83e3f26817a931c0763b6 +Subproject commit ca14e7ccbca8a821d3b860ebb6f3c81c68ccbd06 diff --git a/tests/cases/user/axios-src/axios-src b/tests/cases/user/axios-src/axios-src index 283d7b306ce23..6a4a85c57fcab 160000 --- a/tests/cases/user/axios-src/axios-src +++ b/tests/cases/user/axios-src/axios-src @@ -1 +1 @@ -Subproject commit 283d7b306ce231f092d28e01713905e5c1600d14 +Subproject commit 6a4a85c57fcaba912eee61b87ba34d07323bc60c diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app index 1a61db58d434d..c9b95047b979b 160000 --- a/tests/cases/user/create-react-app/create-react-app +++ b/tests/cases/user/create-react-app/create-react-app @@ -1 +1 @@ -Subproject commit 1a61db58d434d33603f20e73ca643ec83c561b73 +Subproject commit c9b95047b979bebe89ee70590b2a32ab67ef68af diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 1e471a007968b..9f5bd298db26f 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 1e471a007968b7490563b91ed6909ae6046f3fe8 +Subproject commit 9f5bd298db26f76e95ad17f8406edd7ebefacc5a diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer index cba0f98a2ac7e..c2651c2b5cc88 160000 --- a/tests/cases/user/puppeteer/puppeteer +++ b/tests/cases/user/puppeteer/puppeteer @@ -1 +1 @@ -Subproject commit cba0f98a2ac7edd3c2bffd0ac53185877403da6b +Subproject commit c2651c2b5cc888ebd0ca6be87063d9f98b9eb59c diff --git a/tests/cases/user/webpack/webpack b/tests/cases/user/webpack/webpack index dc2668873162f..77cd3d0cff39d 160000 --- a/tests/cases/user/webpack/webpack +++ b/tests/cases/user/webpack/webpack @@ -1 +1 @@ -Subproject commit dc2668873162ff3369eb8787c1b79f909650e040 +Subproject commit 77cd3d0cff39def2f8bd6355dcd270dd80bc5da2 From d36155fa3f54ef90603b63a8183a7be4e4de7aad Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 12 Sep 2019 16:12:08 -0700 Subject: [PATCH 16/25] Update APIs --- tests/baselines/reference/api/tsserverlibrary.d.ts | 14 ++++++++++++-- tests/baselines/reference/api/typescript.d.ts | 8 ++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 720adbe450892..44ada94fe67a7 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3098,7 +3098,6 @@ declare namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; - readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; @@ -5254,6 +5253,11 @@ declare namespace ts { Block = 1, Smart = 2 } + enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove" + } interface EditorOptions { BaseIndentSize?: number; IndentSize: number; @@ -5306,7 +5310,7 @@ declare namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; - readonly insertTrailingSemicolon?: boolean; + readonly semicolons?: SemicolonPreference; } function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings; interface DefinitionInfo extends DocumentSpan { @@ -8154,6 +8158,11 @@ declare namespace ts.server.protocol { Block = "Block", Smart = "Smart" } + enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove" + } interface EditorSettings { baseIndentSize?: number; indentSize?: number; @@ -8179,6 +8188,7 @@ declare namespace ts.server.protocol { placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; insertSpaceBeforeTypeAnnotation?: boolean; + semicolons?: SemicolonPreference; } interface UserPreferences { readonly disableSuggestions?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 0e22d907fb1d6..e427ac2b8c747 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3098,7 +3098,6 @@ declare namespace ts { export interface UserPreferences { readonly disableSuggestions?: boolean; readonly quotePreference?: "auto" | "double" | "single"; - readonly semicolonPreference?: "auto" | "semicolons" | "no semicolons"; readonly includeCompletionsForModuleExports?: boolean; readonly includeCompletionsWithInsertText?: boolean; readonly importModuleSpecifierPreference?: "relative" | "non-relative"; @@ -5254,6 +5253,11 @@ declare namespace ts { Block = 1, Smart = 2 } + enum SemicolonPreference { + Ignore = "ignore", + Insert = "insert", + Remove = "remove" + } interface EditorOptions { BaseIndentSize?: number; IndentSize: number; @@ -5306,7 +5310,7 @@ declare namespace ts { readonly placeOpenBraceOnNewLineForControlBlocks?: boolean; readonly insertSpaceBeforeTypeAnnotation?: boolean; readonly indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean; - readonly insertTrailingSemicolon?: boolean; + readonly semicolons?: SemicolonPreference; } function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings; interface DefinitionInfo extends DocumentSpan { From 047b55fe8e762e490aac71e6f126dd20621bef43 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 13 Sep 2019 08:48:46 -0700 Subject: [PATCH 17/25] Update for ESLint --- .vscode/settings.json | 4 +++- src/compiler/core.ts | 2 +- src/services/types.ts | 6 ++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f481751900d2..c9667538bb0b3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,7 @@ "rulePaths": ["../scripts/eslint/built/rules/"], "ext": [".ts"] }, - "eslint.workingDirectories": ["./src", "./scripts"] + "eslint.workingDirectories": ["./src", "./scripts"], + "typescript.tsserver.log": "normal", + "typescript.tsdk": "built/local" } \ No newline at end of file diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e7668ff140eac..e423e3889e473 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -332,7 +332,7 @@ namespace ts { /** * Like `forEach`, but iterates in reverse order. */ - export function forEachRight(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + export function forEachRight(array: readonly T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array) { for (let i = array.length - 1; i >= 0; i--) { const result = callback(array[i], i); diff --git a/src/services/types.ts b/src/services/types.ts index 22571b3711f00..75e8b2680ef75 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -18,12 +18,10 @@ namespace ts { getText(sourceFile?: SourceFile): string; getFirstToken(sourceFile?: SourceFile): Node | undefined; /* @internal */ - // tslint:disable-next-line unified-signatures - getFirstToken(sourceFile?: SourceFileLike): Node | undefined; + getFirstToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures getLastToken(sourceFile?: SourceFile): Node | undefined; /* @internal */ - // tslint:disable-next-line unified-signatures - getLastToken(sourceFile?: SourceFileLike): Node | undefined; + getLastToken(sourceFile?: SourceFileLike): Node | undefined; // eslint-disable-line @typescript-eslint/unified-signatures // See ts.forEachChild for documentation. forEachChild(cbNode: (node: Node) => T | undefined, cbNodeArray?: (nodes: NodeArray) => T | undefined): T | undefined; } From 9bd7d54ba93ead64943bdf6cdddd6f847c3d3155 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 13 Sep 2019 10:42:22 -0700 Subject: [PATCH 18/25] Revert accidental test change --- .vscode/settings.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c9667538bb0b3..4f481751900d2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,5 @@ "rulePaths": ["../scripts/eslint/built/rules/"], "ext": [".ts"] }, - "eslint.workingDirectories": ["./src", "./scripts"], - "typescript.tsserver.log": "normal", - "typescript.tsdk": "built/local" + "eslint.workingDirectories": ["./src", "./scripts"] } \ No newline at end of file From 2c1eef235ad853d8a69b476fa10a1bbcb8215e50 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 13 Sep 2019 12:14:33 -0700 Subject: [PATCH 19/25] De-kludge deduplication of EOF processing --- src/services/formatting/formatting.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 0ade89420caff..7cb4eb7948abe 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -652,7 +652,7 @@ namespace ts.formatting { consumeTokenAndAdvanceScanner(tokenInfo, node, nodeDynamicIndentation, node); } - if (formattingScanner.isOnEOF()) { + if (!node.parent && formattingScanner.isOnEOF()) { const token = formattingScanner.readEOFTokenRange(); if (token.end <= node.end && previousRange) { processPair( @@ -1148,11 +1148,7 @@ namespace ts.formatting { function recordDelete(start: number, len: number) { if (len) { - const newEdit = createTextChangeFromStartLength(start, len, ""); - const lastEdit = lastOrUndefined(edits); - if (!lastEdit || !textSpansEqual(lastEdit.span, newEdit.span) || lastEdit.newText !== newEdit.newText) { - edits.push(newEdit); - } + edits.push(createTextChangeFromStartLength(start, len, "")); } } @@ -1164,11 +1160,7 @@ namespace ts.formatting { function recordInsert(start: number, text: string) { if (text) { - const newEdit = createTextChangeFromStartLength(start, 0, text); - const lastEdit = lastOrUndefined(edits); - if (!lastEdit || !textSpansEqual(lastEdit.span, newEdit.span) || lastEdit.newText !== newEdit.newText) { - edits.push(newEdit); - } + edits.push(createTextChangeFromStartLength(start, 0, text)); } } From 5625cb023743215d4721bf07b2d41f831399977d Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 24 Sep 2019 17:31:12 -0700 Subject: [PATCH 20/25] Omit last element semicolon from single-line object-like types --- src/server/protocol.ts | 2 + src/services/formatting/rules.ts | 11 +++- src/services/textChanges.ts | 4 +- src/services/types.ts | 2 + src/services/utilities.ts | 54 +++++++++++++++---- .../reference/api/tsserverlibrary.d.ts | 4 +- tests/baselines/reference/api/typescript.d.ts | 2 +- ...on_catchBlockUniqueParamsBindingPattern.ts | 2 +- ...tFunction_VariableDeclaration_Multiple1.ts | 2 +- ...tFunction_VariableDeclaration_Multiple3.ts | 2 +- ...ction_VariableDeclaration_Writes_Mixed3.ts | 2 +- ...riableDeclaration_Writes_UnionUndefined.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc22.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc3.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc4.ts | 2 +- ...FixClassImplementInterfaceObjectLiteral.ts | 2 +- ...assImplementInterfacePropertySignatures.ts | 12 ++--- ...ImplementInterfaceSomePropertiesPresent.ts | 2 +- ...mentInterfaceTypeParamInstantiateDeeply.ts | 2 +- .../codeFixInferFromCallInAssignment.ts | 2 +- .../codeFixInferFromExpressionStatement.ts | 2 +- ...ixInferFromFunctionThisUsageImplicitAny.ts | 2 +- ...odeFixInferFromFunctionThisUsageLiteral.ts | 2 +- .../codeFixInferFromFunctionUsage.ts | 2 +- .../codeFixInferFromUsageEmptyTypePriority.ts | 2 +- .../codeFixInferFromUsageJSXElement.ts | 2 +- ...codeFixInferFromUsageMultipleParameters.ts | 2 +- .../codeFixInferFromUsagePropertyAccess.ts | 2 +- ...deFixInferFromUsageStringIndexSignature.ts | 2 +- ...codeFixInferFromUsageUnifyAnonymousType.ts | 2 +- ...odeFixUndeclaredMethodObjectLiteralArgs.ts | 12 ++--- .../codeFixUndeclaredPropertyObjectLiteral.ts | 2 +- ...edPropertyObjectLiteralStrictNullChecks.ts | 2 +- .../extract-const-callback-function-this1.ts | 2 +- .../extract-const-callback-function-this2.ts | 2 +- tests/cases/fourslash/extract-method18.ts | 2 +- tests/cases/fourslash/extract-method2.ts | 2 +- tests/cases/fourslash/formatAddSemicolons1.ts | 2 +- ...sToDestructuredObject_allParamsOptional.ts | 2 +- ...aramsToDestructuredObject_arrowFunction.ts | 2 +- ...ParamsToDestructuredObject_callComments.ts | 2 +- ...aramsToDestructuredObject_callComments2.ts | 2 +- ...tParamsToDestructuredObject_chainedCall.ts | 2 +- ...cturedObject_classDeclarationGoodUsages.ts | 2 +- ...amsToDestructuredObject_classExpression.ts | 2 +- ...ucturedObject_classExpressionGoodUsages.ts | 2 +- ...oDestructuredObject_classTypeParameters.ts | 2 +- ...tParamsToDestructuredObject_constructor.ts | 2 +- ...vertParamsToDestructuredObject_function.ts | 2 +- ...msToDestructuredObject_functionComments.ts | 2 +- ...sToDestructuredObject_functionComments1.ts | 2 +- ...ToDestructuredObject_functionExpression.ts | 2 +- ...structuredObject_functionTypeParameters.ts | 2 +- ...msToDestructuredObject_importedFunction.ts | 2 +- ...sToDestructuredObject_importedFunction2.ts | 2 +- ...sToDestructuredObject_importedFunction3.ts | 2 +- ...sToDestructuredObject_importedFunction4.ts | 2 +- ...sToDestructuredObject_importedFunction5.ts | 2 +- ...sToDestructuredObject_importedFunction6.ts | 2 +- ...DestructuredObject_inheritedConstructor.ts | 2 +- ...amsToDestructuredObject_inheritedMethod.ts | 2 +- ...tParamsToDestructuredObject_initializer.ts | 2 +- ...DestructuredObject_initializerInference.ts | 2 +- ...onvertParamsToDestructuredObject_method.ts | 2 +- ...tParamsToDestructuredObject_methodCalls.ts | 2 +- ...aramsToDestructuredObject_namelessClass.ts | 2 +- ...sToDestructuredObject_recursiveFunction.ts | 2 +- ...ToDestructuredObject_restParamInference.ts | 2 +- ...sToDestructuredObject_shorthandProperty.ts | 2 +- ...ParamsToDestructuredObject_staticMethod.ts | 2 +- ...ertParamsToDestructuredObject_superCall.ts | 2 +- ...amsToDestructuredObject_templateLiteral.ts | 2 +- ...ertParamsToDestructuredObject_thisParam.ts | 2 +- ...ramsToDestructuredObject_tupleRestParam.ts | 8 +-- ...ramsToDestructuredObject_typedRestParam.ts | 2 +- .../fourslash/typeToStringCrashInCodeFix.ts | 2 +- 76 files changed, 144 insertions(+), 99 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 0ec27a3a2bb38..745284524e772 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2960,6 +2960,8 @@ namespace ts.server.protocol { Ignore = "ignore", Insert = "insert", Remove = "remove", + /*@internal*/ + RemoveUnconventional = "remove-unconventional" } export interface EditorSettings { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 7fa2845edc030..2fe396c267787 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -314,6 +314,7 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), + rule("NoUnconventionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.RemoveUnconventional), isUnconventionalSemicolonDeletionContext], RuleAction.DeleteToken), rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; @@ -790,6 +791,14 @@ namespace ts.formatting { return context.contextNode.kind === SyntaxKind.NonNullExpression; } + function isUnconventionalSemicolonDeletionContext(context: FormattingContext): boolean { + return nodeAllowsUnconventionalTrailingSemicolon( + context.currentTokenParent, + context.contextNode, + context.nextTokenSpan.kind, + context.sourceFile); + } + function isSemicolonDeletionContext(context: FormattingContext): boolean { let nextTokenKind = context.nextTokenSpan.kind; let nextTokenStart = context.nextTokenSpan.pos; @@ -859,7 +868,7 @@ namespace ts.formatting { if (ancestor.end !== context.currentTokenSpan.end) { return "quit"; } - return syntaxMayBeASICandidate(ancestor.kind); + return nodeMayBeASICandidate(ancestor); }); return !!contextAncestor && isASICandidate(contextAncestor, context.sourceFile); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index dd690c8824eff..a47ef6684c8af 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -836,12 +836,12 @@ namespace ts.textChanges { return { ...context.options, // If the user has no semicolon preference defined and the file doesn’t use semicolons, - // make the formatter remove them. Otherwise, ignore semicolons in the formatter because + // make the formatter remove them. Otherwise, only remove unconventional semicolons because // the writer will insert them by default. semicolons: context.options.semicolons === SemicolonPreference.Remove || (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Remove - : SemicolonPreference.Ignore, + : SemicolonPreference.RemoveUnconventional, }; } diff --git a/src/services/types.ts b/src/services/types.ts index 75e8b2680ef75..702132d417588 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -684,6 +684,8 @@ namespace ts { Ignore = "ignore", Insert = "insert", Remove = "remove", + /*@internal*/ + RemoveUnconventional = "remove-unconventional" } /* @deprecated - consider using EditorSettings instead */ diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 5f59cef602bb3..e657634c0f5a9 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2027,13 +2027,37 @@ namespace ts { || kind === SyntaxKind.ExportAssignment; } - export const syntaxMayBeASICandidate = or( - syntaxRequiresTrailingCommaOrSemicolonOrASI, - syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI, - syntaxRequiresTrailingModuleBlockOrSemicolonOrASI, - syntaxRequiresTrailingSemicolonOrASI); + function isMappedTypeNodeType(node: Node) { + return node.parent && isMappedTypeNode(node.parent) && node.parent.type === node; + } + + export function nodeMayBeASICandidate(node: Node) { + if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind) || + syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind) || + syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind) || + syntaxRequiresTrailingSemicolonOrASI(node.kind)) { + return true; + } + return isMappedTypeNodeType(node); + } - export function isASICandidate(node: Node, sourceFile: SourceFileLike): boolean { + export function nodeAllowsUnconventionalTrailingSemicolon(node: Node, contextNode: Node, nextTokenKind: SyntaxKind | undefined, sourceFile: SourceFileLike) { + if (isMappedTypeNodeType(node)) { + return rangeIsOnSingleLine(contextNode, sourceFile as SourceFile); + } + if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) { + return nextTokenKind === SyntaxKind.CloseBraceToken && rangeIsOnSingleLine(contextNode, sourceFile as SourceFile); + } + return false; + } + + /** + * @param strict Return true for positions that allow semicolons but conventionally + * drop them, even in code that largely contains semicolons. Examples include the last + * declaration inside the curly braces of single-line object type literals and mapped types, + * e.g. `type X = { x: string; }` and `type X = { [K in keyof T]: T[K]; }`. + */ + export function isASICandidate(node: Node, sourceFile: SourceFileLike, strict?: boolean): boolean { const lastToken = node.getLastToken(sourceFile); if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) { return false; @@ -2060,20 +2084,28 @@ namespace ts { return false; } + let nextToken = getNextToken(); + const contextNode = findAncestor(node, or(isObjectTypeDeclaration, isMappedTypeNode)); + if (contextNode && nodeAllowsUnconventionalTrailingSemicolon(node, contextNode, nextToken && nextToken.kind, sourceFile)) { + return !!strict; + } + // See comment in parser’s `parseDoStatement` if (node.kind === SyntaxKind.DoStatement) { return true; } - const topNode = findAncestor(node, ancestor => !ancestor.parent)!; - const nextToken = findNextToken(node, topNode, sourceFile); if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) { return true; } - const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; - const endLine = sourceFile.getLineAndCharacterOfPosition(nextToken.getStart(sourceFile)).line; - return startLine !== endLine; + return !positionsAreOnSameLine(node.getEnd(), + nextToken.getStart(sourceFile), + sourceFile as SourceFile); + + function getNextToken(): Node | undefined { + return nextToken || (nextToken = findNextToken(node, findAncestor(node, ancestor => !ancestor.parent)!, sourceFile)); + } } export function probablyUsesSemicolons(sourceFile: SourceFile): boolean { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ca07eebc536f4..98273495b755c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5297,7 +5297,7 @@ declare namespace ts { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove" + Remove = "remove", } interface EditorOptions { BaseIndentSize?: number; @@ -8202,7 +8202,7 @@ declare namespace ts.server.protocol { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove" + Remove = "remove", } interface EditorSettings { baseIndentSize?: number; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index d24f1e9c89cc8..1134469dc33a4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5297,7 +5297,7 @@ declare namespace ts { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove" + Remove = "remove", } interface EditorOptions { BaseIndentSize?: number; diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts index ff7ac4bc944b6..5766e096dda31 100644 --- a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts @@ -7,7 +7,7 @@ function /*[#|*/f/*|]*/() { // ==ASYNC FUNCTION::Convert to async function== async function f() { - let result: { x: number; } | { x: string; }; + let result: { x: number } | { x: string }; try { await Promise.resolve(); result = ({ x: 3 }); diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts index 07ec452e36087..7194456b85039 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts @@ -5,7 +5,7 @@ x; y; // ==SCOPE::Extract to function in global scope== -const { x, y }: { x: number; y: string; } = /*RENAME*/newFunction(); +const { x, y }: { x: number; y: string } = /*RENAME*/newFunction(); x; y; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts index b8ae2b7288326..06da60f571baa 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts @@ -6,7 +6,7 @@ x; y; z; // ==SCOPE::Extract to function in global scope== -var { x, y, z }: { x: number; y: string; z: number; } = /*RENAME*/newFunction(); +var { x, y, z }: { x: number; y: string; z: number } = /*RENAME*/newFunction(); x; y; z; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts index b2c53e3f1602c..f4bfb66dea899 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts @@ -11,7 +11,7 @@ function f() { function f() { let a = 1; - let { x, y }: { x: number; y: number; } = /*RENAME*/newFunction(); + let { x, y }: { x: number; y: number } = /*RENAME*/newFunction(); a; x; y; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts index b76bf6c499961..7f216b3365bb0 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts @@ -12,7 +12,7 @@ function f() { function f() { let a = 1; - let { x, y, z }: { x: number; y: number; z: number; } = /*RENAME*/newFunction(); + let { x, y, z }: { x: number; y: number; z: number } = /*RENAME*/newFunction(); a; x; y; z; function newFunction() { diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts index 98ccf84c3d677..a8f6fa7a1ad1a 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts @@ -14,7 +14,7 @@ verify.codeFix({ ` /** @param {Object} sb * @param {Object} ns */ -function f(sb: { [s: string]: boolean; }, ns: { [n: number]: string; }) { +function f(sb: { [s: string]: boolean }, ns: { [n: number]: string }) { sb; ns; }`, }); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts index f7d0522018abd..a4234127c46b0 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts @@ -30,7 +30,7 @@ verify.codeFix({ * @param alpha - the other best parameter * @param {*} beta - I have no idea how this got here */ -function f(x: number, y: { a: string; b: Date; }, z: string, alpha, beta: any) { +function f(x: number, y: { a: string; b: Date }, z: string, alpha, beta: any) { x; y; z; alpha; beta; }`, }); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts index 4421afbd86777..e432e8f0b20dd 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts @@ -26,7 +26,7 @@ verify.codeFix({ * @param {number?} gamma * @param {number!} delta */ -function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { a: string; }, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) { +function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { a: string }, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) { x; y; z; alpha; beta; gamma; delta; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts index f2e05b24f5769..dba46a5005001 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts @@ -18,6 +18,6 @@ verify.codeFix({ } } class Person implements IPerson { - coordinate: { x: number; y: number; }; + coordinate: { x: number; y: number }; }`, }); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts index 52d69b466fd3b..778aca58dc105 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts @@ -43,13 +43,13 @@ class C implements I { a0: {}; a1: (b1: number, c1: string) => number; a2: (b2: number, c2: string) => number; - a3: { (b3: number, c3: string): number; x: number; }; + a3: { (b3: number, c3: string): number; x: number }; a4: new (b1: number, c1: string) => number; a5: new (b2: number, c2: string) => number; - a6: { new(b3: number, c3: string): number; x: number; }; - a7: { foo(b7: number, c7: string): number; }; - a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number; }; - a9: { (b9: number, c9: string): number;[d9: number]: I; }; - a10: { (b10: number, c10: string): number;[d10: string]: I; }; + a6: { new(b3: number, c3: string): number; x: number }; + a7: { foo(b7: number, c7: string): number }; + a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number }; + a9: { (b9: number, c9: string): number;[d9: number]: I }; + a10: { (b10: number, c10: string): number;[d10: string]: I }; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts index e667783b99580..f43a02886d519 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts @@ -12,5 +12,5 @@ //// } verify.rangeAfterCodeFix(` -z: number & { __iBrand: any; }; +z: number & { __iBrand: any }; `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts index cf8926ff33ecb..6b396a23cdd6c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -12,6 +12,6 @@ verify.codeFix({ x: { y: T, z: T[] }; } class C implements I { - x: { y: number; z: number[]; }; + x: { y: number; z: number[] }; }`, }); diff --git a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts index 85e1d3bc50952..5eb564b830573 100644 --- a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts +++ b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts @@ -6,4 +6,4 @@ //// return result //// } -verify.rangeAfterCodeFix("app: { use: (arg0: string) => any; }"); +verify.rangeAfterCodeFix("app: { use: (arg0: string) => any }"); diff --git a/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts b/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts index b5969c79378d8..282df322de06c 100644 --- a/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts +++ b/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts @@ -5,4 +5,4 @@ //// app.use('hi') //// } -verify.rangeAfterCodeFix("app: { use: (arg0: string) => void; }"); +verify.rangeAfterCodeFix("app: { use: (arg0: string) => void }"); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts index 3f71ae9ddd2d5..f7a0191b8f79a 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts @@ -21,5 +21,5 @@ verify.codeFix({ description: "Infer 'this' type of 'returnThisMember' from usage", index: 0, - newRangeContent: "this: { member: string; returnThisMember: () => any; } ", + newRangeContent: "this: { member: string; returnThisMember: () => any } ", }); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts index f626e4cba2d2b..ccee3d37fc6dc 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts @@ -13,5 +13,5 @@ verify.codeFix({ description: "Infer 'this' type of 'returnThisMember' from usage", index: 0, - newRangeContent: "this: { member: string; returnThisMember: () => any; } ", + newRangeContent: "this: { member: string; returnThisMember: () => any } ", }); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts index 31068469b4db9..dc059ef19459a 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts @@ -6,4 +6,4 @@ //// } // https://github.com/Microsoft/TypeScript/issues/29330 -verify.rangeAfterCodeFix("arr: { other: (arg0: (a: number, b: number) => 1 | -1) => void; }"); +verify.rangeAfterCodeFix("arr: { other: (arg0: (a: number, b: number) => 1 | -1) => void }"); diff --git a/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts b/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts index e9288303781b5..c655138cf8cf8 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts @@ -10,4 +10,4 @@ //// var beforeExpr = !!conf.beforeExpr; ////}; -verify.rangeAfterCodeFix("label: any, conf: { keyword?: any; beforeExpr?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("label: any, conf: { keyword?: any; beforeExpr?: any } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts b/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts index abe35fe46c8df..86c2d747b24d1 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts @@ -30,4 +30,4 @@ //// } -verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts b/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts index 89e2c935e876f..d17a250cdf773 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts @@ -6,4 +6,4 @@ //// f(1, "string", { a: 1 }, {shouldNotBeHere: 2}, {shouldNotBeHere: 2}, 3, "string"); -verify.rangeAfterCodeFix("a: number, b: string, c: { a: number; }, d: number, e = 0, ...d: (string | number)[]", /*includeWhiteSpace*/ false, /*errorCode*/ undefined, /*index*/ 1); +verify.rangeAfterCodeFix("a: number, b: string, c: { a: number }, d: number, e = 0, ...d: (string | number)[]", /*includeWhiteSpace*/ false, /*errorCode*/ undefined, /*index*/ 1); diff --git a/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts b/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts index 44d2e2d7b1c25..94270415f5478 100644 --- a/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts +++ b/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts @@ -12,4 +12,4 @@ //// return x.y.z ////} -verify.rangeAfterCodeFix("a: { b: { c: void; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); +verify.rangeAfterCodeFix("a: { b: { c: void } }, m: { n: () => number }, x: { y: { z: number[] } }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts b/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts index 765f788e054bb..9213df8222a1d 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts @@ -5,4 +5,4 @@ //// return a['hi']; ////} -verify.rangeAfterCodeFix("a: { [x: string]: any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("a: { [x: string]: any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts b/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts index 76e25ab0fe714..a4919e4bc172d 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts @@ -16,4 +16,4 @@ ////kw("6", { beforeExpr: true, prefix: true, startsExpr: true }) -verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts b/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts index 5c0698ad653e9..06b28552dc0aa 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts @@ -12,7 +12,7 @@ verify.codeFix({ description: "Declare method 'foo1'", index: 0, newRangeContent: ` - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { throw new Error("Method not implemented."); } `, @@ -23,10 +23,10 @@ verify.codeFix({ description: "Declare method 'foo2'", index: 0, newRangeContent: ` - foo2(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { + foo2(arg0: null, arg1: {}, arg2: { a: number; b: string }) { throw new Error("Method not implemented."); } - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { throw new Error("Method not implemented."); } `, @@ -37,13 +37,13 @@ verify.codeFix({ description: "Declare method 'foo3'", index: 0, newRangeContent: ` - foo3(arg0: null, arg1: {}, arg2: { a: number; b: string; }): number { + foo3(arg0: null, arg1: {}, arg2: { a: number; b: string }): number { throw new Error("Method not implemented."); } - foo2(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { + foo2(arg0: null, arg1: {}, arg2: { a: number; b: string }) { throw new Error("Method not implemented."); } - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { throw new Error("Method not implemented."); } ` diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts index a2647c5a21e33..d60f62da777ee 100644 --- a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts @@ -9,7 +9,7 @@ verify.rangeAfterCodeFix(` class A { - x: { a: number; b: string; c: any; d: any; e: any; }; + x: { a: number; b: string; c: any; d: any; e: any }; constructor() { let e: any = 10; diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts index 0b0ab8cdd59a2..4c824e9e63c44 100644 --- a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts @@ -11,7 +11,7 @@ verify.rangeAfterCodeFix(` class A { - x: { a: number; b: string; c: undefined; d: null; e: any; }; + x: { a: number; b: string; c: undefined; d: null; e: any }; constructor() { let e: any = 10; diff --git a/tests/cases/fourslash/extract-const-callback-function-this1.ts b/tests/cases/fourslash/extract-const-callback-function-this1.ts index b0d269400bbd1..26bc533893269 100644 --- a/tests/cases/fourslash/extract-const-callback-function-this1.ts +++ b/tests/cases/fourslash/extract-const-callback-function-this1.ts @@ -10,6 +10,6 @@ edit.applyRefactor({ actionDescription: "Extract to constant in enclosing scope", newContent: `declare function fWithThis(fn: (this: { a: string }, a: string) => string): void; -const newLocal = function(this: { a: string; }, a: string): string { return this.a; }; +const newLocal = function(this: { a: string }, a: string): string { return this.a; }; fWithThis(/*RENAME*/newLocal);` }); diff --git a/tests/cases/fourslash/extract-const-callback-function-this2.ts b/tests/cases/fourslash/extract-const-callback-function-this2.ts index 518c39e26e546..42fbd4da7ea0d 100644 --- a/tests/cases/fourslash/extract-const-callback-function-this2.ts +++ b/tests/cases/fourslash/extract-const-callback-function-this2.ts @@ -10,6 +10,6 @@ edit.applyRefactor({ actionDescription: "Extract to constant in enclosing scope", newContent: `declare function fWithThis(fn: (this: { a: string }, a: string) => string): void; -const newLocal = function(this: { a: string; }, a: string): string { return this.a; }; +const newLocal = function(this: { a: string }, a: string): string { return this.a; }; fWithThis(/*RENAME*/newLocal);` }); diff --git a/tests/cases/fourslash/extract-method18.ts b/tests/cases/fourslash/extract-method18.ts index 0a488a04106c9..70a9a3230a8bf 100644 --- a/tests/cases/fourslash/extract-method18.ts +++ b/tests/cases/fourslash/extract-method18.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ /*RENAME*/newFunction(x); } -function newFunction(x: { m: number; }) { +function newFunction(x: { m: number }) { x.m = 3; } ` diff --git a/tests/cases/fourslash/extract-method2.ts b/tests/cases/fourslash/extract-method2.ts index 419d63790756a..8d562324d267f 100644 --- a/tests/cases/fourslash/extract-method2.ts +++ b/tests/cases/fourslash/extract-method2.ts @@ -25,7 +25,7 @@ edit.applyRefactor({ } } -function newFunction(m: number, j: string, k: { x: string; }) { +function newFunction(m: number, j: string, k: { x: string }) { return m + j + k; } ` diff --git a/tests/cases/fourslash/formatAddSemicolons1.ts b/tests/cases/fourslash/formatAddSemicolons1.ts index a24c86732549d..0b0dfa4301196 100644 --- a/tests/cases/fourslash/formatAddSemicolons1.ts +++ b/tests/cases/fourslash/formatAddSemicolons1.ts @@ -46,4 +46,4 @@ enum E { type M = { [K in keyof T]: any }; declare module 'foo' { } declare module 'bar'; -type T = { x: string, y: number; };`); +type T = { x: string, y: number };`); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts index 8d8db71960859..f08498f515a51 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = "1" }: { a?: number; b?: string; } = {}): string { + newContent: `function f({ a, b = "1" }: { a?: number; b?: string } = {}): string { return b; } f();` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts index 97d337d8d1aa3..e5ffd37ea0354 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const foo = ({ a, b }: { a: number; b: number; }) => { }; + newContent: `const foo = ({ a, b }: { a: number; b: number }) => { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts index 2e30f96cd5968..c58bcd5804b93 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[] }) { return a + b; } foo({ /**a*/ a: 1 /**b*/, /**c*/ b: 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts index fcb50b6b8e189..4c9b6a253ae9d 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[] }) { return a + b; } foo( diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts index 0b033200666d0..440beec20c145 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: number; b: number; }) { + newContent: `function foo({ a, b }: { a: number; b: number }) { return { bar: () => a + b }; } var x = foo({ a: 1, b: 2 }).bar();` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts index beba49c17e3ed..510a96ae0af87 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ actionDescription: "Convert parameters to destructured object", newContent: `class C { static a: number = 2; - constructor({ a, b }: { a: number; b: number; }) { } + constructor({ a, b }: { a: number; b: number }) { } } const newC = new C({ a: 1, b: 2 }); const b = C.a; diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts index f779170d643b8..b654174d385b4 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts @@ -11,7 +11,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `const c = class { - constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) { } + constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number } }) { } } var x = new c({ a: 2 });` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts index ce0054fd92a13..7221bffd91311 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionDescription: "Convert parameters to destructured object", newContent: `const c = class C { static a: number = 2; - constructor({ a, b }: { a: number; b: number; }) { } + constructor({ a, b }: { a: number; b: number }) { } } const a = new c({ a: 0, b: 1 }); const b = c.a; diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts index d8c884356bd81..1b6656cbb54d1 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: T; s: T; }) { + bar({ t, s }: { t: T; s: T }) { return s; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts index 1503f2fe7ebbe..ac8ad18ddfd86 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts @@ -18,7 +18,7 @@ edit.applyRefactor({ newContent: `class Foo { t: string; s: string; - constructor({ t, s }: { t: string; s: string; }) { + constructor({ t, s }: { t: string; s: string }) { this.t = t; this.s = s; } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts index d5eed686068d4..423136a6f1b87 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b }: { a: number; b: string; }): string { + newContent: `function f({ a, b }: { a: number; b: string }): string { return b; } f({ a: 4, b: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts index 5340004a060c3..a53757c997b68 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `foo({ a: 1, b: 2 }); /**a*/ -/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/; }) { +/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/ }) { // m /**n*/ return a + b; // o // p diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts index 07bc755c07677..3a2b725629da0 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */; }) { + newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */ }) { return a + b; }` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts index c4e81cf79262b..75ff05d14207f 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const foo = function({ a, b }: { a: number; b: number; }) { }; + newContent: `const foo = function({ a, b }: { a: number; b: number }) { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts index 9a27ae4f09ed2..7b540957de4c5 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ t, s }: { t: T; s: S; }) { + newContent: `function foo({ t, s }: { t: T; s: S }) { return s; } foo({ t: "a", s: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts index a4b37c7405a6b..3d8c97597dff6 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export function f({ a, b }: { a: number; b: string; }): string { + newContent: `export function f({ a, b }: { a: number; b: string }): string { return b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts index a83c11bd1fd10..42465d86f5e27 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export default function f({ a, b }: { a: number; b: string; }): string { + newContent: `export default function f({ a, b }: { a: number; b: string }): string { return b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts index 6a2ecc1cfc373..a72adaa1dafe8 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: string; b: string; }) { } + newContent: `function foo({ a, b }: { a: string; b: string }) { } export = foo;` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts index 729878b2b2233..7739e1df43cc5 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts @@ -17,7 +17,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export { foo as default }; -function foo({ a, b }: { a: number; b: number; }) { +function foo({ a, b }: { a: number; b: number }) { return a + b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts index 17a41880a5547..9a7a1c2f38d4c 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts @@ -17,7 +17,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export class C { - constructor({ a, b }: { a: number; b: number; }) { } + constructor({ a, b }: { a: number; b: number }) { } }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts index 615a49c026767..fc1d86570e134 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export function foo({ a, b }: { a: string; b: string; }) { }` + newContent: `export function foo({ a, b }: { a: string; b: string }) { }` }); goTo.file("a.ts"); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts index 70a5d2022b5d9..a40cdc183bde3 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - constructor({ t, s }: { t: string; s: string; }) { } + constructor({ t, s }: { t: string; s: string }) { } } class Bar extends Foo { } var bar = new Bar({ t: "a", s: "b" }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts index a04560886c087..e61d325290d0a 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string; }): string { + bar({ t, s }: { t: string; s: string }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts index 3f0da9f13a391..9bb6e5f022f8b 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = "1" }: { a: number; b?: string; }): string { + newContent: `function f({ a, b = "1" }: { a: number; b?: string }): string { return b; } f({ a: 4, b: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts index f2b92fc89e718..6fe36e73a3734 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean; }; }; }) { + newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean } } }) { return b; } f({ a: 2 });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts index f3f78de391e45..2272c39020963 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string; }): string { + bar({ t, s }: { t: string; s: string }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts index d1347ad877be2..753c7f8472f87 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string; }): string { + bar({ t, s }: { t: string; s: string }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts index d38bff48b249e..4530fecafec8a 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts @@ -16,7 +16,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export default class { - constructor({ a, b }: { a: string; b: string; }) { } + constructor({ a, b }: { a: string; b: string }) { } }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts index 7655af0510081..755394aa1c5c9 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts @@ -12,7 +12,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const f = function foo({ a, b }: { a: number; b: number; }) { + newContent: `const f = function foo({ a, b }: { a: number; b: number }) { foo({ a: 1, b: 2 }); } function foo(a: number, b: number) { } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts index 1c6350d89f57b..11ebc9cfc0c91 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[]; }) { } + newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[] }) { } let l = log({ a: -1, b: -2, args: [3, 4, 5] }); let k = log({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts index 86ea98582ab9c..7b4096c79d236 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b, rest = [] }: { a: number; b: number; rest?: string[]; }) { } + newContent: `function f({ a, b, rest = [] }: { a: number; b: number; rest?: string[] }) { } const a = 4; const b = 5; f({ a, b }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts index 8c19582c145b9..43cdbf8aa770e 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - static bar({ t, s }: { t: string; s: string; }): string { + static bar({ t, s }: { t: string; s: string }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts index 2bdee83647275..88f8e2cdb1e6c 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class A { - constructor({ a, b }: { a: string; b: string; }) { } + constructor({ a, b }: { a: string; b: string }) { } } class B extends A { constructor(a: string, b: string, c: string) { diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts index 1075fb7dddb96..2cef3d9b08fd0 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: [ - 'function insert({ template, overwriteBefore = 0 }: { template: string; overwriteBefore?: number; }) {}', + 'function insert({ template, overwriteBefore = 0 }: { template: string; overwriteBefore?: number }) {}', 'insert({ template: `this is \\${not} a substitution` });' ].join('\n') }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts index 05a6620707ffd..a01a4cf9cef92 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo(this: void, { t, s }: { t: string; s: string; }) { + newContent: `function foo(this: void, { t, s }: { t: string; s: string }) { return s; } foo({ t: "a", s: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts index 7584308fec004..6fb05f7c02e4d 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts @@ -23,7 +23,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn1({ a, b, args }: { a: number; b: number; args: [number, number]; }) { } + newContent: `function fn1({ a, b, args }: { a: number; b: number; args: [number, number] }) { } fn1({ a: 1, b: 2, args: [3, 4] });` }); @@ -32,7 +32,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn2({ a, b, args }: { a: number; b: number; args: [number, number, ...string[]]; }) { } + newContent: `function fn2({ a, b, args }: { a: number; b: number; args: [number, number, ...string[]] }) { } fn2({ a: 1, b: 2, args: [3, 4] }); fn2({ a: 1, b: 2, args: [3, 4, "a"] });` }); @@ -42,7 +42,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn3({ b, c }: { b: boolean; c: []; }) { } + newContent: `function fn3({ b, c }: { b: boolean; c: [] }) { } fn3({ b: true, c: [] });` }); @@ -51,7 +51,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn4({ a, args = [] }: { a: number; args?: [...string[]]; }) { } + newContent: `function fn4({ a, args = [] }: { a: number; args?: [...string[]] }) { } fn4({ a: 2 }); fn4({ a: 1, args: ["two", "three"] });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts index 88401d83c4bc6..e65a61b10ab76 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[]; }) { } + newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[] }) { } let employeeName = buildName({ firstName: "Joseph", middleName: "Samuel", restOfName: ["Lucas", "MacKinzie"] }); let myName = buildName({ firstName: "Joseph" });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/typeToStringCrashInCodeFix.ts b/tests/cases/fourslash/typeToStringCrashInCodeFix.ts index 5201a6b77fba4..8a9c19817a50f 100644 --- a/tests/cases/fourslash/typeToStringCrashInCodeFix.ts +++ b/tests/cases/fourslash/typeToStringCrashInCodeFix.ts @@ -3,4 +3,4 @@ // @noImplicitAny: true //// function f([|y |], z = { p: y[ -verify.rangeAfterCodeFix("y: { [x: string]: any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("y: { [x: string]: any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); From 525e32e9348a1998954c97ddd71f391f4a37645a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 25 Sep 2019 10:26:01 -0700 Subject: [PATCH 21/25] Revert "Omit last element semicolon from single-line object-like types" This reverts commit 5625cb023743215d4721bf07b2d41f831399977d. --- src/server/protocol.ts | 2 - src/services/formatting/rules.ts | 11 +--- src/services/textChanges.ts | 4 +- src/services/types.ts | 2 - src/services/utilities.ts | 54 ++++--------------- .../reference/api/tsserverlibrary.d.ts | 4 +- tests/baselines/reference/api/typescript.d.ts | 2 +- ...on_catchBlockUniqueParamsBindingPattern.ts | 2 +- ...tFunction_VariableDeclaration_Multiple1.ts | 2 +- ...tFunction_VariableDeclaration_Multiple3.ts | 2 +- ...ction_VariableDeclaration_Writes_Mixed3.ts | 2 +- ...riableDeclaration_Writes_UnionUndefined.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc22.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc3.ts | 2 +- .../fourslash/annotateWithTypeFromJSDoc4.ts | 2 +- ...FixClassImplementInterfaceObjectLiteral.ts | 2 +- ...assImplementInterfacePropertySignatures.ts | 12 ++--- ...ImplementInterfaceSomePropertiesPresent.ts | 2 +- ...mentInterfaceTypeParamInstantiateDeeply.ts | 2 +- .../codeFixInferFromCallInAssignment.ts | 2 +- .../codeFixInferFromExpressionStatement.ts | 2 +- ...ixInferFromFunctionThisUsageImplicitAny.ts | 2 +- ...odeFixInferFromFunctionThisUsageLiteral.ts | 2 +- .../codeFixInferFromFunctionUsage.ts | 2 +- .../codeFixInferFromUsageEmptyTypePriority.ts | 2 +- .../codeFixInferFromUsageJSXElement.ts | 2 +- ...codeFixInferFromUsageMultipleParameters.ts | 2 +- .../codeFixInferFromUsagePropertyAccess.ts | 2 +- ...deFixInferFromUsageStringIndexSignature.ts | 2 +- ...codeFixInferFromUsageUnifyAnonymousType.ts | 2 +- ...odeFixUndeclaredMethodObjectLiteralArgs.ts | 12 ++--- .../codeFixUndeclaredPropertyObjectLiteral.ts | 2 +- ...edPropertyObjectLiteralStrictNullChecks.ts | 2 +- .../extract-const-callback-function-this1.ts | 2 +- .../extract-const-callback-function-this2.ts | 2 +- tests/cases/fourslash/extract-method18.ts | 2 +- tests/cases/fourslash/extract-method2.ts | 2 +- tests/cases/fourslash/formatAddSemicolons1.ts | 2 +- ...sToDestructuredObject_allParamsOptional.ts | 2 +- ...aramsToDestructuredObject_arrowFunction.ts | 2 +- ...ParamsToDestructuredObject_callComments.ts | 2 +- ...aramsToDestructuredObject_callComments2.ts | 2 +- ...tParamsToDestructuredObject_chainedCall.ts | 2 +- ...cturedObject_classDeclarationGoodUsages.ts | 2 +- ...amsToDestructuredObject_classExpression.ts | 2 +- ...ucturedObject_classExpressionGoodUsages.ts | 2 +- ...oDestructuredObject_classTypeParameters.ts | 2 +- ...tParamsToDestructuredObject_constructor.ts | 2 +- ...vertParamsToDestructuredObject_function.ts | 2 +- ...msToDestructuredObject_functionComments.ts | 2 +- ...sToDestructuredObject_functionComments1.ts | 2 +- ...ToDestructuredObject_functionExpression.ts | 2 +- ...structuredObject_functionTypeParameters.ts | 2 +- ...msToDestructuredObject_importedFunction.ts | 2 +- ...sToDestructuredObject_importedFunction2.ts | 2 +- ...sToDestructuredObject_importedFunction3.ts | 2 +- ...sToDestructuredObject_importedFunction4.ts | 2 +- ...sToDestructuredObject_importedFunction5.ts | 2 +- ...sToDestructuredObject_importedFunction6.ts | 2 +- ...DestructuredObject_inheritedConstructor.ts | 2 +- ...amsToDestructuredObject_inheritedMethod.ts | 2 +- ...tParamsToDestructuredObject_initializer.ts | 2 +- ...DestructuredObject_initializerInference.ts | 2 +- ...onvertParamsToDestructuredObject_method.ts | 2 +- ...tParamsToDestructuredObject_methodCalls.ts | 2 +- ...aramsToDestructuredObject_namelessClass.ts | 2 +- ...sToDestructuredObject_recursiveFunction.ts | 2 +- ...ToDestructuredObject_restParamInference.ts | 2 +- ...sToDestructuredObject_shorthandProperty.ts | 2 +- ...ParamsToDestructuredObject_staticMethod.ts | 2 +- ...ertParamsToDestructuredObject_superCall.ts | 2 +- ...amsToDestructuredObject_templateLiteral.ts | 2 +- ...ertParamsToDestructuredObject_thisParam.ts | 2 +- ...ramsToDestructuredObject_tupleRestParam.ts | 8 +-- ...ramsToDestructuredObject_typedRestParam.ts | 2 +- .../fourslash/typeToStringCrashInCodeFix.ts | 2 +- 76 files changed, 99 insertions(+), 144 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 745284524e772..0ec27a3a2bb38 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2960,8 +2960,6 @@ namespace ts.server.protocol { Ignore = "ignore", Insert = "insert", Remove = "remove", - /*@internal*/ - RemoveUnconventional = "remove-unconventional" } export interface EditorSettings { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 2fe396c267787..7fa2845edc030 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -314,7 +314,6 @@ namespace ts.formatting { rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), - rule("NoUnconventionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.RemoveUnconventional), isUnconventionalSemicolonDeletionContext], RuleAction.DeleteToken), rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), ]; @@ -791,14 +790,6 @@ namespace ts.formatting { return context.contextNode.kind === SyntaxKind.NonNullExpression; } - function isUnconventionalSemicolonDeletionContext(context: FormattingContext): boolean { - return nodeAllowsUnconventionalTrailingSemicolon( - context.currentTokenParent, - context.contextNode, - context.nextTokenSpan.kind, - context.sourceFile); - } - function isSemicolonDeletionContext(context: FormattingContext): boolean { let nextTokenKind = context.nextTokenSpan.kind; let nextTokenStart = context.nextTokenSpan.pos; @@ -868,7 +859,7 @@ namespace ts.formatting { if (ancestor.end !== context.currentTokenSpan.end) { return "quit"; } - return nodeMayBeASICandidate(ancestor); + return syntaxMayBeASICandidate(ancestor.kind); }); return !!contextAncestor && isASICandidate(contextAncestor, context.sourceFile); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index a47ef6684c8af..dd690c8824eff 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -836,12 +836,12 @@ namespace ts.textChanges { return { ...context.options, // If the user has no semicolon preference defined and the file doesn’t use semicolons, - // make the formatter remove them. Otherwise, only remove unconventional semicolons because + // make the formatter remove them. Otherwise, ignore semicolons in the formatter because // the writer will insert them by default. semicolons: context.options.semicolons === SemicolonPreference.Remove || (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) ? SemicolonPreference.Remove - : SemicolonPreference.RemoveUnconventional, + : SemicolonPreference.Ignore, }; } diff --git a/src/services/types.ts b/src/services/types.ts index 702132d417588..75e8b2680ef75 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -684,8 +684,6 @@ namespace ts { Ignore = "ignore", Insert = "insert", Remove = "remove", - /*@internal*/ - RemoveUnconventional = "remove-unconventional" } /* @deprecated - consider using EditorSettings instead */ diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e657634c0f5a9..5f59cef602bb3 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2027,37 +2027,13 @@ namespace ts { || kind === SyntaxKind.ExportAssignment; } - function isMappedTypeNodeType(node: Node) { - return node.parent && isMappedTypeNode(node.parent) && node.parent.type === node; - } - - export function nodeMayBeASICandidate(node: Node) { - if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind) || - syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI(node.kind) || - syntaxRequiresTrailingModuleBlockOrSemicolonOrASI(node.kind) || - syntaxRequiresTrailingSemicolonOrASI(node.kind)) { - return true; - } - return isMappedTypeNodeType(node); - } + export const syntaxMayBeASICandidate = or( + syntaxRequiresTrailingCommaOrSemicolonOrASI, + syntaxRequiresTrailingFunctionBlockOrSemicolonOrASI, + syntaxRequiresTrailingModuleBlockOrSemicolonOrASI, + syntaxRequiresTrailingSemicolonOrASI); - export function nodeAllowsUnconventionalTrailingSemicolon(node: Node, contextNode: Node, nextTokenKind: SyntaxKind | undefined, sourceFile: SourceFileLike) { - if (isMappedTypeNodeType(node)) { - return rangeIsOnSingleLine(contextNode, sourceFile as SourceFile); - } - if (syntaxRequiresTrailingCommaOrSemicolonOrASI(node.kind)) { - return nextTokenKind === SyntaxKind.CloseBraceToken && rangeIsOnSingleLine(contextNode, sourceFile as SourceFile); - } - return false; - } - - /** - * @param strict Return true for positions that allow semicolons but conventionally - * drop them, even in code that largely contains semicolons. Examples include the last - * declaration inside the curly braces of single-line object type literals and mapped types, - * e.g. `type X = { x: string; }` and `type X = { [K in keyof T]: T[K]; }`. - */ - export function isASICandidate(node: Node, sourceFile: SourceFileLike, strict?: boolean): boolean { + export function isASICandidate(node: Node, sourceFile: SourceFileLike): boolean { const lastToken = node.getLastToken(sourceFile); if (lastToken && lastToken.kind === SyntaxKind.SemicolonToken) { return false; @@ -2084,28 +2060,20 @@ namespace ts { return false; } - let nextToken = getNextToken(); - const contextNode = findAncestor(node, or(isObjectTypeDeclaration, isMappedTypeNode)); - if (contextNode && nodeAllowsUnconventionalTrailingSemicolon(node, contextNode, nextToken && nextToken.kind, sourceFile)) { - return !!strict; - } - // See comment in parser’s `parseDoStatement` if (node.kind === SyntaxKind.DoStatement) { return true; } + const topNode = findAncestor(node, ancestor => !ancestor.parent)!; + const nextToken = findNextToken(node, topNode, sourceFile); if (!nextToken || nextToken.kind === SyntaxKind.CloseBraceToken) { return true; } - return !positionsAreOnSameLine(node.getEnd(), - nextToken.getStart(sourceFile), - sourceFile as SourceFile); - - function getNextToken(): Node | undefined { - return nextToken || (nextToken = findNextToken(node, findAncestor(node, ancestor => !ancestor.parent)!, sourceFile)); - } + const startLine = sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line; + const endLine = sourceFile.getLineAndCharacterOfPosition(nextToken.getStart(sourceFile)).line; + return startLine !== endLine; } export function probablyUsesSemicolons(sourceFile: SourceFile): boolean { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 98273495b755c..ca07eebc536f4 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5297,7 +5297,7 @@ declare namespace ts { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove", + Remove = "remove" } interface EditorOptions { BaseIndentSize?: number; @@ -8202,7 +8202,7 @@ declare namespace ts.server.protocol { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove", + Remove = "remove" } interface EditorSettings { baseIndentSize?: number; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 1134469dc33a4..d24f1e9c89cc8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5297,7 +5297,7 @@ declare namespace ts { enum SemicolonPreference { Ignore = "ignore", Insert = "insert", - Remove = "remove", + Remove = "remove" } interface EditorOptions { BaseIndentSize?: number; diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts index 5766e096dda31..ff7ac4bc944b6 100644 --- a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts @@ -7,7 +7,7 @@ function /*[#|*/f/*|]*/() { // ==ASYNC FUNCTION::Convert to async function== async function f() { - let result: { x: number } | { x: string }; + let result: { x: number; } | { x: string; }; try { await Promise.resolve(); result = ({ x: 3 }); diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts index 7194456b85039..07ec452e36087 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple1.ts @@ -5,7 +5,7 @@ x; y; // ==SCOPE::Extract to function in global scope== -const { x, y }: { x: number; y: string } = /*RENAME*/newFunction(); +const { x, y }: { x: number; y: string; } = /*RENAME*/newFunction(); x; y; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts index 06da60f571baa..b8ae2b7288326 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Multiple3.ts @@ -6,7 +6,7 @@ x; y; z; // ==SCOPE::Extract to function in global scope== -var { x, y, z }: { x: number; y: string; z: number } = /*RENAME*/newFunction(); +var { x, y, z }: { x: number; y: string; z: number; } = /*RENAME*/newFunction(); x; y; z; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts index f4bfb66dea899..b2c53e3f1602c 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_Mixed3.ts @@ -11,7 +11,7 @@ function f() { function f() { let a = 1; - let { x, y }: { x: number; y: number } = /*RENAME*/newFunction(); + let { x, y }: { x: number; y: number; } = /*RENAME*/newFunction(); a; x; y; function newFunction() { diff --git a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts index 7f216b3365bb0..b76bf6c499961 100644 --- a/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts +++ b/tests/baselines/reference/extractFunction/extractFunction_VariableDeclaration_Writes_UnionUndefined.ts @@ -12,7 +12,7 @@ function f() { function f() { let a = 1; - let { x, y, z }: { x: number; y: number; z: number } = /*RENAME*/newFunction(); + let { x, y, z }: { x: number; y: number; z: number; } = /*RENAME*/newFunction(); a; x; y; z; function newFunction() { diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts index a8f6fa7a1ad1a..98ccf84c3d677 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc22.ts @@ -14,7 +14,7 @@ verify.codeFix({ ` /** @param {Object} sb * @param {Object} ns */ -function f(sb: { [s: string]: boolean }, ns: { [n: number]: string }) { +function f(sb: { [s: string]: boolean; }, ns: { [n: number]: string; }) { sb; ns; }`, }); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts index a4234127c46b0..f7d0522018abd 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc3.ts @@ -30,7 +30,7 @@ verify.codeFix({ * @param alpha - the other best parameter * @param {*} beta - I have no idea how this got here */ -function f(x: number, y: { a: string; b: Date }, z: string, alpha, beta: any) { +function f(x: number, y: { a: string; b: Date; }, z: string, alpha, beta: any) { x; y; z; alpha; beta; }`, }); diff --git a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts index e432e8f0b20dd..4421afbd86777 100644 --- a/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts +++ b/tests/cases/fourslash/annotateWithTypeFromJSDoc4.ts @@ -26,7 +26,7 @@ verify.codeFix({ * @param {number?} gamma * @param {number!} delta */ -function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { a: string }, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) { +function f(x: any, y: any, z: number | undefined, alpha: number[], beta: (this: { a: string; }, arg1: string, arg2: number) => boolean, gamma: number | null, delta: number) { x; y; z; alpha; beta; gamma; delta; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts index dba46a5005001..f2e05b24f5769 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceObjectLiteral.ts @@ -18,6 +18,6 @@ verify.codeFix({ } } class Person implements IPerson { - coordinate: { x: number; y: number }; + coordinate: { x: number; y: number; }; }`, }); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts index 778aca58dc105..52d69b466fd3b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts @@ -43,13 +43,13 @@ class C implements I { a0: {}; a1: (b1: number, c1: string) => number; a2: (b2: number, c2: string) => number; - a3: { (b3: number, c3: string): number; x: number }; + a3: { (b3: number, c3: string): number; x: number; }; a4: new (b1: number, c1: string) => number; a5: new (b2: number, c2: string) => number; - a6: { new(b3: number, c3: string): number; x: number }; - a7: { foo(b7: number, c7: string): number }; - a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number }; - a9: { (b9: number, c9: string): number;[d9: number]: I }; - a10: { (b10: number, c10: string): number;[d10: string]: I }; + a6: { new(b3: number, c3: string): number; x: number; }; + a7: { foo(b7: number, c7: string): number; }; + a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number; }; + a9: { (b9: number, c9: string): number;[d9: number]: I; }; + a10: { (b10: number, c10: string): number;[d10: string]: I; }; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts index f43a02886d519..e667783b99580 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts @@ -12,5 +12,5 @@ //// } verify.rangeAfterCodeFix(` -z: number & { __iBrand: any }; +z: number & { __iBrand: any; }; `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts index 6b396a23cdd6c..cf8926ff33ecb 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -12,6 +12,6 @@ verify.codeFix({ x: { y: T, z: T[] }; } class C implements I { - x: { y: number; z: number[] }; + x: { y: number; z: number[]; }; }`, }); diff --git a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts index 5eb564b830573..85e1d3bc50952 100644 --- a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts +++ b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts @@ -6,4 +6,4 @@ //// return result //// } -verify.rangeAfterCodeFix("app: { use: (arg0: string) => any }"); +verify.rangeAfterCodeFix("app: { use: (arg0: string) => any; }"); diff --git a/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts b/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts index 282df322de06c..b5969c79378d8 100644 --- a/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts +++ b/tests/cases/fourslash/codeFixInferFromExpressionStatement.ts @@ -5,4 +5,4 @@ //// app.use('hi') //// } -verify.rangeAfterCodeFix("app: { use: (arg0: string) => void }"); +verify.rangeAfterCodeFix("app: { use: (arg0: string) => void; }"); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts index f7a0191b8f79a..3f71ae9ddd2d5 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageImplicitAny.ts @@ -21,5 +21,5 @@ verify.codeFix({ description: "Infer 'this' type of 'returnThisMember' from usage", index: 0, - newRangeContent: "this: { member: string; returnThisMember: () => any } ", + newRangeContent: "this: { member: string; returnThisMember: () => any; } ", }); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts index ccee3d37fc6dc..f626e4cba2d2b 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionThisUsageLiteral.ts @@ -13,5 +13,5 @@ verify.codeFix({ description: "Infer 'this' type of 'returnThisMember' from usage", index: 0, - newRangeContent: "this: { member: string; returnThisMember: () => any } ", + newRangeContent: "this: { member: string; returnThisMember: () => any; } ", }); diff --git a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts index dc059ef19459a..31068469b4db9 100644 --- a/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts +++ b/tests/cases/fourslash/codeFixInferFromFunctionUsage.ts @@ -6,4 +6,4 @@ //// } // https://github.com/Microsoft/TypeScript/issues/29330 -verify.rangeAfterCodeFix("arr: { other: (arg0: (a: number, b: number) => 1 | -1) => void }"); +verify.rangeAfterCodeFix("arr: { other: (arg0: (a: number, b: number) => 1 | -1) => void; }"); diff --git a/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts b/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts index c655138cf8cf8..e9288303781b5 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageEmptyTypePriority.ts @@ -10,4 +10,4 @@ //// var beforeExpr = !!conf.beforeExpr; ////}; -verify.rangeAfterCodeFix("label: any, conf: { keyword?: any; beforeExpr?: any } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("label: any, conf: { keyword?: any; beforeExpr?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts b/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts index 86c2d747b24d1..abe35fe46c8df 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageJSXElement.ts @@ -30,4 +30,4 @@ //// } -verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("props: { isLoading: any; update: (arg0: any) => any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts b/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts index d17a250cdf773..89e2c935e876f 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageMultipleParameters.ts @@ -6,4 +6,4 @@ //// f(1, "string", { a: 1 }, {shouldNotBeHere: 2}, {shouldNotBeHere: 2}, 3, "string"); -verify.rangeAfterCodeFix("a: number, b: string, c: { a: number }, d: number, e = 0, ...d: (string | number)[]", /*includeWhiteSpace*/ false, /*errorCode*/ undefined, /*index*/ 1); +verify.rangeAfterCodeFix("a: number, b: string, c: { a: number; }, d: number, e = 0, ...d: (string | number)[]", /*includeWhiteSpace*/ false, /*errorCode*/ undefined, /*index*/ 1); diff --git a/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts b/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts index 94270415f5478..44d2e2d7b1c25 100644 --- a/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts +++ b/tests/cases/fourslash/codeFixInferFromUsagePropertyAccess.ts @@ -12,4 +12,4 @@ //// return x.y.z ////} -verify.rangeAfterCodeFix("a: { b: { c: void } }, m: { n: () => number }, x: { y: { z: number[] } }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); +verify.rangeAfterCodeFix("a: { b: { c: void; }; }, m: { n: () => number; }, x: { y: { z: number[]; }; }", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, /*index*/0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts b/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts index 9213df8222a1d..765f788e054bb 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageStringIndexSignature.ts @@ -5,4 +5,4 @@ //// return a['hi']; ////} -verify.rangeAfterCodeFix("a: { [x: string]: any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("a: { [x: string]: any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts b/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts index a4919e4bc172d..76e25ab0fe714 100644 --- a/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts +++ b/tests/cases/fourslash/codeFixInferFromUsageUnifyAnonymousType.ts @@ -16,4 +16,4 @@ ////kw("6", { beforeExpr: true, prefix: true, startsExpr: true }) -verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("name: string, options: { startsExpr?: boolean; beforeExpr?: boolean; isLoop?: boolean; prefix?: boolean; keyword?: any; } | undefined",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); diff --git a/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts b/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts index 06b28552dc0aa..5c0698ad653e9 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethodObjectLiteralArgs.ts @@ -12,7 +12,7 @@ verify.codeFix({ description: "Declare method 'foo1'", index: 0, newRangeContent: ` - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { throw new Error("Method not implemented."); } `, @@ -23,10 +23,10 @@ verify.codeFix({ description: "Declare method 'foo2'", index: 0, newRangeContent: ` - foo2(arg0: null, arg1: {}, arg2: { a: number; b: string }) { + foo2(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { throw new Error("Method not implemented."); } - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { throw new Error("Method not implemented."); } `, @@ -37,13 +37,13 @@ verify.codeFix({ description: "Declare method 'foo3'", index: 0, newRangeContent: ` - foo3(arg0: null, arg1: {}, arg2: { a: number; b: string }): number { + foo3(arg0: null, arg1: {}, arg2: { a: number; b: string; }): number { throw new Error("Method not implemented."); } - foo2(arg0: null, arg1: {}, arg2: { a: number; b: string }) { + foo2(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { throw new Error("Method not implemented."); } - foo1(arg0: null, arg1: {}, arg2: { a: number; b: string }) { + foo1(arg0: null, arg1: {}, arg2: { a: number; b: string; }) { throw new Error("Method not implemented."); } ` diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts index d60f62da777ee..a2647c5a21e33 100644 --- a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteral.ts @@ -9,7 +9,7 @@ verify.rangeAfterCodeFix(` class A { - x: { a: number; b: string; c: any; d: any; e: any }; + x: { a: number; b: string; c: any; d: any; e: any; }; constructor() { let e: any = 10; diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts index 4c824e9e63c44..0b0ab8cdd59a2 100644 --- a/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyObjectLiteralStrictNullChecks.ts @@ -11,7 +11,7 @@ verify.rangeAfterCodeFix(` class A { - x: { a: number; b: string; c: undefined; d: null; e: any }; + x: { a: number; b: string; c: undefined; d: null; e: any; }; constructor() { let e: any = 10; diff --git a/tests/cases/fourslash/extract-const-callback-function-this1.ts b/tests/cases/fourslash/extract-const-callback-function-this1.ts index 26bc533893269..b0d269400bbd1 100644 --- a/tests/cases/fourslash/extract-const-callback-function-this1.ts +++ b/tests/cases/fourslash/extract-const-callback-function-this1.ts @@ -10,6 +10,6 @@ edit.applyRefactor({ actionDescription: "Extract to constant in enclosing scope", newContent: `declare function fWithThis(fn: (this: { a: string }, a: string) => string): void; -const newLocal = function(this: { a: string }, a: string): string { return this.a; }; +const newLocal = function(this: { a: string; }, a: string): string { return this.a; }; fWithThis(/*RENAME*/newLocal);` }); diff --git a/tests/cases/fourslash/extract-const-callback-function-this2.ts b/tests/cases/fourslash/extract-const-callback-function-this2.ts index 42fbd4da7ea0d..518c39e26e546 100644 --- a/tests/cases/fourslash/extract-const-callback-function-this2.ts +++ b/tests/cases/fourslash/extract-const-callback-function-this2.ts @@ -10,6 +10,6 @@ edit.applyRefactor({ actionDescription: "Extract to constant in enclosing scope", newContent: `declare function fWithThis(fn: (this: { a: string }, a: string) => string): void; -const newLocal = function(this: { a: string }, a: string): string { return this.a; }; +const newLocal = function(this: { a: string; }, a: string): string { return this.a; }; fWithThis(/*RENAME*/newLocal);` }); diff --git a/tests/cases/fourslash/extract-method18.ts b/tests/cases/fourslash/extract-method18.ts index 70a9a3230a8bf..0a488a04106c9 100644 --- a/tests/cases/fourslash/extract-method18.ts +++ b/tests/cases/fourslash/extract-method18.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ /*RENAME*/newFunction(x); } -function newFunction(x: { m: number }) { +function newFunction(x: { m: number; }) { x.m = 3; } ` diff --git a/tests/cases/fourslash/extract-method2.ts b/tests/cases/fourslash/extract-method2.ts index 8d562324d267f..419d63790756a 100644 --- a/tests/cases/fourslash/extract-method2.ts +++ b/tests/cases/fourslash/extract-method2.ts @@ -25,7 +25,7 @@ edit.applyRefactor({ } } -function newFunction(m: number, j: string, k: { x: string }) { +function newFunction(m: number, j: string, k: { x: string; }) { return m + j + k; } ` diff --git a/tests/cases/fourslash/formatAddSemicolons1.ts b/tests/cases/fourslash/formatAddSemicolons1.ts index 0b0dfa4301196..a24c86732549d 100644 --- a/tests/cases/fourslash/formatAddSemicolons1.ts +++ b/tests/cases/fourslash/formatAddSemicolons1.ts @@ -46,4 +46,4 @@ enum E { type M = { [K in keyof T]: any }; declare module 'foo' { } declare module 'bar'; -type T = { x: string, y: number };`); +type T = { x: string, y: number; };`); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts index f08498f515a51..8d8db71960859 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_allParamsOptional.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = "1" }: { a?: number; b?: string } = {}): string { + newContent: `function f({ a, b = "1" }: { a?: number; b?: string; } = {}): string { return b; } f();` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts index e5ffd37ea0354..97d337d8d1aa3 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_arrowFunction.ts @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const foo = ({ a, b }: { a: number; b: number }) => { }; + newContent: `const foo = ({ a, b }: { a: number; b: number; }) => { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts index c58bcd5804b93..2e30f96cd5968 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[] }) { + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { return a + b; } foo({ /**a*/ a: 1 /**b*/, /**c*/ b: 2 /**d*/, rest: [/**e*/ 3 /**f*/, /**g*/ 4 /**h*/] });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts index 4c9b6a253ae9d..fcb50b6b8e189 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_callComments2.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[] }) { + newContent: `function foo({ a, b, rest = [] }: { a: number; b: number; rest?: number[]; }) { return a + b; } foo( diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts index 440beec20c145..0b033200666d0 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_chainedCall.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: number; b: number }) { + newContent: `function foo({ a, b }: { a: number; b: number; }) { return { bar: () => a + b }; } var x = foo({ a: 1, b: 2 }).bar();` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts index 510a96ae0af87..beba49c17e3ed 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classDeclarationGoodUsages.ts @@ -19,7 +19,7 @@ edit.applyRefactor({ actionDescription: "Convert parameters to destructured object", newContent: `class C { static a: number = 2; - constructor({ a, b }: { a: number; b: number }) { } + constructor({ a, b }: { a: number; b: number; }) { } } const newC = new C({ a: 1, b: 2 }); const b = C.a; diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts index b654174d385b4..f779170d643b8 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpression.ts @@ -11,7 +11,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `const c = class { - constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number } }) { } + constructor({ a, b = { x: 1 } }: { a: number; b?: { x: number; }; }) { } } var x = new c({ a: 2 });` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts index 7221bffd91311..ce0054fd92a13 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classExpressionGoodUsages.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionDescription: "Convert parameters to destructured object", newContent: `const c = class C { static a: number = 2; - constructor({ a, b }: { a: number; b: number }) { } + constructor({ a, b }: { a: number; b: number; }) { } } const a = new c({ a: 0, b: 1 }); const b = c.a; diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts index 1b6656cbb54d1..d8c884356bd81 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_classTypeParameters.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: T; s: T }) { + bar({ t, s }: { t: T; s: T; }) { return s; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts index ac8ad18ddfd86..1503f2fe7ebbe 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_constructor.ts @@ -18,7 +18,7 @@ edit.applyRefactor({ newContent: `class Foo { t: string; s: string; - constructor({ t, s }: { t: string; s: string }) { + constructor({ t, s }: { t: string; s: string; }) { this.t = t; this.s = s; } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts index 423136a6f1b87..d5eed686068d4 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_function.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b }: { a: number; b: string }): string { + newContent: `function f({ a, b }: { a: number; b: string; }): string { return b; } f({ a: 4, b: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts index a53757c997b68..5340004a060c3 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `foo({ a: 1, b: 2 }); /**a*/ -/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/ }) { +/**b*/ function foo(/**this1*/ this /**this2*/: /**void1*/ void /**void2*/, { a, b = /**k*/ 1 /**l*/ }: { /**c*/ a /**d*/: /**e*/ number /**f*/; /**g*/ b /**h*/?: /**i*/ number /**j*/; }) { // m /**n*/ return a + b; // o // p diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts index 3a2b725629da0..07bc755c07677 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionComments1.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */ }) { + newContent: `function foo({ a, b }: { a: number /** a */; b: number /** b */; }) { return a + b; }` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts index 75ff05d14207f..c4e81cf79262b 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionExpression.ts @@ -8,6 +8,6 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const foo = function({ a, b }: { a: number; b: number }) { }; + newContent: `const foo = function({ a, b }: { a: number; b: number; }) { }; foo({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts index 7b540957de4c5..9a27ae4f09ed2 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_functionTypeParameters.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ t, s }: { t: T; s: S }) { + newContent: `function foo({ t, s }: { t: T; s: S; }) { return s; } foo({ t: "a", s: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts index 3d8c97597dff6..a4b37c7405a6b 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export function f({ a, b }: { a: number; b: string }): string { + newContent: `export function f({ a, b }: { a: number; b: string; }): string { return b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts index 42465d86f5e27..a83c11bd1fd10 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction2.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export default function f({ a, b }: { a: number; b: string }): string { + newContent: `export default function f({ a, b }: { a: number; b: string; }): string { return b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts index a72adaa1dafe8..6a2ecc1cfc373 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction3.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo({ a, b }: { a: string; b: string }) { } + newContent: `function foo({ a, b }: { a: string; b: string; }) { } export = foo;` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts index 7739e1df43cc5..729878b2b2233 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction4.ts @@ -17,7 +17,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export { foo as default }; -function foo({ a, b }: { a: number; b: number }) { +function foo({ a, b }: { a: number; b: number; }) { return a + b; }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts index 9a7a1c2f38d4c..17a41880a5547 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction5.ts @@ -17,7 +17,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export class C { - constructor({ a, b }: { a: number; b: number }) { } + constructor({ a, b }: { a: number; b: number; }) { } }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts index fc1d86570e134..615a49c026767 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_importedFunction6.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `export function foo({ a, b }: { a: string; b: string }) { }` + newContent: `export function foo({ a, b }: { a: string; b: string; }) { }` }); goTo.file("a.ts"); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts index a40cdc183bde3..70a5d2022b5d9 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedConstructor.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - constructor({ t, s }: { t: string; s: string }) { } + constructor({ t, s }: { t: string; s: string; }) { } } class Bar extends Foo { } var bar = new Bar({ t: "a", s: "b" }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts index e61d325290d0a..a04560886c087 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_inheritedMethod.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string }): string { + bar({ t, s }: { t: string; s: string; }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts index 9bb6e5f022f8b..3f0da9f13a391 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializer.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = "1" }: { a: number; b?: string }): string { + newContent: `function f({ a, b = "1" }: { a: number; b?: string; }): string { return b; } f({ a: 4, b: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts index 6fe36e73a3734..f2b92fc89e718 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_initializerInference.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean } } }) { + newContent: `function f({ a, b = { x: 1, z: { s: true } } }: { a: number; b?: { x: number; z: { s: boolean; }; }; }) { return b; } f({ a: 2 });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts index 2272c39020963..f3f78de391e45 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_method.ts @@ -14,7 +14,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string }): string { + bar({ t, s }: { t: string; s: string; }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts index 753c7f8472f87..d1347ad877be2 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_methodCalls.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - bar({ t, s }: { t: string; s: string }): string { + bar({ t, s }: { t: string; s: string; }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts index 4530fecafec8a..d38bff48b249e 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_namelessClass.ts @@ -16,7 +16,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `export default class { - constructor({ a, b }: { a: string; b: string }) { } + constructor({ a, b }: { a: string; b: string; }) { } }` }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts index 755394aa1c5c9..7655af0510081 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_recursiveFunction.ts @@ -12,7 +12,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `const f = function foo({ a, b }: { a: number; b: number }) { + newContent: `const f = function foo({ a, b }: { a: number; b: number; }) { foo({ a: 1, b: 2 }); } function foo(a: number, b: number) { } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts index 11ebc9cfc0c91..1c6350d89f57b 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_restParamInference.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[] }) { } + newContent: `function log({ a, b, args = [] }: { a: number; b: number; args?: any[]; }) { } let l = log({ a: -1, b: -2, args: [3, 4, 5] }); let k = log({ a: 1, b: 2 });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts index 7b4096c79d236..86ea98582ab9c 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_shorthandProperty.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function f({ a, b, rest = [] }: { a: number; b: number; rest?: string[] }) { } + newContent: `function f({ a, b, rest = [] }: { a: number; b: number; rest?: string[]; }) { } const a = 4; const b = 5; f({ a, b }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts index 43cdbf8aa770e..8c19582c145b9 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_staticMethod.ts @@ -13,7 +13,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class Foo { - static bar({ t, s }: { t: string; s: string }): string { + static bar({ t, s }: { t: string; s: string; }): string { return s + t; } } diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts index 88f8e2cdb1e6c..2bdee83647275 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_superCall.ts @@ -15,7 +15,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: `class A { - constructor({ a, b }: { a: string; b: string }) { } + constructor({ a, b }: { a: string; b: string; }) { } } class B extends A { constructor(a: string, b: string, c: string) { diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts index 2cef3d9b08fd0..1075fb7dddb96 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_templateLiteral.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", newContent: [ - 'function insert({ template, overwriteBefore = 0 }: { template: string; overwriteBefore?: number }) {}', + 'function insert({ template, overwriteBefore = 0 }: { template: string; overwriteBefore?: number; }) {}', 'insert({ template: `this is \\${not} a substitution` });' ].join('\n') }); diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts index a01a4cf9cef92..05a6620707ffd 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_thisParam.ts @@ -10,7 +10,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function foo(this: void, { t, s }: { t: string; s: string }) { + newContent: `function foo(this: void, { t, s }: { t: string; s: string; }) { return s; } foo({ t: "a", s: "b" });` diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts index 6fb05f7c02e4d..7584308fec004 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_tupleRestParam.ts @@ -23,7 +23,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn1({ a, b, args }: { a: number; b: number; args: [number, number] }) { } + newContent: `function fn1({ a, b, args }: { a: number; b: number; args: [number, number]; }) { } fn1({ a: 1, b: 2, args: [3, 4] });` }); @@ -32,7 +32,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn2({ a, b, args }: { a: number; b: number; args: [number, number, ...string[]] }) { } + newContent: `function fn2({ a, b, args }: { a: number; b: number; args: [number, number, ...string[]]; }) { } fn2({ a: 1, b: 2, args: [3, 4] }); fn2({ a: 1, b: 2, args: [3, 4, "a"] });` }); @@ -42,7 +42,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn3({ b, c }: { b: boolean; c: [] }) { } + newContent: `function fn3({ b, c }: { b: boolean; c: []; }) { } fn3({ b: true, c: [] });` }); @@ -51,7 +51,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function fn4({ a, args = [] }: { a: number; args?: [...string[]] }) { } + newContent: `function fn4({ a, args = [] }: { a: number; args?: [...string[]]; }) { } fn4({ a: 2 }); fn4({ a: 1, args: ["two", "three"] });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts index e65a61b10ab76..88401d83c4bc6 100644 --- a/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts +++ b/tests/cases/fourslash/refactorConvertParamsToDestructuredObject_typedRestParam.ts @@ -9,7 +9,7 @@ edit.applyRefactor({ refactorName: "Convert parameters to destructured object", actionName: "Convert parameters to destructured object", actionDescription: "Convert parameters to destructured object", - newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[] }) { } + newContent: `function buildName({ firstName, middleName, restOfName = [] }: { firstName: string; middleName?: string; restOfName?: string[]; }) { } let employeeName = buildName({ firstName: "Joseph", middleName: "Samuel", restOfName: ["Lucas", "MacKinzie"] }); let myName = buildName({ firstName: "Joseph" });` }); \ No newline at end of file diff --git a/tests/cases/fourslash/typeToStringCrashInCodeFix.ts b/tests/cases/fourslash/typeToStringCrashInCodeFix.ts index 8a9c19817a50f..5201a6b77fba4 100644 --- a/tests/cases/fourslash/typeToStringCrashInCodeFix.ts +++ b/tests/cases/fourslash/typeToStringCrashInCodeFix.ts @@ -3,4 +3,4 @@ // @noImplicitAny: true //// function f([|y |], z = { p: y[ -verify.rangeAfterCodeFix("y: { [x: string]: any }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); +verify.rangeAfterCodeFix("y: { [x: string]: any; }",/*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 0); From 498a320229fb59c1ada80c4e15659926ea1e1936 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 25 Sep 2019 16:26:14 -0700 Subject: [PATCH 22/25] Fix straggler test --- tests/cases/fourslash/codeFixInferFromCallInAssignment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts index 85e1d3bc50952..5eb564b830573 100644 --- a/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts +++ b/tests/cases/fourslash/codeFixInferFromCallInAssignment.ts @@ -6,4 +6,4 @@ //// return result //// } -verify.rangeAfterCodeFix("app: { use: (arg0: string) => any; }"); +verify.rangeAfterCodeFix("app: { use: (arg0: string) => any }"); From 68cf56d35be75913f5eda97018a7bf95d4c811e6 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 25 Sep 2019 16:46:53 -0700 Subject: [PATCH 23/25] Add test for leading semicolon class members --- tests/cases/fourslash/formatAddSemicolons1.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/cases/fourslash/formatAddSemicolons1.ts b/tests/cases/fourslash/formatAddSemicolons1.ts index a24c86732549d..304ec32f8baf4 100644 --- a/tests/cases/fourslash/formatAddSemicolons1.ts +++ b/tests/cases/fourslash/formatAddSemicolons1.ts @@ -14,6 +14,8 @@ //// ["two"] //// three: string //// m() { } +//// ;["three"] = {} +//// ;["four"] ////} ////enum E { //// C @@ -39,6 +41,8 @@ class C { ["two"]; three: string; m() { } + ;["three"] = {} + ;["four"]; } enum E { C From 623b4b30f1c2b8c77eaeec8b33c1fd5536515291 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 26 Sep 2019 11:47:21 -0700 Subject: [PATCH 24/25] Rename a lot of stuff for clarity --- src/services/formatting/formatting.ts | 17 +- src/services/formatting/rule.ts | 18 +- src/services/formatting/rules.ts | 260 +++++++++++++------------- src/services/formatting/rulesMap.ts | 32 ++-- src/services/textChanges.ts | 14 +- 5 files changed, 171 insertions(+), 170 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 7cb4eb7948abe..6895ddc96b9b7 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -966,9 +966,8 @@ namespace ts.formatting { let trimTrailingWhitespaces = false; let lineAction = LineAction.None; if (rules) { - // Apply rules in reverse order so that higher when higher priority rules (which are first in the array) - // create text changes at the same position as lower priority rules, the higher priority text changes - // are evaluated last. + // Apply rules in reverse order so that higher priority rules (which are first in the array) + // win in a conflict with lower priority rules. forEachRight(rules, rule => { lineAction = applyRuleEdits(rule, previousItem, previousStartLine, currentItem, currentStartLine); switch (lineAction) { @@ -992,7 +991,7 @@ namespace ts.formatting { } // We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line - trimTrailingWhitespaces = !(rule.action & RuleAction.DeleteTrivia) && rule.flags !== RuleFlags.CanDeleteNewLines; + trimTrailingWhitespaces = !(rule.action & RuleAction.DeleteSpace) && rule.flags !== RuleFlags.CanDeleteNewLines; }); } else { @@ -1172,10 +1171,10 @@ namespace ts.formatting { ): LineAction { const onLaterLine = currentStartLine !== previousStartLine; switch (rule.action) { - case RuleAction.Ignore: + case RuleAction.StopProcessingSpaceActions: // no action required return LineAction.None; - case RuleAction.DeleteTrivia: + case RuleAction.DeleteSpace: if (previousRange.end !== currentRange.pos) { // delete characters starting from t1.end up to t2.pos exclusive recordDelete(previousRange.end, currentRange.pos - previousRange.end); @@ -1185,7 +1184,7 @@ namespace ts.formatting { case RuleAction.DeleteToken: recordDelete(previousRange.pos, previousRange.end - previousRange.pos); break; - case RuleAction.NewLine: + case RuleAction.InsertNewLine: // exit early if we on different lines and rule cannot change number of newlines // if line1 and line2 are on subsequent lines then no edits are required - ok to exit // if line1 and line2 are separated with more than one newline - ok to exit since we cannot delete extra new lines @@ -1200,7 +1199,7 @@ namespace ts.formatting { return onLaterLine ? LineAction.None : LineAction.LineAdded; } break; - case RuleAction.Space: + case RuleAction.InsertSpace: // exit early if we on different lines and rule cannot change number of newlines if (rule.flags !== RuleFlags.CanDeleteNewLines && previousStartLine !== currentStartLine) { return LineAction.None; @@ -1212,7 +1211,7 @@ namespace ts.formatting { return onLaterLine ? LineAction.LineRemoved : LineAction.None; } break; - case RuleAction.TrailingSemicolon: + case RuleAction.InsertTrailingSemicolon: recordInsert(previousRange.end, ";"); } return LineAction.None; diff --git a/src/services/formatting/rule.ts b/src/services/formatting/rule.ts index b9f103a4efa36..ef98461738891 100644 --- a/src/services/formatting/rule.ts +++ b/src/services/formatting/rule.ts @@ -12,15 +12,17 @@ namespace ts.formatting { export const anyContext: readonly ContextPredicate[] = emptyArray; export const enum RuleAction { - Ignore = 1 << 0, - Space = 1 << 1, - NewLine = 1 << 2, - DeleteTrivia = 1 << 3, - DeleteToken = 1 << 4, - TrailingSemicolon = 1 << 5, + StopProcessingSpaceActions = 1 << 0, + StopProcessingTokenActions = 1 << 1, + InsertSpace = 1 << 2, + InsertNewLine = 1 << 3, + DeleteSpace = 1 << 4, + DeleteToken = 1 << 5, + InsertTrailingSemicolon = 1 << 6, - TriviaAction = Space | NewLine | DeleteTrivia, - TokenAction = DeleteToken | TrailingSemicolon + StopAction = StopProcessingSpaceActions | StopProcessingTokenActions, + ModifySpaceAction = InsertSpace | InsertNewLine | DeleteSpace, + ModifyTokenAction = DeleteToken | InsertTrailingSemicolon, } export const enum RuleFlags { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 7fa2845edc030..5500c36213795 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -47,103 +47,103 @@ namespace ts.formatting { // These rules are higher in priority than user-configurable const highPriorityCommonRules = [ // Leave comments alone - rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.Ignore), - rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.Ignore), + rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.StopProcessingSpaceActions), + rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.StopProcessingSpaceActions), - rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteTrivia), - rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Space), - rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), + rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteSpace), + rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.InsertSpace), + rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace), // insert space after '?' only when it is used in conditional operator - rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.Space), + rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.InsertSpace), // in other cases there should be no space between '?' and next token - rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), - rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeDot", anyToken, SyntaxKind.DotToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterDot", SyntaxKind.DotToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), - rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteTrivia), + rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteSpace), // Special handling of unary operators. // Prefix operators generally shouldn't have a space between // them and their target unary expression. - rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // More unary operator special-casing. // DevDiv 181814: Be careful when removing leading whitespace // around unary operators. Examples: // 1 - -2 --X--> 1--2 // a + ++b --X--> a+++b - rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterPostdecrementWhenFollowedBySubtract", SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - - rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterPostdecrementWhenFollowedBySubtract", SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + + rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // For functions and control block place } on a new line [multi-line rule] - rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.NewLine), + rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.InsertNewLine), // Space/new line after }. - rule("SpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, anyTokenExcept(SyntaxKind.CloseParenToken), [isNonJsxSameLineTokenContext, isAfterCodeBlockContext], RuleAction.Space), + rule("SpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, anyTokenExcept(SyntaxKind.CloseParenToken), [isNonJsxSameLineTokenContext, isAfterCodeBlockContext], RuleAction.InsertSpace), // Special case for (}, else) and (}, while) since else & while tokens are not part of the tree which makes SpaceAfterCloseBrace rule not applied // Also should not apply to }) - rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteTrivia), + rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace), // Add a space after control dec context if the next character is an open bracket ex: 'if (false)[a, b] = [1, 2];' -> 'if (false) [a, b] = [1, 2];' - rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.Space), + rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.InsertSpace), - rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.DeleteTrivia), - rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.Space), + rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.DeleteSpace), + rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.InsertSpace), - rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.Space), + rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.InsertSpace), // Insert new line after { and before } in multi-line contexts. - rule("NewLineAfterOpenBraceInBlockContext", SyntaxKind.OpenBraceToken, anyToken, [isMultilineBlockContext], RuleAction.NewLine), + rule("NewLineAfterOpenBraceInBlockContext", SyntaxKind.OpenBraceToken, anyToken, [isMultilineBlockContext], RuleAction.InsertNewLine), // For get/set members, we check for (identifier,identifier) since get/set don't have tokens and they are represented as just an identifier token. // Though, we do extra check on the context to make sure we are dealing with get/set node. Example: // get x() {} // set x(val) {} - rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.Space), + rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.InsertSpace), - rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteTrivia), - rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.Space), + rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteSpace), + rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.InsertSpace), - rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.Space), - rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.DeleteTrivia), + rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.InsertSpace), + rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.DeleteSpace), // Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options. - rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterBinaryKeywordOperator", binaryKeywordOperators, anyToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), + rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterBinaryKeywordOperator", binaryKeywordOperators, anyToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), - rule("SpaceAfterVoidOperator", SyntaxKind.VoidKeyword, anyToken, [isNonJsxSameLineTokenContext, isVoidOpContext], RuleAction.Space), + rule("SpaceAfterVoidOperator", SyntaxKind.VoidKeyword, anyToken, [isNonJsxSameLineTokenContext, isVoidOpContext], RuleAction.InsertSpace), // Async-await - rule("SpaceBetweenAsyncAndOpenParen", SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken, [isArrowFunctionContext, isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("SpaceBetweenAsyncAndOpenParen", SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken, [isArrowFunctionContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, SyntaxKind.FunctionKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), // Template string - rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // JSX opening elements - rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // TypeScript-specific rules // Use of module as a function call. e.g.: import m2 = module("m2"); - rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // Add a space around certain TypeScript keywords rule( "SpaceAfterCertainTypeScriptKeywords", @@ -174,41 +174,41 @@ namespace ts.formatting { ], anyToken, [isNonJsxSameLineTokenContext], - RuleAction.Space), + RuleAction.InsertSpace), rule( "SpaceBeforeCertainTypeScriptKeywords", anyToken, [SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword], [isNonJsxSameLineTokenContext], - RuleAction.Space), + RuleAction.InsertSpace), // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" { - rule("SpaceAfterModuleName", SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken, [isModuleDeclContext], RuleAction.Space), + rule("SpaceAfterModuleName", SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken, [isModuleDeclContext], RuleAction.InsertSpace), // Lambda expressions - rule("SpaceBeforeArrow", anyToken, SyntaxKind.EqualsGreaterThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("SpaceBeforeArrow", anyToken, SyntaxKind.EqualsGreaterThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), // Optional parameters and let args - rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteTrivia), + rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace), // Remove spaces in empty interface literals. e.g.: x: {} - rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.DeleteTrivia), + rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.DeleteSpace), // generics and type assertions - rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), - rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace), + rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace), rule("NoSpaceAfterCloseAngularBracket", SyntaxKind.GreaterThanToken, [SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext, isNotFunctionDeclContext /*To prevent an interference with the SpaceBeforeOpenParenInFuncDecl rule*/], - RuleAction.DeleteTrivia), + RuleAction.DeleteSpace), // decorators - rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // Insert space after @ in decorator rule("SpaceAfterDecorator", anyToken, @@ -228,113 +228,113 @@ namespace ts.formatting { SyntaxKind.AsteriskToken, ], [isEndOfDecoratorContextOnSameLine], - RuleAction.Space), + RuleAction.InsertSpace), - rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.DeleteTrivia), - rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.DeleteSpace), + rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), ]; // These rules are applied after high priority const userConfigurableRules = [ // Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses - rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), - rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.Space), - rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.DeleteTrivia), + rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket], RuleAction.InsertSpace), + rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.DeleteSpace), // Insert space after function keyword for anonymous functions - rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.Space), - rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.DeleteTrivia), + rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.InsertSpace), + rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.DeleteSpace), // Insert space after keywords in control flow statements - rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.Space), - rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.DeleteTrivia), + rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.InsertSpace), + rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.DeleteSpace), // Insert space after opening and before closing nonempty parenthesis - rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // Insert space after opening and before closing nonempty brackets - rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}. - rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space), - rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.Space), - rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace), + rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace), + rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // Insert space after opening and before closing template string braces - rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.Space), - rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace), + rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // No space after { and before } in JSX expression - rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space), - rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.Space), - rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteTrivia), - rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteTrivia), + rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace), + rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace), + rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace), + rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace), // Insert space after semicolon in for statement - rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.Space), - rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.DeleteTrivia), + rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.InsertSpace), + rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.DeleteSpace), // Insert space before and after binary operators - rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.Space), - rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteTrivia), + rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace), + rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace), - rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.Space), - rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.DeleteTrivia), + rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.InsertSpace), + rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.DeleteSpace), // Open Brace braces after control block - rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines), + rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines), // Open Brace braces after function // TypeScript: Function can have return types, which can be made of tons of different token kinds - rule("NewLineBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines), + rule("NewLineBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines), // Open Brace braces after TypeScript module/class/interface - rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.NewLine, RuleFlags.CanDeleteNewLines), + rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines), - rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Space), - rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.DeleteTrivia), + rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.InsertSpace), + rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.DeleteSpace), - rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), - rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteTrivia), + rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.InsertSpace), + rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteSpace), rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken), - rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.TrailingSemicolon), + rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.InsertTrailingSemicolon), ]; // 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 ? - rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), - rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), - rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), - rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.Space, RuleFlags.CanDeleteNewLines), + rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines), + rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines), + rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines), - rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), + rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), // No space before and after indexer `x[]` - rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteTrivia), - rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.DeleteTrivia), - rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), + rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.DeleteSpace), + rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), // Remove extra space between for and await - rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] @@ -343,9 +343,9 @@ namespace ts.formatting { [SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword], anyToken, [isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNotForContext], - RuleAction.Space), + RuleAction.InsertSpace), // This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter. - rule("SpaceAfterTryFinally", [SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + rule("SpaceAfterTryFinally", [SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), ]; return [ diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index 86e256ac76060..f466b397d20dd 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -13,16 +13,23 @@ namespace ts.formatting { return rulesMapCache; } + /** + * For a given rule action, gets a mask of other rule actions that + * cannot be applied at the same position. + */ function getRuleActionExclusion(ruleAction: RuleAction): RuleAction { let mask: RuleAction = 0; - if (ruleAction & RuleAction.Ignore) { - mask |= RuleAction.TriviaAction; + if (ruleAction & RuleAction.StopProcessingSpaceActions) { + mask |= RuleAction.ModifySpaceAction; } - if (ruleAction & RuleAction.TriviaAction) { - mask |= RuleAction.TriviaAction; + if (ruleAction & RuleAction.StopProcessingTokenActions) { + mask |= RuleAction.ModifyTokenAction; } - if (ruleAction & RuleAction.TokenAction) { - mask |= RuleAction.TokenAction; + if (ruleAction & RuleAction.ModifySpaceAction) { + mask |= RuleAction.ModifySpaceAction; + } + if (ruleAction & RuleAction.ModifyTokenAction) { + mask |= RuleAction.ModifyTokenAction; } return mask; } @@ -37,10 +44,7 @@ namespace ts.formatting { let ruleActionMask: RuleAction = 0; for (const rule of bucket) { const acceptRuleActions = ~getRuleActionExclusion(ruleActionMask); - if (!(rule.action & acceptRuleActions)) { - continue; - } - if (every(rule.context, c => c(context))) { + if (rule.action & acceptRuleActions && every(rule.context, c => c(context))) { rules.push(rule); ruleActionMask |= rule.action; } @@ -84,8 +88,8 @@ namespace ts.formatting { const mapRowLength = SyntaxKind.LastToken + 1; enum RulesPosition { - IgnoreRulesSpecific = 0, - IgnoreRulesAny = maskBitSize * 1, + StopRulesSpecific = 0, + StopRulesAny = maskBitSize * 1, ContextRulesSpecific = maskBitSize * 2, ContextRulesAny = maskBitSize * 3, NoContextRulesSpecific = maskBitSize * 4, @@ -108,8 +112,8 @@ namespace ts.formatting { // In order to insert a rule to the end of sub-bucket (3), we get the index by adding // the values in the bitmap segments 3rd, 2nd, and 1st. function addRule(rules: Rule[], rule: Rule, specificTokens: boolean, constructionState: number[], rulesBucketIndex: number): void { - const position = rule.action === RuleAction.Ignore ? - specificTokens ? RulesPosition.IgnoreRulesSpecific : RulesPosition.IgnoreRulesAny : + const position = rule.action & RuleAction.StopAction ? + specificTokens ? RulesPosition.StopRulesSpecific : RulesPosition.StopRulesAny : rule.context !== anyContext ? specificTokens ? RulesPosition.ContextRulesSpecific : RulesPosition.ContextRulesAny : specificTokens ? RulesPosition.NoContextRulesSpecific : RulesPosition.NoContextRulesAny; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index dd690c8824eff..03e7a1c458d33 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -832,16 +832,12 @@ namespace ts.textChanges { return (options.prefix || "") + noIndent + (options.suffix || ""); } - function getFormatCodeSettingsForWriting(context: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings { + function getFormatCodeSettingsForWriting({ options }: formatting.FormatContext, sourceFile: SourceFile): FormatCodeSettings { + const shouldAutoDetectSemicolonPreference = !options.semicolons || options.semicolons === SemicolonPreference.Ignore; + const shouldRemoveSemicolons = options.semicolons === SemicolonPreference.Remove || shouldAutoDetectSemicolonPreference && !probablyUsesSemicolons(sourceFile); return { - ...context.options, - // If the user has no semicolon preference defined and the file doesn’t use semicolons, - // make the formatter remove them. Otherwise, ignore semicolons in the formatter because - // the writer will insert them by default. - semicolons: context.options.semicolons === SemicolonPreference.Remove || - (!context.options.semicolons || context.options.semicolons === SemicolonPreference.Ignore) && !probablyUsesSemicolons(sourceFile) - ? SemicolonPreference.Remove - : SemicolonPreference.Ignore, + ...options, + semicolons: shouldRemoveSemicolons ? SemicolonPreference.Remove : SemicolonPreference.Ignore, }; } From 61c3e61f3e56e2f570b2b6b478913c3878aaa17a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 26 Sep 2019 12:04:22 -0700 Subject: [PATCH 25/25] Invert some boolean logic --- src/services/formatting/rules.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 5500c36213795..75720555f5248 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -829,9 +829,9 @@ namespace ts.formatting { // foo; // (): void // } - return !(isPropertySignature(context.currentTokenParent) - && !context.currentTokenParent.type - && nextTokenKind === SyntaxKind.OpenParenToken); + return !isPropertySignature(context.currentTokenParent) + || !!context.currentTokenParent.type + || nextTokenKind !== SyntaxKind.OpenParenToken; } if (isPropertyDeclaration(context.currentTokenParent)) {