fix option processing, async dirs & start select (#759)

This commit is contained in:
Christian Zangl 2022-02-27 12:00:59 +01:00 committed by GitHub
parent b4637f91aa
commit fb66a21577
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 215 additions and 142 deletions

105
app.go
View File

@ -32,6 +32,24 @@ type app struct {
cmdHistoryInd int
}
var gInvalidate struct {
sort bool
pos bool
dir bool
navSize bool
mouse bool
period bool
}
func init() {
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
gInvalidate.navSize = true
gInvalidate.mouse = true
gInvalidate.period = true
}
func newApp(ui *ui, nav *nav) *app {
quitChan := make(chan struct{}, 1)
@ -245,19 +263,6 @@ func (app *app) loop() {
app.ui.readExpr()
if gSelect != "" {
go func() {
lstat, err := os.Lstat(gSelect)
if err != nil {
app.ui.exprChan <- &callExpr{"echoerr", []string{err.Error()}, 1}
} else if lstat.IsDir() {
app.ui.exprChan <- &callExpr{"cd", []string{gSelect}, 1}
} else {
app.ui.exprChan <- &callExpr{"select", []string{gSelect}, 1}
}
}()
}
if gConfigPath != "" {
if _, err := os.Stat(gConfigPath); !os.IsNotExist(err) {
app.readFile(gConfigPath)
@ -272,6 +277,30 @@ func (app *app) loop() {
}
}
// config has been read, now initialize nav with the wd
wd, err := os.Getwd()
if err != nil {
log.Printf("getting current directory: %s", err)
}
if gSelect != "" {
_, err := os.Lstat(gSelect)
if err != nil {
app.ui.exprChan <- &callExpr{"echoerr", []string{err.Error()}, 1}
} else if abs, err := filepath.Abs(gSelect); err == nil {
// if gSelect contains the /. suffix, the directory itself
// should be selected
if len(gSelect) > 2 && gSelect[len(gSelect)-2:] == "/." {
wd = abs
} else {
wd = filepath.Dir(abs)
app.ui.exprChan <- &callExpr{"select", []string{abs}, 1}
}
}
}
app.nav.getDirs(wd)
app.nav.addJumpList()
// execute commands from args
for _, cmd := range gCommands {
p := newParser(strings.NewReader(cmd))
@ -285,6 +314,49 @@ func (app *app) loop() {
}
for {
// process invalidate flags
if gInvalidate.sort {
app.nav.sort()
app.ui.sort()
gInvalidate.sort = false
}
if gInvalidate.pos {
app.nav.position()
gInvalidate.pos = false
}
if gInvalidate.dir {
app.ui.loadFile(app.nav, true)
app.ui.loadFileInfo(app.nav)
app.ui.draw(app.nav)
gInvalidate.dir = false
}
if gInvalidate.navSize {
app.ui.renew()
if app.nav.height != app.ui.wins[0].h {
app.nav.height = app.ui.wins[0].h
app.nav.regCache = make(map[string]*reg)
}
gInvalidate.navSize = false
}
if gInvalidate.mouse {
if gOpts.mouse {
app.ui.screen.EnableMouse()
} else {
app.ui.screen.DisableMouse()
}
gInvalidate.mouse = false
}
if gInvalidate.period {
if gOpts.period == 0 {
app.ticker.Stop()
} else {
app.ticker.Stop()
app.ticker = time.NewTicker(time.Duration(gOpts.period) * time.Second)
}
gInvalidate.period = false
}
select {
case <-app.quitChan:
if app.nav.copyTotal > 0 {
@ -369,7 +441,12 @@ func (app *app) loop() {
}
app.ui.draw(app.nav)
case d := <-app.nav.dirChan:
app.nav.checkDir(d)
if !app.nav.checkDir(d) {
log.Printf("debug: dirChan skip/reload %s", d.path)
continue
}
log.Printf("debug: dirChan %s %t", d.path, d.loading)
if gOpts.dircache {
prev, ok := app.nav.dirCache[d.path]

181
eval.go
View File

@ -7,12 +7,12 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
func (e *setExpr) eval(app *app, args []string) {
// from app only app.ui.echo*() may be used inside this function
switch e.opt {
case "anchorfind":
gOpts.anchorfind = true
@ -40,91 +40,70 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.dircounts = !gOpts.dircounts
case "dironly":
gOpts.dironly = true
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "nodironly":
gOpts.dironly = false
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "dironly!":
gOpts.dironly = !gOpts.dironly
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "dirfirst":
gOpts.sortType.option |= dirfirstSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "nodirfirst":
gOpts.sortType.option &= ^dirfirstSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "dirfirst!":
gOpts.sortType.option ^= dirfirstSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "drawbox":
gOpts.drawbox = true
app.ui.renew()
if app.nav.height != app.ui.wins[0].h {
app.nav.height = app.ui.wins[0].h
app.nav.regCache = make(map[string]*reg)
}
app.ui.loadFile(app.nav, true)
gInvalidate.navSize = true
gInvalidate.dir = true
case "nodrawbox":
gOpts.drawbox = false
app.ui.renew()
if app.nav.height != app.ui.wins[0].h {
app.nav.height = app.ui.wins[0].h
app.nav.regCache = make(map[string]*reg)
}
app.ui.loadFile(app.nav, true)
gInvalidate.navSize = true
gInvalidate.dir = true
case "drawbox!":
gOpts.drawbox = !gOpts.drawbox
app.ui.renew()
if app.nav.height != app.ui.wins[0].h {
app.nav.height = app.ui.wins[0].h
app.nav.regCache = make(map[string]*reg)
}
app.ui.loadFile(app.nav, true)
gInvalidate.navSize = true
gInvalidate.dir = true
case "globsearch":
gOpts.globsearch = true
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "noglobsearch":
gOpts.globsearch = false
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "globsearch!":
gOpts.globsearch = !gOpts.globsearch
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "hidden":
gOpts.sortType.option |= hiddenSort
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "nohidden":
gOpts.sortType.option &= ^hiddenSort
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "hidden!":
gOpts.sortType.option ^= hiddenSort
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "icons":
gOpts.icons = true
case "noicons":
@ -133,31 +112,28 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.icons = !gOpts.icons
case "ignorecase":
gOpts.ignorecase = true
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "noignorecase":
gOpts.ignorecase = false
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "ignorecase!":
gOpts.ignorecase = !gOpts.ignorecase
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "ignoredia":
gOpts.ignoredia = true
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "noignoredia":
gOpts.ignoredia = false
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "ignoredia!":
gOpts.ignoredia = !gOpts.ignoredia
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "incfilter":
gOpts.incfilter = true
case "noincfilter":
@ -173,21 +149,20 @@ func (e *setExpr) eval(app *app, args []string) {
case "mouse":
if !gOpts.mouse {
gOpts.mouse = true
app.ui.screen.EnableMouse()
gInvalidate.mouse = true
}
case "nomouse":
if gOpts.mouse {
gOpts.mouse = false
app.ui.screen.DisableMouse()
gInvalidate.mouse = true
}
case "mouse!":
if gOpts.mouse {
gOpts.mouse = false
app.ui.screen.DisableMouse()
} else {
gOpts.mouse = true
app.ui.screen.EnableMouse()
}
gInvalidate.mouse = true
case "number":
gOpts.number = true
case "nonumber":
@ -216,31 +191,28 @@ func (e *setExpr) eval(app *app, args []string) {
gOpts.relativenumber = !gOpts.relativenumber
case "reverse":
gOpts.sortType.option |= reverseSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "noreverse":
gOpts.sortType.option &= ^reverseSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "reverse!":
gOpts.sortType.option ^= reverseSort
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "smartcase":
gOpts.smartcase = true
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "nosmartcase":
gOpts.smartcase = false
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "smartcase!":
gOpts.smartcase = !gOpts.smartcase
app.nav.sort()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.dir = true
case "smartdia":
gOpts.smartdia = true
case "nosmartdia":
@ -283,12 +255,7 @@ func (e *setExpr) eval(app *app, args []string) {
return
}
gOpts.period = n
if n == 0 {
app.ticker.Stop()
} else {
app.ticker.Stop()
app.ticker = time.NewTicker(time.Duration(gOpts.period) * time.Second)
}
gInvalidate.period = true
case "scrolloff":
n, err := strconv.Atoi(e.val)
if err != nil {
@ -329,10 +296,9 @@ func (e *setExpr) eval(app *app, args []string) {
}
}
gOpts.hiddenfiles = toks
app.nav.sort()
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
gInvalidate.sort = true
gInvalidate.pos = true
gInvalidate.dir = true
case "ifs":
gOpts.ifs = e.val
case "info":
@ -377,7 +343,7 @@ func (e *setExpr) eval(app *app, args []string) {
}
gOpts.ratios = rats
app.ui.wins = getWins(app.ui.screen)
app.ui.loadFile(app.nav, true)
gInvalidate.dir = true
case "shell":
gOpts.shell = e.val
case "shellflag":
@ -408,8 +374,8 @@ func (e *setExpr) eval(app *app, args []string) {
app.ui.echoerr("sortby: value should either be 'natural', 'name', 'size', 'time', 'atime', 'ctime' or 'ext'")
return
}
app.nav.sort()
app.ui.sort()
gInvalidate.sort = true
gInvalidate.dir = true
case "tempmarks":
if e.val != "" {
gOpts.tempmarks = "'" + e.val
@ -433,7 +399,6 @@ func (e *setExpr) eval(app *app, args []string) {
app.ui.echoerrf("unknown option: %s", e.opt)
return
}
app.ui.loadFileInfo(app.nav)
}
func (e *mapExpr) eval(app *app, args []string) {
@ -442,7 +407,6 @@ func (e *mapExpr) eval(app *app, args []string) {
} else {
gOpts.keys[e.keys] = e.expr
}
app.ui.loadFileInfo(app.nav)
}
func (e *cmapExpr) eval(app *app, args []string) {
@ -451,7 +415,6 @@ func (e *cmapExpr) eval(app *app, args []string) {
} else {
gOpts.cmdkeys[e.key] = e.expr
}
app.ui.loadFileInfo(app.nav)
}
func (e *cmdExpr) eval(app *app, args []string) {
@ -460,7 +423,6 @@ func (e *cmdExpr) eval(app *app, args []string) {
} else {
gOpts.cmds[e.name] = e.expr
}
app.ui.loadFileInfo(app.nav)
}
func preChdir(app *app) {
@ -1294,6 +1256,7 @@ func (e *callExpr) eval(app *app, args []string) {
if err != nil {
log.Printf("getting current directory: %s", err)
}
log.Printf("todo-cd-eval %s", wd)
path = replaceTilde(path)
if !filepath.IsAbs(path) {

67
nav.go
View File

@ -163,6 +163,10 @@ func normalize(s1, s2 string, ignorecase, ignoredia bool) (string, string) {
}
func (dir *dir) sort() {
if dir.loading {
log.Printf("debug: sort/dir still loading: %s", dir.path)
return
}
dir.sortType = gOpts.sortType
dir.dironly = gOpts.dironly
dir.hiddenfiles = gOpts.hiddenfiles
@ -306,6 +310,10 @@ func (dir *dir) name() string {
}
func (dir *dir) sel(name string, height int) {
if dir.loading {
//log.Printf("debug: sel/dir still loading: %s", dir.path)
return
}
if len(dir.files) == 0 {
dir.ind, dir.pos = 0, 0
return
@ -391,11 +399,13 @@ func (nav *nav) loadDir(path string) *dir {
if gOpts.dircache {
d, ok := nav.dirCache[path]
if !ok {
log.Printf("debug: dirCache-new: %s", path)
d = nav.loadDirInternal(path)
nav.dirCache[path] = d
return d
}
log.Printf("debug: dirCache-found: %s", path)
nav.checkDir(d)
return d
@ -404,11 +414,16 @@ func (nav *nav) loadDir(path string) *dir {
}
}
func (nav *nav) checkDir(dir *dir) {
func (nav *nav) checkDir(dir *dir) bool {
if dir.loading {
log.Printf("debug: skip loading: %s", dir.path)
return false
}
s, err := os.Stat(dir.path)
if err != nil {
log.Printf("getting directory info: %s", err)
return
return true
}
switch {
@ -418,9 +433,10 @@ func (nav *nav) checkDir(dir *dir) {
// XXX: Linux builtin exFAT drivers are able to predict modifications in the future
// https://bugs.launchpad.net/ubuntu/+source/ubuntu-meta/+bug/1872504
if s.ModTime().After(now) {
return
return true
}
log.Printf("debug: checkDir-reload: %s time", dir.path)
dir.loading = true
dir.loadTime = now
go func() {
@ -429,18 +445,23 @@ func (nav *nav) checkDir(dir *dir) {
nd.sort()
nav.dirChan <- nd
}()
return false
case dir.sortType != gOpts.sortType ||
dir.dironly != gOpts.dironly ||
!reflect.DeepEqual(dir.hiddenfiles, gOpts.hiddenfiles) ||
dir.ignorecase != gOpts.ignorecase ||
dir.ignoredia != gOpts.ignoredia:
log.Printf("debug: checkDir-reload: %s opt", dir.path)
sd := dir
dir.loading = true
dir.loadTime = time.Now()
go func() {
dir.sort()
dir.loading = false
nav.dirChan <- dir
sd.sort()
nav.dirChan <- sd
}()
return false
}
return true
}
func (nav *nav) getDirs(wd string) {
@ -460,10 +481,6 @@ func (nav *nav) getDirs(wd string) {
}
func newNav(height int) *nav {
wd, err := os.Getwd()
if err != nil {
log.Printf("getting current directory: %s", err)
}
nav := &nav{
copyBytesChan: make(chan int64, 1024),
@ -486,8 +503,7 @@ func newNav(height int) *nav {
jumpListInd: -1,
}
nav.getDirs(wd)
nav.addJumpList()
// do not call nav.getDirs() as our configuration isn't set up yet
return nav
}
@ -573,7 +589,14 @@ func (nav *nav) exportFiles() {
currSelections := nav.currSelections()
exportFiles(currFile, currSelections, nav.currDir().path)
var wd string
if currDir := nav.currDir(); currDir != nil {
wd = currDir.path
} else {
wd, _ = os.Getwd()
}
exportFiles(currFile, currSelections, wd)
}
func (nav *nav) previewLoop(ui *ui) {
@ -1252,19 +1275,26 @@ func (nav *nav) sync() error {
}
func (nav *nav) cd(wd string) error {
currDir := nav.currDir()
wd = replaceTilde(wd)
wd = filepath.Clean(wd)
if !filepath.IsAbs(wd) {
wd = filepath.Join(nav.currDir().path, wd)
if !filepath.IsAbs(wd) && currDir != nil {
wd = filepath.Join(currDir.path, wd)
}
if err := os.Chdir(wd); err != nil {
return fmt.Errorf("cd: %s", err)
}
if currDir == nil || wd != currDir.path {
nav.getDirs(wd)
nav.addJumpList()
} else {
log.Printf("debug: skip cd: %s", wd)
}
return nil
}
@ -1542,13 +1572,16 @@ func (nav *nav) writeMarks() error {
}
func (nav *nav) currDir() *dir {
if len(nav.dirs) == 0 {
return nil
}
return nav.dirs[len(nav.dirs)-1]
}
func (nav *nav) currFile() (*file, error) {
dir := nav.dirs[len(nav.dirs)-1]
dir := nav.currDir()
if len(dir.files) == 0 {
if dir == nil || len(dir.files) == 0 {
return nil, fmt.Errorf("empty directory")
}