Workflows
Executing a Command from the Command Palette
How Ctrl+Shift+P fetches the command list from Mountain and routes execution to either a native Rust handler or a proxied Cocoon extension command.
Workflow Example: Executing a Command from the Command Palette ⌨️
Goal: The user opens the Command Palette (Ctrl+Shift+P), types the name of a command (e.g., “Format Document”), and selects it. The corresponding action is executed. This workflow covers both a native command and an extension-provided command.
The command palette is a thin UI over Mountain’s CommandRegistry. Every command — whether implemented in Rust or contributed by an extension in Cocoon — is registered in that single registry. Wind fetches the list via Tauri IPC, the user selects an entry, and Mountain routes execution either to a native Rust function pointer or to Cocoon via gRPC, depending on the handler type stored at registration time.
flowchart LR
subgraph OpenPalette["Phase 1: Opening Command Palette"]
UserKeybind([User presses<br/>Ctrl+Shift+P]) --> Dispatch["Dispatch<br/>workbench.action.showCommands"]
Dispatch --> InvokeQuickInput["QuickInputService.quickAccess.show()"]
InvokeQuickInput --> CommandsProvider["CommandsQuickAccessProvider"]
end
subgraph FetchCommands["Phase 2: Fetching Command List"]
CommandsProvider --> GetAllCommands["TauriInvoke<br/>mountain://command/get-all"]
GetAllCommands --> Track1["Track Dispatcher"]
Track1 --> Effect1["Common::command::GetAllCommands<br/>Effect"]
Effect1 --> CommandProviderGetAll
end
subgraph GetAll["GetAllCommands Execution"]
CommandProviderGetAll["Mountain<br/>CommandProvider.GetAllCommands()"] --> LockRegistry["Acquire lock on<br/>CommandRegistry"]
LockRegistry --> GetKeys["Get command ID keys<br/>from HashMap"]
GetKeys --> ReturnIds["Return command ID list"]
end
ReturnIds --> DisplayList
subgraph DisplaySelect["Phase 3: Displaying & Selecting"]
DisplayList["Populate Quick Pick UI<br/>with command list"] --> UserSelect([User types & selects<br/>command])
UserSelect --> ExecuteService["ICommandService.executeCommand()"]
end
ExecuteService --> NativeCheck{Native or<br/>Extension?}
subgraph NativeFlow["Phase 4A: Native Command Execution"]
NativeCheck -->|Native| NativeInvoke["TauriInvoke<br/>mountain://command/execute"]
NativeInvoke --> Track2["Track Dispatcher"]
Track2 --> Effect2["Common::command::ExecuteCommand<br/>Effect"]
Effect2 --> LookupNative["Lookup command in<br/>CommandRegistry"]
LookupNative --> NativeHandler["Invoke native Rust<br/>function pointer"]
NativeHandler --> NativeResult["Execute command logic"]
NativeResult --> ReturnNative["Return result"]
end
subgraph ExtensionFlow["Phase 4B: Extension Command Execution"]
NativeCheck -->|Extension| ExtensionCmd["TauriInvoke<br/>mountain://command/execute"]
ExtensionCmd --> Track3["Track Dispatcher"]
Track3 --> Effect3["Common::command::ExecuteCommand<br/>Effect"]
Effect3 --> LookupExt["Lookup Proxied handler<br/>in CommandRegistry"]
LookupExt --> ProxyRequest["IpcProvider makes<br/>$executeContributedCommand<br/>gRPC request"]
ProxyRequest --> CocoonRPC["Cocoon gRPC Server"]
CocoonRPC --> LookupProvider["Lookup command in<br/>Cocoon registry"]
LookupProvider --> ExecuteExt["Execute extension's<br/>JavaScript function"]
ExecuteExt --> ReturnExt["Return result to Mountain"]
ReturnExt --> ForwardToWind["Forward result to Wind"]
endPhase 1 — Opening the Command Palette (Sky → Wind)
Source files:
Wind/Source/Application/QuickInput/Definition.tsvs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts
User Input — The user presses
Ctrl+Shift+P. The keybinding system dispatches theworkbench.action.showCommandscommand.QuickInputService— The handler forshowCommandsinvokesquickInputService.quickAccess.show(), which opens the Quick Pick UI configured for showing commands. Delegates population toCommandsQuickAccessProvider.CommandsQuickAccessProviderneeds the full command list. Since this provider lives in Wind but the registry lives in Mountain, it makes an Integration-layer call. It executes an Effect that wraps:TauriInvoke("mountain://command/get-all");
Phase 2 — Fetching the Command List (Mountain)
Source files:
Track Dispatch — The Tauri command is dispatched to Mountain’s
trackmodule, which creates theCommon::command::GetAllCommandsEffect. TheAppRuntimeexecutes the Effect, callingMountainEnvironment’sCommandExecutor::GetAllCommandsimplementation, which delegates to the handler.CommandProvider.GetAllCommands()(CommandProvider.rs:L322) — Acquires a read lock onAppState.CommandRegistry, retrieves thekeys()of theHashMap(containing IDs of all registered native commands AND all proxied commands from Cocoon), and returns the list of command ID strings.List Resolution — The list resolves back to
CommandsQuickAccessProviderin Wind, which populates the Quick Pick UI, making the commands visible to the user.
Phase 3 — User Selection (Sky)
The user types a query (e.g. “Format Document”) and presses Enter. The Quick Pick widget registers that the command
editor.action.formatDocumenthas been selected, and calls:ICommandService.executeCommand("editor.action.formatDocument");
Phase 4A — Native Command Execution (Wind → Mountain)
CommandService(Wind) — Since Wind’sCommandServiceis a thin client, it immediately forwards the request to the backend by creating and running an Effect that wraps:TauriInvoke("mountain://command/execute", { commandId: "editor.action.formatDocument", args: [], });Arguments are serialized as a JSON array matching the command’s parameter signature. For commands accepting primitive types, objects, or even editor references (resolved by Wind before dispatch), each argument is encoded as a JSON value. Complex types like
TextEditororUriare serialized to their identifier form ({ $mid: 1, path: "/..." }forUri, or the editor ID forTextEditor) and reconstituted on the Mountain side before being passed to the native handler.Track Dispatch (Mountain) — The request is dispatched to
track, creating theCommon::command::ExecuteCommandEffect. TheAppRuntimeexecutes it, callingMountainEnvironment’sCommandExecutor::ExecuteCommand.CommandProvider.ExecuteCommand()(CommandProvider.rs:L231) — Acquires a lock onAppState.CommandRegistryand looks up"editor.action.formatDocument". It finds that the handler is of typeCommandHandler::Native, then invokes the corresponding native Rust function pointer, passing it theAppHandle,Window,AppRuntime, and arguments.Native Formatting Handler — The native Rust code for formatting a document runs. This typically involves:
- Getting the active document’s content from
AppState. - Finding a registered formatting provider in
AppState(which may itself be proxied to Cocoon for extension-backed formatters). - Requesting edits from the provider.
- Applying the edits via the
WorkspaceEditApplier. - Returning the command result (if any) up the call chain.
- Getting the active document’s content from
Phase 4B — Extension Command Execution (Wind → Mountain → Cocoon)
Steps 8 and 9 are identical to Phase 4A. The difference is in what Mountain finds at step 10.
handlers/commands/CommandsLogic.rs(Mountain) —ExecuteCommandLogiclooks up"my-extension.doSomething". It finds the handler is of type:CommandHandler::Proxied { SidecarIdentifier: "cocoon-main", CommandIdentifier: "my-extension.doSomething", }IpcProvider(Mountain) — The handler knows it must proxy the request. It uses theIpcProvidercapability from theMountainEnvironmentand sends a$executeContributedCommandgRPC request to Cocoon, carrying the command ID and its arguments. The arguments are serialized using the same JSON encoding used in Phase 4A, then deserialized by Cocoon before invoking the JavaScript handler.Cocoon Command Resolution — Cocoon’s gRPC server receives the request and dispatches it to the
CommandsProvider. TheCommandsProviderlooks up"my-extension.doSomething"in its own local registry and executes the extension’s JavaScript handler function with the deserialized arguments.Result Propagation — The extension’s command finishes and returns a result (e.g. a string, number, or object). This result is serialized and sent back to Mountain as the gRPC response. Mountain receives the response and forwards it back to Wind as the resolution of the
TauriInvokepromise. The command execution is complete.
Important
Extension commands are registered in Mountain’s CommandRegistry as proxied entries during extension activation — Cocoon sends a $registerExtensionCommand gRPC call for each vscode.commands.registerCommand invocation. Mountain never needs to know a command’s implementation language; the CommandHandler enum value determines the dispatch path at execution time.
registerTextEditorCommand
vscode.commands.registerTextEditorCommand(id, callback) wraps the callback so it always receives (textEditor, editBuilder, ...args) with live objects:
textEditoris the activeTextEditorproxy, including.edit(),.setDecorations(), and.revealRange().editBuilderis a realTextEditorEditbuffer tied to the active Monaco model. Edits collected viabuilder.replace(),builder.insert(), orbuilder.delete()are applied atomically when the callback returns. If the callback is async (returns aPromise), the builder waits for resolution before flushing.
When no editor is active, a no-op builder is passed so the extension’s pre-edit setup still runs without throwing.
onDidExecuteCommand
Mountain emits sky://commands/executed to the Sky renderer after every command dispatch, and simultaneously sends $acceptCommandExecuted over the Vine gRPC channel to Cocoon. Notification/Handler.ts in Cocoon catches the Vine notification and re-emits it on the shared Emitter channel commands.executed. The onDidExecuteCommand subscription in Commands/Namespace.ts listens on that channel, so extensions receive the event for both native and extension-contributed commands. Local (Cocoon-to-Cocoon) executeCommand calls also emit on commands.executed directly, ensuring extension-to-extension calls are visible to listeners without a Mountain round-trip.
The dispatched command’s ID and its arguments are both forwarded as part of the notification payload, so listeners can filter by command or inspect what was passed. This is how downstream features like keybinding recording, command history, and extension debugging observe which commands have been executed.