contentsChanged vs beforeContentsChange

hello,
what are the implications of using contentsChanged (vide : VirtualFileListener) vs beforeContentsChange virtual file event ?
My experiments suggest that from beforeContentsChange callback method I can query the PSI structure of the modified file - as it has not been invalidated yet - but are there any other, possibly not so obvious - differences between these two?
Also, I've noticed that after reverting latest changes using git revert or simple 'Undo' operation (ctrl + z), *contentsChange* events seem not to be propagated at all - am I correct?
Moreover - on some occasions the event gets propagated before I hit ctrl + s to save the edited file and the changes are not reflected in an object tree I build based on it - for example when I comment or uncomment a line using ctrl + / key shortcut.
So the next question is : what triggers these events, if not file save operation ? (I thought that IDEA may have some 'autosave' functionality enabled by default but I have found no such option in Settings - besides 'force save on frame activation / deactivation').
EDIT: 'autosave' functionality shouldn't pose a problem since it is semantically equivalent to simple ctrl + s - the only difference being the 'trigger' (automatic instead of manual).

Thank you in advance for any clarification,
regards
Simon

7 comments

beforeContentsChange is sent before the change, so you have the access to the old state of the system. In contentsChanged, the file's content is already new, so if you query PSI on it, it'll also have new content. I'd discourage doing anything substantial in those listeners because it might slow down the whole IDE. The best strategy is just to drop some caches and maybe queue a background update of any structure you need (preferably using MergingUpdateQueue or Alarm).

These events come from VFS, so they reflect the IDE's view of the file system. When you modify the document, file system stays as it was until the document is saved. So no VFS events are supposed to happen on Undo, but there should be paired ones for Git Revert action. Save should also trigger both events. If you want to react to the editing, please consider using DocumentListener or PsiTreeChangeListener.

0

Thank you for clarification and sorry for a delay (I was away for a while due to winter hollidays..)
What about commenting out lines of code using ctrl + / key shortcut ?
Is that supposed to trigger a contentsChanged event on its own ?
If I'm not mistaken - it does trigger these events and it doesn't wait for ctrl + s to follow - to 'commit' the changes - which is weird.

0

No, line comment action is not supposed to trigger this event, and it doesn't for me. If it does for you, please provide a stack trace when this event occurs.

0

thank you, I must have been mistaken then.

0

one last question, if I may :
my experiments suggest that PSI structure access attempt from within beforeContentsChange listener may fail since parts of the PSI tree of the modified file has already been flagged as invalid (isValid() returns false).
So in fact I don't have access to the old state of the system, as you have suggested.
To shed some light on what I'm trying to achieve here : I use PsiElements as 'source pointers' in my tree-like object structure; each object remembers its 'origin' (the PsiElement its based on - for example a java PsiClass or a PsiMethod that defined it).
I use these 'source pointers' to model relations between objects, since fully-qualified class names may not be unique within the project scope : I remember FQNs of related objects as their IDs and when I need to retrieve the actual object I browse a collection of objects with matching IDs and for each of them I check whether it is reachable from the referencing object using source pointers of both objects :

PsiSearchScopeUtil.isInScope(p_reachableFrom.getResolveScope(), action.getSourcePointer())


So when a change of a file's contents is intercepted, I get the collection of objects that originated from that file, delete them (I use drop&rebuild method to refresh my object structure), delete any objects related to them, and schedule a rebuild of a part of the structure that has just been dropped.
Because I use PsiElements to determine the list of related objects and PsiElements are invalid - I get an exception

com.intellij.psi.PsiInvalidElementAccessException: Element: class com.intellij.psi.impl.source.PsiMethodImpl because: parent is null
    at com.intellij.psi.impl.PsiElementBase.getContainingFile(PsiElementBase.java:197)
    at com.intellij.extapi.psi.StubBasedPsiElementBase.getContainingFile(StubBasedPsiElementBase.java:186)
    at com.intellij.psi.search.PsiSearchScopeUtil.isInScope(PsiSearchScopeUtil.java:54)


In the example above, the source pointer is the PsiMethodImpl (object had been built based on a Java method definition - which has just been deleted via IDEA's file editor) and it's flagged as invalid (I checked it..) - even though I'm accessing it from within beforeContentsChange listener.
Any thoughts on how to do it properly ?

Thanks in advance

0

actually I managed to remake the whole thing and do it properly (without mixing different kinds of events..)
So - no need to answer the post above :)
(in fact - I would have deleted it if I knew how..)

0

Please sign in to leave a comment.