Change builtin obsidian hotkey handling precedence to allow for overriding hardcoded mappings in plugins

Use case or problem

The lack of emacs bindings has always been a pain point, but I opted to try and ride it out. Unfortunately, nearly a year of daily use later, it’s still a source of friction in my workflow.

I decided to be proactive and build a plugin to address this, with some success. Unfortunately, I ultimately found that even by setting Prec.highest() on my CM6 extension, my chorded keymapping plugin still comes in second place after the built-in global hotkey handling on obsidian.

This means that certain critical bindings like Control a, Control y, and the arrow keys are unavailable. I completely understand not wanting to add the complexity of chorded key management as a built-in feature, but without providing a way for plugins to (opt into) a first pass at the key events, obsidian currently makes it impossible to add this feature as a plugin.

Besides this handful of (unfortunately impossible to ignore) unavailable hotkeys, the solution I’ve built around @replit/codemirror-emacs is doing a great job.

Proposed solution

Use Prec.high instead of Prec.highest for the builtin hotkey handler, or otherwise provide an alternate API method for registering extensions to appear at Prec.highest along with the builtin extensions but listed before them.

Current workaround (optional)

I’ve been unable to find one short of decompiling obsidian and making the above changes, or perhaps trying to subvert Obsidian’s editor management by triggering a reconfiguration transaction from another editor extension.

I briefly tried to explore this idea via API.registerCodeMirror but found that it seems to be completely nonfunctional.

Related feature requests (optional)

First party key chord support could theoretically help. There’s an existing plugin for this that I’m already using, but it’s forced to install a global listener on the window, which is inappropriate for running editor-specific commands within pages.

I think I can still hack something together by creating wrapper Commands in obsidian that issue CM commands so that obsidian can manage the editor mapping, but it’d still require me to fork and substantially alter replit’s otherwise suitable codemirror-emacs CM6 plugin.

I think the correct way would be for Obsidian to include emacs as an option like vim. Would the codemirror-emacs package be enough for it? Because for vim obsidian sets precedence for hotkeys correctly for it.
Edit: But the hotkeys cannot be used as global hotkeys then.

codemirror-emacs hard codes a couple of assumptions (like a block-cursor) that I think could be controversial, but I’m happy to create upstream PRs to make that configurable if the Obsidian team is interested in pursuing first party support (or provide a fork as necessary). Ideally, we’d also expose its system for registering new commands and chorded bindings – it’s already designed to be extensible, it just isn’t exporting all the necessary functions to utilize that. It’s MIT licensed, so shouldn’t be any issues there either way.

Pipe dream level integration would include chorded hotkey support in the obsidian bindings as well (e.g. emacs uses Alt+g as the leader key for its various “goto” bindings, and I’m using leader hotkeys obsidian to add bindings like Alt+g d → goto daily note and Alt+g f → navigate forward. This works okay, but it:

  1. renders the builtin hotkey system useless (or detrimental, in case of conflicts)
  2. is much harder to use than the built-in system
  3. having overlapping chorded hotkey systems doesn’t work properly. The fact that I’ve registered Alt+g d in the leader hotkeys plugin causes it to consume all Alt+gs before the codemirror-emacs extension can see them, causing its built-in goto binds like Alt+g g → go to line to not work.

If obsidian were to provide chorded binding support at the top level, (perhaps using logic inspired by codemirror-emacs’s key handling) all of the CM6 emacs-style commands could still be used through it via editorCallback of wrapping obsidian Commands.

If the team is seriously interested in either let me know and I’ll be happy to help out as a weekend project.

I think I just ran into this myself. I added the CM6 extension via Obsidian’s API, but even when it is wrapped in Prec.highest, it doesn’t seem to have any effect. I have made another plug-in that does successfully add a CM6 extension to show white space, so I think I have the gist of how to add editor extensions in general.

Apologies for pestering, but has there been any further consideration of either sucking the replit Emacs extension into Obsidian, or else consideration of jfcole’s suggestion about using Prec.high so plug-ins can override Obsidian?

(I have spent about twenty minutes puttering around Obsidian’s internals in the dev console to see if I could forcibly insert the Emacs extension ahead of Obsidian, but I don’t know if I have the will to drill through compiled JS any longer. :sweat_smile:)