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
This commit is contained in:
Kennedy Mwenja 2019-10-04 21:44:52 +03:00 committed by gokcehan
parent 41339a9e5a
commit 9991a40e87
4 changed files with 32 additions and 1 deletions

View File

@ -179,6 +179,7 @@ The following additional keybindings are provided by default:
map st :set sortby time; set info time map st :set sortby time; set info time
map sa :set sortby atime; set info atime map sa :set sortby atime; set info atime
map sc :set sortby ctime; set info ctime map sc :set sortby ctime; set info ctime
map se :set sortby ext; set info
map gh cd ~ map gh cd ~
The following keybindings to applications are provided by default: The following keybindings to applications are provided by default:

View File

@ -251,8 +251,10 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.sortType.method = ctimeSort gOpts.sortType.method = ctimeSort
case "atime": case "atime":
gOpts.sortType.method = atimeSort gOpts.sortType.method = atimeSort
case "ext":
gOpts.sortType.method = extSort
default: 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 return
} }
app.nav.sort() app.nav.sort()

26
nav.go
View File

@ -32,6 +32,7 @@ type file struct {
dirCount int dirCount int
accessTime time.Time accessTime time.Time
changeTime time.Time changeTime time.Time
ext string
} }
func readdir(path string) ([]*file, error) { func readdir(path string) ([]*file, error) {
@ -77,6 +78,10 @@ func readdir(path string) ([]*file, error) {
ct = lstat.ModTime() 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{ files = append(files, &file{
FileInfo: lstat, FileInfo: lstat,
linkState: linkState, linkState: linkState,
@ -84,6 +89,7 @@ func readdir(path string) ([]*file, error) {
dirCount: -1, dirCount: -1,
accessTime: at, accessTime: at,
changeTime: ct, changeTime: ct,
ext: ext,
}) })
} }
@ -149,6 +155,26 @@ func (dir *dir) sort() {
sort.SliceStable(dir.files, func(i, j int) bool { sort.SliceStable(dir.files, func(i, j int) bool {
return dir.files[i].changeTime.Before(dir.files[j].changeTime) 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 { if gOpts.sortType.option&reverseSort != 0 {

View File

@ -11,6 +11,7 @@ const (
timeSort timeSort
atimeSort atimeSort
ctimeSort ctimeSort
extSort
) )
type sortOption byte type sortOption byte
@ -157,6 +158,7 @@ func init() {
gOpts.keys["st"] = &listExpr{[]expr{&setExpr{"sortby", "time"}, &setExpr{"info", "time"}}} gOpts.keys["st"] = &listExpr{[]expr{&setExpr{"sortby", "time"}, &setExpr{"info", "time"}}}
gOpts.keys["sa"] = &listExpr{[]expr{&setExpr{"sortby", "atime"}, &setExpr{"info", "atime"}}} gOpts.keys["sa"] = &listExpr{[]expr{&setExpr{"sortby", "atime"}, &setExpr{"info", "atime"}}}
gOpts.keys["sc"] = &listExpr{[]expr{&setExpr{"sortby", "ctime"}, &setExpr{"info", "ctime"}}} 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.keys["gh"] = &callExpr{"cd", []string{"~"}, 1}
gOpts.cmdkeys = make(map[string]expr) gOpts.cmdkeys = make(map[string]expr)