Multiple cursors across multiple CodeMirror instances

Imagine you have a page with 2+ CodeMirror instances. I’d like to allow the user to type into multiple instances at the same time.

What’s the best way of going about this?

The browser will only focus one editor, and CodeMirror itself only shows a visible cursor in the focused editor. But you could probably add CSS that makes it visible when you add some class to the editor (or even reuse the built-in cm-focused class for pseudo-focused editors). And then maybe look at the key events and text input transactions in the actually-focused editor and forward them to the others.

Might i ask what’s the purpose of running multiple instances with the same contents? Out of dev curiosity.

Thanks for the quick response!

@5e-Cleric Think along the lines of a multi-pane editor that displays files side-by-side. eg in VS Code, you can hit Ctrl+\ and the current tab splits in half and you get 2 editor instances on screen at once.

There are actually two flavors of this problem:

  • The editor instances share document state, as in VS Code’s split command. The editors are both views in to the same file, so you can scroll them independently, but any changes you make in one editor are immediately visible in the other.
  • The editor instances are completely independent with their own document state; they just happen to be displayed next to each other on screen. (You can do this in VS Code as well by dragging a tab to the side of the window, giving you a split pane view with different files.)

For the former, I know there’s a demo that shows you how to achieve split panes in CM.

My particular use case is actually closer to the latter — my UI can have multiple panes but the editors’ document states are independent. However, the functionality I want to achieve is slightly different from either case, where only one editor can have input focus at a time, and any changes appearing in both panes is simply a side effect of shared document state.

Basically, I plan on my app UI allowing the user to select additional editor(s) to receive secondary “focus” (probably by Ctrl-clicking the secondary editor at the location the user wants to place the secondary cursor). I know this isn’t real focus, so I’ll use CSS to fake it to look like focus. Then the user can start typing and the effects of each keypress are applied to all selected editors. (Except Esc, which returns focus to only the primary editor.)

So spiritually similar to how multiple cursors within an editor work, just across separate editors that have independent documents.

@marijn I suppose I could listen to the change transactions coming out of the primary (actually-focused) editor by overriding dispatch (as in the split pane example), but I’ll have to somehow transform the from/to positions to adjust for arbitrarily-placed cursors. I imagine things will get tricky if the user activates multiple selections within an editor, especially given that the documents aren’t necessarily related.

Or would it make more sense to just use the DOM to dispatchEvent(…) with a synthetic KeybaordEvent to forward keypresses into the secondary editor, and let it process input normally? Of course, then clipboard handling gets tricky, as does inserting accepted autocompletions…

Or are you thinking I’d need to do some combination of those approaches?

I happen to have a collaborative text editor implementation based on CodeMirror and the Croquet framework, and the demo happens to show two text boxes on the page and you can join to the session from other browser tabs.

You can try opening this page: Croquet CodeMirror . The URL gets a q= parameter. You copy the longer URL to another browser window, and those four text editors are in sync. The source code is here: GitHub - yoshikiohshima/croquet-codemirror · GitHub You’d have to set up your own “reflector” but it is open source: GitHub - croquet/croquet: Client-side (!) Multiplayer for JavaScript · GitHub

And, the “test” button below in the page send key events to those two instances. so that could be seen as “typing into two text editors”.

hope this helps.

@ohshima Thanks for the suggestion, but unfortunately that’s not quite what I’m trying to achieve.

It looks like you’re synchronizing document state across multiple editors (local and remote), but I need multiple editors with independent document states.

eg the user can type “foo” in to editor 1, “bar” in to editor 2, then select both editors 1 & 2, type “baz”, and you end up with the editors containing:

  1. “foobaz”
  2. “barbaz”

Hmm, ok. So ignoring whether the text editors in my demo are synchronized or not, what you want is the “test” button in the demo? It sends a synthesized key input to two text editors at the same time when pressed.

I see that the test button is just dispatching a text insert transaction to each editor.

This only gets me part of the way there. I basically need every keypress to be duplicated to each editor, so that non-text-input commands are handled as well — things like cursor and selection movements when you press [Shift+] the arrow keys, Ctrl+D to create multiple selections within an editor, clipboard paste, etc.

So it’s more than just doing inserts on both editors. That’s why I wondered whether it makes sense to try to capture each transaction coming out of the primary editor and apply it to the secondary, but i suspect that the fact that the editors have differing document states makes this difficult to pull off correctly. Or if I should just try to re-emit the raw DOM keyboard events into the secondary editor’s element and let CM deal with processing the input natively.

I haven’t actually dove in to building this quite yet, but I think I’m leaning more toward the latter at the moment.