mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 17:01:02 +00:00
feat(tui): auto-apply single suggestion in force file autocomplete (#993)
When Tab triggers file autocomplete and there's exactly one matching suggestion, apply it immediately without showing the menu. This makes path completion faster by eliminating the extra Tab press to confirm.
This commit is contained in:
parent
2cc2544809
commit
1224b31135
2 changed files with 128 additions and 0 deletions
|
|
@ -1791,6 +1791,25 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
|
|||
);
|
||||
|
||||
if (suggestions && suggestions.items.length > 0) {
|
||||
// If there's exactly one suggestion, apply it immediately
|
||||
if (suggestions.items.length === 1) {
|
||||
const item = suggestions.items[0]!;
|
||||
this.pushUndoSnapshot();
|
||||
this.lastAction = null;
|
||||
const result = this.autocompleteProvider.applyCompletion(
|
||||
this.state.lines,
|
||||
this.state.cursorLine,
|
||||
this.state.cursorCol,
|
||||
item,
|
||||
suggestions.prefix,
|
||||
);
|
||||
this.state.lines = result.lines;
|
||||
this.state.cursorLine = result.cursorLine;
|
||||
this.state.cursorCol = result.cursorCol;
|
||||
if (this.onChange) this.onChange(this.getText());
|
||||
return;
|
||||
}
|
||||
|
||||
this.autocompletePrefix = suggestions.prefix;
|
||||
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
||||
this.isAutocompleting = true;
|
||||
|
|
|
|||
|
|
@ -1726,4 +1726,113 @@ describe("Editor component", () => {
|
|||
assert.strictEqual(editor.getText(), "di");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Autocomplete", () => {
|
||||
it("auto-applies single force-file suggestion without showing menu", () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
|
||||
// Create a mock provider with getForceFileSuggestions that returns single item
|
||||
const mockProvider: AutocompleteProvider & {
|
||||
getForceFileSuggestions: AutocompleteProvider["getSuggestions"];
|
||||
} = {
|
||||
getSuggestions: () => null,
|
||||
getForceFileSuggestions: (lines, _cursorLine, cursorCol) => {
|
||||
const text = lines[0] || "";
|
||||
const prefix = text.slice(0, cursorCol);
|
||||
if (prefix === "Work") {
|
||||
return {
|
||||
items: [{ value: "Workspace/", label: "Workspace/" }],
|
||||
prefix: "Work",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
applyCompletion: (lines, cursorLine, cursorCol, item, prefix) => {
|
||||
const line = lines[cursorLine] || "";
|
||||
const before = line.slice(0, cursorCol - prefix.length);
|
||||
const after = line.slice(cursorCol);
|
||||
const newLines = [...lines];
|
||||
newLines[cursorLine] = before + item.value + after;
|
||||
return {
|
||||
lines: newLines,
|
||||
cursorLine,
|
||||
cursorCol: cursorCol - prefix.length + item.value.length,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
editor.setAutocompleteProvider(mockProvider);
|
||||
|
||||
// Type "Work"
|
||||
editor.handleInput("W");
|
||||
editor.handleInput("o");
|
||||
editor.handleInput("r");
|
||||
editor.handleInput("k");
|
||||
assert.strictEqual(editor.getText(), "Work");
|
||||
|
||||
// Press Tab - should auto-apply without showing menu
|
||||
editor.handleInput("\t");
|
||||
assert.strictEqual(editor.getText(), "Workspace/");
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), false);
|
||||
|
||||
// Undo should restore to "Work"
|
||||
editor.handleInput("\x1b[45;5u"); // Ctrl+- (undo)
|
||||
assert.strictEqual(editor.getText(), "Work");
|
||||
});
|
||||
|
||||
it("shows menu when force-file has multiple suggestions", () => {
|
||||
const editor = new Editor(createTestTUI(), defaultEditorTheme);
|
||||
|
||||
// Create a mock provider with getForceFileSuggestions that returns multiple items
|
||||
const mockProvider: AutocompleteProvider & {
|
||||
getForceFileSuggestions: AutocompleteProvider["getSuggestions"];
|
||||
} = {
|
||||
getSuggestions: () => null,
|
||||
getForceFileSuggestions: (lines, _cursorLine, cursorCol) => {
|
||||
const text = lines[0] || "";
|
||||
const prefix = text.slice(0, cursorCol);
|
||||
if (prefix === "src") {
|
||||
return {
|
||||
items: [
|
||||
{ value: "src/", label: "src/" },
|
||||
{ value: "src.txt", label: "src.txt" },
|
||||
],
|
||||
prefix: "src",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
applyCompletion: (lines, cursorLine, cursorCol, item, prefix) => {
|
||||
const line = lines[cursorLine] || "";
|
||||
const before = line.slice(0, cursorCol - prefix.length);
|
||||
const after = line.slice(cursorCol);
|
||||
const newLines = [...lines];
|
||||
newLines[cursorLine] = before + item.value + after;
|
||||
return {
|
||||
lines: newLines,
|
||||
cursorLine,
|
||||
cursorCol: cursorCol - prefix.length + item.value.length,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
editor.setAutocompleteProvider(mockProvider);
|
||||
|
||||
// Type "src"
|
||||
editor.handleInput("s");
|
||||
editor.handleInput("r");
|
||||
editor.handleInput("c");
|
||||
assert.strictEqual(editor.getText(), "src");
|
||||
|
||||
// Press Tab - should show menu because there are multiple suggestions
|
||||
editor.handleInput("\t");
|
||||
assert.strictEqual(editor.getText(), "src"); // Text unchanged
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), true);
|
||||
|
||||
// Press Tab again to accept first suggestion
|
||||
editor.handleInput("\t");
|
||||
assert.strictEqual(editor.getText(), "src/");
|
||||
assert.strictEqual(editor.isShowingAutocomplete(), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue