Skip to main content

Workflows

Opening a File from the UI

How a file-tree click travels from Sky through Wind's editor and filesystem services to a Mountain disk read, then back to Monaco.

Workflow Example: Opening a File from the UI

Goal: The user clicks on a file in the File Explorer (Wind/Sky UI), and the file’s content is loaded into an editor.

This workflow demonstrates the interplay between the UI, the editor services, the filesystem provider, and the backend. A single file-tree click traverses four layer boundaries: Sky dispatches the intent, Wind’s editor and filesystem services resolve and load the model, a Tauri IPC call reaches Mountain for the native disk read, and the result unwinds back through the same chain until Monaco renders the content.

Mermaid diagram

Phase 1 - User Interaction (Sky)

  1. File Explorer UI - The user clicks on a file entry (<div> representing my-file.ts) in the File Explorer component. The onClick handler holds the file’s URI and calls into IEditorService.

  2. IEditorService.openEditor() (Wind/Source/Effect/Editor) - The UI calls editorService.openEditor({ resource: fileUri }). The openEditorEffect inside the editor Definition.ts is executed. The input is an IUntypedEditorInput. The effect first passes it to TextEditorService to resolve it into a concrete EditorInput instance, then calls findGroup to determine which editor group should handle the opening (e.g. the currently active group).

Phase 2 - Editor and Filesystem Logic (Wind → Mountain)

  1. EditorGroupsService - The openEditor method is called on the target EditorGroupModel. The group model checks whether an editor for fileUri is already open:

    • Already open: Wind focuses that tab and stops.
    • Not open: It begins opening a new editor. It uses the EditorInput’s resolve() method to obtain the editor model.
  2. TextFileEditorModel.load() - To display the content, the editor input loads its underlying model. TextFileEditorModel is responsible for this. Its load() method needs the file content. It calls IFileService.readFile(fileUri) from Wind/Source/FileSystem.

  3. IFileService.readFile() (Wind/Source/FileSystem) - The file service’s readFile(fileUri) method looks up the registered provider for the URI’s scheme (in this case file:). It finds TauriDiskFileSystemProvider from FileSystem/Definition.ts.

  4. TauriDiskFileSystemProvider.readFile() (Wind/Source/FileSystem) - The provider’s readFile method immediately executes the ReadFile Effect from the Tauri integration layer:

    Effect.runPromise(ReadFile(fileUri));
  5. TauriMainProcessService.ts (Wind/Source/Service) - The ReadFile effect executes. It calls:

    TauriInvoke("plugin:fs|ReadFile", { path: fileUri.fsPath });

    This sends the request from the webview across the process boundary into the Mountain backend.

Phase 3 - Native File I/O (Mountain)

  1. Mountain/Source/Binary/Main/Entry.rs → Tauri fs Plugin - Tauri receives the plugin:fs|ReadFile command and routes it to the tauri-plugin-fs internal Rust handler. The plugin performs the native filesystem operation:

    tokio::fs::read(path)

    The file content (Vec<u8>) is read from disk. The result is serialised and sent back to the Wind webview as the resolution of the TauriInvoke promise.

Phase 4 - Data Unwinds and UI Renders (Wind)

  1. TauriMainProcessService.ts (continued) - The TauriInvoke promise resolves with the file content. The ReadFile Effect succeeds, yielding the Uint8Array containing the raw bytes of the file.

  2. TextFileEditorModel (continued) - The load() method completes. The model is now hydrated with the file content. The EditorInput is fully resolved.

  3. EditorGroupsService (continued) - The openEditor method now has a resolved input and model. It creates a new TextEditorPane within the target editor group and sets the TextFileEditorModel on the underlying Monaco editor instance inside that pane.

  4. Monaco Editor - Monaco receives the new text model and renders the text content to the screen. The user now sees their file open in the editor.

This workflow shows how a simple UI action is translated through layers of abstraction (EditorService → FileService → FileSystemProvider → Tauri Integration) until it becomes a native OS call, with the result flowing back up the same chain to update the UI.

File Content Flow

The raw byte payload passes through distinct representations at each layer:

LayerRepresentationComponent
Mountain (disk)Vec<u8>tokio::fs::read
Mountain → Tauri IPCSerialised byte vectortauri-plugin-fs
Wind integrationUint8ArrayTauriInvoke promise
Wind FileSystemUint8ArrayReadFile Effect
Wind EditorDecoded string modelTextFileEditorModel
MonacoText buffereditor.setModel()

Important

The IFileService provider lookup is keyed on URI scheme. Only file: URIs reach TauriDiskFileSystemProvider. Custom-scheme URIs (e.g. git:, output:) are served by their own registered TextDocumentContentProvider in Cocoon, which follows a different path through the gRPC layer.

openTextDocument variants

vscode.workspace.openTextDocument supports three calling forms beyond a plain file URI:

openTextDocument API parameters

The openTextDocument function has three overloads in the VS Code API:

OverloadFirst ParameterDescription
openTextDocument(uri)UriOpens the resource identified by the URI. file:-scheme reads from disk; untitled: creates a blank untitled file; other schemes consult registered providers.
openTextDocument(path)stringShort-hand for openTextDocument(Uri.file(path)). A filesystem path on disk.
openTextDocument(options?){ language?, content?, encoding? }Creates an in-memory untitled document. language sets the language ID, content sets initial contents, encoding specifies text encoding.

All overloads return Thenable<TextDocument>. The uri and string overloads also accept an optional { encoding?: string } options parameter to control text decoding of the underlying buffer.