Refresh after external changes to project structure and sources

I have a plugin Action that launches an external tool which might make substantial changes on disk to both project structure (.idea directory) and Java/Scala sources.  I'm looking for a reliable method to ensure the IDE is up to date after these changes and I'd like to avoid something as heavy-handed as force-reloading the entire project if possible.

My first attempt was based off the code from the IDE's own, involving calls to FileDocumentManager.saveAllDocuments then SaveAndSyncHandlerImpl.refreshOpenFiles and finally VirtualFileManager.refreshWithoutFileWatcher.  This works most of the time, though some users have reported that it occassionally misses changes to project structure (.idea directory).  In my own testing I haven't been able to replicate those misses, but I have seen it behave somewhat nondeterministically (sometimes it shows the "Project Files Changed" dialog and other times it doesn't).

I'd love to hear of other approaches.  Thanks!

Comment actions Permalink

Hi James,

in an ideal world it would sufficient to call VirtualFile.refresh() with recursive=true for a project directory. However, IDEA relies on a native helper process to report file system events and there is an inherent delay in the process. So, one possible solution is to perform refresh after some pause (at the moment the value of 300 ms is used to refresh files on frame activation). Another option is to use VfsUtil.markDirtyAndRefresh() - but it may take some time to finish, especially on a large rpojects.

Also please note that FileDocumentManager.saveAllDocuments() (along with should be invoked before launching an external tool, not after.

Finally, it is not a good iea to modify a project files (those under .idea/) externally - a user may decide to change a project while the tool is running and you'll get a conflict on refresh or on a next project save.

Comment actions Permalink

Thanks for the prompt response Roman!

RE: avoiding external modification of project files, we're blocking the user from actions in IDEA via a modal dialog while this occurs.  Does that alleviate your concern?  I'm happy to go into our justification for this usage (vs. having our plugin modify the project through the approved API) if it helps paint a more complete picture.

RE: your specific suggestions, markDirtyAndRefresh made no noticeable difference over my prior code.  Adding a 500ms delay did seem to improve things, but still didn't get to 100% consistent behavior; the "Project Files Changed" dialog was skipped sometimes even though the project was updated correctly in memory.  

I eventually came up w/ something that behaves consistently w/ respect to the dialog (and, I'm hoping, resolves the issue my users have reported, which I haven't been able to replicate, in which the project state isn't updated after the external program).  Here it is:

Before external modification:

  • FileDocumentManager.getInstance().saveAllDocuments()
  • ProjectManagerEx.getInstanceEx.blockReloadingProjectOnExternalChanges()

After external modification:
  • Sleep 500ms (not sure if this is necessary for success; just left over from trying your suggestion)
  • SaveAndSyncHandlerImpl.refreshOpenFiles()
  • VirtualFileManager.getInstance().refreshWithoutFileWatcher(false)
  • ProjectManagerEx.getInstanceEx.unblockReloadingProjectOnExternalChanges()

The last of those is an async call (returns immediately), so I had to rejigger some of other code to account for that, but otherwise this seems reliable after some brief testing.  Will see what our QA team says in a few days...
Comment actions Permalink

Well, the update scheme looks consistent to me, but it wont't protect you from the "Project Files Changed" dialog. The initial idea of the check was to trigger project reload on any external modification - tnen it was relaxed for some cases. So, may be sometimes the external tool makes insignificant changes which IDEA is able to merge automatically or just ignore, but any modifications to project structure should trigger the dialog.

The difference between modifying a project through API vs. changes via files is that in the former case all listeners will receive notifications. Since IDEA can't provide the same notifications on changes to files the best it can do is to start over - hence the dialog.

Comment actions Permalink

That makes sense.  I just wanted my test case in which I make the same set of external project mods each time to behave consistently.  I'm fine with the dialog itself behaving differently, so long as it feels more deterministic in doing so. Thanks again!


Please sign in to leave a comment.