Add ability to use image previewers (#531)
* Return early on error in nav.preview * nav.checkReg now returns a boolean instead of calling nav.preview * Pass width, height, x, and y parameters to preview script * Check previewer for exit code If non-zero the preview will be assumed to have side-effects. * Add the cleaner option This is called upon selection changes if the previous preview was volatile. To this end, volatilePreview was added to the nav struct
This commit is contained in:
parent
c2fe88e9a0
commit
82f03102a5
8
app.go
8
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()
|
||||
|
@ -131,6 +131,7 @@ var (
|
||||
"ifs",
|
||||
"info",
|
||||
"previewer",
|
||||
"cleaner",
|
||||
"promptfmt",
|
||||
"ratios",
|
||||
"shell",
|
||||
|
10
doc.go
10
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.
|
||||
|
21
docstring.go
21
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")
|
||||
|
||||
|
2
eval.go
2
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":
|
||||
|
9
lf.1
9
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")
|
||||
|
65
nav.go
65
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() {
|
||||
|
2
opts.go
2
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
|
||||
|
5
ui.go
5
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user