Skip to content

feat: add virtual workspace support via registerUriTranslator API#1008

Open
jianyexi wants to merge 1 commit into
astral-sh:mainfrom
jianyexi:feat/virtual-workspace-support
Open

feat: add virtual workspace support via registerUriTranslator API#1008
jianyexi wants to merge 1 commit into
astral-sh:mainfrom
jianyexi:feat/virtual-workspace-support

Conversation

@jianyexi

Copy link
Copy Markdown

Summary

Add virtual workspace support via a registerUriTranslator API, following the same pattern Pylance uses.

VFS providers (e.g., Microsoft Fabric Data Engineering VS Code extension) implement a RuffUriTranslator and register it with Ruff. The translator handles bidirectional URI translation between virtual workspace URIs and local disk cache URIs, enabling Ruff's linting, formatting, and auto-fix to work on virtual files.

Closes #1007

Design

The VFS provider owns the disk cache. Ruff exports the API.

const ruff = vscode.extensions.getExtension('charliermarsh.ruff');
await ruff.activate();
ruff.exports.registerUriTranslator({
  async initialize(folders) { /* sync config files to local cache */ },
  translateToDisk(uri) { /* virtual → cached file:// */ },
  translateToVirtual(uri) { /* file:// → virtual */ },
});
// Ruff restarts server automatically with translator wired in

RuffUriTranslator interface

  • initialize(workspaceFolders) — async, called once before server start. VFS provider syncs config files, creates directory structures, pre-populates translation caches.
  • translateToDisk(uri) — synchronous, returns file:// URI for a virtual URI.
  • translateToVirtual(uri) — synchronous, returns virtual URI for a file:// URI.

How it works

  1. Ruff defers server startup in virtual workspaces until a translator registers
  2. On registration, runServer() is called
  3. translator.initialize() prepares the local cache
  4. uriConverters on the LanguageClient translate all URIs via the translator
  5. Settings (cwd, workspace) are translated before sending to the server
  6. workspace/configuration middleware translates dynamically requested settings
  7. Config file changes trigger workspace/didChangeWatchedFiles notifications

Changes

File Lines Purpose
src/common/uriTranslator.ts +110 Interface + API + registration logic
src/common/vfsSupport.ts +188 Translator init, uriConverters, middleware, settings translation
src/common/server.ts +47 Calls vfsSupport functions
src/extension.ts +48 Returns API, deferred VFS startup, translator guard
src/common/commands.ts +3 Use code2ProtocolConverter for command URIs
src/common/settings.ts +22 Scheme-aware variable resolution
src/common/utilities.ts +3 Virtual workspace getProjectRoot() fix
package.json +2 virtualWorkspaces.supported: "limited"
.vscode-test.js +30 Separate VFS test config
src/test/testVfsExtension/ +270 Test VFS provider + translator extension
src/test/vfs.e2e.test.ts +150 E2E tests for VFS scenarios

Test Plan

5 E2E tests run in a pure virtual workspace (testvfs:/project):

VFS E2E tests
  ✔ Ruff should export registerUriTranslator API
  ✔ Translator should translate URIs bidirectionally
  ✔ Ruff should provide diagnostics for VFS Python file
  ✔ Ruff should format VFS Python file
  ✔ Ruff should apply auto-fix on VFS file
5 passing

Existing tests (Unit, E2E) are unaffected — no existing test files were modified.

Context

I work on the Microsoft Fabric Data Engineering VS Code extension which uses a custom VFS. We already register a URI translator with Pylance for Python IntelliSense. This PR adds the same integration pattern for Ruff.

Add support for virtual workspaces by exporting a registerUriTranslator()
API that VFS providers (e.g., vscode-trident) can use to register a URI
translator. This follows the same pattern as Pylance's registerUriTranslator.

The VFS provider owns the disk cache and implements three methods:
- initialize(workspaceFolders): async setup — sync files to cache, populate
  translation maps. Called once before the server starts.
- translateToDisk(uri): synchronous — returns cached file:// URI for a virtual URI
- translateToVirtual(uri): synchronous — returns virtual URI for a file:// URI

On registration, the Ruff language server restarts with:
- uriConverters that translate all URIs via the translator
- Server cwd set to the translated workspace root
- All initializationOptions settings translated to local paths
- Middleware for config file change notifications (didChangeWatchedFiles)
- Commands (autofix, format, organize imports) use code2ProtocolConverter

Key design decisions:
- translateToDisk/translateToVirtual are synchronous (all async work in initialize)
- Server defers startup in virtual workspaces until translator registers
- Uses toString(true) for file URIs to avoid percent-encoding drive letters

Changed files:
- New: src/common/uriTranslator.ts — interface + API + registration logic
- src/extension.ts — activate() returns API, deferred VFS startup
- src/common/server.ts — translator integration, cwd/settings translation
- src/common/commands.ts — use code2ProtocolConverter for command URIs
- src/common/settings.ts — scheme-aware variable resolution
- src/common/utilities.ts — virtual workspace getProjectRoot() fix
- package.json — virtualWorkspaces.supported: true

Ref: astral-sh#1007

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add virtual workspace support

1 participant