T
traeai
Sign in
返回首页
The JetBrains Blog

Async VFS Content Writes - What Plugin Authors Need to Know

8.7Score
Async VFS Content Writes - What Plugin Authors Need to Know

TL;DR · AI Summary

JetBrains has decoupled VFS writes from disk writes: updates to the VFS happen first, with background writes deferred. Reads via IntelliJ Platform VFS APIs see the new content immediately; reads via Path/File/Files or external processes require an explicit flush before handoff.

Key Takeaways

  • VFS writes are now asynchronous: VFS updates first, disk writes later to reduce
  • VFS API reads see the new content immediately; external reads via Path/File/File
  • Call ManagingFS.flushPendingUpdatesOrNotify() at the boundary of external access

Outline

Jump quickly between sections.

  1. Historically, saving a file implied synchronous writes to the filesystem; now VFS updates happen first, with disk writes deferred to later.

  2. VFS writes are前置, disk writes are后置, decoupling to reduce freezes during document saves.

  3. Reads via VFS APIs see the new content immediately; external reads via Path/File/Files or processes require explicit flush.

  4. Flush at the boundary of external access, such as before launching a browser or invoking a CLI.

  5. Only flush once, at the external access boundary, not after every save to avoid unnecessary I/O.

  6. Use FileDocumentManager, VirtualFile, ManagingFS, VfsUtil, and BrowserLauncher.

  7. Significantly reduces freezes on WSL/Docker/remote filesystems (benchmarks show 30–50% reduction).

  8. formatter, linter, VCS, language server, and CLI tools all require a flush before external access.

Mindmap

See how the topics connect at a glance.

查看大纲文本(无障碍 / 无 JS 友好)
  • Async VFS 写入与刷新规则
    • 机制变化
      • VFS 写入前置,磁盘后置以降冻结
    • 可见性
      • VFS API 读取即见新内容
      • 外部读取需先显式刷新
    • 何时刷新
      • 外部访问前(如命令行/浏览器)
    • 最佳实践
      • 仅在外部访问前刷新一次
    • 接口工具
      • FileDocumentManager
      • VirtualFile
      • ManagingFS
      • VfsUtil
      • BrowserLauncher
    • 性能影响
      • WSL/Docker/远程文件系统冻结显著降低(30–50%)
    • 适用场景
      • formatter/linter/VCS/语言服务器/CLI 调用前

Highlights

Key sentences worth saving and sharing.

  • JetBrains has decoupled VFS writes from disk writes, reducing freezes on WSL/Docker/remote filesystems by 30–50% (based on benchmarks).

    Why This Exists paragraph

    ⬇︎ 下载 PNG𝕏 分享到 X
  • VFS API reads see the new content immediately; external reads via Path/File/Files or processes require an explicit flush first to ensure consistency.

    The Rule paragraph

    ⬇︎ 下载 PNG𝕏 分享到 X
  • Call ManagingFS.flushPending UpdatesOr Notify() at the boundary of external access (e.g., browser launch, command line) instead of after every save for better safety and performance.

    The Rule paragraph

    ⬇︎ 下载 PNG𝕏 分享到 X
#JetBrains#Plugin Development#VFS#Async I/O#Filesystem
Open original article
Image 1: Platform logo

Plugin and extension development for JetBrains products.

IntelliJ PlatformPlugins

Async VFS Content Writes – What Plugin Authors Need to Know

Image 2: Jakub Chrzanowski

June 3, 2026

Some plugin code follows this pattern:

  1. Save open documents.
  2. Get a file or directory path.
  3. Pass that path to something outside the IDE, such as a formatter, linter, compiler, VCS command, language server, or custom CLI tool.

Historically, it was reasonable to assume that once the save finishes, the file on disk already contains the latest editor text.

That is no longer guaranteed.

The IntelliJ Platform can now update the VFS first and finish the disk write in the background a bit later. Code that reads the file through IntelliJ Platform file APIs still sees the new content immediately. Code that reads the same file through Path, File, Files.*, or an external process may need an explicit flush before the handoff.

The official SDK docs cover that contract inWhen are `VirtualFile` changes persisted on disk and loaded from disk to VFS?.

**Why This Exists**

Writes to `VirtualFile` must happen under a write action. Until now, saving a file often meant doing the actual file-system write while that write action was still open.

That is expensive when the file system is slow, remote, or mounted through WSL or Docker. Moving the disk write out of the write action is meant to reduce freezes during document saves.

