Skip to content

Commit c6982bf

Browse files
feat: Implement Sky Bridge for Diagnostics, SCM, and Search
- Added InstallDiagnostics.ts to bridge diagnostics from Mountain to IMarkerService, enabling error visibility in the editor. - Introduced InstallScm.ts to handle SCM events, allowing Sky-side viewlets to mirror SCM state and register providers against the live workbench service. - Created InstallSearch.ts to register a search result provider that routes search queries to Mountain's handlers, ensuring proper file and text search functionality. Signed-off-by: Nikola Hristov <Nikola@PlayForm.Cloud>
1 parent 8fd293a commit c6982bf

4 files changed

Lines changed: 1128 additions & 15 deletions

File tree

Source/Function/Sky/Bridge.ts

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,23 +1557,50 @@ async function _InstallSkyBridgeOnce(): Promise<void> {
15571557
void Error;
15581558
}
15591559
}
1560-
// One-time success-path confirmation. Fires on the FIRST
1561-
// diagnostic event that actually pushed markers, then stays
1562-
// silent. Without this we can't distinguish "bridge runs but
1563-
// markers are empty / malformed" from "bridge never runs at
1564-
// all" - both look like a silent Problems panel. The fields
1565-
// included are exactly what we need to triage either failure
1566-
// mode (URI scheme/normalisation, marker severity validity,
1567-
// message length).
1560+
// One-time success-path confirmation. Also checks the Problems panel
1561+
// view filter state and clears `activeFile` if it was left on, which
1562+
// causes "count shows but panel empty" - the status bar reads from
1563+
// IMarkerService (unfiltered) while the panel reads from
1564+
// IMarkersView.filteredGroups which respects the activeFile toggle.
15681565
if (!MarkersBridgeFirstSuccessLogged && PushedTotal > 0) {
15691566
MarkersBridgeFirstSuccessLogged = true;
1570-
invoke("MountainIPCInvoke", {
1571-
method: "diagnostic:log",
1572-
params: [
1573-
"markers-bridge",
1574-
`first-push owner=${Owner} uris=${Changed.length} markers=${PushedTotal} firstUri=${FirstUri.slice(0, 200)} firstSeverity=${FirstSeverity ?? "?"} firstMsgLen=${FirstMessageLength}`,
1575-
],
1576-
}).catch(() => {});
1567+
1568+
// Read back from IMarkerService to confirm markers are stored.
1569+
const AllStored = (Markers.read as (...args: unknown[]) => unknown[])?.() ?? [];
1570+
1571+
// Check the Problems panel view filter state. `getViewWithId` returns
1572+
// the view regardless of whether it's currently focused; if the panel
1573+
// is open, the view instance exists. Clear `activeFile` if it was on
1574+
// so all markers are visible, not just the active file's.
1575+
try {
1576+
const ViewsSvc = (Services as any)?.Views;
1577+
const MarkersView = ViewsSvc?.getViewWithId?.(
1578+
"workbench.panel.markers.view",
1579+
) as any;
1580+
const FilterStats = MarkersView?.getFilterStats?.() as
1581+
| { total: number; filtered: number }
1582+
| undefined;
1583+
const ActiveFileWasOn =
1584+
MarkersView?.filters?.activeFile === true;
1585+
if (ActiveFileWasOn) {
1586+
MarkersView.filters.activeFile = false;
1587+
}
1588+
invoke("MountainIPCInvoke", {
1589+
method: "diagnostic:log",
1590+
params: [
1591+
"markers-bridge",
1592+
`first-push owner=${Owner} uris=${Changed.length} markers=${PushedTotal} stored=${AllStored.length} firstUri=${FirstUri.slice(0, 200)} firstSeverity=${FirstSeverity ?? "?"} firstMsgLen=${FirstMessageLength} filterTotal=${FilterStats?.total ?? "?"} filterFiltered=${FilterStats?.filtered ?? "?"} activeFileCleared=${ActiveFileWasOn}`,
1593+
],
1594+
}).catch(() => {});
1595+
} catch {
1596+
invoke("MountainIPCInvoke", {
1597+
method: "diagnostic:log",
1598+
params: [
1599+
"markers-bridge",
1600+
`first-push owner=${Owner} uris=${Changed.length} markers=${PushedTotal} stored=${AllStored.length} firstUri=${FirstUri.slice(0, 200)} firstSeverity=${FirstSeverity ?? "?"} firstMsgLen=${FirstMessageLength}`,
1601+
],
1602+
}).catch(() => {});
1603+
}
15771604
}
15781605
});
15791606

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/**
2+
* @module Bridge/InstallDiagnostics
3+
*
4+
* ---- Diagnostics → IMarkerService bridge ----
5+
*
6+
* Mountain emits `sky://diagnostics/changed` after each `Diagnostic.Set`
7+
* from Cocoon's `vscode.languages.createDiagnosticCollection().set(...)`.
8+
* Without a renderer-side consumer that pushes into the workbench's
9+
* `IMarkerService`, the data lands in Mountain's `DiagnosticsMap` but
10+
* the editor never paints red squiggles and the Problems panel stays
11+
* empty - every language extension's compile errors / lint warnings /
12+
* type errors are invisible.
13+
*
14+
* Payload shape (from `DiagnosticProvider.SetDiagnostics`): `{ owner,
15+
* changedURIs: [{ uri, markers }] }`. We translate per-URI marker
16+
* arrays into `IMarkerService.changeOne(owner, URI, markers)` calls.
17+
* `Markers.changeOne` REPLACES the marker set for that URI under the
18+
* given owner - matching VS Code's `MainThreadDiagnostics` behaviour
19+
* where each `set()` call overwrites the previous diagnostic state.
20+
*/
21+
22+
export default async (Dependencies: {
23+
Register: (
24+
Channel: string,
25+
Handler: (Payload: any) => void,
26+
) => Promise<void>;
27+
GetServices: () => {
28+
Markers?: {
29+
changeOne(
30+
owner: string,
31+
uri: unknown,
32+
markers: unknown[],
33+
): void;
34+
read(...args: unknown[]): unknown[];
35+
};
36+
URI?: {
37+
parse(value: string): unknown;
38+
from(components: object): unknown;
39+
};
40+
Views?: {
41+
getViewWithId?(id: string): unknown;
42+
};
43+
[key: string]: unknown;
44+
} | null;
45+
Invoke: (
46+
cmd: string,
47+
args?: Record<string, unknown>,
48+
) => Promise<unknown>;
49+
}): Promise<void> => {
50+
const { Register, GetServices, Invoke } = Dependencies;
51+
52+
let MarkersBridgeFirstSuccessLogged = false;
53+
54+
await Register("sky://diagnostics/changed", (Payload: any) => {
55+
const Services = GetServices();
56+
const Markers = (Services as any)?.Markers;
57+
const URICtor = (Services as any)?.URI;
58+
const Owner = String(Payload?.owner ?? "");
59+
const Changed = Array.isArray(Payload?.changedURIs)
60+
? Payload.changedURIs
61+
: [];
62+
if (!Markers?.changeOne || !URICtor) {
63+
Invoke("MountainIPCInvoke", {
64+
method: "diagnostic:log",
65+
params: [
66+
"markers-bridge",
67+
`owner=${Owner} uris=${Changed.length} pushable=false markers=${typeof Markers?.changeOne} uri=${!!URICtor}`,
68+
],
69+
}).catch(() => {});
70+
return;
71+
}
72+
let PushedTotal = 0;
73+
let FirstUri = "";
74+
let FirstSeverity: number | undefined;
75+
let FirstMessageLength = 0;
76+
for (const Entry of Changed) {
77+
try {
78+
const Uri = Entry?.uri;
79+
const Markers_ = Array.isArray(Entry?.markers)
80+
? Entry.markers
81+
: [];
82+
if (!Uri) continue;
83+
const RealUri =
84+
typeof Uri === "string"
85+
? URICtor.parse(Uri)
86+
: Uri && typeof (Uri as any).with === "function"
87+
? Uri
88+
: URICtor.from(Uri);
89+
Markers.changeOne(Owner, RealUri, Markers_);
90+
PushedTotal += Markers_.length;
91+
if (!FirstUri) {
92+
FirstUri =
93+
typeof Uri === "string"
94+
? Uri
95+
: typeof (RealUri as any)?.toString === "function"
96+
? (RealUri as any).toString()
97+
: "";
98+
if (Markers_[0]) {
99+
FirstSeverity = (Markers_[0] as any)?.severity;
100+
FirstMessageLength = String(
101+
(Markers_[0] as any)?.message ?? "",
102+
).length;
103+
}
104+
}
105+
} catch (Error) {
106+
// Swallow - one bad entry must not stop the rest.
107+
void Error;
108+
}
109+
}
110+
// One-time success-path confirmation. Also checks the Problems panel
111+
// view filter state and clears `activeFile` if it was left on, which
112+
// causes "count shows but panel empty" - the status bar reads from
113+
// IMarkerService (unfiltered) while the panel reads from
114+
// IMarkersView.filteredGroups which respects the activeFile toggle.
115+
if (!MarkersBridgeFirstSuccessLogged && PushedTotal > 0) {
116+
MarkersBridgeFirstSuccessLogged = true;
117+
118+
// Read back from IMarkerService to confirm markers are stored.
119+
const AllStored =
120+
(Markers.read as (...args: unknown[]) => unknown[])?.() ?? [];
121+
122+
// Check the Problems panel view filter state. `getViewWithId`
123+
// returns the view regardless of whether it's currently focused;
124+
// if the panel is open, the view instance exists. Clear
125+
// `activeFile` if it was on so all markers are visible, not just
126+
// the active file's.
127+
try {
128+
const ViewsSvc = (Services as any)?.Views;
129+
const MarkersView = ViewsSvc?.getViewWithId?.(
130+
"workbench.panel.markers.view",
131+
) as any;
132+
const FilterStats = MarkersView?.getFilterStats?.() as
133+
| { total: number; filtered: number }
134+
| undefined;
135+
const ActiveFileWasOn =
136+
MarkersView?.filters?.activeFile === true;
137+
if (ActiveFileWasOn) {
138+
MarkersView.filters.activeFile = false;
139+
}
140+
Invoke("MountainIPCInvoke", {
141+
method: "diagnostic:log",
142+
params: [
143+
"markers-bridge",
144+
`first-push owner=${Owner} uris=${Changed.length} markers=${PushedTotal} stored=${AllStored.length} firstUri=${FirstUri.slice(0, 200)} firstSeverity=${FirstSeverity ?? "?"} firstMsgLen=${FirstMessageLength} filterTotal=${FilterStats?.total ?? "?"} filterFiltered=${FilterStats?.filtered ?? "?"} activeFileCleared=${ActiveFileWasOn}`,
145+
],
146+
}).catch(() => {});
147+
} catch {
148+
Invoke("MountainIPCInvoke", {
149+
method: "diagnostic:log",
150+
params: [
151+
"markers-bridge",
152+
`first-push owner=${Owner} uris=${Changed.length} markers=${PushedTotal} stored=${AllStored.length} firstUri=${FirstUri.slice(0, 200)} firstSeverity=${FirstSeverity ?? "?"} firstMsgLen=${FirstMessageLength}`,
153+
],
154+
}).catch(() => {});
155+
}
156+
}
157+
});
158+
};

0 commit comments

Comments
 (0)