replacing entire document text

I have a use-case where I periodically have to reset the text of the editorto a known value. I made an extension for this, which saves the edtiorView on construct, and then attempts to dispatch a transaction on it:

  public setText(newText: string, remote: boolean): void {
    try {
      this.editorView.dispatch({
        changes: [{ from: 0, to: this.editorView.state.doc.length, insert: newText }],
        annotations: [
          Transaction.remote.of(remote),
          // remote-initiated changes should not be added to the undo history
          Transaction.addToHistory.of(!remote),
        ],
      })
    } 
  }
}

From time to time I will get errors like RangeError(Position ${pos} is out of range for changeset of length ${posA});

And it looks like the state of the document is stale when I dispatch this transaction. But based on the docs it seems to me like editorView.state.doc is the most up-to-date state. Am I doing something wrong, or is there some bug in the state? Is it possible that some other transaction could have altered the length of the document between making this call and applying the transaction, such that the length becomes invalid? Or is there a more correct way to replace the entire document text via a single transaction?

I appreciate any help or guidance here.

You want editorView.state.doc.content.size. Document nodes don’t have a length property.

Ah, thank you so much, I will try that. My approach is based on from the recommendation here: CodeMirror 6: Setting the contents of the editor, which I couldn’t find when I originally posted. If it’s the case that document nodes don’t have a length property, I wonder how it is that this works most of the time, and only fails intermittently?

Thank you for the quick response, and for maintaining this useful tool.

Sorry, ignore my reply above. I was mentally on another forum (the ProseMirror one). Your code looks correct, and I cannot think of any way in which that could produce such an exception, unless you are doing weird stuff with dispatch hooks or invasive wrapper libraries.

I was able to get to the bottom of this. Our use case was streaming some code generated elsewhere into the editor under certain circumstances.

It turns out we had an extension with a naive linter that had an async call out to a worker which in some cases would go really long. When it returned, if we’d already replaced the document text, the linter warnings might be out of range. This caused the next dispatch() of a transaction to fail when it tried to update the state for those linter warnings. The fix is to be careful about async extensions (and I disabled the linter in our case) and also watch out for potential stale extention state.

I wonder if there might be some improvement to the linter subsystem that would be more robust to errors at the point of updating the linter state, rather than having those issues surfaced when dispatching an (assumed to be unrelated) transaction. But, we have a workaround that sorts this out for us in the meantime, so that’s definitely just an idea and not an ask.

The lint scheduler in @codemirror/lint will check whether the document changed while an asynchronous linter was running, and retry if it did.