diff --git a/denops/ddc/app.ts b/denops/ddc/app.ts index 5090bd9..4dbb58b 100644 --- a/denops/ddc/app.ts +++ b/denops/ddc/app.ts @@ -34,6 +34,7 @@ export const main: Entrypoint = (denops: Denops) => { const cbContext = createCallbackContext(); const lock = new Lock(0); let queuedEvent: DdcEvent | null = null; + const autoCompleteTimers = new Map(); const setAlias = (extType: DdcExtType, alias: string, base: string) => { loader.registerAlias(extType, alias, base); @@ -260,6 +261,11 @@ export const main: Entrypoint = (denops: Denops) => { async onEvent(arg1: unknown): Promise { queuedEvent = ensure(arg1, is.String) as DdcEvent; + // Revoke immediately to cancel any pending callback waits from the + // previous event processing, allowing it to fail fast and release the + // lock sooner. + cbContext.revoke(); + // NOTE: must be locked await lock.lock(async () => { while (queuedEvent !== null) { @@ -385,15 +391,29 @@ export const main: Entrypoint = (denops: Denops) => { // Check auto complete delay. if (options.autoCompleteDelay > 0) { - // Cancel previous completion - await ddc.cancelCompletion(denops, context, options); - - await new Promise((resolve) => - setTimeout( - resolve, - options.autoCompleteDelay, - ) - ); + // Cancel previous timer for this buffer (debounce) + clearTimeout(autoCompleteTimers.get(context.bufNr)); + + // Set a new per-buffer debounce timer. When it fires, cancel the + // previous completion once and then run doCompletion, avoiding + // repeated cancelCompletion/hide calls during rapid input. + const timerId = setTimeout(() => { + autoCompleteTimers.delete(context.bufNr); + + lock.lock(async () => { + await ddc.cancelCompletion(denops, context, options); + + if (options.hideOnEvents || event === "Update") { + // Hide the current completion + await ddc.hide(denops, context, options); + } + + await ddc.doCompletion(denops, context, cbContext, options); + }); + }, options.autoCompleteDelay); + + autoCompleteTimers.set(context.bufNr, timerId); + return; } if (options.hideOnEvents || event === "Update") { diff --git a/denops/ddc/context.ts b/denops/ddc/context.ts index c656952..f6d473d 100644 --- a/denops/ddc/context.ts +++ b/denops/ddc/context.ts @@ -420,6 +420,7 @@ export class ContextBuilderImpl implements ContextBuilder { } const context = { + bufNr: world.bufnr, cursor: world.cursor, event: event, filetype: world.filetype, diff --git a/denops/ddc/types.ts b/denops/ddc/types.ts index 5496e28..e62600a 100644 --- a/denops/ddc/types.ts +++ b/denops/ddc/types.ts @@ -16,6 +16,7 @@ export type SourceName = string; export type FilterName = string; export type Context = { + bufNr: number; cursor: (number | undefined)[]; event: DdcEvent; filetype: string;