Workspace.on('editor-change')

I’m creating a plugin and I’ve added the below event listener for editor changes:

app.workspace.on('editor-change', (_, info) => {
      console.log(info.data);
      console.log(info.lastSavedData);
      console.log(info.data === info.lastSavedData);
    });

I expected info.data to equal the current state of the editor (i.e., post-change), while info.lastSavedData to be the previous state (i.e., pre-change), or vice versa. However, it seems to be neither of the two. Neither data nor lastSavedData reflect the current editor state most of the time. Moreover, they are sometimes equal & sometimes not. I suppose this is because of the debounce effect.

Perhaps I’m misunderstanding something or doing something wrong, but how do I access the actual current state of the editor from this callback function?

Since loadSavedData is not exposed in the API, I’m not sure what it means.
But my guess is that

  • data: the current content of the edited document, which might not have been saved to the disk
  • lastSavedData: the content of the document when it was saved to the disk last time

how do I access the actual current state of the editor from this callback function?

It depends on what you mean by “state”.

If you want to get the current text content of the document, use editor.getValue(). Here, editor is the first argument of the callback (the one you named _).

If you want to get the current EditorState (which is an underlying CodeMirror object behind Obsidian’s editor), use editor.cm.state. Here, editor.cm is an EditorView object and editor.cm.state is an EditorState object.

https://codemirror.net/docs/ref/#state.EditorState

https://codemirror.net/docs/guide/#functional-core,-imperative-shell:~:text=Functional%20Core%2C%20Imperative%20Shell

Note that editor.cm is not available for the legacy editor.

Thanks.

What’s the typical way of accessing the previous content state? Is it await app.vault.read(info.file)?

Vault.read reads the file’s content saved in the disk at the moment.
And this is not the same as the previous state of the editor.
It’s important to distinguish between the Editor interface and the Vault interface.

  • Editor: based on the CodeMirror editor engine. Can handle edit history
  • Vault: simply interacts (read & write) with the file system

If you are not familiar with CodeMirror, I strongly recommend reading their system guide.

https://codemirror.net/docs/guide/

This page from the Obsidian developer docs is also helpful for understanding how CodeMirror (and hence Obsidian) handles the edit history.


I’m not sure it’s a typical way, but you can define an editor extension (StateField or ViewPlugin) to access the previous state. Which one to choose depends on what you want to do.

StateField:

The second parameter transaction: Transaction of the update() method (CodeMirror Reference Manual) has both the current & previous EditorState:

  • transaction.startState: previous
  • transaction.state: current

ViewPlugin:

The only parameter update: ViewUpdate of the update() method (CodeMirror Reference Manual) has both the current & previous EditorState:

  • update.startState: previous
  • update.state: current

P.S. Let me know if anyone knows a more handy way!

Here’re some minimal working examples using updateListener, StateField, and ViewPlugin, respectively. Put this in onload() method of your plugin:

		this.registerEditorExtension(
			EditorView.updateListener.of((update) => {
				console.log("-------- Update Listener --------");
				console.log("previos:", update.startState.doc.toString());
				console.log("current:", update.state.doc.toString());
			})
		)

		this.registerEditorExtension(
			StateField.define<null>({
				create: () => null,
				update: (value, tr) => {
					console.log("-------- State Field --------");
					console.log("previos:", tr.startState.doc.toString());
					console.log("current:", tr.state.doc.toString());
					return value;
				}
			})
		)

		this.registerEditorExtension(
			ViewPlugin.fromClass(
				class implements PluginValue {
					constructor(public view: EditorView) { }
					update(update: ViewUpdate) {
						console.log("-------- View Plugin --------");
						console.log("previos:", update.startState.doc.toString());
						console.log("current:", update.state.doc.toString());
					}
				}
			)
		);

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.