Файли
openidea/src/features/run/goActions.ts
T
l0ng-ai ac360b6143 feat(go): add core Go development workflows (#524)
Go settings, execution, debugging and test reporting were disconnected, leaving Go projects without editable build/test fields and workspace maintenance actions.

- Add project build constraints that update gopls in place, and notices on excluded files.
- Add editable Go Build and Go Test configurations with directory, package and file targets, separate tool and program arguments, modules, environment, output selection and build-only execution, and pass the same settings to Delve.
- Decode Go JSON test output into the results view, with subtests, failures, skips and rerun failed. Add gutter targets for benchmarks and generate directives.
- Add module environment and vendoring settings, read go.work members, and offer tidy, download, sync and use from file menus.
- Add file and package generate actions, and format and organize imports through gopls on Save and Reformat.
- Show what a Go program prints in the debug console: Delve's debuggee shares its stdio, which no DAP event carries.
- Keep the default build output out of version control, and keep configurations produced by an earlier run out of the gutter choices.

Fixes #507
Fixes #237
2026-10-31 13:56:06 +08:00

113 рядки
4.4 KiB
TypeScript

import { executeAction, getAction, registerActions } from "@/platform/actions";
import { useProject } from "@/platform/project";
import type { RunConfig } from "@/platform/ipc";
import { registerMenuGroup, maybeEditorService, type MenuGroupContext } from "@/workbench/contributions";
import { promptInput } from "@/workbench/inputDialogs";
import { goGenerateConfig, goGenerateMenu } from "./goGenerate";
import { invalidateGoModules, loadGoModules } from "./goModules";
import { loadConfigs, startRun, useRun } from "./store";
export type GoModuleOperation = "tidy" | "download" | "sync" | "use";
const ACTIONS = {
tidy: { id: "go.mod.tidy", title: "Go Mod Tidy" },
download: { id: "go.mod.download", title: "Download Go Modules" },
sync: { id: "go.work.sync", title: "Sync Go Workspace" },
use: { id: "go.work.use", title: "Add Directory to Go Workspace…" },
} as const;
export function goModuleCommand(operation: GoModuleOperation, path: string, directory?: string): RunConfig | null {
const module = operation === "tidy" || operation === "download";
if (!path.endsWith(module ? "/go.mod" : "/go.work") || path.includes("\0")) return null;
if (operation === "use" && (!directory?.trim() || directory.includes("\0"))) return null;
const args = [module ? "mod" : "work", operation, ...(operation === "use" ? ["--", directory!] : [])];
return {
id: `go:${operation}:${path}`,
name: `go ${args.slice(0, 2).join(" ")}`,
kind: "go",
command: "go",
args,
cwd: path.slice(0, path.lastIndexOf("/")),
env: {},
detected: true,
isTest: false,
debugAdapter: null,
};
}
export function goModuleMenu(ctx: MenuGroupContext) {
const path = ctx.paths.length === 1 ? ctx.paths[0]! : "";
const operations: GoModuleOperation[] = path.endsWith("/go.mod")
? ["tidy", "download"]
: path.endsWith("/go.work")
? ["sync", "use"]
: [];
return operations.map((op) => ({ kind: "action" as const, action: ACTIONS[op].id, arg: { path } }));
}
export function registerGoActions(): () => void {
const actions = registerActions([
...Object.entries(ACTIONS).map(([operation, spec]) => ({
...spec,
category: "Go",
when: "projectOpen",
run: async ({ arg }: { arg?: unknown }) => {
const params = arg && typeof arg === "object" ? (arg as { path?: string; directory?: string }) : {};
const path =
typeof arg === "string"
? arg
: typeof params.path === "string"
? params.path
: (maybeEditorService()?.activePath() ?? "");
let directory = typeof params.directory === "string" ? params.directory : undefined;
if (operation === "use" && !directory) {
directory =
(await promptInput({
title: "Add Directory to Go Workspace",
label: "Module directory",
initial: "./",
hint: "Path relative to go.work, or an absolute directory containing go.mod",
})) ?? undefined;
if (!directory) return;
}
const config = goModuleCommand(operation as GoModuleOperation, path, directory);
if (!config) return;
const root = useProject.getState().project?.root;
await startRun(config, undefined, undefined, {
onExit: () => {
if (useProject.getState().project?.root !== root) return;
invalidateGoModules();
void loadGoModules(root ?? null, useRun.getState().configs);
void loadConfigs();
if (getAction("libraries.reload")) void executeAction("libraries.reload");
},
});
},
})),
...(["file", "package"] as const).map((scope) => ({
id: `go.generate.${scope}`,
title: scope === "file" ? "Go Generate File" : "Go Generate Package",
category: "Go",
when: "projectOpen",
run: async ({ arg }: { arg?: unknown }) => {
const path =
typeof arg === "string"
? arg
: arg && typeof arg === "object" && "path" in arg && typeof arg.path === "string"
? arg.path
: (maybeEditorService()?.activePath() ?? "");
const config = goGenerateConfig(path, scope);
if (config) await startRun(config);
},
})),
]);
const menu = registerMenuGroup({
id: "go.modules",
menus: ["projectView", "editorTab", "editor"],
items: (_menu, ctx) => [...goModuleMenu(ctx), ...goGenerateMenu(ctx)],
});
return () => {
menu();
actions();
};
}