Skip to content

feat: first-class WSL session support#65

Open
Bitblade wants to merge 21 commits into
umage-ai:mainfrom
Bitblade:feat/wsl-sessions
Open

feat: first-class WSL session support#65
Bitblade wants to merge 21 commits into
umage-ai:mainfrom
Bitblade:feat/wsl-sessions

Conversation

@Bitblade
Copy link
Copy Markdown

@Bitblade Bitblade commented May 17, 2026

Summary

Adds WSL as a third session kind alongside Local and SSH. The New Session
dialog gains a WSL radio that auto-detects installed distros via wsl -l -v,
lets the user pick a Linux working folder (with a Browse button rooted at
\\wsl$\<distro>\~), and optionally pins a -u <user>. The session is then
launched via wsl.exe -d <distro> [-u <user>] --cd <linux-path> -- bash -lc '<cmd>',
so anything you can normally run inside the distro — claude, codex, gh copilot, plain bash — Just Works.

Git status, repo-root detection, sibling-worktree listing, and git worktree add all work for WSL sessions: GitService transparently dispatches to
wsl.exe -d <distro> -- git -C <linuxPath> … when the working folder is a
\\wsl$\ UNC, translating arg-side and stdout-side paths so the rest of the
app keeps seeing Windows-shaped paths.

Why

WSL is increasingly where Claude Code and other agent CLIs live for Windows
users (Linux toolchain, native filesystem speed, easier shell environments).
Before this PR the only way to drive WSL through CodeShellManager was to add
wsl.exe as a custom launch command — no distro picker, no working-folder
field, no git status, no Open in Explorer / right-click → New worktree, no
sleep/wake of a distinct-distro session, etc.

Implementation notes

  • SessionKind enum on ShellSession: Local | Ssh | Wsl. The legacy
    IsRemote bool stays as a back-compat shim — its setter promotes
    Local → Ssh so old state.json files that only carried \"IsRemote\": true
    migrate cleanly without any custom converter. IsWsl added for symmetry.
  • WSL sessions store WorkingFolder as a \\wsl$\<distro>\… UNC so things
    that touch a Windows-shaped path (Explorer, the dormant sidebar item, the
    active-pane subtitle) need no special-casing. The Linux-side path lives on
    WslWorkingFolder and is what gets passed to wsl.exe --cd.
  • GitService routing detects the WSL UNC in its single RunGitFullAsync
    funnel — every higher-level method (status, branch, rev-parse, worktree
    list/add) inherits the routing for free. Two translators handle path
    shape:
    • TranslateUncArgsToLinux: \\wsl$\<distro>\foo → /foo in arg strings,
      only when the distro matches (other-distro UNCs pass through unchanged).
    • TranslateLinuxPathsToUnc: absolute Linux paths in stdout → UNC, with a
      conservative regex that only fires at word boundaries so things like
      refs/heads/main aren't mangled.
  • InheritSessionKindFrom consolidates the "clone a session derived from a
    parent" logic used by Duplicate, sibling-worktree, and the new-worktree
    dialog — previously three near-identical inline copies that would have
    drifted as soon as a fourth kind landed.

Test plan

  • New WSL session: + → WSL → pick distro → Browse opens at
    \\wsl$\<distro>\<home> → pick a subfolder → Start. Confirm prompt
    lands inside WSL (whoami/pwd look correct).
  • Git status: open a WSL session pointed at a repo. Sidebar should
    show the branch and dirty state. Run git status inside the session
    and the dirty dot should match.
  • Distro picker / user: switch distros in the combo — name suggestion
    updates. Type a -u user — picker re-seeds at that user's home.
  • Right-click on a WSL session → New session here: dialog should open
    in WSL mode pre-filled with the parent's distro/user/folder, not Local
    mode with the UNC.
  • Right-click → Open WSL console here: spawns a bare bash session
    inside the same distro+folder.
  • Right-click → New worktree from this branch: dialog opens (no
    "not a git repo"), worktree is created via wsl.exe -- git worktree add, the new session opens as WSL (not PS at the UNC) and runs the
    parent's command (claude/etc.) successfully.
  • Sleep/wake a WSL session: should persist distro/user/folder through
    state.json and relaunch identically.
  • Legacy state.json migration: load an old state.json with
    \"IsRemote\": true SSH sessions — should still deserialize as SSH
    (covered by ShellSessionMigrationTests).
  • WSL-less host: WSL radio still appears; the distro combo is empty
    with an inline "No WSL distros found…" hint.
  • 252 unit tests pass locally.

