LSCOLORS & LS_COLORS support (#96)

* LSCOLORS & LS_COLORS support

* Renamed lsColors to gColors

* Fixed gOptWords & setExpr.eval()
This commit is contained in:
M1rt 2018-04-14 14:58:29 +03:00 committed by gokcehan
parent 54dbf5cbe4
commit f92e0f53a5
7 changed files with 186 additions and 21 deletions

View File

@ -67,6 +67,9 @@ var (
"ignorecase", "ignorecase",
"noignorecase", "noignorecase",
"ignorecase!", "ignorecase!",
"lscolors",
"nolscolors",
"lscolors!",
"preview", "preview",
"nopreview", "nopreview",
"preview!", "preview!",

1
doc.go
View File

@ -79,6 +79,7 @@ The following options can be used to customize the behavior of lf:
globsearch boolean (default off) globsearch boolean (default off)
hidden boolean (default off) hidden boolean (default off)
ignorecase boolean (default on) ignorecase boolean (default on)
lscolors boolean (default on if LSCOLORS or LS_COLORS environment variable is set)
preview boolean (default on) preview boolean (default on)
reverse boolean (default off) reverse boolean (default off)
smartcase boolean (default on) smartcase boolean (default on)

View File

@ -83,6 +83,7 @@ The following options can be used to customize the behavior of lf:
globsearch boolean (default off) globsearch boolean (default off)
hidden boolean (default off) hidden boolean (default off)
ignorecase boolean (default on) ignorecase boolean (default on)
lscolors boolean (default on if LSCOLORS or LS_COLORS environment variable is set)
preview boolean (default on) preview boolean (default on)
reverse boolean (default off) reverse boolean (default off)
smartcase boolean (default on) smartcase boolean (default on)

View File

@ -47,6 +47,12 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.ignorecase = false gOpts.ignorecase = false
case "ignorecase!": case "ignorecase!":
gOpts.ignorecase = !gOpts.ignorecase gOpts.ignorecase = !gOpts.ignorecase
case "lscolors":
gOpts.lscolors = true
case "nolscolors":
gOpts.lscolors = false
case "lscolors!":
gOpts.lscolors = !gOpts.lscolors
case "preview": case "preview":
gOpts.preview = true gOpts.preview = true
case "nopreview": case "nopreview":

148
lscolors.go Normal file
View File

@ -0,0 +1,148 @@
package main
import (
"log"
"os"
"strconv"
"strings"
"github.com/nsf/termbox-go"
)
func init() {
if lscolors := os.Getenv("LSCOLORS"); lscolors != "" {
gColors.parseBSD(lscolors)
gOpts.lscolors = true
}
if ls_colors := os.Getenv("LS_COLORS"); ls_colors != "" {
gColors.parseLinux(ls_colors)
gOpts.lscolors = true
}
}
type lsColorsEntry struct {
fg termbox.Attribute
bg termbox.Attribute
}
type lsColorsT map[string]lsColorsEntry
var gColors = make(lsColorsT)
// This function parses LS_COLORS environment variable
func (lsc lsColorsT) parseLinux(env string) {
e := strings.Split(env, ":")
for _, e := range e {
i := strings.IndexRune(e, '=')
if i >= 0 {
key := e[:i]
values := strings.Split(e[i+1:], ";")
var fg, bg termbox.Attribute
for _, a := range values {
i, _ := strconv.Atoi(a) // strconv.Atoi() will returns 0 on error which is termbox.ColorDefault. No need to check the error.
switch {
case i == 1:
fg = fg | termbox.AttrBold
case i == 4:
fg = fg | termbox.AttrUnderline
case i == 5:
// Flashing text
case i == 7:
fg = fg | termbox.AttrReverse
case i == 8:
// Concealed
case 30 <= i && i <= 37:
fg = fg | termbox.Attribute(i-29)
case 40 <= i && i <= 47:
bg = bg | termbox.Attribute(i-39)
}
}
lsc[key] = lsColorsEntry{fg: fg, bg: bg}
}
}
}
// This function parses LSCOLORS variable. See http://www.manpages.info/freebsd/ls.1.html
func (lsc lsColorsT) parseBSD(env string) {
if len(env) != 22 {
log.Print("LSCOLORS variable invalid")
return
}
unixLsColors := []string{"di", "so", "ln", "pi", "ex", "bd", "cd", "su", "sg", "tw", "ow"}
unixColors := map[byte]termbox.Attribute{
'a': termbox.ColorBlack,
'b': termbox.ColorRed,
'c': termbox.ColorGreen,
'd': termbox.ColorYellow, // should be brown
'e': termbox.ColorBlue,
'f': termbox.ColorMagenta,
'g': termbox.ColorCyan,
'h': termbox.ColorWhite, // should be light grey
'A': termbox.AttrBold | termbox.ColorBlack,
'B': termbox.AttrBold | termbox.ColorRed,
'C': termbox.AttrBold | termbox.ColorGreen,
'D': termbox.AttrBold | termbox.ColorWhite, // brown
'E': termbox.AttrBold | termbox.ColorBlue,
'F': termbox.AttrBold | termbox.ColorMagenta,
'G': termbox.AttrBold | termbox.ColorCyan,
'H': termbox.AttrBold | termbox.ColorWhite, // light grey
}
getColor := func(r byte) termbox.Attribute {
if color, ok := unixColors[r]; ok {
return color
} else {
return termbox.ColorDefault
}
}
for i, key := range unixLsColors {
lsc[key] = lsColorsEntry{fg: getColor(env[i*2]), bg: getColor(env[i*2+1])}
}
}
// This function returns foreground and background colors for given file
func (lsc lsColorsT) getColors(f *file) (termbox.Attribute, termbox.Attribute) {
var key = ""
switch {
case f.Mode()&os.ModeSticky != 0:
key = "st"
case f.Mode()&os.ModeSetuid != 0:
key = "su"
case f.Mode()&os.ModeSetgid != 0:
key = "sg"
case f.IsDir():
key = "di"
case f.linkState == working:
key = "ln"
case f.Mode()&os.ModeNamedPipe != 0:
key = "pi"
case f.Mode()&os.ModeSocket != 0:
key = "so"
case f.Mode()&os.ModeCharDevice != 0:
key = "cd"
case f.Mode()&os.ModeDevice != 0:
key = "bd"
case f.linkState == broken:
key = "or"
case f.Mode().IsRegular() && f.Mode()&0111 != 0:
key = "ex"
default:
if extI := strings.LastIndexByte(f.Name(), '.'); extI > 0 {
key = "*" + f.Name()[extI:]
}
}
if val, ok := lsc[key]; ok {
return val.fg, val.bg
} else {
if val, ok = lsc["no"]; ok {
return val.fg, val.bg
} else {
return termbox.ColorDefault, termbox.ColorDefault
}
}
}

View File

@ -8,6 +8,7 @@ var gOpts struct {
globsearch bool globsearch bool
hidden bool hidden bool
ignorecase bool ignorecase bool
lscolors bool
preview bool preview bool
reverse bool reverse bool
smartcase bool smartcase bool
@ -34,6 +35,7 @@ func init() {
gOpts.globsearch = false gOpts.globsearch = false
gOpts.hidden = false gOpts.hidden = false
gOpts.ignorecase = true gOpts.ignorecase = true
// gOpts.lscolors = true this option is initialized in lscolors.go init() depending on whether the corresponding environment variable is set
gOpts.preview = true gOpts.preview = true
gOpts.reverse = false gOpts.reverse = false
gOpts.smartcase = true gOpts.smartcase = true

46
ui.go
View File

@ -296,28 +296,32 @@ func (win *win) printDir(dir *dir, marks map[string]int, saves map[string]bool)
end := min(beg+win.h, maxind+1) end := min(beg+win.h, maxind+1)
for i, f := range dir.fi[beg:end] { for i, f := range dir.fi[beg:end] {
switch { if gOpts.lscolors {
case f.linkState == working: fg, bg = gColors.getColors(f)
fg = termbox.ColorCyan } else {
if f.Mode().IsDir() { switch {
fg |= termbox.AttrBold case f.linkState == working:
fg = termbox.ColorCyan
if f.Mode().IsDir() {
fg |= termbox.AttrBold
}
case f.linkState == broken:
fg = termbox.ColorMagenta
case f.Mode().IsRegular():
if f.Mode()&0111 != 0 {
fg = termbox.AttrBold | termbox.ColorGreen
} else {
fg = termbox.ColorDefault
}
case f.Mode().IsDir():
fg = termbox.AttrBold | termbox.ColorBlue
case f.Mode()&os.ModeNamedPipe != 0:
fg = termbox.ColorRed
case f.Mode()&os.ModeSocket != 0:
fg = termbox.ColorYellow
case f.Mode()&os.ModeDevice != 0:
fg = termbox.ColorWhite
} }
case f.linkState == broken:
fg = termbox.ColorMagenta
case f.Mode().IsRegular():
if f.Mode()&0111 != 0 {
fg = termbox.AttrBold | termbox.ColorGreen
} else {
fg = termbox.ColorDefault
}
case f.Mode().IsDir():
fg = termbox.AttrBold | termbox.ColorBlue
case f.Mode()&os.ModeNamedPipe != 0:
fg = termbox.ColorRed
case f.Mode()&os.ModeSocket != 0:
fg = termbox.ColorYellow
case f.Mode()&os.ModeDevice != 0:
fg = termbox.ColorWhite
} }
path := filepath.Join(dir.path, f.Name()) path := filepath.Join(dir.path, f.Name())