mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-20 18:14:29 -07:00
fix: resolve infinite loop when using 'Modify with external editor' (#7669)
This fix addresses the infinite loop issue reported in #7669 where selecting "Modify with external editor" would loop indefinitely when no editor was configured or available. Root cause: When getPreferredEditor() returned undefined, the code silently returned without changing the outcome, causing the while loop to repeat. Changes: - Add detectFirstAvailableEditor() to auto-detect available editors - Add resolveEditor() to handle editor resolution with proper error messages - Update confirmation.ts to break the loop and show error when editor unavailable - Update coreToolScheduler.ts to cancel operation with feedback when editor unavailable - Add 11 new tests for the new editor resolution functions The fix: 1. Properly validates editor availability before attempting to use it 2. Auto-detects an available editor if none is configured 3. Provides clear error messages explaining why the editor cannot be used 4. Breaks the loop gracefully instead of looping infinitely
This commit is contained in:
@@ -19,6 +19,8 @@ import {
|
||||
openDiff,
|
||||
allowEditorTypeInSandbox,
|
||||
isEditorAvailable,
|
||||
detectFirstAvailableEditor,
|
||||
resolveEditor,
|
||||
type EditorType,
|
||||
} from './editor.js';
|
||||
import { execSync, spawn, spawnSync } from 'node:child_process';
|
||||
@@ -542,4 +544,130 @@ describe('editor utils', () => {
|
||||
expect(isEditorAvailable('neovim')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectFirstAvailableEditor', () => {
|
||||
it('should return undefined when no editors are installed', () => {
|
||||
(execSync as Mock).mockImplementation(() => {
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
expect(detectFirstAvailableEditor()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should prioritize terminal editors over GUI editors', () => {
|
||||
// Mock vim as available
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('vim') && !cmd.includes('nvim')) {
|
||||
return Buffer.from('/usr/bin/vim');
|
||||
}
|
||||
if (cmd.includes('code')) {
|
||||
return Buffer.from('/usr/bin/code');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
expect(detectFirstAvailableEditor()).toBe('vim');
|
||||
});
|
||||
|
||||
it('should return vim when vim is the only editor available in sandbox mode', () => {
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('vim') && !cmd.includes('nvim')) {
|
||||
return Buffer.from('/usr/bin/vim');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', 'sandbox');
|
||||
expect(detectFirstAvailableEditor()).toBe('vim');
|
||||
});
|
||||
|
||||
it('should skip GUI editors in sandbox mode', () => {
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('code')) {
|
||||
return Buffer.from('/usr/bin/code');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', 'sandbox');
|
||||
// vscode is installed but not allowed in sandbox
|
||||
expect(detectFirstAvailableEditor()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return first available terminal editor (neovim)', () => {
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('nvim')) {
|
||||
return Buffer.from('/usr/bin/nvim');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
expect(detectFirstAvailableEditor()).toBe('neovim');
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveEditor', () => {
|
||||
it('should return the preferred editor when available', () => {
|
||||
(execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/vim'));
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
const result = resolveEditor('vim');
|
||||
expect(result.editor).toBe('vim');
|
||||
expect(result.error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return error when preferred editor is not installed', () => {
|
||||
(execSync as Mock).mockImplementation(() => {
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
const result = resolveEditor('vim');
|
||||
expect(result.editor).toBeUndefined();
|
||||
expect(result.error).toContain('Vim');
|
||||
expect(result.error).toContain('not installed');
|
||||
});
|
||||
|
||||
it('should return error when preferred GUI editor cannot be used in sandbox mode', () => {
|
||||
(execSync as Mock).mockReturnValue(Buffer.from('/usr/bin/code'));
|
||||
vi.stubEnv('SANDBOX', 'sandbox');
|
||||
const result = resolveEditor('vscode');
|
||||
expect(result.editor).toBeUndefined();
|
||||
expect(result.error).toContain('VS Code');
|
||||
expect(result.error).toContain('sandbox mode');
|
||||
});
|
||||
|
||||
it('should auto-detect editor when no preference is set', () => {
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('vim') && !cmd.includes('nvim')) {
|
||||
return Buffer.from('/usr/bin/vim');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
const result = resolveEditor(undefined);
|
||||
expect(result.editor).toBe('vim');
|
||||
expect(result.error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return error when no preference is set and no editors are available', () => {
|
||||
(execSync as Mock).mockImplementation(() => {
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', '');
|
||||
const result = resolveEditor(undefined);
|
||||
expect(result.editor).toBeUndefined();
|
||||
expect(result.error).toContain('No external editor');
|
||||
expect(result.error).toContain('/editor');
|
||||
});
|
||||
|
||||
it('should work with terminal editors in sandbox mode when no preference is set', () => {
|
||||
(execSync as Mock).mockImplementation((cmd: string) => {
|
||||
if (cmd.includes('vim') && !cmd.includes('nvim')) {
|
||||
return Buffer.from('/usr/bin/vim');
|
||||
}
|
||||
throw new Error('Command not found');
|
||||
});
|
||||
vi.stubEnv('SANDBOX', 'sandbox');
|
||||
const result = resolveEditor(undefined);
|
||||
expect(result.editor).toBe('vim');
|
||||
expect(result.error).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user