fix: cleanup keybinding events

This commit is contained in:
Felix Roos 2026-02-13 09:37:34 +01:00
parent bdeafbaf75
commit 9bc560b17f
No known key found for this signature in database
5 changed files with 25 additions and 33 deletions

View file

@ -293,6 +293,9 @@ export class StrudelMirror {
// Handle global evaluation requests (e.g., from Vim :w) // Handle global evaluation requests (e.g., from Vim :w)
this.onEvaluateRequest = (e) => { this.onEvaluateRequest = (e) => {
try { try {
if (e.detail.view !== this.editor) {
return; // ignore events from other editors
}
// Evaluate current editor on repl-evaluate // Evaluate current editor on repl-evaluate
logger('[repl] evaluate via event'); logger('[repl] evaluate via event');
this.evaluate(); this.evaluate();
@ -307,6 +310,9 @@ export class StrudelMirror {
// Toggle comments requested from Vim (gc) // Toggle comments requested from Vim (gc)
this.onToggleComment = (e) => { this.onToggleComment = (e) => {
try { try {
if (e.detail.view !== this.editor) {
return; // ignore events from other editors
}
// Honor selections; toggleLineComment handles both selections and // Honor selections; toggleLineComment handles both selections and
// single line // single line
toggleLineComment(this.editor); toggleLineComment(this.editor);
@ -347,6 +353,9 @@ export class StrudelMirror {
// Listen for global stop requests (e.g., from Vim :q) // Listen for global stop requests (e.g., from Vim :q)
onStopRequest = (e) => { onStopRequest = (e) => {
try { try {
if (e.detail.view !== this.editor) {
return; // ignore events from other editors
}
this.stop(); this.stop();
e?.cancelable && e.preventDefault?.(); e?.cancelable && e.preventDefault?.();
} catch (err) { } catch (err) {

View file

@ -4,5 +4,5 @@ export * from './flash.mjs';
export * from './slider.mjs'; export * from './slider.mjs';
export * from './themes.mjs'; export * from './themes.mjs';
export * from './widget.mjs'; export * from './widget.mjs';
export { Vim } from './keybindings.mjs';
export * from './basicSetup.mjs'; export * from './basicSetup.mjs';
export { Vim, prebakeField } from './keybindings.mjs';

View file

@ -9,15 +9,6 @@ import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import { helix, commands } from 'codemirror-helix'; import { helix, commands } from 'codemirror-helix';
import { logger } from '@strudel/core'; import { logger } from '@strudel/core';
export const prebakeField = StateField.define({
create() {
return 'PrebakeEditor';
},
update(value, _tr) {
return value;
},
});
const vscodePlugin = ViewPlugin.fromClass( const vscodePlugin = ViewPlugin.fromClass(
class { class {
constructor() {} constructor() {}
@ -35,7 +26,10 @@ function replEval(view) {
// Dispatch a dedicated evaluate event first // Dispatch a dedicated evaluate event first
let handled = false; let handled = false;
try { try {
const ev = new CustomEvent('repl-evaluate', { detail: { source: 'vim', view }, cancelable: true }); const ev = new CustomEvent('repl-evaluate', {
detail: { source: 'vim', view },
cancelable: true,
});
handled = document.dispatchEvent(ev) === false; // false means preventDefault was called handled = document.dispatchEvent(ev) === false; // false means preventDefault was called
} catch (e) { } catch (e) {
console.error('Error dispatching repl-evaluate event', e); console.error('Error dispatching repl-evaluate event', e);
@ -105,13 +99,11 @@ try {
// internal actions and works with current selections/visual mode. // internal actions and works with current selections/visual mode.
try { try {
Vim.defineAction('strudelToggleComment', (cm) => { Vim.defineAction('strudelToggleComment', (cm) => {
const view = cm?.view || cm;
try { try {
const toggleEventName = const ev = new CustomEvent('repl-toggle-comment', {
view?.cm6?.state?.field(prebakeField, false) !== undefined detail: { source: 'vim', view: cm.cm6 },
? 'prebake-toggle-comment' cancelable: true,
: 'repl-toggle-comment'; });
const ev = new CustomEvent(toggleEventName, { detail: { source: 'vim', view }, cancelable: true });
document.dispatchEvent(ev); document.dispatchEvent(ev);
} catch (e) { } catch (e) {
console.error('strudelToggleComment dispatch failed', e); console.error('strudelToggleComment dispatch failed', e);
@ -132,17 +124,6 @@ try {
// :w to evaluate // :w to evaluate
Vim.defineEx('write', 'w', (cm) => { Vim.defineEx('write', 'w', (cm) => {
const view = cm?.view || cm; // CM6 Vim passes either an object with view or the view itself const view = cm?.view || cm; // CM6 Vim passes either an object with view or the view itself
const isPrebake = view?.cm6?.state?.field(prebakeField, false) !== undefined;
if (isPrebake) {
let prebakeEventHandled = false;
try {
const ev = new CustomEvent('prebake-evaluate', { detail: { source: 'vim', view }, cancelable: true });
prebakeEventHandled = document.dispatchEvent(ev) === false; // false means preventDefault was called
return;
} catch (e) {
console.error('Error dispatching repl-evaluate event', e);
}
}
try { try {
view?.focus?.(); view?.focus?.();
// Let the app know this came from Vim :w // Let the app know this came from Vim :w
@ -151,7 +132,7 @@ try {
} catch (e) { } catch (e) {
console.error('Error logging Vim :w evaluation', e); console.error('Error logging Vim :w evaluation', e);
} }
replEval(view); replEval(cm.cm6);
} catch (e) { } catch (e) {
console.error('Error dispatching :w evaluation event', e); console.error('Error dispatching :w evaluation event', e);
} }

View file

@ -379,6 +379,9 @@ function PrebakeSettingsContent() {
e?.cancelable && e.preventDefault?.(); e?.cancelable && e.preventDefault?.();
}; };
const handleToggleComment = (e) => { const handleToggleComment = (e) => {
if (e.detail.view !== editorRef.current.view) {
return; // ignore events from other editors
}
editorRef.current?.toggleComment(); editorRef.current?.toggleComment();
e?.cancelable && e.preventDefault?.(); e?.cancelable && e.preventDefault?.();
}; };
@ -386,8 +389,8 @@ function PrebakeSettingsContent() {
editorRef.current?.setCode(code); editorRef.current?.setCode(code);
}; };
useEffect(() => { useEffect(() => {
document.addEventListener('prebake-evaluate', handleSaveEvent); document.addEventListener('repl-evaluate', handleSaveEvent);
document.addEventListener('prebake-toggle-comment', handleToggleComment); document.addEventListener('repl-toggle-comment', handleToggleComment);
return () => { return () => {
document.removeEventListener('prebake-evaluate', handleSaveEvent); document.removeEventListener('prebake-evaluate', handleSaveEvent);
document.removeEventListener('prebake-toggle-comment', handleToggleComment); document.removeEventListener('prebake-toggle-comment', handleToggleComment);

View file

@ -4,7 +4,7 @@ import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language'
import { Compartment, EditorState, Prec } from '@codemirror/state'; import { Compartment, EditorState, Prec } from '@codemirror/state';
import { drawSelection, EditorView, keymap } from '@codemirror/view'; import { drawSelection, EditorView, keymap } from '@codemirror/view';
import { logger } from '@strudel/core'; import { logger } from '@strudel/core';
import { basicSetup, flash, prebakeField, initTheme, extensions, parseBooleans } from '@strudel/codemirror'; import { basicSetup, flash, initTheme, extensions, parseBooleans } from '@strudel/codemirror';
export class PrebakeCodeMirror { export class PrebakeCodeMirror {
constructor(initialCode, storePrebake, containerRef, editorRef, settings) { constructor(initialCode, storePrebake, containerRef, editorRef, settings) {
@ -53,7 +53,6 @@ export class PrebakeCodeMirror {
}, },
]), ]),
), ),
prebakeField,
], ],
}); });
editorRef.current = state; editorRef.current = state;