Skip to content

Commit 4775b6d

Browse files
committed
Refactored out side effect in packages/cli/src/ui/components/EditorSettingsDialog.tsx
1 parent 2361895 commit 4775b6d

File tree

2 files changed

+180
-9
lines changed

2 files changed

+180
-9
lines changed

packages/cli/src/ui/components/EditorSettingsDialog.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import type React from 'react';
8-
import { useState } from 'react';
8+
import { useEffect, useState } from 'react';
99
import { Box, Text } from 'ink';
1010
import { theme } from '../semantic-colors.js';
1111
import {
@@ -63,17 +63,26 @@ export function EditorSettingsDialog({
6363

6464
const currentPreference =
6565
settings.forScope(selectedScope).settings.general?.preferredEditor;
66+
67+
useEffect(() => {
68+
const isUnsupported =
69+
currentPreference &&
70+
editorItems.findIndex((item) => item.type === currentPreference) === -1;
71+
72+
if (isUnsupported) {
73+
coreEvents.emitFeedback(
74+
'error',
75+
`Editor is not supported: ${currentPreference}`,
76+
);
77+
onSelect(undefined, selectedScope);
78+
}
79+
}, [currentPreference, editorItems, onSelect, selectedScope]);
80+
6681
let editorIndex = currentPreference
67-
? editorItems.findIndex(
68-
(item: EditorDisplay) => item.type === currentPreference,
69-
)
82+
? editorItems.findIndex((item) => item.type === currentPreference)
7083
: 0;
84+
7185
if (editorIndex === -1) {
72-
coreEvents.emitFeedback(
73-
'error',
74-
`Editor is not supported: ${currentPreference}`,
75-
);
76-
onSelect(undefined, selectedScope);
7786
editorIndex = 0;
7887
}
7988

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {
8+
EDITOR_DISPLAY_NAMES,
9+
type EditorType,
10+
coreEvents,
11+
isEditorAvailable,
12+
} from '@google/gemini-cli-core';
13+
import { useCallback, useEffect, useMemo, useState } from 'react';
14+
import {
15+
SettingScope,
16+
type LoadableSettingScope,
17+
type LoadedSettings,
18+
} from '../../config/settings.js';
19+
import {
20+
editorSettingsManager,
21+
type EditorDisplay,
22+
} from '../editors/editorSettingsManager.js';
23+
import { useKeypress } from './useKeypress.js';
24+
25+
type FocusedSection = 'editor' | 'scope';
26+
27+
const SCOPE_ITEMS: Array<{
28+
label: string;
29+
value: LoadableSettingScope;
30+
key: string;
31+
}> = [
32+
{
33+
label: 'User Settings',
34+
value: SettingScope.User,
35+
key: SettingScope.User,
36+
},
37+
{
38+
label: 'Workspace Settings',
39+
value: SettingScope.Workspace,
40+
key: SettingScope.Workspace,
41+
},
42+
];
43+
44+
export function useEditorSettingsDialog(
45+
settings: LoadedSettings,
46+
onSelect: (
47+
editorType: EditorType | undefined,
48+
scope: LoadableSettingScope,
49+
) => void,
50+
onExit: () => void,
51+
) {
52+
const [selectedScope, setSelectedScope] = useState<LoadableSettingScope>(
53+
SettingScope.User,
54+
);
55+
const [focusedSection, setFocusedSection] =
56+
useState<FocusedSection>('editor');
57+
58+
useKeypress(
59+
(key) => {
60+
if (key.name === 'tab') {
61+
setFocusedSection((prev) => (prev === 'editor' ? 'scope' : 'editor'));
62+
}
63+
if (key.name === 'escape') {
64+
onExit();
65+
}
66+
},
67+
{ isActive: true },
68+
);
69+
70+
const editorItems: EditorDisplay[] = useMemo(
71+
() => editorSettingsManager.getAvailableEditorDisplays(),
72+
[],
73+
);
74+
75+
const currentPreference =
76+
settings.forScope(selectedScope).settings.general?.preferredEditor;
77+
78+
useEffect(() => {
79+
if (!currentPreference) {
80+
return;
81+
}
82+
83+
const isInvalid =
84+
editorItems.findIndex(
85+
(item: EditorDisplay) => item.type === currentPreference,
86+
) === -1;
87+
88+
if (isInvalid) {
89+
coreEvents.emitFeedback(
90+
'error',
91+
`Editor is not supported: ${currentPreference}`,
92+
);
93+
onSelect(undefined, selectedScope);
94+
}
95+
}, [currentPreference, editorItems, onSelect, selectedScope]);
96+
97+
const editorIndex = useMemo(
98+
() =>
99+
Math.max(
100+
0,
101+
currentPreference
102+
? editorItems.findIndex(
103+
(item: EditorDisplay) => item.type === currentPreference,
104+
)
105+
: 0,
106+
),
107+
[currentPreference, editorItems],
108+
);
109+
110+
const handleEditorSelect = useCallback(
111+
(editorType: EditorType | 'not_set') => {
112+
if (editorType === 'not_set') {
113+
onSelect(undefined, selectedScope);
114+
return;
115+
}
116+
onSelect(editorType, selectedScope);
117+
},
118+
[onSelect, selectedScope],
119+
);
120+
121+
const handleScopeSelect = useCallback((scope: LoadableSettingScope) => {
122+
setSelectedScope(scope);
123+
setFocusedSection('editor');
124+
}, []);
125+
126+
const otherScopeModifiedMessage = useMemo(() => {
127+
const otherScope =
128+
selectedScope === SettingScope.User
129+
? SettingScope.Workspace
130+
: SettingScope.User;
131+
if (
132+
settings.forScope(otherScope).settings.general?.preferredEditor !==
133+
undefined
134+
) {
135+
return settings.forScope(selectedScope).settings.general
136+
?.preferredEditor !== undefined
137+
? `(Also modified in ${otherScope})`
138+
: `(Modified in ${otherScope})`;
139+
}
140+
return '';
141+
}, [selectedScope, settings]);
142+
143+
const mergedEditorName = useMemo(() => {
144+
const { preferredEditor } = settings.merged.general ?? {};
145+
if (preferredEditor && isEditorAvailable(preferredEditor)) {
146+
return EDITOR_DISPLAY_NAMES[preferredEditor as EditorType];
147+
}
148+
return 'None';
149+
}, [settings.merged.general]);
150+
151+
return {
152+
selectedScope,
153+
focusedSection,
154+
editorItems,
155+
editorIndex,
156+
scopeItems: SCOPE_ITEMS,
157+
handleEditorSelect,
158+
handleScopeSelect,
159+
otherScopeModifiedMessage,
160+
mergedEditorName,
161+
};
162+
}

0 commit comments

Comments
 (0)