Skip to content

feat(cli): add Sublime Text and Emacs Client editors, improve error messages and documentation#21090

Open
alberti42 wants to merge 23 commits into
google-gemini:mainfrom
alberti42:extra-editors
Open

feat(cli): add Sublime Text and Emacs Client editors, improve error messages and documentation#21090
alberti42 wants to merge 23 commits into
google-gemini:mainfrom
alberti42:extra-editors

Conversation

@alberti42
Copy link
Copy Markdown

@alberti42 alberti42 commented Mar 4, 2026

Summary

Addresses #21084 by improving external editor support across three areas: adds
Sublime Text and Emacs Client to the built-in editor list, emits clear
error messages when editor configuration is invalid or a command is missing, and
extends the documentation and JSON schema for preferredEditor.

Details

New editors (packages/core/src/utils/editor.ts):

  • Sublime Text (sublime): added as a GUI editor using the subl command.
    Opens files with --wait. Diff view opens the new file only, as subl does
    not support a --diff flag.
  • Emacs Client (emacsclient): added as a terminal editor. Automatically
    injects -nw (terminal mode) when opening a file. Diff view uses
    emacsclient -nw --eval "(ediff ...)", consistent with the existing emacs
    implementation.

Error handling (packages/cli/src/ui/utils/editorUtils.ts):

Two cases that previously produced a cryptic internal error are now handled
gracefully:

  1. Unrecognized preferredEditor: validates the editor type before use and
    emits a user-friendly message instead of a generic error. For example,
    setting "preferredEditor": "nvim" (the binary name) instead of the
    supported identifier "neovim" previously produced:
    ✕ [useTextBuffer] external editor error
    
    It now produces:
    ✕ Editor 'nvim' is not recognized or not installed. Run /editor to pick a
      supported editor, or set $VISUAL/$EDITOR in your shell to your preferred
      editor.
    
  2. Missing $VISUAL/$EDITOR command: detects ENOENT on spawn and emits
    a clear message pointing the user to check their environment variable.

Note: $VISUAL/$EDITOR continue to accept any editor command, not just the
built-in supported types, preserving full flexibility for power users.

Documentation (docs/reference/configuration.md,
schemas/settings.schema.json, packages/cli/src/config/settingsSchema.ts):

  • The description field for preferredEditor in settingsSchema.ts has been
    updated to explain the built-in identifier requirement, the /editor command
    as the canonical way to discover supported values, and the $VISUAL/$EDITOR
    escape hatch for power users. Both docs/reference/configuration.md and
    schemas/settings.schema.json are regenerated from this source and updated
    accordingly.

Related Issues

Closes #21084

Note for maintainers: enum constraint in the JSON schema

The current implementation leaves preferredEditor typed as a plain string in
settingsSchema.ts, so the JSON schema carries no enum constraint and IDEs
offer no autocomplete for valid editor identifiers.

A cleaner solution would be to change the definition to type: 'enum' and list
the supported values via options, mirroring how defaultApprovalMode is
defined. This would cause the generation script to emit an enum array in the
JSON schema and a Values: line in the documentation automatically, and would
keep everything in sync with the code. For example:

preferredEditor: {
  type: 'enum',
  label: 'Preferred Editor',
  category: 'General',
  requiresRestart: false,
  default: undefined as EditorType | undefined,
  description: '...',
  showInDialog: false,
  options: (EDITORS as readonly EditorType[]).map((e) => ({
    value: e,
    label: EDITOR_DISPLAY_NAMES[e],
  })),
},

This is a slightly more invasive change (it touches the settings type system and
may affect dialog rendering), so I have left it out of this PR. I am happy to
implement it based on your feedback.

How to Validate

Sublime Text:

  1. Set "preferredEditor": "sublime" in ~/.gemini/settings.json
  2. Open Gemini CLI and press CTRL-X to open the external editor
  3. Sublime Text should open the buffer file and wait until it is closed

Emacs Client:

  1. Start an Emacs daemon: emacs --daemon
  2. Set "preferredEditor": "emacsclient" in ~/.gemini/settings.json
  3. Open Gemini CLI and press CTRL-X to open the external editor
  4. Emacs Client should open in terminal mode (-nw) and wait until the buffer
    is closed

