From e5b3b00a4316a6a6f81ac3fdb310695411a01ded Mon Sep 17 00:00:00 2001 From: "L. Yeung" Date: Thu, 1 Sep 2022 13:44:27 +0800 Subject: [PATCH] fix(git): populate `.Dir` and `.RepoName` correctly --- src/environment/shell.go | 23 ++- src/environment/shell_unix.go | 2 +- src/segments/git.go | 17 +- src/segments/path_test.go | 281 +++++++++++++++++++++++++++++----- src/segments/python_test.go | 2 + src/segments/svn.go | 8 +- 6 files changed, 279 insertions(+), 54 deletions(-) diff --git a/src/environment/shell.go b/src/environment/shell.go index 185efac1..854fcdf3 100644 --- a/src/environment/shell.go +++ b/src/environment/shell.go @@ -727,7 +727,7 @@ func (env *ShellEnvironment) TemplateCache() *TemplateCache { tmplCache.Env[key] = strings.Join(val, separator) } pwd := env.Pwd() - tmplCache.PWD = strings.Replace(pwd, env.Home(), "~", 1) + tmplCache.PWD = ReplaceHomeDirPrefixWithTilde(env, pwd) tmplCache.Folder = Base(env, pwd) tmplCache.UserName = env.User() if host, err := env.Host(); err == nil { @@ -780,6 +780,16 @@ func dirMatchesOneOf(dir, home, goos string, regexes []string) bool { return false } +func isPathSeparator(env Environment, c uint8) bool { + if c == '/' { + return true + } + if env.GOOS() == WINDOWS && c == '\\' { + return true + } + return false +} + // Base returns the last element of path. // Trailing path separators are removed before extracting the last element. // If the path consists entirely of separators, Base returns a single separator. @@ -789,7 +799,7 @@ func Base(env Environment, path string) string { } volumeName := filepath.VolumeName(path) // Strip trailing slashes. - for len(path) > 0 && string(path[len(path)-1]) == env.PathSeparator() { + for len(path) > 0 && isPathSeparator(env, path[len(path)-1]) { path = path[0 : len(path)-1] } if volumeName == path { @@ -799,7 +809,7 @@ func Base(env Environment, path string) string { path = path[len(filepath.VolumeName(path)):] // Find the last element i := len(path) - 1 - for i >= 0 && string(path[i]) != env.PathSeparator() { + for i >= 0 && !isPathSeparator(env, path[i]) { i-- } if i >= 0 { @@ -812,6 +822,13 @@ func Base(env Environment, path string) string { return path } +func ReplaceHomeDirPrefixWithTilde(env Environment, path string) string { + if strings.HasPrefix(path, env.Home()) { + return strings.Replace(path, env.Home(), "~", 1) + } + return path +} + func cleanHostName(hostName string) string { garbage := []string{ ".lan", diff --git a/src/environment/shell_unix.go b/src/environment/shell_unix.go index f9376d63..d0836907 100644 --- a/src/environment/shell_unix.go +++ b/src/environment/shell_unix.go @@ -102,7 +102,7 @@ func (env *ShellEnvironment) InWSLSharedDrive() bool { return false } windowsPath := env.ConvertToWindowsPath(env.Pwd()) - return !strings.HasPrefix(windowsPath, `//wsl.localhost`) && !strings.HasPrefix(windowsPath, `//wsl$`) + return !strings.HasPrefix(windowsPath, `//wsl.localhost/`) && !strings.HasPrefix(windowsPath, `//wsl$/`) } func (env *ShellEnvironment) ConvertToWindowsPath(path string) string { diff --git a/src/segments/git.go b/src/segments/git.go index 1a2fe35d..a7355ef6 100644 --- a/src/segments/git.go +++ b/src/segments/git.go @@ -117,7 +117,7 @@ func (g *Git) Enabled() bool { if !g.shouldDisplay() { return false } - g.RepoName = environment.Base(g.env, g.realDir) + g.RepoName = environment.Base(g.env, g.convertToLinuxPath(g.realDir)) displayStatus := g.props.GetBool(FetchStatus, false) if displayStatus { g.setGitStatus() @@ -141,8 +141,7 @@ func (g *Git) Enabled() bool { } func (g *Git) shouldDisplay() bool { - // when in wsl/wsl2 and in a windows shared folder - // we must use git.exe and convert paths accordingly + // 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() @@ -159,9 +158,12 @@ func (g *Git) shouldDisplay() bool { return false } - // convert the worktree file path to a windows one when in wsl 2 shared folder - dir := strings.Replace(gitdir.Path, g.env.Home(), "~", 1) // align with template PWD - g.Dir = strings.TrimSuffix(g.convertToWindowsPath(dir), "/.git") + dir := environment.ReplaceHomeDirPrefixWithTilde(g.env, gitdir.Path) // align with template PWD + if g.env.GOOS() == environment.WINDOWS { + g.Dir = strings.TrimSuffix(dir, `\.git`) + } else { + g.Dir = strings.TrimSuffix(dir, "/.git") + } if !gitdir.IsDir { return g.hasWorktree(gitdir) @@ -169,6 +171,7 @@ func (g *Git) shouldDisplay() bool { g.workingDir = gitdir.Path g.rootDir = gitdir.Path + // convert the worktree file path to a windows one when in a WSL shared folder g.realDir = strings.TrimSuffix(g.convertToWindowsPath(gitdir.Path), "/.git") return true } @@ -180,7 +183,7 @@ func (g *Git) hasWorktree(gitdir *environment.FileInfo) bool { if matches == nil || matches["dir"] == "" { return false } - // if we open a worktree file in a shared wsl2 folder, we have to convert it back + // if we open a worktree file in a WSL shared folder, we have to convert it back // to the mounted path g.workingDir = g.convertToLinuxPath(matches["dir"]) diff --git a/src/segments/path_test.go b/src/segments/path_test.go index 2b0a2ae1..2e84034b 100644 --- a/src/segments/path_test.go +++ b/src/segments/path_test.go @@ -78,17 +78,18 @@ func TestRootLocationHome(t *testing.T) { HomePath string Pswd string Pwd string + GOOS string PathSeparator string HomeIcon string RegistryIcon string }{ {Expected: "~", HomeIcon: "~", HomePath: "/home/bill/", Pwd: "/home/bill/", PathSeparator: "/"}, {Expected: "usr", HomePath: "/home/bill/", Pwd: "/usr/error/what", PathSeparator: "/"}, - {Expected: "C:", HomePath: "C:\\Users\\Bill", Pwd: "C:\\Program Files\\Go", PathSeparator: "\\"}, - {Expected: "REG", RegistryIcon: "REG", HomePath: "C:\\Users\\Bill", Pwd: "HKCU:\\Program Files\\Go", PathSeparator: "\\"}, - {Expected: "~", HomeIcon: "~", HomePath: "C:\\Users\\Bill", Pwd: "Microsoft.PowerShell.Core\\FileSystem::C:\\Users\\Bill", PathSeparator: "\\"}, - {Expected: "C:", HomePath: "C:\\Users\\Jack", Pwd: "Microsoft.PowerShell.Core\\FileSystem::C:\\Users\\Bill", PathSeparator: "\\"}, - {Expected: "", HomePath: "C:\\Users\\Jack", Pwd: "", PathSeparator: "\\"}, + {Expected: "C:", HomePath: "C:\\Users\\Bill", Pwd: "C:\\Program Files\\Go", GOOS: environment.WINDOWS, PathSeparator: "\\"}, + {Expected: "REG", RegistryIcon: "REG", HomePath: "C:\\Users\\Bill", Pwd: "HKCU:\\Program Files\\Go", GOOS: environment.WINDOWS, PathSeparator: "\\"}, + {Expected: "~", HomeIcon: "~", HomePath: "C:\\Users\\Bill", Pwd: "Microsoft.PowerShell.Core\\FileSystem::C:\\Users\\Bill", GOOS: environment.WINDOWS, PathSeparator: "\\"}, + {Expected: "C:", HomePath: "C:\\Users\\Jack", Pwd: "Microsoft.PowerShell.Core\\FileSystem::C:\\Users\\Bill", GOOS: environment.WINDOWS, PathSeparator: "\\"}, + {Expected: "", HomePath: "C:\\Users\\Jack", Pwd: "", GOOS: environment.WINDOWS, PathSeparator: "\\"}, {Expected: "DRIVE:", HomePath: "/home/bill/", Pwd: "/usr/error/what", Pswd: "DRIVE:", PathSeparator: "/"}, } for _, tc := range cases { @@ -100,7 +101,7 @@ func TestRootLocationHome(t *testing.T) { } env.On("Flags").Return(args) env.On("PathSeparator").Return(tc.PathSeparator) - env.On("GOOS").Return("") + env.On("GOOS").Return(tc.GOOS) path := &Path{ env: env, props: properties.Map{ @@ -563,23 +564,125 @@ func TestAgnosterPath(t *testing.T) { //nolint:dupl Expected string Home string PWD string + GOOS string PathSeparator string }{ - {Case: "Windows outside home", Expected: "C: > f > f > location", Home: homeBillWindows, PWD: "C:\\Program Files\\Go\\location", PathSeparator: "\\"}, - {Case: "Windows oustide home", Expected: "~ > f > f > location", Home: homeBillWindows, PWD: homeBillWindows + "\\Documents\\Bill\\location", PathSeparator: "\\"}, - {Case: "Windows inside home zero levels", Expected: "C: > location", Home: homeBillWindows, PWD: "C:\\location", PathSeparator: "\\"}, - {Case: "Windows inside home one level", Expected: "C: > f > location", Home: homeBillWindows, PWD: "C:\\Program Files\\location", PathSeparator: "\\"}, - {Case: "Windows lower case drive letter", Expected: "C: > Windows", Home: homeBillWindows, PWD: "C:\\Windows\\", PathSeparator: "\\"}, - {Case: "Windows lower case drive letter (other)", Expected: "P: > Other", Home: homeBillWindows, PWD: "P:\\Other\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive", Expected: "some: > some", Home: homeBillWindows, PWD: "some:\\some\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive (ending with c)", Expected: "src: > source", Home: homeBillWindows, PWD: "src:\\source\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive (arbitrary cases)", Expected: "sRc: > source", Home: homeBillWindows, PWD: "sRc:\\source\\", PathSeparator: "\\"}, - {Case: "Windows registry drive", Expected: "\uf013 > f > magnetic:test", Home: homeBillWindows, PWD: "HKLM:\\SOFTWARE\\magnetic:test\\", PathSeparator: "\\"}, - {Case: "Windows registry drive case sensitive", Expected: "\uf013 > f > magnetic:TOAST", Home: homeBillWindows, PWD: "HKLM:\\SOFTWARE\\magnetic:TOAST\\", PathSeparator: "\\"}, - {Case: "Unix outside home", Expected: "mnt > f > f > location", Home: homeJan, PWD: "/mnt/go/test/location", PathSeparator: "/"}, - {Case: "Unix inside home", Expected: "~ > f > f > location", Home: homeJan, PWD: homeJan + "/docs/jan/location", PathSeparator: "/"}, - {Case: "Unix outside home zero levels", Expected: "mnt > location", Home: homeJan, PWD: "/mnt/location", PathSeparator: "/"}, - {Case: "Unix outside home one level", Expected: "mnt > f > location", Home: homeJan, PWD: "/mnt/folder/location", PathSeparator: "/"}, + { + Case: "Windows outside home", + Expected: "C: > f > f > location", + Home: homeBillWindows, + PWD: "C:\\Program Files\\Go\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows oustide home", + Expected: "~ > f > f > location", + Home: homeBillWindows, + PWD: homeBillWindows + "\\Documents\\Bill\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows inside home zero levels", + Expected: "C: > location", + Home: homeBillWindows, + PWD: "C:\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows inside home one level", + Expected: "C: > f > location", + Home: homeBillWindows, + PWD: "C:\\Program Files\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower case drive letter", + Expected: "C: > Windows", + Home: homeBillWindows, + PWD: "C:\\Windows\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower case drive letter (other)", + Expected: "P: > Other", + Home: homeBillWindows, + PWD: "P:\\Other\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive", + Expected: "some: > some", + Home: homeBillWindows, + PWD: "some:\\some\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive (ending with c)", + Expected: "src: > source", + Home: homeBillWindows, + PWD: "src:\\source\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive (arbitrary cases)", + Expected: "sRc: > source", + Home: homeBillWindows, + PWD: "sRc:\\source\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows registry drive", + Expected: "\uf013 > f > magnetic:test", + Home: homeBillWindows, + PWD: "HKLM:\\SOFTWARE\\magnetic:test\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows registry drive case sensitive", + Expected: "\uf013 > f > magnetic:TOAST", + Home: homeBillWindows, + PWD: "HKLM:\\SOFTWARE\\magnetic:TOAST\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Unix outside home", + Expected: "mnt > f > f > location", + Home: homeJan, + PWD: "/mnt/go/test/location", + PathSeparator: "/", + }, + { + Case: "Unix inside home", + Expected: "~ > f > f > location", + Home: homeJan, + PWD: homeJan + "/docs/jan/location", + PathSeparator: "/", + }, + { + Case: "Unix outside home zero levels", + Expected: "mnt > location", + Home: homeJan, + PWD: "/mnt/location", + PathSeparator: "/", + }, + { + Case: "Unix outside home one level", + Expected: "mnt > f > location", + Home: homeJan, + PWD: "/mnt/folder/location", + PathSeparator: "/", + }, } for _, tc := range cases { @@ -587,7 +690,7 @@ func TestAgnosterPath(t *testing.T) { //nolint:dupl env.On("Home").Return(tc.Home) env.On("PathSeparator").Return(tc.PathSeparator) env.On("Pwd").Return(tc.PWD) - env.On("GOOS").Return("") + env.On("GOOS").Return(tc.GOOS) args := &environment.Flags{ PSWD: tc.PWD, } @@ -611,23 +714,125 @@ func TestAgnosterLeftPath(t *testing.T) { //nolint:dupl Expected string Home string PWD string + GOOS string PathSeparator string }{ - {Case: "Windows outside home", Expected: "C: > Program Files > f > f", Home: homeBillWindows, PWD: "C:\\Program Files\\Go\\location", PathSeparator: "\\"}, - {Case: "Windows inside home", Expected: "~ > Documents > f > f", Home: homeBillWindows, PWD: homeBillWindows + "\\Documents\\Bill\\location", PathSeparator: "\\"}, - {Case: "Windows inside home zero levels", Expected: "C: > location", Home: homeBillWindows, PWD: "C:\\location", PathSeparator: "\\"}, - {Case: "Windows inside home one level", Expected: "C: > Program Files > f", Home: homeBillWindows, PWD: "C:\\Program Files\\location", PathSeparator: "\\"}, - {Case: "Windows lower case drive letter", Expected: "C: > Windows", Home: homeBillWindows, PWD: "C:\\Windows\\", PathSeparator: "\\"}, - {Case: "Windows lower case drive letter (other)", Expected: "P: > Other", Home: homeBillWindows, PWD: "P:\\Other\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive", Expected: "some: > some", Home: homeBillWindows, PWD: "some:\\some\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive (ending with c)", Expected: "src: > source", Home: homeBillWindows, PWD: "src:\\source\\", PathSeparator: "\\"}, - {Case: "Windows lower word drive (arbitrary cases)", Expected: "sRc: > source", Home: homeBillWindows, PWD: "sRc:\\source\\", PathSeparator: "\\"}, - {Case: "Windows registry drive", Expected: "\uf013 > SOFTWARE > f", Home: homeBillWindows, PWD: "HKLM:\\SOFTWARE\\magnetic:test\\", PathSeparator: "\\"}, - {Case: "Windows registry drive case sensitive", Expected: "\uf013 > SOFTWARE > f", Home: homeBillWindows, PWD: "HKLM:\\SOFTWARE\\magnetic:TOAST\\", PathSeparator: "\\"}, - {Case: "Unix outside home", Expected: "mnt > go > f > f", Home: homeJan, PWD: "/mnt/go/test/location", PathSeparator: "/"}, - {Case: "Unix inside home", Expected: "~ > docs > f > f", Home: homeJan, PWD: homeJan + "/docs/jan/location", PathSeparator: "/"}, - {Case: "Unix outside home zero levels", Expected: "mnt > location", Home: homeJan, PWD: "/mnt/location", PathSeparator: "/"}, - {Case: "Unix outside home one level", Expected: "mnt > folder > f", Home: homeJan, PWD: "/mnt/folder/location", PathSeparator: "/"}, + { + Case: "Windows outside home", + Expected: "C: > Program Files > f > f", + Home: homeBillWindows, + PWD: "C:\\Program Files\\Go\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows inside home", + Expected: "~ > Documents > f > f", + Home: homeBillWindows, + PWD: homeBillWindows + "\\Documents\\Bill\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows inside home zero levels", + Expected: "C: > location", + Home: homeBillWindows, + PWD: "C:\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows inside home one level", + Expected: "C: > Program Files > f", + Home: homeBillWindows, + PWD: "C:\\Program Files\\location", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower case drive letter", + Expected: "C: > Windows", + Home: homeBillWindows, + PWD: "C:\\Windows\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower case drive letter (other)", + Expected: "P: > Other", + Home: homeBillWindows, + PWD: "P:\\Other\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive", + Expected: "some: > some", + Home: homeBillWindows, + PWD: "some:\\some\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive (ending with c)", + Expected: "src: > source", + Home: homeBillWindows, + PWD: "src:\\source\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows lower word drive (arbitrary cases)", + Expected: "sRc: > source", + Home: homeBillWindows, + PWD: "sRc:\\source\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows registry drive", + Expected: "\uf013 > SOFTWARE > f", + Home: homeBillWindows, + PWD: "HKLM:\\SOFTWARE\\magnetic:test\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Windows registry drive case sensitive", + Expected: "\uf013 > SOFTWARE > f", + Home: homeBillWindows, + PWD: "HKLM:\\SOFTWARE\\magnetic:TOAST\\", + GOOS: environment.WINDOWS, + PathSeparator: "\\", + }, + { + Case: "Unix outside home", + Expected: "mnt > go > f > f", + Home: homeJan, + PWD: "/mnt/go/test/location", + PathSeparator: "/", + }, + { + Case: "Unix inside home", + Expected: "~ > docs > f > f", + Home: homeJan, + PWD: homeJan + "/docs/jan/location", + PathSeparator: "/", + }, + { + Case: "Unix outside home zero levels", + Expected: "mnt > location", + Home: homeJan, + PWD: "/mnt/location", + PathSeparator: "/", + }, + { + Case: "Unix outside home one level", + Expected: "mnt > folder > f", + Home: homeJan, + PWD: "/mnt/folder/location", + PathSeparator: "/", + }, } for _, tc := range cases { @@ -635,7 +840,7 @@ func TestAgnosterLeftPath(t *testing.T) { //nolint:dupl env.On("Home").Return(tc.Home) env.On("PathSeparator").Return(tc.PathSeparator) env.On("Pwd").Return(tc.PWD) - env.On("GOOS").Return("") + env.On("GOOS").Return(tc.GOOS) args := &environment.Flags{ PSWD: tc.PWD, } diff --git a/src/segments/python_test.go b/src/segments/python_test.go index eb5fca64..d53b132b 100644 --- a/src/segments/python_test.go +++ b/src/segments/python_test.go @@ -79,6 +79,7 @@ func TestPythonTemplate(t *testing.T) { for _, tc := range cases { env := new(mock.MockedEnvironment) + env.On("GOOS").Return("") env.On("HasCommand", "python").Return(true) env.On("CommandPath", mock2.Anything).Return(tc.PythonPath) env.On("RunCommand", "python", []string{"--version"}).Return("Python 3.8.4", nil) @@ -118,6 +119,7 @@ func TestPythonPythonInContext(t *testing.T) { for _, tc := range cases { env := new(mock.MockedEnvironment) + env.On("GOOS").Return("") env.On("PathSeparator").Return("") env.On("Getenv", "VIRTUAL_ENV").Return(tc.VirtualEnvName) env.On("Getenv", "CONDA_ENV_PATH").Return("") diff --git a/src/segments/svn.go b/src/segments/svn.go index abd12fb7..ce730cc4 100644 --- a/src/segments/svn.go +++ b/src/segments/svn.go @@ -62,8 +62,7 @@ func (s *Svn) Enabled() bool { } func (s *Svn) shouldDisplay() bool { - // when in wsl/wsl2 and in a windows shared folder - // we must use Svn.exe and convert paths accordingly + // 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)) { @@ -80,7 +79,7 @@ func (s *Svn) shouldDisplay() bool { if Svndir.IsDir { s.workingDir = Svndir.Path s.rootDir = Svndir.Path - // convert the worktree file path to a windows one when in wsl 2 shared folder + // convert the worktree file path to a windows one when in a WSL shared folder s.realDir = strings.TrimSuffix(s.convertToWindowsPath(Svndir.Path), "/.svn") return true } @@ -89,10 +88,9 @@ func (s *Svn) shouldDisplay() bool { dirPointer := strings.Trim(s.env.FileContent(Svndir.Path), " \r\n") matches := regex.FindNamedRegexMatch(`^Svndir: (?P.*)$`, dirPointer) if matches != nil && matches["dir"] != "" { - // if we open a worktree file in a shared wsl2 folder, we have to convert it back + // if we open a worktree file in a WSL shared folder, we have to convert it back // to the mounted path s.workingDir = s.convertToLinuxPath(matches["dir"]) - return false } return false }