Expose API for browser extensions

Browser extensions that act on editable content, such as Grammarly or LanguageTool, currently cannot fully work on CodeMirror 6 because of viewporting. Would you be open to exposing some kind of (possibly read-only) API for such extensions? Specifically, as a minimum, some way of obtaining the full editable document text from a CM6 editor root DOM node. Being able to add something like an updateListener would also be useful.

Rough example, assuming CodeMirror is an object exposed by some means such as a custom event or a global object:

const cm = CodeMirror.fromDom(document.querySelector(".cm-editor"));
const docText = cm.state.doc.toString();
cm.updateListener(update => {
  if (update.viewportChanged) {
    console.log("Viewport changed");
  }
});

What good would a read-only interface be for such extensions? Isn’t the whole point that they want to make edits to the content?

I feel browser extensions that insert themselves into the page’s script are fundamentally incompatible with the kind of closed-world model that page authors need to actually write reliable software—as soon as you have crap like Grammarly mutating stuff that you didn’t assume to be mutable, the invariants that you built your program on go out the window.

I can imagine browsers eventually supporting some kind of interface for this kind of editing helper, or even something being standardized in JavaScript, in a way that is well-defined and that page scripts can work with. But until that exists, I don’t really want to encourage the invasive monkey-patching that these extensions are doing.

In the case of Grammarly, I’m not sure whether you’re aware that its approach changed fundamentally around a year ago. The short version is that its decorations are now drawn in a separate layer outside the editor content, and it doesn’t touch the content itself. It already works reasonably well with CM6, but without access to the full document text, it can’t, for example, provide an accurate number of how many suggestions it wants to give you. It also doesn’t pick up viewport changes accurately, which it would be better able to do with access to an update listener.

So you’re working on one of these tools, and want to add a special case for CodeMirror regardless of what kind of site it is embedded in, accessing its content, and listening for updates, through some kind of API?

Do you have similar special cases for other libraries? Has any discussion on standardizing something like this happened?

Interestingly I’m trying to do the same thing in my extension, except it’s limited to read/write/listen and no changes to the DOM.

I opened an issue in these forums:

And my current progress can be found on:

In this file you can see how I deal with some other editors, like CM5, in a handful of lines each:

So you’re working on one of these tools, and want to add a special case for CodeMirror regardless of what kind of site it is embedded in, accessing its content, and listening for updates, through some kind of API?

I’m not; I work for Overleaf, and we’re looking into how such tools can integrate with our editor. But yes, that’s the general idea.

Do you have similar special cases for other libraries? Has any discussion on standardizing something like this happened?

Not yet, although we’re exploring options, including exposing some kind of API on our editor for browser extensions. For widely-used, non-Overleaf-specific tools such as Grammarly, it seems preferable to have a generic CM6 API; I’m sure we won’t be the only ones wanting good Grammarly integration with our CM6-based editor.

Though this is less bad than it used to be, this is still a tool inserting itself in ways that the author of the page (or the author of the libraries used in the page) didn’t anticipate, and violating the assumptions of isolation in the way web pages are displayed. From what I can tell, it no longer mutates the DOM to decorate it, but it must still read the DOM to decorate it, and mutate the DOM to apply its fixes.

First of all, I don’t see how having access to the CodeMirror APIs would help this tool—it’s working on the level of the DOM already because it needs to know which DOM nodes to place its overlays on top of.

Secondly, I can see exposing the document as a string and a way to inject an update listener on the DOM level as a sure way to invite code that’ll destroy performance when the user is editing a large document, where flattening it (and doing some kind of full-text analysis on it) is expensive.

No, what I’m talking about is for these tools to define a way in which they want to be able to approach 3rd party editor components, so that, instead of them hard-coding some invasive kludges to work with specific libraries, the libraries can interact with them on their own terms.

But I realize that’s probably too much to ask.

Without wishing to get in to a protracted debate, I have two brief points on this: first, having access to the full document text would allow Grammarly to report all of its suggestions for the whole document to the user rather than just those in the current viewport, which seems useful to me. Secondly, it can (and does) already affect performance of the editor simply by using DOM APIs (scroll and input event listeners, for example, or MutationObservers), so I’m not convinced that providing it access to a CodeMirror update listener necessarily worsens the situation. Ultimately, any browser extension can clobber editor performance if it wants to, API or not.