summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2016-05-18 02:06:52 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2016-05-18 02:10:03 +0900
commit6d235bceee66bd6b7ca2de8311a3deae4793bfa8 (patch)
tree7649dba7f8c674471c3280b440fd7d98268d1cdc /src
parent4adebfc8560a367be61bb6c024095586ca6382c8 (diff)
Add jump and jump-accept actions for --bind
jump and jump-accept implement EasyMotion-like movement in fzf. Suggested by @mhrebenyuk. Close #569.
Diffstat (limited to 'src')
-rw-r--r--src/constants.go3
-rw-r--r--src/options.go15
-rw-r--r--src/terminal.go75
3 files changed, 80 insertions, 13 deletions
diff --git a/src/constants.go b/src/constants.go
index 2e41e8be..eb9262df 100644
--- a/src/constants.go
+++ b/src/constants.go
@@ -36,6 +36,9 @@ const (
// History
defaultHistoryMax int = 1000
+
+ // Jump labels
+ defaultJumpLabels string = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM"
)
// fzf events
diff --git a/src/options.go b/src/options.go
index ea7d717c..089b4c2f 100644
--- a/src/options.go
+++ b/src/options.go
@@ -45,6 +45,7 @@ const usage = `usage: fzf [options]
--hscroll-off=COL Number of screen columns to keep to the right of the
highlighted substring (default: 10)
--inline-info Display finder info inline with the query
+ --jump-labels=CHARS Label characters for jump and jump-accept
--prompt=STR Input prompt (default: '> ')
--bind=KEYBINDS Custom key bindings. Refer to the man page.
--history=FILE History file
@@ -112,6 +113,7 @@ type Options struct {
Hscroll bool
HscrollOff int
InlineInfo bool
+ JumpLabels string
Prompt string
Query string
Select1 bool
@@ -153,6 +155,7 @@ func defaultOptions() *Options {
Hscroll: true,
HscrollOff: 10,
InlineInfo: false,
+ JumpLabels: defaultJumpLabels,
Prompt: "> ",
Query: "",
Select1: false,
@@ -553,6 +556,10 @@ func parseKeymap(keymap map[int]actionType, execmap map[int]string, str string)
keymap[key] = actForwardChar
case "forward-word":
keymap[key] = actForwardWord
+ case "jump":
+ keymap[key] = actJump
+ case "jump-accept":
+ keymap[key] = actJumpAccept
case "kill-line":
keymap[key] = actKillLine
case "kill-word":
@@ -804,6 +811,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.InlineInfo = true
case "--no-inline-info":
opts.InlineInfo = false
+ case "--jump-labels":
+ opts.JumpLabels = nextString(allArgs, &i, "label characters required")
case "-1", "--select-1":
opts.Select1 = true
case "+1", "--no-select-1":
@@ -891,6 +900,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Tabstop = atoi(value)
} else if match, value := optString(arg, "--hscroll-off="); match {
opts.HscrollOff = atoi(value)
+ } else if match, value := optString(arg, "--jump-labels="); match {
+ opts.JumpLabels = value
} else {
errorExit("unknown option: " + arg)
}
@@ -908,6 +919,10 @@ func parseOptions(opts *Options, allArgs []string) {
if opts.Tabstop < 1 {
errorExit("tab stop must be a positive integer")
}
+
+ if len(opts.JumpLabels) == 0 {
+ errorExit("empty jump labels")
+ }
}
func postProcessOptions(opts *Options) {
diff --git a/src/terminal.go b/src/terminal.go
index 771cad78..4f611ebe 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -19,6 +19,14 @@ import (
"github.com/junegunn/go-runewidth"
)
+type jumpMode int
+
+const (
+ jumpDisabled jumpMode = iota
+ jumpEnabled
+ jumpAcceptEnabled
+)
+
// Terminal represents terminal input/output
type Terminal struct {
initDelay time.Duration
@@ -50,6 +58,8 @@ type Terminal struct {
count int
progress int
reading bool
+ jumping jumpMode
+ jumpLabels string
merger *Merger
selected map[int32]selectedItem
reqBox *util.EventBox
@@ -88,6 +98,7 @@ const (
reqInfo
reqHeader
reqList
+ reqJump
reqRefresh
reqRedraw
reqClose
@@ -133,6 +144,8 @@ const (
actUp
actPageUp
actPageDown
+ actJump
+ actJumpAccept
actPrintQuery
actToggleSort
actPreviousHistory
@@ -235,6 +248,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
header0: header,
ansi: opts.Ansi,
reading: true,
+ jumping: jumpDisabled,
+ jumpLabels: opts.JumpLabels,
merger: EmptyMerger,
selected: make(map[int32]selectedItem),
reqBox: util.NewEventBox(),
@@ -497,15 +512,25 @@ func (t *Terminal) printList() {
}
t.move(line, 0, true)
if i < count {
- t.printItem(t.merger.Get(i+t.offset), i == t.cy-t.offset)
+ t.printItem(t.merger.Get(i+t.offset), i, i == t.cy-t.offset)
}
}
}
-func (t *Terminal) printItem(item *Item, current bool) {
+func (t *Terminal) printItem(item *Item, i int, current bool) {
_, selected := t.selected[item.Index()]
+ label := " "
+ if t.jumping != jumpDisabled {
+ if i < len(t.jumpLabels) {
+ // Striped
+ current = i%2 == 0
+ label = t.jumpLabels[i : i+1]
+ }
+ } else if current {
+ label = ">"
+ }
+ C.CPrint(C.ColCursor, true, label)
if current {
- C.CPrint(C.ColCursor, true, ">")
if selected {
C.CPrint(C.ColSelected, true, ">")
} else {
@@ -513,7 +538,6 @@ func (t *Terminal) printItem(item *Item, current bool) {
}
t.printHighlighted(item, true, C.ColCurrent, C.ColCurrentMatch, true)
} else {
- C.CPrint(C.ColCursor, true, " ")
if selected {
C.CPrint(C.ColSelected, true, ">")
} else {
@@ -806,6 +830,11 @@ func (t *Terminal) Loop() {
t.printInfo()
case reqList:
t.printList()
+ case reqJump:
+ if t.merger.Length() == 0 {
+ t.jumping = jumpDisabled
+ }
+ t.printList()
case reqHeader:
t.printHeader()
case reqRefresh:
@@ -1025,6 +1054,12 @@ func (t *Terminal) Loop() {
case actPageDown:
t.vmove(-(t.maxItems() - 1))
req(reqList)
+ case actJump:
+ t.jumping = jumpEnabled
+ req(reqJump)
+ case actJumpAccept:
+ t.jumping = jumpAcceptEnabled
+ req(reqJump)
case actBackwardWord:
t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
case actForwardWord:
@@ -1104,18 +1139,32 @@ func (t *Terminal) Loop() {
}
return true
}
- action := t.keymap[event.Type]
+ changed := false
mapkey := event.Type
- if event.Type == C.Rune {
- mapkey = int(event.Char) + int(C.AltZ)
- if act, prs := t.keymap[mapkey]; prs {
- action = act
+ if t.jumping == jumpDisabled {
+ action := t.keymap[mapkey]
+ if mapkey == C.Rune {
+ mapkey = int(event.Char) + int(C.AltZ)
+ if act, prs := t.keymap[mapkey]; prs {
+ action = act
+ }
}
+ if !doAction(action, mapkey) {
+ continue
+ }
+ changed = string(previousInput) != string(t.input)
+ } else {
+ if mapkey == C.Rune {
+ if idx := strings.IndexRune(t.jumpLabels, event.Char); idx >= 0 {
+ t.cy = idx + t.offset
+ if t.jumping == jumpAcceptEnabled {
+ req(reqClose)
+ }
+ }
+ }
+ t.jumping = jumpDisabled
+ req(reqList)
}
- if !doAction(action, mapkey) {
- continue
- }
- changed := string(previousInput) != string(t.input)
t.mutex.Unlock() // Must be unlocked before touching reqBox
if changed {