Screenshots

(I can add screenshots of the WSL panel + browse picker if helpful — happy
to follow up.)

Bitblade added 10 commits May 17, 2026 14:06
Replaces the binary IsRemote flag with a three-valued SessionKind enum so a
WSL session can live alongside Local and SSH ones — picking a distro from a
combobox (auto-detected via `wsl -l -v`), pointing at a Linux working folder,
and optionally pinning a -u user. wsl.exe is the launch process; PTY plumbing
is unchanged.

Legacy state.json that only carried IsRemote still deserializes cleanly: the
IsRemote setter promotes Kind from Local to Ssh, so older files migrate on
first load without bespoke conversion code.

WSL sessions store their WorkingFolder as a `\\wsl$\<distro>\...` UNC view of
the Linux path so Git for Windows, the "Open in Explorer" menu, and the
sidebar's git-branch poll all keep working unmodified — git status, dirty
state, and repo-root-based accent colors light up the same as for Local
sessions.

Run-commands (the F5 / chips strip) now also dispatch correctly inside WSL by
wrapping the command in `wsl.exe -d ... -- bash -lc '<escaped>'`.

The three IsRemote display-label / accent-key ternaries that were drifting
across MainWindow + SessionViewModel are consolidated onto three small
helpers on ShellSession (DefaultDisplayName / FolderShort / AccentKey) so
adding a fourth session kind in future doesn't require chasing call sites.
Mirrors the local Browse… button on the WSL panel. Opens FolderBrowserDialog
rooted at \\wsl$\<selected-distro> (the WSL filesystem appears there as a
native UNC share); on result, parses the picked path back to a Linux path
and, if the user drilled into a different distro than the combo had, updates
the combo too.

ParseWslUncPath is extracted as an internal static so it can be covered
headlessly via InternalsVisibleTo — accepts both the \\wsl$\ and the newer
\\wsl.localhost\ prefixes, plus forward-slash variants.
Symmetry with IsRemote (which remains the SSH predicate). Keeps the
"remote = ssh" convention intact — just gives WSL its own one-liner so
call sites don't have to spell out `Kind == SessionKind.Wsl`.
Git for Windows can't reliably operate on \\wsl$\<distro>\... paths —
the dubious-ownership check refuses to run, and "not a git repo" pops
on perfectly valid repos. Detecting the WSL UNC in the GitService funnel
and dispatching to `wsl.exe -d <distro> -- git -C <linuxPath> <args>`
sidesteps both. Two small translators make the seam invisible:

  TranslateUncArgsToLinux: rewrites \\wsl$\<distro>\foo tokens in the
    arg string to /foo before invocation, so callers can keep passing
    Windows-shaped paths (e.g. `worktree add <unc-target>`).

  TranslateLinuxPathsToUnc: walks git stdout for absolute Linux paths
    (rev-parse --git-common-dir, worktree list --porcelain) and rewrites
    them back to UNC, so the rest of the app sees uniform paths.

Both translators are conservative about what they touch — `refs/heads/foo`
and `M README.md` are passed through untouched per tests.
Three improvements bundled because they're all about the picker landing
somewhere useful:

- Browse seed = the user's home inside the distro, resolved via
  `wsl -d <distro> [-u <user>] -- sh -c "cd ~ && pwd"` and cached
  per (distro, user) so repeated clicks don't re-shell.
- Also set FolderBrowserDialog.InitialDirectory in addition to
  SelectedPath. SelectedPath alone left the COM dialog rooted at the
  user's last folder (typically Documents) and only pre-typed the UNC
  in the entry field — clicking Browse felt broken.
- The WSL panel now has a visible "User (optional)" column header.
  Previously the user textbox was identifiable only by tooltip, which
  read as an empty unlabeled box.
The early-return guard "if NameBox not empty, leave alone" was too greedy:
once we auto-filled the name from the first context, it preserved that
stale value forever — switching distros wouldn't update the suggestion.