Graceful error for unrecognized preferredEditor:

  1. Set "preferredEditor": "emacsclient -nw" in ~/.gemini/settings.json
  2. Press CTRL-X in Gemini CLI
  3. Expected:
    Editor 'emacsclient -nw' is not recognized or not installed. Run /editor to pick a supported editor, or set $VISUAL/$EDITOR in your shell to your preferred editor.
  4. Expected: no crash, no unhandled promise rejection

Graceful error for missing $VISUAL/$EDITOR command:

  1. Run export EDITOR="nonexistent-editor" in your shell
  2. Remove preferredEditor from settings.json
  3. Press CTRL-X in Gemini CLI
  4. Expected:
    Could not find editor 'nonexistent-editor'. Check your $VISUAL/$EDITOR setting.
  5. Expected: no crash, no unhandled promise rejection

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

@alberti42 alberti42 requested review from a team as code owners March 4, 2026 10:32
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the external editor integration for the CLI, making it more robust and user-friendly. It expands the range of supported editors, provides more informative feedback for common configuration issues, and updates all relevant documentation to guide users effectively.

Highlights

  • New Editor Support: Added Sublime Text and Emacs Client to the list of built-in supported external editors.
  • Improved Error Handling: Implemented clearer and more user-friendly error messages for unrecognized preferredEditor configurations and missing editor commands.
  • Enhanced Documentation: Updated documentation and JSON schema for preferredEditor to provide better guidance on configuration and supported values.
Changelog
  • docs/reference/configuration.md
    • Updated the description for general.preferredEditor to clarify supported identifiers and the use of $VISUAL/$EDITOR.
  • packages/cli/src/config/settingsSchema.ts
    • Modified the description for preferredEditor to include guidance on built-in identifiers and environment variables.
  • packages/cli/src/ui/utils/editorUtils.ts
    • Imported isEditorAvailable for editor validation.
    • Added logic to check preferredEditorType availability and emit a user-friendly error if unrecognized.
    • Introduced handleEditorError function to provide specific error messages for ENOENT (command not found) and general editor errors.
    • Injected -nw argument for emacsclient when opening files in terminal mode.
  • packages/core/src/utils/editor.test.ts
    • Added test cases for sublime and emacsclient in getEditorCommands.
    • Included new tests for getDiffCommand specifically for emacsclient (with escaped paths) and sublime.
    • Updated guiEditors and terminalEditors lists for sandbox mode tests to include sublime and emacsclient respectively.
    • Added tests for allowEditorTypeInSandbox for emacsclient.
  • packages/core/src/utils/editor.ts
    • Added sublime to GUI_EDITORS and emacsclient to TERMINAL_EDITORS.
    • Updated EDITOR_DISPLAY_NAMES to include 'Emacs Client' and 'Sublime Text'.
    • Added command definitions for emacsclient and sublime in editorCommands.
    • Modified getDiffCommand to handle sublime (opening only the new file) and emacsclient (with -nw and ediff eval).
  • schemas/settings.schema.json
    • Updated the description and markdownDescription for preferredEditor to match the new guidance.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for Sublime Text and Emacs Client as external editors and enhances error handling for editor configurations, along with clear documentation updates. However, a critical command injection vulnerability was identified in packages/cli/src/ui/utils/editorUtils.ts due to the unsafe handling of VISUAL and EDITOR environment variables, potentially leading to Remote Code Execution. Additionally, one issue in the new test code requires attention for correctness.

Comment thread packages/core/src/utils/editor.test.ts Outdated
@gemini-cli gemini-cli Bot added the area/core Issues related to User Interface, OS Support, Core Functionality label Mar 4, 2026
default: undefined as string | undefined,
description: 'The preferred editor to open files in.',
description:
'The preferred editor to open files in. Must be one of the built-in supported identifiers — run /editor in the CLI for the full list. Power users who need an editor not in this list can leave this unset and configure $VISUAL or $EDITOR in their shell environment instead.',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This descript is too long. Descriptions need to fit in the UI.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing it. I shortened the description.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See commit 15bda94

