import { searchKeymap } from '@codemirror/search';
import { vim } from './vim_test.mjs';
12const vscodePlugin = ViewPlugin.fromClass( 13 class { 14 constructor() {} 15 }, 16 { 17 provide: () => { 18 return Prec.highest(keymap.of([...vscodeKeymap])); 19 }, 20 }, 21); 22const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []); 23 24function replEval(view) { 25 try { 26 // Dispatch a dedicated evaluate event first 27 let handled = false; 28 try { 29 const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true }); 30 handled = document.dispatchEvent(ev) === false; // false means preventDefault was called 31 } catch (e) { 32 console.error('Error dispatching repl-evaluate event', e); 33 } 34 if (handled) { 35 return; 36 } 37 // Try Ctrl+Enter first if not handled by custom event 38 const ctrlEnter = new KeyboardEvent('keydown', { 39 key: 'Enter', 40 code: 'Enter', 41 ctrlKey: true, 42 bubbles: true, 43 cancelable: true, 44 }); 45 view?.dom?.dispatchEvent?.(ctrlEnter); 46 // If not handled (no handler called preventDefault), try Alt+Enter as 47 // fallback 48 if (!ctrlEnter.defaultPrevented) { 49 const altEnter = new KeyboardEvent('keydown', { 50 key: 'Enter', 51 code: 'Enter', 52 altKey: true, 53 bubbles: true, 54 cancelable: true, 55 }); 56 view?.dom?.dispatchEvent?.(altEnter); 57 } 58 } catch (e) { 59 console.error('Error dispatching repl evaluation event', e); 60 } 61} 62 63function replStop(view) { 64 try { 65 // First try dispatching our custom stop event, then fallback to Alt+. 66 let handled = false; 67 try { 68 const ev = new CustomEvent('repl-stop', { detail: { source: 'vim', view }, cancelable: true }); 69 handled = document.dispatchEvent(ev) === false; 70 } catch (e) { 71 console.error('Error dispatching repl-stop event', e); 72 } 73 if (!handled) { 74 const altDot = new KeyboardEvent('keydown', { 75 key: '.', 76 code: 'Period', 77 altKey: true, 78 bubbles: true, 79 cancelable: true, 80 }); 81 view?.dom?.dispatchEvent?.(altDot); 82 } 83 } catch (e) { 84 console.error('Error dispatching repl stop event', e); 85 } 86}
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 appears in the Console panel.
92try { 93 if (Vim && typeof Vim.defineEx === 'function') { 94 // Map gc to toggle line comments by dispatching a custom event that our 95 // CodeMirror integration listens to. This avoids depending on Vim's 96 // internal actions and works with current selections/visual mode. 97 try { 98 Vim.defineAction('strudelToggleComment', (cm) => { 99 const view = cm.cm6; 100 try { 101 const ev = new CustomEvent('repl-toggle-comment', { detail: { source: 'vim', view }, cancelable: true }); 102 document.dispatchEvent(ev); 103 } catch (e) { 104 console.error('strudelToggleComment dispatch failed', e); 105 } 106 }); 107 Vim.mapCommand('gc', 'action', 'strudelToggleComment', {}, { context: 'normal' }); 108 Vim.mapCommand('gc', 'action', 'strudelToggleComment', {}, { context: 'visual' }); 109 } catch (e) { 110 console.error('Vim gc mapping failed', e); 111 }
:q to pause/stop
:w to evaluate
121 Vim.defineEx('write', 'w', (cm) => { 122 const view = cm.cm6; 123 try { 124 view.focus?.(); 125 // Let the app know this came from Vim :w 126 try { 127 logger('[vim] :w — evaluating code'); 128 } catch (e) { 129 console.error('Error logging Vim :w evaluation', e); 130 } 131 replEval(view); 132 } catch (e) { 133 console.error('Error dispatching :w evaluation event', e); 134 } 135 }); 136 } 137} catch (e) { 138 console.error('Vim ex command setup failed (defineEx missing or Vim unavailable)', e); 139}
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.
145const helixCommands = commands.of([ 146 { 147 // :w to evaluate 148 name: 'write', 149 aliases: ['w'], 150 help: 'Repl-eval', 151 handler(view, args) { 152 try { 153 view?.focus?.(); // Let the app know this came from Helix :w 154 logger('[helix] :w — evaluating code'); 155 replEval(view); 156 } catch (e) { 157 console.error('Error dispatching helix :w evaluation event', e); 158 } 159 }, 160 }, 161 { 162 // :q to pause/stop 163 name: 'quit', 164 aliases: ['q'], 165 help: 'Repl-stop', 166 handler(view, args) { 167 try { 168 view?.focus?.(); // Let the app know this came from Helix :q 169 logger('[helix] :q — stopping repl'); 170 replStop(view); 171 } catch (e) { 172 console.error('Error dispatching helix :q stop event', e); 173 } 174 }, 175 }, 176]);
178const keymaps = { 179 vim, 180 emacs, 181 codemirror: () => keymap.of(defaultKeymap), 182 vscode: vscodeExtension, 183 helix: () => [helix(), helixCommands], 184}; 185 186export { Vim } from '@replit/codemirror-vim'; 187 188export function keybindings(name) { 189 const active = keymaps[name]; 190 const extensions = active ? [Prec.high(active())] : []; 191 if (name === 'vim') { 192 extensions.push(EditorState.allowMultipleSelections.of(true)); 193 } 194 return extensions; 195}