possible vim leader key work-around?

Vim-leader-key behavior has never made it into CM, but could a work-around be possible using defineAction() and mapCommand()?
I’d be happy if someone could tell me why this is impossible or why it’s a very bad idea so I can stop wondering about it :).

I haven’t found many examples, but here’s what I’ve got so far:

I’d like to type <Space>x from normal-mode to get my handy GitHub-Flavored-Markdown checkbox shortcut <ESC>:s/\[\s\]/[x]/g<CR>:noh<CR>jhh.
Can defineAction and mapCommand work togther to acheive anything similar?
Please no vim-golf w/my checkbox shortcut, it’s just an example :slight_smile:

(Just a heads-up that I’m not actively maintaining the vim bindings, and though @benhormann has been creating a bunch of wonderful patches against it recently, I’m not sure if he reads the forum or has any plans to pick up further feature requests.)

Thanks for the reply.
I know vim mode isn’t under active development, so I also know this post is a long-shot.
No complaints though!..I think CM’s vim mode is already great.

My intent was not for a feature request, my hope was that someone out there would have experience with these parts of the API and could help with custom multi-keystroke configuration.
Or perhaps someone could tell me I’m reading the manual wrong (maybe that someone is Marijn who already knows what I’m asking for is not possible using the API and it would require a new feature :slight_smile:).

I’ll try to dig into the code to see what I might learn.

You can easily do what you want. There are three parts to it: multi-key / prefix mappings, custom commands, and <Space>.

A simple example shows that multi-key mappings work: do :map \g G, then press \g.

The catch is that <Space> is already mapped to l, so maps like <Space>x have no effect until you unmap the default (known issue).
I recommend you use the following:

CodeMirror.Vim.unmap('<Space>');
CodeMirror.Vim.map('<Space><Space>', 'l');

Then add your custom maps after.


The fun part - your custom command. Did you know you can map to an ex-cmd, e.g. :map \s :s/\[\s\]/[x]/g? Unfortunately you can’t chain ex-cmds by adding <CR>. (You can make a mapping for each part, then a map to chain those maps…)

For custom commands you can use either defineAction or defineEx, the main differences are in how easy it is to map and how args are handled. To do what you asked, you’ll probably want to use handleKey and handleEx:

const { Vim } = CodeMirror;
Vim.unmap('<Space>');
Vim.map('<Space><Space>', 'l');
Vim.defineAction('githubMdCheckbox', (cm, args) => {
  Vim.handleEx(cm, 's/\[\s\]/[x]/g');
  Vim.handleEx(cm, 'nohls');
  Vim.handleKey(cm, 'h');
  Vim.handleKey(cm, 'j');
  Vim.handleKey(cm, 'j');
});
Vim.mapCommand('<Space>s', 'action', 'githubMdCheckbox');

If you need to pass args to your action, that’s the 4th argument on mapCommand. Its an object that get cloned and there are a few default keys to watch out for: repeat, repeatIsExplicit, registerName, selectedCharacter. If you want to limit it to Normal mode, add a 5th arg {context: 'normal'}.

Ex-cmds also can work on a range, so give that a try too. Good luck!

Thank you, thank you, thank you!
These are the dots that I needed to have connected :).
It’s a shift from my normal vim mind-set, but I’ve been playing with it and making some progress.

I’ve got a couple substitutions that work as expected when used in-editor, but behave differently when configured with defineAction.
So I’ve got some incorrect assumptions, or maybe some bugs to hunt down.

Thanks again.

Vim.handleEx(cm, 's/\[\s\]/[x]/g');

Is that because I forgot to escape the backslashes? 🤦
Try it with 's/\\[\\s\\]/[x]/g' instead.


I forgot to mention before, Vim.handleKey can’t do Insert mode typing. Hacky, but you could inject the text into a register then use p / P. Otherwise try the cm.doc.replaceRange method.

Yep, it was the backslashes :grimacing:.
I know they’re escape-characters in JS strings, but they’re already serving as escape-characters in the vim substitution-expression!..like I said it’s a difficult shift in mind-set for me.

Fortunately, I’ve hacked on codeMirror just enough over the past few years to independently arrive at replaceRange() for insert mode content entry.

I can’t thank you enough for getting me un-stuck on this.
I’m off to find other stuff in my .vimrc that might be fun to port over.