Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
28ba71a
feat(cli): added external editor Sublime
alberti42 Mar 3, 2026
5504fba
feat(cli): added external editor emacsclient
alberti42 Mar 3, 2026
7be3c61
fix(cli): gracefully handle unrecognized or missing editor commands
alberti42 Mar 3, 2026
37b7fe1
fix(cli): address code review issues raised by gemini-code-assist
alberti42 Mar 3, 2026
64c9ed7
docs(cli): improve preferredEditor description in settings schema and…
alberti42 Mar 4, 2026
9933468
fix(cli): address code review issues raised by gemini-code-assist
alberti42 Mar 4, 2026
c4a056c
fix(cli): address code review issues raised by @jacob314
alberti42 Mar 5, 2026
2e65b7a
fix(editor): shows descriptive errors when error command fails
AnanthKini1 Mar 6, 2026
b9a5e74
fix(editor): removes emitFeedback from EditorSettingsDialog render body
AnanthKini1 Mar 6, 2026
201e285
fix(editor): add --new-window flag for VS Code-family editors
AnanthKini1 Mar 6, 2026
b1019d0
docs(editor): document preferredEditor accepted values and env fallback
alberti42 Mar 11, 2026
b2732b7
fix(editor): applies editor-specific args when using /
AnanthKini1 Mar 6, 2026
cc261b8
fix(cli): restore short preferredEditor description per review feedback
alberti42 Mar 6, 2026
bde4eef
fix(cli): generate preferredEditor enum from settingsSchema.ts
alberti42 Mar 6, 2026
1edb4f3
refactor: derive preferredEditor options from EDITOR_OPTIONS
alberti42 Mar 11, 2026
00f72d3
refactor: extract HEURISTIC_GUI_COMMANDS const
alberti42 Mar 11, 2026
41bf860
fix: replace throw with emitFeedback in openFileInEditor
alberti42 Mar 11, 2026
3e4de2e
fix: replace unsafe EditorType cast with isValidEditorType
alberti42 Mar 11, 2026
21b0a8b
feat: guard --new-window behind openEditorInNewWindow setting
alberti42 Mar 11, 2026
af27839
docs: add comment explaining HEURISTIC_GUI_COMMANDS
alberti42 Mar 11, 2026
0f62a18
fix: add antigravity to NEW_WINDOW_EDITORS and HEURISTIC_GUI_COMMANDS
alberti42 Mar 11, 2026
fd7d811
refactor: read openEditorInNewWindow directly via useSettings in text…
alberti42 Mar 11, 2026
73bc237
refactor(cli): remove unused coreEvents and cleanup type assertion in…
alberti42 Apr 10, 2026
8af6a6c
fix(test): mock SettingsContext in useTextBuffer test suites
alberti42 May 19, 2026
b3f6642
style(cli): apply Prettier formatting to EditorSettingsDialog
alberti42 May 19, 2026
e8a0acc
fix(editor): resolveEditorTypeFromCommand recognizes commands across …
alberti42 May 19, 2026
e509136
fix(test): pin platform=linux in emacs getDiffCommand escaped-paths test
alberti42 May 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions docs/reference/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,19 @@ their corresponding top-level category object in your `settings.json` file.

#### `general`

- **`general.preferredEditor`** (string):
- **Description:** The preferred editor to open files in.
- **`general.preferredEditor`** (enum):
- **Description:** The preferred editor to open files in. Must be one of the
built-in supported identifiers. Use /editor in the CLI to pick
interactively, or leave unset to use $VISUAL/$EDITOR.
- **Default:** `undefined`
- **Values:** `"vscode"`, `"vscodium"`, `"windsurf"`, `"cursor"`, `"zed"`,
`"antigravity"`, `"sublimetext"`, `"lapce"`, `"nova"`, `"bbedit"`, `"vim"`,
`"neovim"`, `"emacs"`, `"hx"`, `"emacsclient"`, `"micro"`

- **`general.openEditorInNewWindow`** (boolean):
- **Description:** Open VS Code-family editors in a new window when editing
files.
- **Default:** `false`

