Invalid position when calling coordsAtPos()

@marijn We’re trying to track down a bug where we’re seeing:

Error “Invalid position” firing from: prosemirror-view/src/viewdesc.js at 3898b66e0ee3a701ec321af0bf06c5eadb5129de · ProseMirror/prosemirror-view · GitHub

When we print the pos and the document size, everything seems to be fine. So is this the case of the display updating asynchronously? You mentioned this in: How to find the DOM node path from Pos - #3 by whoisterencelee

You can use the coordsAtPos method to find the screen coordinates at a position. But display updates are asynchronous, so you should probably wait for a draw event – when transform fires, the changes haven’t been flushed to the display yet.

But that was a while ago - is this still the case in the latest versions? What is the equivalent of the draw event now? How do we know when it’s safe, after dispatching a transaction, to call coordsAtPos? We’re only triggering this when trying to render other user’s cursor positions in collaborative mode - but I’m fairly certain at this point that the computed cursor positions are valid - something’s just not sync-ed up in the DOM?

Thanks!

Update: after digging around, we discovered that we could call:

if (this.view.inDOMChange) {
  this.view.inDOMChange.finish(true);
}

Adding this before coordsAtPos() - this seemed to fix the error we were seeing, but is this the right way to handle this?

When you (or the default dispatchTransaction method) calls updateState, the view is updated synchronously, except when…

if (this.view.inDOMChange) {

For various reasons, such as not being able to touch the DOM without disrupting IME compositions, and DOM changes often coming in groups which we all want to respond to together, responding to changes detected in the DOM is asynchronous. And the solution you show will break IME input, so it’s probably not a good idea. But coordsAtPos not being reliable like this is indeed an issue. For what reason are you calling coordsAtPos?

We are rendering cursors in a collaborative environment.

When a client receives a transaction from another client, it dispatches the transaction, computes the other client’s newest cursor position, and then renders it (using coordsAtPos) on top of prosemirror.

So how do we safely get this info if the DOM is out of sync with the document model?

I’m not sure yet – this is actually a somewhat tricky problem. It somewhat ties into #543, but isn’t the same issue. I’ll put some more thought into it.

The essence of the problem is that we can’t update the dom during an IME composition without disturbing that composition (so ProseMirror currently delays display updates until it finishes), which makes drawing something based on a transaction difficult.

One option would be to have updateState return something that indicates whether it was able to update the actual display, and delay drawing something like a remote cursor (or tooltip, or similar) until a transaction actually updates the display. The question remains how problematic it is for collaborative changes to only become visible when the current composition finishes, which might take arbitrarily long. Maybe we could introduce a feature that automatically resets/finishes compositions after a given period of inactivity when they are blocking other state updates.

This would still be somewhat more awkward to program against (you can’t just call coordsAtPos and know that you get a sane answer), but it’d be better than what we have now.

I’ll get back to you on this.

2 Likes

This patch should make things work a little better in your case. Still not perfect, I’m afraid, but solving this in the right way, which would involve cautiously updating the DOM during a composition, is really hard to do, so I’ve put it off until after the next release (and am not really sure I’ll be able to get it to work at all).

Thanks for following up with an update!

We’ve been using the this.view.inDOMChange.finish(true) hack and we haven’t actually noticed any issues with it yet. I’m not sure I understand what IME composition actually is - is it only for languages like Chinese / Japanese / etc? If we’re only supporting english input is this something we need to worry about?

That, and almost all input on Android works that way.

1 Like