Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flexible filename truncation #1029

Merged
merged 8 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ var (
"infotimefmtnew",
"infotimefmtold",
"truncatechar",
"truncatepct",
}
)

Expand Down
20 changes: 20 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ The following options can be used to customize the behavior of lf:
tempmarks string (default '')
timefmt string (default 'Mon Jan _2 15:04:05 2006')
truncatechar string (default '~')
truncatepct int (default 100)
waitmsg string (default 'Press any key to continue')
wrapscan bool (default true)
wrapscroll bool (default false)
Expand Down Expand Up @@ -941,6 +942,25 @@ Format string of the file modification time shown in the bottom line.

Truncate character shown at the end when the file name does not fit to the pane.

truncatepct int (default 100)

When a filename is too long to be shown completely, the available space is
partitioned in two pieces. truncatepct defines a fraction (in percent
between 0 and 100) for the size of the first piece, which will show the
beginning of the filename. The second piece will show the end of the filename
and will use the rest of the available space. Both pieces are separated by the
truncation character (truncatechar).
A value of 100 will only show the beginning of the filename,
while a value of 0 will only show the end of the filename, e.g.:

- `set truncatepct 100` -> "very-long-filename-tr~" (default)

- `set truncatepct 50` -> "very-long-f~-truncated"

- `set truncatepct 0` -> "~ng-filename-truncated"

