Skip to content

feat(editor): add an Emacs keybindings preset to the Monaco file editor - #31496

Open
wtwoo wants to merge 13 commits into
stablyai:mainfrom
wtwoo:feat/editor-emacs-keymap
Open

wtwoo wants to merge 13 commits into
stablyai:mainfrom
wtwoo:feat/editor-emacs-keymap

Conversation

@wtwoo

@wtwoo wtwoo commented Oct 21, 2026 •

Copy link
Copy Markdown

ELI5

Orca's file editor gets an Emacs option next to Default and Vim in Settings → General → Editor. Turn it on and the editor answers to the Emacs keys people know from IntelliJ/Android Studio's Emacs keymap: C-f/C-b/C-n/C-p to move, C-SPC to set a mark, C-k/C-w/M-w/C-y for the kill ring, C-s to search, C-x C-s to save. On Linux and Windows those Ctrl keys used to belong to Orca (Ctrl+P opened Go to File, Ctrl+W closed the tab); while an Emacs editor has focus they now reach the editor, and everywhere else they keep doing what they always did.

Stacked on #9755 (Vim preset) as its author suggested in #30055, so the first three commits here are xxjjvxb's. The Emacs work is the six commits on top.

What Changed

Before: the editorKeybindings setting from #9755 knows default and vim. On macOS, Monaco's built-in Ctrl keys cover basic movement but there is no Meta (Option+F types ƒ), no mark, no kill ring. On Linux/Windows the Emacs Ctrl keys are Orca app shortcuts and are consumed before the editor sees them.

After:

  1. Setting. editorKeybindings accepts 'emacs'; the Settings segment shows Default / Vim / Emacs. Unknown stored values fall back to Default, so a profile written by a newer build does not break an older one.
  2. Editor keys. The monaco-emacs adapter is imported lazily and attached to writable, full-height editors only, with the same attach/dispose lifecycle as the Vim adapter. A status row under the editor echoes the pending prefix (C-x) and "Mark set", and clears once the chord has run (upstream leaves the last prefix on screen). Orca adds what the library lacks: C-x h (select all), redo on Emacs 28's C-? / C-M-_ plus S-C-z, C-x C-s (save through Orca's normal save path, like :w), C-g also closes the find widget, and inside the find widget C-s/C-r step to the next/previous match while C-g closes it. Emacs window commands map onto Orca's split groups: C-x 2 / C-x 3 show the same file in a new group below / to the right and move point there, C-x o cycles through the groups in layout order (a terminal or browser in the next group gets focus the way its tab click would), and C-x 0 closes this view while the file stays open in its other windows (refused for the sole window, as in Emacs). Plain Tab is handed back to Monaco (upstream maps it to format-document).
  3. Editor-first keys. A new editor keybinding context. While a Monaco editor running the Emacs preset has focus (its find widget included), both shortcut layers yield the chords monaco-emacs binds — the main-process before-input-event allowlist and the renderer's window-capture handlers, plus the editor-local save/find handlers. Everything the preset does not bind (Ctrl+Shift+P, Ctrl+Tab, Ctrl+1…) keeps working inside the editor, and nothing changes for Default/Vim users or outside the editor. Settings → Shortcuts shows an "Emacs editor" badge on the affected rows while the preset is on.
  4. Bundling fix. monaco-editor's require export condition points at its AMD bundle, so a CommonJS dependency doing require('monaco-editor') pulled a second, AMD copy of Monaco into the renderer and threw define is not defined at runtime. A Vite alias (desktop and web configs) pins the bare specifier to the ESM entry.

Why

  • Library over hand-written rules. monaco.editor.addKeybindingRules alone could remap keys, but mark, kill ring and M-y would have to be reimplemented; monaco-emacs already has them and works on the Monaco 0.55.1 Orca ships (verified in the live app by the e2e spec below). Same call xxjjvxb made for monaco-vim.
  • A context, not a remap. Freeing the Emacs keys one by one in ~/.orca/keybindings.json only moves the collision to another default and cannot cover editor commands. Yielding by focus context mirrors the existing terminal-first policy, is per-chord (an explicit list of what the preset binds, not "all Ctrl keys"), and leaves every other surface untouched.
  • Both processes have to yield. Orca resolves app shortcuts in the main process before the renderer ever sees the key, so a renderer-only context would still lose Ctrl+P. The editor reports its focus to main over IPC the same way the markdown editor already does for Cmd+B.
  • Physical Control, not Mod. Emacs chords are Control chords on macOS too, so the yielded list is written with Ctrl; macOS Cmd shortcuts are never affected.

