Sort By Access Time and Change Time (#226)

- Add access time and change time as sort by types. This is
  powered by github.com/djherbis/times.
- Fall back to modification time if access time and change time
  cannot be determined.
- Add `sa` and `sc` as default bindings for sort by access time and
  sort by change time respectively.
- Add access time and change time to info types allowing them to be
  displayed by the file list in the ui
This commit is contained in:
Kennedy Mwenja 2019-09-18 21:52:30 +03:00 committed by gokcehan
parent d13cc79bc4
commit 9515bd73d0
7 changed files with 56 additions and 13 deletions

View File

@ -177,6 +177,8 @@ The following additional keybindings are provided by default:
map sn :set sortby natural; set info
map ss :set sortby size; set info size
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 gh cd ~
The following keybindings to applications are provided by default:

17
eval.go
View File

@ -238,10 +238,6 @@ func (e *setExpr) eval(app *app, args []string) {
case "shell":
gOpts.shell = e.val
case "sortby":
if e.val != "natural" && e.val != "name" && e.val != "size" && e.val != "time" {
app.ui.echoerr("sortby: value should either be 'natural', 'name', 'size' or 'time'")
return
}
switch e.val {
case "natural":
gOpts.sortType.method = naturalSort
@ -251,6 +247,13 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.sortType.method = sizeSort
case "time":
gOpts.sortType.method = timeSort
case "ctime":
gOpts.sortType.method = ctimeSort
case "atime":
gOpts.sortType.method = atimeSort
default:
app.ui.echoerr("sortby: value should either be 'natural', 'name', 'size', 'time', 'atime' or 'ctime'")
return
}
app.nav.sort()
app.ui.sort()
@ -281,8 +284,10 @@ func (e *setExpr) eval(app *app, args []string) {
case "info":
toks := strings.Split(e.val, ":")
for _, s := range toks {
if s != "" && s != "size" && s != "time" {
app.ui.echoerr("info: should consist of 'size' or 'time' separated with colon")
switch s {
case "", "size", "time", "atime", "ctime":
default:
app.ui.echoerr("info: should consist of 'size', 'time', 'atime' or 'ctime' separated with colon")
return
}
}

1
go.mod
View File

@ -5,4 +5,5 @@ go 1.12
require (
github.com/mattn/go-runewidth v0.0.4
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e
gopkg.in/djherbis/times.v1 v1.2.0
)

2
go.sum
View File

@ -2,3 +2,5 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k=
github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
gopkg.in/djherbis/times.v1 v1.2.0 h1:UCvDKl1L/fmBygl2Y7hubXCnY7t4Yj46ZrBFNUipFbM=
gopkg.in/djherbis/times.v1 v1.2.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=

39
nav.go
View File

@ -13,6 +13,8 @@ import (
"strconv"
"strings"
"time"
times "gopkg.in/djherbis/times.v1"
)
type linkState byte
@ -25,9 +27,11 @@ const (
type file struct {
os.FileInfo
linkState linkState
path string
dirCount int
linkState linkState
path string
dirCount int
accessTime time.Time
changeTime time.Time
}
func readdir(path string) ([]*file, error) {
@ -62,11 +66,24 @@ func readdir(path string) ([]*file, error) {
}
}
ts := times.Get(lstat)
at := ts.AccessTime()
var ct time.Time
// from times docs: ChangeTime() panics unless HasChangeTime() is true
if ts.HasChangeTime() {
ct = ts.ChangeTime()
} else {
// fall back to ModTime if ChangeTime cannot be determined
ct = lstat.ModTime()
}
files = append(files, &file{
FileInfo: lstat,
linkState: linkState,
path: fpath,
dirCount: -1,
FileInfo: lstat,
linkState: linkState,
path: fpath,
dirCount: -1,
accessTime: at,
changeTime: ct,
})
}
@ -124,6 +141,14 @@ func (dir *dir) sort() {
sort.SliceStable(dir.files, func(i, j int) bool {
return dir.files[i].ModTime().Before(dir.files[j].ModTime())
})
case atimeSort:
sort.SliceStable(dir.files, func(i, j int) bool {
return dir.files[i].accessTime.Before(dir.files[j].accessTime)
})
case ctimeSort:
sort.SliceStable(dir.files, func(i, j int) bool {
return dir.files[i].changeTime.Before(dir.files[j].changeTime)
})
}
if gOpts.sortType.option&reverseSort != 0 {

View File

@ -9,6 +9,8 @@ const (
nameSort
sizeSort
timeSort
atimeSort
ctimeSort
)
type sortOption byte
@ -153,6 +155,8 @@ func init() {
gOpts.keys["sn"] = &listExpr{[]expr{&setExpr{"sortby", "natural"}, &setExpr{"info", ""}}}
gOpts.keys["ss"] = &listExpr{[]expr{&setExpr{"sortby", "size"}, &setExpr{"info", "size"}}}
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["gh"] = &callExpr{"cd", []string{"~"}, 1}
gOpts.cmdkeys = make(map[string]expr)

4
ui.go
View File

@ -231,6 +231,10 @@ func fileInfo(f *file, d *dir) string {
}
case "time":
info = fmt.Sprintf("%s %12s", info, f.ModTime().Format("Jan _2 15:04"))
case "atime":
info = fmt.Sprintf("%s %12s", info, f.accessTime.Format("Jan _2 15:04"))
case "ctime":
info = fmt.Sprintf("%s %12s", info, f.changeTime.Format("Jan _2 15:04"))
default:
log.Printf("unknown info type: %s", s)
}