Get PSI reference in standalone parser

已回答

I'm trying to get references for a method using a standalone PSI parser. The following code gets close but results in an NPE:

import com.intellij.core.JavaCoreApplicationEnvironment
import com.intellij.core.JavaCoreProjectEnvironment
import com.intellij.model.search.SearchService
import com.intellij.model.search.impl.SearchServiceImpl
import com.intellij.openapi.Disposable
import com.intellij.openapi.extensions.ExtensionPoint
import com.intellij.openapi.extensions.Extensions
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.augment.PsiAugmentProvider
import com.intellij.psi.impl.search.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.descendantsOfType
import com.intellij.util.QueryExecutor
import java.io.File

object GetReferences {
@JvmStatic
fun main(args: Array<String>) {
val disposable = Disposable {}
val applicationEnvironment = JavaCoreApplicationEnvironment(disposable)
val rootArea = Extensions.getRootArea()
JavaCoreApplicationEnvironment.registerExtensionPoint(
rootArea,
PsiAugmentProvider.EP_NAME,
PsiAugmentProvider::class.java
)
JavaCoreApplicationEnvironment.registerExtensionPoint(
rootArea,
"com.intellij.useScopeEnlarger",
com.intellij.psi.search.UseScopeEnlarger::class.java
)
JavaCoreApplicationEnvironment.registerExtensionPoint(
rootArea,
"com.intellij.useScopeOptimizer",
com.intellij.psi.search.ScopeOptimizer::class.java
)
JavaCoreApplicationEnvironment.registerExtensionPoint(
rootArea,
"com.intellij.referencesSearch",
QueryExecutor::class.java
)
JavaCoreApplicationEnvironment.registerExtensionPoint(
rootArea,
"com.intellij.codeUsageScopeOptimizer",
com.intellij.psi.search.ScopeOptimizer::class.java
)

applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
SearcherQueryExecutor()
)

applicationEnvironment.registerApplicationService(
SearchService::class.java,
SearchServiceImpl()
)

applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
VariableInIncompleteCodeSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
SimpleAccessorReferenceSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
SPIReferencesSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
ConstructorReferencesSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
PsiAnnotationMethodReferencesSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
JavaRecordComponentSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
SearcherQueryExecutor()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
CachesBasedRefSearcher()
)
applicationEnvironment.addExtension(
ReferencesSearch.EP_NAME,
NonPhysicalReferenceSearcher()
)

val projectEnvironment = JavaCoreProjectEnvironment(disposable, applicationEnvironment)
projectEnvironment.project.extensionArea.registerExtensionPoint(
PsiElementFinder.EP.name,
PsiElementFinder::class.java.name,
ExtensionPoint.Kind.INTERFACE
)
projectEnvironment.project.registerService(
com.intellij.psi.search.PsiSearchHelper::class.java,
PsiSearchHelperImpl(projectEnvironment.project)
)

val file = File("/tmp/GetReferences.java")
val root = applicationEnvironment.localFileSystem.findFileByIoFile(file)!!
projectEnvironment.addSourcesToClasspath(root)

val psiFile = PsiManager.getInstance(projectEnvironment.project).findFile(
applicationEnvironment.localFileSystem.findFileByIoFile(file)!!
)!!

val calleeMethod = psiFile.descendantsOfType<PsiMethod>().find { it.name == "calleeMethod" }!!
val references = ReferencesSearch.search(
calleeMethod,
GlobalSearchScope.projectScope(projectEnvironment.project)
).findAll()
println(references)
}
}

The NPE it throws is:

Caused by: java.lang.NullPointerException
    at com.intellij.util.indexing.ID.getCallerPluginId(ID.java:185)
    at com.intellij.util.indexing.ID.create(ID.java:106)
    at com.intellij.find.ngrams.TrigramIndex.<clinit>(TrigramIndex.java:27)
    ... 22 more

 

I would appreciate help solving this issue and please let me know if there is an easier way to get PSI references than the code above. I started with JavaCoreApplicationEnvironment/JavaCoreProjectEnvironment and only added extension points and services to resolve any errors raised, but perhaps there is a more straightforward way of doing this.

Also, I had to set the idea.home.path property. Is there a way to get references without having the IDE installed locally?

评论操作 固定链接

You'll probably end up re-building the whole infrastructure programmatically in this setup and most probably continue running in such issues regularly. JavaCoreApplicationEnvironment and related are currently not meant for external use really.

 

What is the goal of running all in standalone mode?

0
评论操作 固定链接

Thanks for the input. I was able to get past this issue by using LocalSearchScope. It looks like the idea.home.path property and the ID.getCallerPluginId() NPE are both coming from trying to do a text search on the entire project.

I'm more than willing to move forward with LocalSearchScope, but it looks like cross-file references do not work with this approach. I'm creating a LocalSearchScope with two files where one references the other but the reference isn't picked up. Do you know why that would happen?

As for the goal, I'm looking to offload PSI processing that occurs on the developer's IDE. I'm basically creating a "shared indexes" feature for my plugin. Having a centralized server that holds the latest processing results would allow the plugin to skip recalculating those results locally.

0
评论操作 固定链接

What exactly are the performance issues you're trying to solve in this way?

0
评论操作 固定链接

They aren't performance issues. My plugin supports teams working on the same code and I'd like to avoid duplicate processing. Otherwise, each developer will be running the same analyses even when the results haven't changed.

My first thought was to have the plugin perform the analyses and save the results to a centralized server. That could work but I'd like to process the PSI even when there are no IDEs connected. So I'm trying to build something like the shared indexes functionality.

0
评论操作 固定链接

Yann, would you mind providing a bit of insight into why LocalSearchScope does not work for cross-file references? There doesn't seem to be much documentation on it so I'm not sure entirely sure if it should work the way I described above.

0
评论操作 固定链接

Search relies on a number of other things to work. e.g. ID indexing. Also, it does optimize the final search scope in many ways. So basically it's quite impossible to tell what's going wrong without debugging and verifying all required indexes/services are correctly setup in your environment.

0

请先登录再写评论。