Creating separate resolve scope for specific files
My plugin provides support for Clojure files. A project which is gaining popularity recently is Babashka, which uses Clojure for shell scripting. Babashka provides an executable with a lot of built-in libraries, but I'm not sure how to support this.
The basic problem is that there is nothing distinguishing Babashka files from Clojure files, and they tend to be mixed into the same project in arbitrary locations. My plan is to allow the user to specify which files are Babashka files, and then symbol resolution within those files will have to take into account the extra dependencies and other semantic elements available to Babashka scripts by default.
I've added a Babashka facet, and from a user-specified path to the executable I can get the required dependencies and download them using Maven. But I'm not sure how to attach them to the project. If I attach them to the module as libraries, then they will be available to normal Clojure code, which is incorrect. I need a way to be able to create a completely separate resolve scope, almost like a separate module. I considered using a separate Babashka module, but since the Babashka scripts are often intermingled in with the Clojure files managing the content roots would be difficult. Could I do this by using a separate module with no content roots to manage the Babashka dependencies, and then use that module's resolve scope when resolving some files in the original module using ResolveScopeProvider or something similar?
Any suggestions for the best way to handle this?
Please sign in to leave a comment.
It's possible to add individual files as content roots, but this may be inconvenient. You may try adding these additional dependencies via ResolveScopeEnlarger extension point instead.
Thanks Nikolay. I finally got around to implementing this. In the end what I have done is:
However, this doesn't work - no symbols resolve, and I think it's because the project libraries are not added to a module so they're not indexed. I can imagine a couple of possible solutions:
Any advice on the best solution here?
Yes, libraries which aren't added to dependencies of some module aren't indexed indeed. You may try using com.intellij.openapi.roots.AdditionalLibraryRootsProvider to create a synthetic library instead. It won't be shown in Project Structure dialog and user won't be able to delete or edit it, but it may be shown under 'External Libraries' node in Project View and its files will be indexed.
Thanks Nikolay, that looks useful. I have a couple of questions about that:
Hi Colin,
Please see answers below.
Provided SyntheticLibrary instances are automatically added to `GlobalSearchScope.allScope(project)`. This scope is used in common places like "Navigate | File..." popup. However, you can build a proper resolve scope when resolving symbols from Closure files. Something like you've already done before:
Will it work?
Currently, IDE doesn't keep list of all SyntheticLibraries, so you can go ahead and fetch them:
The old way was to fire `rootsChanged` event:
However, a new way is to use `com.intellij.openapi.roots.AdditionalLibraryRootsListener#fireAdditionalLibraryChanged`.
Thanks Sergey. I agree that the allScope thing is probably ok. I am now creating a SyntheticLibrary which I can see in the project view. I'm also returning a scope from my ResolveScopeProvider which I can see being called and is switching correctly based on the file being marked.
However the scope I'm using isn't returning anything from my index searches. The scope I return is based on the list of jars in the SyntheticLibrary, and I created a scope extending LibraryScopeBase and passed it the list of VirtualFiles as classes in the constructor. As far as I can tell the jars from the SyntheticLibrary are never being indexed, and the files from those jars are never being passed to my custom Scope - I only see the files from my project libraries which are added as module dependencies. I'm actually not sure whether LibraryScopeBase is appropriate, since it has the following:
and I don't know whether those index checks will return true for files in SyntheticLibraries or not. In the project view, I can see the correct list of jars, but only the two jars which also appear as module dependencies are able to be expanded (which I believe means that they're actually in use) - see below. I thought that perhaps the files weren't being indexed because the indexing happens lazily on demand and the scope wasn't correct, but I never see my scope being called for files in those jars at all. Can you think why this might be happening?
Indexing shouldn't depend on resolve scopes. What files are being added to SyntheticLibrary roots? What does `file.getPath()` returns for the library roots? Is it file://... or jar://...? If it's file://..., the content of such zip/jar files won't be traversed by indexer. You can convert it to jar:// with
If jar://... files are passed as roots to new SynthticLibrary instance, these files should be indexed. You can verify if a file inside jar/zip is indexed in the following way: open "Navigate | File..." and type there the file name. For example, here is how it works for Node.js Yarn integration:
Thanks Sergey, that was indeed the problem. I have this all working now, it's fairly complex with a fair number of moving parts, but it seems to be working well. Thanks for the help!