fix(tui): await async slash command completions closes #2719
This commit is contained in:
@@ -966,7 +966,7 @@ export interface RegisteredCommand {
|
||||
name: string;
|
||||
sourceInfo: SourceInfo;
|
||||
description?: string;
|
||||
getArgumentCompletions?: (argumentPrefix: string) => AutocompleteItem[] | null;
|
||||
getArgumentCompletions?: (argumentPrefix: string) => AutocompleteItem[] | null | Promise<AutocompleteItem[] | null>;
|
||||
handler: (args: string, ctx: ExtensionCommandContext) => Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed slash-command argument autocomplete to await async `getArgumentCompletions()` results and ignore invalid return values, preventing crashes when extension commands provide asynchronous completions ([#2719](https://github.com/badlogic/pi-mono/issues/2719))
|
||||
|
||||
## [0.64.0] - 2026-03-29
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -218,12 +218,14 @@ export interface AutocompleteItem {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
type Awaitable<T> = T | Promise<T>;
|
||||
|
||||
export interface SlashCommand {
|
||||
name: string;
|
||||
description?: string;
|
||||
// Function to get argument completions for this command
|
||||
// Returns null if no argument completion is available
|
||||
getArgumentCompletions?(argumentPrefix: string): AutocompleteItem[] | null;
|
||||
getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
|
||||
}
|
||||
|
||||
export interface AutocompleteSuggestions {
|
||||
@@ -332,8 +334,8 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
const argumentSuggestions = command.getArgumentCompletions(argumentText);
|
||||
if (!argumentSuggestions || argumentSuggestions.length === 0) {
|
||||
const argumentSuggestions = await command.getArgumentCompletions(argumentText);
|
||||
if (!Array.isArray(argumentSuggestions) || argumentSuggestions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -2141,7 +2141,7 @@ export class Editor implements Component, Focusable {
|
||||
|
||||
this.autocompleteAbort = undefined;
|
||||
|
||||
if (!suggestions || suggestions.items.length === 0) {
|
||||
if (!suggestions || !Array.isArray(suggestions.items) || suggestions.items.length === 0) {
|
||||
this.cancelAutocomplete();
|
||||
this.tui.requestRender();
|
||||
return;
|
||||
|
||||
@@ -2458,6 +2458,48 @@ describe("Editor component", () => {
|
||||
assert.strictEqual(editor.getText(), "/model gpt-4o-mini");
|
||||
});
|
||||
|
||||
it("awaits async slash command argument completions", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: async (prefix) =>
|
||||
prefix.startsWith("s") ? [{ value: "skill-a", label: "skill-a" }] : null,
|
||||
},
|
||||
]);
|
||||
editor.setAutocompleteProvider(provider);
|
||||
editor.setText("/load-skills ");
|
||||
|
||||
editor.handleInput("s");
|
||||
await flushAutocomplete();
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), true);
|
||||
|
||||
editor.handleInput("\t");
|
||||
assert.strictEqual(editor.getText(), "/load-skills skill-a");
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), false);
|
||||
});
|
||||
|
||||
it("ignores invalid slash command argument completion results", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
{
|
||||
name: "load-skills",
|
||||
description: "Load skills",
|
||||
getArgumentCompletions: (() => "not-an-array") as unknown as (
|
||||
argumentPrefix: string,
|
||||
) => Promise<{ value: string; label: string }[] | null>,
|
||||
},
|
||||
]);
|
||||
editor.setAutocompleteProvider(provider);
|
||||
editor.setText("/load-skills ");
|
||||
|
||||
editor.handleInput("s");
|
||||
await flushAutocomplete();
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), false);
|
||||
assert.strictEqual(editor.getText(), "/load-skills s");
|
||||
});
|
||||
|
||||
it("does not show argument completions when command has no argument completer", async () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
const provider = new CombinedAutocompleteProvider([
|
||||
|
||||
Reference in New Issue
Block a user