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