Skip to content

Commit 2dddf3e

Browse files
committed
[clangd] Cleans up the semantic highlighting resources if clangd stops.
Summary: Disposes of the vscode listeners when clangd crashes and reuses the old highlighter when it restarts. The reason for reusing the highlighter is because this way the highlightings will not disappear as we won't have to dispose of them. Reviewers: hokein, ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D66743 llvm-svn: 370202
1 parent 2f323fc commit 2dddf3e

File tree

3 files changed

+47
-34
lines changed

3 files changed

+47
-34
lines changed

clang-tools-extra/clangd/clients/clangd-vscode/src/extension.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,6 @@ export function activate(context: vscode.ExtensionContext) {
112112
const semanticHighlightingFeature =
113113
new semanticHighlighting.SemanticHighlightingFeature();
114114
clangdClient.registerFeature(semanticHighlightingFeature);
115-
// The notification handler must be registered after the client is ready or
116-
// the client will crash.
117-
clangdClient.onReady().then(
118-
() => clangdClient.onNotification(
119-
semanticHighlighting.NotificationType,
120-
semanticHighlightingFeature.handleNotification.bind(
121-
semanticHighlightingFeature)));
122-
123115
console.log('Clang Language Server is now active!');
124116
context.subscriptions.push(clangdClient.start());
125117
context.subscriptions.push(vscode.commands.registerCommand(
@@ -149,9 +141,14 @@ export function activate(context: vscode.ExtensionContext) {
149141
clangdClient.onNotification(
150142
'textDocument/clangd.fileStatus',
151143
(fileStatus) => { status.onFileUpdated(fileStatus); });
144+
clangdClient.onNotification(
145+
semanticHighlighting.NotificationType,
146+
semanticHighlightingFeature.handleNotification.bind(
147+
semanticHighlightingFeature));
152148
} else if (newState == vscodelc.State.Stopped) {
153149
// Clear all cached statuses when clangd crashes.
154150
status.clear();
151+
semanticHighlightingFeature.dispose();
155152
}
156153
})
157154
// An empty place holder for the activate command, otherwise we'll get an

clang-tools-extra/clangd/clients/clangd-vscode/src/semantic-highlighting.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export class SemanticHighlightingFeature implements vscodelc.StaticFeature {
5656
scopeLookupTable: string[][];
5757
// The object that applies the highlightings clangd sends.
5858
highlighter: Highlighter;
59+
// Any disposables that should be cleaned up when clangd crashes.
60+
private subscriptions: vscode.Disposable[] = [];
5961
fillClientCapabilities(capabilities: vscodelc.ClientCapabilities) {
6062
// Extend the ClientCapabilities type and add semantic highlighting
6163
// capability to the object.
@@ -88,21 +90,27 @@ export class SemanticHighlightingFeature implements vscodelc.StaticFeature {
8890
// otherwise it could try to update the themeRuleMatcher without the
8991
// highlighter being created.
9092
this.highlighter = new Highlighter(this.scopeLookupTable);
93+
this.subscriptions.push(vscode.Disposable.from(this.highlighter));
9194
this.loadCurrentTheme();
9295
// Event handling for handling with TextDocuments/Editors lifetimes.
93-
vscode.window.onDidChangeVisibleTextEditors(
96+
this.subscriptions.push(vscode.window.onDidChangeVisibleTextEditors(
9497
(editors: vscode.TextEditor[]) =>
9598
editors.forEach((e) => this.highlighter.applyHighlights(
96-
e.document.uri.toString())));
97-
vscode.workspace.onDidCloseTextDocument(
98-
(doc) => this.highlighter.removeFileHighlightings(doc.uri.toString()));
99+
e.document.uri.toString()))));
100+
this.subscriptions.push(vscode.workspace.onDidCloseTextDocument(
101+
(doc) => this.highlighter.removeFileHighlightings(doc.uri.toString())));
99102
}
100103

101104
handleNotification(params: SemanticHighlightingParams) {
102105
const lines: SemanticHighlightingLine[] = params.lines.map(
103106
(line) => ({line : line.line, tokens : decodeTokens(line.tokens)}));
104107
this.highlighter.highlight(params.textDocument.uri, lines);
105108
}
109+
// Disposes of all disposable resources used by this object.
110+
public dispose() {
111+
this.subscriptions.forEach((d) => d.dispose());
112+
this.subscriptions = [];
113+
}
106114
}
107115

