`vault.process` and `vault.modify` don't work when there is a `requestSave` debounce event

Neither vault.process nor vault.modify work if there’s a requestSave debounce event running in the background. This means they don’t work if they are being called within 2 seconds of a file being edited.

Steps to reproduce

Minimal plugin example:

export class OnSavePlugin extends Plugin {
    async onload() {
        const saveCommandDefinition = this.app.commands?.commands?.["editor:save-file"];
        saveCommandDefinition.checkCallback = (checking: boolean) => {
            if (checking) return this.originalSaveCallback(checking);

            const file = this.app.workspace.getActiveFile();
            this.app.vault.process(file, (data) => {
                return data.replace('Hello', 'World');
            });
        }
    }
}

There are two outcomes:

  1. Edit the file and immediately press Ctrl+S. The content doesn’t change, despite vault.process being called.
  2. Edit the file and wait for two seconds for the file to automatically save. The content is being changed as expected.
  3. Edit the file, and press Ctrl+S two times. The content will only change the second time the file is being saved.

Workarounds

  • obsidian-plugin-prettier has a workaround for this by using editor.setValue(formatted) and then manually restoring the scroll and cursor position. But that workaround is very dirty.
  • I wasn’t able to figure out what exactly Obsidian Linter does, but I think they hook into codemirror directly.
  • You can also wait for markdownView.dirty to change and then call process, but this would add a delay when pressing Ctrl+S.

Environment

Obsidian version: v1.10.3
Installer version: v1.9.14
Operating system: Windows 10 Home 10.0.19045