feat(desktop): add Android Studio editor target (#3531)

This commit is contained in:
Caique Maurano
2026-08-19 14:14:13 +02:00
committed by GitHub
parent e12bdc2fc5
commit 97a6deaa39
3 changed files with 78 additions and 0 deletions
@@ -233,6 +233,41 @@ describe("editor target registry", () => {
]); ]);
}); });
it("lists and opens Android Studio through its bundled macOS launcher", async () => {
const runtime = new FakeEditorTargets("darwin", { HOME: "/Users/me" });
const bundledCommand = "/Users/me/Applications/Android Studio.app/Contents/MacOS/studio";
runtime.installCommand(bundledCommand, bundledCommand);
runtime.addPath("/repo");
runtime.addPath("/repo/app/src/main/MainActivity.kt");
const targets = await listAvailableEditorTargets(runtime);
expect(targets).toContainEqual({
id: "android-studio",
label: "Android Studio",
kind: "editor",
icon: { kind: "symbol", name: "terminal" },
});
await openEditorTarget(
{
editorId: "android-studio",
workspacePath: "/repo",
filePath: "/repo/app/src/main/MainActivity.kt",
line: 18,
column: 3,
},
runtime,
);
expect(runtime.launches).toEqual([
{
command: bundledCommand,
args: ["/repo", "--line", "18", "--column", "3", "/repo/app/src/main/MainActivity.kt"],
},
]);
});
it("detects Cursor's installed Windows command when it is absent from PATH", async () => { it("detects Cursor's installed Windows command when it is absent from PATH", async () => {
const runtime = new FakeEditorTargets("win32", { const runtime = new FakeEditorTargets("win32", {
LOCALAPPDATA: "C:/Users/me/AppData/Local", LOCALAPPDATA: "C:/Users/me/AppData/Local",
@@ -4,6 +4,7 @@ import type {
EditorTargetLaunchInput, EditorTargetLaunchInput,
EditorTargetRuntime, EditorTargetRuntime,
} from "./target.js"; } from "./target.js";
import { androidStudioTarget } from "./targets/android-studio.js";
import { antigravityTarget } from "./targets/antigravity.js"; import { antigravityTarget } from "./targets/antigravity.js";
import { aquaTarget } from "./targets/aqua.js"; import { aquaTarget } from "./targets/aqua.js";
import { clionTarget } from "./targets/clion.js"; import { clionTarget } from "./targets/clion.js";
@@ -35,6 +36,7 @@ export const EDITOR_TARGETS: readonly EditorTarget[] = [
vscodiumTarget, vscodiumTarget,
zedTarget, zedTarget,
antigravityTarget, antigravityTarget,
androidStudioTarget,
intellijIdeaTarget, intellijIdeaTarget,
aquaTarget, aquaTarget,
clionTarget, clionTarget,
@@ -0,0 +1,41 @@
import type { EditorTarget, EditorTargetLaunchInput, EditorTargetRuntime } from "../target.js";
function commands(runtime: EditorTargetRuntime): string[] {
const candidates = ["studio", "studio64"];
if (runtime.platform === "darwin") {
candidates.push("/Applications/Android Studio.app/Contents/MacOS/studio");
if (runtime.env.HOME) {
candidates.push(`${runtime.env.HOME}/Applications/Android Studio.app/Contents/MacOS/studio`);
}
}
return candidates;
}
function launchArgs(input: EditorTargetLaunchInput): string[] {
if (!input.filePath) return [input.workspacePath];
const args = [input.workspacePath];
if (input.line) args.push("--line", String(input.line));
if (input.column) args.push("--column", String(input.column));
args.push(input.filePath);
return args;
}
export const androidStudioTarget: EditorTarget = {
id: "android-studio",
async describe() {
return {
id: this.id,
label: "Android Studio",
kind: "editor",
icon: { kind: "symbol", name: "terminal" },
};
},
async isInstalled(runtime) {
return runtime.resolveCommand(commands(runtime)) !== null;
},
async launch(input, runtime) {
const command = runtime.resolveCommand(commands(runtime));
if (!command) throw new Error("Android Studio is not installed");
await runtime.spawnDetached({ command, args: launchArgs(input) });
},
};