diff --git a/tabby-core/src/utils.ts b/tabby-core/src/utils.ts index ba67ecad3..bcbce7e6b 100644 --- a/tabby-core/src/utils.ts +++ b/tabby-core/src/utils.ts @@ -5,6 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker' export const WIN_BUILD_CONPTY_SUPPORTED = 17692 export const WIN_BUILD_CONPTY_STABLE = 18309 export const WIN_BUILD_WSL_EXE_DISTRO_FLAG = 17763 +export const WIN_BUILD_WSL_EXE_CD_FLAG = 19041 export const WIN_BUILD_FLUENT_BG_SUPPORTED = 17063 export function getWindows10Build (): number|undefined { diff --git a/tabby-electron/src/shells/wsl.ts b/tabby-electron/src/shells/wsl.ts index e97b7f52f..4734aacea 100644 --- a/tabby-electron/src/shells/wsl.ts +++ b/tabby-electron/src/shells/wsl.ts @@ -2,7 +2,7 @@ import * as fs from 'mz/fs' import slugify from 'slugify' import { Injectable } from '@angular/core' -import { HostAppService, Platform, isWindowsBuild, WIN_BUILD_WSL_EXE_DISTRO_FLAG } from 'tabby-core' +import { HostAppService, Platform, isWindowsBuild, WIN_BUILD_WSL_EXE_DISTRO_FLAG, WIN_BUILD_WSL_EXE_CD_FLAG } from 'tabby-core' import { ShellProvider, Shell } from 'tabby-local' @@ -63,6 +63,11 @@ export class WSLShellProvider extends ShellProvider { const lxss = wnr.getRegistryKey(wnr.HK.CU, lxssPath) const shells: Shell[] = [] + // WSL only honors `--cd` for Linux-style paths (e.g. `~`) starting with this build. + // On older builds, omit it and fall back to the previous (native cwd inheritance) behavior. + // https://github.com/microsoft/terminal/blob/main/src/cascadia/TerminalSettingsModel/WslDistroGenerator.cpp + const homeDirArgs = isWindowsBuild(WIN_BUILD_WSL_EXE_CD_FLAG) ? ['--cd', '~'] : undefined + if (lxss?.DefaultDistribution) { const defaultDistKey = wnr.getRegistryKey(wnr.HK.CU, lxssPath + '\\' + String(lxss.DefaultDistribution.value)) if (defaultDistKey?.DistributionName) { @@ -71,6 +76,7 @@ export class WSLShellProvider extends ShellProvider { id: 'wsl', name: 'WSL / Default distro', command: wslPath, + homeDirArgs, env: { TERM: 'xterm-color', COLORTERM: 'truecolor', @@ -114,6 +120,7 @@ export class WSLShellProvider extends ShellProvider { name: `WSL / ${name}`, command: wslPath, args: ['-d', name], + homeDirArgs, fsBase, env: { TERM: 'xterm-color', diff --git a/tabby-local/src/api.ts b/tabby-local/src/api.ts index 3144dd7bb..5eacc71d9 100644 --- a/tabby-local/src/api.ts +++ b/tabby-local/src/api.ts @@ -17,6 +17,13 @@ export interface Shell { cwd?: string + /** + * Args that make the shell start in its own home directory (e.g. WSL's `--cd ~`). + * Only applied when the session has no working directory of its own, since these + * would otherwise override it. + */ + homeDirArgs?: string[] + /** * SVG icon */ @@ -39,6 +46,8 @@ export interface SessionOptions { restoreFromPTYID: string | null command: string args: string[] + /** See {@link Shell.homeDirArgs} */ + homeDirArgs: string[] cwd: string | null env: Record width: number | null diff --git a/tabby-local/src/profiles.ts b/tabby-local/src/profiles.ts index 9afaf2226..ef48218ba 100644 --- a/tabby-local/src/profiles.ts +++ b/tabby-local/src/profiles.ts @@ -16,6 +16,7 @@ export class LocalProfilesService extends ProfileProvider { restoreFromPTYID: null, command: '', args: [], + homeDirArgs: [], cwd: null, env: { __nonStructural: true, @@ -82,6 +83,7 @@ export class LocalProfilesService extends ProfileProvider { ...this.configDefaults.options, command: shell.command, args: shell.args ?? [], + homeDirArgs: shell.homeDirArgs ?? [], env: shell.env, cwd: shell.cwd ?? null, shellType: shell.shellType ?? null, diff --git a/tabby-local/src/session.ts b/tabby-local/src/session.ts index 881f697e0..7defef617 100644 --- a/tabby-local/src/session.ts +++ b/tabby-local/src/session.ts @@ -7,6 +7,17 @@ import { getEnvironment, substituteEnv } from './environment' import { resolveGuestCWD } from './wslPath' import { isDirectory, isDirectorySync } from './util' +function usableCWD (path?: string|null): string|undefined { + if (!path) { + return undefined + } + if (!isDirectorySync(path)) { + console.warn('Ignoring invalid CWD:', path) + return undefined + } + return path +} + const windowsDirectoryRegex = /([a-zA-Z]:[^\:\[\]\?\"\<\>\|]+)/mi function mergeEnv (...envs) { @@ -87,16 +98,18 @@ export class Session extends BaseSession { }) } - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - let cwd = resolveGuestCWD(options.cwd, options.fsBase) || process.env.HOME + // resolveGuestCWD maps a POSIX cwd reported by a guest shell (WSL) onto its + // Windows-visible path before we validate it. + const explicitCWD = usableCWD(resolveGuestCWD(options.cwd, options.fsBase)) - if (!isDirectorySync(cwd)) { - console.warn('Ignoring invalid CWD:', cwd) - cwd = undefined - } + // A shell that knows how to start in its own home directory (WSL's `--cd ~`) only gets + // to do so when we have no working directory for it - those args outrank the cwd below. + const args = explicitCWD ? options.args : [...options.args, ...options.homeDirArgs] + + const cwd = explicitCWD ?? usableCWD(process.env.HOME) try { - pty = await this.ptyInterface.spawn(options.command, options.args, { + pty = await this.ptyInterface.spawn(options.command, args, { name: 'xterm-256color', cols: options.width ?? 80, rows: options.height ?? 30,