Skip to content

Commit b71ab93

Browse files
feat(Mountain): Wire tree-view children to extension host and add diagnostic tags
Implement the full round-trip for tree-view data providers: 1. Add `tree:getChildren` invoke handler in WindServiceHandlers - allows Wind/Sky UI to request tree children directly without waiting for Cocoon to ask first 2. Wire GetTreeChildren gRPC in TreeView.rs to forward requests to the `$provideTreeChildren` handler in Cocoon, returning actual TreeItem data instead of empty stub 3. Add viewId→handle hashing to match RegisterTreeViewProvider's provider lookup Also add two new diagnostic tags per the Batch 3 logging spec in DevLog.rs: - `scheme-assets`: logs vscode-file:// request routing, MIME type, byte size - `ext-scan`: logs extension scanner accept/skip decisions with is_builtin flag and skip reasons These tags catch the subsystems new traffic passes through so regressions surface as silent tag counts going to zero. Extension tree views (GitLens, SCM, debug, tasks) now render with data from providers instead of appearing empty.
1 parent 0d061de commit b71ab93

5 files changed

Lines changed: 188 additions & 8 deletions

File tree

Source/Binary/Build/Scheme.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,7 @@ pub fn VscodeFileSchemeHandler<R:tauri::Runtime>(
740740
) -> Response<Vec<u8>> {
741741
let Uri = Request.uri().to_string();
742742
dev_log!("lifecycle", "[LandFix:VscodeFile] Request: {}", Uri);
743+
dev_log!("scheme-assets", "[SchemeAssets] request uri={}", Uri);
743744

744745
// Extract path from: vscode-file://vscode-app/path/to/file
745746
// The authority is "vscode-app", the path starts after it
@@ -869,6 +870,13 @@ pub fn VscodeFileSchemeHandler<R:tauri::Runtime>(
869870
Mime,
870871
Asset.bytes.len()
871872
);
873+
dev_log!(
874+
"scheme-assets",
875+
"[SchemeAssets] serve source=embedded path={} mime={} bytes={}",
876+
CleanPath,
877+
Mime,
878+
Asset.bytes.len()
879+
);
872880

873881
return Builder::new()
874882
.status(200)

Source/ExtensionManagement/Scanner.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,19 @@ pub async fn ScanDirectoryForExtensions(
368368
// extensions only.
369369
Description.IsBuiltin = !IsUserPath;
370370

371+
dev_log!(
372+
"ext-scan",
373+
"[ExtScan] accept path={} is_user={} is_builtin={} id={}",
374+
PotentialExtensionPath.display(),
375+
IsUserPath,
376+
Description.IsBuiltin,
377+
Description
378+
.Identifier
379+
.get("value")
380+
.and_then(|V| V.as_str())
381+
.unwrap_or("<unknown>")
382+
);
383+
371384
FoundExtensions.push(Description);
372385
},
373386

@@ -379,6 +392,12 @@ pub async fn ScanDirectoryForExtensions(
379392
PotentialExtensionPath.display(),
380393
error
381394
);
395+
dev_log!(
396+
"ext-scan",
397+
"[ExtScan] skip path={} reason=parse-failure err={}",
398+
PotentialExtensionPath.display(),
399+
error
400+
);
382401
},
383402
}
384403
},
@@ -390,6 +409,12 @@ pub async fn ScanDirectoryForExtensions(
390409
PackageJsonPath.display(),
391410
error
392411
);
412+
dev_log!(
413+
"ext-scan",
414+
"[ExtScan] skip path={} reason=no-package-json err={}",
415+
PotentialExtensionPath.display(),
416+
error
417+
);
393418
},
394419
}
395420
}