- **`general.vimMode`** (boolean):
- **Description:** Enable Vim keybindings
Expand Down
20 changes: 18 additions & 2 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
DEFAULT_MODEL_CONFIGS,
EDITOR_OPTIONS,
AuthProviderType,
type MCPServerConfig,
type RequiredMcpServerConfig,
Expand Down Expand Up @@ -192,12 +193,27 @@ const SETTINGS_SCHEMA = {
showInDialog: false,
properties: {
preferredEditor: {
type: 'string',
type: 'enum',
label: 'Preferred Editor',
category: 'General',
requiresRestart: false,
default: undefined as string | undefined,
description: 'The preferred editor to open files in.',
description: oneLine`
The preferred editor to open files in. Must be one of the built-in
supported identifiers. Use /editor in the CLI to pick interactively,
or leave unset to use $VISUAL/$EDITOR.
`,
showInDialog: false,
options: EDITOR_OPTIONS,
},
openEditorInNewWindow: {
type: 'boolean',
label: 'Open Editor in New Window',
category: 'General',
requiresRestart: false,
default: false,
description:
'Open VS Code-family editors in a new window when editing files.',
showInDialog: false,
},
vimMode: {
Expand Down
11 changes: 5 additions & 6 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import { MouseProvider } from './contexts/MouseContext.js';
import { ScrollProvider } from './contexts/ScrollProvider.js';
import {
type StartupWarning,
type EditorType,
type Config,
type IdeInfo,
type IdeContext,
Expand All @@ -68,6 +67,7 @@ import {
ShellExecutionService,
saveApiKey,
debugLogger,
isValidEditorType,
coreEvents,
CoreEvent,
flattenMemory,
Expand Down Expand Up @@ -609,11 +609,10 @@ export const AppContainer = (props: AppContainerProps) => {

const staticAreaMaxItemHeight = Math.max(terminalHeight * 4, 100);

const getPreferredEditor = useCallback(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
() => settings.merged.general.preferredEditor as EditorType,
[settings.merged.general.preferredEditor],
);
const getPreferredEditor = useCallback(() => {
const val = settings.merged.general.preferredEditor;
return isValidEditorType(val) ? val : undefined;
}, [settings.merged.general.preferredEditor]);

const buffer = useTextBuffer({
initialText: '',
Expand Down
11 changes: 2 additions & 9 deletions packages/cli/src/ui/components/EditorSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
type EditorType,
isEditorAvailable,
EDITOR_DISPLAY_NAMES,
coreEvents,
} from '@google/gemini-cli-core';
import { useKeypress } from '../hooks/useKeypress.js';

Expand Down Expand Up @@ -72,10 +71,6 @@ export function EditorSettingsDialog({
)
: 0;
if (editorIndex === -1) {
coreEvents.emitFeedback(
'error',
`Editor is not supported: ${currentPreference}`,
);
editorIndex = 0;
}

Expand Down Expand Up @@ -131,10 +126,7 @@ export function EditorSettingsDialog({
isEditorAvailable(settings.merged.general.preferredEditor)
) {
mergedEditorName =
EDITOR_DISPLAY_NAMES[
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
settings.merged.general.preferredEditor as EditorType
];
EDITOR_DISPLAY_NAMES[settings.merged.general.preferredEditor];
}

return (
Expand All @@ -161,6 +153,7 @@ export function EditorSettingsDialog({
onSelect={handleEditorSelect}
isFocused={focusedSection === 'editor'}
key={selectedScope}
maxItemsToShow={editorItems.length}
/>

<Box marginTop={1} flexDirection="column">
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/ui/components/shared/performance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import { renderHook } from '../../../test-utils/render.js';
import { useTextBuffer } from './text-buffer.js';
import { parseInputForHighlighting } from '../../utils/highlight.js';

vi.mock('../../contexts/SettingsContext.js', async (importOriginal) => {
const actual =
await importOriginal<typeof import('../../contexts/SettingsContext.js')>();
return {
...actual,
useSettings: () => ({
merged: { general: { openEditorInNewWindow: false } },
}),
};
});

describe('text-buffer performance', () => {
afterEach(() => {
vi.restoreAllMocks();
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/ui/components/shared/text-buffer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ import { cpLen } from '../../utils/textUtils.js';
import { type Key } from '../../hooks/useKeypress.js';
import { escapePath } from '@google/gemini-cli-core';

vi.mock('../../contexts/SettingsContext.js', async (importOriginal) => {
const actual =
await importOriginal<typeof import('../../contexts/SettingsContext.js')>();
return {
...actual,
useSettings: () => ({
merged: { general: { openEditorInNewWindow: false } },
}),
};
});

const defaultVisualLayout: VisualLayout = {
visualLines: [''],
logicalToVisualMap: [[[0, 0]]],
Expand Down
19 changes: 13 additions & 6 deletions packages/cli/src/ui/components/shared/text-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { LRUCache } from 'mnemonist';
import {
coreEvents,
debugLogger,
getErrorMessage,
unescapePath,
type EditorType,
} from '@google/gemini-cli-core';
Expand All @@ -30,6 +31,7 @@ import type { VimAction } from './vim-buffer-actions.js';
import { handleVimAction } from './vim-buffer-actions.js';
import { LRU_BUFFER_PERF_CACHE_LIMIT } from '../../constants.js';
import { openFileInEditor } from '../../utils/editorUtils.js';
import { useSettings } from '../../contexts/SettingsContext.js';
import { useKeyMatchers } from '../../hooks/useKeyMatchers.js';

export const LARGE_PASTE_LINE_THRESHOLD = 5;
Expand Down Expand Up @@ -2840,6 +2842,7 @@ export function useTextBuffer({
singleLine = false,
getPreferredEditor,
}: UseTextBufferProps): TextBuffer {
const settings = useSettings();
const keyMatchers = useKeyMatchers();
const initialState = useMemo((): TextBufferState => {
const lines = initialText.split('\n');
Expand Down Expand Up @@ -3325,6 +3328,7 @@ export function useTextBuffer({
stdin,
setRawMode,
getPreferredEditor?.(),
settings.merged.general.openEditorInNewWindow,
);

let newText = fs.readFileSync(filePath, 'utf8');
Expand All @@ -3342,11 +3346,7 @@ export function useTextBuffer({

dispatch({ type: 'set_text', payload: newText, pushToUndo: false });
} catch (err) {
coreEvents.emitFeedback(
'error',
'[useTextBuffer] external editor error',
err,
);
coreEvents.emitFeedback('error', getErrorMessage(err), err);
} finally {
try {
fs.unlinkSync(filePath);
Expand All @@ -3359,7 +3359,14 @@ export function useTextBuffer({
/* ignore */
}
}
}, [text, pastedContent, stdin, setRawMode, getPreferredEditor]);
}, [
text,
pastedContent,
stdin,
setRawMode,
getPreferredEditor,
settings.merged.general.openEditorInNewWindow,
]);

const handleInput = useCallback(
(key: Key): boolean => {
Expand Down
Loading
Loading