maxandClaude Fable 5.1 63de8a31f6 Per-project launch options, publish/test tasks, explorer badge, keybindings
- dotnetSolution.launch.projects: args, env, cwd, console and profile
  per project name, layered over the global settings and
  Properties/launchSettings.json. Applied to the debug button and,
  through resolveDebugConfiguration, to the launch.json entry too.
- publish and test task targets with the same configuration/platform.
- Problems panel opens when a task fails.
- The startup csproj and its folder are marked in the explorer.
- F5 / Ctrl+F5 / Ctrl+Shift+B map to debug/run/build until a launch.json
  entry exists; Ctrl+Alt+C and Ctrl+Alt+P open the pickers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169iPWwKHZoBTNN9qwXiwqk
2026-09-08 13:33:12 +02:00

.NET Solution Launcher

Startup project and solution configuration in the status bar, with build and debug that actually use them. Built for DotRush, next to the .NET Hot Reload extension in this repo.

$(project) MyGame.Editor   $(settings-gear) Test | x64   $(debug-alt)
  • The project item shows which .csproj is the startup project, and picks another one from the executables in the solution (Runtime is a library, so it is not offered).
  • The configuration item shows the solution configuration — Debug | x64, Test | x64, Release | Any CPU — exactly as MyGame.sln lists them.
  • The debug button builds with that configuration and launches the startup project.

Why, when DotRush already has a status bar item

DotRush shows Debug | net10.0. Two things are missing from that:

  1. The project name. It is in the tooltip only. With three executables in the solution (Builder, Editor, and whatever comes next) that is the thing you need to see.

  2. The platform. DotRush's build task runs dotnet build <project> -p:Configuration=X with no -p:Platform. MSBuild then defaults to AnyCPU, and every property group conditioned on '$(Configuration)|$(Platform)' == 'Debug|x64' is skipped. In this solution that is not cosmetic:

    MyGame.Runtime, Configuration=Debug DefineConstants
    without -p:Platform (what DotRush runs) TRACE;DEBUG
    with -p:Platform=x64 TRACE;LOG_INFO;PROFILING;DEBUG

    So logging and profiling silently vanish, Optimize is never set for Test/Release, and MyGame.Builder loses AllowUnsafeBlocks and fails to compile.

DotRush also cannot be told a configuration from outside — its selection lives in its own workspace state — so this extension owns the build rather than wrapping dotrush: Build. The startup project is synced with DotRush in both directions (dotnetSolution.syncDotRush), so its test explorer, launchSettings.json lookup and ${command:dotrush.activeProjectPath} all agree with what the status bar says.

What a build does

dotnetSolution.buildScope decides (default solution):

Scope Command Notes
solution dotnet build MyGame.sln -c Test -p:Platform=x64 Like Visual Studio's Build Solution. MSBuild maps every project through the .sln: MyGame.* build as Test|x64, MoonWorks as Debug|Any CPU, and the Editor → Builder ProjectDependencies entry is honoured, so the Builder exists before the Editor's post-build step needs it.
project dotnet build MyGame.Editor.csproj -c Test -p:Platform=x64 Only the startup project and its ProjectReferences. Faster, but solution-only dependencies are ignored.

Rebuild adds --no-incremental; Clean runs dotnet clean. Two more targets use the same selection:

Target Command Notes
publish dotnet publish MyGame.Editor.csproj -c Release -p:Platform=x64 [-r win-x64] Always the startup project. dotnetSolution.publish.runtime sets -r, which PublishAot needs; publish.args adds the rest
test dotnet test MyGame.sln -c Test -p:Platform=x64 Follows buildScope; dotnetSolution.test.args adds filters or --no-build

All five are tasks of type dotnet-solution, so they show up under Run Task and can be used as preLaunchTask. When one fails the Problems panel opens (dotnetSolution.showProblemsOnFailure).

What a launch does

program comes from MSBuild, not from guessing: the extension evaluates the startup project with the mapped configuration and platform (dotnet msbuild -getProperty:TargetPath …), which is the only way to get it right here — OutDir comes from Directory.Build.props and AppendTargetFrameworkToOutputPath is off. The result is cached until a project or solution file changes.

The apphost .exe is launched when the project produces one, since vsdbg wants an executable; otherwise it runs dotnet <TargetPath>. DotRush's debug configuration provider still runs after this one and fills in justMyCode and symbol options.

Arguments, environment, working directory

Builder and Editor want different arguments, so these are layered, most specific wins:

  1. dotnetSolution.launch.projects, keyed by project name:
    "dotnetSolution.launch.projects": {
        "MyGame.Builder": { "args": ["-build", "-resourcePath", "Resources"], "cwd": "../MyGame.Editor" },
        "MyGame.Editor":  { "console": "integratedTerminal" }
    }
    
    cwd is relative to the project folder. profile names a launchSettings.json profile.
  2. The global dotnetSolution.launch.args / .env / .cwd / .console.
  3. Properties/launchSettings.json next to the project: commandLineArgs, environmentVariables and workingDirectory of the first "commandName": "Project" profile, or the one named by dotnetSolution.launch.profile.
  4. Otherwise no arguments and TargetDir as the working directory.

