public interface AsyncFileListener
BulkFileListener
that allows
for moving parts of VFS event processing to background thread and thus reduce the duration
of UI freezes. Asynchronous listeners should preferably be registered as "vfs.asyncListener" extensions.
If that's too inconvenient, manual registration via VirtualFileManager.addAsyncFileListener(com.intellij.openapi.vfs.AsyncFileListener, com.intellij.openapi.Disposable)
is possible.
prepareChange(java.util.List<? extends com.intellij.openapi.vfs.newvfs.events.VFileEvent>)
,
and the second one into AsyncFileListener.ChangeApplier.beforeVfsChange()
. Please ensure that the ordering of events
(if it's important) isn't lost during this splitting, and that your listener can handle several events about the same file.
If your listener depends on state that's not synchronized using read-write actions, be aware that this state can be changed
during prepareChange(java.util.List<? extends com.intellij.openapi.vfs.newvfs.events.VFileEvent>)
or before AsyncFileListener.ChangeApplier.beforeVfsChange()
.
Take care to synchronize the state properly and handle its changes.
The "after"-part is more complicated, as it might need to observe the state of the whole system (e.g. VFS, project model, PSI) after
the file system is changed. So moving these computations into "before"-part might not be straightforward. It's still possible sometimes:
e.g. if you only check for changed file names, or whether some file (non-directory) with a specific name is created under project's roots.
In this case the check can go into prepareChange(java.util.List<? extends com.intellij.openapi.vfs.newvfs.events.VFileEvent>)
, and the action based on it — into AsyncFileListener.ChangeApplier.afterVfsChange()
.
When you migrate a listener with both "before" and "after" parts, you can try to just move the whole "after"-processing into
AsyncFileListener.ChangeApplier.afterVfsChange()
. But make it as fast as possible to shorten the UI freezes.
If possible, consider moving heavy processing into background threads and/or performing it lazily.
There's no general solution, each "after" event processing should be evaluated separately considering the needs and contracts
of each specific subsystem it serves. Note that it'll likely need a consistent model of the world, probably with the help of
ReadAction.nonBlocking(java.lang.Runnable)
(and note that other changes might happen after yours, and
the state of the system can change when your asynchronous handler starts). This might also
introduce a discrepancy in the world when the VFS is already changed but other subsystems aren't, and this discrepancy
should be made as explicit as possible.Modifier and Type | Interface and Description |
---|---|
static interface |
AsyncFileListener.ChangeApplier |
Modifier and Type | Method and Description |
---|---|
AsyncFileListener.ChangeApplier |
prepareChange(java.util.List<? extends VFileEvent> events)
Called (possibly on a background thread) when a batch of VFS events is ready to be fired.
|
AsyncFileListener.ChangeApplier prepareChange(java.util.List<? extends VFileEvent> events)
ProgressManager.checkCanceled()
often enough.
Note that this listener can only observe the state of the system before VFS events, and so
it can't work with anything that would be after them, e.g. there will be no file in
VFileCreateEvent
, FileIndexFacade
won't know anything about the updated state after VFS change, and so on.
Note that the events posted passed might differ from the ones passed into BulkFileListener
.
In particular, there may occasionally be several events about the same file (e.g deletion and then creation, or creation and
then deletion of the file's parent). Hence the order of events is significant. Implementations should be prepared to such situations.