Fix(vim): Fix shell mode in Vim mode (#5567)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
parent
12a9bc3ed9
commit
91035ad7b0
|
@ -1203,7 +1203,9 @@ describe('useVim hook', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Press escape to clear pending state
|
// Press escape to clear pending state
|
||||||
exitInsertMode(result);
|
act(() => {
|
||||||
|
result.current.handleInput({ name: 'escape' });
|
||||||
|
});
|
||||||
|
|
||||||
// Now 'w' should just move cursor, not delete
|
// Now 'w' should just move cursor, not delete
|
||||||
act(() => {
|
act(() => {
|
||||||
|
@ -1215,6 +1217,69 @@ describe('useVim hook', () => {
|
||||||
expect(testBuffer.vimMoveWordForward).toHaveBeenCalledWith(1);
|
expect(testBuffer.vimMoveWordForward).toHaveBeenCalledWith(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('NORMAL mode escape behavior', () => {
|
||||||
|
it('should pass escape through when no pending operator is active', () => {
|
||||||
|
mockVimContext.vimMode = 'NORMAL';
|
||||||
|
const { result } = renderVimHook();
|
||||||
|
|
||||||
|
const handled = result.current.handleInput({ name: 'escape' });
|
||||||
|
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle escape and clear pending operator', () => {
|
||||||
|
mockVimContext.vimMode = 'NORMAL';
|
||||||
|
const { result } = renderVimHook();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.handleInput({ sequence: 'd' });
|
||||||
|
});
|
||||||
|
|
||||||
|
let handled: boolean | undefined;
|
||||||
|
act(() => {
|
||||||
|
handled = result.current.handleInput({ name: 'escape' });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(handled).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Shell command pass-through', () => {
|
||||||
|
it('should pass through ctrl+r in INSERT mode', () => {
|
||||||
|
mockVimContext.vimMode = 'INSERT';
|
||||||
|
const { result } = renderVimHook();
|
||||||
|
|
||||||
|
const handled = result.current.handleInput({ name: 'r', ctrl: true });
|
||||||
|
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass through ! in INSERT mode when buffer is empty', () => {
|
||||||
|
mockVimContext.vimMode = 'INSERT';
|
||||||
|
const emptyBuffer = createMockBuffer('');
|
||||||
|
const { result } = renderVimHook(emptyBuffer);
|
||||||
|
|
||||||
|
const handled = result.current.handleInput({ sequence: '!' });
|
||||||
|
|
||||||
|
expect(handled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle ! as input in INSERT mode when buffer is not empty', () => {
|
||||||
|
mockVimContext.vimMode = 'INSERT';
|
||||||
|
const nonEmptyBuffer = createMockBuffer('not empty');
|
||||||
|
const { result } = renderVimHook(nonEmptyBuffer);
|
||||||
|
const key = { sequence: '!', name: '!' };
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.handleInput(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(nonEmptyBuffer.handleInput).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining(key),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Line operations (dd, cc) are tested in text-buffer.test.ts
|
// Line operations (dd, cc) are tested in text-buffer.test.ts
|
||||||
|
|
|
@ -260,7 +260,8 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
|
||||||
normalizedKey.name === 'tab' ||
|
normalizedKey.name === 'tab' ||
|
||||||
(normalizedKey.name === 'return' && !normalizedKey.ctrl) ||
|
(normalizedKey.name === 'return' && !normalizedKey.ctrl) ||
|
||||||
normalizedKey.name === 'up' ||
|
normalizedKey.name === 'up' ||
|
||||||
normalizedKey.name === 'down'
|
normalizedKey.name === 'down' ||
|
||||||
|
(normalizedKey.ctrl && normalizedKey.name === 'r')
|
||||||
) {
|
) {
|
||||||
return false; // Let InputPrompt handle completion
|
return false; // Let InputPrompt handle completion
|
||||||
}
|
}
|
||||||
|
@ -270,6 +271,11 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
|
||||||
return false; // Let InputPrompt handle clipboard functionality
|
return false; // Let InputPrompt handle clipboard functionality
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let InputPrompt handle shell commands
|
||||||
|
if (normalizedKey.sequence === '!' && buffer.text.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Special handling for Enter key to allow command submission (lower priority than completion)
|
// Special handling for Enter key to allow command submission (lower priority than completion)
|
||||||
if (
|
if (
|
||||||
normalizedKey.name === 'return' &&
|
normalizedKey.name === 'return' &&
|
||||||
|
@ -399,10 +405,14 @@ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
|
||||||
|
|
||||||
// Handle NORMAL mode
|
// Handle NORMAL mode
|
||||||
if (state.mode === 'NORMAL') {
|
if (state.mode === 'NORMAL') {
|
||||||
// Handle Escape key in NORMAL mode - clear all pending states
|
// If in NORMAL mode, allow escape to pass through to other handlers
|
||||||
|
// if there's no pending operation.
|
||||||
if (normalizedKey.name === 'escape') {
|
if (normalizedKey.name === 'escape') {
|
||||||
dispatch({ type: 'CLEAR_PENDING_STATES' });
|
if (state.pendingOperator) {
|
||||||
return true; // Handled by vim
|
dispatch({ type: 'CLEAR_PENDING_STATES' });
|
||||||
|
return true; // Handled by vim
|
||||||
|
}
|
||||||
|
return false; // Pass through to other handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle count input (numbers 1-9, and 0 if count > 0)
|
// Handle count input (numbers 1-9, and 0 if count > 0)
|
||||||
|
|
Loading…
Reference in New Issue