feat(wsl): allow using native executable on shared drive

relates to #2097
This commit is contained in:
Jan De Dobbeleer 2022-09-14 14:50:08 +02:00 committed by Jan De Dobbeleer
parent 620187aada
commit 4056729e8e
12 changed files with 95 additions and 69 deletions

View file

@ -38,12 +38,11 @@ func (f *Fossil) Template() string {
} }
func (f *Fossil) Enabled() bool { func (f *Fossil) Enabled() bool {
command := f.getCommand(FOSSILCOMMAND) if !f.hasCommand(FOSSILCOMMAND) {
if !f.env.HasCommand(command) {
return false return false
} }
// run fossil command // run fossil command
output, err := f.env.RunCommand(command, "status") output, err := f.env.RunCommand(f.command, "status")
if err != nil { if err != nil {
return false return false
} }

View file

@ -54,6 +54,7 @@ func TestFossilStatus(t *testing.T) {
env := new(mock.MockedEnvironment) env := new(mock.MockedEnvironment)
env.On("GOOS").Return("unix") env.On("GOOS").Return("unix")
env.On("IsWsl").Return(false) env.On("IsWsl").Return(false)
env.On("InWSLSharedDrive").Return(false)
env.On("HasCommand", FOSSILCOMMAND).Return(tc.HasCommand) env.On("HasCommand", FOSSILCOMMAND).Return(tc.HasCommand)
env.On("RunCommand", FOSSILCOMMAND, []string{"status"}).Return(strings.ReplaceAll(tc.Output, "\t", ""), tc.OutputError) env.On("RunCommand", FOSSILCOMMAND, []string{"status"}).Return(strings.ReplaceAll(tc.Output, "\t", ""), tc.OutputError)
f := &Fossil{ f := &Fossil{

View file

@ -164,11 +164,7 @@ func (g *Git) Kraken() string {
} }
func (g *Git) shouldDisplay() bool { func (g *Git) shouldDisplay() bool {
// when in a WSL shared folder, we must use git.exe and convert paths accordingly if !g.hasCommand(GITCOMMAND) {
// for worktrees, stashes, and path to work
g.IsWslSharedPath = g.env.InWSLSharedDrive()
if !g.env.HasCommand(g.getCommand(GITCOMMAND)) {
return false return false
} }
@ -376,7 +372,7 @@ func (g *Git) setGitStatus() {
func (g *Git) getGitCommandOutput(args ...string) string { func (g *Git) getGitCommandOutput(args ...string) string {
args = append([]string{"-C", g.realDir, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...) args = append([]string{"-C", g.realDir, "--no-optional-locks", "-c", "core.quotepath=false", "-c", "color.status=false"}, args...)
val, err := g.env.RunCommand(g.getCommand(GITCOMMAND), args...) val, err := g.env.RunCommand(g.command, args...)
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -156,6 +156,7 @@ func TestGetGitOutputForCommand(t *testing.T) {
scm: scm{ scm: scm{
env: env, env: env,
props: properties.Map{}, props: properties.Map{},
command: GITCOMMAND,
}, },
} }
got := g.getGitCommandOutput(commandArgs...) got := g.getGitCommandOutput(commandArgs...)
@ -308,6 +309,7 @@ func TestSetGitHEADContextClean(t *testing.T) {
TagIcon: "tag ", TagIcon: "tag ",
RevertIcon: "revert ", RevertIcon: "revert ",
}, },
command: GITCOMMAND,
}, },
ShortHash: "1234567", ShortHash: "1234567",
Ref: tc.Ref, Ref: tc.Ref,
@ -346,6 +348,7 @@ func TestSetPrettyHEADName(t *testing.T) {
CommitIcon: "commit ", CommitIcon: "commit ",
TagIcon: "tag ", TagIcon: "tag ",
}, },
command: GITCOMMAND,
}, },
ShortHash: tc.ShortHash, ShortHash: tc.ShortHash,
} }
@ -475,6 +478,7 @@ func TestSetGitStatus(t *testing.T) {
scm: scm{ scm: scm{
env: env, env: env,
props: properties.Map{}, props: properties.Map{},
command: GITCOMMAND,
}, },
} }
if tc.ExpectedWorking == nil { if tc.ExpectedWorking == nil {
@ -548,6 +552,7 @@ func TestGitUpstream(t *testing.T) {
scm: scm{ scm: scm{
env: env, env: env,
props: props, props: props,
command: GITCOMMAND,
}, },
Upstream: "origin/main", Upstream: "origin/main",
} }
@ -595,48 +600,6 @@ func TestGetBranchStatus(t *testing.T) {
} }
} }
func TestGetGitCommand(t *testing.T) {
cases := []struct {
Case string
Expected string
IsWSL bool
IsWSL1 bool
GOOS string
CWD string
IsWslSharedPath bool
}{
{Case: "On Windows", Expected: "git.exe", GOOS: environment.WINDOWS},
{Case: "Non Windows", Expected: "git"},
{Case: "Iside WSL2, non shared", IsWSL: true, Expected: "git"},
{Case: "Iside WSL2, shared", Expected: "git.exe", IsWSL: true, IsWslSharedPath: true, CWD: "/mnt/bill"},
{Case: "Iside WSL1, shared", Expected: "git", IsWSL: true, IsWSL1: true, CWD: "/mnt/bill"},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("IsWsl").Return(tc.IsWSL)
env.On("GOOS").Return(tc.GOOS)
env.On("Pwd").Return(tc.CWD)
wslUname := "5.10.60.1-microsoft-standard-WSL2"
if tc.IsWSL1 {
wslUname = "4.4.0-19041-Microsoft"
}
env.On("RunCommand", "uname", []string{"-r"}).Return(wslUname, nil)
g := &Git{
scm: scm{
env: env,
},
}
if tc.IsWslSharedPath {
env.On("InWSLSharedDrive").Return(true)
g.IsWslSharedPath = tc.IsWslSharedPath
} else {
env.On("InWSLSharedDrive").Return(false)
}
assert.Equal(t, tc.Expected, g.getCommand(GITCOMMAND), tc.Case)
}
}
func TestGitTemplateString(t *testing.T) { func TestGitTemplateString(t *testing.T) {
cases := []struct { cases := []struct {
Case string Case string

View file

@ -193,6 +193,7 @@ func TestPoshGitSegment(t *testing.T) {
props: &properties.Map{ props: &properties.Map{
FetchUpstreamIcon: tc.FetchUpstreamIcon, FetchUpstreamIcon: tc.FetchUpstreamIcon,
}, },
command: GITCOMMAND,
}, },
} }
if len(tc.Template) == 0 { if len(tc.Template) == 0 {

View file

@ -7,6 +7,11 @@ import (
"strings" "strings"
) )
const (
// Fallback to native command
NativeFallback properties.Property = "native_fallback"
)
// ScmStatus represents part of the status of a repository // ScmStatus represents part of the status of a repository
type ScmStatus struct { type ScmStatus struct {
Unmerged int Unmerged int
@ -45,6 +50,7 @@ type scm struct {
env environment.Environment env environment.Environment
IsWslSharedPath bool IsWslSharedPath bool
CommandMissing bool
Dir string // actual repo root directory Dir string // actual repo root directory
workingDir string workingDir string
@ -107,13 +113,28 @@ func (s *scm) convertToLinuxPath(path string) string {
return s.env.ConvertToLinuxPath(path) return s.env.ConvertToLinuxPath(path)
} }
func (s *scm) getCommand(command string) string { func (s *scm) hasCommand(command string) bool {
if len(s.command) > 0 { if len(s.command) > 0 {
return s.command return true
} }
s.command = command // when in a WSL shared folder, we must use command.exe and convert paths accordingly
// for worktrees, stashes, and path to work
s.IsWslSharedPath = s.env.InWSLSharedDrive()
if s.env.GOOS() == environment.WINDOWS || s.IsWslSharedPath { if s.env.GOOS() == environment.WINDOWS || s.IsWslSharedPath {
s.command += ".exe" command += ".exe"
} }
return s.command if s.env.HasCommand(command) {
s.command = command
return true
}
s.CommandMissing = true
// only use the native fallback when set by the user
if s.IsWslSharedPath && s.props.GetBool(NativeFallback, false) {
command = strings.TrimSuffix(command, ".exe")
if s.env.HasCommand(command) {
s.command = command
return true
}
}
return false
} }

View file

@ -1,6 +1,8 @@
package segments package segments
import ( import (
"oh-my-posh/environment"
"oh-my-posh/mock"
"oh-my-posh/properties" "oh-my-posh/properties"
"testing" "testing"
@ -154,3 +156,39 @@ func TestTruncateBranchWithSymbol(t *testing.T) {
assert.Equal(t, tc.Expected, p.truncateBranch(tc.Branch), tc.Case) assert.Equal(t, tc.Expected, p.truncateBranch(tc.Branch), tc.Case)
} }
} }
func TestHasCommand(t *testing.T) {
cases := []struct {
Case string
ExpectedCommand string
Command string
GOOS string
IsWslSharedPath bool
NativeFallback bool
}{
{Case: "On Windows", ExpectedCommand: "git.exe", GOOS: environment.WINDOWS},
{Case: "Cache", ExpectedCommand: "git.exe", Command: "git.exe"},
{Case: "Non Windows", ExpectedCommand: "git"},
{Case: "Iside WSL2, non shared", ExpectedCommand: "git"},
{Case: "Iside WSL2, shared", ExpectedCommand: "git.exe", IsWslSharedPath: true},
{Case: "Iside WSL2, shared fallback", ExpectedCommand: "git", IsWslSharedPath: true, NativeFallback: true},
}
for _, tc := range cases {
env := new(mock.MockedEnvironment)
env.On("GOOS").Return(tc.GOOS)
env.On("InWSLSharedDrive").Return(tc.IsWslSharedPath)
env.On("HasCommand", "git").Return(true)
env.On("HasCommand", "git.exe").Return(!tc.NativeFallback)
s := &scm{
env: env,
props: properties.Map{
NativeFallback: tc.NativeFallback,
},
command: tc.Command,
}
_ = s.hasCommand(GITCOMMAND)
assert.Equal(t, tc.ExpectedCommand, s.command, tc.Case)
}
}

View file

@ -62,10 +62,7 @@ func (s *Svn) Enabled() bool {
} }
func (s *Svn) shouldDisplay() bool { func (s *Svn) shouldDisplay() bool {
// when in a WSL shared folder, we must use git.exe and convert paths accordingly if !s.hasCommand(SVNCOMMAND) {
// for worktrees, stashes, and path to work
s.IsWslSharedPath = s.env.InWSLSharedDrive()
if !s.env.HasCommand(s.getCommand(SVNCOMMAND)) {
return false return false
} }
Svndir, err := s.env.HasParentFilePath(".svn") Svndir, err := s.env.HasParentFilePath(".svn")
@ -120,7 +117,7 @@ func (s *Svn) setSvnStatus() {
func (s *Svn) getSvnCommandOutput(command string, args ...string) string { func (s *Svn) getSvnCommandOutput(command string, args ...string) string {
args = append([]string{command, s.realDir}, args...) args = append([]string{command, s.realDir}, args...)
val, err := s.env.RunCommand(s.getCommand(SVNCOMMAND), args...) val, err := s.env.RunCommand(s.command, args...)
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -236,6 +236,7 @@ R Moved.File`,
scm: scm{ scm: scm{
env: env, env: env,
props: properties.Map{}, props: properties.Map{},
command: SVNCOMMAND,
}, },
} }
s.setSvnStatus() s.setSvnStatus()

View file

@ -28,6 +28,11 @@ Local changes can also be displayed which uses the following syntax:
} }
``` ```
## Properties
- native_fallback: `boolean` - when set to `true` and `fossil.exe` is not available when inside a WSL2 shared Windows drive, we will fallback to the native fossil
executable to fetch data. Not all information can be displayed in this case. Defaults to `false`.
## Template ([info][templates]) ## Template ([info][templates])
:::note default template :::note default template

View file

@ -66,6 +66,8 @@ You can set the following properties to `true` to enable fetching additional inf
- ignore_submodules: `map[string]string` - map of repo's where to change the [--ignore-submodules][submodules] flag (`none`, `untracked`, `dirty` or `all`). - ignore_submodules: `map[string]string` - map of repo's where to change the [--ignore-submodules][submodules] flag (`none`, `untracked`, `dirty` or `all`).
For example `"ignore_submodules": { "/Users/me/repos/repo1": "all" }`. If you want to override for all repo's, use `*` to set the mode For example `"ignore_submodules": { "/Users/me/repos/repo1": "all" }`. If you want to override for all repo's, use `*` to set the mode
instead of the repo path. instead of the repo path.
- native_fallback: `boolean` - when set to `true` and `git.exe` is not available when inside a WSL2 shared Windows drive, we will fallback to the native git
executable to fetch data. Not all information can be displayed in this case. Defaults to `false`.
### Icons ### Icons

View file

@ -40,6 +40,8 @@ As doing multiple [subversion][svn] calls can slow down the prompt experience, w
You can set the following properties to `true` to enable fetching additional information (and populate the template). You can set the following properties to `true` to enable fetching additional information (and populate the template).
- fetch_status: `boolean` - fetch the local changes - defaults to `false` - fetch_status: `boolean` - fetch the local changes - defaults to `false`
- native_fallback: `boolean` - when set to `true` and `svn.exe` is not available when inside a WSL2 shared Windows drive, we will fallback to the native svn
executable to fetch data. Not all information can be displayed in this case. Defaults to `false`.
## Template ([info][templates]) ## Template ([info][templates])