References and Resolve
实现自定义语言PSI最重要和最棘手的部分之一是解析引用.
解析引用使用户能够从PSI元素用法(访问变量,调用方法等)导航到该元素的声明(变量的定义,方法声明等).
需要此功能才能支持** Ctrl-B 和 Ctrl-Click **调用的“Go to Declaration”操作,这是实现Find Usages
操作的先决条件,Rename
重构和代码完成.
所有作为参考的PSI元素(适用于“Go to Declaration”操作)都需要实现
方法并返回一个
从该方法实现.
该
接口可以由同一个类实现
或者由另一个班级. 元素也可以包含多个引用(例如,字符串文字可以包含多个子字符串,这些子字符串是有效的完全限定类名),在这种情况下它可以实现
并将引用作为数组返回.
主要方法是
interface是resolve()
,它返回引用指向的元素,如果无法解析对有效元素的引用,则返回“null”(例如,它应该指向未定义的类).
已解析的元素应实现PsiNamedElement接口.
resolve()
方法的对应部分是isReferenceTo()
,它检查引用是否解析为指定的元素.
后一种方法可以通过调用resolve()
并将结果与传递的PSI元素进行比较来实现,但是可以进行额外的优化(例如,仅当元素文本等于引用的文本时才执行树遍历)
.
例:
到了一个ResourceBundle
有一组接口可以作为实现解析支持的基础,即
PsiElement.processDeclarations()
方法.
这些接口具有许多额外的复杂性,这些复杂性对于大多数自定义语言来说不是必需的(例如支持替换Java泛型类型),但如果自定义语言可以引用Java代码,则它们是必需的.
如果不需要Java互操作性,插件可以放弃标准接口并提供自己的,不同的解析实现.
基于标准辅助类的resolve的实现包含以下组件:
*一个实现的类
PsiScopeProcessor
接口,它收集引用的可能声明,并在成功完成后停止解析过程.
需要实现的主要方法是execute()
,它被调用来处理在解析期间遇到的每个声明,如果需要继续解析则返回“true”,如果找到声明则返回“false”.
方法getHint()
和handleEvent()
用于内部优化,可以留空
PsiScopeProcessor
自定义语言的实现.
*一个函数,它从参考位置向上走PSI树,直到成功完成解析或直到达到解析范围的末尾.
如果引用的目标位于不同的文件中,则可以定位文件,例如,使用
FilenameIndex.getFilesByName()
(如果文件名已知)或者通过迭代项目中的所有自定义语言文件(iterateContent()
FileIndex
界面获得
ProjectRootManager.getFileIndex()
).
*各个PSI元素,在PSI树步行期间调用processDeclarations()
方法.
如果PSI元素是一个声明,它会将自身传递给.的execute()
方法
PsiScopeProcessor
传递给它.
此外,根据语言范围规则,如果需要,PSI元素可以通过
PsiScopeProcessor
它的子元素.
延伸的
接口,允许引用解析为多个目标,是
接口.
引用解析的目标从multiResolve()
方法返回.
针对此类引用的“转至声明”操作允许用户选择导航目标.
multiResolve()
的实现也可以基于
并且可以收集所有有效目标以供参考,而不是在找到第一个有效目标时停止.
“快速定义查找”操作基于与“转到声明”相同的机制,因此它可以自动用于语言插件可以解析的所有引用.