From 9991a40e87ff74b7a21fcaeda0414593a4627093 Mon Sep 17 00:00:00 2001 From: Kennedy Mwenja Date: Fri, 4 Oct 2019 21:44:52 +0300 Subject: [PATCH] Sort by file extensions (#230) - preserve natural ordering of filenames if extensions are the same or are missing - files without extensions rank higher on ascending sort and lower on descending sort --- docstring.go | 1 + eval.go | 4 +++- nav.go | 26 ++++++++++++++++++++++++++ opts.go | 2 ++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docstring.go b/docstring.go index 2132b35..438b31f 100644 --- a/docstring.go +++ b/docstring.go @@ -179,6 +179,7 @@ The following additional keybindings are provided by default: map st :set sortby time; set info time map sa :set sortby atime; set info atime map sc :set sortby ctime; set info ctime + map se :set sortby ext; set info map gh cd ~ The following keybindings to applications are provided by default: diff --git a/eval.go b/eval.go index 9f4a5eb..2d0ade2 100644 --- a/eval.go +++ b/eval.go @@ -251,8 +251,10 @@ func (e *setExpr) eval(app *app, args []string) { gOpts.sortType.method = ctimeSort case "atime": gOpts.sortType.method = atimeSort + case "ext": + gOpts.sortType.method = extSort default: - app.ui.echoerr("sortby: value should either be 'natural', 'name', 'size', 'time', 'atime' or 'ctime'") + app.ui.echoerr("sortby: value should either be 'natural', 'name', 'size', 'time', 'atime', 'ctime' or 'ext'") return } app.nav.sort() diff --git a/nav.go b/nav.go index 7b369c5..1921387 100644 --- a/nav.go +++ b/nav.go @@ -32,6 +32,7 @@ type file struct { dirCount int accessTime time.Time changeTime time.Time + ext string } func readdir(path string) ([]*file, error) { @@ -77,6 +78,10 @@ func readdir(path string) ([]*file, error) { ct = lstat.ModTime() } + // returns an empty string if extension could not be determined + // i.e. directories, filenames without extensions + ext := filepath.Ext(fpath) + files = append(files, &file{ FileInfo: lstat, linkState: linkState, @@ -84,6 +89,7 @@ func readdir(path string) ([]*file, error) { dirCount: -1, accessTime: at, changeTime: ct, + ext: ext, }) } @@ -149,6 +155,26 @@ func (dir *dir) sort() { sort.SliceStable(dir.files, func(i, j int) bool { return dir.files[i].changeTime.Before(dir.files[j].changeTime) }) + case extSort: + sort.SliceStable(dir.files, func(i, j int) bool { + leftExt := strings.ToLower(dir.files[i].ext) + rightExt := strings.ToLower(dir.files[j].ext) + + // if the extension could not be determined (directories, files without) + // use a zero byte so that these files can be ranked higher + if leftExt == "" { + leftExt = "\x00" + } + if rightExt == "" { + rightExt = "\x00" + } + + // in order to also have natural sorting with the filenames + // combine the name with the ext but have the ext at the front + left := leftExt + strings.ToLower(dir.files[i].Name()) + right := rightExt + strings.ToLower(dir.files[j].Name()) + return left < right + }) } if gOpts.sortType.option&reverseSort != 0 { diff --git a/opts.go b/opts.go index aef8c06..25cdc1c 100644 --- a/opts.go +++ b/opts.go @@ -11,6 +11,7 @@ const ( timeSort atimeSort ctimeSort + extSort ) type sortOption byte @@ -157,6 +158,7 @@ func init() { gOpts.keys["st"] = &listExpr{[]expr{&setExpr{"sortby", "time"}, &setExpr{"info", "time"}}} gOpts.keys["sa"] = &listExpr{[]expr{&setExpr{"sortby", "atime"}, &setExpr{"info", "atime"}}} gOpts.keys["sc"] = &listExpr{[]expr{&setExpr{"sortby", "ctime"}, &setExpr{"info", "ctime"}}} + gOpts.keys["se"] = &listExpr{[]expr{&setExpr{"sortby", "ext"}, &setExpr{"info", ""}}} gOpts.keys["gh"] = &callExpr{"cd", []string{"~"}, 1} gOpts.cmdkeys = make(map[string]expr)