From 439fb038e52348cc14b55efbe6f2c37a928e0fd5 Mon Sep 17 00:00:00 2001 From: Joe Lim <50560759+joelim-work@users.noreply.github.com> Date: Sun, 3 Sep 2023 13:31:50 +1000 Subject: [PATCH] Add `rulerfmt` option for improved ruler customization (#1386) * Implement basic rulerfmt mechanism * Add deprecation warning for ruler option * Implement basic placeholders * Add placeholder for number of hidden files * Implement placeholders for options * Add documentation --- complete.go | 1 + doc.go | 10 +++++ docstring.go | 18 +++++++++ eval.go | 4 ++ lf.1 | 9 ++++- misc.go | 1 + opts.go | 4 +- ui.go | 101 ++++++++++++++++++++++++++++++++++++++------------- 8 files changed, 120 insertions(+), 28 deletions(-) diff --git a/complete.go b/complete.go index b43a4248..e31a3caa 100644 --- a/complete.go +++ b/complete.go @@ -179,6 +179,7 @@ var ( "noreverse", "reverse!", "ruler", + "rulerfmt", "preserve", "sixel", "nosixel", diff --git a/doc.go b/doc.go index 4e43332c..cec820a1 100644 --- a/doc.go +++ b/doc.go @@ -159,6 +159,7 @@ The following options can be used to customize the behavior of lf: relativenumber bool (default false) reverse bool (default false) ruler []string (default 'acc:progress:selection:filter:ind') + rulerfmt string (default "%a |%p |\033[7;31m %m \033[0m |\033[7;33m %c \033[0m |\033[7;35m %s \033[0m |\033[7;34m %f \033[0m |%i/%t") scrolloff int (default 0) selmode string (default 'all') shell string (default 'sh' for Unix and 'cmd' for Windows) @@ -875,6 +876,7 @@ Reverse the direction of sort. ruler []string (default 'acc:progress:selection:filter:ind') +This option is deprecated in favor of using the `rulerfmt` option (see below). List of information shown in status line ruler. Currently supported information types are 'acc', 'progress', 'selection', 'filter', 'ind', 'df' and names starting with 'lf_'. `acc` shows the pressed keys (e.g. for bindings with multiple key presses or counts given to bindings). @@ -886,6 +888,14 @@ Currently supported information types are 'acc', 'progress', 'selection', 'filte Names starting with `lf_` show the value of environment variables exported by lf. This is useful for displaying the current settings (e.g. `lf_selmode` displays the current setting for the `selmode` option). User defined options starting with `lf_user_` are also supported, so it is possible to display information set from external sources. + rulerfmt string (default "%a |%p |\033[7;31m %m \033[0m |\033[7;33m %c \033[0m |\033[7;35m %s \033[0m |\033[7;34m %f \033[0m |%i/%t") + +Format string of the ruler shown in the bottom right corner. +Special expansions are provided, '%a' as the pressed keys, '%p' as the progress of file operations, '%m' as the number of files to be cut (moved), '%c' as the number of files to be copied, '%s' as the number of selected files, '%f' as the filter, '%i' as the position of the cursor, '%t' as the number of files shown in the current directory, '%h' as the number of files hidden in the current directory, and '%d' as the amount of free disk space remaining. +Additional expansions are provided for environment variables exported by lf, in the form `%{lf_}` (e.g. `%{lf_selmode}`). This is useful for displaying the current settings. +Expansions are also provided for user defined options, in the form `%{lf_user_}` (e.g. `%{lf_user_foo}`). +The `|` character splits the format string into sections. Any section containing a failed expansion (result is a blank string) is discarded and not shown. + selmode string (default 'all') Selection mode for commands. diff --git a/docstring.go b/docstring.go index 4facbb20..36893927 100644 --- a/docstring.go +++ b/docstring.go @@ -162,6 +162,7 @@ The following options can be used to customize the behavior of lf: relativenumber bool (default false) reverse bool (default false) ruler []string (default 'acc:progress:selection:filter:ind') + rulerfmt string (default "%a |%p |\033[7;31m %m \033[0m |\033[7;33m %c \033[0m |\033[7;35m %s \033[0m |\033[7;34m %f \033[0m |%i/%t") scrolloff int (default 0) selmode string (default 'all') shell string (default 'sh' for Unix and 'cmd' for Windows) @@ -938,6 +939,7 @@ Reverse the direction of sort. ruler []string (default 'acc:progress:selection:filter:ind') +This option is deprecated in favor of using the 'rulerfmt' option (see below). List of information shown in status line ruler. Currently supported information types are 'acc', 'progress', 'selection', 'filter', 'ind', 'df' and names starting with 'lf_'. 'acc' shows the pressed keys (e.g. for bindings with @@ -953,6 +955,22 @@ for the 'selmode' option). User defined options starting with 'lf_user_' are also supported, so it is possible to display information set from external sources. + rulerfmt string (default "%a |%p |\033[7;31m %m \033[0m |\033[7;33m %c \033[0m |\033[7;35m %s \033[0m |\033[7;34m %f \033[0m |%i/%t") + +Format string of the ruler shown in the bottom right corner. Special expansions +are provided, '%a' as the pressed keys, '%p' as the progress of file operations, +'%m' as the number of files to be cut (moved), '%c' as the number of files to +be copied, '%s' as the number of selected files, '%f' as the filter, '%i' as +the position of the cursor, '%t' as the number of files shown in the current +directory, '%h' as the number of files hidden in the current directory, and '%d' +as the amount of free disk space remaining. Additional expansions are provided +for environment variables exported by lf, in the form '%{lf_}' (e.g. +'%{lf_selmode}'). This is useful for displaying the current settings. Expansions +are also provided for user defined options, in the form '%{lf_user_}' +(e.g. '%{lf_user_foo}'). The '|' character splits the format string into +sections. Any section containing a failed expansion (result is a blank string) +is discarded and not shown. + selmode string (default 'all') Selection mode for commands. When set to 'all' it will use the selected files diff --git a/eval.go b/eval.go index 101f92f8..0c38c793 100644 --- a/eval.go +++ b/eval.go @@ -503,6 +503,10 @@ func (e *setExpr) eval(app *app, args []string) { } } gOpts.ruler = toks + app.ui.echoerr("option 'ruler' is deprecated, use 'rulerfmt' instead") + return + case "rulerfmt": + gOpts.rulerfmt = e.val case "preserve": if e.val == "" { gOpts.preserve = nil diff --git a/lf.1 b/lf.1 index 4d238fa8..c325374a 100644 --- a/lf.1 +++ b/lf.1 @@ -178,6 +178,7 @@ The following options can be used to customize the behavior of lf: relativenumber bool (default false) reverse bool (default false) ruler []string (default 'acc:progress:selection:filter:ind') + rulerfmt string (default "%a |%p |\e033[7;31m %m \e033[0m |\e033[7;33m %c \e033[0m |\e033[7;35m %s \e033[0m |\e033[7;34m %f \e033[0m |%i/%t") scrolloff int (default 0) selmode string (default 'all') shell string (default 'sh' for Unix and 'cmd' for Windows) @@ -1052,7 +1053,13 @@ Reverse the direction of sort. ruler []string (default 'acc:progress:selection:filter:ind') .EE .PP -List of information shown in status line ruler. Currently supported information types are 'acc', 'progress', 'selection', 'filter', 'ind', 'df' and names starting with 'lf_'. `acc` shows the pressed keys (e.g. for bindings with multiple key presses or counts given to bindings). `progress` shows the progress of file operations (e.g. copying a large directory). `selection` shows the number of files that are selected, or designated for being cut/copied. `filter` shows 'F' if a filter is currently being applied. `ind` shows the current position of the cursor as well as the number of files in the current directory. `df` shows the amount of free disk space remaining. Names starting with `lf_` show the value of environment variables exported by lf. This is useful for displaying the current settings (e.g. `lf_selmode` displays the current setting for the `selmode` option). User defined options starting with `lf_user_` are also supported, so it is possible to display information set from external sources. +This option is deprecated in favor of using the `rulerfmt` option (see below). List of information shown in status line ruler. Currently supported information types are 'acc', 'progress', 'selection', 'filter', 'ind', 'df' and names starting with 'lf_'. `acc` shows the pressed keys (e.g. for bindings with multiple key presses or counts given to bindings). `progress` shows the progress of file operations (e.g. copying a large directory). `selection` shows the number of files that are selected, or designated for being cut/copied. `filter` shows 'F' if a filter is currently being applied. `ind` shows the current position of the cursor as well as the number of files in the current directory. `df` shows the amount of free disk space remaining. Names starting with `lf_` show the value of environment variables exported by lf. This is useful for displaying the current settings (e.g. `lf_selmode` displays the current setting for the `selmode` option). User defined options starting with `lf_user_` are also supported, so it is possible to display information set from external sources. +.PP +.EX + rulerfmt string (default "%a |%p |\e033[7;31m %m \e033[0m |\e033[7;33m %c \e033[0m |\e033[7;35m %s \e033[0m |\e033[7;34m %f \e033[0m |%i/%t") +.EE +.PP +Format string of the ruler shown in the bottom right corner. Special expansions are provided, '%a' as the pressed keys, '%p' as the progress of file operations, '%m' as the number of files to be cut (moved), '%c' as the number of files to be copied, '%s' as the number of selected files, '%f' as the filter, '%i' as the position of the cursor, '%t' as the number of files shown in the current directory, '%h' as the number of files hidden in the current directory, and '%d' as the amount of free disk space remaining. Additional expansions are provided for environment variables exported by lf, in the form `%{lf_}` (e.g. `%{lf_selmode}`). This is useful for displaying the current settings. Expansions are also provided for user defined options, in the form `%{lf_user_}` (e.g. `%{lf_user_foo}`). The `|` character splits the format string into sections. Any section containing a failed expansion (result is a blank string) is discarded and not shown. .PP .EX selmode string (default 'all') diff --git a/misc.go b/misc.go index 8199697f..b38dab2d 100644 --- a/misc.go +++ b/misc.go @@ -292,6 +292,7 @@ func naturalLess(s1, s2 string) bool { } var reModKey = regexp.MustCompile(`<(c|s|a)-(.+)>`) +var reRulerSub = regexp.MustCompile(`%[apmcsfithd]|%\{[^}]+\}`) var reWord = regexp.MustCompile(`(\pL|\pN)+`) var reWordBeg = regexp.MustCompile(`([^\pL\pN]|^)(\pL|\pN)`) diff --git a/opts.go b/opts.go index d04d305e..45bf3b60 100644 --- a/opts.go +++ b/opts.go @@ -83,6 +83,7 @@ var gOpts struct { history bool info []string ruler []string + rulerfmt string preserve []string shellopts []string keys map[string]expr @@ -232,7 +233,8 @@ func init() { gOpts.hiddenfiles = []string{".*"} gOpts.history = true gOpts.info = nil - gOpts.ruler = []string{"acc", "progress", "selection", "filter", "ind"} + gOpts.ruler = nil + gOpts.rulerfmt = "%a |%p |\033[7;31m %m \033[0m |\033[7;33m %c \033[0m |\033[7;35m %s \033[0m |\033[7;34m %f \033[0m |%i/%t" gOpts.preserve = []string{"mode"} gOpts.shellopts = nil gOpts.sortType = sortType{naturalSort, dirfirstSort} diff --git a/ui.go b/ui.go index 6861f7e8..0b12ec96 100644 --- a/ui.go +++ b/ui.go @@ -830,7 +830,7 @@ func formatRulerOpt(name string, val string) string { return val } -func (ui *ui) drawStatLine(nav *nav) { +func (ui *ui) drawRuler(nav *nav) { st := tcell.StyleDefault dir := nav.currDir() @@ -839,13 +839,14 @@ func (ui *ui) drawStatLine(nav *nav) { tot := len(dir.files) ind := min(dir.ind+1, tot) + hid := len(dir.allFiles) - tot acc := string(ui.keyCount) + string(ui.keyAcc) selection := []string{} + copy := 0 + move := 0 if len(nav.saves) > 0 { - copy := 0 - move := 0 for _, cp := range nav.saves { if cp { copy++ @@ -882,34 +883,82 @@ func (ui *ui) drawStatLine(nav *nav) { } opts := getOptsMap() - ruler := []string{} - for _, s := range gOpts.ruler { - switch s { - case "df": - df := diskFree(dir.path) - if df != "" { - ruler = append(ruler, df) - } - case "acc": - ruler = append(ruler, acc) - case "progress": - ruler = append(ruler, progress...) - case "selection": - ruler = append(ruler, selection...) - case "filter": - if len(dir.filter) != 0 { - ruler = append(ruler, "\033[34;7m F \033[0m") + + // 'ruler' option is deprecated and can be removed in future + if len(gOpts.ruler) > 0 { + ruler := []string{} + for _, s := range gOpts.ruler { + switch s { + case "df": + df := diskFree(dir.path) + if df != "" { + ruler = append(ruler, df) + } + case "acc": + ruler = append(ruler, acc) + case "progress": + ruler = append(ruler, progress...) + case "selection": + ruler = append(ruler, selection...) + case "filter": + if len(dir.filter) != 0 { + ruler = append(ruler, "\033[34;7m F \033[0m") + } + case "ind": + ruler = append(ruler, fmt.Sprintf("%d/%d", ind, tot)) + default: + if val, ok := opts[s]; ok { + ruler = append(ruler, formatRulerOpt(s, val)) + } } - case "ind": - ruler = append(ruler, fmt.Sprintf("%d/%d", ind, tot)) + } + + ui.msgWin.printRight(ui.screen, 0, st, strings.Join(ruler, " ")) + return + } + + rulerfmt := strings.ReplaceAll(gOpts.rulerfmt, "|", "\x1f") + rulerfmt = reRulerSub.ReplaceAllStringFunc(rulerfmt, func(s string) string { + var result string + switch s { + case "%a": + result = acc + case "%p": + result = strings.Join(progress, " ") + case "%m": + result = fmt.Sprintf("%.d", move) + case "%c": + result = fmt.Sprintf("%.d", copy) + case "%s": + result = fmt.Sprintf("%.d", len(currSelections)) + case "%f": + result = strings.Join(dir.filter, " ") + case "%i": + result = fmt.Sprint(ind) + case "%t": + result = fmt.Sprint(tot) + case "%h": + result = fmt.Sprint(hid) + case "%d": + result = diskFree(dir.path) default: + s = strings.TrimSuffix(strings.TrimPrefix(s, "%{"), "}") if val, ok := opts[s]; ok { - ruler = append(ruler, formatRulerOpt(s, val)) + result = formatRulerOpt(s, val) } } + if result == "" { + return "\x00" + } + return result + }) + ruler := "" + for _, section := range strings.Split(rulerfmt, "\x1f") { + if !strings.Contains(section, "\x00") { + ruler += section + } } - - ui.msgWin.printRight(ui.screen, 0, st, strings.Join(ruler, " ")) + ui.msgWin.printRight(ui.screen, 0, st, ruler) } func (ui *ui) drawBox() { @@ -988,7 +1037,7 @@ func (ui *ui) draw(nav *nav) { switch ui.cmdPrefix { case "": - ui.drawStatLine(nav) + ui.drawRuler(nav) ui.screen.HideCursor() case ">": maxWidth := ui.msgWin.w - 1 // leave space for cursor at the end