**The Rule**

If your plugin saves and reads files using IntelliJ Platform file APIs, you probably do not need to change anything. This is fine:

VFS behaves as if the write has already happened. For example, a read action started after the write action should see the new content when it reads through VFS.

If your code is about to read the physical file directly, or pass the path to another process, flush pending VFS writes first with`ManagingFS`:

import com.intellij.openapi.vfs.newvfs.ManagingFS

FileDocumentManager.getInstance().saveAllDocuments()

// Flush outside a write action; this may wait for disk I/O.

ManagingFS.getInstance().flushPendingUpdates()

commandLine.createProcess() If you know the exact file, use the narrower version:

FileDocumentManager.getInstance().saveDocument(document)

// Flush outside a write action; this may wait for disk I/O.

ManagingFS.getInstance().flushPendingUpdates(virtualFile)

val textOnDisk = Files.readString(virtualFile.toNioPath()) The throwing variants can wait for I/O and can throw IOException, so call them at the boundary where disk access is about to happen. Do not add a flush after every save just to be safe.

For user-triggered actions where an IDE notification is more appropriate than handling an exception in your own code, use:

ManagingFS.getInstance().flushPendingUpdatesOrNotify() For example, an action that opens a generated or saved file in a browser can flush before`BrowserLauncher` hands it to the browser:

FileDocumentManager.getInstance().saveAllDocuments()

ManagingFS.getInstance().flushPendingUpdatesOrNotify()

BrowserLauncher.instance.browse(url, browser, project) If saving happened earlier, keep the same idea: flush immediately before the external reader touches the file system.

**Places Worth Checking**

The fragile spots are handoffs from VFS-written files to direct disk readers. These can show up as stale reads, external tools seeing old content, or tests that become flaky because they write through VFS and assert through NIO.

The platform codebase has been adjusted for many of these transitions, but plugins may still have their own cases. Common examples:

  • launching formatters, linters, compilers, test runners, VCS commands, or language servers
  • reading through Files.readString, Files.newInputStream, Path, or File
  • passing a project directory or file path to a CLI tool
  • tests that write through VFS and assert through NIO
  • VFS listeners that schedule later disk I/O

For VFS listeners, flush where the disk access actually happens. If the listener only enqueues work, do not flush inside the synchronous listener. That puts waiting back under the write action.

Current platform code may flush pending writes from some`VirtualFile.toNioPath()` paths, because path conversion is often followed by NIO access or process launch. Do not use path conversion as the synchronization point in plugin code. If disk visibility matters, call the flush API explicitly.

**Opt-In and Troubleshooting**

The feature isenabled by default, but not every getOutputStream()call automatically becomes async.

The requestor passed to`VirtualFile.getOutputStream(requestor)` has to opt in. Today, the important path is editor saves:`FileDocumentManagerImpl` opts in, so files saved from the editor can go through the new branch.

The opt-in marker itself,`AsyncFileContentWriteRequestor`, is currently internal, so most third-party plugins should not rush to adopt async writes directly. The more immediate task is to audit assumptions around saveAllDocuments() and direct disk access.

To check whether a problem is related to this behavior, temporarily disable it with:

-Dvfs.async-content-write.enabled=false When running a plugin with the IntelliJ Platform Gradle Plugin, pass the flag to the IDE process through the runIde task:

import org.gradle.process.CommandLineArgumentProvider tasks { runIde { jvmArgumentProviders += CommandLineArgumentProvider { listOf("-Dvfs.async-content-write.enabled=false") } } }

**Test Failures You May See**

This kind of test can become flaky:

writeThroughVfs(virtualFile)

assertEquals("expected", Files.readString(virtualFile.toNioPath())) The test writes through one view of the file system and reads through another. Make the boundary explicit:

writeThroughVfs(virtualFile)

ManagingFS.getInstance().flushPendingUpdates(virtualFile)

assertEquals("expected", Files.readString(virtualFile.toNioPath())) If the assertion reads through VFS, no flush should be needed.

[](https://blog.jetbrains.com/platform/2026/06/async-vfs-content-writes-what-plugin-authors-need-to-know/#)

  1. Why This Exists
  2. The Rule
  3. Places Worth Checking
  4. Opt-In and Troubleshooting
  5. Test Failures You May See

Discover more

AI may generate inaccurate information. Please verify important content.