Track our own last auto-fill so the guard fires only on truly-user-edited
content. Empty box and "still shows our previous suggestion" are both
treated as free-to-overwrite; anything else is preserved as user input.
…sion

Right-click → "New session here" on a WSL session used to open the dialog
in Local mode with the parent's `\\wsl$\…` UNC pre-filled into the
working-folder textbox — a layer-cake of subtle wrongness.

Now the dialog auto-selects the WSL radio, pre-fills user and Linux
working folder from the parent in the constructor, and remembers the
parent's distro so PopulateWslDistrosAsync can mark the right combo
entry as selected once the async distro list lands.
PowerShell-here on a WSL parent still opens a Windows shell (PS handles
the UNC path well enough as cwd, so it works), but the natural shell to
ask for from a WSL session is bash inside the same distro. Add a sibling
menu item that creates a fresh WSL session pointed at the parent's
distro / user / Linux folder. Only shown when the parent IsWsl.
A WSL session whose user picked "New worktree from this branch" got a
fresh session at the UNC path of the new worktree — but as a Local kind
running PowerShell, so the parent's command (claude, codex, etc.) failed
with "not recognized" inside a PS prompt at \\wsl$\<distro>\....

Same fix applies to "New session in sibling worktree" and the sibling-
worktree checkbox in the New Session dialog: all three created child
sessions without copying Kind/SSH/WSL fields, so a WSL parent quietly
demoted its children to Local.

Centralized in InheritSessionKindFrom, which also derives the child's
WslWorkingFolder from its WorkingFolder UNC (the worktree path the
caller built). DuplicateSessionAsync refactored to use the same helper
— its inline copy logic was about to drift.
…starts clean

Passing the seed UNC to both InitialDirectory and SelectedPath made the
COM file dialog show the raw UNC in the bottom "Folder:" textbox — which
Windows then renders as a truncated, slash-flipped tail (e.g.
"bu/home/bitblade" for \\wsl$\Ubuntu\home\bitblade). InitialDirectory
alone navigates the tree to the right place; SelectedPath was only
making the visible textbox look broken.
Copilot AI review requested due to automatic review settings May 17, 2026 12:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class WSL session support across the app (new session creation, launching, git integration, and session derivation flows), alongside existing Local and SSH sessions.

Changes:

  • Introduces SessionKind (Local | Ssh | Wsl) on ShellSession, retaining IsRemote as a state.json back-compat shim and adding WSL-specific fields (distro/user/linux working folder).
  • Extends the New Session dialog UI/logic to support selecting a WSL distro, optional user, and Linux working directory (with UNC-based browsing).
  • Routes Git operations for \\wsl$\... working folders through wsl.exe with path translation so existing git-dependent features work for WSL sessions.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
tests/CodeShellManager.Tests/WslDiscoveryServiceTests.cs Adds unit coverage for parsing wsl -l -v output and UNC conversion helpers.
tests/CodeShellManager.Tests/ShellSessionTests.cs Adds tests for SessionKind, IsRemote migration behavior, and WSL arg building/display helpers.
tests/CodeShellManager.Tests/ShellSessionMigrationTests.cs Adds explicit state.json migration tests for legacy IsRemote and new Kind serialization.
tests/CodeShellManager.Tests/RunInstanceTests.cs Adds tests for WSL run-command arg building and quote escaping.
tests/CodeShellManager.Tests/NewSessionDialogTests.cs Adds headless tests for WSL UNC parsing helper(s) in the dialog code-behind.
tests/CodeShellManager.Tests/GitServiceWslRoutingTests.cs Adds tests for WSL UNC detection and argument/stdout path translation in GitService.
src/CodeShellManager/Views/NewSessionDialog.xaml.cs Implements WSL mode UI behavior: distro discovery, browse-seeding, UNC parsing, and output fields.
src/CodeShellManager/Views/NewSessionDialog.xaml Adds WSL radio + WSL configuration panel controls.
src/CodeShellManager/ViewModels/SessionViewModel.cs Updates display/accent/git-refresh logic to use SessionKind and centralized display helpers.
src/CodeShellManager/Services/WslDiscoveryService.cs Adds discovery of installed WSL distros and home resolution helpers.
src/CodeShellManager/Services/RunInstance.cs Enables “Run” commands to execute inside WSL sessions via wsl.exe ... bash -lc.
src/CodeShellManager/Services/GitService.cs Adds WSL routing through wsl.exe when working directory is a WSL UNC path, with translation helpers.
src/CodeShellManager/Models/ShellSession.cs Adds SessionKind + WSL fields, WSL arg building, and centralized display/accent helpers.
src/CodeShellManager/MainWindow.xaml.cs Wires dialog output to session creation, launching WSL sessions, and kind inheritance for derived sessions.
README.md Documents the new WSL session feature at a high level.
Comments suppressed due to low confidence (1)

