Skip to content

Commit dd5f623

Browse files
committed
Add basic support for delaying and combining text changes (#262)
1 parent b768b01 commit dd5f623

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"@clangd/install": "0.1.4",
4848
"abort-controller": "^3.0.0",
4949
"jsonc-parser": "^2.1.0",
50-
"vscode-languageclient": "7.1.0-next.1",
50+
"vscode-languageclient": "7.1.0-next.2",
5151
"vscode-languageserver-types": "3.16.0"
5252
},
5353
"devDependencies": {

src/clangd-context.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as vscode from 'vscode';
22
import * as vscodelc from 'vscode-languageclient/node';
3+
import * as vscodelcAsync from 'vscode-languageclient/lib/common/utils/async';
34

45
import * as ast from './ast';
56
import * as config from './config';
@@ -55,6 +56,41 @@ class EnableEditsNearCursorFeature implements vscodelc.StaticFeature {
5556
dispose() {}
5657
}
5758

59+
class PendingTextChange {
60+
_document: vscode.TextDocument;
61+
_consumer: (data: vscode.TextDocumentChangeEvent) => vscode.ProviderResult<void>;
62+
_changes?: ReadonlyArray<vscode.TextDocumentContentChangeEvent>;
63+
64+
constructor(document: vscode.TextDocument, consumer: (data: vscode.TextDocumentChangeEvent) => vscode.ProviderResult<void>) {
65+
this._document = document;
66+
this._consumer = consumer;
67+
}
68+
69+
matches(document: vscode.TextDocument) {
70+
return document.uri == this._document.uri;
71+
}
72+
73+
merge(changes: ReadonlyArray<vscode.TextDocumentContentChangeEvent>) {
74+
if (this._changes) {
75+
this._changes = this._changes.concat(changes);
76+
}
77+
else {
78+
this._changes = changes;
79+
}
80+
}
81+
82+
async send() {
83+
let event: vscode.TextDocumentChangeEvent = {
84+
document: this._document,
85+
contentChanges: this._changes ? this._changes : [],
86+
};
87+
88+
this._changes = undefined;
89+
90+
return await this._consumer(event);
91+
}
92+
}
93+
5894
export class ClangdContext implements vscode.Disposable {
5995
subscriptions: vscode.Disposable[] = [];
6096
client!: ClangdLanguageClient;
@@ -107,6 +143,8 @@ export class ClangdContext implements vscode.Disposable {
107143
middleware: {
108144
provideCompletionItem: async (document, position, context, token,
109145
next) => {
146+
await this.flushPendingTextChanges();
147+
110148
let list = await next(document, position, context, token);
111149
if (!config.get<boolean>('serverCompletionRanking'))
112150
return list;
@@ -126,6 +164,8 @@ export class ClangdContext implements vscode.Disposable {
126164
// from filtering out any results, e.g. enable workspaceSymbols for
127165
// qualified symbols.
128166
provideWorkspaceSymbols: async (query, token, next) => {
167+
await this.flushPendingTextChanges();
168+
129169
let symbols = await next(query, token);
130170
return symbols?.map(symbol => {
131171
// Only make this adjustment if the query is in fact qualified.
@@ -142,6 +182,28 @@ export class ClangdContext implements vscode.Disposable {
142182
return symbol;
143183
})
144184
},
185+
didChange: async (event, next) => {
186+
if (this.pendingTextChange) {
187+
this.textChangeDelayer.cancel();
188+
189+
if (this.pendingTextChange.matches(event.document)) {
190+
this.pendingTextChange.merge(event.contentChanges);
191+
}
192+
else {
193+
let sendingTextChange = this.pendingTextChange;
194+
this.pendingTextChange = new PendingTextChange(event.document, next);
195+
this.pendingTextChange.merge(event.contentChanges);
196+
197+
await sendingTextChange.send();
198+
}
199+
}
200+
else {
201+
this.pendingTextChange = new PendingTextChange(event.document, next);
202+
this.pendingTextChange.merge(event.contentChanges);
203+
}
204+
205+
this.textChangeDelayer.trigger(() => { this.flushPendingTextChanges(); });
206+
},
145207
},
146208
};
147209

@@ -175,4 +237,16 @@ export class ClangdContext implements vscode.Disposable {
175237
this.subscriptions.forEach((d) => { d.dispose(); });
176238
this.subscriptions = []
177239
}
240+
241+
pendingTextChange?: PendingTextChange;
242+
textChangeDelayer = new vscodelcAsync.Delayer<void>(2000);
243+
244+
async flushPendingTextChanges() {
245+
let sendingTextChange = this.pendingTextChange;
246+
if (sendingTextChange) {
247+
this.pendingTextChange = undefined;
248+
249+
return await sendingTextChange.send();
250+
}
251+
}
178252
}

0 commit comments

Comments
 (0)