Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement directory previews #842

Merged
merged 7 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,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 @@ -318,6 +319,7 @@ func (app *app) loop() {
app.quit()

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

log.Print("bye!")

Expand Down Expand Up @@ -382,6 +384,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 @@ -482,6 +485,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 @@ -90,6 +90,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 @@ -117,6 +117,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 @@ -591,6 +592,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 @@ -644,7 +676,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 @@ -83,6 +84,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 @@ -848,7 +863,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 @@ -882,7 +897,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