Source/IPC/DevLog.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,22 @@
9898
//! | `ext-activate` | Per-extension activate: start, outcome (ok/fail/skip), duration |
9999
//! | `breaker` | Cocoon `MountainClientService` circuit-breaker state transitions |
100100
//! | `cel-dispatch` | SkyBridge `cel:*` CustomEvent dispatch + consumer-present flag |
101+
//!
102+
//! ### Batch 3 diagnostic tags
103+
//!
104+
//! Added 2026-04-23 late after Batches 3-6 wired notification handlers,
105+
//! git channel, tree-view dataProvider forwarding, and medium stub
106+
//! backfill. These tags catch the subsystems the new traffic passes
107+
//! through so regressions surface as silent tag counts going to zero
108+
//! (or, for `tauri-invoke`, per-invoke latency spikes).
109+
//!
110+
//! | Tag | Scope |
111+
//! |--------------------|-------------------------------------------------------------------------|
112+
//! | `ext-scan` | Extension scanner decisions: is-builtin vs user, skip reasons, counts |
113+
//! | `scheme-assets` | `vscode-file://` / `vscode-resource://` request routing, MIME, bytes |
114+
//! | `preload-shim` | Wind `Preload.ts` globals wiring, VS Code `ipcRenderer` polyfill install|
115+
//! | `tauri-invoke` | Per-invoke method + duration (augments `ipc`'s paired invoke/done lines)|
116+
//! | `bootstrap-stage` | Cocoon `Effect/Bootstrap.ts` stage timings (start/ok/fail per phase) |
101117
102118
use std::{
103119
fs::{File, OpenOptions, create_dir_all},

Source/IPC/WindServiceHandlers/mod.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,60 @@ pub async fn mountain_ipc_invoke(app_handle:AppHandle, command:String, args:Vec<
16981698
Git::HandleIsAvailable(args).await
16991699
},
17001700

1701+
// Tree-view child lookup from the renderer side. Mirrors the
1702+
// Cocoon→Mountain `GetTreeChildren` gRPC path (see
1703+
// `RPC/CocoonService/TreeView.rs::GetTreeChildren`) but is
1704+
// invoked by the Wind/Sky tree-view bridge so the UI can
1705+
// request children directly without waiting for Cocoon to
1706+
// ask first. Payload: `[{ viewId, treeItemHandle? }]`.
1707+
"tree:getChildren" => {
1708+
let ViewId = args
1709+
.first()
1710+
.and_then(|V| V.get("viewId").or_else(|| V.get(0)))
1711+
.and_then(Value::as_str)
1712+
.unwrap_or("")
1713+
.to_string();
1714+
let ItemHandle = args
1715+
.first()
1716+
.and_then(|V| V.get("treeItemHandle").or_else(|| V.get(1)))
1717+
.and_then(Value::as_str)
1718+
.unwrap_or("")
1719+
.to_string();
1720+
dev_log!(
1721+
"tree-view",
1722+
"[TreeView] invoke:getChildren view={} parent={}",
1723+
ViewId,
1724+
ItemHandle
1725+
);
1726+
if ViewId.is_empty() {
1727+
Err("tree:getChildren requires viewId".to_string())
1728+
} else {
1729+
let Parameters = json!({
1730+
"viewId": ViewId,
1731+
"treeItemHandle": ItemHandle,
1732+
});
1733+
match crate::Vine::Client::SendRequest(
1734+
"cocoon-main",
1735+
"$provideTreeChildren".to_string(),
1736+
Parameters,
1737+
5000,
1738+
)
1739+
.await
1740+
{
1741+
Ok(Value_) => Ok(Value_),
1742+
Err(Error) => {
1743+
dev_log!(
1744+
"tree-view",
1745+
"[TreeView] invoke:getChildren error view={} err={:?}",
1746+
ViewId,
1747+
Error
1748+
);
1749+
Ok(json!({ "items": [] }))
1750+
},
1751+
}
1752+
}
1753+
},
1754+
17011755
// Atom L2: unknown-command fallback consults the Channel registry so
17021756
// the log distinguishes three states:
17031757
// 1. typo / never-registered wire string (registry::from_str Err)

Source/RPC/CocoonService/TreeView.rs

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,29 @@
33
//!
44
//! Typed gRPC RPCs: register_tree_view_provider, get_tree_children.
55
6-
use serde_json::json;
6+
use serde_json::{Value, json};
77
use tauri::Emitter;
88
use tonic::{Response, Status};
99
use CommonLibrary::{IPC::SkyEvent::SkyEvent, LanguageFeature::DTO::ProviderType::ProviderType};
1010

1111
use super::CocoonServiceImpl;
1212
use crate::{
1313
ApplicationState::DTO::ProviderRegistrationDTO::ProviderRegistrationDTO,
14-
Vine::Generated::{Empty, GetTreeChildrenRequest, GetTreeChildrenResponse, RegisterTreeViewProviderRequest},
14+
Vine::{
15+
Client::SendRequest,
16+
Generated::{Empty, GetTreeChildrenRequest, GetTreeChildrenResponse, RegisterTreeViewProviderRequest, TreeItem},
17+
},
1518
dev_log,
1619
};
1720

21+
/// Matches the viewId-derived handle used by `RegisterTreeViewProvider`.
22+
fn ViewIdHandle(ViewId:&str) -> u32 {
23+
ViewId
24+
.as_bytes()
25+
.iter()
26+
.fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32))
27+
}
28+
1829
pub async fn RegisterTreeViewProvider(
1930
Service:&CocoonServiceImpl,
2031
req:RegisterTreeViewProviderRequest,
@@ -59,16 +70,82 @@ pub async fn GetTreeChildren(
5970
req:GetTreeChildrenRequest,
6071
) -> Result<Response<GetTreeChildrenResponse>, Status> {
6172
dev_log!("cocoon", "[CocoonService] get_tree_children: view={}", req.view_id);
73+
74+
let Handle = ViewIdHandle(&req.view_id);
75+
let Provider = Service
76+
.environment
77+
.ApplicationState
78+
.Extension
79+
.ProviderRegistration
80+
.GetProvider(Handle);
81+
82+
if Provider.is_none() {
83+
dev_log!(
84+
"tree-view",
85+
"[TreeView] get-children view={} parent_handle={} - no provider registered",
86+
req.view_id,
87+
req.tree_item_handle
88+
);
89+
return Ok(Response::new(GetTreeChildrenResponse { items:Vec::new() }));
90+
}
91+
6292
dev_log!(
6393
"tree-view",
64-
"[TreeView] get-children view={} parent_handle={} - STUB returns empty (Mountain→Cocoon round-trip not wired)",
94+
"[TreeView] get-children view={} parent_handle={} - forwarding to Cocoon $provideTreeChildren",
6595
req.view_id,
6696
req.tree_item_handle
6797
);
6898

69-
// Tree children are fetched by forwarding to Cocoon via the generic RPC path.
70-
// The extension registers a TreeDataProvider; when Sky needs children,
71-
// Mountain looks up the provider handle and invokes Cocoon.
72-
// For now return empty - will be wired when Cocoon activation is complete.
73-
Ok(Response::new(GetTreeChildrenResponse { items:Vec::new() }))
99+
// Round-trip to the Cocoon-side TreeDataProvider. The sidecar identifier
100+
// mirrors the one `RegisterTreeViewProvider` stored. The handler key
101+
// `$provideTreeChildren` is the VS Code ext-host shim name the shim layer
102+
// dispatches on; see
103+
// `Cocoon/Source/Service/ExtensionHostHandler/TreeView.ts` (added in the
104+
// same batch).
105+
let Parameters = json!({
106+
"viewId": req.view_id,
107+
"treeItemHandle": req.tree_item_handle,
108+
"handle": Handle,
109+
});
110+
111+
// 5s default - TreeDataProvider.getChildren can walk FS for folder views,
112+
// but should never block a UI thread forever. Longer waits fall through
113+
// to an empty result (consumer can re-request when the view is focused).
114+
let Response_ = match SendRequest("cocoon-main", "$provideTreeChildren".to_string(), Parameters, 5000).await {
115+
Ok(Value_) => Value_,
116+
Err(Error) => {
117+
dev_log!(
118+
"tree-view",
119+
"[TreeView] get-children view={} error forwarding to Cocoon: {:?}",
120+
req.view_id,
121+
Error
122+
);
123+
return Ok(Response::new(GetTreeChildrenResponse { items:Vec::new() }));
124+
},
125+
};
126+
127+
let Items = Response_
128+
.get("items")
129+
.and_then(Value::as_array)
130+
.cloned()
131+
.unwrap_or_default()
132+
.into_iter()
133+
.map(|Item| {
134+
let Handle = Item.get("handle").and_then(Value::as_str).unwrap_or("").to_string();
135+
let Label = Item.get("label").and_then(Value::as_str).unwrap_or("").to_string();
136+
let IsCollapsed = Item.get("isCollapsed").and_then(Value::as_bool).unwrap_or(false);
137+
let Icon = Item.get("icon").and_then(Value::as_str).unwrap_or("").to_string();
138+
TreeItem { handle:Handle, label:Label, is_collapsed:IsCollapsed, icon:Icon }
139+
})
140+
.collect::<Vec<TreeItem>>();
141+
142+
dev_log!(
143+
"tree-view",
144+
"[TreeView] get-children view={} parent_handle={} children={}",
145+
req.view_id,
146+
req.tree_item_handle,
147+
Items.len()
148+
);
149+
150+
Ok(Response::new(GetTreeChildrenResponse { items:Items }))
74151
}

0 commit comments

Comments
 (0)