diff --git a/package.json b/package.json index 91526456..8ab58a43 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@clangd/install": "0.1.4", "abort-controller": "^3.0.0", "jsonc-parser": "^2.1.0", - "vscode-languageclient": "7.1.0-next.1", + "vscode-languageclient": "7.1.0-next.2", "vscode-languageserver-types": "3.16.0" }, "devDependencies": { diff --git a/src/clangd-context.ts b/src/clangd-context.ts index 0f0f6426..8539deed 100644 --- a/src/clangd-context.ts +++ b/src/clangd-context.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import * as vscodelc from 'vscode-languageclient/node'; +import * as vscodelcAsync from 'vscode-languageclient/lib/common/utils/async'; import * as ast from './ast'; import * as config from './config'; @@ -55,6 +56,41 @@ class EnableEditsNearCursorFeature implements vscodelc.StaticFeature { dispose() {} } +class PendingTextChange { + _document: vscode.TextDocument; + _consumer: (data: vscode.TextDocumentChangeEvent) => vscode.ProviderResult; + _changes?: ReadonlyArray; + + constructor(document: vscode.TextDocument, consumer: (data: vscode.TextDocumentChangeEvent) => vscode.ProviderResult) { + this._document = document; + this._consumer = consumer; + } + + matches(document: vscode.TextDocument) { + return document.uri == this._document.uri; + } + + merge(changes: ReadonlyArray) { + if (this._changes) { + this._changes = this._changes.concat(changes); + } + else { + this._changes = changes; + } + } + + async send() { + let event: vscode.TextDocumentChangeEvent = { + document: this._document, + contentChanges: this._changes ? this._changes : [], + }; + + this._changes = undefined; + + return await this._consumer(event); + } +} + export class ClangdContext implements vscode.Disposable { subscriptions: vscode.Disposable[] = []; client!: ClangdLanguageClient; @@ -107,6 +143,8 @@ export class ClangdContext implements vscode.Disposable { middleware: { provideCompletionItem: async (document, position, context, token, next) => { + await this.flushPendingTextChanges(); + let list = await next(document, position, context, token); if (!config.get('serverCompletionRanking')) return list; @@ -126,6 +164,8 @@ export class ClangdContext implements vscode.Disposable { // from filtering out any results, e.g. enable workspaceSymbols for // qualified symbols. provideWorkspaceSymbols: async (query, token, next) => { + await this.flushPendingTextChanges(); + let symbols = await next(query, token); return symbols?.map(symbol => { // Only make this adjustment if the query is in fact qualified. @@ -142,6 +182,34 @@ export class ClangdContext implements vscode.Disposable { return symbol; }) }, + didSave: async (document, next) => { + if (this.pendingTextChange && this.pendingTextChange.matches(document)) { + await this.flushPendingTextChanges(); + } + return await next(document); + }, + didChange: async (event, next) => { + if (this.pendingTextChange) { + this.textChangeDelayer.cancel(); + + if (this.pendingTextChange.matches(event.document)) { + this.pendingTextChange.merge(event.contentChanges); + } + else { + let sendingTextChange = this.pendingTextChange; + this.pendingTextChange = new PendingTextChange(event.document, next); + this.pendingTextChange.merge(event.contentChanges); + + await sendingTextChange.send(); + } + } + else { + this.pendingTextChange = new PendingTextChange(event.document, next); + this.pendingTextChange.merge(event.contentChanges); + } + + this.textChangeDelayer.trigger(() => { this.flushPendingTextChanges(); }); + }, }, }; @@ -175,4 +243,16 @@ export class ClangdContext implements vscode.Disposable { this.subscriptions.forEach((d) => { d.dispose(); }); this.subscriptions = [] } + + pendingTextChange?: PendingTextChange; + textChangeDelayer = new vscodelcAsync.Delayer(2000); + + async flushPendingTextChanges() { + let sendingTextChange = this.pendingTextChange; + if (sendingTextChange) { + this.pendingTextChange = undefined; + + return await sendingTextChange.send(); + } + } }