diff --git a/pkg/gui/controllers/helpers/repos_helper.go b/pkg/gui/controllers/helpers/repos_helper.go index 1fb8588e7..c280d7153 100644 --- a/pkg/gui/controllers/helpers/repos_helper.go +++ b/pkg/gui/controllers/helpers/repos_helper.go @@ -135,7 +135,10 @@ func (self *ReposHelper) CreateRecentReposMenu() error { LabelColumns: []string{ filepath.Base(path), style.FgCyan.Sprint(branchName), - style.FgMagenta.Sprint(path), + // The last segment of the path is already in the first column, + // so showing the directory that contains the repo is enough to + // tell repos with the same name apart. + style.FgMagenta.Sprint(utils.ContractTilde(filepath.Dir(path))), }, OnPress: func() error { // Check before clearing the stack, so a refused switch doesn't diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 0494bb035..d706fe719 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -120,3 +120,24 @@ func ExpandTilde(path string) string { } return filepath.Join(home, path[2:]) } + +// ContractTilde is the inverse of ExpandTilde: it replaces the current user's +// home directory at the start of a path with "~", so that paths can be shown +// in a shorter form. Paths outside the home directory are left untouched, as +// is the path if the home directory can't be determined. +func ContractTilde(path string) string { + home, err := os.UserHomeDir() + if err != nil { + return path + } + + if path == home { + return "~" + } + + if rest, found := strings.CutPrefix(path, home+string(filepath.Separator)); found { + return "~" + string(filepath.Separator) + rest + } + + return path +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 8304e7ba4..4d51575fc 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -125,3 +125,27 @@ func TestExpandTilde(t *testing.T) { }) } } + +func TestContractTilde(t *testing.T) { + home, err := os.UserHomeDir() + assert.NoError(t, err) + + scenarios := []struct { + name string + path string + expected string + }{ + {"home directory", home, "~"}, + {"path inside the home directory", filepath.Join(home, "worktrees"), filepath.Join("~", "worktrees")}, + {"path outside the home directory is untouched", filepath.Join("/absolute", "path"), filepath.Join("/absolute", "path")}, + {"path merely starting with the home directory's name is untouched", home + "-backup", home + "-backup"}, + {"relative path is untouched", filepath.Join("relative", "path"), filepath.Join("relative", "path")}, + {"empty string is untouched", "", ""}, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + assert.Equal(t, s.expected, ContractTilde(s.path)) + }) + } +}