Linked Issue

Fixes #30055 (follow-up to #9699; based on #9755)

Visual Proof

Before (Default preset): Meta keys do nothing, C-SPC opens suggestions instead of setting a mark, C-x 3 types 3, and Ctrl+P (bound to quick open as on Linux/Windows) opens Go to File from inside the editor.

orca-emacs-before.webm

After (Emacs preset, Settings → General → Editor Keybindings): movement, mark + M-w + C-y, C-k / C-y, Ctrl+P as previous-line inside the editor, C-s / C-g, C-x C-s save, C-x 3 / C-x o / C-x 0, undo / redo.

orca-emacs-after.webm

Testing

Platforms: macOS (Apple Silicon) locally. Linux/Windows behaviour is covered by platform-parameterised unit tests on the shared matcher and the main-process router; the e2e spec rebinds quick open to Ctrl+P so the Linux/Windows collision is exercised on macOS too.

  • pnpm test for the touched areas: setting + Settings segment, Emacs adapter, shared keybinding matcher (editor context, per platform), main-process router (before-input-event with editor focus reported/withdrawn, foreign-sender IPC ignored), renderer context resolution and focus mirror, Settings badge, localization catalog.
  • tests/e2e/emacs-keybindings-file-editor.spec.ts (@golden, headless Electron): preset attaches to the live editor, C-x prefix echo and C-g, mark + M-w + C-y, C-k/C-y, Tab indents, C-s opens find and C-s/C-r/C-g work inside the widget, C-x C-s writes to disk with autosave off, C-x 3 / C-x o / C-x 0 split, cycle and close views, and — driven through Electron's before-input-event with a native input event — Ctrl+P is line-up inside the editor but opens quick open from app focus. tests/e2e/vim-keybindings-file-editor.spec.ts still passes.
  • pnpm tc:web, pnpm tc:node, pnpm run check:code-quality:changed (the one remaining finding, VimCheatsheet.tsx:235, is from feat: add a Vim keybindings preset to the Monaco file editor #9755), pnpm build:web.

Note for anyone extending the e2e: Playwright's keyboard.press never reaches Electron's before-input-event; main-routed chords have to be sent with webContents.sendInputEvent.

  • I manually tested these changes locally
  • Automated tests added/updated, or explained why not below

AI Disclosure

Written with Claude Code (Claude Fable 5.1): design discussion, implementation, tests and this description, reviewed and run by me.

Review

Agent skill upstream boundary

  • Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Known upstream limitation: C-u numeric arguments do not work with current Monaco, because monaco-emacs expects digit keys to be named 4 while Monaco 0.55 names them Digit4. The window commands are registered under Monaco's names for that reason. Not fixed here; it belongs upstream.
  • New dependency: monaco-emacs@0.3.0 (devDependency, bundled by Vite like monaco-vim; last release 2022, verified on Monaco 0.55.1).
  • Wire compatibility: 'emacs' is a new optional value of an existing optional setting; older clients fall back to Default.
  • SSH / folder workspaces: no change in behaviour, the preset is a renderer-side editor keymap.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

@wtwoo
wtwoo force-pushed the feat/editor-emacs-keymap branch from 82d1aa3 to 812a5f6 Compare October 21, 2026 13:49
@wtwoo
wtwoo marked this pull request as ready for review October 22, 2026 08:44
@wtwoo
wtwoo requested a review from brennanb2025 as a code owner October 22, 2026 08:44
@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
Walkthrough

The editor now supports Default, Vim, and Emacs keybinding presets. The changes add Monaco mode adapters, an editor-focus signal for shortcut routing, and shortcut-setting status labels for bindings that yield to Emacs. The pull request also centralizes workspace-tab activation and updates tab-number shortcuts to use it.

Priority: Normal

Merge Risk: Low · previous review cb69d

A late focus message could fail during window closure, and some plugin shortcuts may lack an accurate Emacs status badge. These bounded issues are suitable for owner follow-up before or shortly after merge.

Pre-merge checks: Passed 4 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Linked Issues check Inconclusive #30055 is open and directly linked. The PR adds the emacs setting and fallback, Settings control, lazy monaco-emacs lifecycle, editor focus context, shortcut routing, search, redo, C-x h, save, … Provide focused source or automated-test evidence that Emacs Meta handling does not intercept IME composition for Korean, Japanese, and Chinese input. Reassess #30055 after that evidence is available.
Passed Passed checks: 4 passed
Check name Status Explanation
Description check Passed The description is detailed and mostly complete. It includes the required ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, AI Disclosure, Notes, and Checklist sections. It explains the us…
Title check Passed The title clearly identifies the primary change: adding an Emacs keybindings preset to the Monaco file editor. It is concise, specific, and consistent with the pull request objectives.
Out of Scope Changes check Passed The reported changes support #30055. The editor context, focus IPC, Monaco ESM alias, adapter lifecycle, status UI, window activation helpers, localization, and tests implement or verify the Emacs edi…
Docstring Coverage Passed Docstring coverage is inconclusive: files from earlier reviews still need to be rechecked within the check's analysis limits. Not blocking.
Full details: Description check

Explanation

The description is detailed and mostly complete. It includes the required ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, AI Disclosure, Notes, and Checklist sections. It explains the user impact, implementation, alternatives, platform coverage, known limitation, and validation results.

Full details: Out of Scope Changes check

Explanation

The reported changes support #30055. The editor context, focus IPC, Monaco ESM alias, adapter lifecycle, status UI, window activation helpers, localization, and tests implement or verify the Emacs editor preset. The stacked Vim foundation supports the shared editor-keybindings setting and does not establish unrelated scope. The PR does not implement the app-level Emacs chords assigned to #22465.

Full details: Linked Issues check

Explanation

#30055 is open and directly linked. The PR adds the emacs setting and fallback, Settings control, lazy monaco-emacs lifecycle, editor focus context, shortcut routing, search, redo, C-x h, save, and window commands. The PR also includes unit and end-to-end tests for these behaviors, including platform-specific Control and Meta matching. The available evidence does not establish the required protection against intercepting IME composition on macOS. No focused source or test evidence for Korean, Japanese, or Chinese IME composition is available.

Resolution

Provide focused source or automated-test evidence that Emacs Meta handling does not intercept IME composition for Korean, Japanese, and Chinese input. Reassess #30055 after that evidence is available.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 5

🧹 Nitpick comments: 1
tests/e2e/emacs-keybindings-file-editor.spec.ts: 1

111-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert Quick Open through the DOM in both routing checks.

activeModal only checks store state and can miss a broken Quick Open render. Use the dialog exposed by CommandDialog for both assertions.

Suggested fix
   const kb = orcaPage.keyboard
+  const quickOpenDialog = orcaPage.getByRole('dialog', { name: 'Go to file' })
 
   // Prefix echo: a pending C-x shows in the status row and C-g cancels it without editing.
@@
-  expect(await orcaPage.evaluate(() => window.__store?.getState().activeModal)).not.toBe(
-    'quick-open'
-  )
+  await expect(quickOpenDialog).toBeHidden()
@@
-  await expect
-    .poll(() => orcaPage.evaluate(() => window.__store?.getState().activeModal), {
-      timeout: 5_000
-    })
-    .toBe('quick-open')
+  await expect(quickOpenDialog).toBeVisible({ timeout: 5_000 })

ℹ️ Review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 31829d4b-e0b8-4c0b-8449-cb80e4104eef
📥 Commits

Reviewing files that changed from the base of the PR and between fd8b2dc and 812a5f66c99796583b16d91fe219b6337fc626f6.

⛔ Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing: 55
  • config/build-plugins/monaco-editor-esm-alias.ts
  • electron.vite.config.ts
  • package.json
  • src/main/window/createMainWindow-editor-keymap-focus-shortcuts.test.ts
  • src/main/window/main-window-focus-lifecycle.ts
  • src/main/window/main-window-shortcut-routing.ts
  • src/preload/api/ui-bridge-clipboard-and-window-controls.ts
  • src/preload/api/ui-window-api.ts
  • src/renderer/src/app-shell/app-command-handlers.ts
  • src/renderer/src/components/editor/MonacoEditor.tsx
  • src/renderer/src/components/editor/VimCheatsheet.tsx
  • src/renderer/src/components/editor/editor-shortcuts.ts
  • src/renderer/src/components/editor/emacs-window-commands.test.ts
  • src/renderer/src/components/editor/emacs-window-commands.ts
  • src/renderer/src/components/editor/monaco-emacs-mode.test.ts
  • src/renderer/src/components/editor/monaco-emacs-mode.ts
  • src/renderer/src/components/editor/monaco-vim-mode.ts
  • src/renderer/src/components/editor/use-editor-keymap-focus-mirror.test.tsx
  • src/renderer/src/components/editor/use-editor-keymap-focus-mirror.ts
  • src/renderer/src/components/editor/use-monaco-emacs-mode.ts
  • src/renderer/src/components/settings/EditorKeybindingsSetting.test.tsx
  • src/renderer/src/components/settings/EditorKeybindingsSetting.tsx
  • src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx
  • src/renderer/src/components/settings/ShortcutCommandBlock.tsx
  • src/renderer/src/components/settings/ShortcutFilterRail.tsx
  • src/renderer/src/components/settings/ShortcutRowsList.tsx
  • src/renderer/src/components/settings/ShortcutsPane.tsx
  • src/renderer/src/components/settings/shortcut-editor-status.ts
  • src/renderer/src/components/settings/shortcut-row-visibility.test.ts
  • src/renderer/src/components/settings/shortcut-row-visibility.ts
  • src/renderer/src/components/terminal-workspace-model.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/activate-workspace-tab.ts
  • src/renderer/src/lib/keybinding-context.test.ts
  • src/renderer/src/lib/keybinding-context.ts
  • src/renderer/src/lib/tab-number-shortcuts.ts
  • src/renderer/src/web/preload-api/web-ui-api.ts
  • src/shared/constants.test.ts
  • src/shared/default-global-settings.ts
  • src/shared/editor-keybindings-preset.ts
  • src/shared/global-settings-base.ts
  • src/shared/keybindings-editor-context.test.ts
  • src/shared/keybindings.ts
  • src/shared/keybindings/editor-keymap-chords.ts
  • src/shared/keybindings/matching.ts
  • src/shared/keybindings/types.ts
  • src/shared/window-shortcut-policy-editor-context.test.ts
  • src/shared/workspace-layout/tab-group-layout-tree.ts
  • tests/e2e/emacs-keybindings-file-editor.spec.ts
  • tests/e2e/vim-keybindings-file-editor.spec.ts
  • vite.web.config.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@wtwoo
wtwoo force-pushed the feat/editor-emacs-keymap branch from 812a5f6 to 3585551 Compare October 22, 2026 11:11
@wtwoo

wtwoo commented Oct 22, 2026

Copy link
Copy Markdown
Author

Re the nitpick on tests/e2e/emacs-keybindings-file-editor.spec.ts (assert Quick Open through the DOM): Tried it, but the dialog's accessible name is localized ("파일로 이동" on a Korean system), so getByRole('dialog', { name: 'Go to file' }) fails there, and toBeHidden() would pass for the wrong reason. The store check stays locale-independent.

@wtwoo
wtwoo force-pushed the feat/editor-emacs-keymap branch from 93a121a to bf00874 Compare October 22, 2026 11:37
@wtwoo

wtwoo commented Oct 22, 2026

Copy link
Copy Markdown
Author

Rebased onto main again after #31974 (one-line conflict in MonacoEditor.tsx, both sides kept), so the review-fix commits referenced above have new hashes: e9e36db23e → e933e63, 96915fa905 → cd02425, 93a121abaa → bf00874.

xxjjvxb and others added 6 commits October 28, 2026 13:49
Add an `editorKeybindings: 'default' | 'vim'` setting (default 'default')
that enables monaco-vim modal editing in the built-in file editor, with a
status bar and :w/:wq/:x wired to save. The toggle lives in Settings →
Editor next to Word Wrap as a Default/Vim segmented control.

monaco-vim loads lazily and is scoped to the editable, full-height editor
(not read-only diffs or auto-height preview editors); the status bar is a
real flex-column row below the editor rather than an overlay. The adapter
disposes idempotently on toggle or unmount, and :w routes to the focused
editor. It shares Orca's locally-bundled monaco instance via loader.config.

monaco-vim is a keymap emulator, so LSP-backed :gd, :%s live preview, and
macro replay @q are unsupported — documented as known limitations.

Closes stablyai#9699.
…tsheet

Build on the initial Vim keybindings preset with the app-integration and
discoverability pieces the first pass left out:

- Wire :q/:q! (close tab) and make :wq/:x save-and-close, routed through the
  focused editor's Orca save/close like :w already was.
- Map editor-display :set options onto Monaco -- wrap/nowrap, number/nonumber,
  tabstop/shiftwidth/expandtab -- with :set paste/nopaste and unknown options as
  graceful no-ops (Monaco has no terminal paste-mangling to disable).
- Add a command cheatsheet: a hint button in the Vim status bar opens a popover
  listing supported motions/operators/registers/visual/macro/Ex commands plus the
  documented limitations.
- Correct the preset's limitation notes: registers, visual mode, macros, and :%s
  substitution are verified working (save/sync proven clean); the real gaps are
  gd, :%s live-preview, Backspace replay inside a macro, and monaco-vim's extra
  trailing blank line on linewise paste of a newline-terminated buffer.
Drive the real monaco-vim adapter in a headless Electron run: mode switching
with i/Esc/o/dd, :w save-to-disk, yank/paste + :%s substitution + visual-mode
delete, macro record/replay, and the :set line-number toggle + :q close. Also
exercises the status-bar cheatsheet popover.
- Extend `editorKeybindings` to `'default' | 'vim' | 'emacs'` via an exported
  `EDITOR_KEYBINDINGS_PRESETS` tuple so the editor and settings share one list
- Add the Emacs segment to Settings → General → Editor; unknown stored presets
  fall back to Default instead of being coerced
- Reword the English description and retire the Vim-only copy from target
  locales so they fall back to English until retranslated
…s selected

- Add `monaco-emacs-mode.ts`, which lazy-loads `monaco-emacs` 0.3.0, starts the
  extension on the mounted editor, and mirrors the pending prefix key / `C-u`
  argument and mark state into the status row below the editor
- Patch monaco-emacs's process-global command table once per renderer: plain
  Tab goes back to Monaco instead of formatDocument, and `C-x h` (select all)
  and `S-C-z` (redo) are added
- Generalise the Vim status bar row into a keymap status bar shared by both
  presets; the Vim cheatsheet still renders only for Vim
- Move the preset tuple and `isEditorKeybindingsPreset` into
  `src/shared/editor-keybindings-preset.ts` so the editor and the settings
  control agree on which stored values are presets
… to its ESM entry

- Add `tests/e2e/emacs-keybindings-file-editor.spec.ts`: the lazy import
  attaches (block caret), the `C-x` prefix echoes and `C-g` cancels it, `C-SPC`
  / `M-w` / `C-y` copy a region through the kill ring, `C-k` / `C-y` kill and
  yank a line, and plain Tab indents; edits are read back from disk via autosave
- Add `config/build-plugins/monaco-editor-esm-alias.ts` and use it in the
  desktop and web Vite configs: monaco-editor's `require` export condition is
  its AMD bundle, so monaco-emacs's CommonJS `require('monaco-editor')` bundled
  a second Monaco that threw `define is not defined` at runtime
@wtwoo
wtwoo force-pushed the feat/editor-emacs-keymap branch from bf00874 to cb69d69 Compare October 28, 2026 07:22
@wtwoo

wtwoo commented Oct 28, 2026

Copy link
Copy Markdown
Author

Rebased onto current main (conflicts in MonacoEditor.tsx, global-settings-base.ts and ShortcutCommandBlock.tsx, both sides kept) and added one commit that trims this PR's comments to match the #33249 comment audit; no behavior change. Typecheck, the changed-code gate, the unit tests and the Emacs/Vim e2e specs pass locally. Ready for review; CI is waiting for workflow approval.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a90c4e35-7ce3-4894-9890-6733c0394968
Commits

Reviewing files that changed from the base of the PR and between bf00874 and cb69d69.

Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing: 37
  • config/build-plugins/monaco-editor-esm-alias.ts
  • electron.vite.config.ts
  • package.json
  • src/main/window/createMainWindow-editor-keymap-focus-shortcuts.test.ts
  • src/main/window/main-window-focus-lifecycle.ts
  • src/main/window/main-window-shortcut-routing.ts
  • src/preload/api/ui-bridge-clipboard-and-window-controls.ts
  • src/preload/api/ui-window-api.ts
  • src/renderer/src/app-shell/app-command-handlers.ts
  • src/renderer/src/components/editor/MonacoEditor.tsx
  • src/renderer/src/components/editor/emacs-window-commands.ts
  • src/renderer/src/components/editor/monaco-emacs-mode.ts
  • src/renderer/src/components/editor/use-editor-keymap-focus-mirror.ts
  • src/renderer/src/components/editor/use-monaco-emacs-mode.ts
  • src/renderer/src/components/settings/EditorKeybindingsSetting.tsx
  • src/renderer/src/components/settings/GeneralEditorSettingsSection.tsx
  • src/renderer/src/components/settings/ShortcutCommandBlock.tsx
  • src/renderer/src/components/settings/ShortcutsPane.tsx
  • src/renderer/src/components/settings/shortcut-editor-status.ts
  • src/renderer/src/components/settings/shortcut-row-visibility.test.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/keybinding-context.ts
  • src/renderer/src/lib/tab-number-shortcuts.ts
  • src/renderer/src/web/preload-api/web-ui-api.ts
  • src/shared/default-global-settings.ts
  • src/shared/editor-keybindings-preset.ts
  • src/shared/global-settings-base.ts
  • src/shared/keybindings.ts
  • src/shared/keybindings/editor-keymap-chords.ts
  • src/shared/keybindings/matching.ts
  • src/shared/workspace-layout/tab-group-layout-tree.ts
  • tests/e2e/emacs-keybindings-file-editor.spec.ts
  • vite.web.config.ts
Files skipped from review as they are similar to previous changes: 5
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/es.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

wtwoo added 7 commits October 28, 2026 17:33
- Add an `editor` keybinding context: while a Monaco editor running the
  Emacs preset has focus, `keybindingMatchesAction` ignores the chords
  monaco-emacs binds (Ctrl+P/N/F/B/A/E/W/K/Y/S/…, Alt+F/B/W/…), listed in
  `src/shared/keybindings/editor-keymap-chords.ts`; every other chord
  keeps working, and Default/Vim users see no change
- Renderer resolves the context from the `data-orca-editor-keymap` marker
  MonacoEditor sets; the editor-local save/find handlers honour it too
- Mirror editor-keymap focus to the main process
  (`ui:setEditorKeymapFocused`) so the before-input-event allowlist
  yields the same chords instead of stealing them first
- Settings → Shortcuts badges the rows the Emacs preset takes over
- E2E drives Ctrl+P natively through before-input-event: line-up inside
  the editor, quick open outside it
- Register `C-x C-s` in the monaco-emacs table and route it to the
  focused editor's save hook, the way `:w` reaches it for Vim; with the
  Emacs preset Ctrl+S is incremental search, so the file needs this path
- Make `C-g` close Monaco's find widget after keyboard-quit, as it ends
  an isearch in Emacs; the command's precondition keeps it a no-op
  otherwise
- Add Monaco keybinding rules scoped to Emacs editors (`orcaEmacsKeymap`
  context key) so `C-s`/`C-r`/`C-g` step through matches and close the
  widget while its input has focus, where monaco-emacs never sees keys
- Bind redo to Emacs 28's `undo-redo` keys (`C-?`, `C-M-_`) alongside
  `S-C-z`, and yield those chords to the editor like the rest
- E2E: autosave off, every write goes through `C-x C-s`; cover the find
  widget flow and focus returning to the editor
- `C-x 2` / `C-x 3` open the same file in a new group below / right and
  move point there; `C-x o` cycles the layout leaves and focuses the
  group's visible tab (editor, terminal, browser) through the activation
  path the tab-number shortcuts already use; `C-x 0` closes this view
  only and refuses the sole window, as Emacs does
- Extract `activateWorkspaceTab` from the tab-number shortcut so both
  callers share one activation path, and move the Emacs attach effect
  into `useMonacoEmacsMode` to keep MonacoEditor under the line budget
- Register the digit chords under Monaco's key names (`C-x Digit2`):
  monaco-emacs only strips the `Key` prefix, so `C-x 2` never matches
- Hold focus on the new view until the layout settles or the next window
  command runs: inserting a split re-parents the source group, whose
  editor remounts and would take the keyboard back
- Re-read the echo state after each key so a finished `C-x …` clears;
  upstream fires before running the command and never after the reset
- Note that `C-u` numeric arguments are broken upstream for the same
  digit-naming reason
- Check the caller's cancellation after the lazy import, before any state is created
- A stale install's dispose no longer removes the hooks and context key of a newer install on the same editor
… signal

- Track which editor container last reported focus to main
- Unmounting another split view, or turning the preset off there, no longer hands Emacs chords such as Ctrl+P back to Orca while an Emacs editor still has focus
…sewhere

- A pointerdown while the split settles cancels the focus hold
- Clicking another surface right after C-x 2 or C-x 3 keeps the focus the user chose
@wtwoo
wtwoo force-pushed the feat/editor-emacs-keymap branch from cb69d69 to 28579fb Compare October 28, 2026 08:34

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Emacs preset for editor keybindings (follow-up to #9699)

2 participants