add a push command to simulate key pushes
This commit is contained in:
parent
155fbc5a16
commit
27135faccd
32
doc.go
32
doc.go
@ -37,8 +37,9 @@ The following commands are provided by lf with default keybindings.
|
||||
|
||||
The following commands are provided by lf without default keybindings.
|
||||
|
||||
echo
|
||||
cd
|
||||
echo prints its arguments to the message line
|
||||
cd changes working directory to its argument
|
||||
push simulate key pushes given in its argument
|
||||
|
||||
The following options can be used to customize the behavior of lf.
|
||||
|
||||
@ -104,7 +105,32 @@ group statements until a "\n" occurs. This is especially useful for "map" and
|
||||
"cmd" commands. If you need multiline you can wrap statements in "{{" and "}}"
|
||||
after the proper prefix.
|
||||
|
||||
Custom Commands
|
||||
Mappings
|
||||
|
||||
The usual way to map a key sequence is to assign it to a named or unnamed
|
||||
command. While this provides a clean way to remap builtin keys as well as other
|
||||
commands, it can be limiting at times. For this reason "push" command is
|
||||
provided by lf. This command is used to simulate key pushes given as its
|
||||
arguments. You can "map" a key to a "push" command with an argument to create
|
||||
various keybindings.
|
||||
|
||||
This is mainly useful for two purposes. First, it can be used to map a command
|
||||
with a command count.
|
||||
|
||||
map <c-j> push 10j
|
||||
|
||||
Second, it can be used to avoid typing the name when a command takes arguments.
|
||||
|
||||
map r push :rename<space>
|
||||
|
||||
One thing to be careful is that since "push" command works with keys instead of
|
||||
commands it is possible to accidentally create recursive bindings.
|
||||
|
||||
map j push 2j
|
||||
|
||||
These types of bindings create a deadlock when executed.
|
||||
|
||||
Commands
|
||||
|
||||
For demonstration let us write a shell command to move selected file(s) to
|
||||
trash.
|
||||
|
34
docstring.go
34
docstring.go
@ -41,8 +41,9 @@ The following commands are provided by lf with default keybindings.
|
||||
|
||||
The following commands are provided by lf without default keybindings.
|
||||
|
||||
echo
|
||||
cd
|
||||
echo prints its arguments to the message line
|
||||
cd changes working directory to its argument
|
||||
push simulate key pushes given in its argument
|
||||
|
||||
The following options can be used to customize the behavior of lf.
|
||||
|
||||
@ -113,7 +114,34 @@ and "cmd" commands. If you need multiline you can wrap statements in "{{"
|
||||
and "}}" after the proper prefix.
|
||||
|
||||
|
||||
Custom Commands
|
||||
Mappings
|
||||
|
||||
The usual way to map a key sequence is to assign it to a named or unnamed
|
||||
command. While this provides a clean way to remap builtin keys as well as
|
||||
other commands, it can be limiting at times. For this reason "push" command
|
||||
is provided by lf. This command is used to simulate key pushes given as its
|
||||
arguments. You can "map" a key to a "push" command with an argument to
|
||||
create various keybindings.
|
||||
|
||||
This is mainly useful for two purposes. First, it can be used to map a
|
||||
command with a command count.
|
||||
|
||||
map <c-j> push 10j
|
||||
|
||||
Second, it can be used to avoid typing the name when a command takes
|
||||
arguments.
|
||||
|
||||
map r push :rename<space>
|
||||
|
||||
One thing to be careful is that since "push" command works with keys instead
|
||||
of commands it is possible to accidentally create recursive bindings.
|
||||
|
||||
map j push 2j
|
||||
|
||||
These types of bindings create a deadlock when executed.
|
||||
|
||||
|
||||
Commands
|
||||
|
||||
For demonstration let us write a shell command to move selected file(s) to
|
||||
trash.
|
||||
|
@ -74,6 +74,7 @@ cmd open-file ${{
|
||||
|
||||
# rename current file without overwrite
|
||||
cmd rename $[ -e "$1" ] || mv "$f" "$1"
|
||||
map r push :rename<space>
|
||||
|
||||
# show disk usage
|
||||
cmd usage $du -h . | less
|
||||
|
24
eval.go
24
eval.go
@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func (e *SetExpr) eval(app *App, args []string) {
|
||||
@ -120,6 +121,25 @@ func (e *CmdExpr) eval(app *App, args []string) {
|
||||
gOpts.cmds[e.name] = e.expr
|
||||
}
|
||||
|
||||
func splitKeys(s string) (keys []string) {
|
||||
for i := 0; i < len(s); {
|
||||
c, w := utf8.DecodeRuneInString(s[i:])
|
||||
if c != '<' {
|
||||
keys = append(keys, s[i:i+w])
|
||||
i += w
|
||||
} else {
|
||||
j := i + w
|
||||
for c != '>' && j < len(s) {
|
||||
c, w = utf8.DecodeRuneInString(s[j:])
|
||||
j += w
|
||||
}
|
||||
keys = append(keys, s[i:j])
|
||||
i = j
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *CallExpr) eval(app *App, args []string) {
|
||||
// TODO: check for extra toks in each case
|
||||
switch e.name {
|
||||
@ -303,6 +323,10 @@ func (e *CallExpr) eval(app *App, args []string) {
|
||||
return
|
||||
}
|
||||
app.ui.loadFile(app.nav)
|
||||
case "push":
|
||||
if len(e.args) > 0 {
|
||||
app.ui.keysbuf = append(app.ui.keysbuf, splitKeys(strings.Join(e.args, ""))...)
|
||||
}
|
||||
default:
|
||||
cmd, ok := gOpts.cmds[e.name]
|
||||
if !ok {
|
||||
|
36
eval_test.go
36
eval_test.go
@ -1,7 +1,11 @@
|
||||
package main
|
||||
|
||||
// These inputs are used in scan and parse tests.
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// These inputs are used in scan and parse tests.
|
||||
var gTests = []struct {
|
||||
inp string
|
||||
toks []string
|
||||
@ -199,3 +203,33 @@ var gTests = []struct {
|
||||
`}}},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSplitKeys(t *testing.T) {
|
||||
inps := []struct {
|
||||
s string
|
||||
keys []string
|
||||
}{
|
||||
{"", nil},
|
||||
{"j", []string{"j"}},
|
||||
{"jk", []string{"j", "k"}},
|
||||
{"1j", []string{"1", "j"}},
|
||||
{"42j", []string{"4", "2", "j"}},
|
||||
{"<space>", []string{"<space>"}},
|
||||
{"j<space>", []string{"j", "<space>"}},
|
||||
{"j<space>k", []string{"j", "<space>", "k"}},
|
||||
{"1j<space>k", []string{"1", "j", "<space>", "k"}},
|
||||
{"1j<space>1k", []string{"1", "j", "<space>", "1", "k"}},
|
||||
{"<>", []string{"<>"}},
|
||||
{"<space", []string{"<space"}},
|
||||
{"<space<", []string{"<space<"}},
|
||||
{"<space<>", []string{"<space<>"}},
|
||||
{"><space>", []string{">", "<space>"}},
|
||||
{"><space>>", []string{">", "<space>", ">"}},
|
||||
}
|
||||
|
||||
for _, inp := range inps {
|
||||
if keys := splitKeys(inp.s); !reflect.DeepEqual(keys, inp.keys) {
|
||||
t.Errorf("at input '%s' expected '%v' but got '%v'", inp.s, inp.keys, keys)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
217
ui.go
217
ui.go
@ -22,6 +22,74 @@ import (
|
||||
|
||||
const EscapeCode = 27
|
||||
|
||||
var gKeyVal = map[termbox.Key][]rune{
|
||||
termbox.KeyF1: []rune{'<', 'f', '-', '1', '>'},
|
||||
termbox.KeyF2: []rune{'<', 'f', '-', '2', '>'},
|
||||
termbox.KeyF3: []rune{'<', 'f', '-', '3', '>'},
|
||||
termbox.KeyF4: []rune{'<', 'f', '-', '4', '>'},
|
||||
termbox.KeyF5: []rune{'<', 'f', '-', '5', '>'},
|
||||
termbox.KeyF6: []rune{'<', 'f', '-', '6', '>'},
|
||||
termbox.KeyF7: []rune{'<', 'f', '-', '7', '>'},
|
||||
termbox.KeyF8: []rune{'<', 'f', '-', '8', '>'},
|
||||
termbox.KeyF9: []rune{'<', 'f', '-', '9', '>'},
|
||||
termbox.KeyF10: []rune{'<', 'f', '-', '1', '0', '>'},
|
||||
termbox.KeyF11: []rune{'<', 'f', '-', '1', '1', '>'},
|
||||
termbox.KeyF12: []rune{'<', 'f', '-', '1', '2', '>'},
|
||||
termbox.KeyInsert: []rune{'<', 'i', 'n', 's', 'e', 'r', 't', '>'},
|
||||
termbox.KeyDelete: []rune{'<', 'd', 'e', 'l', 'e', 't', 'e', '>'},
|
||||
termbox.KeyHome: []rune{'<', 'h', 'o', 'm', 'e', '>'},
|
||||
termbox.KeyEnd: []rune{'<', 'e', 'n', 'd', '>'},
|
||||
termbox.KeyPgup: []rune{'<', 'p', 'g', 'u', 'p', '>'},
|
||||
termbox.KeyPgdn: []rune{'<', 'p', 'g', 'd', 'n', '>'},
|
||||
termbox.KeyArrowUp: []rune{'<', 'u', 'p', '>'},
|
||||
termbox.KeyArrowDown: []rune{'<', 'd', 'o', 'w', 'n', '>'},
|
||||
termbox.KeyArrowLeft: []rune{'<', 'l', 'e', 'f', 't', '>'},
|
||||
termbox.KeyArrowRight: []rune{'<', 'r', 'i', 'g', 'h', 't', '>'},
|
||||
termbox.KeyCtrlSpace: []rune{'<', 'c', '-', 's', 'p', 'a', 'c', 'e', '>'},
|
||||
termbox.KeyCtrlA: []rune{'<', 'c', '-', 'a', '>'},
|
||||
termbox.KeyCtrlB: []rune{'<', 'c', '-', 'b', '>'},
|
||||
termbox.KeyCtrlC: []rune{'<', 'c', '-', 'c', '>'},
|
||||
termbox.KeyCtrlD: []rune{'<', 'c', '-', 'd', '>'},
|
||||
termbox.KeyCtrlE: []rune{'<', 'c', '-', 'e', '>'},
|
||||
termbox.KeyCtrlF: []rune{'<', 'c', '-', 'f', '>'},
|
||||
termbox.KeyCtrlG: []rune{'<', 'c', '-', 'g', '>'},
|
||||
termbox.KeyBackspace: []rune{'<', 'b', 's', '>'},
|
||||
termbox.KeyTab: []rune{'<', 't', 'a', 'b', '>'},
|
||||
termbox.KeyCtrlJ: []rune{'<', 'c', '-', 'j', '>'},
|
||||
termbox.KeyCtrlK: []rune{'<', 'c', '-', 'k', '>'},
|
||||
termbox.KeyCtrlL: []rune{'<', 'c', '-', 'l', '>'},
|
||||
termbox.KeyEnter: []rune{'<', 'e', 'n', 't', 'e', 'r', '>'},
|
||||
termbox.KeyCtrlN: []rune{'<', 'c', '-', 'n', '>'},
|
||||
termbox.KeyCtrlO: []rune{'<', 'c', '-', 'o', '>'},
|
||||
termbox.KeyCtrlP: []rune{'<', 'c', '-', 'p', '>'},
|
||||
termbox.KeyCtrlQ: []rune{'<', 'c', '-', 'q', '>'},
|
||||
termbox.KeyCtrlR: []rune{'<', 'c', '-', 'r', '>'},
|
||||
termbox.KeyCtrlS: []rune{'<', 'c', '-', 's', '>'},
|
||||
termbox.KeyCtrlT: []rune{'<', 'c', '-', 't', '>'},
|
||||
termbox.KeyCtrlU: []rune{'<', 'c', '-', 'u', '>'},
|
||||
termbox.KeyCtrlV: []rune{'<', 'c', '-', 'v', '>'},
|
||||
termbox.KeyCtrlW: []rune{'<', 'c', '-', 'w', '>'},
|
||||
termbox.KeyCtrlX: []rune{'<', 'c', '-', 'x', '>'},
|
||||
termbox.KeyCtrlY: []rune{'<', 'c', '-', 'y', '>'},
|
||||
termbox.KeyCtrlZ: []rune{'<', 'c', '-', 'z', '>'},
|
||||
termbox.KeyEsc: []rune{'<', 'e', 's', 'c', '>'},
|
||||
termbox.KeyCtrlBackslash: []rune{'<', 'c', '-', '\\', '>'},
|
||||
termbox.KeyCtrlRsqBracket: []rune{'<', 'c', '-', ']', '>'},
|
||||
termbox.KeyCtrl6: []rune{'<', 'c', '-', '6', '>'},
|
||||
termbox.KeyCtrlSlash: []rune{'<', 'c', '-', '/', '>'},
|
||||
termbox.KeySpace: []rune{'<', 's', 'p', 'a', 'c', 'e', '>'},
|
||||
termbox.KeyBackspace2: []rune{'<', 'b', 's', '2', '>'},
|
||||
}
|
||||
|
||||
var gValKey map[string]termbox.Key
|
||||
|
||||
func init() {
|
||||
gValKey = make(map[string]termbox.Key)
|
||||
for k, v := range gKeyVal {
|
||||
gValKey[string(v)] = k
|
||||
}
|
||||
}
|
||||
|
||||
type Win struct {
|
||||
w int
|
||||
h int
|
||||
@ -253,6 +321,7 @@ type UI struct {
|
||||
message string
|
||||
regprev []string
|
||||
dirprev *Dir
|
||||
keysbuf []string
|
||||
}
|
||||
|
||||
func getWidths(wtot int) []int {
|
||||
@ -467,6 +536,35 @@ func findBinds(keys map[string]Expr, prefix string) (binds map[string]Expr, ok b
|
||||
return
|
||||
}
|
||||
|
||||
func (ui *UI) pollEvent() termbox.Event {
|
||||
if len(ui.keysbuf) > 0 {
|
||||
ev := termbox.Event{Type: termbox.EventKey}
|
||||
keys := ui.keysbuf[0]
|
||||
if len(keys) == 1 {
|
||||
ev.Ch, _ = utf8.DecodeRuneInString(keys)
|
||||
} else {
|
||||
switch keys {
|
||||
case "<lt>":
|
||||
ev.Ch = '<'
|
||||
case "<gt>":
|
||||
ev.Ch = '>'
|
||||
default:
|
||||
if val, ok := gValKey[keys]; ok {
|
||||
ev.Key = val
|
||||
} else {
|
||||
ev.Key = termbox.KeyEsc
|
||||
msg := fmt.Sprintf("unknown key: %s", keys)
|
||||
ui.message = msg
|
||||
log.Print(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
ui.keysbuf = ui.keysbuf[1:]
|
||||
return ev
|
||||
}
|
||||
return termbox.PollEvent()
|
||||
}
|
||||
|
||||
func (ui *UI) getExpr(nav *Nav) (expr Expr, count int) {
|
||||
expr = &CallExpr{"renew", nil}
|
||||
count = 1
|
||||
@ -475,7 +573,7 @@ func (ui *UI) getExpr(nav *Nav) (expr Expr, count int) {
|
||||
var cnt []rune
|
||||
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
switch ev := ui.pollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
if ev.Ch != 0 {
|
||||
switch {
|
||||
@ -492,121 +590,12 @@ func (ui *UI) getExpr(nav *Nav) (expr Expr, count int) {
|
||||
acc = append(acc, ev.Ch)
|
||||
}
|
||||
} else {
|
||||
switch ev.Key {
|
||||
case termbox.KeyF1:
|
||||
acc = append(acc, '<', 'f', '-', '1', '>')
|
||||
case termbox.KeyF2:
|
||||
acc = append(acc, '<', 'f', '-', '2', '>')
|
||||
case termbox.KeyF3:
|
||||
acc = append(acc, '<', 'f', '-', '3', '>')
|
||||
case termbox.KeyF4:
|
||||
acc = append(acc, '<', 'f', '-', '4', '>')
|
||||
case termbox.KeyF5:
|
||||
acc = append(acc, '<', 'f', '-', '5', '>')
|
||||
case termbox.KeyF6:
|
||||
acc = append(acc, '<', 'f', '-', '6', '>')
|
||||
case termbox.KeyF7:
|
||||
acc = append(acc, '<', 'f', '-', '7', '>')
|
||||
case termbox.KeyF8:
|
||||
acc = append(acc, '<', 'f', '-', '8', '>')
|
||||
case termbox.KeyF9:
|
||||
acc = append(acc, '<', 'f', '-', '9', '>')
|
||||
case termbox.KeyF10:
|
||||
acc = append(acc, '<', 'f', '-', '1', '0', '>')
|
||||
case termbox.KeyF11:
|
||||
acc = append(acc, '<', 'f', '-', '1', '1', '>')
|
||||
case termbox.KeyF12:
|
||||
acc = append(acc, '<', 'f', '-', '1', '2', '>')
|
||||
case termbox.KeyInsert:
|
||||
acc = append(acc, '<', 'i', 'n', 's', 'e', 'r', 't', '>')
|
||||
case termbox.KeyDelete:
|
||||
acc = append(acc, '<', 'd', 'e', 'l', 'e', 't', 'e', '>')
|
||||
case termbox.KeyHome:
|
||||
acc = append(acc, '<', 'h', 'o', 'm', 'e', '>')
|
||||
case termbox.KeyEnd:
|
||||
acc = append(acc, '<', 'e', 'n', 'd', '>')
|
||||
case termbox.KeyPgup:
|
||||
acc = append(acc, '<', 'p', 'g', 'u', 'p', '>')
|
||||
case termbox.KeyPgdn:
|
||||
acc = append(acc, '<', 'p', 'g', 'd', 'n', '>')
|
||||
case termbox.KeyArrowUp:
|
||||
acc = append(acc, '<', 'u', 'p', '>')
|
||||
case termbox.KeyArrowDown:
|
||||
acc = append(acc, '<', 'd', 'o', 'w', 'n', '>')
|
||||
case termbox.KeyArrowLeft:
|
||||
acc = append(acc, '<', 'l', 'e', 'f', 't', '>')
|
||||
case termbox.KeyArrowRight:
|
||||
acc = append(acc, '<', 'r', 'i', 'g', 'h', 't', '>')
|
||||
case termbox.KeyCtrlSpace: // also KeyCtrlTilde and KeyCtrl2
|
||||
acc = append(acc, '<', 'c', '-', 's', 'p', 'a', 'c', 'e', '>')
|
||||
case termbox.KeyCtrlA:
|
||||
acc = append(acc, '<', 'c', '-', 'a', '>')
|
||||
case termbox.KeyCtrlB:
|
||||
acc = append(acc, '<', 'c', '-', 'b', '>')
|
||||
case termbox.KeyCtrlC:
|
||||
acc = append(acc, '<', 'c', '-', 'c', '>')
|
||||
case termbox.KeyCtrlD:
|
||||
acc = append(acc, '<', 'c', '-', 'd', '>')
|
||||
case termbox.KeyCtrlE:
|
||||
acc = append(acc, '<', 'c', '-', 'e', '>')
|
||||
case termbox.KeyCtrlF:
|
||||
acc = append(acc, '<', 'c', '-', 'f', '>')
|
||||
case termbox.KeyCtrlG:
|
||||
acc = append(acc, '<', 'c', '-', 'g', '>')
|
||||
case termbox.KeyBackspace: // also KeyCtrlH
|
||||
acc = append(acc, '<', 'b', 's', '>')
|
||||
case termbox.KeyTab: // also KeyCtrlI
|
||||
acc = append(acc, '<', 't', 'a', 'b', '>')
|
||||
case termbox.KeyCtrlJ:
|
||||
acc = append(acc, '<', 'c', '-', 'j', '>')
|
||||
case termbox.KeyCtrlK:
|
||||
acc = append(acc, '<', 'c', '-', 'k', '>')
|
||||
case termbox.KeyCtrlL:
|
||||
acc = append(acc, '<', 'c', '-', 'l', '>')
|
||||
case termbox.KeyEnter: // also KeyCtrlM
|
||||
acc = append(acc, '<', 'e', 'n', 't', 'e', 'r', '>')
|
||||
case termbox.KeyCtrlN:
|
||||
acc = append(acc, '<', 'c', '-', 'n', '>')
|
||||
case termbox.KeyCtrlO:
|
||||
acc = append(acc, '<', 'c', '-', 'o', '>')
|
||||
case termbox.KeyCtrlP:
|
||||
acc = append(acc, '<', 'c', '-', 'p', '>')
|
||||
case termbox.KeyCtrlQ:
|
||||
acc = append(acc, '<', 'c', '-', 'q', '>')
|
||||
case termbox.KeyCtrlR:
|
||||
acc = append(acc, '<', 'c', '-', 'r', '>')
|
||||
case termbox.KeyCtrlS:
|
||||
acc = append(acc, '<', 'c', '-', 's', '>')
|
||||
case termbox.KeyCtrlT:
|
||||
acc = append(acc, '<', 'c', '-', 't', '>')
|
||||
case termbox.KeyCtrlU:
|
||||
acc = append(acc, '<', 'c', '-', 'u', '>')
|
||||
case termbox.KeyCtrlV:
|
||||
acc = append(acc, '<', 'c', '-', 'v', '>')
|
||||
case termbox.KeyCtrlW:
|
||||
acc = append(acc, '<', 'c', '-', 'w', '>')
|
||||
case termbox.KeyCtrlX:
|
||||
acc = append(acc, '<', 'c', '-', 'x', '>')
|
||||
case termbox.KeyCtrlY:
|
||||
acc = append(acc, '<', 'c', '-', 'y', '>')
|
||||
case termbox.KeyCtrlZ:
|
||||
acc = append(acc, '<', 'c', '-', 'z', '>')
|
||||
case termbox.KeyEsc: // also KeyCtrlLsqBracket and KeyCtrl3
|
||||
val := gKeyVal[ev.Key]
|
||||
if string(val) == "<esc>" {
|
||||
acc = nil
|
||||
return
|
||||
case termbox.KeyCtrlBackslash: // also KeyCtrl4
|
||||
acc = append(acc, '<', 'c', '-', '\\', '>')
|
||||
case termbox.KeyCtrlRsqBracket: // also KeyCtrl5
|
||||
acc = append(acc, '<', 'c', '-', ']', '>')
|
||||
case termbox.KeyCtrl6:
|
||||
acc = append(acc, '<', 'c', '-', '6', '>')
|
||||
case termbox.KeyCtrlSlash: // also KeyCtrlUnderscore and KeyCtrl7
|
||||
acc = append(acc, '<', 'c', '-', '/', '>')
|
||||
case termbox.KeySpace:
|
||||
acc = append(acc, '<', 's', 'p', 'a', 'c', 'e', '>')
|
||||
case termbox.KeyBackspace2: // also KeyCtrl8
|
||||
acc = append(acc, '<', 'b', 's', '2', '>')
|
||||
}
|
||||
acc = append(acc, val...)
|
||||
}
|
||||
|
||||
binds, ok := findBinds(gOpts.keys, string(acc))
|
||||
@ -672,7 +661,7 @@ func (ui *UI) prompt(nav *Nav, pref string) string {
|
||||
var buf []rune
|
||||
|
||||
for {
|
||||
switch ev := termbox.PollEvent(); ev.Type {
|
||||
switch ev := ui.pollEvent(); ev.Type {
|
||||
case termbox.EventKey:
|
||||
if ev.Ch != 0 {
|
||||
lacc = append(lacc, ev.Ch)
|
||||
|
Loading…
Reference in New Issue
Block a user