跳转至

VFS、索引与 Dumb Mode

VFS 和索引是插件性能与正确性的关键。插件一旦需要处理项目文件、查找符号或跨文件分析,就必须理解这三件事:VFS 快照、索引访问和 Dumb Mode。

Virtual File System

VFS 是 IntelliJ Platform 对文件的统一抽象。它用 VirtualFile 表示文件,不关心文件实际来自磁盘、压缩包、远程位置还是其他来源。

VFS 的核心特性:

  • 提供统一文件访问 API。
  • 跟踪文件变化。
  • 维护一个应用级快照。
  • 通过刷新操作把磁盘变化同步到 IDE。

注意:IDE UI 中看到的文件状态来自 VFS 快照,短时间内可能与磁盘实际状态不完全一致。

刷新

优先使用异步刷新:

virtualFile.refresh(false, true) {
    // run after refresh
}

同步刷新会阻塞调用线程;如果调用线程持有 Read Lock,可能导致死锁。除非你非常明确当前锁和线程状态,否则不要在后台读动作中发起同步刷新。

VFS 事件

监听 VFS 变化常用 BulkFileListener

project.messageBus.connect(disposable).subscribe(
    VirtualFileManager.VFS_CHANGES,
    object : BulkFileListener {
        override fun after(events: List<VFileEvent>) {
            // filter relevant files
        }
    }
)

VFS 监听器是应用级的,会收到所有打开项目的变化。插件应使用 ProjectFileIndex 或其他条件过滤无关文件。

索引

索引用于在大项目中快速查找文件或 PSI 元素。

两类常见索引:

类型 数据来源 查询结果 适合场景
File-Based Index 文件内容 匹配的文件 按文本、元数据或轻量结构查文件
Stub Index Stub Tree 匹配的 PSI 元素 自定义语言查声明、类、函数、符号

自定义语言插件通常依赖 Stub Index 查找可见声明,避免全项目遍历 PSI。

Dumb Mode

索引构建期间,IDE 进入 Dumb Mode。此时依赖索引的功能不能运行,否则可能抛出 IndexNotReadyException

处理方式:

  • 不访问索引的扩展实现 DumbAware
  • Action 使用 DumbAwareAction,不要只覆写 isDumbAware()
  • 需要索引的逻辑通过 DumbService 延迟到 Smart Mode。
DumbService.getInstance(project).runWhenSmart {
    // index-dependent code
}

性能建议

  • 不要用全项目 PSI 遍历替代索引。
  • 索引构建尽量基于 Lexer 信息,能不构建完整 AST 就不要构建。
  • Completion、Annotator、Inspection 中避免昂贵索引查询。
  • 对可缓存但不需要全项目预计算的数据,考虑 Gist。
  • 在内部模式中测试 Dumb Mode,确认插件在索引构建期间不会破坏编辑体验。

参考来源