Stub索引

原文链接:
http://www.jetbrains.org/intellij/sdk/docs/basics/indexing_and_psi_stubs/stub_indexes.html

Stub树

Stub树是文件PSI树的子集;它以紧凑的序列化二进制格式存储。文件的PSI树可以由AST(通过解析文件的文本构建)或由从磁盘反序列化的stub树支持。两者之间的切换是透明的。

Stub树仅包含节点的子集。通常它只包含需要解析文件中来自外部文件声明的节点。试图访问不是stub树部分的任何节点或执行stub树不能满足的任何操作,例如访问PSI元素的文本,都将导致文件解析并将支持从PSI切换到AST。

Stub树中的每个stub都是简单的没有行为的bean类。 Stub存储对应PSI元素状态的子集,如元素的名称,public或final等访问修饰符。Stub还保存指向它在树中的父元素和其子Stub列表的指针。

要为你的自定义语言支持stub,你首先需要确定PSI树的哪些元素要存储为stub。 通常你需要将那些对其它文件可见的方法或字段存储为stub,不需要将那些对其它文件不可见的语句或本地变量存储为stub。

对于要存储在stub树中的每个元素类型,你需要执行以下步骤:

  • 为stub定义一个继承StubElement接口的接口(例子);
  • 提供一个该接口的实现(例子);
  • 确保PSI元素的接口继承由stub接口类型参数化的StubBasedPsiElement(例子);
  • 确保PSI元素的实现类继承由stub接口类型参数化的StubBasedPsiElementBase(例子)。提供接受ASTNode的stub的构造函数;
  • 创建一个实现由stub接口和实际PSI元素接口参数化的IStubElementType接口的类(例子)。实现createPsi()createStub()方法从stub创建PSI或相反。实现serialize()deserialize()方法将数据保存为二进制流;
  • 在解析时使用实现IStubElementType的类作为元素类型常量(例子);
  • 确保PSI元素接口中的所有方法在适当时访问stub数据而不是PSI树(例子:Property.getKey()的实现)。

以下步骤对于支持stub的每种语言只需执行一次:

  • 更改语言的元素类型(ParserDefinition.getFileNodeType()返回的元素类型)为继承 IStubFileElementType的类;
  • plugin.xml中定义扩展并指定包含语言解析器使用的IElementType常量的接口(例子)。

对于stub中的序列化字符串数据, 如元素名称,我们推荐使用StubOutputStream.writeName()StubInputStream.readName()方法。 这些方法确保每个唯一标识符在数据流中仅存储一次。 这减少了序列化stub树数据的大小。

如果你需要更改存储stub的二进制格式(例如你想要存储一些额外的数据或新元素),确保使用你的语言的StubFileElementType.getStubVersion()`提高stub的版本。这将导致stub和stub索引被重建,避免存储的数据格式和试图加载它的代码之间的不匹配。

默认情况下,如果一个PSI元素继承了StubBasedPsiElement,该类型的所有元素将被存储在stub树中。 如果你需要更精确的控制哪些元素被存储,重写IStubElementType.shouldCreateStub(),对那些不应该包括在stub树中的元素返回false

注意 排除不是递归的:如果返回false的元素的某些元素也是基于stub的PSI元素,则它们将包含在stub树中。

确保存储在stub树中的所有信息仅取决于构建stub的文件的内容,并且不依赖于任何外部文件。 否则当外部依赖项更改时,stub树不会被重建,stub树中将存在过时且不正确的数据。

Stub索引

当构建stub树时,你可以同时将关于stub元素的一些数据放入多个索引中,然后可以通过相应的键查找PSI元素。 与基于文件的索引不同,stub索引不支持将自定义数据存储为值;该值始终是PSI元素。 stub索引中的键通常是字符串(例如类名);如果需要也支持其他数据类型。

Stub索引是一个继承AbstractStubIndex的类。大多是情况下键类型是String,你可以使用更具体的基类即StringStubIndexExtension。Stub索引实现类在扩展点中注册。

要将数据放到索引中,你需要实现方法IStubElementType.indexStub()(例子:JavaClassElementType.indexStub())。这个方法接受IndexSink作为参数,并放入索引ID和应存储元素的每个索引的键。

要从索引访问数据可以使用以下两种方法:

  • AbstractStubIndex.getAllKeys()返回指定项目的指定索引中的所有键的列表(例如,项目中找到的所有类名的列表)。
  • AbstractStubIndex.get()返回与指定范围内的键(例如具有指定短名称的类)相对应的PSI元素的集合。

相关论坛讨论

  • Lifecycle of stub creation

你可能感兴趣的:(Stub索引)