Skip to content

Commit 8754b68

Browse files
authored
Ensure command keybindings work reliably in any vim-mode (#1821)
When the vim extension is enabled and in "normal" mode, some default bindings collide with Silverbullets commands (eg: `ctrl-p` - Share, `ctrl-o` - Open Document, `ctrl-e` - Export, `ctrl-g h` - Home). These vim bindings are not always functional (they may appear to do nothing depending on vims state). This gives the impression that Silverbullet is bugged and wont trigger commands. In "insert" mode, most bindings are passed through correctly with some exceptions (eg: `ctrl-o`). By splitting the command keybindings into their own keymap extension and loading it before the vim extension, we can ensure Silverbullet commands always trigger. This does mean some vim bindings are overshadowed, but if a binding triggers a Silverbullet command, its generally obvious that *something* happened and the user should be able to recognise why the vim binding failed and then rebind the command or vim mapping.
1 parent d285288 commit 8754b68

File tree

4 files changed

+28
-17
lines changed

4 files changed

+28
-17
lines changed

client/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export class Client {
121121

122122
// CodeMirror editor
123123
editorView!: EditorView;
124-
keyHandlerCompartment?: Compartment;
124+
commandKeyHandlerCompartment?: Compartment;
125125
indentUnitCompartment?: Compartment;
126126
undoHistoryCompartment?: Compartment;
127127

client/client_system.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { languageSyscalls } from "./plugos/syscalls/language.ts";
3333
import { codeWidgetSyscalls } from "./plugos/syscalls/code_widget.ts";
3434
import { clientCodeWidgetSyscalls } from "./plugos/syscalls/client_code_widget.ts";
3535
import { KVPrimitivesManifestCache } from "./plugos/manifest_cache.ts";
36-
import { createKeyBindings } from "./codemirror/editor_state.ts";
36+
import { createCommandKeyBindings } from "./codemirror/editor_state.ts";
3737
import type { DataStoreMQ } from "./data/mq.datastore.ts";
3838
import { jsonschemaSyscalls } from "./plugos/syscalls/jsonschema.ts";
3939
import { luaSyscalls } from "./plugos/syscalls/lua.ts";
@@ -125,8 +125,8 @@ export class ClientSystem {
125125
});
126126
// Replace the key mapping compartment (keybindings)
127127
this.client.editorView.dispatch({
128-
effects: this.client.keyHandlerCompartment?.reconfigure(
129-
createKeyBindings(this.client),
128+
effects: this.client.commandKeyHandlerCompartment?.reconfigure(
129+
createCommandKeyBindings(this.client),
130130
),
131131
});
132132
},

client/codemirror/editor_state.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,15 @@ export function createEditorState(
6060
): EditorState {
6161
let touchCount = 0;
6262

63-
// Ugly: keep the keyhandler compartment in the client, to be replaced later once more commands are loaded
64-
client.keyHandlerCompartment = new Compartment();
65-
const keyBindings = client.keyHandlerCompartment.of(
66-
createKeyBindings(client),
63+
// Ugly: keep the commandKeyHandler compartment in the client, to be replaced
64+
// later once more commands are loaded
65+
client.commandKeyHandlerCompartment = new Compartment();
66+
const commandKeyBindings = client.commandKeyHandlerCompartment.of(
67+
createCommandKeyBindings(client),
6768
);
69+
// Regular key bindings are not dynamically updated and do not require a
70+
// compartment.
71+
const regularKeyBindings = createRegularKeyBindings(client);
6872

6973
client.indentUnitCompartment = new Compartment();
7074
const indentUnits = client.indentUnitCompartment.of(
@@ -82,6 +86,12 @@ export function createEditorState(
8286
dark: client.ui.viewState.uiOptions.darkMode,
8387
}),
8488

89+
// Insert our command key bindings *before* vim mode. Vim in normal-mode is
90+
// greedy and captures all key events, preventing them from reaching our
91+
// own handlers to trigger commands. This will mean some vim-mode
92+
// bindings wont trigger if they have the same keys.
93+
commandKeyBindings,
94+
8595
// Enable vim mode, or not
8696
[
8797
...client.ui.viewState.uiOptions.vimMode
@@ -183,7 +193,7 @@ export function createEditorState(
183193
},
184194
]),
185195
disableSpellcheck(["InlineCode", "CodeText", "CodeInfo", "FrontMatter"]),
186-
keyBindings,
196+
regularKeyBindings,
187197
EditorView.domEventHandlers({
188198
// This may result in duplicated touch events on mobile devices
189199
touchmove: () => {
@@ -331,7 +341,7 @@ export function isValidEditor(
331341
(currentEditor !== undefined && requiredEditor === "notpage");
332342
}
333343

334-
export function createCommandKeyBindings(client: Client): KeyBinding[] {
344+
export function createCommandKeyBindings(client: Client): Extension {
335345
const commandKeyBindings: KeyBinding[] = [];
336346

337347
// Then add bindings for plug commands
@@ -386,17 +396,16 @@ export function createCommandKeyBindings(client: Client): KeyBinding[] {
386396
}
387397
}
388398

389-
return commandKeyBindings;
399+
return keymap.of([
400+
...commandKeyBindings,
401+
]);
390402
}
391403

392-
export function createKeyBindings(client: Client): Extension {
404+
export function createRegularKeyBindings(client: Client): Extension {
393405
if (client.isDocumentEditor()) {
394-
return keymap.of([
395-
...createCommandKeyBindings(client),
396-
]);
406+
return keymap.of([]);
397407
} else {
398408
return keymap.of([
399-
...createCommandKeyBindings(client),
400409
...createSmartQuoteKeyBindings(client),
401410
...closeBracketsKeymap,
402411
...client.ui.viewState.uiOptions.vimMode

client/components/mini_editor.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ export function MiniEditor(
148148
doc: text,
149149
extensions: [
150150
EditorView.theme({}, { dark: darkMode }),
151+
// Insert command bindings before vim-mode to ensure they're available
152+
// in normal mode. See editor_state.ts for more details.
153+
createCommandKeyBindings(globalThis.client),
151154
// Enable vim mode, or not
152155
[...vimMode ? [vim()] : []],
153156
[
@@ -182,7 +185,6 @@ export function MiniEditor(
182185
},
183186
...standardKeymap,
184187
...historyKeymap,
185-
...createCommandKeyBindings(globalThis.client),
186188
]),
187189
EditorView.domEventHandlers({
188190
click: (e) => {

0 commit comments

Comments
 (0)