Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 72c71ea

Browse filesBrowse files
committed
RulesProvider performance improvements
1 parent 9796557 commit 72c71ea
Copy full SHA for 72c71ea

5 files changed

+108-42Lines changed: 108 additions & 42 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/server/project.ts‎

Copy file name to clipboardExpand all lines: src/server/project.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ namespace ts.server {
205205
this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
206206
this.lsHost.setCompilationSettings(this.compilerOptions);
207207

208-
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
208+
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry, this.projectService.getFormatCodeOptions());
209209

210210
if (!languageServiceEnabled) {
211211
this.disableLanguageService();
Collapse file

‎src/services/formatting/rulesMap.ts‎

Copy file name to clipboardExpand all lines: src/services/formatting/rulesMap.ts
+84-25Lines changed: 84 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,55 @@
33
/* @internal */
44
namespace ts.formatting {
55
export class RulesMap {
6+
// This array is used during construction & updating of the rules buckets in the map
7+
private rulesBucketConstructionStateList: RulesBucketConstructionState[];
8+
private readonly lowPriorityCommonRules: Rule[];
9+
610
public map: RulesBucket[];
711
public mapRowLength: number;
812

9-
constructor() {
13+
constructor(lowPriorityCommonRules: Rule[]) {
1014
this.map = [];
1115
this.mapRowLength = 0;
16+
this.lowPriorityCommonRules = lowPriorityCommonRules;
1217
}
1318

14-
static create(rules: Rule[]): RulesMap {
15-
const result = new RulesMap();
16-
result.Initialize(rules);
19+
static create(highPriorityCommonRules: Rule[], lowPriorityCommonRules: Rule[]): RulesMap {
20+
const result = new RulesMap(lowPriorityCommonRules);
21+
result.Initialize(highPriorityCommonRules);
1722
return result;
1823
}
1924

25+
public Update(oldRules: Rule[], newRules: Rule[]): void {
26+
const addRules = filter(newRules, r => oldRules.indexOf(r) < 0);
27+
const deleteRules = filter(oldRules, r => newRules.indexOf(r) < 0);
28+
29+
if (addRules.length === 0 && deleteRules.length === 0) {
30+
return;
31+
}
32+
33+
this.RemoveRules(deleteRules.concat(this.lowPriorityCommonRules));
34+
this.FillRules(addRules.concat(this.lowPriorityCommonRules));
35+
}
36+
2037
public Initialize(rules: Rule[]) {
2138
this.mapRowLength = SyntaxKind.LastToken + 1;
2239
this.map = <any>new Array(this.mapRowLength * this.mapRowLength); // new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
40+
this.rulesBucketConstructionStateList = new Array(this.map.length); // new Array<RulesBucketConstructionState>(this.map.length);
2341

24-
// This array is used only during construction of the rulesbucket in the map
25-
const rulesBucketConstructionStateList: RulesBucketConstructionState[] = <any>new Array(this.map.length); // new Array<RulesBucketConstructionState>(this.map.length);
26-
27-
this.FillRules(rules, rulesBucketConstructionStateList);
42+
this.FillRules(rules);
2843
return this.map;
2944
}
3045

31-
public FillRules(rules: Rule[], rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
46+
public FillRules(rules: Rule[]): void {
47+
rules.forEach((rule) => {
48+
this.AddOrRemoveRule(rule, RulesAction.Add);
49+
});
50+
}
51+
52+
public RemoveRules(rules: Rule[]): void {
3253
rules.forEach((rule) => {
33-
this.FillRule(rule, rulesBucketConstructionStateList);
54+
this.AddOrRemoveRule(rule, RulesAction.Remove);
3455
});
3556
}
3657

@@ -40,7 +61,7 @@ namespace ts.formatting {
4061
return rulesBucketIndex;
4162
}
4263

43-
private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void {
64+
private AddOrRemoveRule(rule: Rule, action: RulesAction): void {
4465
const specificRule = rule.Descriptor.LeftTokenRange !== Shared.TokenRange.Any &&
4566
rule.Descriptor.RightTokenRange !== Shared.TokenRange.Any;
4667

@@ -49,11 +70,19 @@ namespace ts.formatting {
4970
const rulesBucketIndex = this.GetRuleBucketIndex(left, right);
5071

5172
let rulesBucket = this.map[rulesBucketIndex];
52-
if (rulesBucket === undefined) {
53-
rulesBucket = this.map[rulesBucketIndex] = new RulesBucket();
73+
if (action === RulesAction.Add) {
74+
if (rulesBucket === undefined) {
75+
rulesBucket = this.map[rulesBucketIndex] = new RulesBucket();
76+
}
77+
rulesBucket.AddRule(rule, specificRule, this.rulesBucketConstructionStateList, rulesBucketIndex);
78+
}
79+
else {
80+
if (rulesBucket === undefined) {
81+
// The rules bucket does not exist for this rule
82+
return;
83+
}
84+
rulesBucket.RemoveRule(rule, specificRule, this.rulesBucketConstructionStateList, rulesBucketIndex);
5485
}
55-
56-
rulesBucket.AddRule(rule, specificRule, rulesBucketConstructionStateList, rulesBucketIndex);
5786
});
5887
});
5988
}
@@ -75,6 +104,11 @@ namespace ts.formatting {
75104
const MaskBitSize = 5;
76105
const Mask = 0x1f;
77106

107+
enum RulesAction {
108+
Add,
109+
Remove
110+
}
111+
78112
export enum RulesPosition {
79113
IgnoreRulesSpecific = 0,
80114
IgnoreRulesAny = MaskBitSize * 1,
@@ -121,10 +155,17 @@ namespace ts.formatting {
121155
return index;
122156
}
123157

124-
public IncreaseInsertionIndex(maskPosition: RulesPosition): void {
158+
public SetInsertionIndex(maskPosition: RulesPosition, action: RulesAction): void {
125159
let value = (this.rulesInsertionIndexBitmap >> maskPosition) & Mask;
126-
value++;
127-
Debug.assert((value & Mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
160+
161+
if (action === RulesAction.Add) {
162+
value++;
163+
Debug.assert((value & Mask) === value, "Adding more rules into the sub-bucket than allowed. Maximum allowed is 32 rules.");
164+
}
165+
else {
166+
value--;
167+
Debug.assert(value >= 0, "Index should never be less than 0.");
168+
}
128169

129170
let temp = this.rulesInsertionIndexBitmap & ~(Mask << maskPosition);
130171
temp |= value << maskPosition;
@@ -145,6 +186,30 @@ namespace ts.formatting {
145186
}
146187

147188
public AddRule(rule: Rule, specificTokens: boolean, constructionState: RulesBucketConstructionState[], rulesBucketIndex: number): void {
189+
const position = this.GetMaskPosition(rule, specificTokens);
190+
let state = constructionState[rulesBucketIndex];
191+
if (state === undefined) {
192+
state = constructionState[rulesBucketIndex] = new RulesBucketConstructionState();
193+
}
194+
const index = state.GetInsertionIndex(position);
195+
this.rules.splice(index, 0, rule);
196+
state.SetInsertionIndex(position, RulesAction.Add);
197+
}
198+
199+
public RemoveRule(rule: Rule, specificTokens: boolean, constructionState: RulesBucketConstructionState[], rulesBucketIndex: number): void {
200+
const position = this.GetMaskPosition(rule, specificTokens);
201+
const state = constructionState[rulesBucketIndex];
202+
if (state === undefined) {
203+
return;
204+
}
205+
const index = this.rules.indexOf(rule);
206+
if (index > -1) {
207+
this.rules.splice(index, 1);
208+
state.SetInsertionIndex(position, RulesAction.Remove);
209+
}
210+
}
211+
212+
private GetMaskPosition(rule: Rule, specificTokens: boolean): RulesPosition {
148213
let position: RulesPosition;
149214

150215
if (rule.Operation.Action === RuleAction.Ignore) {
@@ -163,13 +228,7 @@ namespace ts.formatting {
163228
RulesPosition.NoContextRulesAny;
164229
}
165230

166-
let state = constructionState[rulesBucketIndex];
167-
if (state === undefined) {
168-
state = constructionState[rulesBucketIndex] = new RulesBucketConstructionState();
169-
}
170-
const index = state.GetInsertionIndex(position);
171-
this.rules.splice(index, 0, rule);
172-
state.IncreaseInsertionIndex(position);
231+
return position;
173232
}
174233
}
175234
}
Collapse file

‎src/services/formatting/rulesProvider.ts‎

Copy file name to clipboardExpand all lines: src/services/formatting/rulesProvider.ts
+10-9Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ namespace ts.formatting {
55
export class RulesProvider {
66
private globalRules: Rules;
77
private options: ts.FormatCodeSettings;
8-
private activeRules: Rule[];
8+
private activeFormatOptionsRules: Rule[];
99
private rulesMap: RulesMap;
1010

1111
constructor() {
1212
this.globalRules = new Rules();
13+
14+
// Initialize the rulesMap with the high priority rules
15+
this.rulesMap = RulesMap.create(this.globalRules.HighPriorityCommonRules.slice(0), this.globalRules.LowPriorityCommonRules.slice(0));
16+
this.activeFormatOptionsRules = [];
1317
}
1418

1519
public getRuleName(rule: Rule): string {
@@ -26,17 +30,16 @@ namespace ts.formatting {
2630

2731
public ensureUpToDate(options: ts.FormatCodeSettings) {
2832
if (!this.options || !ts.compareDataObjects(this.options, options)) {
29-
const activeRules = this.createActiveRules(options);
30-
const rulesMap = RulesMap.create(activeRules);
33+
const newFormatOptionsRules = this.createFormatOptionsRules(options);
3134

32-
this.activeRules = activeRules;
33-
this.rulesMap = rulesMap;
35+
this.rulesMap.Update(this.activeFormatOptionsRules, newFormatOptionsRules);
36+
this.activeFormatOptionsRules = newFormatOptionsRules;
3437
this.options = ts.clone(options);
3538
}
3639
}
3740

38-
private createActiveRules(options: ts.FormatCodeSettings): Rule[] {
39-
let rules = this.globalRules.HighPriorityCommonRules.slice(0);
41+
private createFormatOptionsRules(options: ts.FormatCodeSettings): Rule[] {
42+
const rules = [];
4043

4144
if (options.insertSpaceAfterConstructor) {
4245
rules.push(this.globalRules.SpaceAfterConstructor);
@@ -158,8 +161,6 @@ namespace ts.formatting {
158161
rules.push(this.globalRules.NoSpaceAfterTypeAssertion);
159162
}
160163

161-
rules = rules.concat(this.globalRules.LowPriorityCommonRules);
162-
163164
return rules;
164165
}
165166
}
Collapse file

‎src/services/services.ts‎

Copy file name to clipboardExpand all lines: src/services/services.ts
+9-7Lines changed: 9 additions & 7 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) :
@@ -973,10 +975,9 @@ namespace ts {
973975
}
974976

975977
export function createLanguageService(host: LanguageServiceHost,
976-
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService {
978+
documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory()), formatOptions?: FormatCodeSettings): LanguageService {
977979

978980
const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
979-
let ruleProvider: formatting.RulesProvider;
980981
let program: Program;
981982
let lastProjectVersion: string;
982983

@@ -991,6 +992,11 @@ namespace ts {
991992
localizedDiagnosticMessages = host.getLocalizedDiagnosticMessages();
992993
}
993994

995+
// Update rules provider with code formatting options
996+
if (formatOptions) {
997+
ruleProvider.ensureUpToDate(formatOptions);
998+
}
999+
9941000
function log(message: string) {
9951001
if (host.log) {
9961002
host.log(message);
@@ -1008,11 +1014,7 @@ namespace ts {
10081014
}
10091015

10101016
function getRuleProvider(options: FormatCodeSettings) {
1011-
// Ensure rules are initialized and up to date wrt to formatting options
1012-
if (!ruleProvider) {
1013-
ruleProvider = new formatting.RulesProvider();
1014-
}
1015-
1017+
// Ensure rules are up to date wrt to formatting options
10161018
ruleProvider.ensureUpToDate(options);
10171019
return ruleProvider;
10181020
}
Collapse file

‎src/services/utilities.ts‎

Copy file name to clipboardExpand all lines: src/services/utilities.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,10 @@ namespace ts {
10491049
}
10501050

10511051
export function compareDataObjects(dst: any, src: any): boolean {
1052+
if (Object.keys(dst).length !== Object.keys(src).length) {
1053+
return false;
1054+
}
1055+
10521056
for (const e in dst) {
10531057
if (typeof dst[e] === "object") {
10541058
if (!compareDataObjects(dst[e], src[e])) {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.