Skip to content

Commit dcab328

Browse files
committed
RulesProvider performance improvements
1 parent e2cc27b commit dcab328

File tree

7 files changed

+211
-295
lines changed

7 files changed

+211
-295
lines changed

src/services/formatting/formatting.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ namespace ts.formatting {
362362
sourceFile: SourceFileLike): TextChange[] {
363363

364364
// formatting context is used by rules provider
365-
const formattingContext = new FormattingContext(sourceFile, requestKind);
365+
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
366366
let previousRangeHasError: boolean;
367367
let previousRange: TextRangeWithKind;
368368
let previousParent: Node;

src/services/formatting/formattingContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace ts.formatting {
1515
private contextNodeBlockIsOnOneLine: boolean;
1616
private nextNodeBlockIsOnOneLine: boolean;
1717

18-
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind) {
18+
constructor(public readonly sourceFile: SourceFileLike, public formattingRequestKind: FormattingRequestKind, public options: ts.FormatCodeSettings) {
1919
}
2020

2121
public updateContext(currentRange: TextRangeWithKind, currentTokenParent: Node, nextRange: TextRangeWithKind, nextTokenParent: Node, commonParent: Node) {

src/services/formatting/ruleOperationContext.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@
44
namespace ts.formatting {
55

66
export class RuleOperationContext {
7-
private customContextChecks: { (context: FormattingContext): boolean; }[];
8-
9-
constructor(...funcs: { (context: FormattingContext): boolean; }[]) {
10-
this.customContextChecks = funcs;
7+
constructor(private optionName: string, private checkApplyRuleOperation: { (optionName: string, options: FormatCodeSettings): boolean }, private customContextChecks: { (context: FormattingContext): boolean; }[]) {
118
}
129

13-
static Any: RuleOperationContext = new RuleOperationContext();
10+
static Any: RuleOperationContext = new RuleOperationContext(undefined, undefined, []);
1411

12+
static Create1(...funcs: { (context: FormattingContext): boolean; }[]) {
13+
return new RuleOperationContext(undefined, undefined, funcs);
14+
}
15+
static Create2(optionName: string, checkApplyRuleOperation: { (optionName: string, options: FormatCodeSettings): boolean }, ...funcs: { (context: FormattingContext): boolean; }[]) {
16+
return new RuleOperationContext(optionName, checkApplyRuleOperation, funcs);
17+
}
1518

1619
public IsAny(): boolean {
1720
return this === RuleOperationContext.Any;
1821
}
1922

20-
public InContext(context: FormattingContext): boolean {
23+
public InContext(context: FormattingContext): boolean {
24+
if (this.checkApplyRuleOperation && !this.checkApplyRuleOperation(this.optionName, context.options)) {
25+
return false;
26+
}
27+
2128
if (this.IsAny()) {
2229
return true;
2330
}

src/services/formatting/rules.ts

Lines changed: 185 additions & 149 deletions
Large diffs are not rendered by default.

src/services/formatting/rulesProvider.ts

Lines changed: 5 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ namespace ts.formatting {
55
export class RulesProvider {
66
private globalRules: Rules;
77
private options: ts.FormatCodeSettings;
8-
private activeRules: Rule[];
98
private rulesMap: RulesMap;
109

1110
constructor() {
1211
this.globalRules = new Rules();
12+
const activeRules = this.createActiveRules();
13+
this.rulesMap = RulesMap.create(activeRules);
1314
}
1415

1516
public getRuleName(rule: Rule): string {
@@ -30,141 +31,13 @@ namespace ts.formatting {
3031

3132
public ensureUpToDate(options: ts.FormatCodeSettings) {
3233
if (!this.options || !ts.compareDataObjects(this.options, options)) {
33-
const activeRules = this.createActiveRules(options);
34-
const rulesMap = RulesMap.create(activeRules);
35-
36-
this.activeRules = activeRules;
37-
this.rulesMap = rulesMap;
3834
this.options = ts.clone(options);
3935
}
4036
}
4137

42-
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
43-
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
44-
45-
if (options.insertSpaceAfterConstructor) {
46-
rules.push(this.globalRules.SpaceAfterConstructor);
47-
}
48-
else {
49-
rules.push(this.globalRules.NoSpaceAfterConstructor);
50-
}
51-
52-
if (options.insertSpaceAfterCommaDelimiter) {
53-
rules.push(this.globalRules.SpaceAfterComma);
54-
}
55-
else {
56-
rules.push(this.globalRules.NoSpaceAfterComma);
57-
}
58-
59-
if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) {
60-
rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword);
61-
}
62-
else {
63-
rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword);
64-
}
65-
66-
if (options.insertSpaceAfterKeywordsInControlFlowStatements) {
67-
rules.push(this.globalRules.SpaceAfterKeywordInControl);
68-
}
69-
else {
70-
rules.push(this.globalRules.NoSpaceAfterKeywordInControl);
71-
}
72-
73-
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) {
74-
rules.push(this.globalRules.SpaceAfterOpenParen);
75-
rules.push(this.globalRules.SpaceBeforeCloseParen);
76-
rules.push(this.globalRules.NoSpaceBetweenParens);
77-
}
78-
else {
79-
rules.push(this.globalRules.NoSpaceAfterOpenParen);
80-
rules.push(this.globalRules.NoSpaceBeforeCloseParen);
81-
rules.push(this.globalRules.NoSpaceBetweenParens);
82-
}
83-
84-
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) {
85-
rules.push(this.globalRules.SpaceAfterOpenBracket);
86-
rules.push(this.globalRules.SpaceBeforeCloseBracket);
87-
rules.push(this.globalRules.NoSpaceBetweenBrackets);
88-
}
89-
else {
90-
rules.push(this.globalRules.NoSpaceAfterOpenBracket);
91-
rules.push(this.globalRules.NoSpaceBeforeCloseBracket);
92-
rules.push(this.globalRules.NoSpaceBetweenBrackets);
93-
}
94-
95-
// The default value of InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces is true
96-
// so if the option is undefined, we should treat it as true as well
97-
if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces !== false) {
98-
rules.push(this.globalRules.SpaceAfterOpenBrace);
99-
rules.push(this.globalRules.SpaceBeforeCloseBrace);
100-
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
101-
}
102-
else {
103-
rules.push(this.globalRules.NoSpaceAfterOpenBrace);
104-
rules.push(this.globalRules.NoSpaceBeforeCloseBrace);
105-
rules.push(this.globalRules.NoSpaceBetweenEmptyBraceBrackets);
106-
}
107-
108-
if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) {
109-
rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle);
110-
rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail);
111-
}
112-
else {
113-
rules.push(this.globalRules.NoSpaceAfterTemplateHeadAndMiddle);
114-
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
115-
}
116-
117-
if (options.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
118-
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
119-
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
120-
}
121-
else {
122-
rules.push(this.globalRules.NoSpaceAfterOpenBraceInJsxExpression);
123-
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
124-
}
125-
126-
if (options.insertSpaceAfterSemicolonInForStatements) {
127-
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
128-
}
129-
else {
130-
rules.push(this.globalRules.NoSpaceAfterSemicolonInFor);
131-
}
132-
133-
if (options.insertSpaceBeforeAndAfterBinaryOperators) {
134-
rules.push(this.globalRules.SpaceBeforeBinaryOperator);
135-
rules.push(this.globalRules.SpaceAfterBinaryOperator);
136-
}
137-
else {
138-
rules.push(this.globalRules.NoSpaceBeforeBinaryOperator);
139-
rules.push(this.globalRules.NoSpaceAfterBinaryOperator);
140-
}
141-
142-
if (options.insertSpaceBeforeFunctionParenthesis) {
143-
rules.push(this.globalRules.SpaceBeforeOpenParenInFuncDecl);
144-
}
145-
else {
146-
rules.push(this.globalRules.NoSpaceBeforeOpenParenInFuncDecl);
147-
}
148-
149-
if (options.placeOpenBraceOnNewLineForControlBlocks) {
150-
rules.push(this.globalRules.NewLineBeforeOpenBraceInControl);
151-
}
152-
153-
if (options.placeOpenBraceOnNewLineForFunctions) {
154-
rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction);
155-
rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock);
156-
}
157-
158-
if (options.insertSpaceAfterTypeAssertion) {
159-
rules.push(this.globalRules.SpaceAfterTypeAssertion);
160-
}
161-
else {
162-
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
163-
}
164-
165-
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
166-
167-
return rules;
38+
private createActiveRules(): Rule[] {
39+
const rules = this.globalRules.HighPriorityCommonRules.slice(0);
40+
return rules.concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules);
16841
}
16942
}
17043
}

