diff --git a/app.go b/app.go index 41316339..d81c76d9 100644 --- a/app.go +++ b/app.go @@ -300,6 +300,8 @@ func (app *app) loop() { }() } + onUiEnter(app) + for { select { case <-app.quitChan: @@ -322,6 +324,7 @@ func (app *app) loop() { cmd.eval(app, nil) } + onUiExit(app) app.quit() app.nav.previewChan <- "" @@ -461,12 +464,14 @@ func (app *app) loop() { app.ui.draw(app.nav) } } + } func (app *app) runCmdSync(cmd *exec.Cmd, pause_after bool) { app.nav.previewChan <- "" app.nav.dirPreviewChan <- nil + onUiExit(app) if err := app.ui.suspend(); err != nil { log.Printf("suspend: %s", err) } @@ -475,6 +480,7 @@ func (app *app) runCmdSync(cmd *exec.Cmd, pause_after bool) { app.quit() os.Exit(3) } + onUiEnter(app) }() if err := cmd.Run(); err != nil { @@ -495,6 +501,7 @@ func (app *app) runCmdSync(cmd *exec.Cmd, pause_after bool) { // % No No Yes Yes Yes Statline for input/output // ! Yes No Yes Yes Yes Pause and then resume // & No Yes No No No Do nothing +// ^ No No Yes Yes Yes (internal for events) func (app *app) runShell(s string, args []string, prefix string) { app.nav.exportFiles() app.ui.exportSizes() @@ -513,12 +520,17 @@ func (app *app) runShell(s string, args []string, prefix string) { var out io.Reader var err error switch prefix { - case "$", "!": + case "$", "!", "^": cmd.Stdin = os.Stdin cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr - - app.runCmdSync(cmd, prefix == "!") + if prefix != "^" { + app.runCmdSync(cmd, prefix == "!") + } else { + if err := cmd.Run(); err != nil { + app.ui.echoerrf("running ^shell: %s", err) + } + } return } diff --git a/doc.go b/doc.go index da7fe6ed..82343079 100644 --- a/doc.go +++ b/doc.go @@ -131,6 +131,7 @@ The following options can be used to customize the behavior of lf: errorfmt string (default "\033[7;31;47m") filesep string (default "\n") findlen int (default 1) + focus bool (default true) globsearch bool (default false) hidden bool (default false) hiddenfiles []string (default '.*') @@ -208,6 +209,8 @@ The following special shell commands are used to customize the behavior of lf wh pre-cd on-cd on-select + on-ui-enter + on-ui-exit on-quit The following commands/keybindings are provided by default: @@ -743,6 +746,10 @@ File separator used in environment variables 'fs' and 'fx'. Number of characters prompted for the find command. When this value is set to 0, find command prompts until there is only a single match left. + focus bool (default on) + +Focus displays the selection line inversed if enabled. It can be set from a terminal event to indicate if (or which) lf has focus. + globsearch bool (default false) When this option is enabled, search command patterns are considered as globs, otherwise they are literals. @@ -1110,6 +1117,14 @@ This shell command can be defined to be executed after changing a directory. This shell command can be defined to be executed after the selection changes. + on-ui-enter + +This shell command can be defined to be executed when the UI of lf becomes active. This will be triggered after lf starts and when foreground shell commands finish. + + on-ui-exit + +This shell command can be defined to be executed when the UI of lf becomes inactive. This will be triggered before lf exits and before starting foreground shell commands. + on-quit This shell command can be defined to be executed before quit. diff --git a/docstring.go b/docstring.go index a16a31c1..633367b1 100644 --- a/docstring.go +++ b/docstring.go @@ -134,6 +134,7 @@ The following options can be used to customize the behavior of lf: errorfmt string (default "\033[7;31;47m") filesep string (default "\n") findlen int (default 1) + focus bool (default true) globsearch bool (default false) hidden bool (default false) hiddenfiles []string (default '.*') @@ -212,6 +213,8 @@ when defined: pre-cd on-cd on-select + on-ui-enter + on-ui-exit on-quit The following commands/keybindings are provided by default: @@ -788,6 +791,11 @@ File separator used in environment variables 'fs' and 'fx'. Number of characters prompted for the find command. When this value is set to 0, find command prompts until there is only a single match left. + focus bool (default on) + +Focus displays the selection line inversed if enabled. It can be set from a +terminal event to indicate if (or which) lf has focus. + globsearch bool (default false) When this option is enabled, search command patterns are considered as globs, @@ -1202,6 +1210,18 @@ This shell command can be defined to be executed after changing a directory. This shell command can be defined to be executed after the selection changes. + on-ui-enter + +This shell command can be defined to be executed when the UI of lf becomes +active. This will be triggered after lf starts and when foreground shell +commands finish. + + on-ui-exit + +This shell command can be defined to be executed when the UI of lf becomes +inactive. This will be triggered before lf exits and before starting foreground +shell commands. + on-quit This shell command can be defined to be executed before quit. diff --git a/eval.go b/eval.go index ff04308e..fb37c3ce 100644 --- a/eval.go +++ b/eval.go @@ -831,6 +831,18 @@ func (e *setExpr) eval(app *app, args []string) { } else { gOpts.tempmarks = "'" } + case "focus": + gOpts.focus = true + app.ui.loadFile(app, true) + app.ui.loadFileInfo(app.nav) + case "nofocus": + gOpts.focus = false + app.ui.loadFile(app, true) + app.ui.loadFileInfo(app.nav) + case "focus!": + gOpts.focus = !gOpts.focus + app.ui.loadFile(app, true) + app.ui.loadFileInfo(app.nav) case "timefmt": gOpts.timefmt = e.val case "truncatechar": @@ -1157,6 +1169,22 @@ func onSelect(app *app) { } } +func onUiEnter(app *app) { + if cmd, ok := gOpts.cmds["on-ui-enter"]; ok { + ecmd := cmd.(*execExpr) + ecmd.prefix = "^" + ecmd.eval(app, nil) + } +} + +func onUiExit(app *app) { + if cmd, ok := gOpts.cmds["on-ui-exit"]; ok { + ecmd := cmd.(*execExpr) + ecmd.prefix = "^" + ecmd.eval(app, nil) + } +} + func splitKeys(s string) (keys []string) { for i := 0; i < len(s); { r, w := utf8.DecodeRuneInString(s[i:]) @@ -2764,6 +2792,9 @@ func (e *execExpr) eval(app *app, args []string) { case "&": log.Printf("shell-async: %s -- %s", e, args) app.runShell(e.value, args, e.prefix) + case "^": + log.Printf("shell-sync-evt: %s -- %s", e, args) + app.runShell(e.value, args, e.prefix) default: log.Printf("evaluating unknown execution prefix: %q", e.prefix) } diff --git a/lf.1 b/lf.1 index f7682fa0..10dc6812 100644 --- a/lf.1 +++ b/lf.1 @@ -150,6 +150,7 @@ The following options can be used to customize the behavior of lf: errorfmt string (default "\e033[7;31;47m") filesep string (default "\en") findlen int (default 1) + focus bool (default true) globsearch bool (default false) hidden bool (default false) hiddenfiles []string (default '.*') @@ -231,6 +232,8 @@ The following special shell commands are used to customize the behavior of lf wh pre-cd on-cd on-select + on-ui-enter + on-ui-exit on-quit .EE .PP @@ -905,6 +908,12 @@ File separator used in environment variables 'fs' and 'fx'. .PP Number of characters prompted for the find command. When this value is set to 0, find command prompts until there is only a single match left. .PP +.EX + focus bool (default on) +.EE +.PP +Focus displays the selection line inversed if enabled. It can be set from a terminal event to indicate if (or which) lf has focus. +.PP .EX globsearch bool (default false) .EE @@ -1332,6 +1341,18 @@ This shell command can be defined to be executed after changing a directory. .PP This shell command can be defined to be executed after the selection changes. .PP +.EX + on-ui-enter +.EE +.PP +This shell command can be defined to be executed when the UI of lf becomes active. This will be triggered after lf starts and when foreground shell commands finish. +.PP +.EX + on-ui-exit +.EE +.PP +This shell command can be defined to be executed when the UI of lf becomes inactive. This will be triggered before lf exits and before starting foreground shell commands. +.PP .EX on-quit .EE diff --git a/opts.go b/opts.go index bbce7997..3e5197be 100644 --- a/opts.go +++ b/opts.go @@ -94,6 +94,7 @@ var gOpts struct { tempmarks string numberfmt string tagfmt string + focus bool } var gLocalOpts struct { @@ -241,6 +242,7 @@ func init() { gOpts.tempmarks = "'" gOpts.numberfmt = "\033[33m" gOpts.tagfmt = "\033[31m" + gOpts.focus = true gOpts.keys = make(map[string]expr) diff --git a/ui.go b/ui.go index 9a181201..4cee02e5 100644 --- a/ui.go +++ b/ui.go @@ -486,7 +486,7 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, context *dirContext, dir } ce := "" - if i == dir.pos { + if i == dir.pos && gOpts.focus { switch dirStyle.role { case Active: ce = gOpts.cursoractivefmt @@ -504,7 +504,7 @@ func (win *win) printDir(screen tcell.Screen, dir *dir, context *dirContext, dir tag, ok := context.tags[path] if ok { - if i == dir.pos { + if i == dir.pos && gOpts.focus { win.print(screen, lnwidth+1, i, st, fmt.Sprintf(cursorescapefmt, tag)) } else { win.print(screen, lnwidth+1, i, tcell.StyleDefault, fmt.Sprintf(optionToFmtstr(gOpts.tagfmt), tag)) @@ -1449,17 +1449,32 @@ func (ui *ui) readNormalEvent(ev tcell.Event, nav *nav) expr { return nil } -func readCmdEvent(ev tcell.Event) expr { +func (ui *ui) readCmdEvent(ev tcell.Event) expr { switch tev := ev.(type) { case *tcell.EventKey: if tev.Key() == tcell.KeyRune { if tev.Modifiers() == tcell.ModMask(tcell.ModAlt) { - val := string([]rune{'<', 'a', '-', tev.Rune(), '>'}) - if expr, ok := gOpts.cmdkeys[val]; ok { - return expr + // support multi-key bindings but only for ESC[ at the moment + if tev.Rune() == '[' { + ui.keyAcc = append(ui.keyAcc, '<', 'a', '-', tev.Rune(), '>') + } else { + val := string([]rune{'<', 'a', '-', tev.Rune(), '>'}) + if expr, ok := gOpts.cmdkeys[val]; ok { + return expr + } } } else { - return &callExpr{"cmd-insert", []string{string(tev.Rune())}, 1} + // multi-key bindings + if ui.keyAcc != nil { + ui.keyAcc = append(ui.keyAcc, tev.Rune()) + val := string(ui.keyAcc) + ui.keyAcc = nil + if expr, ok := gOpts.cmdkeys[val]; ok { + return expr + } + } else { + return &callExpr{"cmd-insert", []string{string(tev.Rune())}, 1} + } } } else { val := gKeyVal[tev.Key()] @@ -1478,7 +1493,7 @@ func (ui *ui) readEvent(ev tcell.Event, nav *nav) expr { } if _, ok := ev.(*tcell.EventKey); ok && ui.cmdPrefix != "" { - return readCmdEvent(ev) + return ui.readCmdEvent(ev) } return ui.readNormalEvent(ev, nav)