From b6bd396983d50a59349f2f3ad6aa02af6f0822b6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 May 2017 08:39:22 -0700 Subject: [PATCH] Fix formatting for async computed method: Allow space between 'async' and '[' --- src/services/formatting/rules.ts | 4 +- src/services/formatting/rulesMap.ts | 3 +- src/services/formatting/tokenRange.ts | 46 +++++++++++++++---- .../fourslash/formatAsyncComputedMethod.ts | 9 ++++ 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 tests/cases/fourslash/formatAsyncComputedMethod.ts diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index c34b5c6aa94..2c1becb8dd1 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -283,7 +283,9 @@ namespace ts.formatting { this.NoSpaceAfterDot = new Rule(RuleDescriptor.create3(SyntaxKind.DotToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); // No space before and after indexer - this.NoSpaceBeforeOpenBracket = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.OpenBracketToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); + this.NoSpaceBeforeOpenBracket = new Rule( + RuleDescriptor.create2(Shared.TokenRange.AnyExcept(SyntaxKind.AsyncKeyword), SyntaxKind.OpenBracketToken), + RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete)); this.NoSpaceAfterCloseBracket = new Rule(RuleDescriptor.create3(SyntaxKind.CloseBracketToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNotBeforeBlockInFunctionDeclarationContext), RuleAction.Delete)); // Place a space before open brace in a function declaration diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index cd6f2e539d6..5b4eacd2c2a 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -41,8 +41,7 @@ namespace ts.formatting { } private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { - const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any && - rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any; + const specificRule = rule.Descriptor.LeftTokenRange.isSpecific() && rule.Descriptor.RightTokenRange.isSpecific(); rule.Descriptor.LeftTokenRange.GetTokens().forEach((left) => { rule.Descriptor.RightTokenRange.GetTokens().forEach((right) => { diff --git a/src/services/formatting/tokenRange.ts b/src/services/formatting/tokenRange.ts index 05d7369e77f..28f22cec475 100644 --- a/src/services/formatting/tokenRange.ts +++ b/src/services/formatting/tokenRange.ts @@ -6,6 +6,7 @@ namespace ts.formatting { export interface ITokenAccess { GetTokens(): SyntaxKind[]; Contains(token: SyntaxKind): boolean; + isSpecific(): boolean; } export class TokenRangeAccess implements ITokenAccess { @@ -27,6 +28,8 @@ namespace ts.formatting { public Contains(token: SyntaxKind): boolean { return this.tokens.indexOf(token) >= 0; } + + public isSpecific() { return true; } } export class TokenValuesAccess implements ITokenAccess { @@ -43,6 +46,8 @@ namespace ts.formatting { public Contains(token: SyntaxKind): boolean { return this.tokens.indexOf(token) >= 0; } + + public isSpecific() { return true; } } export class TokenSingleValueAccess implements ITokenAccess { @@ -56,15 +61,18 @@ namespace ts.formatting { public Contains(tokenValue: SyntaxKind): boolean { return tokenValue === this.token; } + + public isSpecific() { return true; } + } + + const allTokens: SyntaxKind[] = []; + for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) { + allTokens.push(token); } export class TokenAllAccess implements ITokenAccess { public GetTokens(): SyntaxKind[] { - const result: SyntaxKind[] = []; - for (let token = SyntaxKind.FirstToken; token <= SyntaxKind.LastToken; token++) { - result.push(token); - } - return result; + return allTokens; } public Contains(): boolean { @@ -74,6 +82,22 @@ namespace ts.formatting { public toString(): string { return "[allTokens]"; } + + public isSpecific() { return false; } + } + + export class TokenAllExceptAccess implements ITokenAccess { + constructor(readonly except: SyntaxKind) {} + + public GetTokens(): SyntaxKind[] { + return allTokens.filter(t => t !== this.except); + } + + public Contains(token: SyntaxKind): boolean { + return token !== this.except; + } + + public isSpecific() { return false; } } export class TokenRange { @@ -92,8 +116,8 @@ namespace ts.formatting { return new TokenRange(new TokenRangeAccess(f, to, except)); } - static AllTokens(): TokenRange { - return new TokenRange(new TokenAllAccess()); + static AnyExcept(token: SyntaxKind): TokenRange { + return new TokenRange(new TokenAllExceptAccess(token)); } public GetTokens(): SyntaxKind[] { @@ -108,8 +132,12 @@ namespace ts.formatting { return this.tokenAccess.toString(); } - static Any: TokenRange = TokenRange.AllTokens(); - static AnyIncludingMultilineComments = TokenRange.FromTokens(TokenRange.Any.GetTokens().concat([SyntaxKind.MultiLineCommentTrivia])); + public isSpecific() { + return this.tokenAccess.isSpecific(); + } + + static Any: TokenRange = new TokenRange(new TokenAllAccess()); + static AnyIncludingMultilineComments = TokenRange.FromTokens([...allTokens, SyntaxKind.MultiLineCommentTrivia]); static Keywords = TokenRange.FromRange(SyntaxKind.FirstKeyword, SyntaxKind.LastKeyword); static BinaryOperators = TokenRange.FromRange(SyntaxKind.FirstBinaryOperator, SyntaxKind.LastBinaryOperator); static BinaryKeywordOperators = TokenRange.FromTokens([SyntaxKind.InKeyword, SyntaxKind.InstanceOfKeyword, SyntaxKind.OfKeyword, SyntaxKind.AsKeyword, SyntaxKind.IsKeyword]); diff --git a/tests/cases/fourslash/formatAsyncComputedMethod.ts b/tests/cases/fourslash/formatAsyncComputedMethod.ts new file mode 100644 index 00000000000..b0365fd35cc --- /dev/null +++ b/tests/cases/fourslash/formatAsyncComputedMethod.ts @@ -0,0 +1,9 @@ +/// + +////class C { +//// /*method*/async [0]() { } +////} + +format.document(); +goTo.marker("method"); +verify.currentLineContentIs(" async [0]() { }");