src/services/services.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ namespace ts {
3131
/** The version of the language service API */
3232
export const servicesVersion = "0.5";
3333

34+
const ruleProvider: formatting.RulesProvider = new formatting.RulesProvider();
35+
3436
function createNode<TKind extends SyntaxKind>(kind: TKind, pos: number, end: number, parent?: Node): NodeObject | TokenObject<TKind> | IdentifierObject {
3537
const node = kind >= SyntaxKind.FirstNode ? new NodeObject(kind, pos, end) :
3638
kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) :
@@ -1041,7 +1043,6 @@ namespace ts {
10411043
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
10421044

10431045
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
1044-
let ruleProvider: formatting.RulesProvider;
10451046
let program: Program;
10461047
let lastProjectVersion: string;
10471048

@@ -1073,11 +1074,6 @@ namespace ts {
10731074
}
10741075

10751076
function getRuleProvider(options: FormatCodeSettings) {
1076-
// Ensure rules are initialized and up to date wrt to formatting options
1077-
if (!ruleProvider) {
1078-
ruleProvider = new formatting.RulesProvider();
1079-
}
1080-
10811077
ruleProvider.ensureUpToDate(options);
10821078
return ruleProvider;
10831079
}

src/services/utilities.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,10 @@ namespace ts {
10361036
}
10371037

10381038
export function compareDataObjects(dst: any, src: any): boolean {
1039+
if (!dst || !src || Object.keys(dst).length !== Object.keys(src).length) {
1040+
return false;
1041+
}
1042+
10391043
for (const e in dst) {
10401044
if (typeof dst[e] === "object") {
10411045
if (!compareDataObjects(dst[e], src[e])) {

0 commit comments

Comments
 (0)