108116
// Converts a string of base64 encoded tokens into the corresponding array of
@@ -138,6 +146,13 @@ export class Highlighter {
138146
constructor(scopeLookupTable: string[][]) {
139147
this.scopeLookupTable = scopeLookupTable;
140148
}
149+
public dispose() {
150+
this.files.clear();
151+
this.decorationTypes.forEach((t) => t.dispose());
152+
// Dispose must not be not called multiple times if initialize is
153+
// called again.
154+
this.decorationTypes = [];
155+
}
141156
// This function must be called at least once or no highlightings will be
142157
// done. Sets the theme that is used when highlighting. Also triggers a
143158
// recolorization for all current highlighters. Should be called whenever the
@@ -174,6 +189,27 @@ export class Highlighter {
174189
this.applyHighlights(fileUri);
175190
}
176191

192+
// Applies all the highlightings currently stored for a file with fileUri.
193+
public applyHighlights(fileUri: string) {
194+
if (!this.files.has(fileUri))
195+
// There are no highlightings for this file, must return early or will get
196+
// out of bounds when applying the decorations below.
197+
return;
198+
if (!this.decorationTypes.length)
199+
// Can't apply any decorations when there is no theme loaded.
200+
return;
201+
// This must always do a full re-highlighting due to the fact that
202+
// TextEditorDecorationType are very expensive to create (which makes
203+
// incremental updates infeasible). For this reason one
204+
// TextEditorDecorationType is used per scope.
205+
const ranges = this.getDecorationRanges(fileUri);
206+
vscode.window.visibleTextEditors.forEach((e) => {
207+
if (e.document.uri.toString() !== fileUri)
208+
return;
209+
this.decorationTypes.forEach((d, i) => e.setDecorations(d, ranges[i]));
210+
});
211+
}
212+
177213
// Called when a text document is closed. Removes any highlighting entries for
178214
// the text document that was closed.
179215
public removeFileHighlightings(fileUri: string) {
@@ -207,27 +243,6 @@ export class Highlighter {
207243
});
208244
return decorations;
209245
}
210-
211-
// Applies all the highlightings currently stored for a file with fileUri.
212-
public applyHighlights(fileUri: string) {
213-
if (!this.files.has(fileUri))
214-
// There are no highlightings for this file, must return early or will get
215-
// out of bounds when applying the decorations below.
216-
return;
217-
if (!this.decorationTypes.length)
218-
// Can't apply any decorations when there is no theme loaded.
219-
return;
220-
// This must always do a full re-highlighting due to the fact that
221-
// TextEditorDecorationType are very expensive to create (which makes
222-
// incremental updates infeasible). For this reason one
223-
// TextEditorDecorationType is used per scope.
224-
const ranges = this.getDecorationRanges(fileUri);
225-
vscode.window.visibleTextEditors.forEach((e) => {
226-
if (e.document.uri.toString() !== fileUri)
227-
return;
228-
this.decorationTypes.forEach((d, i) => e.setDecorations(d, ranges[i]));
229-
});
230-
}
231246
}
232247

233248
// A rule for how to color TextMate scopes.

clang-tools-extra/clangd/clients/clangd-vscode/test/semantic-highlighting.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ suite('SemanticHighlighting Tests', () => {
107107
highlighter.highlight('file1', []);
108108
assert.deepEqual(highlighter.applicationUriHistory, [ 'file1' ]);
109109
highlighter.initialize(tm);
110-
assert.deepEqual(highlighter.applicationUriHistory, [ 'file1', 'file1', 'file2' ]);
110+
assert.deepEqual(highlighter.applicationUriHistory,
111+
[ 'file1', 'file1', 'file2' ]);
111112
// Groups decorations into the scopes used.
112113
let highlightingsInLine: semanticHighlighting.SemanticHighlightingLine[] = [
113114
{

0 commit comments

Comments
 (0)