diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c44fa006c03c2..df103e9dafc33 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -381,6 +381,7 @@ namespace FourSlash { insertSpaceAfterTypeAssertion: false, placeOpenBraceOnNewLineForFunctions: false, placeOpenBraceOnNewLineForControlBlocks: false, + insertSpaceBeforeTypeAnnotation: false }; // Open the first file by default diff --git a/src/server/protocol.ts b/src/server/protocol.ts index d406aa9c65b2a..ef59cc164069f 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2556,6 +2556,7 @@ namespace ts.server.protocol { insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } export interface CompilerOptions { diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 470f3237eb94b..04f599039daf5 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -47,7 +47,7 @@ namespace ts.formatting { rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.Ignore), rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.Ignore), - rule("NoSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete), + rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.Delete), rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Space), rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.Delete), // insert space after '?' only when it is used in conditional operator @@ -300,6 +300,9 @@ namespace ts.formatting { rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Space), rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.Delete), + + rule("SpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Space), + rule("NoSpaceBeforeTypeAnnotation", anyToken, SyntaxKind.ColonToken, [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.Delete), ]; // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list. @@ -441,6 +444,19 @@ namespace ts.formatting { return !isBinaryOpContext(context); } + function isNotTypeAnnotationContext(context: FormattingContext): boolean { + return !isTypeAnnotationContext(context); + } + + function isTypeAnnotationContext(context: FormattingContext): boolean { + const contextKind = context.contextNode.kind; + return contextKind === SyntaxKind.PropertyDeclaration || + contextKind === SyntaxKind.PropertySignature || + contextKind === SyntaxKind.Parameter || + contextKind === SyntaxKind.VariableDeclaration || + isFunctionLikeKind(contextKind); + } + function isConditionalOperatorContext(context: FormattingContext): boolean { return context.contextNode.kind === SyntaxKind.ConditionalExpression; } diff --git a/src/services/types.ts b/src/services/types.ts index 813e8790933ec..8e6704866495f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -597,6 +597,7 @@ namespace ts { InsertSpaceBeforeFunctionParenthesis?: boolean; PlaceOpenBraceOnNewLineForFunctions: boolean; PlaceOpenBraceOnNewLineForControlBlocks: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } export interface FormatCodeSettings extends EditorSettings { @@ -615,6 +616,7 @@ namespace ts { insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } export interface DefinitionInfo { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index eafef37a5ecca..495e6083f4b2e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4209,6 +4209,7 @@ declare namespace ts { InsertSpaceBeforeFunctionParenthesis?: boolean; PlaceOpenBraceOnNewLineForFunctions: boolean; PlaceOpenBraceOnNewLineForControlBlocks: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } interface FormatCodeSettings extends EditorSettings { insertSpaceAfterCommaDelimiter?: boolean; @@ -4226,6 +4227,7 @@ declare namespace ts { insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } interface DefinitionInfo { fileName: string; @@ -6845,6 +6847,7 @@ declare namespace ts.server.protocol { insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } interface CompilerOptions { allowJs?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6748b39e58e97..5563d95aa0c56 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4209,6 +4209,7 @@ declare namespace ts { InsertSpaceBeforeFunctionParenthesis?: boolean; PlaceOpenBraceOnNewLineForFunctions: boolean; PlaceOpenBraceOnNewLineForControlBlocks: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } interface FormatCodeSettings extends EditorSettings { insertSpaceAfterCommaDelimiter?: boolean; @@ -4226,6 +4227,7 @@ declare namespace ts { insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; + insertSpaceBeforeTypeAnnotation?: boolean; } interface DefinitionInfo { fileName: string; diff --git a/tests/cases/fourslash/formattingOptionsChange.ts b/tests/cases/fourslash/formattingOptionsChange.ts index 0e731412ede3c..390ed426aa990 100644 --- a/tests/cases/fourslash/formattingOptionsChange.ts +++ b/tests/cases/fourslash/formattingOptionsChange.ts @@ -9,6 +9,7 @@ /////*insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets*/[1 ]; [ ]; []; [,]; /////*insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces*/`${1}`;`${ 1 }` /////*insertSpaceAfterTypeAssertion*/const bar = Thing.getFoo(); +/////*insertSpaceBeforeTypeAnnotation*/const bar : number = 1; /////*placeOpenBraceOnNewLineForFunctions*/class foo { ////} /////*placeOpenBraceOnNewLineForControlBlocks*/if (true) { @@ -26,6 +27,7 @@ runTest("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis", " ( 1 ) runTest("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets", "[ 1 ];[];[];[ , ];", "[1];[];[];[,];"); runTest("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces", "`${ 1 }`; `${ 1 }`", "`${1}`; `${1}`"); runTest("insertSpaceAfterTypeAssertion", "const bar = Thing.getFoo();", "const bar = Thing.getFoo();"); +runTest("insertSpaceBeforeTypeAnnotation", "const bar : number = 1;", "const bar: number = 1;"); runTest("placeOpenBraceOnNewLineForFunctions", "class foo", "class foo {"); runTest("placeOpenBraceOnNewLineForControlBlocks", "if (true)", "if (true) {"); runTest("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces", "{ var t = 1 }; var { a, b } = { a: 'sw', b: 'r' }; function f({ a, b }) { }", "{var t = 1}; var {a, b} = {a: 'sw', b: 'r'}; function f({a, b}) {}"); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 933e7a88f94e3..ec2a5d1a2ea37 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -97,6 +97,7 @@ declare namespace FourSlashInterface { InsertSpaceAfterTypeAssertion: boolean; PlaceOpenBraceOnNewLineForFunctions: boolean; PlaceOpenBraceOnNewLineForControlBlocks: boolean; + insertSpaceBeforeTypeAnnotation: boolean; [s: string]: boolean | number | string | undefined; } interface Range {