Skip to content

Commit 18f9fb7

Browse files
fix(Sky): Add BuildOpenArg helper for real URI instances in open commands
The previous manual POJO construction `{ $mid: 1, path, scheme }` for vscode.open and similar commands fails when in-process consumers (command palette, quick pick) call `uri.with()` or `uri.toString()` directly without going through `revive` - the same root cause as the search results dedup bug fixed in the previous commit. Add `BuildOpenArg` function that accepts string, URI instance, UriComponents, or workspace-folder shape and produces a real URI when `GetServices()?.URI` is available. Falls back to POJO with `$mid: 1` for code paths that DO call `revive`. Updates two call sites in SkyBridge: OpenFile and the other vscode.open invocation to use the helper instead of manual object construction.
1 parent a2c0d3c commit 18f9fb7

1 file changed

Lines changed: 102 additions & 21 deletions

File tree

Source/Function/SkyBridge.ts

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,106 @@ function GetServices(): CelServices | null {
236236
return (window as any).__CEL_SERVICES__ ?? null;
237237
}
238238

239+
// ============================================================================
240+
// URI helpers for command arguments
241+
// ============================================================================
242+
//
243+
// Stock VS Code commands like `vscode.open` accept either a real `URI`
244+
// instance OR a `URIComponents` POJO with `$mid:1` (the workbench's
245+
// `URI.revive(...)` lifts those at the boundary). We prefer the real
246+
// class when available because:
247+
//
248+
// 1. In-process consumers (search dedup Map, command palette quick
249+
// pick) call `uri.with(...)` / `uri.toString()` directly without
250+
// going through `revive` - same root cause as the search-provider
251+
// `uri.with is not a function` bug.
252+
// 2. Round-tripping a real URI through serialisation is loss-free,
253+
// while a POJO has to be rebuilt by every consumer.
254+
//
255+
// `BuildOpenArg` accepts whatever the caller gives us (string,
256+
// pre-built URI instance, plain UriComponents, or a workspace-folder
257+
// shape with `.uri` nested) and produces a real URI when the bundled
258+
// class is available. Fallback POJO retains `$mid:1` so the few code
259+
// paths that DO call `revive` still work.
260+
function BuildOpenArg(Source: unknown): unknown {
261+
const Ctor = GetServices()?.URI;
262+
const ExtractParts = (
263+
Value: unknown,
264+
): {
265+
Scheme: string;
266+
Authority: string;
267+
Path: string;
268+
Query: string;
269+
Fragment: string;
270+
} | null => {
271+
if (Value == null) return null;
272+
if (typeof Value === "string") {
273+
const Trimmed = Value.trim();
274+
if (!Trimmed) return null;
275+
if (Trimmed.includes("://")) {
276+
try {
277+
const Parsed = new URL(Trimmed);
278+
return {
279+
Scheme: Parsed.protocol.replace(/:$/, ""),
280+
Authority: Parsed.host,
281+
Path: decodeURIComponent(Parsed.pathname),
282+
Query: Parsed.search.replace(/^\?/, ""),
283+
Fragment: Parsed.hash.replace(/^#/, ""),
284+
};
285+
} catch {
286+
return null;
287+
}
288+
}
289+
return {
290+
Scheme: "file",
291+
Authority: "",
292+
Path: Trimmed,
293+
Query: "",
294+
Fragment: "",
295+
};
296+
}
297+
if (typeof Value !== "object") return null;
298+
const Holder = Value as Record<string, unknown>;
299+
// Workspace-folder-style nested shape.
300+
if (Holder["uri"] && typeof Holder["uri"] === "object") {
301+
return ExtractParts(Holder["uri"]);
302+
}
303+
const Scheme = String(Holder["scheme"] ?? "file");
304+
const Path = String(Holder["path"] ?? Holder["fsPath"] ?? "");
305+
if (!Path) return null;
306+
return {
307+
Scheme,
308+
Authority: String(Holder["authority"] ?? ""),
309+
Path,
310+
Query: String(Holder["query"] ?? ""),
311+
Fragment: String(Holder["fragment"] ?? ""),
312+
};
313+
};
314+
const Parts = ExtractParts(Source);
315+
if (!Parts) return Source;
316+
if (Ctor) {
317+
try {
318+
return Ctor.from({
319+
scheme: Parts.Scheme,
320+
authority: Parts.Authority,
321+
path: Parts.Path,
322+
query: Parts.Query,
323+
fragment: Parts.Fragment,
324+
});
325+
} catch {
326+
// Fall through to POJO.
327+
}
328+
}
329+
return {
330+
$mid: 1,
331+
scheme: Parts.Scheme,
332+
authority: Parts.Authority,
333+
path: Parts.Path,
334+
query: Parts.Query,
335+
fragment: Parts.Fragment,
336+
};
337+
}
338+
239339
// ============================================================================
240340
// Output channel state (local mirror of Mountain's channel registry)
241341
// ============================================================================
@@ -436,15 +536,7 @@ export async function InstallSkyBridge(): Promise<void> {
436536
const Wb = GetWorkbench();
437537
if (!Wb) return;
438538
Wb.commands
439-
.executeCommand(
440-
"vscode.open",
441-
{
442-
$mid: 1,
443-
path: uri,
444-
scheme: uri.startsWith("file://") ? "file" : "untitled",
445-
},
446-
viewColumn,
447-
)
539+
.executeCommand("vscode.open", BuildOpenArg(uri), viewColumn)
448540
.catch(() => {
449541
// Fallback: generic open
450542
Wb.env.openUri({ path: uri }).catch(() => {});
@@ -506,18 +598,7 @@ export async function InstallSkyBridge(): Promise<void> {
506598
if (Wb && UriValue) {
507599
await Wb.commands.executeCommand(
508600
"vscode.open",
509-
{
510-
$mid: 1,
511-
path: typeof UriValue === "string" ? UriValue : UriValue?.path,
512-
scheme:
513-
(typeof UriValue === "string"
514-
? UriValue
515-
: (UriValue?.scheme ?? "")
516-
).startsWith?.("file://") ||
517-
UriValue?.scheme === "file"
518-
? "file"
519-
: "untitled",
520-
},
601+
BuildOpenArg(UriValue),
521602
ViewColumn,
522603
);
523604
}

0 commit comments

Comments
 (0)