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