From 49a1e79d535ef1fead50cbabba11e4681373dfd6 Mon Sep 17 00:00:00 2001 From: Lorentz <25649139+lorentzforces@users.noreply.github.com> Date: Sat, 16 Mar 2024 23:34:05 -0500 Subject: [PATCH 1/5] use symlink target for stat styling & icons (#566) Support LS_COLORS/dircolors use of "target" for symlinks to select icons and styles for them according to the target stat. This mainly applies to directories - file name-based styles will still be based on the name of the link, not its target. This mirrors `ls` behavior. --- colors.go | 46 +++++++++++++++++++++++++------------- doc.md | 2 ++ doc.txt | 10 ++++++--- icons.go | 67 +++++++++++++++++++++++++++++++------------------------ lf.1 | 14 +++++++----- 5 files changed, 86 insertions(+), 53 deletions(-) diff --git a/colors.go b/colors.go index 95204801..be65ec2d 100644 --- a/colors.go +++ b/colors.go @@ -10,10 +10,16 @@ import ( "github.com/gdamore/tcell/v2" ) -type styleMap map[string]tcell.Style +type styleMap struct { + styles map[string]tcell.Style + useLinkTarget bool +} func parseStyles() styleMap { - sm := make(styleMap) + sm := styleMap{ + styles: make(map[string]tcell.Style), + useLinkTarget: false, + } // Default values from dircolors // @@ -199,12 +205,12 @@ func (sm styleMap) parseFile(path string) { key = filepath.Clean(key) } - sm[key] = applyAnsiCodes(val, tcell.StyleDefault) + sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault) } } // This function parses $LS_COLORS environment variable. -func (sm styleMap) parseGNU(env string) { +func (sm *styleMap) parseGNU(env string) { for _, entry := range strings.Split(env, ":") { if entry == "" { continue @@ -225,7 +231,13 @@ func (sm styleMap) parseGNU(env string) { key = filepath.Clean(key) } - sm[key] = applyAnsiCodes(val, tcell.StyleDefault) + if key == "ln" && val == "target" { + sm.useLinkTarget = true + log.Printf("using link target for styles") + continue + } + + sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault) } } @@ -267,17 +279,17 @@ func (sm styleMap) parseBSD(env string) { } for i, key := range colorNames { - sm[key] = getStyle(env[i*2], env[i*2+1]) + sm.styles[key] = getStyle(env[i*2], env[i*2+1]) } } func (sm styleMap) get(f *file) tcell.Style { - if val, ok := sm[f.path]; ok { + if val, ok := sm.styles[f.path]; ok { return val } if f.IsDir() { - if val, ok := sm[f.Name()+"/"]; ok { + if val, ok := sm.styles[f.Name()+"/"]; ok { return val } } @@ -285,8 +297,6 @@ func (sm styleMap) get(f *file) tcell.Style { var key string switch { - case f.linkState == working: - key = "ln" case f.linkState == broken: key = "or" case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0: @@ -313,27 +323,31 @@ func (sm styleMap) get(f *file) tcell.Style { key = "ex" } - if val, ok := sm[key]; ok { + if f.linkState == working && !sm.useLinkTarget { + key = "ln" + } + + if val, ok := sm.styles[key]; ok { return val } - if val, ok := sm[f.Name()+"*"]; ok { + if val, ok := sm.styles[f.Name()+"*"]; ok { return val } - if val, ok := sm["*"+f.Name()]; ok { + if val, ok := sm.styles["*"+f.Name()]; ok { return val } - if val, ok := sm[filepath.Base(f.Name())+".*"]; ok { + if val, ok := sm.styles[filepath.Base(f.Name())+".*"]; ok { return val } - if val, ok := sm["*"+strings.ToLower(f.ext)]; ok { + if val, ok := sm.styles["*"+strings.ToLower(f.ext)]; ok { return val } - if val, ok := sm["fi"]; ok { + if val, ok := sm.styles["fi"]; ok { return val } diff --git a/doc.md b/doc.md index 92dee461..f825b255 100644 --- a/doc.md +++ b/doc.md @@ -1790,6 +1790,7 @@ You may instead divide it into multiple lines in between double quotes by escapi ex=01;32:\ " +The `ln` entry supports the special value `target`, which will use the link target to select a style. (this mirrors GNU's ls and dircolors behavior) Having such a long variable definition in a shell configuration file might be undesirable. You may instead use the colors file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) for configuration. A sample colors file can be found at @@ -1803,6 +1804,7 @@ Icons are configured using `LF_ICONS` environment variable or an icons file (ref The variable uses the same syntax as `LS_COLORS/LF_COLORS`. Instead of colors, you should put a single characters as values of entries. The icons file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) should consist of whitespace-separated pairs with a `#` character to start comments until the end of the line. +The `ln` entry supports the special value `target`, which will use the link target to select an icon. (this mirrors GNU's ls and dircolors behavior) Do not forget to add `set icons true` to your `lfrc` to see the icons. Default values are as follows given with their matching order in lf: diff --git a/doc.txt b/doc.txt index f932ec11..2bd0a3a5 100644 --- a/doc.txt +++ b/doc.txt @@ -2026,6 +2026,8 @@ escaping newlines with backslashes as follows: ex=01;32:\ " +The ln entry supports the special value target, which will use the link +target to select a style. (this mirrors GNU's ls and dircolors behavior) Having such a long variable definition in a shell configuration file might be undesirable. You may instead use the colors file (refer to the CONFIGURATION section) for configuration. A sample colors file can be @@ -2040,9 +2042,11 @@ file (refer to the CONFIGURATION section). The variable uses the same syntax as LS_COLORS/LF_COLORS. Instead of colors, you should put a single characters as values of entries. The icons file (refer to the CONFIGURATION section) should consist of whitespace-separated pairs with -a # character to start comments until the end of the line. Do not forget -to add set icons true to your lfrc to see the icons. Default values are -as follows given with their matching order in lf: +a # character to start comments until the end of the line. The ln entry +supports the special value target, which will use the link target to +select an icon. (this mirrors GNU's ls and dircolors behavior) Do not +forget to add set icons true to your lfrc to see the icons. Default +values are as follows given with their matching order in lf: ln l or l diff --git a/icons.go b/icons.go index ac4230fa..25563bb8 100644 --- a/icons.go +++ b/icons.go @@ -7,10 +7,16 @@ import ( "strings" ) -type iconMap map[string]string +type iconMap struct { + icons map[string]string + useLinkTarget bool +} func parseIcons() iconMap { - im := make(iconMap) + im := iconMap{ + icons: make(map[string]string), + useLinkTarget: false, + } defaultIcons := []string{ "ln=l", @@ -44,7 +50,7 @@ func parseIcons() iconMap { return im } -func (im iconMap) parseFile(path string) { +func (im *iconMap) parseFile(path string) { log.Printf("reading file: %s", path) f, err := os.Open(path) @@ -61,19 +67,11 @@ func (im iconMap) parseFile(path string) { } for _, pair := range pairs { - key, val := pair[0], pair[1] - - key = replaceTilde(key) - - if filepath.IsAbs(key) { - key = filepath.Clean(key) - } - - im[key] = val + im.parsePair(pair) } } -func (im iconMap) parseEnv(env string) { +func (im *iconMap) parseEnv(env string) { for _, entry := range strings.Split(env, ":") { if entry == "" { continue @@ -86,25 +84,34 @@ func (im iconMap) parseEnv(env string) { return } - key, val := pair[0], pair[1] + im.parsePair(pair) + } +} + +func (im *iconMap) parsePair(pair []string) { + key, val := pair[0], pair[1] - key = replaceTilde(key) + key = replaceTilde(key) - if filepath.IsAbs(key) { - key = filepath.Clean(key) - } + if filepath.IsAbs(key) { + key = filepath.Clean(key) + } - im[key] = val + if key == "ln" && val == "target" { + im.useLinkTarget = true + log.Printf("using link target for icons") } + + im.icons[key] = val } func (im iconMap) get(f *file) string { - if val, ok := im[f.path]; ok { + if val, ok := im.icons[f.path]; ok { return val } if f.IsDir() { - if val, ok := im[f.Name()+"/"]; ok { + if val, ok := im.icons[f.Name()+"/"]; ok { return val } } @@ -112,8 +119,6 @@ func (im iconMap) get(f *file) string { var key string switch { - case f.linkState == working: - key = "ln" case f.linkState == broken: key = "or" case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0: @@ -140,27 +145,31 @@ func (im iconMap) get(f *file) string { key = "ex" } - if val, ok := im[key]; ok { + if f.linkState == working && !im.useLinkTarget { + key = "ln" + } + + if val, ok := im.icons[key]; ok { return val } - if val, ok := im[f.Name()+"*"]; ok { + if val, ok := im.icons[f.Name()+"*"]; ok { return val } - if val, ok := im["*"+f.Name()]; ok { + if val, ok := im.icons["*"+f.Name()]; ok { return val } - if val, ok := im[filepath.Base(f.Name())+".*"]; ok { + if val, ok := im.icons[filepath.Base(f.Name())+".*"]; ok { return val } - if val, ok := im["*"+strings.ToLower(f.ext)]; ok { + if val, ok := im.icons["*"+strings.ToLower(f.ext)]; ok { return val } - if val, ok := im["fi"]; ok { + if val, ok := im.icons["fi"]; ok { return val } diff --git a/lf.1 b/lf.1 index 73ee3df3..7711c101 100644 --- a/lf.1 +++ b/lf.1 @@ -1,6 +1,6 @@ .\" Automatically generated by Pandoc 2.11.4 .\" -.TH "LF" "1" "2024-02-02" "" "DOCUMENTATION" +.TH "LF" "1" "2024-03-17" "r31-50-g33d880c" "DOCUMENTATION" .hy .SH NAME .PP @@ -2363,8 +2363,10 @@ ex=01;32:\[rs] \f[R] .fi .PP -Having such a long variable definition in a shell configuration file -might be undesirable. +The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R], +which will use the link target to select a style. +(this mirrors GNU\[aq]s ls and dircolors behavior) Having such a long +variable definition in a shell configuration file might be undesirable. You may instead use the colors file (refer to the CONFIGURATION section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) for configuration. @@ -2384,8 +2386,10 @@ The icons file (refer to the CONFIGURATION section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) should consist of whitespace-separated pairs with a \f[C]#\f[R] character to start comments until the end of the line. -Do not forget to add \f[C]set icons true\f[R] to your \f[C]lfrc\f[R] to -see the icons. +The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R], +which will use the link target to select an icon. +(this mirrors GNU\[aq]s ls and dircolors behavior) Do not forget to add +\f[C]set icons true\f[R] to your \f[C]lfrc\f[R] to see the icons. Default values are as follows given with their matching order in lf: .IP .nf From e1d3292bc68f58d7b1a7ab41713500617f758939 Mon Sep 17 00:00:00 2001 From: Lorentz <25649139+lorentzforces@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:04:06 -0500 Subject: [PATCH 2/5] remove unnecessary debugging and factor out parsePair in color parsing --- colors.go | 34 ++++++++++++++-------------------- icons.go | 1 - 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/colors.go b/colors.go index be65ec2d..b95b292f 100644 --- a/colors.go +++ b/colors.go @@ -197,15 +197,7 @@ func (sm styleMap) parseFile(path string) { } for _, pair := range pairs { - key, val := pair[0], pair[1] - - key = replaceTilde(key) - - if filepath.IsAbs(key) { - key = filepath.Clean(key) - } - - sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault) + sm.parsePair(pair) } } @@ -223,22 +215,24 @@ func (sm *styleMap) parseGNU(env string) { return } - key, val := pair[0], pair[1] + sm.parsePair(pair) + } +} - key = replaceTilde(key) +func (sm *styleMap) parsePair(pair []string) { + key, val := pair[0], pair[1] - if filepath.IsAbs(key) { - key = filepath.Clean(key) - } + key = replaceTilde(key) - if key == "ln" && val == "target" { - sm.useLinkTarget = true - log.Printf("using link target for styles") - continue - } + if filepath.IsAbs(key) { + key = filepath.Clean(key) + } - sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault) + if key == "ln" && val == "target" { + sm.useLinkTarget = true } + + sm.styles[key] = applyAnsiCodes(val, tcell.StyleDefault) } // This function parses $LSCOLORS environment variable. diff --git a/icons.go b/icons.go index 25563bb8..9b614c19 100644 --- a/icons.go +++ b/icons.go @@ -99,7 +99,6 @@ func (im *iconMap) parsePair(pair []string) { if key == "ln" && val == "target" { im.useLinkTarget = true - log.Printf("using link target for icons") } im.icons[key] = val From 799b0b795397259e952b2ad4e29ae8196c537870 Mon Sep 17 00:00:00 2001 From: Lorentz <25649139+lorentzforces@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:06:45 -0500 Subject: [PATCH 3/5] move conditional to switch block --- colors.go | 6 ++---- icons.go | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/colors.go b/colors.go index b95b292f..8558b3fa 100644 --- a/colors.go +++ b/colors.go @@ -291,6 +291,8 @@ func (sm styleMap) get(f *file) tcell.Style { var key string switch { + case f.linkState == working && !sm.useLinkTarget: + key = "ln" case f.linkState == broken: key = "or" case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0: @@ -317,10 +319,6 @@ func (sm styleMap) get(f *file) tcell.Style { key = "ex" } - if f.linkState == working && !sm.useLinkTarget { - key = "ln" - } - if val, ok := sm.styles[key]; ok { return val } diff --git a/icons.go b/icons.go index 9b614c19..e6203975 100644 --- a/icons.go +++ b/icons.go @@ -118,6 +118,8 @@ func (im iconMap) get(f *file) string { var key string switch { + case f.linkState == working && !im.useLinkTarget: + key= "ln" case f.linkState == broken: key = "or" case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0: @@ -144,10 +146,6 @@ func (im iconMap) get(f *file) string { key = "ex" } - if f.linkState == working && !im.useLinkTarget { - key = "ln" - } - if val, ok := im.icons[key]; ok { return val } From e0148c35e8896d6cd9546e413c4aeb5d50911832 Mon Sep 17 00:00:00 2001 From: Lorentz <25649139+lorentzforces@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:11:39 -0500 Subject: [PATCH 4/5] clarify ln target behavior in docs --- doc.md | 4 ++-- doc.txt | 20 +++++++++++--------- lf.1 | 18 +++++++++++------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/doc.md b/doc.md index f825b255..faf38c9f 100644 --- a/doc.md +++ b/doc.md @@ -1790,7 +1790,7 @@ You may instead divide it into multiple lines in between double quotes by escapi ex=01;32:\ " -The `ln` entry supports the special value `target`, which will use the link target to select a style. (this mirrors GNU's ls and dircolors behavior) +The `ln` entry supports the special value `target`, which will use the link target to select a style. File name rules will still apply based on the link's name -- this mirrors GNU's `ls` and `dircolors` behavior. Having such a long variable definition in a shell configuration file might be undesirable. You may instead use the colors file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) for configuration. A sample colors file can be found at @@ -1803,8 +1803,8 @@ https://en.wikipedia.org/wiki/ANSI_escape_code Icons are configured using `LF_ICONS` environment variable or an icons file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)). The variable uses the same syntax as `LS_COLORS/LF_COLORS`. Instead of colors, you should put a single characters as values of entries. +The `ln` entry supports the special value `target`, which will use the link target to select a icon. File name rules will still apply based on the link's name -- this mirrors GNU's `ls` and `dircolors` behavior. The icons file (refer to the [CONFIGURATION section](https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) should consist of whitespace-separated pairs with a `#` character to start comments until the end of the line. -The `ln` entry supports the special value `target`, which will use the link target to select an icon. (this mirrors GNU's ls and dircolors behavior) Do not forget to add `set icons true` to your `lfrc` to see the icons. Default values are as follows given with their matching order in lf: diff --git a/doc.txt b/doc.txt index 2bd0a3a5..5910ce21 100644 --- a/doc.txt +++ b/doc.txt @@ -2027,9 +2027,10 @@ escaping newlines with backslashes as follows: " The ln entry supports the special value target, which will use the link -target to select a style. (this mirrors GNU's ls and dircolors behavior) -Having such a long variable definition in a shell configuration file -might be undesirable. You may instead use the colors file (refer to the +target to select a style. File name rules will still apply based on the +link's name -- this mirrors GNU's ls and dircolors behavior. Having such +a long variable definition in a shell configuration file might be +undesirable. You may instead use the colors file (refer to the CONFIGURATION section) for configuration. A sample colors file can be found at https://github.com/gokcehan/lf/blob/master/etc/colors.example You may also see the wiki page for ANSI escape codes @@ -2040,13 +2041,14 @@ ICONS Icons are configured using LF_ICONS environment variable or an icons file (refer to the CONFIGURATION section). The variable uses the same syntax as LS_COLORS/LF_COLORS. Instead of colors, you should put a -single characters as values of entries. The icons file (refer to the +single characters as values of entries. The ln entry supports the +special value target, which will use the link target to select a icon. +File name rules will still apply based on the link's name -- this +mirrors GNU's ls and dircolors behavior. The icons file (refer to the CONFIGURATION section) should consist of whitespace-separated pairs with -a # character to start comments until the end of the line. The ln entry -supports the special value target, which will use the link target to -select an icon. (this mirrors GNU's ls and dircolors behavior) Do not -forget to add set icons true to your lfrc to see the icons. Default -values are as follows given with their matching order in lf: +a # character to start comments until the end of the line. Do not forget +to add set icons true to your lfrc to see the icons. Default values are +as follows given with their matching order in lf: ln l or l diff --git a/lf.1 b/lf.1 index 7711c101..dec24f37 100644 --- a/lf.1 +++ b/lf.1 @@ -1,6 +1,6 @@ .\" Automatically generated by Pandoc 2.11.4 .\" -.TH "LF" "1" "2024-03-17" "r31-50-g33d880c" "DOCUMENTATION" +.TH "LF" "1" "2024-03-18" "r31-52-g799b0b7" "DOCUMENTATION" .hy .SH NAME .PP @@ -2365,8 +2365,10 @@ ex=01;32:\[rs] .PP The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R], which will use the link target to select a style. -(this mirrors GNU\[aq]s ls and dircolors behavior) Having such a long -variable definition in a shell configuration file might be undesirable. +File name rules will still apply based on the link\[aq]s name -- this +mirrors GNU\[aq]s \f[C]ls\f[R] and \f[C]dircolors\f[R] behavior. +Having such a long variable definition in a shell configuration file +might be undesirable. You may instead use the colors file (refer to the CONFIGURATION section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) for configuration. @@ -2382,14 +2384,16 @@ section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)). The variable uses the same syntax as \f[C]LS_COLORS/LF_COLORS\f[R]. Instead of colors, you should put a single characters as values of entries. +The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R], +which will use the link target to select a icon. +File name rules will still apply based on the link\[aq]s name -- this +mirrors GNU\[aq]s \f[C]ls\f[R] and \f[C]dircolors\f[R] behavior. The icons file (refer to the CONFIGURATION section (https://github.com/gokcehan/lf/blob/master/doc.md#configuration)) should consist of whitespace-separated pairs with a \f[C]#\f[R] character to start comments until the end of the line. -The \f[C]ln\f[R] entry supports the special value \f[C]target\f[R], -which will use the link target to select an icon. -(this mirrors GNU\[aq]s ls and dircolors behavior) Do not forget to add -\f[C]set icons true\f[R] to your \f[C]lfrc\f[R] to see the icons. +Do not forget to add \f[C]set icons true\f[R] to your \f[C]lfrc\f[R] to +see the icons. Default values are as follows given with their matching order in lf: .IP .nf From 61139264acbf946540f60022b35b8756e7e9298a Mon Sep 17 00:00:00 2001 From: Lorentz <25649139+lorentzforces@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:22:41 -0500 Subject: [PATCH 5/5] run gofmt on single file --- icons.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icons.go b/icons.go index e6203975..5583864a 100644 --- a/icons.go +++ b/icons.go @@ -119,7 +119,7 @@ func (im iconMap) get(f *file) string { switch { case f.linkState == working && !im.useLinkTarget: - key= "ln" + key = "ln" case f.linkState == broken: key = "or" case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0: