Skip to content

Commit

Permalink
Implement directory previews (#842)
Browse files Browse the repository at this point in the history
* Directory previews, initial implementation.

* Continue previous; add goroutine for directory previews.

* Enable dirpreviews as an option, run gofmt and fix referencing nil.

* Only use dirpreviews if previewer script exists.

* Remove redundant function call to `dirPreview`.

* Remove unnecessary dev-comments.

* Send to `dirPreviewChan` also on modifying folder contents.
  • Loading branch information
ilmari-h authored Oct 15, 2022
1 parent 4968b1d commit 479ed92
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 4 deletions.
5 changes: 5 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ func (app *app) writeHistory() error {
// separate goroutines and sent here for update.
func (app *app) loop() {
go app.nav.previewLoop(app.ui)
go app.nav.dirPreviewLoop(app.ui)

var serverChan <-chan expr
if !gSingleMode {
Expand Down Expand Up @@ -320,6 +321,7 @@ func (app *app) loop() {
app.quit()

app.nav.previewChan <- ""
app.nav.dirPreviewChan <- nil

log.Print("bye!")

Expand Down Expand Up @@ -384,6 +386,7 @@ func (app *app) loop() {
}
app.ui.draw(app.nav)
case d := <-app.nav.dirChan:

app.nav.checkDir(d)

if gOpts.dircache {
Expand Down Expand Up @@ -484,6 +487,8 @@ func (app *app) runShell(s string, args []string, prefix string) {
cmd.Stderr = os.Stderr

app.nav.previewChan <- ""
app.nav.dirPreviewChan <- nil

if err := app.ui.suspend(); err != nil {
log.Printf("suspend: %s", err)
}
Expand Down
1 change: 1 addition & 0 deletions complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ var (
"nodirfirst",
"dirfirst!",
"dironly",
"dirpreviews",
"nodironly",
"dironly!",
"drawbox",
Expand Down
5 changes: 5 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ The following options can be used to customize the behavior of lf:
dircounts bool (default off)
dirfirst bool (default on)
dironly bool (default off)
dirpreviews bool (default off)
drawbox bool (default off)
errorfmt string (default "\033[7;31;47m%s\033[0m")
filesep string (default "\n")
Expand Down Expand Up @@ -597,6 +598,10 @@ Show directories first above regular files.
dironly bool (default off)
If enabled, directories will also be passed to the previewer script. This allows custom previews for directories.
dirpreviews bool (default off)
Show only directories.
drawbox bool (default off)
Expand Down
2 changes: 2 additions & 0 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ func (e *setExpr) eval(app *app, args []string) {
app.nav.position()
app.ui.sort()
app.ui.loadFile(app.nav, true)
case "dirpreviews":
gOpts.dirpreviews = true
case "nodironly":
gOpts.dironly = false
app.nav.sort()
Expand Down
95 changes: 95 additions & 0 deletions nav.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ type dir struct {
ignorecase bool // ignorecase value from last sort
ignoredia bool // ignoredia value from last sort
noPerm bool // whether lf has no permission to open the directory
lines []string // lines of text to display if directory previews are enabled
}

func newDir(path string) *dir {
Expand All @@ -161,6 +162,7 @@ func newDir(path string) *dir {
}

return &dir{
loading: gOpts.dirpreviews, // Directory is loaded after previewer function exits.
loadTime: time,
path: path,
files: files,
Expand Down Expand Up @@ -365,6 +367,7 @@ type nav struct {
deleteCountChan chan int
deleteTotalChan chan int
previewChan chan string
dirPreviewChan chan *dir
dirChan chan *dir
regChan chan *reg
dirCache map[string]*dir
Expand Down Expand Up @@ -403,7 +406,11 @@ func (nav *nav) loadDirInternal(path string) *dir {
d := newDir(path)
d.sort()
d.ind, d.pos = 0, 0
if gOpts.dirpreviews {
nav.dirPreviewChan <- d
}
nav.dirChan <- d

}()
return d
}
Expand Down Expand Up @@ -447,6 +454,9 @@ func (nav *nav) checkDir(dir *dir) {
nd := newDir(dir.path)
nd.filter = dir.filter
nd.sort()
if gOpts.dirpreviews {
nav.dirPreviewChan <- nd
}
nav.dirChan <- nd
}()
case dir.sortType != gOpts.sortType ||
Expand Down Expand Up @@ -490,6 +500,7 @@ func newNav(height int) *nav {
deleteCountChan: make(chan int, 1024),
deleteTotalChan: make(chan int, 1024),
previewChan: make(chan string, 1024),
dirPreviewChan: make(chan *dir, 1024),
dirChan: make(chan *dir),
regChan: make(chan *reg),
dirCache: make(map[string]*dir),
Expand Down Expand Up @@ -599,6 +610,27 @@ func (nav *nav) exportFiles() {
exportFiles(currFile, currSelections, nav.currDir().path)
}

func (nav *nav) dirPreviewLoop(ui *ui) {
var prevPath string
for {
select {
case dir := <-nav.dirPreviewChan:

if dir == nil && len(gOpts.previewer) != 0 && len(gOpts.cleaner) != 0 && nav.volatilePreview {
cmd := exec.Command(gOpts.cleaner, prevPath)
if err := cmd.Run(); err != nil {
log.Printf("cleaning preview: %s", err)
}
nav.volatilePreview = false
} else if dir != nil {
win := ui.wins[len(ui.wins)-1]
nav.previewDir(dir, win)
prevPath = dir.path
}
}
}
}

func (nav *nav) previewLoop(ui *ui) {
var prev string
for path := range nav.previewChan {
Expand Down Expand Up @@ -648,7 +680,70 @@ func matchPattern(pattern, name, path string) bool {
return matched
}

func (nav *nav) previewDir(dir *dir, win *win) {

defer func() {
dir.loading = false
nav.dirChan <- dir
}()

var reader io.Reader

if len(gOpts.previewer) != 0 {
nav.exportFiles()
exportOpts()
cmd := exec.Command(gOpts.previewer, dir.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 dir: %s", err)
return
}

if err := cmd.Start(); err != nil {
log.Printf("previewing dir: %s", err)
out.Close()
return
}

defer func() {
if err := cmd.Wait(); err != nil {
if e, ok := err.(*exec.ExitError); ok {
if e.ExitCode() != 0 {
nav.volatilePreview = true
}
} else {
log.Printf("loading dir: %s", err)
}
}
}()
defer out.Close()
reader = out
buf := bufio.NewScanner(reader)

for i := 0; i < win.h && buf.Scan(); i++ {
for _, r := range buf.Text() {
if r == 0 {
dir.lines = []string{"\033[7mbinary\033[0m"}
return
}
}
dir.lines = append(dir.lines, buf.Text())
}

if buf.Err() != nil {
log.Printf("loading dir: %s", buf.Err())
}
}

}

func (nav *nav) preview(path string, win *win) {

reg := &reg{loadTime: time.Now(), path: path}
defer func() { nav.regChan <- reg }()

Expand Down
2 changes: 2 additions & 0 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var gOpts struct {
dircache bool
dircounts bool
dironly bool
dirpreviews bool
drawbox bool
globsearch bool
icons bool
Expand Down Expand Up @@ -84,6 +85,7 @@ func init() {
gOpts.dircache = true
gOpts.dircounts = false
gOpts.dironly = false
gOpts.dirpreviews = false
gOpts.drawbox = false
gOpts.globsearch = false
gOpts.icons = false
Expand Down
23 changes: 19 additions & 4 deletions ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,8 @@ func fileInfo(f *file, d *dir) string {
return info
}

func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]int, saves map[string]bool, tags map[string]string, colors styleMap, icons iconMap) {
func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]int, saves map[string]bool, tags map[string]string, colors styleMap, icons iconMap, previewAllowed bool) {

if win.w < 5 || dir == nil {
return
}
Expand All @@ -334,11 +335,25 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, selections map[string]in
return
}

if dir.loading && len(dir.files) == 0 {
if (dir.loading && len(dir.files) == 0) || (previewAllowed && dir.loading && gOpts.dirpreviews) {
win.print(screen, 2, 0, tcell.StyleDefault.Reverse(true), "loading...")
return
}

if previewAllowed && gOpts.dirpreviews && len(gOpts.previewer) > 0 {

// Print previewer result instead of default directory print operation.
st := tcell.StyleDefault
for i, l := range dir.lines {
if i > win.h-1 {
break
}

st = win.print(screen, 2, i, st, l)
}
return
}

if len(dir.files) == 0 {
win.print(screen, 2, 0, tcell.StyleDefault.Reverse(true), "empty")
return
Expand Down Expand Up @@ -855,7 +870,7 @@ func (ui *ui) draw(nav *nav) {

doff := len(nav.dirs) - length
for i := 0; i < length; i++ {
ui.wins[woff+i].printDir(ui.screen, nav.dirs[doff+i], nav.selections, nav.saves, nav.tags, ui.styles, ui.icons)
ui.wins[woff+i].printDir(ui.screen, nav.dirs[doff+i], nav.selections, nav.saves, nav.tags, ui.styles, ui.icons, false)
}

switch ui.cmdPrefix {
Expand Down Expand Up @@ -889,7 +904,7 @@ func (ui *ui) draw(nav *nav) {
preview := ui.wins[len(ui.wins)-1]

if curr.IsDir() {
preview.printDir(ui.screen, ui.dirPrev, nav.selections, nav.saves, nav.tags, ui.styles, ui.icons)
preview.printDir(ui.screen, ui.dirPrev, nav.selections, nav.saves, nav.tags, ui.styles, ui.icons, true)
} else if curr.Mode().IsRegular() {
preview.printReg(ui.screen, ui.regPrev)
}
Expand Down

0 comments on commit 479ed92

Please sign in to comment.