From 95fc3dbe2491f03145f40f91d8b16556dd5c8707 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Mon, 7 Sep 2026 19:08:19 +0200 Subject: [PATCH] Truncate the path column of the recent repos menu too The path column is the last one, so nothing pushes it aside; instead it runs off the right edge of the menu itself, and the reader has no way of telling that there is more to it. Give it a maximum width as well, sized so that the three columns and the spaces between them fill the menu at its widest. A path loses its middle rather than its end, because the directory that immediately contains the repo says more about where it is than the root of the tree does. The whole path, with the home directory still abbreviated, joins the names in the tooltip when it doesn't fit. Co-authored-by: Claude Opus 5 (1M context) --- pkg/gui/controllers/helpers/repos_helper.go | 43 +++++++++++++------ pkg/i18n/english.go | 2 + .../misc/recent_repos_with_long_names.go | 8 +++- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/pkg/gui/controllers/helpers/repos_helper.go b/pkg/gui/controllers/helpers/repos_helper.go index 19bdec135..463443137 100644 --- a/pkg/gui/controllers/helpers/repos_helper.go +++ b/pkg/gui/controllers/helpers/repos_helper.go @@ -103,29 +103,48 @@ func (self *ReposHelper) getCurrentBranch(path string) string { return self.c.Tr.BranchUnknown } -// The maximum width of the repo name and branch name columns of the recent -// repos menu. Without a limit, one long name pushes the path column off the -// right edge of the menu for every entry, because each column is padded to the -// width of its widest entry. -const recentReposColumnMaxWidth = 30 +// The maximum widths of the three columns of the recent repos menu. Each +// column is padded to the width of its widest entry, so without a limit one +// long entry pushes the columns after it off the right edge of the menu for +// every entry. The three widths plus the two spaces between the columns add up +// to the content width of a menu that is as wide as it gets, so that a whole +// row fits into it. +const ( + recentReposNameMaxWidth = 30 + recentReposBranchMaxWidth = 30 + // The menu's frame takes up two columns, and two more separate the three + // columns from each other. + recentReposPathMaxWidth = menuMaxWidth - 2 - 2 - recentReposNameMaxWidth - recentReposBranchMaxWidth +) func (self *ReposHelper) recentRepoMenuItem(path string, branchName string) *types.MenuItem { repoName := filepath.Base(path) - displayedRepoName := utils.TruncateWithEllipsis(repoName, recentReposColumnMaxWidth) + displayedRepoName := utils.TruncateWithEllipsis(repoName, recentReposNameMaxWidth) // The icon is part of the column, so it counts towards the maximum width. branchColumn := branchName if icons.IsIconEnabled() { branchColumn = icons.BRANCH_ICON + " " + branchName } - displayedBranchColumn := utils.TruncateWithEllipsis(branchColumn, recentReposColumnMaxWidth) + displayedBranchColumn := utils.TruncateWithEllipsis(branchColumn, recentReposBranchMaxWidth) + + // 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. Its beginning and its end are both worth seeing, so it + // loses its middle rather than its end when it doesn't fit. + dir := utils.ContractTilde(filepath.Dir(path)) + displayedDir := utils.TruncateWithEllipsisInMiddle(dir, recentReposPathMaxWidth) // Spell out whatever the columns show in truncated form. The labels are // padded to the same width no matter which of them appear, so that the // tooltip doesn't shift around as the selection moves. tooltipLines := []string{} labelWidth := utils.MaxFn( - []string{self.c.Tr.RecentReposRepoLabel, self.c.Tr.RecentReposBranchLabel}, + []string{ + self.c.Tr.RecentReposRepoLabel, + self.c.Tr.RecentReposBranchLabel, + self.c.Tr.RecentReposPathLabel, + }, utils.StringWidth) addTooltipLine := func(label string, value string) { tooltipLines = append(tooltipLines, @@ -138,15 +157,15 @@ func (self *ReposHelper) recentRepoMenuItem(path string, branchName string) *typ if displayedBranchColumn != branchColumn { addTooltipLine(self.c.Tr.RecentReposBranchLabel, branchName) } + if displayedDir != dir { + addTooltipLine(self.c.Tr.RecentReposPathLabel, utils.ContractTilde(path)) + } return &types.MenuItem{ LabelColumns: []string{ displayedRepoName, style.FgCyan.Sprint(displayedBranchColumn), - // 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))), + style.FgMagenta.Sprint(displayedDir), }, // Filtering matches the full text, including the parts that the columns // above truncate or leave out. diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 918247902..0f33d9bc0 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -334,6 +334,7 @@ type TranslationSet struct { RecentRepos string RecentReposRepoLabel string RecentReposBranchLabel string + RecentReposPathLabel string MergeOptionsTitle string RebaseOptionsTitle string CherryPickOptionsTitle string @@ -1494,6 +1495,7 @@ func EnglishTranslationSet() *TranslationSet { RecentRepos: "Recent repositories", RecentReposRepoLabel: "Repo:", RecentReposBranchLabel: "Branch:", + RecentReposPathLabel: "Path:", MergeOptionsTitle: "Merge options", RebaseOptionsTitle: "Rebase options", CherryPickOptionsTitle: "Cherry-pick options", diff --git a/pkg/integration/tests/misc/recent_repos_with_long_names.go b/pkg/integration/tests/misc/recent_repos_with_long_names.go index 7396faff3..7369dd367 100644 --- a/pkg/integration/tests/misc/recent_repos_with_long_names.go +++ b/pkg/integration/tests/misc/recent_repos_with_long_names.go @@ -29,10 +29,14 @@ var RecentReposWithLongNames = NewIntegrationTest(NewIntegrationTestArgs{ t.ExpectPopup().Menu(). Title(Equals("Recent repositories")). Lines( - Contains("repo-with-a-name-that-is-far-… branch-with-a-name-that-is-to…").IsSelected(), + // The repo lives in the test's own directory, so its path is + // long enough to lose its middle + Contains("repo-with-a-name-that-is-far-… branch-with-a-name-that-is-to… ~/_results/mi…names/actual").IsSelected(), Contains("Cancel"), ). - Tooltip(Equals("Repo: repo-with-a-name-that-is-far-too-long\nBranch: branch-with-a-name-that-is-too-long")). + Tooltip(Contains("Repo: repo-with-a-name-that-is-far-too-long\n" + + "Branch: branch-with-a-name-that-is-too-long\n" + + "Path: ~/")). // Filtering matches the full names, including the part that the // menu truncates Filter("too-long").