vim mode for use with the Monaco Editor
Note: This package does not include the monaco-editor. It requires that Monaco has already been loaded, and depends on window.monaco
having been populated. See the monaco-editor
readme for how to accomplish this.
You can see this in use at monaco-and-vim, the code of which is at github.com/pollrobots/monaco-and-vim, and described in detail in this blog article.
At a minimum you can simply
import { VimMode } from 'vim-monaco';
.
.
.
// editor is a monaco.editor.IStandaloneCodeEditor
const vimMode = new VimMode(editor);
// enable vim mode.
vimMode.enable();
It isn't particularly usable without a status bar, there is no mode indication, and no way to input commands.
The VimMode
constructor has an optional second parameter statusBar: IStatusBar
.
The IStatusBar
type documentation describes how this needs to be implemented.
Alternatively, there is a simple DOM implementation of this that can be created
using makeDomStatusBar
which takes two arguments. A parent DOM node (to which the status bar will append itself), and a callback function that will be used to pass the focus back to Monaco
import { VimMode, makeDomStatusBar } from "vim-monaco";
// parent is an html element (likely the parent of the editor)
// editor is a monaco.editor.IStandaloneCodeEditor
const statusBar = makeDomStatusBar(parent, () => editor.focus());
const vimMode = new VimMode(editor, statusBar);
// enable vim mode.
vimMode.enable();
By default :write
, :edit
and :save
don't do anything. You can enable this functionality by adding event listeners to the vimMode
object for the open-file
and save-file
events.
The vim mode doesn't track the current filename, so these commands will only send any filename information passed in to the command.
Getting the current editor text for saving, and overwriting it for opening need to be handled directly with the IStandaloneCodeEditor instance.
vimMode.addEventListener("open-file", () => {
// code to open a file
});
vimMode.addEventListener("save-file", (evt) => {
// the filename specified in a :save command (for example)
const filename = evt.filename;
const contents = editor.getValue();
// code to save the file
});
The *
and +
clipboard registers are not implemented by default. To enable interaction with the system clipboad you need to implement IRegister
and add the two clipboard registers.
Because getting the contents of the clipboard is asynchronous, the vim mode raises a clipboard
event whenever the keybuffer indicates that one of the clipboard registers is about to be accessed. This can be used to pre-fetch the cliboard contents.
const clipboard = new ClipboardRegister(); // a class that implements IRegister
vimMode.setClipboardRegister(clipboard);
vimMode.addEventListener("clipboard", () => clipboard.update());
vimMode.disable()
will disable the vim mode. This reverts the editor to its default inferior key bindings.vimMode.executeCommand(cmd)
can be used to execute a command. For exampleset iskeyword+=-
would add '-' to the set of keyword characters (used byw
*
etc.)vimMode.setOption
can be used to set options. One use of this is to set the theme. The vim instance can use the ':colorscheme' command to get and set the theme, but while it can set the theme on the monaco editor instance, it cannot read the current theme name, so calling this on external theme changes will keep the value in sync.
This was based on the monaco-vim package by Brijesh Bittu.
Unfortunately that package didn't work for my needs, it depends on internal features of an older version of monaco. So I ported to typescript and made some different design decisions to allow me to avoid those dependencies.