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