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 {
command := f.getCommand(FOSSILCOMMAND)
if !f.env.HasCommand(command) {
if !f.hasCommand(FOSSILCOMMAND) {
return false
}
// run fossil command
output, err := f.env.RunCommand(command, "status")
output, err := f.env.RunCommand(f.command, "status")
if err != nil {
return false
}

View file

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

View file

@ -164,11 +164,7 @@ func (g *Git) Kraken() string {
}
func (g *Git) shouldDisplay() bool {
// when in a WSL shared folder, we must use git.exe and convert paths accordingly
// for worktrees, stashes, and path to work
g.IsWslSharedPath = g.env.InWSLSharedDrive()
if !g.env.HasCommand(g.getCommand(GITCOMMAND)) {
if !g.hasCommand(GITCOMMAND) {
return false
}
@ -376,7 +372,7 @@ func (g *Git) setGitStatus() {
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...)
val, err := g.env.RunCommand(g.getCommand(GITCOMMAND), args...)
val, err := g.env.RunCommand(g.command, args...)
if err != nil {
return ""
}

View file

@ -154,8 +154,9 @@ func TestGetGitOutputForCommand(t *testing.T) {
env.On("GOOS").Return("unix")
g := &Git{
scm: scm{
env: env,
props: properties.Map{},
env: env,
props: properties.Map{},
command: GITCOMMAND,
},
}
got := g.getGitCommandOutput(commandArgs...)
@ -308,6 +309,7 @@ func TestSetGitHEADContextClean(t *testing.T) {
TagIcon: "tag ",
RevertIcon: "revert ",
},
command: GITCOMMAND,
},
ShortHash: "1234567",
Ref: tc.Ref,
@ -346,6 +348,7 @@ func TestSetPrettyHEADName(t *testing.T) {
CommitIcon: "commit ",
TagIcon: "tag ",
},
command: GITCOMMAND,
},
ShortHash: tc.ShortHash,
}
@ -473,8 +476,9 @@ func TestSetGitStatus(t *testing.T) {
env.MockGitCommand("", strings.ReplaceAll(tc.Output, "\t", ""), "status", "-unormal", "--branch", "--porcelain=2")
g := &Git{
scm: scm{
env: env,
props: properties.Map{},
env: env,
props: properties.Map{},
command: GITCOMMAND,
},
}
if tc.ExpectedWorking == nil {
@ -546,8 +550,9 @@ func TestGitUpstream(t *testing.T) {
}
g := &Git{
scm: scm{
env: env,
props: props,
env: env,
props: props,
command: GITCOMMAND,
},
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) {
cases := []struct {
Case string

View file

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

View file

@ -7,6 +7,11 @@ import (
"strings"
)
const (
// Fallback to native command
NativeFallback properties.Property = "native_fallback"
)
// ScmStatus represents part of the status of a repository
type ScmStatus struct {
Unmerged int
@ -45,6 +50,7 @@ type scm struct {
env environment.Environment
IsWslSharedPath bool
CommandMissing bool
Dir string // actual repo root directory
workingDir string
@ -107,13 +113,28 @@ func (s *scm) convertToLinuxPath(path string) string {
return s.env.ConvertToLinuxPath(path)
}
func (s *scm) getCommand(command string) string {
func (s *scm) hasCommand(command string) bool {
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 {
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
import (
"oh-my-posh/environment"
"oh-my-posh/mock"
"oh-my-posh/properties"
"testing"
@ -154,3 +156,39 @@ func TestTruncateBranchWithSymbol(t *testing.T) {
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 {
// when in a WSL shared folder, we must use git.exe and convert paths accordingly
// for worktrees, stashes, and path to work
s.IsWslSharedPath = s.env.InWSLSharedDrive()
if !s.env.HasCommand(s.getCommand(SVNCOMMAND)) {
if !s.hasCommand(SVNCOMMAND) {
return false
}
Svndir, err := s.env.HasParentFilePath(".svn")
@ -120,7 +117,7 @@ func (s *Svn) setSvnStatus() {
func (s *Svn) getSvnCommandOutput(command string, args ...string) string {
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 {
return ""
}

View file

@ -234,8 +234,9 @@ R Moved.File`,
s := &Svn{
scm: scm{
env: env,
props: properties.Map{},
env: env,
props: properties.Map{},
command: SVNCOMMAND,
},
}
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])
:::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`).
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.
- 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

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).
- 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])