Skip to content

Commit 165c25b

Browse files
feat: add mergeindicators option (gokcehan#2330)
* feat: add `mergeindicators` option * docs: update `CHANGELOG.md` * docs: add documentation * style: fix typo Co-authored-by: Joe Lim <[email protected]> --------- Co-authored-by: Joe Lim <[email protected]>
1 parent c1b9793 commit 165c25b

File tree

7 files changed

+67
-22
lines changed

7 files changed

+67
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
1818

1919
- A new server command `list` is added to print the IDs of all currently connected clients (#2314).
2020
- The `previewer` and `cleaner` scripts now have their `stderr` output logged (#2316).
21+
- A new option `mergeindicators` is added to reduce the gap before filenames, by merging tag and selection indicators into a single column (#2330).
2122

2223
### Fixed
2324

doc.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ The following options can be used to customize the behavior of lf:
287287
menufmt string (default "\033[0m")
288288
menuheaderfmt string (default "\033[1m")
289289
menuselectfmt string (default "\033[7m")
290+
mergeindicators bool (default false)
290291
mouse bool (default false)
291292
number bool (default false)
292293
numberfmt string (default "\033[33m")
@@ -1018,6 +1019,11 @@ Format string of the header row in the menu.
10181019

10191020
Format string of the currently selected item in the menu.
10201021

1022+
## mergeindicators (bool) (default false)
1023+
1024+
When `mergeindicators` is enabled, tag and selection indicators are drawn in a single column to reduce the gap before filenames.
1025+
If a file is both tagged and selected, the tag uses the selection format (e.g. `copyfmt`) instead of `tagfmt`.
1026+
10211027
## mouse (bool) (default false)
10221028

10231029
Send mouse events as input.

doc.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ The following options can be used to customize the behavior of lf:
299299
menufmt string (default "\033[0m")
300300
menuheaderfmt string (default "\033[1m")
301301
menuselectfmt string (default "\033[7m")
302+
mergeindicators bool (default false)
302303
mouse bool (default false)
303304
number bool (default false)
304305
numberfmt string (default "\033[33m")
@@ -1098,6 +1099,13 @@ menuselectfmt (string) (default \033[7m)
10981099

10991100
Format string of the currently selected item in the menu.
11001101

1102+
mergeindicators (bool) (default false)
1103+
1104+
When mergeindicators is enabled, tag and selection indicators are drawn
1105+
in a single column to reduce the gap before filenames. If a file is both
1106+
tagged and selected, the tag uses the selection format (e.g. copyfmt)
1107+
instead of tagfmt.
1108+
11011109
mouse (bool) (default false)
11021110

11031111
Send mouse events as input.

eval.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ func (e *setExpr) eval(app *app, _ []string) {
120120
err = applyBoolOpt(&gOpts.incfilter, e)
121121
case "incsearch", "noincsearch", "incsearch!":
122122
err = applyBoolOpt(&gOpts.incsearch, e)
123+
case "mergeindicators", "nomergeindicators", "mergeindicators!":
124+
err = applyBoolOpt(&gOpts.mergeindicators, e)
123125
case "mouse", "nomouse", "mouse!":
124126
err = applyBoolOpt(&gOpts.mouse, e)
125127
if err == nil {

lf.1

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.\" Automatically generated by Pandoc 3.7.0.2
22
.\"
3-
.TH "LF" "1" "2025\-12\-21" "r41" "DOCUMENTATION"
3+
.TH "LF" "1" "2025\-12\-31" "r41" "DOCUMENTATION"
44
.SH NAME
55
lf \- terminal file manager
66
.SH SYNOPSIS
@@ -311,6 +311,7 @@ infotimefmtold string (default \(aqJan _2 2006\(aq)
311311
menufmt string (default \(dq\(rs033[0m\(dq)
312312
menuheaderfmt string (default \(dq\(rs033[1m\(dq)
313313
menuselectfmt string (default \(dq\(rs033[7m\(dq)
314+
mergeindicators bool (default false)
314315
mouse bool (default false)
315316
number bool (default false)
316317
numberfmt string (default \(dq\(rs033[33m\(dq)
@@ -972,6 +973,12 @@ Format string of the menu.
972973
Format string of the header row in the menu.
973974
.SS menuselectfmt (string) (default \f[CR]\(rs033[7m\f[R])
974975
Format string of the currently selected item in the menu.
976+
.SS mergeindicators (bool) (default false)
977+
When \f[CR]mergeindicators\f[R] is enabled, tag and selection indicators
978+
are drawn in a single column to reduce the gap before filenames.
979+
If a file is both tagged and selected, the tag uses the selection format
980+
(e.g.
981+
\f[CR]copyfmt\f[R]) instead of \f[CR]tagfmt\f[R].
975982
.SS mouse (bool) (default false)
976983
Send mouse events as input.
977984
.SS number (bool) (default false)

opts.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ var gOpts struct {
7878
menufmt string
7979
menuheaderfmt string
8080
menuselectfmt string
81+
mergeindicators bool
8182
mouse bool
8283
number bool
8384
numberfmt string
@@ -240,6 +241,7 @@ func init() {
240241
gOpts.menufmt = "\033[0m"
241242
gOpts.menuheaderfmt = "\033[1m"
242243
gOpts.menuselectfmt = "\033[7m"
244+
gOpts.mergeindicators = false
243245
gOpts.mouse = false
244246
gOpts.number = false
245247
gOpts.numberfmt = "\033[33m"

ui.go

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,12 @@ func (win *win) printDir(ui *ui, dir *dir, context *dirContext, dirStyle *dirSty
419419
}
420420
}
421421

422+
indOff, tagOff, nameOff := lnwidth, lnwidth, lnwidth+1
423+
if !gOpts.mergeindicators {
424+
tagOff++
425+
nameOff++
426+
}
427+
422428
visualSelections := dir.visualSelections()
423429
for i, f := range dir.files[beg:end] {
424430
st := dirStyle.colors.get(f)
@@ -458,8 +464,18 @@ func (win *win) printDir(ui *ui, dir *dir, context *dirContext, dirStyle *dirSty
458464
fmtStr = gOpts.cutfmt
459465
}
460466
}
467+
468+
tag := " "
469+
if val, ok := context.tags[path]; ok && len(val) > 0 {
470+
tag = val
471+
}
472+
461473
if fmtStr != "" {
462-
win.print(ui.screen, lnwidth, i, parseEscapeSequence(fmtStr), " ")
474+
ind := " "
475+
if gOpts.mergeindicators {
476+
ind = tag
477+
}
478+
win.print(ui.screen, indOff, i, parseEscapeSequence(fmtStr), ind)
463479
}
464480

465481
// make space for select marker, and leave another space at the end
@@ -469,22 +485,21 @@ func (win *win) printDir(ui *ui, dir *dir, context *dirContext, dirStyle *dirSty
469485
maxWidth--
470486
}
471487

472-
tag := " "
473-
if val, ok := context.tags[path]; ok && len(val) > 0 {
474-
tag = val
475-
}
476-
477488
var icon []rune
478489
var iconDef iconDef
479490
if gOpts.icons {
480491
iconDef = dirStyle.icons.get(f)
481492
icon = slices.Concat([]rune(iconDef.icon), []rune{' '})
482493
}
483494

484-
// subtract space for tag and icon
485-
maxFilenameWidth := maxWidth - 1 - runeSliceWidth(icon)
495+
// subtract space for icon
496+
maxFilenameWidth := maxWidth - runeSliceWidth(icon)
497+
// subtract space for tag if not merged with selection marker
498+
if !gOpts.mergeindicators {
499+
maxFilenameWidth--
500+
}
486501

487-
info, custom, off := fileInfo(f, dir, userWidth, groupWidth, customWidth)
502+
info, custom, customOff := fileInfo(f, dir, userWidth, groupWidth, customWidth)
488503
infolen := len(info)
489504
showInfo := infolen > 0 && 2*infolen < maxWidth
490505
if showInfo {
@@ -498,7 +513,7 @@ func (win *win) printDir(ui *ui, dir *dir, context *dirContext, dirStyle *dirSty
498513

499514
if showInfo {
500515
filename = append(filename, []rune(info)...)
501-
off += lnwidth + 2 + runeSliceWidth(icon) + maxFilenameWidth
516+
customOff += nameOff + runeSliceWidth(icon) + maxFilenameWidth
502517
}
503518

504519
if i == dir.pos {
@@ -513,37 +528,41 @@ func (win *win) printDir(ui *ui, dir *dir, context *dirContext, dirStyle *dirSty
513528
}
514529

515530
// print tag separately as it can contain color escape sequences
516-
win.print(ui.screen, lnwidth+1, i, st, fmt.Sprintf(cursorFmt, tag))
531+
if !gOpts.mergeindicators || fmtStr == "" {
532+
win.print(ui.screen, tagOff, i, st, fmt.Sprintf(cursorFmt, tag))
533+
}
517534

518535
line := slices.Concat(icon, filename, []rune{' '})
519-
win.print(ui.screen, lnwidth+2, i, st, fmt.Sprintf(cursorFmt, string(line)))
536+
win.print(ui.screen, nameOff, i, st, fmt.Sprintf(cursorFmt, string(line)))
520537

521538
// print over the empty space we reserved for the custom info
522539
if showInfo && custom != "" {
523-
win.print(ui.screen, off, i, st, fmt.Sprintf(cursorFmt, stripTermSequence(custom)))
540+
win.print(ui.screen, customOff, i, st, fmt.Sprintf(cursorFmt, stripTermSequence(custom)))
524541
}
525542
} else {
526-
if tag == " " {
527-
win.print(ui.screen, lnwidth+1, i, st, " ")
528-
} else {
529-
tagStr := fmt.Sprintf(optionToFmtstr(gOpts.tagfmt), tag)
530-
win.print(ui.screen, lnwidth+1, i, tcell.StyleDefault, tagStr)
543+
if !gOpts.mergeindicators || fmtStr == "" {
544+
if tag == " " {
545+
win.print(ui.screen, tagOff, i, st, " ")
546+
} else {
547+
tagStr := fmt.Sprintf(optionToFmtstr(gOpts.tagfmt), tag)
548+
win.print(ui.screen, tagOff, i, tcell.StyleDefault, tagStr)
549+
}
531550
}
532551

533552
if len(icon) > 0 {
534553
iconStyle := st
535554
if iconDef.hasStyle {
536555
iconStyle = iconDef.style
537556
}
538-
win.print(ui.screen, lnwidth+2, i, iconStyle, string(icon))
557+
win.print(ui.screen, nameOff, i, iconStyle, string(icon))
539558
}
540559

541560
line := slices.Concat(filename, []rune{' '})
542-
win.print(ui.screen, lnwidth+2+runeSliceWidth(icon), i, st, string(line))
561+
win.print(ui.screen, nameOff+runeSliceWidth(icon), i, st, string(line))
543562

544563
// print over the empty space we reserved for the custom info
545564
if showInfo && custom != "" {
546-
win.print(ui.screen, off, i, st, custom)
565+
win.print(ui.screen, customOff, i, st, custom)
547566
}
548567
}
549568
}

0 commit comments

Comments
 (0)