From 32cccda215ab11465e3a0ccdeb800746ce17e5c4 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 14 Oct 2026 22:00:06 +0200 Subject: [PATCH] revert #1821 helix keybindings pr --- packages/codemirror/keybindings.mjs | 162 ++++++------------ packages/codemirror/package.json | 1 - pnpm-lock.yaml | 20 --- website/src/pages/technical-manual/helix.mdx | 85 --------- .../src/repl/components/panel/SettingsTab.jsx | 2 +- 5 files changed, 56 insertions(+), 214 deletions(-) delete mode 100644 website/src/pages/technical-manual/helix.mdx diff --git a/packages/codemirror/keybindings.mjs b/packages/codemirror/keybindings.mjs index 16c7ec99e..9b217cfed 100644 --- a/packages/codemirror/keybindings.mjs +++ b/packages/codemirror/keybindings.mjs @@ -6,7 +6,6 @@ import { emacs } from '@replit/codemirror-emacs'; import { vim, Vim } from '@replit/codemirror-vim'; // import { vim } from './vim_test.mjs'; import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; -import { helix, commands } from 'codemirror-helix'; import { logger } from '@strudel/core'; const vscodePlugin = ViewPlugin.fromClass( @@ -21,70 +20,6 @@ const vscodePlugin = ViewPlugin.fromClass( ); const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); -function replEval(view) { - try { - // Dispatch a dedicated evaluate event first - let handled = false; - try { - const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true }); - handled = document.dispatchEvent(ev) === false; // false means preventDefault was called - } catch (e) { - console.error('Error dispatching repl-evaluate event', e); - } - if (handled) { - return; - } - // Try Ctrl+Enter first if not handled by custom event - const ctrlEnter = new KeyboardEvent('keydown', { - key: 'Enter', - code: 'Enter', - ctrlKey: true, - bubbles: true, - cancelable: true, - }); - view?.dom?.dispatchEvent?.(ctrlEnter); - // If not handled (no handler called preventDefault), try Alt+Enter as - // fallback - if (!ctrlEnter.defaultPrevented) { - const altEnter = new KeyboardEvent('keydown', { - key: 'Enter', - code: 'Enter', - altKey: true, - bubbles: true, - cancelable: true, - }); - view?.dom?.dispatchEvent?.(altEnter); - } - } catch (e) { - console.error('Error dispatching repl evaluation event', e); - } -} - -function replStop(view) { - try { - // First try dispatching our custom stop event, then fallback to Alt+. - let handled = false; - try { - const ev = new CustomEvent('repl-stop', { detail: { source: 'vim', view }, cancelable: true }); - handled = document.dispatchEvent(ev) === false; - } catch (e) { - console.error('Error dispatching repl-stop event', e); - } - if (!handled) { - const altDot = new KeyboardEvent('keydown', { - key: '.', - code: 'Period', - altKey: true, - bubbles: true, - cancelable: true, - }); - view?.dom?.dispatchEvent?.(altDot); - } - } catch (e) { - console.error('Error dispatching repl stop event', e); - } -} - // Map Vim :w to trigger the same action as evaluation. We dispatch a custom // event 'repl-evaluate' that the editor listens for, and also simulate // Ctrl+Enter/Alt+Enter as a fallback. We log to the Strudel logger so it @@ -112,9 +47,29 @@ try { // :q to pause/stop Vim.defineEx('quit', 'q', (cm) => { - const view = cm.cm6; - logger('[vim] :q — stopping repl'); - replStop(view); + try { + const view = cm?.view || cm; + // First try dispatching our custom stop event, then fallback to Alt+. + let handled = false; + try { + const ev = new CustomEvent('repl-stop', { detail: { source: 'vim', view }, cancelable: true }); + handled = document.dispatchEvent(ev) === false; + } catch (e) { + console.error('Error dispatching repl-stop event', e); + } + if (!handled) { + const altDot = new KeyboardEvent('keydown', { + key: '.', + code: 'Period', + altKey: true, + bubbles: true, + cancelable: true, + }); + view?.dom?.dispatchEvent?.(altDot); + } + } catch (e) { + console.error('Error dispatching :q stop event', e); + } }); // :w to evaluate @@ -128,7 +83,38 @@ try { } catch (e) { console.error('Error logging Vim :w evaluation', e); } - replEval(view); + // Dispatch a dedicated evaluate event first + let handled = false; + try { + const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true }); + handled = document.dispatchEvent(ev) === false; // false means preventDefault was called + } catch (e) { + console.error('Error dispatching repl-evaluate event', e); + } + if (handled) { + return; + } + // Try Ctrl+Enter first if not handled by custom event + const ctrlEnter = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + ctrlKey: true, + bubbles: true, + cancelable: true, + }); + view?.dom?.dispatchEvent?.(ctrlEnter); + // If not handled (no handler called preventDefault), try Alt+Enter as + // fallback + if (!ctrlEnter.defaultPrevented) { + const altEnter = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + altKey: true, + bubbles: true, + cancelable: true, + }); + view?.dom?.dispatchEvent?.(altEnter); + } } catch (e) { console.error('Error dispatching :w evaluation event', e); } @@ -138,49 +124,11 @@ try { console.error('Vim ex command setup failed (defineEx missing or Vim unavailable)', e); } -// Map Helix :w to trigger the same action as evaluation. We dispatch a custom -// event 'repl-evaluate' that the editor listens for, and also simulate -// Ctrl+Enter/Alt+Enter as a fallback. We log to the Strudel logger so it -// appears in the Console panel. -const helixCommands = commands.of([ - { - // :w to evaluate - name: 'write', - aliases: ['w'], - help: 'Repl-eval', - handler(view, args) { - try { - view?.focus?.(); // Let the app know this came from Helix :w - logger('[helix] :w — evaluating code'); - replEval(view); - } catch (e) { - console.error('Error dispatching helix :w evaluation event', e); - } - }, - }, - { - // :q to pause/stop - name: 'quit', - aliases: ['q'], - help: 'Repl-stop', - handler(view, args) { - try { - view?.focus?.(); // Let the app know this came from Helix :q - logger('[helix] :q — stopping repl'); - replStop(view); - } catch (e) { - console.error('Error dispatching helix :q stop event', e); - } - }, - }, -]); - const keymaps = { vim, emacs, codemirror: () => keymap.of(defaultKeymap), vscode: vscodeExtension, - helix: () => [helix(), helixCommands], }; export { Vim } from '@replit/codemirror-vim'; diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index d2432ef83..a18019e37 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -44,7 +44,6 @@ "@replit/codemirror-emacs": "^6.1.0", "@replit/codemirror-vim": "^6.3.0", "@replit/codemirror-vscode-keymap": "^6.0.2", - "codemirror-helix": "^0.5.0", "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", "@strudel/tonal": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9930ef857..20c96e9d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,9 +251,6 @@ importers: '@tonaljs/tonal': specifier: ^4.10.0 version: 4.10.0 - codemirror-helix: - specifier: ^0.5.0 - version: 0.5.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2) nanostores: specifier: ^0.11.3 version: 0.11.3 @@ -3660,15 +3657,6 @@ packages: resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - codemirror-helix@0.5.0: - resolution: {integrity: sha512-hI56hf9VGz53H1YvL6H1GC7HtP6te8vX+MsIHaE9J7Q3PQ6KFapKtIRg6lqSH898ikHWpMCPu42r6HJN0IfVLA==} - peerDependencies: - '@codemirror/commands': ^6.0.0 - '@codemirror/language': ^6.0.0 - '@codemirror/search': ^6.0.0 - '@codemirror/state': ^6.0.0 - '@codemirror/view': ^6.0.0 - collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -11411,14 +11399,6 @@ snapshots: cmd-shim@6.0.3: {} - codemirror-helix@0.5.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2): - dependencies: - '@codemirror/commands': 6.8.0 - '@codemirror/language': 6.10.8 - '@codemirror/search': 6.5.8 - '@codemirror/state': 6.5.1 - '@codemirror/view': 6.36.2 - collapse-white-space@2.1.0: {} color-convert@2.0.1: diff --git a/website/src/pages/technical-manual/helix.mdx b/website/src/pages/technical-manual/helix.mdx deleted file mode 100644 index 36f058985..000000000 --- a/website/src/pages/technical-manual/helix.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Helix Keybindings -layout: ../../layouts/MainLayout.astro ---- - -# Helix Keybindings in the REPL - -The Strudel REPL supports [Helix editor](https://helix-editor.com/) keybindings through the `codemirror-helix` extension. Helix is a post-modern modal text editor with a focus on selection-first editing and multiple cursors. - -## Enabling Helix Mode - -1. Open the Settings panel in the REPL (click the settings icon or use the keyboard shortcut) -2. Find the "Keybindings" section -3. Select "Helix" from the available options - -## Key Differences from Vim - -Helix uses a selection-first approach, which means: - -- **Selection → Action** (Helix) vs **Action → Motion** (Vim) -- For example, to delete a word in Helix: `w` (select word) → `d` (delete) -- In Vim, it would be: `dw` (delete word) - -## Common Helix Commands - -### Normal Mode - -- `i` — Enter insert mode before selection -- `a` — Enter insert mode after selection -- `v` — Enter visual/select mode -- `w` — Select next word -- `e` — Select to end of word -- `b` — Select previous word -- `x` — Select/extend line -- `d` — Delete selection -- `c` — Change selection (delete and enter insert mode) -- `y` — Yank (copy) selection -- `p` — Paste after selection -- `u` — Undo -- `U` — Redo -- `/` — Search -- `n` — Select next search match -- `N` — Select previous search match - -### Movement - -- `h, j, k, l` — Move left, down, up, right -- `gg` — Go to start of document -- `ge` — Go to end of document -- `{` / `}` — Move to previous/next paragraph -- `%` — Match bracket - -### Multi-cursor - -- `C` — Duplicate cursor to line below -- `Alt-C` — Duplicate cursor to line above -- `,` — Remove primary cursor -- `Alt-,` — Remove all secondary cursors - -## Strudel-Specific Features - -Currently, Helix mode in Strudel provides standard Helix keybindings. Unlike Vim mode, there are no custom Strudel-specific commands (like `:w` to evaluate) yet. - -To evaluate code while in Helix mode, use the standard shortcuts: - -- `Ctrl+Enter` or `Alt+Enter` — Evaluate code -- `Alt+.` — Stop playback - -## Resources - -- [Helix Documentation](https://docs.helix-editor.com/) -- [Helix Keymap](https://docs.helix-editor.com/keymap.html) -- [codemirror-helix on npm](https://www.npmjs.com/package/codemirror-helix) -- [codemirror-helix on GitLab](https://gitlab.com/_rvidal/codemirror-helix) - -## Notes - -- The `codemirror-helix` extension is described as an "initial version" and may not implement all Helix features -- Behavior may differ slightly from the native Helix editor -- If you encounter issues, you can switch back to other keybinding modes in Settings -- The keybinding preference is saved in your browser's local storage - -## Contributing - -If you'd like to add Strudel-specific Helix commands (similar to Vim's `:w` for evaluate), contributions are welcome! See the implementation in `/packages/codemirror/keybindings.mjs` for reference. diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index a385fd48a..f01c1669c 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -235,7 +235,7 @@ function MainSettingsContent({ started }) { settingsMap.setKey('keybindings', keybindings)} - items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', helix: 'Helix', vscode: 'VSCode' }} + items={{ codemirror: 'Codemirror', vim: 'Vim', emacs: 'Emacs', vscode: 'VSCode' }} >