Comment thread schemas/settings.schema.json Outdated
@@ -41,8 +41,8 @@
"properties": {
"preferredEditor": {
"title": "Preferred Editor",
"description": "The preferred editor to open files in.",
"markdownDescription": "The preferred editor to open files in.\n\n- Category: `General`\n- Requires restart: `no`",
"description": "The preferred editor to open files in. Must be one of the built-in supported identifiers — run /editor in the CLI for the full list. Power users who need an editor not in this list can leave this unset and configure $VISUAL or $EDITOR in their shell environment instead.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tweak so you can specify a separate longer markdown description from the short description suitable for the TUI.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I shortened the description in 15bda94.

The file schemas/settings.schema.json is automatically generated by npm run docs:settings.

The new commit already contains the newly generated one.

@jacob314 jacob314 enabled auto-merge March 4, 2026 17:31
@gemini-cli gemini-cli Bot added priority/p2 Important but can be addressed in a future release. help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels Mar 4, 2026
auto-merge was automatically disabled March 5, 2026 08:55

Head branch was pushed to by a user without write access

alberti42 added a commit to alberti42/fork-gemini-cli that referenced this pull request Mar 6, 2026
jacob314 requested a shorter description to fit the TUI. This was already
addressed in PR google-gemini#21090 via settingsSchema.ts. Regenerate schema from source
of truth and restore the accepted-values table in configuration.md.
@alberti42 alberti42 requested a review from a team as a code owner March 6, 2026 23:04
@alberti42
Copy link
Copy Markdown
Author

Update: I've integrated the work from the duplicate PR #21377 by @AnanthKini1 into this branch. His commits have been cherry-picked on top of mine with authorship preserved.

His additions beyond what was already in this PR:

  • Fixed an infinite re-render loop in EditorSettingsDialog (emitFeedback called inside render body)
  • Added --new-window flag for VS Code-family editors (Cursor, Windsurf, VSCodium) so the editor opens directly to the file instead of the Welcome screen
  • Fixed maxItemsToShow so all editors are visible in the /editor picker
  • Expanded editor support: Lapce, Nova, BBEdit, Micro, Sublimetext (renamed from sublime)
  • Smarter $VISUAL/$EDITOR handling: extra args and wait flags now apply even when the editor is set via environment variable

I also fixed a documentation issue: schemas/settings.schema.json and docs/reference/configuration.md are auto-generated from settingsSchema.ts, so I moved the enum values there to make them permanent.

Comment thread package-lock.json
"node_modules/@octokit/core": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.5.tgz",
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert changes to this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: I removed the package-lock.json changes from the commit history.

or leave unset to use $VISUAL/$EDITOR.
`,
showInDialog: false,
options: [
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these duplicate options in editor.ts. Please Refactor so they don't have to be duplicated or remove this. Including these entries here isn't crucial as this is hidden from the settings dialog but would be nice to have so it ends up in settings.schema.json

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The fix is in commit "refactor: derive preferredEditor options from EDITOR_OPTIONS"

} else {
// Heuristic fallback for commands not in the registry
const lower = envCommand.toLowerCase();
const isGui = ['code', 'cursor', 'subl', 'zed', 'atom'].some((g) =>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add these to a const rather than including them here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done → changes are in commit "refactor: extract HEURISTIC_GUI_COMMANDS const"

'error',
'[editorUtils] external editor spawn error',
err,
const spawnErr = err as NodeJS.ErrnoException;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emitFeedback is preferred to throwing an exception. why this change?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: see commit "fix: replace throw with emitFeedback in openFileInEditor"

Comment thread packages/core/src/utils/editor.ts Outdated
const editorExtraArgs: Partial<Record<EditorType, string[]>> = {
emacsclient: ['-nw'], // Force terminal (no-window) mode
vscode: ['--new-window'], // Force a new window to open the file directly
vscodium: ['--new-window'],
Copy link
Copy Markdown
Contributor

@jacob314 jacob314 Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addubg new window will be frustrating for some users. If I have a project open in vscode why do I want a new window? Would suggest protecting this behind a setting.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: see commit → "feat: guard --new-window behind openEditorInNewWindow setting"

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments. once these are addressed this can be approved.

alberti42 added a commit to alberti42/fork-gemini-cli that referenced this pull request Mar 11, 2026
jacob314 requested a shorter description to fit the TUI. This was already
addressed in PR google-gemini#21090 via settingsSchema.ts. Regenerate schema from source
of truth and restore the accepted-values table in configuration.md.
@alberti42 alberti42 force-pushed the extra-editors branch 2 times, most recently from 19b1f71 to 1fcb713 Compare March 11, 2026 06:37
Copy link
Copy Markdown
Author

@alberti42 alberti42 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I split the resolution of concerns into different commits for easier review/understanding. If needed, we can squash things at the end.

or leave unset to use $VISUAL/$EDITOR.
`,
showInDialog: false,
options: [
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The fix is in commit "refactor: derive preferredEditor options from EDITOR_OPTIONS"

} else {
// Heuristic fallback for commands not in the registry
const lower = envCommand.toLowerCase();
const isGui = ['code', 'cursor', 'subl', 'zed', 'atom'].some((g) =>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done → changes are in commit "refactor: extract HEURISTIC_GUI_COMMANDS const"

'error',
'[editorUtils] external editor spawn error',
err,
const spawnErr = err as NodeJS.ErrnoException;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: see commit "fix: replace throw with emitFeedback in openFileInEditor"

Comment thread packages/core/src/utils/editor.ts Outdated
const editorExtraArgs: Partial<Record<EditorType, string[]>> = {
emacsclient: ['-nw'], // Force terminal (no-window) mode
vscode: ['--new-window'], // Force a new window to open the file directly
vscodium: ['--new-window'],
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: see commit → "feat: guard --new-window behind openEditorInNewWindow setting"

alberti42 added a commit to alberti42/fork-gemini-cli that referenced this pull request Apr 10, 2026
jacob314 requested a shorter description to fit the TUI. This was already
addressed in PR google-gemini#21090 via settingsSchema.ts. Regenerate schema from source
of truth and restore the accepted-values table in configuration.md.
@alberti42
Copy link
Copy Markdown
Author

@cocosheng-g Thanks for following up. The code is now ready for being re-reviewed.

alberti42 and others added 23 commits April 24, 2026 12:30
1. Added 'sublime' to GUI_EDITORS
2. Added sublime: 'Sublime Text' to EDITOR_DISPLAY_NAMES
3. Added sublime: { win32: ['subl'], default: ['subl'] } to editorCommands
4. Added a case 'sublime' in getDiffCommand with ['--wait', oldPath] (Sublime doesn't support --diff)

And in editor.test.ts: added sublime to the hasValidEditorCommand, getDiffCommand, openDiff, and allowEditorTypeInSandbox test suites.
packages/core/src/utils/editor.ts:
- Added 'emacsclient' to TERMINAL_EDITORS
- Added emacsclient: 'Emacs Client' to EDITOR_DISPLAY_NAMES
- Added emacsclient: { win32: ['emacsclient'], default: ['emacsclient'] } to editorCommands
- Added case 'emacsclient' in getDiffCommand using emacsclient -nw --eval "(ediff ...)"

packages/cli/src/ui/utils/editorUtils.ts:
- Added -nw flag injection for emacsclient when opening a file (equivalent to the -i NONE treatment for vim)

packages/core/src/utils/editor.test.ts:
- Added emacsclient to hasValidEditorCommand, getDiffCommand, openDiff, and allowEditorTypeInSandbox test suites
Instead of crashing with an unhandled promise rejection, users now get a clear message whether their preferredEditor setting is invalid or their $VISUAL/$EDITOR command can't be found.
jacob314 requested a shorter description to fit the TUI. This was already
addressed in PR google-gemini#21090 via settingsSchema.ts. Regenerate schema from source
of truth and restore the accepted-values table in configuration.md.
Change preferredEditor type to 'enum' and add options so the schema
generator permanently emits the accepted values list in configuration.md
and the enum array in settings.schema.json without manual edits.
@alberti42
Copy link
Copy Markdown
Author

Dear Jacob (@jacob314),

I wanted to check with you what you think the status of this PR is. You had reviewed it before, and I believe I took care of all your comments. Please let us know if you need more time and/or need to have a more thorough review of the code.

Thanks!
Andrea

PS I just rebased it, and luckily there were no conflicts.

@gemini-cli gemini-cli Bot added the priority/p3 Backlog - a good idea but not currently a priority. label May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release. priority/p3 Backlog - a good idea but not currently a priority.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

External editor support: unclear error messages, missing editors, incomplete documentation

4 participants