Dynamically adding an extension to an Editor

I’m trying to write an extension for a software that utilizes CodeMirror and I need to add an extension to an already instantiated instance of a CodeMirror Editor.

I select the editor, and then use cmView.view to access the EditorView. Then, I try to do:

 const simpleViewPlugin = EditorView.updateListener.of((update) => {
    console.log("View plugin triggered!", update);
  });

  requestEditor.dispatch({
    effects: StateEffect.appendConfig.of([simpleViewPlugin])
  });

However, this does not apply the plugin. I tried this in the sandbox and it worked:

https://codemirror.net/try/?c=aW1wb3J0IHtiYXNpY1NldHVwLCBFZGl0b3JWaWV3fSBmcm9tICJjb2RlbWlycm9yIgppbXBvcnQge2phdmFzY3JpcHR9IGZyb20gIkBjb2RlbWlycm9yL2xhbmctamF2YXNjcmlwdCIKaW1wb3J0IHtTdGF0ZUVmZmVjdH0gZnJvbSAiQGNvZGVtaXJyb3Ivc3RhdGUiCgpjb25zdCBlZGl0b3IgPSBuZXcgRWRpdG9yVmlldyh7CiAgZG9jOiAiY29uc29sZS5sb2coJ2hlbGxvJylcbiIsCiAgZXh0ZW5zaW9uczogW2Jhc2ljU2V0dXBdLAogIHBhcmVudDogZG9jdW1lbnQuYm9keQp9KQoKICBjb25zdCBzaW1wbGVWaWV3UGx1Z2luID0gRWRpdG9yVmlldy51cGRhdGVMaXN0ZW5lci5vZigodXBkYXRlKSA9PiB7CiAgICBjb25zb2xlLmxvZygicmFuIikKICB9KTsKCiAgZWRpdG9yLmRpc3BhdGNoKHsKICAgIGVmZmVjdHM6IFN0YXRlRWZmZWN0LmFwcGVuZENvbmZpZy5vZihbc2ltcGxlVmlld1BsdWdpbl0pCiAgfSk7

Any idea why this might be happening?

That code looks correct. The only theory why it might not be working would be that you’re loading multiple instances of @codemirror packages (if you have to access the editor through cmView.view that suggests you’re not working from inside the script that creates the editor).

That’s accurate, I’m loading it through my plugin code. How would that cause an issue?

How is your plugin loading the @codemirror packages? Will it get the same instance of the modules as the rest of the site, or load them in some other way?

The plugins are their own encapsulated zip files made by Vite and pnpm build. All dependencies are loaded into the zip files and that is loaded into the electron environment. So I guess we would be using a different set of dependencies. Do I need to simply use the same version as the main application? Or do I need to somehow get a reference to the actual classes from the main app?

I just confirmed that both the main app and my plugin resolve to codemirror/state 6.4.1, so no version mismatch there. Would it be better if I tried to somehow get a reference to StateEffect or something like that from something inside of cmView.view?

This works totally fine :confused:

 requestEditor.dispatch({
    changes: {
      from: 0,
      to: 5,
      insert: Math.random().toString(36).substring(2, 7)
    }
  });

So I suppose the issue is with StateEffect. I wrote a recursive function to go through cmView.view and look at all the classes for one that has a .appendConfig attribute, but I can’t seem to get a reference :confused:

That doesn’t work. As soon as you pass object created by your version into the site’s version, you’ll get issues with instanceof not working (since the actual classes are separate objects) and === on statically allocated objects not doing what it should. This is simply not something the library supports.

Gotcha, thanks for the clarification. So, to be clear, there is no way for outside code to modify the extensions on a CodeMirror editor without references to the original objects?

And there is no way to get references to the original objects from the DOM (ie querySelector → cmView.view?

If the original site doesn’t provide imports for these that you can somehow access, then no, there’s no good way to get at most of these types. You may be able to access some of them via .constructor properties on existing objects, but that probably won’t get you everything you need.

It is interesting that obsidian managed to do it: Editor extensions - Developer Documentation
I am not sure how they made it so it could bypass those instanceof checks?