src/CodeShellManager/MainWindow.xaml.cs:690

  • InheritSessionKindFrom covers Duplicate/worktree-derived sessions, but session-history reopen uses RecentlyClosedEntry which currently only persists IsRemote + SSH fields (no Kind/WslDistro/WslUser/WslWorkingFolder). As a result, reopening a closed WSL session will likely come back as Local and try to run the command on the UNC path. Consider extending RecentlyClosedEntry + the reopen path to persist/restore Kind and WSL fields (or reusing this helper there).
    /// <summary>
    /// Propagates a parent session's <see cref="Models.SessionKind"/> and kind-specific
    /// fields (SSH host/user/port, WSL distro/user) onto a freshly-created child
    /// session. For WSL children it also derives <c>WslWorkingFolder</c> from the
    /// child's <c>WorkingFolder</c>, which the worktree code paths set to a
    /// <c>\\wsl$\&lt;distro&gt;\…</c> UNC. Without this step a new session spawned
    /// from a WSL parent (Duplicate, sibling worktree, new worktree) silently falls
    /// back to <see cref="Models.SessionKind.Local"/> and tries to run the parent's
    /// command (e.g. <c>claude</c>) inside a Windows PowerShell at the UNC path.
    /// </summary>
    private static void InheritSessionKindFrom(Models.ShellSession target, Models.ShellSession source)
    {
        target.Kind = source.Kind;
        if (source.Kind == Models.SessionKind.Ssh)
        {
            target.SshUser = source.SshUser;
            target.SshHost = source.SshHost;
            target.SshPort = source.SshPort;
            target.SshRemoteFolder = source.SshRemoteFolder;
            return;
        }
        if (source.Kind == Models.SessionKind.Wsl)
        {
            target.WslDistro = source.WslDistro;
            target.WslUser = source.WslUser;


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/CodeShellManager/Services/WslDiscoveryService.cs Outdated
Comment thread src/CodeShellManager/Models/ShellSession.cs Outdated
Comment thread src/CodeShellManager/Services/RunInstance.cs
Comment thread src/CodeShellManager/Services/GitService.cs
Comment thread src/CodeShellManager/Services/GitService.cs Outdated
Comment thread src/CodeShellManager/Views/NewSessionDialog.xaml.cs Outdated
Comment thread src/CodeShellManager/MainWindow.xaml.cs
Comment thread src/CodeShellManager/ViewModels/SessionViewModel.cs Outdated
Comment thread src/CodeShellManager/MainWindow.xaml.cs Outdated
Bitblade added 8 commits May 17, 2026 15:18
…dlock

GetDistrosAsync correctly drains both streams; GetDistroHomeAsync didn't.
If wsl.exe writes enough to stderr (transient init notices, a stopped
distro error) the child would block on its stderr buffer and the stdout
await would never complete — silently falling through to the 3s timeout
on every Browse click for WSL sessions.

Per Copilot review.
The comment claimed Git for Windows handles WSL UNCs natively — but the
preceding GitService routing through wsl.exe exists precisely because
it doesn't. Comment now reflects the actual dispatch.

Per Copilot review.
The previous guard "Kind != Local → return" was too conservative —
RunCommandTemplatesService.SeedFor calls Directory.EnumerateFiles, which
works fine on `\\wsl$\<distro>\…` UNCs, and RunInstance already runs WSL
sessions' commands via `wsl.exe -- bash -lc`. So a WSL project with a
package.json or Cargo.toml at its root now gets its templates the same
way a Local one does. Only SSH stays opted out.

Per Copilot review.
… WSL

RecentlyClosedEntry only captured IsRemote + SSH fields. Closing a WSL
session and reopening it via Ctrl+Shift+T (or the Recently Closed list
in the New Session dialog) resurrected it as Local at the `\\wsl$\…`
UNC path — same failure mode Copilot called out for the worktree path,
just on a different code route.

- Add Kind / WslDistro / WslUser / WslWorkingFolder to the entry record;
  FromSession copies them.
- Mirror the IsRemote→Ssh migration shim on RecentlyClosedEntry too, so
  state.json files written before this commit (no Kind key) still render
  the right subtitle and reopen as SSH.
- ReopenClosedSessionAsync copies Kind first (so a WSL entry doesn't get
  demoted by the IsRemote shim) and the WSL fields second.
- Subtitle now keys on Kind so a WSL entry shows `<distro>: <path>`.

Per Copilot review.
The unquoted regex stops at whitespace, so a quoted UNC like
"\\wsl\$\Ubuntu\home\alice\my repo" (the shape `worktree add <target>`
would produce for a Linux path with a space) used to be half-translated
and yield a broken git command.

Add a two-pass approach: first replace quoted runs (consuming the
content up to the closing quote and re-quoting the Linux output),
then fall back to the existing unquoted pass for arguments that
never needed quoting in the first place.

Per Copilot review.
ShellSession.BuildWslArgs, RunInstance.BuildWslArgs, and GitService's
RunGitInWslAsync concatenated WslDistro/WslUser/WslWorkingFolder
straight into a single argument string. Most distro names are
space-free in practice, but Linux working folders genuinely can have
spaces (`/home/alice/my proj`) and `wsl --cd` then sees two arguments.

Add a conservative QuoteForCmd helper on ShellSession (internal, so
tests reach it via InternalsVisibleTo): leaves space-free values
alone so existing call sites and tests don't churn, and double-quotes
anything that needs it (with embedded `"` escaped as `\"`).

Not migrating to ProcessStartInfo.ArgumentList — PseudoTerminal's API
takes a single command-line string, and reshaping it is out of scope
for this PR. The quoting helper is the surgical fix.

Per Copilot review.
If the user navigated out of `\\wsl$\` (e.g. into `C:\Users\…`) and
picked there, we silently stuffed the Windows path into
WslWorkingFolderBox — `wsl.exe --cd C:\…` then failed at session start
with a confusing message. Show a clear MessageBox naming the picked
path and explaining which folders are valid, and leave the textbox
unchanged so the user can try again.

Per Copilot review.
If the user picked a WSL distro but left the Linux Working Folder
empty, the launcher omitted `--cd` (so the shell correctly landed in
\$HOME) but ToUncPath produced `\\wsl\$\<distro>` — the distro root.
GitService then keyed on that UNC, asked git for status at "/", and
came back empty even when \$HOME was a real repo. Sidebar branch info
silently disappeared.

Make Start_Click async and call GetDistroHomeAsync (the cached lookup
we already use for the Browse picker) when WslWorkingFolder is empty,
so the session's WorkingFolder UNC and its Linux path stay aligned.
Best-effort: if WSL is unreachable, fall through to the existing
behavior (land in \$HOME, no git info).

Per Copilot review.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

src/CodeShellManager/Services/WslDiscoveryService.cs:109

  • Parse() splits each row on spaces, which breaks if a WSL distro name contains spaces (e.g. an imported distro named "My Distro"). In that case name/state/version tokens shift and the picker + downstream -d <distro> invocations will be wrong. Consider parsing by fixed columns (based on header offsets) or by taking the last 2 columns as STATE/VERSION and treating the remainder as the name (after optional leading *).
            // Header row: "  NAME                   STATE           VERSION".
            // Detect by the presence of the literal "NAME" token and skip.
            if (line.TrimStart().StartsWith("NAME", StringComparison.Ordinal)) continue;

            var tokens = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (tokens.Length < 2) continue;

            bool isDefault = tokens[0] == "*";
            int idx = isDefault ? 1 : 0;
            if (tokens.Length - idx < 1) continue;

            string name = tokens[idx];
            string state = tokens.Length - idx >= 2 ? tokens[idx + 1] : "";
            int version = 0;
            if (tokens.Length - idx >= 3) int.TryParse(tokens[idx + 2], out version);

            results.Add(new WslDistro(name, version, isDefault, state));

src/CodeShellManager/Services/WslDiscoveryService.cs:140

  • GetDistroHomeAsync builds wsl.exe args via string interpolation (-d {distro} -u {user}) without quoting. This will fail for distro/user values containing spaces and also makes argument boundaries ambiguous. Use the same quoting helper as the other wsl.exe call sites (e.g. ShellSession.QuoteForCmd) or switch to ProcessStartInfo.ArgumentList to avoid manual quoting entirely.
        try
        {
            string args = $"-d {distro}";
            if (!string.IsNullOrEmpty(normalizedUser)) args += $" -u {normalizedUser}";
            args += " -- sh -c \"cd ~ && pwd\"";

Comment thread src/CodeShellManager/Services/WslDiscoveryService.cs
Bitblade added 3 commits May 17, 2026 15:37
GetDistrosAsync and GetDistroHomeAsync caught Win32Exception and
FileNotFoundException only, but Process.Start can throw
InvalidOperationException / PlatformNotSupportedException and the
read pipeline can throw IOException — so the doc claim "Never throws —
every failure mode collapses to an empty list" wasn't quite accurate.
A stray exception here crashes the New Session dialog's Loaded
handler, which is a worse outcome than "no WSL distros listed."

Broaden the outer catch to Exception and document why.

Per Copilot review (round 2).
The token splitter took tokens[idx] as the name, so a `wsl --import "My
Distro" …` row was tokenized as name="My", state="Distro", version=0
(the actual "Running"/"Stopped" text isn't a digit, so int.TryParse
silently failed). The picker would then list a phantom "My" distro and
`wsl -d My` would error at session start.

Switch to consuming from the trailing end of the line: VERSION is
always the last token, STATE the second-to-last, and everything in
between (after an optional leading `*` for the default-distro marker)
is the name joined by spaces.

Per Copilot review (round 2, suppressed comment).
Last unquoted wsl.exe-arg interpolation we missed in the earlier sweep.
With Parse now accepting space-containing distro names, the launcher
side has to be ready to receive one — without quoting, a "My Distro"
distro would arrive as `-d My Distro` (two args to wsl.exe) and the
home-resolution would silently fail.

Per Copilot review (round 2, suppressed comment).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Comment on lines +273 to +287
/// <summary>
/// Builds wsl.exe args for a run executed inside the parent's WSL distro. Pattern:
/// -d &lt;distro&gt; [-u &lt;user&gt;] [--cd &lt;folder&gt;] -- bash -lc '&lt;escaped&gt;'
/// </summary>
internal static string BuildWslArgs(ShellSession parent, string commandLine)
{
var sb = new StringBuilder();
sb.Append($"-d {ShellSession.QuoteForCmd(parent.WslDistro)}");
if (!string.IsNullOrWhiteSpace(parent.WslUser))
sb.Append($" -u {ShellSession.QuoteForCmd(parent.WslUser)}");
if (!string.IsNullOrWhiteSpace(parent.WslWorkingFolder))
sb.Append($" --cd {ShellSession.QuoteForCmd(parent.WslWorkingFolder)}");
sb.Append(" -- bash -lc ");
sb.Append(SingleQuoteEscape(commandLine));
return sb.ToString();
Comment on lines +304 to +318
/// <summary>
/// Replaces absolute Linux paths in <paramref name="text"/> (typically git stdout)
/// with <c>\\wsl$\&lt;distro&gt;\…</c> equivalents so callers see Windows-shaped
/// paths. Conservative — only matches tokens at start-of-line or after whitespace
/// to avoid mangling text that happens to contain a slash.
/// </summary>
internal static string TranslateLinuxPathsToUnc(string text, string distro)
{
if (string.IsNullOrEmpty(text)) return text;
return Regex.Replace(text, @"(^|[\s=:])(/[^\s'""<>|]+)", m =>
{
string linuxPath = m.Groups[2].Value;
string unc = $@"\\wsl$\{distro}" + linuxPath.Replace('/', '\\');
return m.Groups[1].Value + unc;
}, RegexOptions.Multiline);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants