fix: resolve infinite loop when using 'Modify with external editor' (#17453)

Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
Co-authored-by: ehedlund <ehedlund@google.com>
This commit is contained in:
Philippe
2026-02-05 21:52:41 +01:00
committed by GitHub
parent 8efae719ee
commit 2498114df6
8 changed files with 336 additions and 51 deletions

View File

@@ -525,12 +525,22 @@ export const AppContainer = (props: AppContainerProps) => {
refreshStatic();
}, [refreshStatic, isAlternateBuffer, app, config]);
const [editorError, setEditorError] = useState<string | null>(null);
const {
isEditorDialogOpen,
openEditorDialog,
handleEditorSelect,
exitEditorDialog,
} = useEditorSettings(settings, setEditorError, historyManager.addItem);
useEffect(() => {
coreEvents.on(CoreEvent.ExternalEditorClosed, handleEditorClose);
coreEvents.on(CoreEvent.RequestEditorSelection, openEditorDialog);
return () => {
coreEvents.off(CoreEvent.ExternalEditorClosed, handleEditorClose);
coreEvents.off(CoreEvent.RequestEditorSelection, openEditorDialog);
};
}, [handleEditorClose]);
}, [handleEditorClose, openEditorDialog]);
useEffect(() => {
if (
@@ -544,6 +554,9 @@ export const AppContainer = (props: AppContainerProps) => {
}
}, [bannerVisible, bannerText, settings, config, refreshStatic]);
const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } =
useSettingsCommand();
const {
isThemeDialogOpen,
openThemeDialog,
@@ -739,17 +752,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
onAuthError,
]);
const [editorError, setEditorError] = useState<string | null>(null);
const {
isEditorDialogOpen,
openEditorDialog,
handleEditorSelect,
exitEditorDialog,
} = useEditorSettings(settings, setEditorError, historyManager.addItem);
const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } =
useSettingsCommand();
const { isModelDialogOpen, openModelDialog, closeModelDialog } =
useModelCommand();

View File

@@ -6,7 +6,7 @@
import {
allowEditorTypeInSandbox,
checkHasEditorType,
hasValidEditorCommand,
type EditorType,
EDITOR_DISPLAY_NAMES,
} from '@google/gemini-cli-core';
@@ -31,7 +31,7 @@ class EditorSettingsManager {
disabled: false,
},
...editorTypes.map((type) => {
const hasEditor = checkHasEditorType(type);
const hasEditor = hasValidEditorCommand(type);
const isAllowedInSandbox = allowEditorTypeInSandbox(type);
let labelSuffix = !isAllowedInSandbox

View File

@@ -24,7 +24,7 @@ import { SettingScope } from '../../config/settings.js';
import { MessageType } from '../types.js';
import {
type EditorType,
checkHasEditorType,
hasValidEditorCommand,
allowEditorTypeInSandbox,
} from '@google/gemini-cli-core';
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
@@ -35,12 +35,12 @@ vi.mock('@google/gemini-cli-core', async () => {
const actual = await vi.importActual('@google/gemini-cli-core');
return {
...actual,
checkHasEditorType: vi.fn(() => true),
hasValidEditorCommand: vi.fn(() => true),
allowEditorTypeInSandbox: vi.fn(() => true),
};
});
const mockCheckHasEditorType = vi.mocked(checkHasEditorType);
const mockHasValidEditorCommand = vi.mocked(hasValidEditorCommand);
const mockAllowEditorTypeInSandbox = vi.mocked(allowEditorTypeInSandbox);
describe('useEditorSettings', () => {
@@ -69,7 +69,7 @@ describe('useEditorSettings', () => {
mockAddItem = vi.fn();
// Reset mock implementations to default
mockCheckHasEditorType.mockReturnValue(true);
mockHasValidEditorCommand.mockReturnValue(true);
mockAllowEditorTypeInSandbox.mockReturnValue(true);
});
@@ -224,7 +224,7 @@ describe('useEditorSettings', () => {
it('should not set preference for unavailable editors', () => {
render(<TestComponent />);
mockCheckHasEditorType.mockReturnValue(false);
mockHasValidEditorCommand.mockReturnValue(false);
const editorType: EditorType = 'vscode';
const scope = SettingScope.User;

View File

@@ -13,8 +13,10 @@ import { MessageType } from '../types.js';
import type { EditorType } from '@google/gemini-cli-core';
import {
allowEditorTypeInSandbox,
checkHasEditorType,
hasValidEditorCommand,
getEditorDisplayName,
coreEvents,
CoreEvent,
} from '@google/gemini-cli-core';
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
@@ -45,7 +47,7 @@ export const useEditorSettings = (
(editorType: EditorType | undefined, scope: LoadableSettingScope) => {
if (
editorType &&
(!checkHasEditorType(editorType) ||
(!hasValidEditorCommand(editorType) ||
!allowEditorTypeInSandbox(editorType))
) {
return;
@@ -66,6 +68,7 @@ export const useEditorSettings = (
);
setEditorError(null);
setIsEditorDialogOpen(false);
coreEvents.emit(CoreEvent.EditorSelected, { editor: editorType });
} catch (error) {
setEditorError(`Failed to set editor preference: ${error}`);
}
@@ -75,6 +78,7 @@ export const useEditorSettings = (
const exitEditorDialog = useCallback(() => {
setIsEditorDialogOpen(false);
coreEvents.emit(CoreEvent.EditorSelected, { editor: undefined });
}, []);
return {