From 2f7b93a487b7b7600e795184f11949bc15571e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Rainone?= <476650+arl@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:09:23 +0100 Subject: [PATCH] Issue 60/ellipsis (#85) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Ellipsis option, defaults to … * Modify Test_truncate * Change default layout (remove .. between branch and remote) * README.md format * README.md: document ellipsis Fixes #60 * Update golden file and fix tests Since the default config has changed the golden file must be updated. --- README.md | 10 +- testdata/default.output.txt | 2 +- tmux/formater.go | 33 +++--- tmux/formater_test.go | 207 ++++++++++++++++++++++-------------- 4 files changed, 150 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index ea2a255..e6f2a15 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ - [Prerequisites](#prerequisites) - [Installing](#installing) - [Binary release](#binary-release) - - [Homebrew (tap) macOS and linux, amd64 and arm64](#homebrew-tap-macos-and-linux-amd64-and-arm64) + - [Homebrew tap (macOS and linux) (amd64 and arm64)](#homebrew-tap-macos-and-linux-amd64-and-arm64) - [AUR](#aur) - [From source](#from-source) - [Getting started](#getting-started) @@ -132,6 +132,7 @@ tmux: options: branch_max_len: 0 branch_trim: right + ellipsis: … ``` First, save the default configuration to a new file: @@ -261,10 +262,11 @@ layout: [branch, "|", flags, "|", stats] This is the list of additional configuration `options`: -| Option | Description | Default | -| :--------------- | :--------------------------------------------------------- | :----------------- | -| `branch_max_len` | Maximum displayed length for local and remote branch names | `0` (no limit) | +| Option | Description | Default | +| :--------------- | :--------------------------------------------------------- | :----------------: | +| `branch_max_len` | Maximum displayed length for local and remote branch names | `0` (no limit) | | `branch_trim` | Trim left or right end of the branch (`right` or `left`) | `right` (trailing) | +| `ellipsis` | Character to show branch name has been truncated | `…` | ## Troubleshooting diff --git a/testdata/default.output.txt b/testdata/default.output.txt index bfefa48..126c0b6 100644 --- a/testdata/default.output.txt +++ b/testdata/default.output.txt @@ -51,4 +51,4 @@ with some different content -- empty_file -- -- output.golden -- -#[fg=default]#[fg=default]#[fg=white,bold]⎇ #[fg=default]#[fg=white,bold]main#[fg=default]..#[fg=default] - #[fg=default]#[fg=green,bold]● 1 #[fg=red,bold]✚ 1 #[fg=cyan,bold]⚑ 1 #[fg=magenta,bold]… 2 \ No newline at end of file +#[fg=default]#[fg=default]#[fg=white,bold]⎇ #[fg=default]#[fg=white,bold]main#[fg=default] #[fg=default] - #[fg=default]#[fg=green,bold]● 1 #[fg=red,bold]✚ 1 #[fg=cyan,bold]⚑ 1 #[fg=magenta,bold]… 2 diff --git a/tmux/formater.go b/tmux/formater.go index 75404a2..5749ffc 100644 --- a/tmux/formater.go +++ b/tmux/formater.go @@ -88,6 +88,7 @@ func (d *direction) UnmarshalYAML(value *yaml.Node) error { type options struct { BranchMaxLen int `yaml:"branch_max_len"` BranchTrim direction `yaml:"branch_trim"` + Ellipsis string `yaml:"ellipsis"` } // DefaultCfg is the default tmux configuration. @@ -103,7 +104,6 @@ var DefaultCfg = Config{ Ahead: "↑·", Behind: "↓·", HashPrefix: ":", - Insertions: "Σ", Deletions: "Δ", }, @@ -122,10 +122,11 @@ var DefaultCfg = Config{ Insertions: "#[fg=green]", Deletions: "#[fg=red]", }, - Layout: []string{"branch", "..", "remote-branch", "divergence", " - ", "flags"}, + Layout: []string{"branch", " ", "remote-branch", "divergence", " - ", "flags"}, Options: options{ BranchMaxLen: 0, BranchTrim: dirRight, + Ellipsis: "…", }, } @@ -136,25 +137,27 @@ type Formater struct { st *gitstatus.Status } -// truncate returns s, truncated so that it is no more than max characters long. +// truncate returns s, truncated so that it is no more than max runes long. // Depending on the provided direction, truncation is performed right or left. -// If max is zero, negative or greater than the number of rnues in s, truncate -// just returns s. However, if truncation is applied, then the last 3 chars (or -// 3 first, depending on provided direction) are replaced with "...". +// If s is returned truncated, the truncated part is replaced with the +// 'ellipsis' string. +// +// If max is zero, negative or greater than the number of runes in s, truncate +// just returns s. // -// NOTE: If max is lower than 3, in other words if we can't even have ellispis, -// then truncate just truncates the maximum number of characters, without -// bothering with ellipsis. -func truncate(s string, max int, dir direction) string { +// NOTE: If max is lower than len(ellipsis), in other words it we're not even +// allowed to just return the ellipsis string, then we just return the maximum +// number of runes we can, without inserting ellpisis. +func truncate(s, ellipsis string, max int, dir direction) string { slen := utf8.RuneCountInString(s) if max <= 0 || slen <= max { return s } runes := []rune(s) - ell := []rune("...") + ell := []rune(ellipsis) - if max < 3 { + if max < len(ellipsis) { ell = nil // Just truncate s since even ellipsis don't fit. } @@ -176,7 +179,7 @@ func (f *Formater) Format(w io.Writer, st *gitstatus.Status) error { // overall working tree state if f.st.IsInitial { - branch := truncate(f.st.LocalBranch, f.Options.BranchMaxLen, f.Options.BranchTrim) + branch := truncate(f.st.LocalBranch, f.Options.Ellipsis, f.Options.BranchMaxLen, f.Options.BranchTrim) fmt.Fprintf(w, "%s%s [no commits yet]", f.Styles.Branch, branch) f.flags() _, err := f.b.WriteTo(w) @@ -245,7 +248,7 @@ func (f *Formater) remoteBranch() { f.clear() - branch := truncate(f.st.RemoteBranch, f.Options.BranchMaxLen, f.Options.BranchTrim) + branch := truncate(f.st.RemoteBranch, f.Options.Ellipsis, f.Options.BranchMaxLen, f.Options.BranchTrim) fmt.Fprintf(&f.b, "%s%s", f.Styles.Remote, branch) } @@ -280,7 +283,7 @@ func (f *Formater) currentRef() { return } - branch := truncate(f.st.LocalBranch, f.Options.BranchMaxLen, f.Options.BranchTrim) + branch := truncate(f.st.LocalBranch, f.Options.Ellipsis, f.Options.BranchMaxLen, f.Options.BranchTrim) fmt.Fprintf(&f.b, "%s%s", f.Styles.Branch, branch) } diff --git a/tmux/formater_test.go b/tmux/formater_test.go index def5120..d4b21bc 100644 --- a/tmux/formater_test.go +++ b/tmux/formater_test.go @@ -199,128 +199,168 @@ func TestDivergence(t *testing.T) { } } -func TestTruncate(t *testing.T) { +func Test_truncate(t *testing.T) { tests := []struct { - s string - max int - dir direction - want string + s string + max int + ellipsis string + dir direction + want string }{ /* trim right */ { - s: "br", - max: 1, - dir: dirRight, - want: "b", + s: "br", + ellipsis: "...", + max: 1, + dir: dirRight, + want: "b", }, { - s: "br", - max: 3, - dir: dirRight, - want: "br", + s: "br", + ellipsis: "...", + max: 3, + dir: dirRight, + want: "br", }, { - s: "super-long-branch", - max: 3, - dir: dirRight, - want: "...", + s: "super-long-branch", + ellipsis: "...", + max: 3, + dir: dirRight, + want: "...", }, { - s: "super-long-branch", - max: 15, - dir: dirRight, - want: "super-long-b...", + s: "super-long-branch", + ellipsis: "...", + max: 15, + dir: dirRight, + want: "super-long-b...", }, { - s: "super-long-branch", - max: 17, - dir: dirRight, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 17, + dir: dirRight, + want: "super-long-branch", }, { - s: "长長的-树樹枝", - max: 6, - dir: dirRight, - want: "长長的...", + s: "super-long-branch", + ellipsis: "…", + max: 17, + dir: dirRight, + want: "super-long-branch", }, { - s: "super-long-branch", - max: 32, - dir: dirRight, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "…", + max: 15, + dir: dirRight, + want: "super-long-bra…", }, { - s: "super-long-branch", - max: 0, - dir: dirRight, - want: "super-long-branch", + s: "长長的-树樹枝", + ellipsis: "...", + max: 6, + dir: dirRight, + want: "长長的...", }, { - s: "super-long-branch", - max: -1, - dir: dirRight, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 32, + dir: dirRight, + want: "super-long-branch", + }, + { + s: "super-long-branch", + ellipsis: "...", + max: 0, + dir: dirRight, + want: "super-long-branch", + }, + { + s: "super-long-branch", + ellipsis: "...", + max: -1, + dir: dirRight, + want: "super-long-branch", }, /* trim left */ { - s: "br", - max: 1, - dir: dirLeft, - want: "r", + s: "br", + ellipsis: "...", + max: 1, + dir: dirLeft, + want: "r", }, { - s: "br", - max: 3, - dir: dirLeft, - want: "br", + s: "br", + ellipsis: "", + max: 1, + dir: dirLeft, + want: "r", }, { - s: "super-long-branch", - max: 3, - dir: dirLeft, - want: "...", + s: "br", + ellipsis: "...", + max: 3, + dir: dirLeft, + want: "br", }, { - s: "super-long-branch", - max: 15, - dir: dirLeft, - want: "...-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 3, + dir: dirLeft, + want: "...", }, { - s: "super-long-branch", - max: 17, - dir: dirLeft, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 15, + dir: dirLeft, + want: "...-long-branch", }, { - s: "长長的-树樹枝", - max: 6, - dir: dirLeft, - want: "...树樹枝", + s: "super-long-branch", + ellipsis: "...", + max: 17, + dir: dirLeft, + want: "super-long-branch", }, { - s: "super-long-branch", - max: 32, - dir: dirLeft, - want: "super-long-branch", + s: "长長的-树樹枝", + ellipsis: "...", + max: 6, + dir: dirLeft, + want: "...树樹枝", }, { - s: "super-long-branch", - max: 0, - dir: dirLeft, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 32, + dir: dirLeft, + want: "super-long-branch", }, { - s: "super-long-branch", - max: -1, - dir: dirLeft, - want: "super-long-branch", + s: "super-long-branch", + ellipsis: "...", + max: 0, + dir: dirLeft, + want: "super-long-branch", + }, + { + s: "super-long-branch", + ellipsis: "...", + max: -1, + dir: dirLeft, + want: "super-long-branch", }, } for _, tt := range tests { t.Run("", func(t *testing.T) { - if got := truncate(tt.s, tt.max, tt.dir); got != tt.want { + if got := truncate(tt.s, tt.ellipsis, tt.max, tt.dir); got != tt.want { t.Errorf("truncate(%q, %d, %s) = %q, want %q", tt.s, tt.max, tt.dir, got, tt.want) } }) @@ -445,10 +485,11 @@ func TestFormat(t *testing.T) { symbols: symbols{ Branch: "SymbolBranch", }, - layout: []string{"branch", " ", "remote"}, + layout: []string{"branch", "/", "remote"}, options: options{ BranchMaxLen: 9, BranchTrim: dirRight, + Ellipsis: `…`, }, st: &gitstatus.Status{ Porcelain: gitstatus.Porcelain{ @@ -457,9 +498,9 @@ func TestFormat(t *testing.T) { }, }, want: "StyleClear" + "StyleBranch" + "SymbolBranch" + - "StyleClear" + "StyleBranch" + "branch..." + - "StyleClear" + " " + - "StyleClear" + "StyleRemote" + "remote...", + "StyleClear" + "StyleBranch" + "branchNa…" + + "StyleClear" + "/" + + "StyleClear" + "StyleRemote" + "remote/b…", }, { name: "branch and remote, branch_max_len not zero and trim left", @@ -475,6 +516,7 @@ func TestFormat(t *testing.T) { options: options{ BranchMaxLen: 9, BranchTrim: dirLeft, + Ellipsis: "...", }, st: &gitstatus.Status{ Porcelain: gitstatus.Porcelain{ @@ -514,6 +556,7 @@ func TestFormat(t *testing.T) { if err := f.Format(io.Discard, tt.st); err != nil { t.Fatalf("Format error: %s", err) + return } f.format()