.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix! One last nit: could you remove this period and make sure there's one blank line before the next item?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, well.
After removing the "100 does not change the behaviour" line after the examples list, I saw that go fmt would re-format the next line with waitmsg (which I don't want to touch in this change) unnecessarily (i.e. convert the leading tab to space). That's why I introduced the line with the single dot, to work around this unexpected formatting behaviour.
However, I tried other things and found that removing the spaces before the list items works too (i.e. go fmt doesn't touch the next item with waitmsg)
See the additional commit.


waitmsg string (default 'Press any key to continue')

String shown after commands of shell-wait type.
Expand Down
19 changes: 19 additions & 0 deletions docstring.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,17 @@ func (e *setExpr) eval(app *app, args []string) {
}

gOpts.truncatechar = e.val
case "truncatepct":
n, err := strconv.Atoi(e.val)
if err != nil {
app.ui.echoerrf("truncatepct: %s", err)
return
}
if n < 0 || n > 100 {
app.ui.echoerrf("truncatepct: must be between 0 and 100 (both inclusive), got %d", n)
return
}
gOpts.truncatepct = n
case "waitmsg":
gOpts.waitmsg = e.val
case "wrapscan":
Expand Down
21 changes: 21 additions & 0 deletions lf.1
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ The following options can be used to customize the behavior of lf:
tempmarks string (default '')
timefmt string (default 'Mon Jan _2 15:04:05 2006')
truncatechar string (default '~')
truncatepct int (default 100)
waitmsg string (default 'Press any key to continue')
wrapscan bool (default true)
wrapscroll bool (default false)
Expand Down Expand Up @@ -1119,6 +1120,26 @@ Format string of the file modification time shown in the bottom line.
.PP
Truncate character shown at the end when the file name does not fit to the pane.
.PP
.EX
truncatepct int (default 100)
.EE
.PP
When a filename is too long to be shown completely, the available space is partitioned in two pieces. truncatepct defines a fraction (in percent between 0 and 100) for the size of the first piece, which will show the beginning of the filename. The second piece will show the end of the filename and will use the rest of the available space. Both pieces are separated by the truncation character (truncatechar). A value of 100 will only show the beginning of the filename, while a value of 0 will only show the end of the filename, e.g.:
.PP
.EX
- `set truncatepct 100` -> "very-long-filename-tr~" (default)
.EE
.PP
.EX
- `set truncatepct 50` -> "very-long-f~-truncated"
.EE
.PP
.EX
- `set truncatepct 0` -> "~ng-filename-truncated"
.EE
.PP
.
.PP
.EX
waitmsg string (default 'Press any key to continue')
.EE
Expand Down
13 changes: 13 additions & 0 deletions misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ func runeSliceWidthRange(rs []rune, beg, end int) []rune {
return rs[b:]
}

// Returns the last runes of `rs` that take up at most `maxWidth` space.
func runeSliceWidthLastRange(rs []rune, maxWidth int) []rune {
lastWidth := 0
for i := len(rs) - 1; i >= 0; i-- {
w := runewidth.RuneWidth(rs[i])
if lastWidth+w > maxWidth {
return rs[i+1:]
}
lastWidth += w
}
return rs
}

// This function is used to escape whitespaces and special characters with
// backlashes in a given string.
func escape(s string) string {
Expand Down
41 changes: 41 additions & 0 deletions misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,47 @@ func TestRuneSliceWidthRange(t *testing.T) {
}
}

func TestRuneSliceWidthLastRange(t *testing.T) {
tests := []struct {
rs []rune
maxWidth int
exp []rune
}{
{[]rune{}, 0, []rune{}},
{[]rune{}, 1, []rune{}},
{[]rune{'a', 'ı', 'b', ' '}, 0, []rune{}},
{[]rune{'a', 'ı', 'b', ' '}, 1, []rune{' '}},
{[]rune{'a', 'ı', 'b', ' '}, 2, []rune{'b', ' '}},
{[]rune{'a', 'ı', 'b', ' '}, 3, []rune{'ı', 'b', ' '}},
{[]rune{'a', 'ı', 'b', ' '}, 4, []rune{'a', 'ı', 'b', ' '}},
{[]rune{'a', 'ı', 'b', ' '}, 5, []rune{'a', 'ı', 'b', ' '}},
{[]rune{'世', '界', '世', '界'}, 0, []rune{}},
{[]rune{'世', '界', '世', '界'}, 1, []rune{}},
{[]rune{'世', '界', '世', '界'}, 2, []rune{'界'}},
{[]rune{'世', '界', '世', '界'}, 3, []rune{'界'}},
{[]rune{'世', '界', '世', '界'}, 4, []rune{'世', '界'}},
{[]rune{'世', '界', '世', '界'}, 5, []rune{'世', '界'}},
{[]rune{'世', '界', '世', '界'}, 6, []rune{'界', '世', '界'}},
{[]rune{'世', '界', '世', '界'}, 7, []rune{'界', '世', '界'}},
{[]rune{'世', '界', '世', '界'}, 8, []rune{'世', '界', '世', '界'}},
{[]rune{'世', '界', '世', '界'}, 9, []rune{'世', '界', '世', '界'}},
{[]rune{'世', 'a', '界', 'ı'}, 0, []rune{}},
{[]rune{'世', 'a', '界', 'ı'}, 1, []rune{'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 2, []rune{'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 3, []rune{'界', 'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 4, []rune{'a', '界', 'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 5, []rune{'a', '界', 'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 6, []rune{'世', 'a', '界', 'ı'}},
{[]rune{'世', 'a', '界', 'ı'}, 7, []rune{'世', 'a', '界', 'ı'}},
}

for _, test := range tests {
if got := runeSliceWidthLastRange(test.rs, test.maxWidth); !reflect.DeepEqual(got, test.exp) {
t.Errorf("at input '%v' expected '%v' but got '%v'", test.rs, test.exp, got)
}
}
}

func TestEscape(t *testing.T) {
tests := []struct {
s string
Expand Down
2 changes: 2 additions & 0 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var gOpts struct {
infotimefmtnew string
infotimefmtold string
truncatechar string
truncatepct int
ratios []int
hiddenfiles []string
history bool
Expand Down Expand Up @@ -133,6 +134,7 @@ func init() {
gOpts.infotimefmtnew = "Jan _2 15:04"
gOpts.infotimefmtold = "Jan _2 2006"
gOpts.truncatechar = "~"
gOpts.truncatepct = 100
gOpts.ratios = []int{1, 2, 3}
gOpts.hiddenfiles = []string{".*"}
gOpts.history = true
Expand Down
5 changes: 4 additions & 1 deletion ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,8 +468,11 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, context *dirContext, dir

filename := []rune(f.Name())
if runeSliceWidth(filename) > maxFilenameWidth {
filename = runeSliceWidthRange(filename, 0, maxFilenameWidth-1)
truncatePos := (maxFilenameWidth - 1) * gOpts.truncatepct / 100
lastPart := runeSliceWidthLastRange(filename, maxFilenameWidth-truncatePos-1)
filename = runeSliceWidthRange(filename, 0, truncatePos)
filename = append(filename, []rune(gOpts.truncatechar)...)
filename = append(filename, lastPart...)
}
for i := runeSliceWidth(filename); i < maxFilenameWidth; i++ {
filename = append(filename, ' ')
Expand Down