mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-14 13:22:37 +00:00
fix(desktop): find Zed installed as a Flatpak on Linux
Flatpak puts app launchers in its exports/bin directories but does not add them to PATH, so Zed installed from Flathub never appeared in the Open in editor list. Search the user and system Flatpak export directories after PATH on Linux, and look for Zed's Flathub launcher. Closes #5458
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import type { SpawnOptions } from "node:child_process";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { listAvailableEditorTargets, openEditorTarget } from "./registry.js";
|
||||
import { createEditorTargetRuntime } from "./runtime.js";
|
||||
import { zedTarget } from "./targets/zed.js";
|
||||
|
||||
interface SpawnRecord {
|
||||
command: string;
|
||||
@@ -10,6 +12,23 @@ interface SpawnRecord {
|
||||
unrefed: boolean;
|
||||
}
|
||||
|
||||
function recordSpawns(records: SpawnRecord[]) {
|
||||
return (command: string, args: string[], options: SpawnOptions) => {
|
||||
const record = { command, args, options, unrefed: false };
|
||||
records.push(record);
|
||||
const child = {
|
||||
once(event: "error" | "spawn", handler: (error?: Error) => void) {
|
||||
if (event === "spawn") queueMicrotask(() => handler());
|
||||
return child;
|
||||
},
|
||||
unref() {
|
||||
record.unrefed = true;
|
||||
},
|
||||
};
|
||||
return child;
|
||||
};
|
||||
}
|
||||
|
||||
describe("editor target runtime", () => {
|
||||
it("resolves command aliases and safely launches Windows command scripts", async () => {
|
||||
const records: SpawnRecord[] = [];
|
||||
@@ -20,20 +39,7 @@ describe("editor target runtime", () => {
|
||||
ELECTRON_RUN_AS_NODE: "1",
|
||||
},
|
||||
pathExists: (targetPath) => targetPath === "C:/Program Files/Editors & Tools/bin/code.cmd",
|
||||
spawn: (command, args, options) => {
|
||||
const record = { command, args, options, unrefed: false };
|
||||
records.push(record);
|
||||
const child = {
|
||||
once(event: "error" | "spawn", handler: (error?: Error) => void) {
|
||||
if (event === "spawn") queueMicrotask(() => handler());
|
||||
return child;
|
||||
},
|
||||
unref() {
|
||||
record.unrefed = true;
|
||||
},
|
||||
};
|
||||
return child;
|
||||
},
|
||||
spawn: recordSpawns(records),
|
||||
});
|
||||
|
||||
const command = runtime.resolveCommand(["missing", "code"]);
|
||||
@@ -57,4 +63,32 @@ describe("editor target runtime", () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["system", "/var/lib/flatpak/exports/bin/dev.zed.Zed"],
|
||||
["user", "/home/dev/.local/share/flatpak/exports/bin/dev.zed.Zed"],
|
||||
])("finds and opens Zed from a %s Flatpak install on Linux", async (_scope, flatpakExport) => {
|
||||
const records: SpawnRecord[] = [];
|
||||
const existingPaths = new Set([flatpakExport, "/repo", "/repo/src/app.ts"]);
|
||||
const runtime = createEditorTargetRuntime({
|
||||
platform: "linux",
|
||||
env: { PATH: "/usr/local/bin:/usr/bin:/bin" },
|
||||
homeDirectory: "/home/dev",
|
||||
pathExists: (targetPath) => existingPaths.has(targetPath),
|
||||
spawn: recordSpawns(records),
|
||||
loadIcon: async (fileName) => ({ kind: "image", dataUrl: fileName }),
|
||||
});
|
||||
|
||||
const targets = await listAvailableEditorTargets(runtime, [zedTarget]);
|
||||
expect(targets.map((target) => target.id)).toEqual(["zed"]);
|
||||
|
||||
await openEditorTarget(
|
||||
{ editorId: "zed", workspacePath: "/repo", filePath: "/repo/src/app.ts", line: 3 },
|
||||
runtime,
|
||||
[zedTarget],
|
||||
);
|
||||
expect(records.map(({ command, args }) => ({ command, args }))).toEqual([
|
||||
{ command: flatpakExport, args: ["/repo", "/repo/src/app.ts:3"] },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,10 +56,28 @@ function isAbsolutePath(value: string, platform: NodeJS.Platform): boolean {
|
||||
return platform === "win32" ? win32.isAbsolute(value) : posix.isAbsolute(value);
|
||||
}
|
||||
|
||||
// Flatpak exports app launchers here, but does not put these directories on PATH.
|
||||
function flatpakExportDirectories(env: NodeJS.ProcessEnv, homeDirectory: string): string[] {
|
||||
const userDataDirectory = env.XDG_DATA_HOME || `${homeDirectory}/.local/share`;
|
||||
return [`${userDataDirectory}/flatpak/exports/bin`, "/var/lib/flatpak/exports/bin"];
|
||||
}
|
||||
|
||||
function commandSearchDirectories(input: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
platform: NodeJS.Platform;
|
||||
homeDirectory: string;
|
||||
}): string[] {
|
||||
const pathValue = input.env.PATH ?? input.env.Path ?? input.env.path ?? "";
|
||||
const pathDelimiter = input.platform === "win32" ? ";" : ":";
|
||||
const directories = pathValue.split(pathDelimiter).filter(Boolean);
|
||||
if (input.platform !== "linux") return directories;
|
||||
return [...directories, ...flatpakExportDirectories(input.env, input.homeDirectory)];
|
||||
}
|
||||
|
||||
function resolveExecutable(
|
||||
commands: readonly string[],
|
||||
input: {
|
||||
env: NodeJS.ProcessEnv;
|
||||
searchDirectories: readonly string[];
|
||||
pathExists: (path: string) => boolean;
|
||||
platform: NodeJS.Platform;
|
||||
},
|
||||
@@ -69,10 +87,7 @@ function resolveExecutable(
|
||||
return command;
|
||||
}
|
||||
|
||||
const pathValue = input.env.PATH ?? input.env.Path ?? input.env.path ?? "";
|
||||
const pathDelimiter = input.platform === "win32" ? ";" : ":";
|
||||
for (const directory of pathValue.split(pathDelimiter)) {
|
||||
if (!directory) continue;
|
||||
for (const directory of input.searchDirectories) {
|
||||
const candidate = `${directory}/${command}`;
|
||||
if (input.platform !== "win32") {
|
||||
if (input.pathExists(candidate)) return candidate;
|
||||
@@ -140,7 +155,12 @@ export function createEditorTargetRuntime(
|
||||
env,
|
||||
pathExists,
|
||||
isAbsolutePath: (targetPath) => isAbsolutePath(targetPath, platform),
|
||||
resolveCommand: (commands) => resolveExecutable(commands, { env, pathExists, platform }),
|
||||
resolveCommand: (commands) =>
|
||||
resolveExecutable(commands, {
|
||||
searchDirectories: commandSearchDirectories({ env, platform, homeDirectory }),
|
||||
pathExists,
|
||||
platform,
|
||||
}),
|
||||
async spawnDetached({ command, args }) {
|
||||
const commandScript = isWindowsCommandScript(command, platform);
|
||||
const launchCommand = commandScript ? escapeWindowsCmdValue(command) : command;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { EditorTarget, EditorTargetLaunchInput } from "../target.js";
|
||||
|
||||
const COMMANDS = ["zed", "zeditor"] as const;
|
||||
const COMMANDS = ["zed", "zeditor", "dev.zed.Zed"] as const;
|
||||
|
||||
function location(input: EditorTargetLaunchInput): string {
|
||||
if (!input.line) return input.filePath!;
|
||||
|
||||
Reference in New Issue
Block a user