From b401318046c4d4260ab982e9a4ce1dabf5b5623f Mon Sep 17 00:00:00 2001 From: Joe Lim <50560759+joelim-work@users.noreply.github.com> Date: Thu, 18 Jan 2024 11:15:06 +1100 Subject: [PATCH] Implement sort options as separate variables (#1577) --- eval.go | 37 ++++++++++---------- main.go | 7 +--- nav.go | 71 ++++++++++++++++++++++----------------- opts.go | 102 +++++++++++++++++++++++--------------------------------- 4 files changed, 100 insertions(+), 117 deletions(-) diff --git a/eval.go b/eval.go index 59bfc621..25be197b 100644 --- a/eval.go +++ b/eval.go @@ -137,9 +137,9 @@ func (e *setExpr) eval(app *app, args []string) { gOpts.dircounts = !gOpts.dircounts case "dirfirst": if e.val == "" || e.val == "true" { - gOpts.sortType.option |= dirfirstSort + gOpts.dirfirst = true } else if e.val == "false" { - gOpts.sortType.option &= ^dirfirstSort + gOpts.dirfirst = false } else { app.ui.echoerr("dirfirst: value should be empty, 'true', or 'false'") return @@ -151,7 +151,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("nodirfirst: unexpected value: %s", e.val) return } - gOpts.sortType.option &= ^dirfirstSort + gOpts.dirfirst = false app.nav.sort() app.ui.sort() case "dirfirst!": @@ -159,7 +159,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("dirfirst!: unexpected value: %s", e.val) return } - gOpts.sortType.option ^= dirfirstSort + gOpts.dirfirst = !gOpts.dirfirst app.nav.sort() app.ui.sort() case "dironly": @@ -304,9 +304,9 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.loadFile(app, true) case "hidden": if e.val == "" || e.val == "true" { - gOpts.sortType.option |= hiddenSort + gOpts.hidden = true } else if e.val == "false" { - gOpts.sortType.option &= ^hiddenSort + gOpts.hidden = false } else { app.ui.echoerr("hidden: value should be empty, 'true', or 'false'") return @@ -320,7 +320,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("nohidden: unexpected value: %s", e.val) return } - gOpts.sortType.option &= ^hiddenSort + gOpts.hidden = false app.nav.sort() app.nav.position() app.ui.sort() @@ -330,7 +330,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("hidden!: unexpected value: %s", e.val) return } - gOpts.sortType.option ^= hiddenSort + gOpts.hidden = !gOpts.hidden app.nav.sort() app.nav.position() app.ui.sort() @@ -708,9 +708,9 @@ func (e *setExpr) eval(app *app, args []string) { gOpts.relativenumber = !gOpts.relativenumber case "reverse": if e.val == "" || e.val == "true" { - gOpts.sortType.option |= reverseSort + gOpts.reverse = true } else if e.val == "false" { - gOpts.sortType.option &= ^reverseSort + gOpts.reverse = false } else { app.ui.echoerr("reverse: value should be empty, 'true', or 'false'") return @@ -722,7 +722,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("noreverse: unexpected value: %s", e.val) return } - gOpts.sortType.option &= ^reverseSort + gOpts.reverse = false app.nav.sort() app.ui.sort() case "reverse!": @@ -730,7 +730,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerrf("reverse!: unexpected value: %s", e.val) return } - gOpts.sortType.option ^= reverseSort + gOpts.reverse = !gOpts.reverse app.nav.sort() app.ui.sort() case "scrolloff": @@ -821,7 +821,7 @@ func (e *setExpr) eval(app *app, args []string) { app.ui.echoerr(invalidSortErrorMessage) return } - gOpts.sortType.method = method + gOpts.sortby = method app.nav.sort() app.ui.sort() case "statfmt": @@ -977,8 +977,7 @@ func (e *setLocalExpr) eval(app *app, args []string) { if val, ok := gLocalOpts.dirfirsts[path]; ok { gLocalOpts.dirfirsts[path] = !val } else { - val = gOpts.sortType.option&dirfirstSort != 0 - gLocalOpts.dirfirsts[path] = !val + gLocalOpts.dirfirsts[path] = !gOpts.dirfirst } app.nav.sort() app.ui.sort() @@ -1040,8 +1039,7 @@ func (e *setLocalExpr) eval(app *app, args []string) { if val, ok := gLocalOpts.hiddens[path]; ok { gLocalOpts.hiddens[path] = !val } else { - val = gOpts.sortType.option&hiddenSort != 0 - gLocalOpts.hiddens[path] = !val + gLocalOpts.hiddens[path] = !gOpts.hidden } app.nav.sort() app.ui.sort() @@ -1087,8 +1085,7 @@ func (e *setLocalExpr) eval(app *app, args []string) { if val, ok := gLocalOpts.reverses[path]; ok { gLocalOpts.reverses[path] = !val } else { - val = gOpts.sortType.option&reverseSort != 0 - gLocalOpts.reverses[path] = !val + gLocalOpts.reverses[path] = !gOpts.reverse } app.nav.sort() app.ui.sort() @@ -1098,7 +1095,7 @@ func (e *setLocalExpr) eval(app *app, args []string) { app.ui.echoerr(invalidSortErrorMessage) return } - gLocalOpts.sortMethods[path] = method + gLocalOpts.sortbys[path] = method app.nav.sort() app.ui.sort() default: diff --git a/main.go b/main.go index 759f72f5..985d842b 100644 --- a/main.go +++ b/main.go @@ -137,12 +137,7 @@ func getOptsMap() map[string]string { continue } - if name == "lf_sortType" { - opts["lf_sortby"] = string(gOpts.sortType.method) - opts["lf_reverse"] = strconv.FormatBool(gOpts.sortType.option&reverseSort != 0) - opts["lf_hidden"] = strconv.FormatBool(gOpts.sortType.option&hiddenSort != 0) - opts["lf_dirfirst"] = strconv.FormatBool(gOpts.sortType.option&dirfirstSort != 0) - } else if name == "lf_user" { + if name == "lf_user" { // set each user option for key, value := range gOpts.user { opts[name+"_"+key] = value diff --git a/nav.go b/nav.go index c63e8297..8acedde6 100644 --- a/nav.go +++ b/nav.go @@ -161,21 +161,24 @@ func readdir(path string) ([]*file, error) { } type dir struct { - loading bool // directory is loading from disk - loadTime time.Time // current loading or last load time - ind int // index of current entry in files - pos int // position of current entry in ui - path string // full path of directory - files []*file // displayed files in directory including or excluding hidden ones - allFiles []*file // all files in directory including hidden ones (same array as files) - sortType sortType // sort method and options from last sort - dironly bool // dironly value from last sort - hiddenfiles []string // hiddenfiles value from last sort - filter []string // last filter for this directory - ignorecase bool // ignorecase value from last sort - ignoredia bool // ignoredia value from last sort - noPerm bool // whether lf has no permission to open the directory - lines []string // lines of text to display if directory previews are enabled + loading bool // directory is loading from disk + loadTime time.Time // current loading or last load time + ind int // index of current entry in files + pos int // position of current entry in ui + path string // full path of directory + files []*file // displayed files in directory including or excluding hidden ones + allFiles []*file // all files in directory including hidden ones (same array as files) + sortby sortMethod // sortby value from last sort + dirfirst bool // dirfirst value from last sort + dironly bool // dironly value from last sort + hidden bool // hidden value from last sort + reverse bool // reverse value from last sort + hiddenfiles []string // hiddenfiles value from last sort + filter []string // last filter for this directory + ignorecase bool // ignorecase value from last sort + ignoredia bool // ignoredia value from last sort + noPerm bool // whether lf has no permission to open the directory + lines []string // lines of text to display if directory previews are enabled } func newDir(path string) *dir { @@ -209,8 +212,11 @@ func normalize(s1, s2 string, ignorecase, ignoredia bool) (string, string) { } func (dir *dir) sort() { - dir.sortType = getSortType(dir.path) + dir.sortby = getSortBy(dir.path) + dir.dirfirst = getDirFirst(dir.path) dir.dironly = getDirOnly(dir.path) + dir.hidden = getHidden(dir.path) + dir.reverse = getReverse(dir.path) dir.hiddenfiles = gOpts.hiddenfiles dir.ignorecase = gOpts.ignorecase dir.ignoredia = gOpts.ignoredia @@ -219,13 +225,11 @@ func (dir *dir) sort() { // reverse order cannot be applied after stable sorting, otherwise the order // of equivalent elements will be reversed - reverse := dir.sortType.option&reverseSort != 0 - - switch dir.sortType.method { + switch dir.sortby { case naturalSort: sort.SliceStable(dir.files, func(i, j int) bool { s1, s2 := normalize(dir.files[i].Name(), dir.files[j].Name(), dir.ignorecase, dir.ignoredia) - if !reverse { + if !dir.reverse { return naturalLess(s1, s2) } else { return naturalLess(s2, s1) @@ -234,7 +238,7 @@ func (dir *dir) sort() { case nameSort: sort.SliceStable(dir.files, func(i, j int) bool { s1, s2 := normalize(dir.files[i].Name(), dir.files[j].Name(), dir.ignorecase, dir.ignoredia) - if !reverse { + if !dir.reverse { return s1 < s2 } else { return s2 < s1 @@ -242,7 +246,7 @@ func (dir *dir) sort() { }) case sizeSort: sort.SliceStable(dir.files, func(i, j int) bool { - if !reverse { + if !dir.reverse { return dir.files[i].TotalSize() < dir.files[j].TotalSize() } else { return dir.files[j].TotalSize() < dir.files[i].TotalSize() @@ -250,7 +254,7 @@ func (dir *dir) sort() { }) case timeSort: sort.SliceStable(dir.files, func(i, j int) bool { - if !reverse { + if !dir.reverse { return dir.files[i].ModTime().Before(dir.files[j].ModTime()) } else { return dir.files[j].ModTime().Before(dir.files[i].ModTime()) @@ -258,7 +262,7 @@ func (dir *dir) sort() { }) case atimeSort: sort.SliceStable(dir.files, func(i, j int) bool { - if !reverse { + if !dir.reverse { return dir.files[i].accessTime.Before(dir.files[j].accessTime) } else { return dir.files[j].accessTime.Before(dir.files[i].accessTime) @@ -266,7 +270,7 @@ func (dir *dir) sort() { }) case ctimeSort: sort.SliceStable(dir.files, func(i, j int) bool { - if !reverse { + if !dir.reverse { return dir.files[i].changeTime.Before(dir.files[j].changeTime) } else { return dir.files[j].changeTime.Before(dir.files[i].changeTime) @@ -289,7 +293,7 @@ func (dir *dir) sort() { // in order to also have natural sorting with the filenames // combine the name with the ext but have the ext at the front - if !reverse { + if !dir.reverse { return ext1 < ext2 || ext1 == ext2 && name1 < name2 } else { return ext2 < ext1 || ext2 == ext1 && name2 < name1 @@ -297,7 +301,7 @@ func (dir *dir) sort() { }) } - if dir.sortType.option&dirfirstSort != 0 { + if dir.dirfirst { sort.SliceStable(dir.files, func(i, j int) bool { if dir.files[i].IsDir() == dir.files[j].IsDir() { return i < j @@ -329,7 +333,7 @@ func (dir *dir) sort() { // when hidden option is disabled, we move hidden files to the // beginning of our file list and then set the beginning of displayed // files to the first non-hidden file in the list - if dir.sortType.option&hiddenSort == 0 { + if !dir.hidden { sort.SliceStable(dir.files, func(i, j int) bool { if isHidden(dir.files[i], dir.path, dir.hiddenfiles) && isHidden(dir.files[j], dir.path, dir.hiddenfiles) { return i < j @@ -471,7 +475,11 @@ func (nav *nav) loadDirInternal(path string) *dir { loading: true, loadTime: time.Now(), path: path, - sortType: getSortType(path), + sortby: getSortBy(path), + dirfirst: getDirFirst(path), + dironly: getDirOnly(path), + hidden: getHidden(path), + reverse: getReverse(path), hiddenfiles: gOpts.hiddenfiles, ignorecase: gOpts.ignorecase, ignoredia: gOpts.ignoredia, @@ -534,8 +542,11 @@ func (nav *nav) checkDir(dir *dir) { } nav.dirChan <- nd }() - case dir.sortType != getSortType(dir.path) || + case dir.sortby != getSortBy(dir.path) || + dir.dirfirst != getDirFirst(dir.path) || dir.dironly != getDirOnly(dir.path) || + dir.hidden != getHidden(dir.path) || + dir.reverse != getReverse(dir.path) || !reflect.DeepEqual(dir.hiddenfiles, gOpts.hiddenfiles) || dir.ignorecase != gOpts.ignorecase || dir.ignoredia != gOpts.ignoredia: diff --git a/opts.go b/opts.go index 315943be..fc211e05 100644 --- a/opts.go +++ b/opts.go @@ -29,19 +29,6 @@ func isValidSortMethod(method sortMethod) bool { const invalidSortErrorMessage = `sortby: value should either be 'natural', 'name', 'size', 'time', 'atime', 'ctime' or 'ext'` -type sortOption byte - -const ( - dirfirstSort sortOption = 1 << iota - hiddenSort - reverseSort -) - -type sortType struct { - method sortMethod - option sortOption -} - var gOpts struct { anchorfind bool autoquit bool @@ -54,11 +41,13 @@ var gOpts struct { hidecursorinactive bool dircache bool dircounts bool + dirfirst bool dironly bool dirpreviews bool drawbox bool dupfilefmt string globsearch bool + hidden bool icons bool ignorecase bool ignoredia bool @@ -67,9 +56,11 @@ var gOpts struct { mouse bool number bool preview bool + relativenumber bool + reverse bool selectfmt string sixel bool - relativenumber bool + sortby sortMethod smartcase bool smartdia bool waitmsg string @@ -106,19 +97,18 @@ var gOpts struct { cmdkeys map[string]expr cmds map[string]expr user map[string]string - sortType sortType tempmarks string numberfmt string tagfmt string } var gLocalOpts struct { - sortMethods map[string]sortMethod - dirfirsts map[string]bool - dironlys map[string]bool - hiddens map[string]bool - reverses map[string]bool - infos map[string][]string + sortbys map[string]sortMethod + dirfirsts map[string]bool + dironlys map[string]bool + hiddens map[string]bool + reverses map[string]bool + infos map[string][]string } func localOptPaths(path string) []string { @@ -130,71 +120,58 @@ func localOptPaths(path string) []string { return list } -func getSortMethod(path string) sortMethod { +func getDirFirst(path string) bool { for _, key := range localOptPaths(path) { - if val, ok := gLocalOpts.sortMethods[key]; ok { + if val, ok := gLocalOpts.dirfirsts[key]; ok { return val } } - return gOpts.sortType.method + return gOpts.dirfirst } -func getSortType(path string) sortType { - method := getSortMethod(path) - option := gOpts.sortType.option +func getDirOnly(path string) bool { for _, key := range localOptPaths(path) { - if val, ok := gLocalOpts.dirfirsts[key]; ok { - if val { - option |= dirfirstSort - } else { - option &= ^dirfirstSort - } - break + if val, ok := gLocalOpts.dironlys[key]; ok { + return val } } + return gOpts.dironly +} + +func getHidden(path string) bool { for _, key := range localOptPaths(path) { if val, ok := gLocalOpts.hiddens[key]; ok { - if val { - option |= hiddenSort - } else { - option &= ^hiddenSort - } - break + return val } } + return gOpts.hidden +} + +func getInfo(path string) []string { for _, key := range localOptPaths(path) { - if val, ok := gLocalOpts.reverses[key]; ok { - if val { - option |= reverseSort - } else { - option &= ^reverseSort - } - break + if val, ok := gLocalOpts.infos[key]; ok { + return val } } - val := sortType{ - method: method, - option: option, - } - return val + return gOpts.info } -func getDirOnly(path string) bool { +func getReverse(path string) bool { for _, key := range localOptPaths(path) { - if val, ok := gLocalOpts.dironlys[key]; ok { + if val, ok := gLocalOpts.reverses[key]; ok { return val } } - return gOpts.dironly + return gOpts.reverse } -func getInfo(path string) []string { +func getSortBy(path string) sortMethod { for _, key := range localOptPaths(path) { - if val, ok := gLocalOpts.infos[key]; ok { + if val, ok := gLocalOpts.sortbys[key]; ok { return val } } - return gOpts.info + return gOpts.sortby } func init() { @@ -202,6 +179,7 @@ func init() { gOpts.autoquit = false gOpts.dircache = true gOpts.dircounts = false + gOpts.dirfirst = true gOpts.dironly = false gOpts.dirpreviews = false gOpts.drawbox = false @@ -214,6 +192,7 @@ func init() { gOpts.cutfmt = "\033[7;31m" gOpts.hidecursorinactive = false gOpts.globsearch = false + gOpts.hidden = false gOpts.icons = false gOpts.ignorecase = true gOpts.ignoredia = true @@ -222,9 +201,11 @@ func init() { gOpts.mouse = false gOpts.number = false gOpts.preview = true + gOpts.relativenumber = false + gOpts.reverse = false gOpts.selectfmt = "\033[7;35m" gOpts.sixel = false - gOpts.relativenumber = false + gOpts.sortby = naturalSort gOpts.smartcase = true gOpts.smartdia = false gOpts.waitmsg = "Press any key to continue" @@ -257,7 +238,6 @@ func init() { 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} gOpts.tempmarks = "'" gOpts.numberfmt = "\033[33m" gOpts.tagfmt = "\033[31m" @@ -380,7 +360,7 @@ func init() { gOpts.cmds = make(map[string]expr) gOpts.user = make(map[string]string) - gLocalOpts.sortMethods = make(map[string]sortMethod) + gLocalOpts.sortbys = make(map[string]sortMethod) gLocalOpts.dirfirsts = make(map[string]bool) gLocalOpts.dironlys = make(map[string]bool) gLocalOpts.hiddens = make(map[string]bool)