diff --git a/src/features/run/dotnetTypes.test.tsx b/src/features/run/dotnetTypes.test.tsx
index 93d5f16a9..aef05b425 100644
--- a/src/features/run/dotnetTypes.test.tsx
+++ b/src/features/run/dotnetTypes.test.tsx
@@ -6,7 +6,7 @@ import { useProject } from "@/platform/project";
import { dotnetBuildConfig, dotnetProjectType, dotnetTestType } from "./dotnetTypes";
import { ConfigForm } from "./ConfigForm";
import { validateConfig } from "./configs";
-import { newConfigOfType, registerBuiltinTypes, withParams } from "./types";
+import { formParams, newConfigOfType, registerBuiltinTypes, withParams } from "./types";
const resolveDotnet = vi.hoisted(() => vi.fn());
vi.mock("@/platform/ipc", async (importOriginal) => {
@@ -69,6 +69,36 @@ describe(".NET configurations", () => {
]);
});
+ it("opens a new configuration, and a typed dotnet run command, as the form with its project field", () => {
+ const fresh = newConfigOfType(dotnetProjectType, []);
+ expect(formParams(fresh, dotnetProjectType)).toEqual({ project: "" });
+ render(
+ ,
+ );
+ expect(screen.getByLabelText(".NET project")).toBeTruthy();
+ // #2733: typed into the command fields of a configuration without params.
+ const typed: RunConfig = {
+ ...fresh,
+ params: {},
+ command: "dotnet",
+ args: ["run", "--project", "Worker.Service", "--no-launch-profile", "--", "--verbose"],
+ };
+ expect(formParams(typed, dotnetProjectType)).toEqual({
+ project: "Worker.Service",
+ programArgs: "--verbose",
+ });
+ // Options the form has no field for keep the command editor.
+ expect(formParams({ ...typed, args: ["run", "--project", "A", "--no-build"] }, dotnetProjectType)).toBeNull();
+ expect(formParams({ ...typed, args: ["build", "A"] }, dotnetProjectType)).toBeNull();
+ });
+
it("builds edited project/framework from the open root instead of a profile cwd", () => {
useProject.setState({ project: { root: "/repo" } as never });
const c = withParams(
diff --git a/src/features/run/dotnetTypes.ts b/src/features/run/dotnetTypes.ts
index e9ca7a8c5..1eb0b93bb 100644
--- a/src/features/run/dotnetTypes.ts
+++ b/src/features/run/dotnetTypes.ts
@@ -1,7 +1,8 @@
import { call, commands, type RunConfig } from "@/platform/ipc";
import { useProject } from "@/platform/project";
import type { RunConfigTypeContribution } from "@/workbench/contributions";
-import { parseArgs } from "./configs";
+import { formatArgs, parseArgs } from "./configs";
+import { commandName } from "./commandName";
import { prepareDotnetTest } from "@/platform/dotnet";
function profile(p: Readonly>): {
@@ -28,6 +29,38 @@ export function dotnetCommand(p: Readonly>, test = false)
};
}
+/**
+ * The form fields of a `dotnet run` command line ([`dotnetCommand`] reversed), or `null` when it
+ * uses anything the form can't show. Lets a new configuration, and one typed as a command, open
+ * as the form with its project field.
+ */
+function dotnetRunParams(c: RunConfig): Record | null {
+ if (!/^dotnet(\.exe)?$/i.test(commandName(c.command))) return null;
+ const [verb, ...rest] = c.args;
+ if (verb !== "run") return null;
+ const p: Record = c.command === "dotnet" ? {} : { executable: c.command };
+ const flags: Record = {
+ "--project": "project",
+ "--framework": "framework",
+ "--configuration": "configuration",
+ "--launch-profile": "launchProfile",
+ };
+ for (let i = 0; i < rest.length; i++) {
+ const arg = rest[i]!;
+ if (arg === "--") {
+ p.programArgs = formatArgs(rest.slice(i + 1));
+ break;
+ }
+ if (arg === "--no-launch-profile") continue;
+ const key = flags[arg];
+ const value = rest[i + 1];
+ if (!key || value === undefined) return null;
+ p[key] = value;
+ i++;
+ }
+ return { project: "", ...p };
+}
+
/** Build the current project/framework, independently of the run's profile working directory. */
export function dotnetBuildConfig(config: RunConfig): RunConfig {
const p = config.params ?? {};
@@ -104,6 +137,7 @@ export const dotnetProjectType: RunConfigTypeContribution = {
},
],
commandLine: (p) => dotnetCommand(p),
+ paramsFrom: dotnetRunParams,
async prepareLaunch(config) {
const resolved = await call(commands.runDotnetResolve(config, false));
const p = resolved.params ?? {};