support fish shell with wsh (#874)

This commit is contained in:
Mike Sawka 2024-09-26 15:34:52 -07:00 committed by GitHub
parent 3ebdd9c01e
commit 29b80bc028
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 53 additions and 17 deletions

View File

@ -301,6 +301,7 @@ declare global {
"term:fontfamily"?: string; "term:fontfamily"?: string;
"term:mode"?: string; "term:mode"?: string;
"term:theme"?: string; "term:theme"?: string;
"term:localshellpath"?: string;
count?: number; count?: number;
}; };
@ -417,6 +418,7 @@ declare global {
"term:fontsize"?: number; "term:fontsize"?: number;
"term:fontfamily"?: string; "term:fontfamily"?: string;
"term:disablewebgl"?: boolean; "term:disablewebgl"?: boolean;
"term:localshellpath"?: string;
"editor:minimapenabled"?: boolean; "editor:minimapenabled"?: boolean;
"editor:stickyscrollenabled"?: boolean; "editor:stickyscrollenabled"?: boolean;
"web:*"?: boolean; "web:*"?: boolean;

View File

@ -20,6 +20,7 @@ import (
"github.com/wavetermdev/waveterm/pkg/shellexec" "github.com/wavetermdev/waveterm/pkg/shellexec"
"github.com/wavetermdev/waveterm/pkg/wavebase" "github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/waveobj" "github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wconfig"
"github.com/wavetermdev/waveterm/pkg/wps" "github.com/wavetermdev/waveterm/pkg/wps"
"github.com/wavetermdev/waveterm/pkg/wshrpc" "github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshutil" "github.com/wavetermdev/waveterm/pkg/wshutil"
@ -286,6 +287,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
return err return err
} }
} else { } else {
// local terminal
if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) { if !blockMeta.GetBool(waveobj.MetaKey_CmdNoWsh, false) {
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, wavebase.GetDomainSocketName()) jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, wavebase.GetDomainSocketName())
if err != nil { if err != nil {
@ -293,6 +295,13 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj
} }
cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr cmdOpts.Env[wshutil.WaveJwtTokenVarName] = jwtStr
} }
settings := wconfig.GetWatcher().GetFullConfig().Settings
if settings.TermLocalShellPath != "" {
cmdOpts.ShellPath = settings.TermLocalShellPath
}
if blockMeta.GetString(waveobj.MetaKey_TermLocalShellPath, "") != "" {
cmdOpts.ShellPath = blockMeta.GetString(waveobj.MetaKey_TermLocalShellPath, "")
}
shellProc, err = shellexec.StartShellProc(rc.TermSize, cmdStr, cmdOpts) shellProc, err = shellexec.StartShellProc(rc.TermSize, cmdStr, cmdOpts)
if err != nil { if err != nil {
return err return err

View File

@ -21,6 +21,7 @@ import (
"github.com/wavetermdev/waveterm/pkg/remote" "github.com/wavetermdev/waveterm/pkg/remote"
"github.com/wavetermdev/waveterm/pkg/remote/conncontroller" "github.com/wavetermdev/waveterm/pkg/remote/conncontroller"
"github.com/wavetermdev/waveterm/pkg/util/shellutil" "github.com/wavetermdev/waveterm/pkg/util/shellutil"
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
"github.com/wavetermdev/waveterm/pkg/wavebase" "github.com/wavetermdev/waveterm/pkg/wavebase"
"github.com/wavetermdev/waveterm/pkg/waveobj" "github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wshutil" "github.com/wavetermdev/waveterm/pkg/wshutil"
@ -33,6 +34,7 @@ type CommandOptsType struct {
Login bool `json:"login,omitempty"` Login bool `json:"login,omitempty"`
Cwd string `json:"cwd,omitempty"` Cwd string `json:"cwd,omitempty"`
Env map[string]string `json:"env,omitempty"` Env map[string]string `json:"env,omitempty"`
ShellPath string `json:"shellPath,omitempty"`
} }
type ShellProc struct { type ShellProc struct {
@ -140,15 +142,19 @@ func (pp *PipePty) WriteString(s string) (n int, err error) {
func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *conncontroller.SSHConn) (*ShellProc, error) { func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType, conn *conncontroller.SSHConn) (*ShellProc, error) {
client := conn.GetClient() client := conn.GetClient()
shellPath, err := remote.DetectShell(client) shellPath := cmdOpts.ShellPath
if err != nil { if shellPath == "" {
return nil, err remoteShellPath, err := remote.DetectShell(client)
if err != nil {
return nil, err
}
shellPath = remoteShellPath
} }
var shellOpts []string var shellOpts []string
var cmdCombined string var cmdCombined string
log.Printf("detected shell: %s", shellPath) log.Printf("detected shell: %s", shellPath)
err = remote.InstallClientRcFiles(client) err := remote.InstallClientRcFiles(client)
if err != nil { if err != nil {
log.Printf("error installing rc files: %v", err) log.Printf("error installing rc files: %v", err)
return nil, err return nil, err
@ -163,6 +169,9 @@ func StartRemoteShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts Comm
// add --rcfile // add --rcfile
// cant set -l or -i with --rcfile // cant set -l or -i with --rcfile
shellOpts = append(shellOpts, "--rcfile", fmt.Sprintf(`"%s"/.waveterm/%s/.bashrc`, homeDir, shellutil.BashIntegrationDir)) shellOpts = append(shellOpts, "--rcfile", fmt.Sprintf(`"%s"/.waveterm/%s/.bashrc`, homeDir, shellutil.BashIntegrationDir))
} else if isFishShell(shellPath) {
carg := fmt.Sprintf(`"set -x PATH \"%s\"/.waveterm/%s $PATH"`, homeDir, shellutil.WaveHomeBinDir)
shellOpts = append(shellOpts, "-C", carg)
} else if remote.IsPowershell(shellPath) { } else if remote.IsPowershell(shellPath) {
// powershell is weird about quoted path executables and requires an ampersand first // powershell is weird about quoted path executables and requires an ampersand first
shellPath = "& " + shellPath shellPath = "& " + shellPath
@ -264,17 +273,29 @@ func isBashShell(shellPath string) bool {
return strings.Contains(shellBase, "bash") return strings.Contains(shellBase, "bash")
} }
func isFishShell(shellPath string) bool {
// get the base path, and then check contains
shellBase := filepath.Base(shellPath)
return strings.Contains(shellBase, "fish")
}
func StartShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType) (*ShellProc, error) { func StartShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOptsType) (*ShellProc, error) {
shellutil.InitCustomShellStartupFiles() shellutil.InitCustomShellStartupFiles()
var ecmd *exec.Cmd var ecmd *exec.Cmd
var shellOpts []string var shellOpts []string
shellPath := cmdOpts.ShellPath
shellPath := shellutil.DetectLocalShellPath() if shellPath == "" {
shellPath = shellutil.DetectLocalShellPath()
}
if cmdStr == "" { if cmdStr == "" {
if isBashShell(shellPath) { if isBashShell(shellPath) {
// add --rcfile // add --rcfile
// cant set -l or -i with --rcfile // cant set -l or -i with --rcfile
shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride()) shellOpts = append(shellOpts, "--rcfile", shellutil.GetBashRcFileOverride())
} else if isFishShell(shellPath) {
wshBinDir := filepath.Join(wavebase.GetWaveHomeDir(), shellutil.WaveHomeBinDir)
quotedWshBinDir := utilfn.ShellQuote(wshBinDir, false, 300)
shellOpts = append(shellOpts, "-C", fmt.Sprintf("set -x PATH %s $PATH", quotedWshBinDir))
} else if remote.IsPowershell(shellPath) { } else if remote.IsPowershell(shellPath) {
shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", shellutil.GetWavePowershellEnv()) shellOpts = append(shellOpts, "-ExecutionPolicy", "Bypass", "-NoExit", "-File", shellutil.GetWavePowershellEnv())
} else { } else {
@ -322,7 +343,6 @@ func StartShellProc(termSize waveobj.TermSize, cmdStr string, cmdOpts CommandOpt
} }
cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)}) cmdPty, err := pty.StartWithSize(ecmd, &pty.Winsize{Rows: uint16(termSize.Rows), Cols: uint16(termSize.Cols)})
if err != nil { if err != nil {
cmdPty.Close()
return nil, err return nil, err
} }
return &ShellProc{Cmd: CmdWrap{ecmd, cmdPty}, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil return &ShellProc{Cmd: CmdWrap{ecmd, cmdPty}, CloseOnce: &sync.Once{}, DoneCh: make(chan any)}, nil

View File

@ -59,6 +59,7 @@ const (
MetaKey_TermFontFamily = "term:fontfamily" MetaKey_TermFontFamily = "term:fontfamily"
MetaKey_TermMode = "term:mode" MetaKey_TermMode = "term:mode"
MetaKey_TermTheme = "term:theme" MetaKey_TermTheme = "term:theme"
MetaKey_TermLocalShellPath = "term:localshellpath"
MetaKey_Count = "count" MetaKey_Count = "count"
) )

View File

@ -54,12 +54,14 @@ type MetaTSType struct {
BgOpacity float64 `json:"bg:opacity,omitempty"` BgOpacity float64 `json:"bg:opacity,omitempty"`
BgBlendMode string `json:"bg:blendmode,omitempty"` BgBlendMode string `json:"bg:blendmode,omitempty"`
TermClear bool `json:"term:*,omitempty"` TermClear bool `json:"term:*,omitempty"`
TermFontSize int `json:"term:fontsize,omitempty"` TermFontSize int `json:"term:fontsize,omitempty"`
TermFontFamily string `json:"term:fontfamily,omitempty"` TermFontFamily string `json:"term:fontfamily,omitempty"`
TermMode string `json:"term:mode,omitempty"` TermMode string `json:"term:mode,omitempty"`
TermTheme string `json:"term:theme,omitempty"` TermTheme string `json:"term:theme,omitempty"`
Count int `json:"count,omitempty"` // temp for cpu plot. will remove later TermLocalShellPath string `json:"term:localshellpath,omitempty"` // matches settings
Count int `json:"count,omitempty"` // temp for cpu plot. will remove later
} }
type MetaDataDecl struct { type MetaDataDecl struct {

View File

@ -18,6 +18,7 @@ const (
ConfigKey_TermFontSize = "term:fontsize" ConfigKey_TermFontSize = "term:fontsize"
ConfigKey_TermFontFamily = "term:fontfamily" ConfigKey_TermFontFamily = "term:fontfamily"
ConfigKey_TermDisableWebGl = "term:disablewebgl" ConfigKey_TermDisableWebGl = "term:disablewebgl"
ConfigKey_TermLocalShellPath = "term:localshellpath"
ConfigKey_EditorMinimapEnabled = "editor:minimapenabled" ConfigKey_EditorMinimapEnabled = "editor:minimapenabled"
ConfigKey_EditorStickyScrollEnabled = "editor:stickyscrollenabled" ConfigKey_EditorStickyScrollEnabled = "editor:stickyscrollenabled"

View File

@ -48,10 +48,11 @@ type SettingsType struct {
AiMaxTokens float64 `json:"ai:maxtokens,omitempty"` AiMaxTokens float64 `json:"ai:maxtokens,omitempty"`
AiTimeoutMs float64 `json:"ai:timeoutms,omitempty"` AiTimeoutMs float64 `json:"ai:timeoutms,omitempty"`
TermClear bool `json:"term:*,omitempty"` TermClear bool `json:"term:*,omitempty"`
TermFontSize float64 `json:"term:fontsize,omitempty"` TermFontSize float64 `json:"term:fontsize,omitempty"`
TermFontFamily string `json:"term:fontfamily,omitempty"` TermFontFamily string `json:"term:fontfamily,omitempty"`
TermDisableWebGl bool `json:"term:disablewebgl,omitempty"` TermDisableWebGl bool `json:"term:disablewebgl,omitempty"`
TermLocalShellPath string `json:"term:localshellpath,omitempty"`
EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"` EditorMinimapEnabled bool `json:"editor:minimapenabled,omitempty"`
EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"` EditorStickyScrollEnabled bool `json:"editor:stickyscrollenabled,omitempty"`