SyncEdit

Answered

I'm back at working on my "Sync edit" plugins.
It works in various circumstances but fails in others.

I'm using "childReplaced" event, "caretPositionChanged" event, "documentChanged" event.

So far, I'm focusing on an issue with caretPositionChanged.

Example:

<input| type> 
<input| type>


(where | are carets)
When I type a letter, let's say a, to make "inputa", caretPositionChanged is triggered. There, I need to check if the token under primary cursor has multiple occurences within a range that has been previously determined. So I need to know what is the token under the caret, however, it turns out the underlying PsiFile hasn't been updated yet. I'm now forcing a PSI refresh using BlockSupport. If somebody has other method to suggest, I'm interested.

For more details, it relates to this: https://intellij-support.jetbrains.com/hc/en-us/community/posts/206667839-Caret-beyond-end-of-line-

Ideally, "all I need" is a way to know the caret position has changed or the file has changed, after the PSI tree has been updated, and prior to further modifications being possible.

Now my last (for now?) problem is in case of multiple selection:

 

<in[put] type> 
<in[put] type>

 

where the caret is just after the t of put, if I hit delete, then in my code, I let go of 1 caret position change message, and handle the 2nd one (because there are 2 carets), then here's the file content at the time 2nd update arrives:

<in type>
<input type>Any help with fixing this appreciated!
0
6 comments

PSI changes usually happen asynchronously with respect to document modifications (typing, etc). This is because recalculating PSI can take a noticeable time.
So you must be prepared in your code that PSI is out-of-sync with document. It's possible to force a synchronous PSI update (PsiDocumentManager.commitDocument), but this can lead to UI lags.

In a multi-caret state, editing is implemented by iterating over carets, and applying corresponding logic to each of them in order. So the behaviour you observe on 'Delete' looks expected. How do you expect this behaviour to be fixed?

0

Thanks for your answer! I'm aware of the async nature of PSI generation, but for my plugin to work I need correct PSI, so it's gonna be more "deal with it or don't use the plugin". Do you recommend PsiDocumentManager.commitDocument over BlockSupport.reparseRange() ?

I also figured by experience how multi-caret works, and for example, if a character is inserted and I have 3 carets, I get 3 change notifications. On the 3rd one, the document content is "final" (ie it contains the 3 occurences of the new character). However, in my delete example, upon 3rd notification, content selected at the 3rd caret hasn't been deleted yet. I might be able to work around if I would have a way to know that the caret change was triggered by a "selection delete".

Also if I do a delete using backspace without a selection, again it's all fine, I get the caret update notification after all modifications are done. Of course, using delete key, I don't get a caret update notification since it doesn't move.

0

The fact that caret movement event is generated on 'Delete' (before the document change) shouldn't be relied upon - this is an implementation detail, that can possibly change in future. Cannot you listen to document changes instead?

0

BlockSupport.reparseRange() is an internal API and shouldn't be used by clients. So, of those two, I'd recommend commitDocument. In general, I'd recommend not to do any synchronous reparse at all, but that's obviously much harder to implement.

0

It's ok for me to rely on internal details, won't be the first time I'll adapt plugins...

I need to rely on caretPositionUpdated() because to implement multiple editing of tokens, whenever primary caret moves, I check the token under caret, and if there's any other token of the same class with same text. If there is, I add carets on all token occurences, "at the same place" (if you were on the 2nd character of the token, I put carets on the 2nd character of all "same" tokens).

Now, "Document changed" event is generated before the caret position updated event. In general, using it was ok, but then I can't do what is described above.

Thanks!

0

You can try to perform your logic in a command listener (see CommandProcessor.addCommandListener). You can notice whether any caret or document change happened during command execution, and then apply required modifications.

0

Please sign in to leave a comment.