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