diff --git a/app.go b/app.go index 4377214..2cddad1 100644 --- a/app.go +++ b/app.go @@ -213,6 +213,8 @@ func (app *app) loop() { continue } + go app.nav.previewClear() + log.Print("bye!") if err := app.writeHistory(); err != nil { @@ -310,7 +312,10 @@ func (app *app) loop() { app.ui.draw(app.nav) case r := <-app.nav.regChan: - app.nav.checkReg(r) + if app.nav.checkReg(r) { + win := app.ui.wins[len(app.ui.wins)-1] + go app.nav.preview(r.path, win) + } app.nav.regCache[r.path] = r @@ -402,6 +407,7 @@ func (app *app) runShell(s string, args []string, prefix string) { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + go app.nav.previewClear() app.ui.pause() defer app.ui.resume() defer app.nav.renew() diff --git a/complete.go b/complete.go index ae5cf68..5f85ab2 100644 --- a/complete.go +++ b/complete.go @@ -131,6 +131,7 @@ var ( "ifs", "info", "previewer", + "cleaner", "promptfmt", "ratios", "shell", diff --git a/doc.go b/doc.go index d276efc..9ebc91e 100644 --- a/doc.go +++ b/doc.go @@ -118,6 +118,7 @@ The following options can be used to customize the behavior of lf: period int (default 0) preview bool (default on) previewer string (default '') + cleaner string (default '') promptfmt string (default "\033[32;1m%u@%h\033[0m:\033[34;1m%w\033[0m\033[1m%f\033[0m") ratios []int (default '1:2:3') relativenumber bool (default off) @@ -591,10 +592,17 @@ Files containing the null character (U+0000) in the read portion are considered Set the path of a previewer file to filter the content of regular files for previewing. The file should be executable. -Two arguments are passed to the file, first is the current file name, and second is the height of preview pane. +Five arguments are passed to the file, first is the current file name; the second, third, fourth, and fifth are height, width, horizontal position, and vertical position of preview pane respectively. SIGPIPE signal is sent when enough lines are read. +If the previewer returns a non-zero exit code, then the preview cache for the given file is disabled. This means that if the file is selected in the future, the previewer is called once again. Preview filtering is disabled and files are displayed as they are when the value of this option is left empty. + cleaner string (default '') (not called if empty) + +Set the path of a cleaner file. This file will be called if previewing is enabled, the previewer is set, and the previously selected file had its preview cache disabled. +The file should be executable. +Preview clearing is disabled when the value of this option is left empty. + promptfmt string (default "\033[32;1m%u@%h\033[0m:\033[34;1m%w/\033[0m\033[1m%f\033[0m") Format string of the prompt shown in the top line. diff --git a/docstring.go b/docstring.go index 443bdec..f4a46d3 100644 --- a/docstring.go +++ b/docstring.go @@ -121,6 +121,7 @@ The following options can be used to customize the behavior of lf: period int (default 0) preview bool (default on) previewer string (default '') + cleaner string (default '') promptfmt string (default "\033[32;1m%u@%h\033[0m:\033[34;1m%w\033[0m\033[1m%f\033[0m") ratios []int (default '1:2:3') relativenumber bool (default off) @@ -624,11 +625,21 @@ binary files and displayed as 'binary'. previewer string (default '') (not filtered if empty) Set the path of a previewer file to filter the content of regular files for -previewing. The file should be executable. Two arguments are passed to the -file, first is the current file name, and second is the height of preview -pane. SIGPIPE signal is sent when enough lines are read. Preview filtering -is disabled and files are displayed as they are when the value of this -option is left empty. +previewing. The file should be executable. Five arguments are passed to the +file, first is the current file name; the second, third, fourth, and fifth +are height, width, horizontal position, and vertical position of preview +pane respectively. SIGPIPE signal is sent when enough lines are read. If the +previewer returns a non-zero exit code, then the preview cache for the given +file is disabled. This means that if the file is selected in the future, the +previewer is called once again. Preview filtering is disabled and files are +displayed as they are when the value of this option is left empty. + + cleaner string (default '') (not called if empty) + +Set the path of a cleaner file. This file will be called if previewing is +enabled, the previewer is set, and the previously selected file had its +preview cache disabled. The file should be executable. Preview clearing is +disabled when the value of this option is left empty. promptfmt string (default "\033[32;1m%u@%h\033[0m:\033[34;1m%w/\033[0m\033[1m%f\033[0m") diff --git a/eval.go b/eval.go index b201d9b..da3e90a 100644 --- a/eval.go +++ b/eval.go @@ -263,6 +263,8 @@ func (e *setExpr) eval(app *app, args []string) { gOpts.info = toks case "previewer": gOpts.previewer = replaceTilde(e.val) + case "cleaner": + gOpts.cleaner = replaceTilde(e.val) case "promptfmt": gOpts.promptfmt = e.val case "ratios": diff --git a/lf.1 b/lf.1 index 1f6a41d..4bc36ec 100644 --- a/lf.1 +++ b/lf.1 @@ -132,6 +132,7 @@ The following options can be used to customize the behavior of lf: period int (default 0) preview bool (default on) previewer string (default '') + cleaner string (default '') promptfmt string (default "\e033[32;1m%u@%h\e033[0m:\e033[34;1m%w\e033[0m\e033[1m%f\e033[0m") ratios []int (default '1:2:3') relativenumber bool (default off) @@ -712,7 +713,13 @@ Show previews of files and directories at the right most pane. If the file has m previewer string (default '') (not filtered if empty) .EE .PP -Set the path of a previewer file to filter the content of regular files for previewing. The file should be executable. Two arguments are passed to the file, first is the current file name, and second is the height of preview pane. SIGPIPE signal is sent when enough lines are read. Preview filtering is disabled and files are displayed as they are when the value of this option is left empty. +Set the path of a previewer file to filter the content of regular files for previewing. The file should be executable. Five arguments are passed to the file, first is the current file name; the second, third, fourth, and fifth are height, width, horizontal position, and vertical position of preview pane respectively. SIGPIPE signal is sent when enough lines are read. If the previewer returns a non-zero exit code, then the preview cache for the given file is disabled. This means that if the file is selected in the future, the previewer is called once again. Preview filtering is disabled and files are displayed as they are when the value of this option is left empty. +.PP +.EX + cleaner string (default '') (not called if empty) +.EE +.PP +Set the path of a cleaner file. This file will be called if previewing is enabled, the previewer is set, and the previously selected file had its preview cache disabled. The file should be executable. Preview clearing is disabled when the value of this option is left empty. .PP .EX promptfmt string (default "\e033[32;1m%u@%h\e033[0m:\e033[34;1m%w/\e033[0m\e033[1m%f\e033[0m") diff --git a/nav.go b/nav.go index befdc2c..29c3500 100644 --- a/nav.go +++ b/nav.go @@ -302,6 +302,7 @@ type nav struct { searchBack bool searchInd int searchPos int + volatilePreview bool } func (nav *nav) loadDir(path string) *dir { @@ -456,31 +457,51 @@ func (nav *nav) position() { } } -func (nav *nav) preview(path string) { +func (nav *nav) preview(path string, win *win) { reg := ®{loadTime: time.Now(), path: path} + defer func() { nav.regChan <- reg }() var reader io.Reader if len(gOpts.previewer) != 0 { exportOpts() - cmd := exec.Command(gOpts.previewer, path, strconv.Itoa(nav.height)) + cmd := exec.Command(gOpts.previewer, path, + strconv.Itoa(win.w), + strconv.Itoa(win.h), + strconv.Itoa(win.x), + strconv.Itoa(win.y)) out, err := cmd.StdoutPipe() if err != nil { log.Printf("previewing file: %s", err) + return } if err := cmd.Start(); err != nil { log.Printf("previewing file: %s", err) + out.Close() + return } - defer cmd.Wait() + defer func() { + if err := cmd.Wait(); err != nil { + if e, ok := err.(*exec.ExitError); ok { + if e.ExitCode() != 0 { + nav.volatilePreview = true + reg.volatile = true + } + } else { + log.Printf("loading file: %s", err) + } + } + }() defer out.Close() reader = out } else { f, err := os.Open(path) if err != nil { log.Printf("opening file: %s", err) + return } defer f.Close() @@ -489,11 +510,10 @@ func (nav *nav) preview(path string) { buf := bufio.NewScanner(reader) - for i := 0; i < nav.height && buf.Scan(); i++ { + for i := 0; i < win.h && buf.Scan(); i++ { for _, r := range buf.Text() { if r == 0 { reg.lines = []string{"\033[7mbinary\033[0m"} - nav.regChan <- reg return } } @@ -503,28 +523,39 @@ func (nav *nav) preview(path string) { if buf.Err() != nil { log.Printf("loading file: %s", buf.Err()) } - - nav.regChan <- reg } -func (nav *nav) loadReg(path string) *reg { +func (nav *nav) previewClear() { + if len(gOpts.cleaner) != 0 && nav.volatilePreview { + nav.volatilePreview = false + + cmd := exec.Command(gOpts.cleaner) + if err := cmd.Run(); err != nil { + log.Printf("cleaning preview: %s", err) + } + } +} + +func (nav *nav) loadReg(path string, win *win) *reg { r, ok := nav.regCache[path] - if !ok { - r := ®{loading: true, loadTime: time.Now(), path: path} + if !ok || r.volatile { + r := ®{loading: true, loadTime: time.Now(), path: path, volatile: true} nav.regCache[path] = r - go nav.preview(path) + go nav.preview(path, win) return r } - nav.checkReg(r) + if nav.checkReg(r) { + go nav.preview(path, win) + } return r } -func (nav *nav) checkReg(reg *reg) { +func (nav *nav) checkReg(reg *reg) bool { s, err := os.Stat(reg.path) if err != nil { - return + return false } now := time.Now() @@ -532,13 +563,15 @@ func (nav *nav) checkReg(reg *reg) { // 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 false } if s.ModTime().After(reg.loadTime) { reg.loadTime = now - go nav.preview(reg.path) + return true } + + return false } func (nav *nav) sort() { diff --git a/opts.go b/opts.go index 9070e5c..bdda51f 100644 --- a/opts.go +++ b/opts.go @@ -51,6 +51,7 @@ var gOpts struct { filesep string ifs string previewer string + cleaner string promptfmt string shell string timefmt string @@ -89,6 +90,7 @@ func init() { gOpts.filesep = "\n" gOpts.ifs = "" gOpts.previewer = "" + gOpts.cleaner = "" gOpts.promptfmt = "\033[32;1m%u@%h\033[0m:\033[34;1m%w\033[0m\033[1m%f\033[0m" gOpts.shell = gDefaultShell gOpts.timefmt = time.ANSIC diff --git a/ui.go b/ui.go index d58f307..6bcc889 100644 --- a/ui.go +++ b/ui.go @@ -609,6 +609,7 @@ func (ui *ui) echoerrf(format string, a ...interface{}) { type reg struct { loading bool + volatile bool loadTime time.Time path string lines []string @@ -624,10 +625,12 @@ func (ui *ui) loadFile(nav *nav) { return } + go nav.previewClear() if curr.IsDir() { ui.dirPrev = nav.loadDir(curr.path) } else if curr.Mode().IsRegular() { - ui.regPrev = nav.loadReg(curr.path) + win := ui.wins[len(ui.wins)-1] + ui.regPrev = nav.loadReg(curr.path, win) } }