feat(cli): add filepath autosuggestion after slash commands (#14738)

Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
This commit is contained in:
Jasmeet Bhatia
2026-01-07 17:47:05 -08:00
committed by GitHub
parent 41cc6cf105
commit d48c934357
2 changed files with 127 additions and 9 deletions

View File

@@ -585,4 +585,117 @@ describe('useCommandCompletion', () => {
);
});
});
describe('@ completion after slash commands (issue #14420)', () => {
it('should show file suggestions when typing @path after a slash command', async () => {
setupMocks({
atSuggestions: [{ label: 'src/file.txt', value: 'src/file.txt' }],
});
const text = '/mycommand @src/fi';
const cursorOffset = text.length;
renderCommandCompletionHook(text, cursorOffset);
await waitFor(() => {
expect(useAtCompletion).toHaveBeenLastCalledWith(
expect.objectContaining({
enabled: true,
pattern: 'src/fi',
}),
);
});
});
it('should show slash suggestions when cursor is on command part (no @)', async () => {
setupMocks({
slashSuggestions: [{ label: 'mycommand', value: 'mycommand' }],
});
const text = '/mycom';
const cursorOffset = text.length;
const { result } = renderCommandCompletionHook(text, cursorOffset);
await waitFor(() => {
expect(result.current.suggestions).toHaveLength(1);
expect(result.current.suggestions[0]?.label).toBe('mycommand');
});
});
it('should switch to @ completion when typing @ after slash command', async () => {
setupMocks({
atSuggestions: [{ label: 'file.txt', value: 'file.txt' }],
});
const text = '/command @';
const cursorOffset = text.length;
renderCommandCompletionHook(text, cursorOffset);
await waitFor(() => {
expect(useAtCompletion).toHaveBeenLastCalledWith(
expect.objectContaining({
enabled: true,
pattern: '',
}),
);
});
});
it('should handle multiple @ references in a slash command', async () => {
setupMocks({
atSuggestions: [{ label: 'src/bar.ts', value: 'src/bar.ts' }],
});
const text = '/diff @src/foo.ts @src/ba';
const cursorOffset = text.length;
renderCommandCompletionHook(text, cursorOffset);
await waitFor(() => {
expect(useAtCompletion).toHaveBeenLastCalledWith(
expect.objectContaining({
enabled: true,
pattern: 'src/ba',
}),
);
});
});
it('should complete file path and add trailing space', async () => {
setupMocks({
atSuggestions: [{ label: 'src/file.txt', value: 'src/file.txt' }],
});
const { result } = renderCommandCompletionHook('/cmd @src/fi');
await waitFor(() => {
expect(result.current.suggestions.length).toBe(1);
});
act(() => {
result.current.handleAutocomplete(0);
});
expect(result.current.textBuffer.text).toBe('/cmd @src/file.txt ');
});
it('should stay in slash mode when slash command has trailing space but no @', async () => {
setupMocks({
slashSuggestions: [{ label: 'help', value: 'help' }],
});
const text = '/help ';
renderCommandCompletionHook(text);
await waitFor(() => {
expect(useSlashCompletion).toHaveBeenLastCalledWith(
expect.objectContaining({
enabled: true,
}),
);
});
});
});
});

View File

@@ -92,16 +92,11 @@ export function useCommandCompletion(
const { completionMode, query, completionStart, completionEnd } =
useMemo(() => {
const currentLine = buffer.lines[cursorRow] || '';
if (cursorRow === 0 && isSlashCommand(currentLine.trim())) {
return {
completionMode: CompletionMode.SLASH,
query: currentLine,
completionStart: 0,
completionEnd: currentLine.length,
};
}
const codePoints = toCodePoints(currentLine);
// FIRST: Check for @ completion (scan backwards from cursor)
// This must happen before slash command check so that `/cmd @file`
// triggers file completion, not just slash command completion.
for (let i = cursorCol - 1; i >= 0; i--) {
const char = codePoints[i];
@@ -139,6 +134,16 @@ export function useCommandCompletion(
}
}
// THEN: Check for slash command (only if no @ completion is active)
if (cursorRow === 0 && isSlashCommand(currentLine.trim())) {
return {
completionMode: CompletionMode.SLASH,
query: currentLine,
completionStart: 0,
completionEnd: currentLine.length,
};
}
// Check for prompt completion - only if enabled
const trimmedText = buffer.text.trim();
const isPromptCompletionEnabled =