• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    anyContext, ContextPredicate, FormattingContext, FormattingRequestKind, Rule, RuleAction, RuleFlags,
3    TextRangeWithKind, TokenRange,
4} from "../_namespaces/ts.formatting";
5import {
6    BinaryExpression, contains, findAncestor, findNextToken, FormatCodeSettings, hasDecorators, hasProperty, isArray,
7    isExpression, isFunctionLikeKind, isNumericLiteral, isPropertyAccessExpression, isPropertyDeclaration,
8    isPropertySignature, isTrivia, Node, positionIsASICandidate, SemicolonPreference, SyntaxKind, typeKeywords,
9    YieldExpression,
10} from "../_namespaces/ts";
11
12/** @internal */
13export interface RuleSpec {
14    readonly leftTokenRange: TokenRange;
15    readonly rightTokenRange: TokenRange;
16    readonly rule: Rule;
17}
18
19/** @internal */
20export function getAllRules(): RuleSpec[] {
21    const allTokens: SyntaxKind[] = [];
22    for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) {
23        if (token !== SyntaxKind.EndOfFileToken) {
24            allTokens.push(token);
25        }
26    }
27    function anyTokenExcept(...tokens: SyntaxKind[]): TokenRange {
28        return { tokens: allTokens.filter(t => !tokens.some(t2 => t2 === t)), isSpecific: false };
29    }
30
31    const anyToken: TokenRange = { tokens: allTokens, isSpecific: false };
32    const anyTokenIncludingMultilineComments = tokenRangeFrom([...allTokens, SyntaxKind.MultiLineCommentTrivia]);
33    const anyTokenIncludingEOF = tokenRangeFrom([...allTokens, SyntaxKind.EndOfFileToken]);
34    const keywords = tokenRangeFromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword);
35    const binaryOperators = tokenRangeFromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator);
36    const binaryKeywordOperators = [SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword];
37    const unaryPrefixOperators = [SyntaxKind.PlusPlusToken, SyntaxKind.MinusMinusToken, SyntaxKind.TildeToken, SyntaxKind.ExclamationToken];
38    const unaryPrefixExpressions = [
39        SyntaxKind.NumericLiteral, SyntaxKind.BigIntLiteral, SyntaxKind.Identifier, SyntaxKind.OpenParenToken,
40        SyntaxKind.OpenBracketToken, SyntaxKind.OpenBraceToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword];
41    const unaryPreincrementExpressions = [SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword];
42    const unaryPostincrementExpressions = [SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword];
43    const unaryPredecrementExpressions = [SyntaxKind.Identifier, SyntaxKind.OpenParenToken, SyntaxKind.ThisKeyword, SyntaxKind.NewKeyword];
44    const unaryPostdecrementExpressions = [SyntaxKind.Identifier, SyntaxKind.CloseParenToken, SyntaxKind.CloseBracketToken, SyntaxKind.NewKeyword];
45    const comments = [SyntaxKind.SingleLineCommentTrivia, SyntaxKind.MultiLineCommentTrivia];
46    const typeNames = [SyntaxKind.Identifier, ...typeKeywords];
47
48    // Place a space before open brace in a function declaration
49    // TypeScript: Function can have return types, which can be made of tons of different token kinds
50    const functionOpenBraceLeftTokenRange = anyTokenIncludingMultilineComments;
51
52    // Place a space before open brace in a TypeScript declaration that has braces as children (class, module, enum, etc)
53    const typeScriptOpenBraceLeftTokenRange = tokenRangeFrom([SyntaxKind.Identifier, SyntaxKind.MultiLineCommentTrivia, SyntaxKind.ClassKeyword, SyntaxKind.StructKeyword, SyntaxKind.ExportKeyword, SyntaxKind.ImportKeyword]);
54
55    // Place a space before open brace in a control flow construct
56    const controlOpenBraceLeftTokenRange = tokenRangeFrom([SyntaxKind.CloseParenToken, SyntaxKind.MultiLineCommentTrivia, SyntaxKind.DoKeyword, SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword, SyntaxKind.ElseKeyword]);
57
58    // These rules are higher in priority than user-configurable
59    const highPriorityCommonRules = [
60        // Leave comments alone
61        rule("IgnoreBeforeComment", anyToken, comments, anyContext, RuleAction.StopProcessingSpaceActions),
62        rule("IgnoreAfterLineComment", SyntaxKind.SingleLineCommentTrivia, anyToken, anyContext, RuleAction.StopProcessingSpaceActions),
63
64        rule("NotSpaceBeforeColon", anyToken, SyntaxKind.ColonToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteSpace),
65        rule("SpaceAfterColon", SyntaxKind.ColonToken, anyToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.InsertSpace),
66        rule("NoSpaceBeforeQuestionMark", anyToken, SyntaxKind.QuestionToken, [isNonJsxSameLineTokenContext, isNotBinaryOpContext, isNotTypeAnnotationContext], RuleAction.DeleteSpace),
67        // insert space after '?' only when it is used in conditional operator
68        rule("SpaceAfterQuestionMarkInConditionalOperator", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext, isConditionalOperatorContext], RuleAction.InsertSpace),
69
70        // in other cases there should be no space between '?' and next token
71        rule("NoSpaceAfterQuestionMark", SyntaxKind.QuestionToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
72
73        rule("NoSpaceBeforeDot", anyToken, [SyntaxKind.DotToken, SyntaxKind.QuestionDotToken], [isNonJsxSameLineTokenContext, isNotPropertyAccessOnIntegerLiteral], RuleAction.DeleteSpace),
74        rule("NoSpaceAfterDot", [SyntaxKind.DotToken, SyntaxKind.QuestionDotToken], anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
75
76        rule("NoSpaceBetweenImportParenInImportType", SyntaxKind.ImportKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isImportTypeContext], RuleAction.DeleteSpace),
77
78        // Special handling of unary operators.
79        // Prefix operators generally shouldn't have a space between
80        // them and their target unary expression.
81        rule("NoSpaceAfterUnaryPrefixOperator", unaryPrefixOperators, unaryPrefixExpressions, [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace),
82        rule("NoSpaceAfterUnaryPreincrementOperator", SyntaxKind.PlusPlusToken, unaryPreincrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
83        rule("NoSpaceAfterUnaryPredecrementOperator", SyntaxKind.MinusMinusToken, unaryPredecrementExpressions, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
84        rule("NoSpaceBeforeUnaryPostincrementOperator", unaryPostincrementExpressions, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isNotStatementConditionContext], RuleAction.DeleteSpace),
85        rule("NoSpaceBeforeUnaryPostdecrementOperator", unaryPostdecrementExpressions, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isNotStatementConditionContext], RuleAction.DeleteSpace),
86
87        // More unary operator special-casing.
88        // DevDiv 181814: Be careful when removing leading whitespace
89        // around unary operators.  Examples:
90        //      1 - -2  --X--> 1--2
91        //      a + ++b --X--> a+++b
92        rule("SpaceAfterPostincrementWhenFollowedByAdd", SyntaxKind.PlusPlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
93        rule("SpaceAfterAddWhenFollowedByUnaryPlus", SyntaxKind.PlusToken, SyntaxKind.PlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
94        rule("SpaceAfterAddWhenFollowedByPreincrement", SyntaxKind.PlusToken, SyntaxKind.PlusPlusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
95        rule("SpaceAfterPostdecrementWhenFollowedBySubtract", SyntaxKind.MinusMinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
96        rule("SpaceAfterSubtractWhenFollowedByUnaryMinus", SyntaxKind.MinusToken, SyntaxKind.MinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
97        rule("SpaceAfterSubtractWhenFollowedByPredecrement", SyntaxKind.MinusToken, SyntaxKind.MinusMinusToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
98
99        rule("NoSpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, [SyntaxKind.CommaToken, SyntaxKind.SemicolonToken], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
100        // For functions and control block place } on a new line [multi-line rule]
101        rule("NewLineBeforeCloseBraceInBlockContext", anyTokenIncludingMultilineComments, SyntaxKind.CloseBraceToken, [isMultilineBlockContext], RuleAction.InsertNewLine),
102
103        // Space/new line after }.
104        rule("SpaceAfterCloseBrace", SyntaxKind.CloseBraceToken, anyTokenExcept(SyntaxKind.CloseParenToken), [isNonJsxSameLineTokenContext, isAfterCodeBlockContext], RuleAction.InsertSpace),
105        // Special case for (}, else) and (}, while) since else & while tokens are not part of the tree which makes SpaceAfterCloseBrace rule not applied
106        // Also should not apply to })
107        rule("SpaceBetweenCloseBraceAndElse", SyntaxKind.CloseBraceToken, SyntaxKind.ElseKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
108        rule("SpaceBetweenCloseBraceAndWhile", SyntaxKind.CloseBraceToken, SyntaxKind.WhileKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
109        rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace),
110
111        // 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];'
112        rule("SpaceAfterConditionalClosingParen", SyntaxKind.CloseParenToken, SyntaxKind.OpenBracketToken, [isControlDeclContext], RuleAction.InsertSpace),
113
114        rule("NoSpaceBetweenFunctionKeywordAndStar", SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.DeleteSpace),
115        rule("SpaceAfterStarInGeneratorDeclaration", SyntaxKind.AsteriskToken, SyntaxKind.Identifier, [isFunctionDeclarationOrFunctionExpressionContext], RuleAction.InsertSpace),
116
117        rule("SpaceAfterFunctionInFuncDecl", SyntaxKind.FunctionKeyword, anyToken, [isFunctionDeclContext], RuleAction.InsertSpace),
118        // Insert new line after { and before } in multi-line contexts.
119        rule("NewLineAfterOpenBraceInBlockContext", SyntaxKind.OpenBraceToken, anyToken, [isMultilineBlockContext], RuleAction.InsertNewLine),
120
121        // 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.
122        // Though, we do extra check on the context to make sure we are dealing with get/set node. Example:
123        //      get x() {}
124        //      set x(val) {}
125        rule("SpaceAfterGetSetInMember", [SyntaxKind.GetKeyword, SyntaxKind.SetKeyword], SyntaxKind.Identifier, [isFunctionDeclContext], RuleAction.InsertSpace),
126
127        rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteSpace),
128        rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.InsertSpace),
129
130        rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
131        rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
132        rule("SpaceAfterLetConstInVariableDeclaration", [SyntaxKind.LetKeyword, SyntaxKind.ConstKeyword], anyToken, [isNonJsxSameLineTokenContext, isStartOfVariableDeclarationList], RuleAction.InsertSpace),
133        rule("NoSpaceBeforeOpenParenInFuncCall", anyToken, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isFunctionCallOrNewContext, isPreviousTokenNotComma], RuleAction.DeleteSpace),
134
135        // Special case for binary operators (that are keywords). For these we have to add a space and shouldn't follow any user options.
136        rule("SpaceBeforeBinaryKeywordOperator", anyToken, binaryKeywordOperators, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
137        rule("SpaceAfterBinaryKeywordOperator", binaryKeywordOperators, anyToken, [isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
138
139        rule("SpaceAfterVoidOperator", SyntaxKind.VoidKeyword, anyToken, [isNonJsxSameLineTokenContext, isVoidOpContext], RuleAction.InsertSpace),
140
141        // Async-await
142        rule("SpaceBetweenAsyncAndOpenParen", SyntaxKind.AsyncKeyword, SyntaxKind.OpenParenToken, [isArrowFunctionContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
143        rule("SpaceBetweenAsyncAndFunctionKeyword", SyntaxKind.AsyncKeyword, [SyntaxKind.FunctionKeyword, SyntaxKind.Identifier], [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
144
145        // Template string
146        rule("NoSpaceBetweenTagAndTemplateString", [SyntaxKind.Identifier, SyntaxKind.CloseParenToken], [SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead], [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
147
148        // JSX opening elements
149        rule("SpaceBeforeJsxAttribute", anyToken, SyntaxKind.Identifier, [isNextTokenParentJsxAttribute, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
150        rule("SpaceBeforeSlashInJsxOpeningElement", anyToken, SyntaxKind.SlashToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
151        rule("NoSpaceBeforeGreaterThanTokenInJsxOpeningElement", SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken, [isJsxSelfClosingElementContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
152        rule("NoSpaceBeforeEqualInJsxAttribute", anyToken, SyntaxKind.EqualsToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
153        rule("NoSpaceAfterEqualInJsxAttribute", SyntaxKind.EqualsToken, anyToken, [isJsxAttributeContext, isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
154
155        // TypeScript-specific rules
156        // Use of module as a function call. e.g.: import m2 = module("m2");
157        rule("NoSpaceAfterModuleImport", [SyntaxKind.ModuleKeyword, SyntaxKind.RequireKeyword], SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
158        // Add a space around certain TypeScript keywords
159        rule(
160            "SpaceAfterCertainTypeScriptKeywords",
161            [
162                SyntaxKind.AbstractKeyword,
163                SyntaxKind.AccessorKeyword,
164                SyntaxKind.ClassKeyword,
165                SyntaxKind.StructKeyword,
166                SyntaxKind.DeclareKeyword,
167                SyntaxKind.DefaultKeyword,
168                SyntaxKind.EnumKeyword,
169                SyntaxKind.ExportKeyword,
170                SyntaxKind.ExtendsKeyword,
171                SyntaxKind.GetKeyword,
172                SyntaxKind.ImplementsKeyword,
173                SyntaxKind.ImportKeyword,
174                SyntaxKind.InterfaceKeyword,
175                SyntaxKind.ModuleKeyword,
176                SyntaxKind.NamespaceKeyword,
177                SyntaxKind.PrivateKeyword,
178                SyntaxKind.PublicKeyword,
179                SyntaxKind.ProtectedKeyword,
180                SyntaxKind.ReadonlyKeyword,
181                SyntaxKind.SetKeyword,
182                SyntaxKind.StaticKeyword,
183                SyntaxKind.TypeKeyword,
184                SyntaxKind.FromKeyword,
185                SyntaxKind.KeyOfKeyword,
186                SyntaxKind.InferKeyword,
187            ],
188            anyToken,
189            [isNonJsxSameLineTokenContext],
190            RuleAction.InsertSpace),
191        rule(
192            "SpaceBeforeCertainTypeScriptKeywords",
193            anyToken,
194            [SyntaxKind.ExtendsKeyword, SyntaxKind.ImplementsKeyword, SyntaxKind.FromKeyword],
195            [isNonJsxSameLineTokenContext],
196            RuleAction.InsertSpace),
197        // Treat string literals in module names as identifiers, and add a space between the literal and the opening Brace braces, e.g.: module "m2" {
198        rule("SpaceAfterModuleName", SyntaxKind.StringLiteral, SyntaxKind.OpenBraceToken, [isModuleDeclContext], RuleAction.InsertSpace),
199
200        // Lambda expressions
201        rule("SpaceBeforeArrow", anyToken, SyntaxKind.EqualsGreaterThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
202        rule("SpaceAfterArrow", SyntaxKind.EqualsGreaterThanToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
203
204        // Optional parameters and let args
205        rule("NoSpaceAfterEllipsis", SyntaxKind.DotDotDotToken, SyntaxKind.Identifier, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
206        rule("NoSpaceAfterOptionalParameters", SyntaxKind.QuestionToken, [SyntaxKind.CloseParenToken, SyntaxKind.CommaToken], [isNonJsxSameLineTokenContext, isNotBinaryOpContext], RuleAction.DeleteSpace),
207
208        // Remove spaces in empty interface literals. e.g.: x: {}
209        rule("NoSpaceBetweenEmptyInterfaceBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectTypeContext], RuleAction.DeleteSpace),
210
211        // generics and type assertions
212        rule("NoSpaceBeforeOpenAngularBracket", typeNames, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
213        rule("NoSpaceBetweenCloseParenAndAngularBracket", SyntaxKind.CloseParenToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
214        rule("NoSpaceAfterOpenAngularBracket", SyntaxKind.LessThanToken, anyToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
215        rule("NoSpaceBeforeCloseAngularBracket", anyToken, SyntaxKind.GreaterThanToken, [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext], RuleAction.DeleteSpace),
216        rule("NoSpaceAfterCloseAngularBracket",
217            SyntaxKind.GreaterThanToken,
218            [SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.GreaterThanToken, SyntaxKind.CommaToken],
219            [isNonJsxSameLineTokenContext, isTypeArgumentOrParameterOrAssertionContext, isNotFunctionDeclContext /*To prevent an interference with the SpaceBeforeOpenParenInFuncDecl rule*/],
220            RuleAction.DeleteSpace),
221
222        // decorators
223        rule("SpaceBeforeAt", [SyntaxKind.CloseParenToken, SyntaxKind.Identifier], SyntaxKind.AtToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
224        rule("NoSpaceAfterAt", SyntaxKind.AtToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
225        // Insert space after @ in decorator
226        rule("SpaceAfterDecorator",
227            anyToken,
228            [
229                SyntaxKind.AbstractKeyword,
230                SyntaxKind.Identifier,
231                SyntaxKind.ExportKeyword,
232                SyntaxKind.DefaultKeyword,
233                SyntaxKind.ClassKeyword,
234                SyntaxKind.StructKeyword,
235                SyntaxKind.StaticKeyword,
236                SyntaxKind.PublicKeyword,
237                SyntaxKind.PrivateKeyword,
238                SyntaxKind.ProtectedKeyword,
239                SyntaxKind.GetKeyword,
240                SyntaxKind.SetKeyword,
241                SyntaxKind.OpenBracketToken,
242                SyntaxKind.AsteriskToken,
243            ],
244            [isEndOfDecoratorContextOnSameLine],
245            RuleAction.InsertSpace),
246
247        rule("NoSpaceBeforeNonNullAssertionOperator", anyToken, SyntaxKind.ExclamationToken, [isNonJsxSameLineTokenContext, isNonNullAssertionContext], RuleAction.DeleteSpace),
248        rule("NoSpaceAfterNewKeywordOnConstructorSignature", SyntaxKind.NewKeyword, SyntaxKind.OpenParenToken, [isNonJsxSameLineTokenContext, isConstructorSignatureContext], RuleAction.DeleteSpace),
249        rule("SpaceLessThanAndNonJSXTypeAnnotation", SyntaxKind.LessThanToken, SyntaxKind.LessThanToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
250    ];
251
252    // These rules are applied after high priority
253    const userConfigurableRules = [
254        // Treat constructor as an identifier in a function declaration, and remove spaces between constructor and following left parentheses
255        rule("SpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
256        rule("NoSpaceAfterConstructor", SyntaxKind.ConstructorKeyword, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterConstructor"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
257
258        rule("SpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionEnabled("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNextTokenNotCloseBracket, isNextTokenNotCloseParen], RuleAction.InsertSpace),
259        rule("NoSpaceAfterComma", SyntaxKind.CommaToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterCommaDelimiter"), isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext], RuleAction.DeleteSpace),
260
261        // Insert space after function keyword for anonymous functions
262        rule("SpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.InsertSpace),
263        rule("NoSpaceAfterAnonymousFunctionKeyword", [SyntaxKind.FunctionKeyword, SyntaxKind.AsteriskToken], SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterFunctionKeywordForAnonymousFunctions"), isFunctionDeclContext], RuleAction.DeleteSpace),
264
265        // Insert space after keywords in control flow statements
266        rule("SpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.InsertSpace),
267        rule("NoSpaceAfterKeywordInControl", keywords, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterKeywordsInControlFlowStatements"), isControlDeclContext], RuleAction.DeleteSpace),
268
269        // Insert space after opening and before closing nonempty parenthesis
270        rule("SpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
271        rule("SpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
272        rule("SpaceBetweenOpenParens", SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
273        rule("NoSpaceBetweenParens", SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
274        rule("NoSpaceAfterOpenParen", SyntaxKind.OpenParenToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
275        rule("NoSpaceBeforeCloseParen", anyToken, SyntaxKind.CloseParenToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
276
277        // Insert space after opening and before closing nonempty brackets
278        rule("SpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
279        rule("SpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
280        rule("NoSpaceBetweenBrackets", SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
281        rule("NoSpaceAfterOpenBracket", SyntaxKind.OpenBracketToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
282        rule("NoSpaceBeforeCloseBracket", anyToken, SyntaxKind.CloseBracketToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
283
284        // Insert a space after { and before } in single-line contexts, but remove space from empty object literals {}.
285        rule("SpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace),
286        rule("SpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isBraceWrappedContext], RuleAction.InsertSpace),
287        rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isNonJsxSameLineTokenContext, isObjectContext], RuleAction.DeleteSpace),
288        rule("NoSpaceAfterOpenBrace", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
289        rule("NoSpaceBeforeCloseBrace", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
290
291        // Insert a space after opening and before closing empty brace brackets
292        rule("SpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingEmptyBraces")], RuleAction.InsertSpace),
293        rule("NoSpaceBetweenEmptyBraceBrackets", SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, [isOptionDisabled("insertSpaceAfterOpeningAndBeforeClosingEmptyBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
294
295        // Insert space after opening and before closing template string braces
296        rule("SpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxTextContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
297        rule("SpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
298        rule("NoSpaceAfterTemplateHeadAndMiddle", [SyntaxKind.TemplateHead, SyntaxKind.TemplateMiddle], anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxTextContext], RuleAction.DeleteSpace, RuleFlags.CanDeleteNewLines),
299        rule("NoSpaceBeforeTemplateMiddleAndTail", anyToken, [SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail], [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces"), isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
300
301        // No space after { and before } in JSX expression
302        rule("SpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace),
303        rule("SpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.InsertSpace),
304        rule("NoSpaceAfterOpenBraceInJsxExpression", SyntaxKind.OpenBraceToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace),
305        rule("NoSpaceBeforeCloseBraceInJsxExpression", anyToken, SyntaxKind.CloseBraceToken, [isOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces"), isNonJsxSameLineTokenContext, isJsxExpressionContext], RuleAction.DeleteSpace),
306
307        // Insert space after semicolon in for statement
308        rule("SpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionEnabled("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.InsertSpace),
309        rule("NoSpaceAfterSemicolonInFor", SyntaxKind.SemicolonToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterSemicolonInForStatements"), isNonJsxSameLineTokenContext, isForContext], RuleAction.DeleteSpace),
310
311        // Insert space before and after binary operators
312        rule("SpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
313        rule("SpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionEnabled("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.InsertSpace),
314        rule("NoSpaceBeforeBinaryOperator", anyToken, binaryOperators, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace),
315        rule("NoSpaceAfterBinaryOperator", binaryOperators, anyToken, [isOptionDisabledOrUndefined("insertSpaceBeforeAndAfterBinaryOperators"), isNonJsxSameLineTokenContext, isBinaryOpContext], RuleAction.DeleteSpace),
316
317        rule("SpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionEnabled("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.InsertSpace),
318        rule("NoSpaceBeforeOpenParenInFuncDecl", anyToken, SyntaxKind.OpenParenToken, [isOptionDisabledOrUndefined("insertSpaceBeforeFunctionParenthesis"), isNonJsxSameLineTokenContext, isFunctionDeclContext], RuleAction.DeleteSpace),
319
320        // Open Brace braces after control block
321        rule("NewLineBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
322
323        // Open Brace braces after function
324        // TypeScript: Function can have return types, which can be made of tons of different token kinds
325        rule("NewLineBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
326        // Open Brace braces after TypeScript module/class/interface
327        rule("NewLineBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionEnabled("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isBeforeMultilineBlockContext], RuleAction.InsertNewLine, RuleFlags.CanDeleteNewLines),
328
329        rule("SpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionEnabled("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.InsertSpace),
330        rule("NoSpaceAfterTypeAssertion", SyntaxKind.GreaterThanToken, anyToken, [isOptionDisabledOrUndefined("insertSpaceAfterTypeAssertion"), isNonJsxSameLineTokenContext, isTypeAssertionContext], RuleAction.DeleteSpace),
331
332        rule("SpaceBeforeTypeAnnotation", anyToken, [SyntaxKind.QuestionToken, SyntaxKind.ColonToken], [isOptionEnabled("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.InsertSpace),
333        rule("NoSpaceBeforeTypeAnnotation", anyToken, [SyntaxKind.QuestionToken, SyntaxKind.ColonToken], [isOptionDisabledOrUndefined("insertSpaceBeforeTypeAnnotation"), isNonJsxSameLineTokenContext, isTypeAnnotationContext], RuleAction.DeleteSpace),
334
335        rule("NoOptionalSemicolon", SyntaxKind.SemicolonToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Remove), isSemicolonDeletionContext], RuleAction.DeleteToken),
336        rule("OptionalSemicolon", anyToken, anyTokenIncludingEOF, [optionEquals("semicolons", SemicolonPreference.Insert), isSemicolonInsertionContext], RuleAction.InsertTrailingSemicolon),
337    ];
338
339    // These rules are lower in priority than user-configurable. Rules earlier in this list have priority over rules later in the list.
340    const lowPriorityCommonRules = [
341        // Space after keyword but not before ; or : or ?
342        rule("NoSpaceBeforeSemicolon", anyToken, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
343
344        rule("SpaceBeforeOpenBraceInControl", controlOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForControlBlocks"), isControlDeclContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
345        rule("SpaceBeforeOpenBraceInFunction", functionOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isFunctionDeclContext, isBeforeBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
346        rule("SpaceBeforeOpenBraceInTypeScriptDeclWithBlock", typeScriptOpenBraceLeftTokenRange, SyntaxKind.OpenBraceToken, [isOptionDisabledOrUndefinedOrTokensOnSameLine("placeOpenBraceOnNewLineForFunctions"), isTypeScriptDeclWithBlockContext, isNotFormatOnEnter, isSameLineTokenOrBeforeBlockContext], RuleAction.InsertSpace, RuleFlags.CanDeleteNewLines),
347
348        rule("NoSpaceBeforeComma", anyToken, SyntaxKind.CommaToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
349
350        // No space before and after indexer `x[]`
351        rule("NoSpaceBeforeOpenBracket", anyTokenExcept(SyntaxKind.AsyncKeyword, SyntaxKind.CaseKeyword), SyntaxKind.OpenBracketToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace),
352        rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.DeleteSpace),
353        rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
354
355        // Remove extra space between for and await
356        rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
357
358        // Add a space between statements. All keywords except (do,else,case) has open/close parens after them.
359        // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any]
360        rule(
361            "SpaceBetweenStatements",
362            [SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword],
363            anyToken,
364            [isNonJsxSameLineTokenContext, isNonJsxElementOrFragmentContext, isNotForContext],
365            RuleAction.InsertSpace),
366        // This low-pri rule takes care of "try {", "catch {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter.
367        rule("SpaceAfterTryCatchFinally", [SyntaxKind.TryKeyword, SyntaxKind.CatchKeyword, SyntaxKind.FinallyKeyword], SyntaxKind.OpenBraceToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace),
368    ];
369
370    return [
371        ...highPriorityCommonRules,
372        ...userConfigurableRules,
373        ...lowPriorityCommonRules,
374    ];
375}
376
377/**
378 * A rule takes a two tokens (left/right) and a particular context
379 * for which you're meant to look at them. You then declare what should the
380 * whitespace annotation be between these tokens via the action param.
381 *
382 * @param debugName Name to print
383 * @param left The left side of the comparison
384 * @param right The right side of the comparison
385 * @param context A set of filters to narrow down the space in which this formatter rule applies
386 * @param action a declaration of the expected whitespace
387 * @param flags whether the rule deletes a line or not, defaults to no-op
388 */
389function rule(
390    debugName: string,
391    left: SyntaxKind | readonly SyntaxKind[] | TokenRange,
392    right: SyntaxKind | readonly SyntaxKind[] | TokenRange,
393    context: readonly ContextPredicate[],
394    action: RuleAction,
395    flags: RuleFlags = RuleFlags.None,
396): RuleSpec {
397    return { leftTokenRange: toTokenRange(left), rightTokenRange: toTokenRange(right), rule: { debugName, context, action, flags } };
398}
399
400function tokenRangeFrom(tokens: readonly SyntaxKind[]): TokenRange {
401    return { tokens, isSpecific: true };
402}
403
404function toTokenRange(arg: SyntaxKind | readonly SyntaxKind[] | TokenRange): TokenRange {
405    return typeof arg === "number" ? tokenRangeFrom([arg]) : isArray(arg) ? tokenRangeFrom(arg) : arg;
406}
407
408function tokenRangeFromRange(from: SyntaxKind, to: SyntaxKind, except: readonly SyntaxKind[] = []): TokenRange {
409    const tokens: SyntaxKind[] = [];
410    for (let token = from; token <= to; token++) {
411        if (!contains(except, token)) {
412            tokens.push(token);
413        }
414    }
415    return tokenRangeFrom(tokens);
416}
417
418///
419/// Contexts
420///
421
422function optionEquals<K extends keyof FormatCodeSettings>(optionName: K, optionValue: FormatCodeSettings[K]): (context: FormattingContext) => boolean {
423    return (context) => context.options && context.options[optionName] === optionValue;
424}
425
426function isOptionEnabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
427    return (context) => context.options && hasProperty(context.options, optionName) && !!context.options[optionName];
428}
429
430function isOptionDisabled(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
431    return (context) => context.options && hasProperty(context.options, optionName) && !context.options[optionName];
432}
433
434function isOptionDisabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
435    return (context) => !context.options || !hasProperty(context.options, optionName) || !context.options[optionName];
436}
437
438function isOptionDisabledOrUndefinedOrTokensOnSameLine(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
439    return (context) => !context.options || !hasProperty(context.options, optionName) || !context.options[optionName] || context.TokensAreOnSameLine();
440}
441
442function isOptionEnabledOrUndefined(optionName: keyof FormatCodeSettings): (context: FormattingContext) => boolean {
443    return (context) => !context.options || !hasProperty(context.options, optionName) || !!context.options[optionName];
444}
445
446function isForContext(context: FormattingContext): boolean {
447    return context.contextNode.kind === SyntaxKind.ForStatement;
448}
449
450function isNotForContext(context: FormattingContext): boolean {
451    return !isForContext(context);
452}
453
454function isBinaryOpContext(context: FormattingContext): boolean {
455    switch (context.contextNode.kind) {
456        case SyntaxKind.BinaryExpression:
457            return (context.contextNode as BinaryExpression).operatorToken.kind !== SyntaxKind.CommaToken;
458        case SyntaxKind.ConditionalExpression:
459        case SyntaxKind.ConditionalType:
460        case SyntaxKind.AsExpression:
461        case SyntaxKind.ExportSpecifier:
462        case SyntaxKind.ImportSpecifier:
463        case SyntaxKind.TypePredicate:
464        case SyntaxKind.UnionType:
465        case SyntaxKind.IntersectionType:
466        case SyntaxKind.SatisfiesExpression:
467            return true;
468
469        // equals in binding elements: function foo([[x, y] = [1, 2]])
470        case SyntaxKind.BindingElement:
471        // equals in type X = ...
472        // falls through
473        case SyntaxKind.TypeAliasDeclaration:
474        // equal in import a = module('a');
475        // falls through
476        case SyntaxKind.ImportEqualsDeclaration:
477        // equal in export = 1
478        // falls through
479        case SyntaxKind.ExportAssignment:
480        // equal in let a = 0
481        // falls through
482        case SyntaxKind.VariableDeclaration:
483        // equal in p = 0
484        // falls through
485        case SyntaxKind.Parameter:
486        case SyntaxKind.EnumMember:
487        case SyntaxKind.PropertyDeclaration:
488        case SyntaxKind.PropertySignature:
489            return context.currentTokenSpan.kind === SyntaxKind.EqualsToken || context.nextTokenSpan.kind === SyntaxKind.EqualsToken;
490        // "in" keyword in for (let x in []) { }
491        case SyntaxKind.ForInStatement:
492        // "in" keyword in [P in keyof T]: T[P]
493        // falls through
494        case SyntaxKind.TypeParameter:
495            return context.currentTokenSpan.kind === SyntaxKind.InKeyword || context.nextTokenSpan.kind === SyntaxKind.InKeyword || context.currentTokenSpan.kind === SyntaxKind.EqualsToken || context.nextTokenSpan.kind === SyntaxKind.EqualsToken;
496        // Technically, "of" is not a binary operator, but format it the same way as "in"
497        case SyntaxKind.ForOfStatement:
498            return context.currentTokenSpan.kind === SyntaxKind.OfKeyword || context.nextTokenSpan.kind === SyntaxKind.OfKeyword;
499    }
500    return false;
501}
502
503function isNotBinaryOpContext(context: FormattingContext): boolean {
504    return !isBinaryOpContext(context);
505}
506
507function isNotTypeAnnotationContext(context: FormattingContext): boolean {
508    return !isTypeAnnotationContext(context);
509}
510
511function isTypeAnnotationContext(context: FormattingContext): boolean {
512    const contextKind = context.contextNode.kind;
513    return contextKind === SyntaxKind.PropertyDeclaration ||
514        contextKind === SyntaxKind.PropertySignature ||
515        contextKind === SyntaxKind.Parameter ||
516        contextKind === SyntaxKind.VariableDeclaration ||
517        isFunctionLikeKind(contextKind);
518}
519
520function isConditionalOperatorContext(context: FormattingContext): boolean {
521    return context.contextNode.kind === SyntaxKind.ConditionalExpression ||
522            context.contextNode.kind === SyntaxKind.ConditionalType;
523}
524
525function isSameLineTokenOrBeforeBlockContext(context: FormattingContext): boolean {
526    return context.TokensAreOnSameLine() || isBeforeBlockContext(context);
527}
528
529function isBraceWrappedContext(context: FormattingContext): boolean {
530    return context.contextNode.kind === SyntaxKind.ObjectBindingPattern ||
531        context.contextNode.kind === SyntaxKind.MappedType ||
532        isSingleLineBlockContext(context);
533}
534
535// This check is done before an open brace in a control construct, a function, or a typescript block declaration
536function isBeforeMultilineBlockContext(context: FormattingContext): boolean {
537    return isBeforeBlockContext(context) && !(context.NextNodeAllOnSameLine() || context.NextNodeBlockIsOnOneLine());
538}
539
540function isMultilineBlockContext(context: FormattingContext): boolean {
541    return isBlockContext(context) && !(context.ContextNodeAllOnSameLine() || context.ContextNodeBlockIsOnOneLine());
542}
543
544function isSingleLineBlockContext(context: FormattingContext): boolean {
545    return isBlockContext(context) && (context.ContextNodeAllOnSameLine() || context.ContextNodeBlockIsOnOneLine());
546}
547
548function isBlockContext(context: FormattingContext): boolean {
549    return nodeIsBlockContext(context.contextNode);
550}
551
552function isBeforeBlockContext(context: FormattingContext): boolean {
553    return nodeIsBlockContext(context.nextTokenParent);
554}
555
556// IMPORTANT!!! This method must return true ONLY for nodes with open and close braces as immediate children
557function nodeIsBlockContext(node: Node): boolean {
558    if (nodeIsTypeScriptDeclWithBlockContext(node)) {
559        // This means we are in a context that looks like a block to the user, but in the grammar is actually not a node (it's a class, module, enum, object type literal, etc).
560        return true;
561    }
562
563    switch (node.kind) {
564        case SyntaxKind.Block:
565        case SyntaxKind.CaseBlock:
566        case SyntaxKind.ObjectLiteralExpression:
567        case SyntaxKind.ModuleBlock:
568            return true;
569    }
570
571    return false;
572}
573
574function isFunctionDeclContext(context: FormattingContext): boolean {
575    switch (context.contextNode.kind) {
576        case SyntaxKind.FunctionDeclaration:
577        case SyntaxKind.MethodDeclaration:
578        case SyntaxKind.MethodSignature:
579        // case SyntaxKind.MemberFunctionDeclaration:
580        // falls through
581        case SyntaxKind.GetAccessor:
582        case SyntaxKind.SetAccessor:
583        // case SyntaxKind.MethodSignature:
584        // falls through
585        case SyntaxKind.CallSignature:
586        case SyntaxKind.FunctionExpression:
587        case SyntaxKind.Constructor:
588        case SyntaxKind.ArrowFunction:
589        // case SyntaxKind.ConstructorDeclaration:
590        // case SyntaxKind.SimpleArrowFunctionExpression:
591        // case SyntaxKind.ParenthesizedArrowFunctionExpression:
592        // falls through
593        case SyntaxKind.InterfaceDeclaration: // This one is not truly a function, but for formatting purposes, it acts just like one
594            return true;
595    }
596
597    return false;
598}
599
600function isNotFunctionDeclContext(context: FormattingContext): boolean {
601    return !isFunctionDeclContext(context);
602}
603
604function isFunctionDeclarationOrFunctionExpressionContext(context: FormattingContext): boolean {
605    return context.contextNode.kind === SyntaxKind.FunctionDeclaration || context.contextNode.kind === SyntaxKind.FunctionExpression;
606}
607
608function isTypeScriptDeclWithBlockContext(context: FormattingContext): boolean {
609    return nodeIsTypeScriptDeclWithBlockContext(context.contextNode);
610}
611
612function nodeIsTypeScriptDeclWithBlockContext(node: Node): boolean {
613    switch (node.kind) {
614        case SyntaxKind.ClassDeclaration:
615        case SyntaxKind.StructDeclaration:
616        case SyntaxKind.ClassExpression:
617        case SyntaxKind.InterfaceDeclaration:
618        case SyntaxKind.EnumDeclaration:
619        case SyntaxKind.TypeLiteral:
620        case SyntaxKind.ModuleDeclaration:
621        case SyntaxKind.ExportDeclaration:
622        case SyntaxKind.NamedExports:
623        case SyntaxKind.ImportDeclaration:
624        case SyntaxKind.NamedImports:
625            return true;
626    }
627
628    return false;
629}
630
631function isAfterCodeBlockContext(context: FormattingContext): boolean {
632    switch (context.currentTokenParent.kind) {
633        case SyntaxKind.ClassDeclaration:
634        case SyntaxKind.StructDeclaration:
635        case SyntaxKind.ModuleDeclaration:
636        case SyntaxKind.EnumDeclaration:
637        case SyntaxKind.CatchClause:
638        case SyntaxKind.ModuleBlock:
639        case SyntaxKind.SwitchStatement:
640            return true;
641        case SyntaxKind.Block: {
642            const blockParent = context.currentTokenParent.parent;
643            // In a codefix scenario, we can't rely on parents being set. So just always return true.
644            if (!blockParent || blockParent.kind !== SyntaxKind.ArrowFunction && blockParent.kind !== SyntaxKind.FunctionExpression) {
645                return true;
646            }
647        }
648    }
649    return false;
650}
651
652function isControlDeclContext(context: FormattingContext): boolean {
653    switch (context.contextNode.kind) {
654        case SyntaxKind.IfStatement:
655        case SyntaxKind.SwitchStatement:
656        case SyntaxKind.ForStatement:
657        case SyntaxKind.ForInStatement:
658        case SyntaxKind.ForOfStatement:
659        case SyntaxKind.WhileStatement:
660        case SyntaxKind.TryStatement:
661        case SyntaxKind.DoStatement:
662        case SyntaxKind.WithStatement:
663        // TODO
664        // case SyntaxKind.ElseClause:
665        // falls through
666        case SyntaxKind.CatchClause:
667            return true;
668
669        default:
670            return false;
671    }
672}
673
674function isObjectContext(context: FormattingContext): boolean {
675    return context.contextNode.kind === SyntaxKind.ObjectLiteralExpression;
676}
677
678function isFunctionCallContext(context: FormattingContext): boolean {
679    return context.contextNode.kind === SyntaxKind.CallExpression;
680}
681
682function isNewContext(context: FormattingContext): boolean {
683    return context.contextNode.kind === SyntaxKind.NewExpression;
684}
685
686function isFunctionCallOrNewContext(context: FormattingContext): boolean {
687    return isFunctionCallContext(context) || isNewContext(context);
688}
689
690function isPreviousTokenNotComma(context: FormattingContext): boolean {
691    return context.currentTokenSpan.kind !== SyntaxKind.CommaToken;
692}
693
694function isNextTokenNotCloseBracket(context: FormattingContext): boolean {
695    return context.nextTokenSpan.kind !== SyntaxKind.CloseBracketToken;
696}
697
698function isNextTokenNotCloseParen(context: FormattingContext): boolean {
699    return context.nextTokenSpan.kind !== SyntaxKind.CloseParenToken;
700}
701
702function isArrowFunctionContext(context: FormattingContext): boolean {
703    return context.contextNode.kind === SyntaxKind.ArrowFunction;
704}
705
706function isImportTypeContext(context: FormattingContext): boolean {
707    return context.contextNode.kind === SyntaxKind.ImportType;
708}
709
710function isNonJsxSameLineTokenContext(context: FormattingContext): boolean {
711    return context.TokensAreOnSameLine() && context.contextNode.kind !== SyntaxKind.JsxText;
712}
713
714function isNonJsxTextContext(context: FormattingContext): boolean {
715    return context.contextNode.kind !== SyntaxKind.JsxText;
716}
717
718function isNonJsxElementOrFragmentContext(context: FormattingContext): boolean {
719    return context.contextNode.kind !== SyntaxKind.JsxElement && context.contextNode.kind !== SyntaxKind.JsxFragment;
720}
721
722function isJsxExpressionContext(context: FormattingContext): boolean {
723    return context.contextNode.kind === SyntaxKind.JsxExpression || context.contextNode.kind === SyntaxKind.JsxSpreadAttribute;
724}
725
726function isNextTokenParentJsxAttribute(context: FormattingContext): boolean {
727    return context.nextTokenParent.kind === SyntaxKind.JsxAttribute;
728}
729
730function isJsxAttributeContext(context: FormattingContext): boolean {
731    return context.contextNode.kind === SyntaxKind.JsxAttribute;
732}
733
734function isJsxSelfClosingElementContext(context: FormattingContext): boolean {
735    return context.contextNode.kind === SyntaxKind.JsxSelfClosingElement;
736}
737
738function isNotBeforeBlockInFunctionDeclarationContext(context: FormattingContext): boolean {
739    return !isFunctionDeclContext(context) && !isBeforeBlockContext(context);
740}
741
742function isEndOfDecoratorContextOnSameLine(context: FormattingContext): boolean {
743    return context.TokensAreOnSameLine() &&
744        hasDecorators(context.contextNode) &&
745        nodeIsInDecoratorContext(context.currentTokenParent) &&
746        !nodeIsInDecoratorContext(context.nextTokenParent);
747}
748
749function nodeIsInDecoratorContext(node: Node): boolean {
750    while (node && isExpression(node)) {
751        node = node.parent;
752    }
753    return node && node.kind === SyntaxKind.Decorator;
754}
755
756function isStartOfVariableDeclarationList(context: FormattingContext): boolean {
757    return context.currentTokenParent.kind === SyntaxKind.VariableDeclarationList &&
758        context.currentTokenParent.getStart(context.sourceFile) === context.currentTokenSpan.pos;
759}
760
761function isNotFormatOnEnter(context: FormattingContext): boolean {
762    return context.formattingRequestKind !== FormattingRequestKind.FormatOnEnter;
763}
764
765function isModuleDeclContext(context: FormattingContext): boolean {
766    return context.contextNode.kind === SyntaxKind.ModuleDeclaration;
767}
768
769function isObjectTypeContext(context: FormattingContext): boolean {
770    return context.contextNode.kind === SyntaxKind.TypeLiteral; // && context.contextNode.parent.kind !== SyntaxKind.InterfaceDeclaration;
771}
772
773function isConstructorSignatureContext(context: FormattingContext): boolean {
774    return context.contextNode.kind === SyntaxKind.ConstructSignature;
775}
776
777function isTypeArgumentOrParameterOrAssertion(token: TextRangeWithKind, parent: Node): boolean {
778    if (token.kind !== SyntaxKind.LessThanToken && token.kind !== SyntaxKind.GreaterThanToken) {
779        return false;
780    }
781    switch (parent.kind) {
782        case SyntaxKind.TypeReference:
783        case SyntaxKind.TypeAssertionExpression:
784        case SyntaxKind.TypeAliasDeclaration:
785        case SyntaxKind.ClassDeclaration:
786        case SyntaxKind.ClassExpression:
787        case SyntaxKind.StructDeclaration:
788        case SyntaxKind.InterfaceDeclaration:
789        case SyntaxKind.FunctionDeclaration:
790        case SyntaxKind.FunctionExpression:
791        case SyntaxKind.ArrowFunction:
792        case SyntaxKind.MethodDeclaration:
793        case SyntaxKind.MethodSignature:
794        case SyntaxKind.CallSignature:
795        case SyntaxKind.ConstructSignature:
796        case SyntaxKind.CallExpression:
797        case SyntaxKind.NewExpression:
798        case SyntaxKind.ExpressionWithTypeArguments:
799            return true;
800        default:
801            return false;
802
803    }
804}
805
806function isTypeArgumentOrParameterOrAssertionContext(context: FormattingContext): boolean {
807    return isTypeArgumentOrParameterOrAssertion(context.currentTokenSpan, context.currentTokenParent) ||
808        isTypeArgumentOrParameterOrAssertion(context.nextTokenSpan, context.nextTokenParent);
809}
810
811function isTypeAssertionContext(context: FormattingContext): boolean {
812    return context.contextNode.kind === SyntaxKind.TypeAssertionExpression;
813}
814
815function isVoidOpContext(context: FormattingContext): boolean {
816    return context.currentTokenSpan.kind === SyntaxKind.VoidKeyword && context.currentTokenParent.kind === SyntaxKind.VoidExpression;
817}
818
819function isYieldOrYieldStarWithOperand(context: FormattingContext): boolean {
820    return context.contextNode.kind === SyntaxKind.YieldExpression && (context.contextNode as YieldExpression).expression !== undefined;
821}
822
823function isNonNullAssertionContext(context: FormattingContext): boolean {
824    return context.contextNode.kind === SyntaxKind.NonNullExpression;
825}
826
827function isNotStatementConditionContext(context: FormattingContext): boolean {
828    return !isStatementConditionContext(context);
829}
830
831function isStatementConditionContext(context: FormattingContext): boolean {
832    switch (context.contextNode.kind) {
833        case SyntaxKind.IfStatement:
834        case SyntaxKind.ForStatement:
835        case SyntaxKind.ForInStatement:
836        case SyntaxKind.ForOfStatement:
837        case SyntaxKind.DoStatement:
838        case SyntaxKind.WhileStatement:
839            return true;
840
841        default:
842            return false;
843    }
844}
845
846function isSemicolonDeletionContext(context: FormattingContext): boolean {
847    let nextTokenKind = context.nextTokenSpan.kind;
848    let nextTokenStart = context.nextTokenSpan.pos;
849    if (isTrivia(nextTokenKind)) {
850        const nextRealToken = context.nextTokenParent === context.currentTokenParent
851            ? findNextToken(
852                context.currentTokenParent,
853                findAncestor(context.currentTokenParent, a => !a.parent)!,
854                context.sourceFile)
855            : context.nextTokenParent.getFirstToken(context.sourceFile);
856        if (!nextRealToken) {
857            return true;
858        }
859        nextTokenKind = nextRealToken.kind;
860        nextTokenStart = nextRealToken.getStart(context.sourceFile);
861    }
862
863    const startLine = context.sourceFile.getLineAndCharacterOfPosition(context.currentTokenSpan.pos).line;
864    const endLine = context.sourceFile.getLineAndCharacterOfPosition(nextTokenStart).line;
865    if (startLine === endLine) {
866        return nextTokenKind === SyntaxKind.CloseBraceToken
867            || nextTokenKind === SyntaxKind.EndOfFileToken;
868    }
869
870    if (nextTokenKind === SyntaxKind.SemicolonClassElement ||
871        nextTokenKind === SyntaxKind.SemicolonToken
872    ) {
873        return false;
874    }
875
876    if (context.contextNode.kind === SyntaxKind.InterfaceDeclaration ||
877        context.contextNode.kind === SyntaxKind.TypeAliasDeclaration
878    ) {
879        // Can’t remove semicolon after `foo`; it would parse as a method declaration:
880        //
881        // interface I {
882        //   foo;
883        //   (): void
884        // }
885        return !isPropertySignature(context.currentTokenParent)
886            || !!context.currentTokenParent.type
887            || nextTokenKind !== SyntaxKind.OpenParenToken;
888    }
889
890    if (isPropertyDeclaration(context.currentTokenParent)) {
891        return !context.currentTokenParent.initializer;
892    }
893
894    return context.currentTokenParent.kind !== SyntaxKind.ForStatement
895        && context.currentTokenParent.kind !== SyntaxKind.EmptyStatement
896        && context.currentTokenParent.kind !== SyntaxKind.SemicolonClassElement
897        && nextTokenKind !== SyntaxKind.OpenBracketToken
898        && nextTokenKind !== SyntaxKind.OpenParenToken
899        && nextTokenKind !== SyntaxKind.PlusToken
900        && nextTokenKind !== SyntaxKind.MinusToken
901        && nextTokenKind !== SyntaxKind.SlashToken
902        && nextTokenKind !== SyntaxKind.RegularExpressionLiteral
903        && nextTokenKind !== SyntaxKind.CommaToken
904        && nextTokenKind !== SyntaxKind.TemplateExpression
905        && nextTokenKind !== SyntaxKind.TemplateHead
906        && nextTokenKind !== SyntaxKind.NoSubstitutionTemplateLiteral
907        && nextTokenKind !== SyntaxKind.DotToken;
908}
909
910function isSemicolonInsertionContext(context: FormattingContext): boolean {
911    return positionIsASICandidate(context.currentTokenSpan.end, context.currentTokenParent, context.sourceFile);
912}
913
914function isNotPropertyAccessOnIntegerLiteral(context: FormattingContext): boolean {
915    return !isPropertyAccessExpression(context.contextNode)
916        || !isNumericLiteral(context.contextNode.expression)
917        || context.contextNode.expression.getText().indexOf(".") !== -1;
918}
919