Multiple Threads Invoking PsiReference.resolve()
Hi there,
I'm making a plugin that primarily creates PsiReferences for certain YAML strings. For example, the plugin will create a reference for the string value of project.value in this YAML:
project:
file: 'https://github.com/some-project/some-file.txt'
When the user ctrl-clicks on 'https://github.com/some-project/some-file.txt', the plugin will clone that repo in a temporary location on disk (outside of the IntelliJ project) and return the `some-file.txt` as a PsiFile, so that it will be opened in an editor tab. I also have some simplistic logic for keeping that local repo synced up with upstream. All this is housed within my PsiReference's resolve() method.
The problem is there are actually multiple threads that will invoke the PsiReference's resolve() method when reacting to the user's ctrl-click (e.g., ApplicationImpl pool thread, Job Scheduler FJ pool, Action Updater (Common), etc). As you can imagine, this introduces a lot of race conditions; e.g., the repo gets cloned multiple times across the threads to the same temporary directory, so one thread might succeed but all the others throw path-already-exist errors.
I'm starting to think that I may be abusing the intended use case of PsiReference and reference contributors. :)
Do you have any suggestions or guidance on what I should try to resolve this issue? Would utilizing CachedValues help here?
Thank you!
请先登录再写评论。
Hi! I had a very similar problem. So similar, that the developers answering questions on slack thought I made this post. Anyway, one solution I found was to use `DirectNavigationProvider`. However, I am still not sure if this is the right approach.
PsiReference#resolve
should be used for pure resolve and navigation purposes only. I’d strongly suggest to re-think your workflow and use a completely different mechanism here.DirectNavigationProvider
is also for navigation purposes.One possible solution could be an Inspection registering a quickfix (with corresponding highlighting), so users can invoke the “other” action explicitly from context. https://plugins.jetbrains.com/docs/intellij/code-inspections-and-intentions.html
Alternatively, create a dedicated Action that could be invoked eventually from other contexts than the editor https://plugins.jetbrains.com/docs/intellij/basic-action-system.html.
Yann Cebron I am developing a plugin for Gitlab yaml files. You can include a file from remote server with include statement. It would be really useful, if you could ctrl + click on the include statement and the included file would open in a new editor. As said, I managed to do this with DirectNavigationProvider by downloading the file via Gitlab API and opening it. The whole point of doing it on ctrl + click is to provide a similar experience to navigation. Is it a bad practice to download something on ctrl + click? Would you mind explaining a bit more why we shouldn't use this?
I just feel that Inspection registering a quickfix and invoking the action explicitly won't provide the same user experience, since users are used to ctrl + clicking on references.
Thanks for answering :)
Another option would be to detect such missing navigation targets and display a banner on top of the editor that provides "fix" to perform necessary download/clone actions, https://plugins.jetbrains.com/docs/intellij/notifications.html#editor-banner
Update: there are plans to provide a true async-navigation API that might help in above use cases, but we can't share any details yet.