PSI引用(References)
PSI引用(References)
本文档主要讲2点:
- 如何查找变量方法等在哪里被使用了,
- 使用的变量在哪里声明的
PSI引用(References):从使用了变量的位置链接到声明变量的位置。
解析引用(Resolving a reference): 从声明变量的位置链接到使用该变量的位置。
编程语言定义了引用类型。例如:定义一个JAVA方法
public void hello(String message) {
System.out.println(message);
}
上述方法包含了5个引用(References)
- String: 引用了JDK中的 java.lang.String 类
- System: 引用了JDK中的 java.lang.System 类
- out: 引用了java.lang.System 类的成员变量 out
- println: 引用了java.lang.System 类的 println() 方法
- message: println(message)引用了hello(String message) 中的message参数
提示
注意: hello(String message) 中的message参数不是引用,不能被 resolve。因为此处是声明了一个变量,并没有引用其它变量
引用(Reference) 实例都是 PsiReference 接口的子类 。引用(Reference) 和 PSI元素 不同,PsiElement.getReferences() 方法会返回引用该PSI元素 的所有 References,PsiReference.getElement() 能获取该 References 使用到的 PSI元素。
解析引用(Resolving reference) - PsiReference.resolve() 方法可以定位到该元素在哪里声明的。 PsiReference.getElement() 和 PsiReference.resolve() 的不同的地方在于:PsiReference.getElement() 返回的是引用的元素,PsiReference.resolve() 返回引用的声明。例如上面示例中的第5个引用 message,getElement() 方法会返回第2行的message元素标识符 ,resolve() 返回第1行(message参数)中的message标识符
解析引用(Resolving reference) 的过程与解析代码不同,它们不是同时执行的。解析引用(resolving references)有时可能会失败,如果当前在 IDE 中打开的代码没有编译,或者在其他异常情况下,PsiReference.resolve() 可能会返回 null ,所以使用引用(references)的代码必须处理返回null这种情况。
Contributed References
IDE 能识别编程语言定义的引用,也能识别代码中API和框架定义的引用,如下面的示例:
File f = new File("foo.txt");
如上:在JAVA的语法中,"foo.txt" 只是一个字符串,没有特殊含义。但是,在IntelliJ IDEA输入上述代码,并在"foo.txt"上Ctrl + Click,如果在当前目录中有一个foo.txt文件,这时IntelliJ IDEA 会解析它的语法并提供定位到该文件上的引用(Reference)。
通常,如果一个元素没有定义好的引用(Reference)(例如,字符串和注释),IDE将会提供引用。IDE也会为非代码文件(例如xml或json)提供引用。
Contributing references 经常用来扩展编辑语言的引用(Reference)。例如:Java PSI 是intellij platform的一部分,在您的插件中并没有定义它,但是你的插件可以给JAVA提供额外引用,参考下面示例:
<extensions defaultExtensionNs="com.intellij">
<!-- language 指定编辑语言的ID,表示该Contributor 是扩展的哪个编辑语言-->
<psi.referenceContributor language="JAVA" implementation=""></psi.referenceContributor>
</extensions>
The exact places to contribute references to are then specified using Element Patterns in calls to PsiReferenceRegistrar.registerReferenceProvider() .
更多内容可以文档 Reference Contributor tutorial.
References with Optional or Multiple Resolve Results
通常情况下,PsiReference.resolve() 只会定位到一个元素, 如果 resolve 失败了,IDE 需要高亮显示代码异常的错误提示。但发生下面的特殊情况时,可以不用高亮显示异常:
soft references :如上面的示例中 File("foo.txt") ,如果IDE找不到 foo.txt文件,这并不代表是代码异常,可能是因为这个文件只会在程序运行的时候有效。所以如果 PsiReference.isSoft() 返回 True ,则可以忽略它的 resolve 异常。
polyvariant references :例如在 JavaScript 中,由于它是 动态类型语言,所以 resolve 时 IDE 就不能准确地确定哪个方法被调用了,这时 resolve 可能会定位到多个可能的元素,这样的 Reference 需要实现 PsiPolyVariantReference 接口
resolve PsiPolyVariantReference 时,应该使用 multiResolve() 方法,会返回 ResolveResult[] ,每一个 ResolveResult 对象都对应一个 PSI元素,并指定了该 PSI元素是否有效。例如:在JAVA中,一个方法拥有多个重载方法,你调用该方法时,指定的参数不能匹配到任意一个重载方法,这时调用multiResolve() 方法 你会得到 ResolveResult[] ,其中每一个ResolveResult 都关联了一个重载方法,但是isValidResult() 方法返回结果都是 false
搜索References
Resolving reference 是从使用了变量的位置链接到声明变量的位置。
references search 是从声明的位置搜索使用了该声明的位置
ReferencesSearch.search() 执行搜索功能,传入要查找的元素(也可以传入可选参数:Scope-查找的范围)。返回的 Query 对象包含所有的使用位置。可以迭代 Query 对象,找到需要的引用(Reference) 时,可以停止迭代。