The same layering applies to F5 through the launch.json entry: its args stay empty in the file and are filled in at launch time.

Usage

Action How
Pick the startup project Click the project name (Ctrl+Alt+P), or right-click a .csprojSet as Startup Project (.NET Solution). The explorer marks it with ▶
Pick the configuration Click Debug | x64 (Ctrl+Alt+C). Entries the solution does not build the project under are marked with $(warning)
Build / Rebuild / Clean .NET Solution: Build etc., or the dotnet-solution tasks. Ctrl+Shift+B until a launch.json entry exists, after that VS Code's default build task
Publish / Test .NET Solution: Publish Startup Project, .NET Solution: Run Tests
Debug The $(debug-alt) button, .NET Solution: Debug Startup Project, or F5
Run without debugging .NET Solution: Run Startup Project (without debugging), or Ctrl+F5
Several .sln files .NET Solution: Select Solution, or dotnetSolution.solution

F5

Without a launch.json entry, F5, Ctrl+F5 and Ctrl+Shift+B are bound to this extension's debug, run and build commands (only while a solution is loaded and no debug session is running). Once the entry below exists those keys go back to VS Code's own handling, which then uses the entry — same result, but editable in launch.json.

The first time it sees a solution the extension offers to write a launch.json entry. Later, run .NET Solution: Create launch.json and tasks.json entries from the command palette, or pick it at the bottom of the startup-project list (click the project name):

{
    "name": ".NET Solution: Debug startup project",
    "type": "coreclr",
    "request": "launch",
    "program": "${command:dotnetSolution.activeProgram}",
    "cwd": "${command:dotnetSolution.activeCwd}",
    "preLaunchTask": "dotnet-solution: Build"
}

The same entry is offered dynamically in the Run and Debug dropdown even without a launch.json. It replaces the DotRush template's ${command:dotrush.activeTargetPath} + preLaunchTask: "dotrush: Build" pair, which is the one that builds without a platform.

Variables for your own launch.json / tasks.json

${command:…} Value
dotnetSolution.activeProgram apphost .exe, or the .dll when there is none
dotnetSolution.activeTargetPath the built assembly
dotnetSolution.activeTargetDir its directory
dotnetSolution.activeCwd the working directory after the layering above
dotnetSolution.activeProjectPath, activeProjectName the startup project
dotnetSolution.activeSolutionPath the solution
dotnetSolution.activeConfiguration, activePlatform solution configuration, e.g. Test, Any CPU
dotnetSolution.activeProjectConfiguration, activeProjectPlatform what the project gets, e.g. Test, AnyCPU

Settings

Setting Default
dotnetSolution.solution "" Solution to use; empty picks the shallowest, then the largest
dotnetSolution.buildScope solution See above
dotnetSolution.targetFramework "" For TargetFrameworks projects; empty takes the first
dotnetSolution.launch.args / .env / .cwd / .console Passed to the launched application
dotnetSolution.launch.projects {} The same, per project name; wins over the global ones
dotnetSolution.launch.profile "" launchSettings.json profile to read
dotnetSolution.publish.runtime / .args "" / [] -r and extra arguments for publish
dotnetSolution.test.args [] Extra arguments for test
dotnetSolution.showProblemsOnFailure true Open Problems when a task fails
dotnetSolution.debugType coreclr
dotnetSolution.syncDotRush true Push the startup project to DotRush and follow its changes
dotnetSolution.syncDotRushWorkspaceProperties false Write Configuration=…;Platform=… into dotrush.roslyn.workspaceProperties so IntelliSense sees the same DefineConstants. Off because DotRush reloads its workspace on every change
dotnetSolution.additionalBuildArguments [] Appended to every build

dotrush.roslyn.dotnetSdkDirectory and dotrush.msbuild.additionalEnvironment are honoured, so both extensions use the same SDK.

Tips

  • DotRush's own Debug | net10.0 item can be hidden: right-click the status bar and untick DotRush. Nothing else depends on it once this extension drives the build.
  • The status bar goes to a spinner while a build runs; the build output is in the terminal and the Problems panel ($msCompile).
  • The .NET Solution output channel logs every dotnet invocation.

Development

npm install
npm run compile
npm run test:unit   # parser tests, against D:\Projects\MyGame when present
npm test            # launches VS Code on that folder (SOLUTION_TEST_FOLDER overrides)
S
Description
No description provided
Readme MIT
257 KiB
Languages
TypeScript 100%