Skip to content

Commit b2b0ad3

Browse files
authored
Avoid applying edits to cached colorization tokens until needed (#3793)
1 parent cee6ebe commit b2b0ad3

3 files changed

Lines changed: 64 additions & 48 deletions

File tree

Extension/src/LanguageServer/client.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,14 +583,15 @@ class DefaultClient implements Client {
583583
private editVersion: number = 0;
584584

585585
public onDidChangeTextDocument(textDocumentChangeEvent: vscode.TextDocumentChangeEvent): void {
586+
// Increment editVersion for every call to onDidChangeTextDocument, regardless of whether the file is handled
587+
this.editVersion++;
586588
if (textDocumentChangeEvent.document.uri.scheme === "file") {
587589
if (textDocumentChangeEvent.document.languageId === "cpp" || textDocumentChangeEvent.document.languageId === "c") {
588-
this.editVersion++;
589590
try {
590591
let colorizationState: ColorizationState = this.getColorizationState(textDocumentChangeEvent.document.uri.toString());
591592

592593
// Adjust colorization ranges after this edit. (i.e. if a line was added, push decorations after it down one line)
593-
colorizationState.updateAfterEdits(textDocumentChangeEvent.contentChanges, this.editVersion);
594+
colorizationState.addEdits(textDocumentChangeEvent.contentChanges, this.editVersion);
594595
} catch (e) {
595596
// Ensure an exception does not prevent pass-through to native handler, or editVersion could become inconsistent
596597
console.log(e.toString());

Extension/src/LanguageServer/colorization.ts

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class ThemeStyle {
7171
export class ColorizationSettings {
7272
private uri: vscode.Uri;
7373
private pendingTask: util.BlockingTask<any>;
74+
private editorBackground: string;
7475

7576
public themeStyleCMap: ThemeStyle[] = [];
7677
public themeStyleCppMap: ThemeStyle[] = [];
@@ -98,7 +99,7 @@ export class ColorizationSettings {
9899
if (textMateRuleSettings.foreground) {
99100
baseStyle.foreground = textMateRuleSettings.foreground;
100101
}
101-
if (textMateRuleSettings.background) {
102+
if (textMateRuleSettings.background && textMateRuleSettings.background !== this.editorBackground) {
102103
baseStyle.background = textMateRuleSettings.background;
103104
}
104105
// Any (even empty) string for fontStyle removes inherited value
@@ -261,12 +262,19 @@ export class ColorizationSettings {
261262
let includedThemePath: string = path.join(path.dirname(themePath), themeContent.include);
262263
rules = await this.loadTheme(includedThemePath, defaultStyle);
263264
}
265+
266+
if (themeContent.colors && themeContent.colors["editor.background"]) {
267+
this.editorBackground = themeContent.colors["editor.background"];
268+
}
264269
}
265270
}
266271

267272
if (textMateRules) {
268273
let scopelessSetting: any = textMateRules.find(e => e.settings && !e.scope);
269274
if (scopelessSetting) {
275+
if (scopelessSetting.settings.background) {
276+
this.editorBackground = scopelessSetting.settings.background;
277+
}
270278
this.updateStyleFromTextMateRuleSettings(defaultStyle, scopelessSetting.settings);
271279
}
272280
rules.push(textMateRules);
@@ -298,6 +306,10 @@ export class ColorizationSettings {
298306
let themeFullPath: string = path.join(extensionPath, themeRelativePath);
299307
let defaultStyle: ThemeStyle = new ThemeStyle();
300308
let rulesSet: TextMateRule[][] = await this.loadTheme(themeFullPath, defaultStyle);
309+
let editorBackgroundSetting: string = otherSettings.editorBackground;
310+
if (editorBackgroundSetting) {
311+
this.editorBackground = editorBackgroundSetting;
312+
}
301313
this.updateStyles(themeName, defaultStyle, rulesSet);
302314
return;
303315
}
@@ -390,8 +402,10 @@ export class ColorizationState {
390402
private inactiveDecoration: vscode.TextEditorDecorationType = null;
391403
private inactiveRanges: vscode.Range[] = [];
392404
private versionedEdits: VersionedEdits[] = [];
393-
private lastSyntacticVersion: number = 0;
394-
private lastSemanticVersion: number = 0;
405+
private currentSyntacticVersion: number = 0;
406+
private lastReceivedSyntacticVersion: number = 0;
407+
private currentSemanticVersion: number = 0;
408+
private lastReceivedSemanticVersion: number = 0;
395409

396410
public constructor(uri: vscode.Uri, colorizationSettings: ColorizationSettings) {
397411
this.uri = uri;
@@ -472,6 +486,7 @@ export class ColorizationState {
472486
}
473487

474488
public refresh(e: vscode.TextEditor): void {
489+
this.applyEdits();
475490
let f: () => void = async () => {
476491
this.refreshInner(e);
477492
};
@@ -480,6 +495,7 @@ export class ColorizationState {
480495

481496
public onSettingsChanged(uri: vscode.Uri): void {
482497
let f: () => void = async () => {
498+
this.applyEdits();
483499
this.disposeColorizationDecorations();
484500
let isCpp: boolean = util.isEditorFileCpp(uri.toString());
485501
this.createColorizationDecorations(isCpp);
@@ -609,44 +625,50 @@ export class ColorizationState {
609625
return ranges;
610626
}
611627

612-
public updateAfterEdits(changes: vscode.TextDocumentContentChangeEvent[], editVersion: number): void {
613-
for (let i: number = 0; i < this.syntacticRanges.length; i++) {
614-
this.syntacticRanges[i] = this.fixRanges(this.syntacticRanges[i], changes);
615-
}
616-
for (let i: number = 0; i < this.semanticRanges.length; i++) {
617-
this.semanticRanges[i] = this.fixRanges(this.semanticRanges[i], changes);
618-
}
619-
this.inactiveRanges = this.fixRanges(this.inactiveRanges, changes);
628+
// Add edits to be applied when/if cached tokens need to be reapplied.
629+
public addEdits(changes: vscode.TextDocumentContentChangeEvent[], editVersion: number): void {
620630
let edits: VersionedEdits = {
621631
editVersion: editVersion,
622632
changes: changes
623633
};
624634
this.versionedEdits.push(edits);
625635
}
626636

637+
// Apply any pending edits to the currently cached tokens
638+
private applyEdits() : void {
639+
this.versionedEdits.forEach((edit) => {
640+
if (edit.editVersion > this.currentSyntacticVersion) {
641+
for (let i: number = 0; i < TokenKind.Count; i++) {
642+
this.syntacticRanges[i] = this.fixRanges(this.syntacticRanges[i], edit.changes);
643+
}
644+
this.currentSyntacticVersion = edit.editVersion;
645+
}
646+
if (edit.editVersion > this.currentSemanticVersion) {
647+
for (let i: number = 0; i < TokenKind.Count; i++) {
648+
this.semanticRanges[i] = this.fixRanges(this.semanticRanges[i], edit.changes);
649+
}
650+
this.inactiveRanges = this.fixRanges(this.inactiveRanges, edit.changes);
651+
this.currentSemanticVersion = edit.editVersion;
652+
}
653+
});
654+
}
655+
656+
// Remove any edits from the list if we will never receive tokens that old.
627657
private purgeOldVersionedEdits(): void {
628-
let minVersion: number = Math.min(this.lastSemanticVersion, this.lastSyntacticVersion);
658+
let minVersion: number = Math.min(this.lastReceivedSemanticVersion, this.lastReceivedSyntacticVersion);
629659
let index: number = this.versionedEdits.findIndex((edit) => edit.editVersion > minVersion);
630660
if (index === -1) {
631661
this.versionedEdits = [];
632662
} else if (index > 0) {
633663
this.versionedEdits = this.versionedEdits.slice(index);
634664
}
635665
}
636-
637-
private updateColorizationRanges(uri: string, syntacticRanges: vscode.Range[][], semanticRanges: vscode.Range[][], inactiveRanges: vscode.Range[]): void {
638-
if (inactiveRanges) {
639-
this.inactiveRanges = inactiveRanges;
640-
}
641-
for (let i: number = 0; i < TokenKind.Count; i++) {
642-
if (syntacticRanges) {
643-
this.syntacticRanges[i] = syntacticRanges[i];
644-
}
645-
if (semanticRanges) {
646-
this.semanticRanges[i] = semanticRanges[i];
647-
}
648-
}
666+
667+
private updateColorizationRanges(uri: string): void {
649668
let f: () => void = async () => {
669+
this.applyEdits();
670+
this.purgeOldVersionedEdits();
671+
650672
// The only way to un-apply decorators is to dispose them.
651673
// If we dispose old decorators before applying new decorators, we see a flicker on Mac,
652674
// likely due to a race with UI updates. Here we set aside the existing decorators to be
@@ -682,29 +704,21 @@ export class ColorizationState {
682704
}
683705

684706
public updateSyntactic(uri: string, syntacticRanges: vscode.Range[][], editVersion: number): void {
685-
this.versionedEdits.forEach((edit) => {
686-
if (edit.editVersion > editVersion) {
687-
for (let i: number = 0; i < TokenKind.Count; i++) {
688-
syntacticRanges[i] = this.fixRanges(syntacticRanges[i], edit.changes);
689-
}
690-
}
691-
});
692-
this.updateColorizationRanges(uri, syntacticRanges, null, null);
693-
this.lastSyntacticVersion = editVersion;
694-
this.purgeOldVersionedEdits();
707+
for (let i: number = 0; i < TokenKind.Count; i++) {
708+
this.syntacticRanges[i] = syntacticRanges[i];
709+
}
710+
this.currentSyntacticVersion = editVersion;
711+
this.lastReceivedSyntacticVersion = editVersion;
712+
this.updateColorizationRanges(uri);
695713
}
696714

697715
public updateSemantic(uri: string, semanticRanges: vscode.Range[][], inactiveRanges: vscode.Range[], editVersion: number): void {
698-
this.versionedEdits.forEach((edit) => {
699-
if (edit.editVersion > editVersion) {
700-
for (let i: number = 0; i < TokenKind.Count; i++) {
701-
semanticRanges[i] = this.fixRanges(semanticRanges[i], edit.changes);
702-
}
703-
inactiveRanges = this.fixRanges(inactiveRanges, edit.changes);
704-
}
705-
});
706-
this.updateColorizationRanges(uri, null, semanticRanges, inactiveRanges);
707-
this.lastSemanticVersion = editVersion;
708-
this.purgeOldVersionedEdits();
716+
this.inactiveRanges = inactiveRanges;
717+
for (let i: number = 0; i < TokenKind.Count; i++) {
718+
this.semanticRanges[i] = semanticRanges[i];
719+
}
720+
this.currentSemanticVersion = editVersion;
721+
this.lastReceivedSemanticVersion = editVersion;
722+
this.updateColorizationRanges(uri);
709723
}
710724
}

Extension/src/LanguageServer/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export class OtherSettings {
117117
public get filesExclude(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration("files", this.resource).get("exclude"); }
118118
public get searchExclude(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration("search", this.resource).get("exclude"); }
119119
public get settingsEditor(): string { return vscode.workspace.getConfiguration("workbench.settings").get<string>("editor"); }
120+
public get editorBackground(): string { return vscode.workspace.getConfiguration("editor", this.resource).get<string>("background"); }
120121

121122
public get colorTheme(): string { return vscode.workspace.getConfiguration("workbench").get<string>("colorTheme"); }
122123

0 commit comments

Comments
 (0)