diff --git a/packages/core/src/prompts/prompt.ts b/packages/core/src/prompts/prompt.ts index 089e1dc8..95200d29 100644 --- a/packages/core/src/prompts/prompt.ts +++ b/packages/core/src/prompts/prompt.ts @@ -12,6 +12,7 @@ import type { Action } from '../utils/index.js'; export interface PromptOptions { render(this: Omit): string | undefined; + autocomplete?: string[] | ((input: string) => Promise); placeholder?: string; initialValue?: any; validate?: ((value: any) => string | Error | undefined) | undefined; @@ -194,10 +195,32 @@ export default class Prompt { if (char && (char.toLowerCase() === 'y' || char.toLowerCase() === 'n')) { this.emit('confirm', char.toLowerCase() === 'y'); } - if (char === '\t' && this.opts.placeholder) { - if (!this.value) { - this.rl?.write(this.opts.placeholder); - this.emit('value', this.opts.placeholder); + if (char === '\t' && (this.opts.placeholder || this.opts.autocomplete)) { + // Prevent tab character from being processed + this.rl?.write(null as any, { name: 'backspace' }); + + const { placeholder, autocomplete } = this.opts; + + if (!this.value && placeholder) { + this.rl?.write(placeholder); + this.emit('value', placeholder); + } + + if (this.value && autocomplete) { + (async () => { + const matchedOption = + typeof autocomplete === 'function' + ? await autocomplete(this.value) + : autocomplete.find((option) => option.startsWith(this.value)); + + if (matchedOption) { + this.rl?.write('', { ctrl: true, name: 'u' }); + this.rl?.write(matchedOption); + this.emit('value', matchedOption); + this.render(); + } + })(); + return; } } if (char) { diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index d49c735b..0d311d4d 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -109,6 +109,7 @@ export interface CommonOptions { export interface TextOptions extends CommonOptions { message: string; + autocomplete?: string[] | ((input: string) => Promise); placeholder?: string; defaultValue?: string; initialValue?: string; @@ -117,6 +118,7 @@ export interface TextOptions extends CommonOptions { export const text = (opts: TextOptions) => { return new TextPrompt({ validate: opts.validate, + autocomplete: opts.autocomplete, placeholder: opts.placeholder, defaultValue: opts.defaultValue, initialValue: opts.initialValue,