parent
f92e0f53a5
commit
5a9a829252
215
colors.go
Normal file
215
colors.go
Normal file
@ -0,0 +1,215 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type colorEntry struct {
|
||||
fg termbox.Attribute
|
||||
bg termbox.Attribute
|
||||
}
|
||||
|
||||
type colorMap map[string]colorEntry
|
||||
|
||||
func parseColors() colorMap {
|
||||
if env := os.Getenv("LS_COLORS"); env != "" {
|
||||
return parseColorsGNU(env)
|
||||
}
|
||||
|
||||
if env := os.Getenv("LSCOLORS"); env != "" {
|
||||
return parseColorsBSD(env)
|
||||
}
|
||||
|
||||
// default values from dircolors
|
||||
defaultColors := []string{
|
||||
// "rs=0",
|
||||
"di=01;34",
|
||||
"ln=01;36",
|
||||
// "mh=00",
|
||||
"pi=40;33",
|
||||
"so=01;35",
|
||||
"do=01;35",
|
||||
"bd=40;33;01",
|
||||
"cd=40;33;01",
|
||||
"or=40;31;01",
|
||||
// "mi=00",
|
||||
"su=37;41",
|
||||
"sg=30;43",
|
||||
"ca=30;41",
|
||||
"tw=30;42",
|
||||
"ow=34;42",
|
||||
"st=37;44",
|
||||
"ex=01;32",
|
||||
}
|
||||
|
||||
return parseColorsGNU(strings.Join(defaultColors, ":"))
|
||||
}
|
||||
|
||||
func applyAnsiCodes(s string, fg, bg termbox.Attribute) (termbox.Attribute, termbox.Attribute) {
|
||||
toks := strings.Split(s, ";")
|
||||
|
||||
var nums []int
|
||||
for _, t := range toks {
|
||||
if t == "" {
|
||||
fg = termbox.ColorDefault
|
||||
bg = termbox.ColorDefault
|
||||
break
|
||||
}
|
||||
n, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
log.Printf("converting escape code: %s", err)
|
||||
continue
|
||||
}
|
||||
nums = append(nums, n)
|
||||
}
|
||||
|
||||
// parse 256 color terminal ansi codes
|
||||
if len(nums) == 3 {
|
||||
if nums[0] == 48 && nums[1] == 5 {
|
||||
bg = termbox.Attribute(nums[2] + 1)
|
||||
}
|
||||
if nums[0] == 38 && nums[1] == 5 {
|
||||
fg = termbox.Attribute(nums[2] + 1)
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range nums {
|
||||
attr, ok := gAnsiCodes[n]
|
||||
if !ok {
|
||||
log.Printf("unknown ansi code: %d", n)
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case n == 0:
|
||||
fg, bg = attr, attr
|
||||
case n == 1 || n == 4 || n == 7:
|
||||
fg |= attr
|
||||
case 30 <= n && n <= 37:
|
||||
fg &= gAnsiColorResetMask
|
||||
fg |= attr
|
||||
case 40 <= n && n <= 47:
|
||||
bg = attr
|
||||
}
|
||||
}
|
||||
|
||||
return fg, bg
|
||||
}
|
||||
|
||||
// This function parses $LS_COLORS environment variable.
|
||||
func parseColorsGNU(env string) colorMap {
|
||||
colors := make(colorMap)
|
||||
|
||||
entries := strings.Split(env, ":")
|
||||
for _, entry := range entries {
|
||||
if entry == "" {
|
||||
continue
|
||||
}
|
||||
pair := strings.Split(entry, "=")
|
||||
if len(pair) != 2 {
|
||||
log.Printf("invalid $LS_COLORS entry: %s", entry)
|
||||
return colors
|
||||
}
|
||||
key, val := pair[0], pair[1]
|
||||
fg, bg := applyAnsiCodes(val, termbox.ColorDefault, termbox.ColorDefault)
|
||||
colors[key] = colorEntry{fg: fg, bg: bg}
|
||||
}
|
||||
|
||||
return colors
|
||||
}
|
||||
|
||||
// This function parses $LSCOLORS environment variable.
|
||||
func parseColorsBSD(env string) colorMap {
|
||||
colors := make(colorMap)
|
||||
|
||||
if len(env) != 22 {
|
||||
log.Printf("invalid $LSCOLORS variable: %s", env)
|
||||
return colors
|
||||
}
|
||||
|
||||
colorNames := []string{"di", "ln", "so", "pi", "ex", "bd", "cd", "su", "sg", "tw", "ow"}
|
||||
colorCodes := map[byte]termbox.Attribute{
|
||||
'a': termbox.ColorBlack,
|
||||
'b': termbox.ColorRed,
|
||||
'c': termbox.ColorGreen,
|
||||
'd': termbox.ColorYellow, // brown
|
||||
'e': termbox.ColorBlue,
|
||||
'f': termbox.ColorMagenta,
|
||||
'g': termbox.ColorCyan,
|
||||
'h': termbox.ColorWhite, // light grey
|
||||
'A': termbox.AttrBold | termbox.ColorBlack,
|
||||
'B': termbox.AttrBold | termbox.ColorRed,
|
||||
'C': termbox.AttrBold | termbox.ColorGreen,
|
||||
'D': termbox.AttrBold | termbox.ColorYellow, // brown
|
||||
'E': termbox.AttrBold | termbox.ColorBlue,
|
||||
'F': termbox.AttrBold | termbox.ColorMagenta,
|
||||
'G': termbox.AttrBold | termbox.ColorCyan,
|
||||
'H': termbox.AttrBold | termbox.ColorWhite, // light grey
|
||||
'x': termbox.ColorDefault,
|
||||
}
|
||||
|
||||
getColor := func(r byte) termbox.Attribute {
|
||||
if color, ok := colorCodes[r]; ok {
|
||||
return color
|
||||
}
|
||||
|
||||
log.Printf("invalid $LSCOLORS entry: %c", r)
|
||||
return termbox.ColorDefault
|
||||
}
|
||||
|
||||
for i, key := range colorNames {
|
||||
colors[key] = colorEntry{fg: getColor(env[i*2]), bg: getColor(env[i*2+1])}
|
||||
}
|
||||
|
||||
return colors
|
||||
}
|
||||
|
||||
func (cm colorMap) get(f *file) (termbox.Attribute, termbox.Attribute) {
|
||||
var key string
|
||||
|
||||
switch {
|
||||
case f.IsDir() && f.Mode()&os.ModeSticky != 0 && f.Mode()&0002 != 0:
|
||||
key = "tw"
|
||||
case f.IsDir() && f.Mode()&os.ModeSticky != 0:
|
||||
key = "st"
|
||||
case f.IsDir() && f.Mode()&0002 != 0:
|
||||
key = "ow"
|
||||
case f.IsDir():
|
||||
key = "di"
|
||||
case f.linkState == working:
|
||||
key = "ln"
|
||||
case f.linkState == broken:
|
||||
key = "or"
|
||||
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.Mode()&os.ModeSetuid != 0:
|
||||
key = "su"
|
||||
case f.Mode()&os.ModeSetgid != 0:
|
||||
key = "sg"
|
||||
case f.Mode().IsRegular() && f.Mode()&0111 != 0:
|
||||
key = "ex"
|
||||
default:
|
||||
key = "*" + filepath.Ext(f.Name())
|
||||
}
|
||||
|
||||
if val, ok := cm[key]; ok {
|
||||
return val.fg, val.bg
|
||||
}
|
||||
|
||||
if val, ok := cm["fi"]; ok {
|
||||
return val.fg, val.bg
|
||||
}
|
||||
|
||||
return termbox.ColorDefault, termbox.ColorDefault
|
||||
}
|
2
doc.go
2
doc.go
@ -79,7 +79,7 @@ The following options can be used to customize the behavior of lf:
|
||||
globsearch boolean (default off)
|
||||
hidden boolean (default off)
|
||||
ignorecase boolean (default on)
|
||||
lscolors boolean (default on if LSCOLORS or LS_COLORS environment variable is set)
|
||||
lscolors boolean (default on)
|
||||
preview boolean (default on)
|
||||
reverse boolean (default off)
|
||||
smartcase boolean (default on)
|
||||
|
@ -83,7 +83,7 @@ The following options can be used to customize the behavior of lf:
|
||||
globsearch boolean (default off)
|
||||
hidden boolean (default off)
|
||||
ignorecase boolean (default on)
|
||||
lscolors boolean (default on if LSCOLORS or LS_COLORS environment variable is set)
|
||||
lscolors boolean (default on)
|
||||
preview boolean (default on)
|
||||
reverse boolean (default off)
|
||||
smartcase boolean (default on)
|
||||
|
3
eval.go
3
eval.go
@ -49,10 +49,13 @@ func (e *setExpr) eval(app *app, args []string) {
|
||||
gOpts.ignorecase = !gOpts.ignorecase
|
||||
case "lscolors":
|
||||
gOpts.lscolors = true
|
||||
app.ui.colors = parseColors()
|
||||
case "nolscolors":
|
||||
gOpts.lscolors = false
|
||||
app.ui.colors = parseColors()
|
||||
case "lscolors!":
|
||||
gOpts.lscolors = !gOpts.lscolors
|
||||
app.ui.colors = parseColors()
|
||||
case "preview":
|
||||
gOpts.preview = true
|
||||
case "nopreview":
|
||||
|
148
lscolors.go
148
lscolors.go
@ -1,148 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
2
opts.go
2
opts.go
@ -35,7 +35,7 @@ func init() {
|
||||
gOpts.globsearch = false
|
||||
gOpts.hidden = false
|
||||
gOpts.ignorecase = true
|
||||
// gOpts.lscolors = true this option is initialized in lscolors.go init() depending on whether the corresponding environment variable is set
|
||||
gOpts.lscolors = true
|
||||
gOpts.preview = true
|
||||
gOpts.reverse = false
|
||||
gOpts.smartcase = true
|
||||
|
90
ui.go
90
ui.go
@ -126,60 +126,6 @@ func (win *win) renew(w, h, x, y int) {
|
||||
win.w, win.h, win.x, win.y = w, h, x, y
|
||||
}
|
||||
|
||||
func applyAnsiCodes(s string, fg, bg termbox.Attribute) (termbox.Attribute, termbox.Attribute) {
|
||||
toks := strings.Split(s, ";")
|
||||
|
||||
var nums []int
|
||||
for _, t := range toks {
|
||||
if t == "" {
|
||||
fg = termbox.ColorDefault
|
||||
bg = termbox.ColorDefault
|
||||
break
|
||||
}
|
||||
n, err := strconv.Atoi(t)
|
||||
if err != nil {
|
||||
log.Printf("converting escape code: %s", err)
|
||||
continue
|
||||
}
|
||||
nums = append(nums, n)
|
||||
}
|
||||
|
||||
// Parse 256 color terminal ansi codes
|
||||
// termbox-go has a color offset of one, because of attributes
|
||||
if len(nums) == 3 {
|
||||
if nums[0] == 48 && nums[1] == 5 {
|
||||
bg = termbox.Attribute(nums[2])
|
||||
bg++
|
||||
}
|
||||
if nums[0] == 38 && nums[1] == 5 {
|
||||
fg = termbox.Attribute(nums[2])
|
||||
fg++
|
||||
}
|
||||
return fg, bg
|
||||
}
|
||||
|
||||
for _, n := range nums {
|
||||
attr, ok := gAnsiCodes[n]
|
||||
if !ok {
|
||||
log.Printf("unknown ansi code: %d", n)
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case n == 0:
|
||||
fg, bg = attr, attr
|
||||
case n == 1 || n == 4 || n == 7:
|
||||
fg |= attr
|
||||
case 30 <= n && n <= 37:
|
||||
fg &= gAnsiColorResetMask
|
||||
fg |= attr
|
||||
case 40 <= n && n <= 47:
|
||||
bg = attr
|
||||
}
|
||||
}
|
||||
|
||||
return fg, bg
|
||||
}
|
||||
|
||||
func printLength(s string) int {
|
||||
ind := 0
|
||||
off := 0
|
||||
@ -267,7 +213,7 @@ func (win *win) printReg(reg *reg) {
|
||||
return
|
||||
}
|
||||
|
||||
func (win *win) printDir(dir *dir, marks map[string]int, saves map[string]bool) {
|
||||
func (win *win) printDir(dir *dir, marks map[string]int, saves map[string]bool, colors colorMap) {
|
||||
if win.w < 3 {
|
||||
return
|
||||
}
|
||||
@ -296,33 +242,7 @@ func (win *win) printDir(dir *dir, marks map[string]int, saves map[string]bool)
|
||||
end := min(beg+win.h, maxind+1)
|
||||
|
||||
for i, f := range dir.fi[beg:end] {
|
||||
if gOpts.lscolors {
|
||||
fg, bg = gColors.getColors(f)
|
||||
} else {
|
||||
switch {
|
||||
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
|
||||
}
|
||||
}
|
||||
fg, bg = colors.get(f)
|
||||
|
||||
path := filepath.Join(dir.path, f.Name())
|
||||
|
||||
@ -429,6 +349,7 @@ type ui struct {
|
||||
cmdBuf []rune
|
||||
keyAcc []rune
|
||||
keyCount []rune
|
||||
colors colorMap
|
||||
}
|
||||
|
||||
func getWidths(wtot int) []int {
|
||||
@ -485,6 +406,7 @@ func newUI() *ui {
|
||||
menuWin: newWin(wtot, 1, 0, htot-2),
|
||||
keyChan: make(chan string, 1000),
|
||||
evChan: evChan,
|
||||
colors: parseColors(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,7 +536,7 @@ func (ui *ui) draw(nav *nav) {
|
||||
|
||||
doff := len(nav.dirs) - length
|
||||
for i := 0; i < length; i++ {
|
||||
ui.wins[woff+i].printDir(nav.dirs[doff+i], nav.marks, nav.saves)
|
||||
ui.wins[woff+i].printDir(nav.dirs[doff+i], nav.marks, nav.saves, ui.colors)
|
||||
}
|
||||
|
||||
switch ui.cmdPrefix {
|
||||
@ -640,7 +562,7 @@ func (ui *ui) draw(nav *nav) {
|
||||
preview := ui.wins[len(ui.wins)-1]
|
||||
|
||||
if f.IsDir() {
|
||||
preview.printDir(ui.dirPrev, nav.marks, nav.saves)
|
||||
preview.printDir(ui.dirPrev, nav.marks, nav.saves, ui.colors)
|
||||
} else if f.Mode().IsRegular() {
|
||||
preview.printReg(ui.regPrev)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user