ToB企服应用市场:ToB评测及商务社交产业平台

标题: 索引和PSI存根 (Indexing and PSI Stubs) [打印本页]

作者: 光之使者    时间: 2023-5-9 11:45
标题: 索引和PSI存根 (Indexing and PSI Stubs)
索引

通过索引可以快速查找元素,例如:在代码库中,查找包含某个单词或某个方法的文件。插件开发者可以使用IDE已有的索引来构建和使用自己的索引。
有以下2种索引:
提示
PSI tree: PSI树,例如在java类中,java类会被解构为一个一个的PsiElement(PSI元素),所有的PsiElement构建为一个PSI树,可以参考文档 PSI元素
提示
可以使用 Index Viewer
插件查看索引的内容和属性。
 
# Dumb mode

索引在后台进程中进行构建,在构建索引的过程中,DumbService
限制了 只能使用 文本编辑或版本控制等不需要使用索引的功能 ,如果调用了需要使用索引的功能,会抛出 IndexNotReadyException异常。
通过DumbService 可以获取当前项目处于 dumb模式(该模式下不能访问索引)还是 smart模式(该模式下,索引构建完成,可以使用索引),也可以等待项目处于smart模式时再执行操作。
 
  1.     //判断项目处于什么模式下
  2.     // dumb模式(该模式下不能访问索引)
  3.     //  smart模式(该模式下,索引构建完成,可以使用索引)
  4.     DumbService.getInstance(project).isDumb();
  5.     DumbService.isDumb(project);
  6.    
  7.     //等待项目处于 smart模式 时,再执行业务操作
  8.     DumbService.getInstance(project).runWhenSmart(()->{//业务逻辑});
复制代码
 # Gists

有时候会碰到以下情况:
大部分情况下可以使用 文件索引 ,但是通过 gists的API 可以延迟计算,并缓存到硬盘上, 可以参考 VirtualFileGist
PsiFileGist的文档。
示例:
 

# 提高索引性能

# 性能指标

在2020.2及以后的版本中,索引的性能指标以json格式存储在了沙盒中的logs文件夹中,从2021.1版本开始,以HTML格式存储了另外的一些指标,参考下图:

沙盒路径查看文档:插件开发项目配置
# 避免使用AST

如果可能,尽量使用词法分析器信息而不是解析树。 如果不可能,请使用不会占用大量内存的经量级AST(LighterAST) ,这样可以提升遍历速度。可以通过将传过来的 FileContent参数 转换为 PsiDependentFileContent 然后调用 getLighterAST() 来获取 LighterAST,使用 LighterASTNodeVisitor
LightTreeUtil来遍历自己需要用到的节点,
继承 LightStubBuilder
来实现轻量级 Stub 索引
If a custom language contains lazy-parseable elements that never or rarely contain any stubs, consider implementing StubBuilder.skipChildProcessingWhenBuildingStubs()
(preferably using Lexer/node text).
考虑使用 NanoXmlUtil
来索引 xml 文件
# 预创建Stubs

如果你使用的编程语言有大量的对所有用户都一样的组件库,你可以注册 PrebuiltStubsProvider
扩展 来预创建Stubs索引,避免每次安装都需要构建Stubs索引。 文件索引
 
文件索引基于Map/Reduce
的数据结构。每一个索引都有一个指定类型的key,一个指定类型的value.
示例:word索引中,key就是word自己。value是跟key关联的,包含了word的context的对象(code, string literal, 或 comment)
在索引中,如果获取索引中某key的value的 type 等于 Void ,则表示不存在跟key相关的数据。
当一个索引实现类对一个文件创建了索引,那么这个索引实现类会收到该文件的内容,并返回一个Map, Map包含了该文件的索引key,及跟key相关的value
访问索引的时候,传入key,就能获取到跟key相关的所有文件。
提示
某些情况,可以不使用索引,而使用 Gist
# 实现文件索引

提示
UI Designer bound forms index
是一个相对简单的索引示例,它存储了 GUI Designer
.form中使用的class的全限定命令
注册索引实现类,如下:
 
  1. <extensions defaultExtensionNs="com.intellij">
  2.         <fileBasedIndex implementation="com.intellij.util.indexing.FileBasedIndexExtension 的子类"></fileBasedIndex>
  3. </extensions>
复制代码
 实现 com.intellij.util.indexing.FileBasedIndexExtension
分为以下几部分:
,最常用的是 KeyDescriptor的子类 EnumeratorStringDescriptor
getInputFilter() :只过滤自己需要的文件。可以使用 DefaultFileTypeSpecificInputFilter
如果获取到的value的type 等于 Void ,则表示不存在该文件。
通过继承 ScalarIndexExtension
可以实现简单的索引。如果索引的Value只有一个,可以继承 SingleEntryFileBasedIndexExtension
来实现
警告
为了保证相同的Value只存储一个,所有索引Value的实现类必须重写 equals() 和 hashCode() 方法。
通过 DataIndexer.map() 方法返回的数据必须只依赖用户输入的参数,不能依赖任何外部元素,否则,你的索引将不能实时更新
在开发的时候,debug索引时,可以把 intellij.idea.indices.debug/intellij.idea.indices.debug.extra.sanity 设置为true
# 访问文件索引

提示
注意:在项目处于 Dumb mode 时,不能访问索引
通过FileBasedIndex
来访问文件索引,主要包括以下操作:
提示
保证能返回最新项目中所有的key,也可能有最新项目中不包含的key(项目更新时,会更新索引,但不会删除老索引)。
警告
索引不支持嵌套访问,可能会引起死锁。应该先从索引A中获取到所有结果,然后再在索引B中使用索引A的结果。
# 标准索引

IntelliJ Platform 提供了常用的索引,如下:
# Word Index

通常使用 PsiSearchHelper
来访问
# File Name Index

FilenameIndex
可以快速的根据文件名称查找文件
# File Type Index

FileTypeIndex
可以快速的根据文件类型查找文件
# 向原有的索引根中添加索引

继承 IndexableSetContributor
,并在plugin.xml里注册,如下:
 
  1. <extensions defaultExtensionNs="com.intellij">
  2.         <fileBasedIndex implementation="com.intellij.util.indexing.FileBasedIndexExtension 的子类"></fileBasedIndex>
  3. </extensions>   
复制代码
 Stub Trees

Stub Tree是PSI tree的子集,以紧凑的二进制格式进行存储。PSI tree有2种格式,一种是AST(Abstract Syntax Tree:抽象语法树,解析文件构建而成),别一种是从硬盘上反序列化的stub tree , 这2种格式可以互相切换。
Stub Tree只包含一部分节点。通常,它只包含外部文件有访问权限的(public protected default修饰的) 的节点。如果访问Stub Tree不包含的节点,或访问像 PSI element的文本Stub tree不支持的操作,都会引起文件的解析由PSI解析切换到 AST backing。
Stub Tree中的每个stub都是一个没有任何行为的bean class 。stub存储了相关PSI Element 相关信息的子集(例如名称,public,final等),还能获取到它的父子stubs
当你想要 stubs 支持你自定义的编辑语言时,你需要确定PSI tree 中的哪些元素需要被存储进 Stubs。通常,Stubs 需要存储外部文件有访问权限的(public protected default修饰的) 方法和成员变量,不需要存储外部文件无访问权限的局部变量,private修改的方法和变量等元素
# 实现Stub索引

一个编程语言想要支持stubs ,需要执行以下几步:

中注册的 JavaStubElementTypes
 
  1. <extensions defaultExtensionNs="com.intellij">
  2.         <fileBasedIndex implementation="com.intellij.util.indexing.FileBasedIndexExtension 的子类"></fileBasedIndex>
  3. </extensions> <extensions defaultExtensionNs="com.intellij">
  4.         <fileBasedIndex implementation="com.intellij.util.indexing.FileBasedIndexExtension 的子类"></fileBasedIndex>
  5. </extensions> <extensions defaultExtensionNs="com.intellij">
  6.         <fileBasedIndex implementation="com.intellij.util.indexing.FileBasedIndexExtension 的子类"></fileBasedIndex>
  7. </extensions>
复制代码
 对于要存储在Stub tree中的每种Element type ,你需要参考下面示例的实现步骤:
,该接口需要继承StubElement
, 该接口需要继承泛型为PropertyStub(第一步定义的接口)StubBasedPsiElement
,该类需要实现 Property接口(第3步定义的接口) ,同时也要继承泛型为PropertyStub(第一步定义的接口)StubBasedPsiElementBase
实现 Property.getKey()(示例:PropertyImpl.getKey()
提示
如果使用 Grammar-Kit
来生成编程语言的 PSI ,可以通过 Stub indices support文档了解如何让你的语法整合 stub
默认情况下,所有继承了 StubBasedPsiElementPSI element都会存到 stub tree 。如果不需要存储某类型元素,可以在该类型元素中重写 IStubElementType.shouldCreateStub() 方法并返回 false ,这个策略只作用于元素自身,如果该PSI元素的子元素继承了 StubBasedPsiElement,该子元素依然会存储到stub tree 中。
# 序列化数据

推荐使用 StubOutputStream.writeName()StubInputStream.readName() 方法来序列化元素名称等String类型的数据。这些方法可以保证在数据流中同一个数据只存储一次,从而减少 stub tree的占用空间。也可以参考 DataInputOutputUtil
如果需要改变存储Stub的二进制格式(例如:想存储额外的数据或一些新元素),你需要升级通过 IStubFileElementType.getStubVersion() 获取到的 stub 版本。版本升级后,为了避免 Stub 和项目的代码不匹配,会重新构建Stubs和Stub索引
必须要保证在 stub tree 中存储的数据只依赖于stub的文件的内容,不依赖于任何外部文件。这是因为外部文件更新后,stub tree 并不会重新构建,从而导致stub tree 中数据不准确。
# Stub索引

在构建 stub tree 的同时,可以把 stub elements 的相关数据存储到其它索引里,这些索引就可以用来查找 PSI elements 。和文件索引不一样,stub索引的value不支持存储自定义数据,只会存储 PSI elements 。stub索引中的key一般都是String类型(例如使用类名),如果需要,也可以使用其它类型。
stub index的类必须继承 AbstractStubIndex
。但是,大多数情况下,如果 stub index 的key的类型是string,可以通过继承 StringStubIndexExtension来实现 ,然后在 plugin.xml 里注册为 com.intellij.stubIndex 类型的扩展。
向某个Stub索引中存储数据,需要实现 IStubElementType.indexStub() 方法 (示例:JavaClassElementType.indexStub()
)。该方法接收 IndexSink 参数,并存储每一个需要存储的元素的key和 索引id
可以使用下面2个方法来访问stub索引:
# 相关论坛

Lifecycle of stub creation
 
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4