Skip to content

Commit bc7c8b2

Browse files
committed
feat: debounce the inline completion
1 parent b494ef9 commit bc7c8b2

File tree

2 files changed

+59
-2
lines changed

2 files changed

+59
-2
lines changed

src/completion.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { logger } from "./logger";
33
import { ModelProviderManager } from "./providers";
44
import { statusIcon } from "./status-icon";
55
import { storage } from "./storage";
6-
import { getLanguageConfig } from "./utilities";
6+
import { getLanguageConfig, Queue } from "./utilities";
77

88
/**
99
* InlineCompletionProvider class provides inline completion functionality for the active document.
@@ -108,14 +108,25 @@ class InlineCompletionProvider implements vscode.InlineCompletionItemProvider {
108108
});
109109
}
110110

111+
private readonly queue = new Queue<vscode.InlineCompletionList>(500);
112+
private async generateCompletions(
113+
document: vscode.TextDocument,
114+
position: vscode.Position,
115+
abortController: AbortController,
116+
): Promise<vscode.InlineCompletionList> {
117+
return this.queue.enqueue(() =>
118+
this.doGenerateCompletions(document, position, abortController),
119+
);
120+
}
121+
111122
/**
112123
* Generates completions for the current document.
113124
* @param {vscode.TextDocument} document - The current document.
114125
* @param {vscode.Position} position - The current position in the document.
115126
* @param {AbortController} abortController - The abort controller for the operation.
116127
* @returns {Promise<vscode.InlineCompletionList>} A list of inline completions.
117128
*/
118-
private async generateCompletions(
129+
private async doGenerateCompletions(
119130
document: vscode.TextDocument,
120131
position: vscode.Position,
121132
abortController: AbortController,

src/utilities.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,49 @@ const LANGUAGES: { [key: string]: ILanguageConfig } = {
426426
comment: { start: "/* ", end: " */" },
427427
},
428428
};
429+
430+
interface ITask<T> {
431+
factory: () => Promise<T>;
432+
resolve: (value: T) => void;
433+
reject: (reason?: unknown) => void;
434+
}
435+
436+
// Debounce an async function.
437+
export class Queue<T> {
438+
private queue: ITask<T>[] = [];
439+
private pending = false;
440+
441+
constructor(private readonly delay: number) {}
442+
443+
async enqueue(factory: () => Promise<T>): Promise<T> {
444+
return new Promise<T>((resolve, reject) => {
445+
this.queue.push({ factory, resolve, reject });
446+
if (!this.pending) {
447+
this.pending = true;
448+
setTimeout(() => this.consume(), this.delay);
449+
}
450+
});
451+
}
452+
453+
private consume() {
454+
const { queue } = this;
455+
this.queue = [];
456+
const latest = queue.pop();
457+
queue.forEach((task) => task.resolve([] as T));
458+
if (latest) {
459+
const promise = latest.factory();
460+
promise.then(latest.resolve, latest.reject);
461+
promise.then(
462+
() => this.consumed(),
463+
() => this.consumed(),
464+
);
465+
}
466+
this.pending = false;
467+
}
468+
469+
private consumed() {
470+
if (this.queue.length) {
471+
setTimeout(() => this.consume(), this.delay);
472+
}
473+
}
474+
}

0 commit comments

Comments
 (0)