summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2015-10-13 02:24:38 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2015-10-13 02:24:38 +0900
commitf80ff8c9174f1a4bc8ed25dd5dbde8a90dbfe7e5 (patch)
treeb943aa4a924b032bd5d958c98dc708139d6a465f
parentb4ce89bbf58da1cadfda4be2f9f8b1257317f848 (diff)
downloadfzf-f80ff8c9174f1a4bc8ed25dd5dbde8a90dbfe7e5.tar.gz
Add bindable double-click event (#374)
-rw-r--r--src/curses/curses.go1
-rw-r--r--src/options.go2
-rw-r--r--src/terminal.go370
3 files changed, 192 insertions, 181 deletions
diff --git a/src/curses/curses.go b/src/curses/curses.go
index 3ea35bf0..8311719e 100644
--- a/src/curses/curses.go
+++ b/src/curses/curses.go
@@ -50,6 +50,7 @@ const (
Invalid
Mouse
+ DoubleClick
BTab
BSpace
diff --git a/src/options.go b/src/options.go
index 81fa3772..16de221a 100644
--- a/src/options.go
+++ b/src/options.go
@@ -343,6 +343,8 @@ func parseKeyChords(str string, message string) map[int]string {
chord = curses.SLeft
case "shift-right":
chord = curses.SRight
+ case "double-click":
+ chord = curses.DoubleClick
default:
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
chord = curses.CtrlA + int(lkey[5]) - 'a'
diff --git a/src/terminal.go b/src/terminal.go
index ab77ecfe..ca4ca212 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -180,6 +180,7 @@ func defaultKeymap() map[int]actionType {
keymap[C.Rune] = actRune
keymap[C.Mouse] = actMouse
+ keymap[C.DoubleClick] = actAccept
return keymap
}
@@ -858,204 +859,211 @@ func (t *Terminal) Loop() {
action = act
}
}
- switch action {
- case actIgnore:
- case actExecute:
- if t.cy >= 0 && t.cy < t.merger.Length() {
- item := t.merger.Get(t.cy)
- executeCommand(t.execmap[mapkey], item.AsString(t.ansi))
- }
- case actInvalid:
- t.mutex.Unlock()
- continue
- case actToggleSort:
- t.sort = !t.sort
- t.eventBox.Set(EvtSearchNew, t.sort)
- t.mutex.Unlock()
- continue
- case actBeginningOfLine:
- t.cx = 0
- case actBackwardChar:
- if t.cx > 0 {
- t.cx--
- }
- case actAbort:
- req(reqQuit)
- case actDeleteChar:
- t.delChar()
- case actDeleteCharEOF:
- if !t.delChar() && t.cx == 0 {
- req(reqQuit)
- }
- case actEndOfLine:
- t.cx = len(t.input)
- case actCancel:
- if len(t.input) == 0 {
- req(reqQuit)
- } else {
- t.yanked = t.input
- t.input = []rune{}
+ var doAction func(actionType) bool
+ doAction = func(action actionType) bool {
+ switch action {
+ case actIgnore:
+ case actExecute:
+ if t.cy >= 0 && t.cy < t.merger.Length() {
+ item := t.merger.Get(t.cy)
+ executeCommand(t.execmap[mapkey], item.AsString(t.ansi))
+ }
+ case actInvalid:
+ t.mutex.Unlock()
+ return false
+ case actToggleSort:
+ t.sort = !t.sort
+ t.eventBox.Set(EvtSearchNew, t.sort)
+ t.mutex.Unlock()
+ return false
+ case actBeginningOfLine:
t.cx = 0
- }
- case actForwardChar:
- if t.cx < len(t.input) {
- t.cx++
- }
- case actBackwardDeleteChar:
- if t.cx > 0 {
- t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
- t.cx--
- }
- case actSelectAll:
- if t.multi {
- for i := 0; i < t.merger.Length(); i++ {
- item := t.merger.Get(i)
- selectItem(item)
+ case actBackwardChar:
+ if t.cx > 0 {
+ t.cx--
}
- req(reqList, reqInfo)
- }
- case actDeselectAll:
- if t.multi {
- for i := 0; i < t.merger.Length(); i++ {
- item := t.merger.Get(i)
- delete(t.selected, item.index)
+ case actAbort:
+ req(reqQuit)
+ case actDeleteChar:
+ t.delChar()
+ case actDeleteCharEOF:
+ if !t.delChar() && t.cx == 0 {
+ req(reqQuit)
}
- req(reqList, reqInfo)
- }
- case actToggle:
- if t.multi && t.merger.Length() > 0 {
- toggle()
- req(reqList)
- }
- case actToggleAll:
- if t.multi {
- for i := 0; i < t.merger.Length(); i++ {
- toggleY(i)
+ case actEndOfLine:
+ t.cx = len(t.input)
+ case actCancel:
+ if len(t.input) == 0 {
+ req(reqQuit)
+ } else {
+ t.yanked = t.input
+ t.input = []rune{}
+ t.cx = 0
}
- req(reqList, reqInfo)
- }
- case actToggleDown:
- if t.multi && t.merger.Length() > 0 {
- toggle()
+ case actForwardChar:
+ if t.cx < len(t.input) {
+ t.cx++
+ }
+ case actBackwardDeleteChar:
+ if t.cx > 0 {
+ t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
+ t.cx--
+ }
+ case actSelectAll:
+ if t.multi {
+ for i := 0; i < t.merger.Length(); i++ {
+ item := t.merger.Get(i)
+ selectItem(item)
+ }
+ req(reqList, reqInfo)
+ }
+ case actDeselectAll:
+ if t.multi {
+ for i := 0; i < t.merger.Length(); i++ {
+ item := t.merger.Get(i)
+ delete(t.selected, item.index)
+ }
+ req(reqList, reqInfo)
+ }
+ case actToggle:
+ if t.multi && t.merger.Length() > 0 {
+ toggle()
+ req(reqList)
+ }
+ case actToggleAll:
+ if t.multi {
+ for i := 0; i < t.merger.Length(); i++ {
+ toggleY(i)
+ }
+ req(reqList, reqInfo)
+ }
+ case actToggleDown:
+ if t.multi && t.merger.Length() > 0 {
+ toggle()
+ t.vmove(-1)
+ req(reqList)
+ }
+ case actToggleUp:
+ if t.multi && t.merger.Length() > 0 {
+ toggle()
+ t.vmove(1)
+ req(reqList)
+ }
+ case actDown:
t.vmove(-1)
req(reqList)
- }
- case actToggleUp:
- if t.multi && t.merger.Length() > 0 {
- toggle()
+ case actUp:
t.vmove(1)
req(reqList)
- }
- case actDown:
- t.vmove(-1)
- req(reqList)
- case actUp:
- t.vmove(1)
- req(reqList)
- case actAccept:
- req(reqClose)
- case actClearScreen:
- req(reqRedraw)
- case actUnixLineDiscard:
- if t.cx > 0 {
- t.yanked = copySlice(t.input[:t.cx])
- t.input = t.input[t.cx:]
- t.cx = 0
- }
- case actUnixWordRubout:
- if t.cx > 0 {
- t.rubout("\\s\\S")
- }
- case actBackwardKillWord:
- if t.cx > 0 {
- t.rubout("[^[:alnum:]][[:alnum:]]")
- }
- case actYank:
- suffix := copySlice(t.input[t.cx:])
- t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
- t.cx += len(t.yanked)
- case actPageUp:
- t.vmove(t.maxItems() - 1)
- req(reqList)
- case actPageDown:
- t.vmove(-(t.maxItems() - 1))
- req(reqList)
- case actBackwardWord:
- t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
- case actForwardWord:
- t.cx += findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
- case actKillWord:
- ncx := t.cx +
- findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
- if ncx > t.cx {
- t.yanked = copySlice(t.input[t.cx:ncx])
- t.input = append(t.input[:t.cx], t.input[ncx:]...)
- }
- case actKillLine:
- if t.cx < len(t.input) {
- t.yanked = copySlice(t.input[t.cx:])
- t.input = t.input[:t.cx]
- }
- case actRune:
- prefix := copySlice(t.input[:t.cx])
- t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
- t.cx++
- case actPreviousHistory:
- if t.history != nil {
- t.history.override(string(t.input))
- t.input = []rune(t.history.previous())
- t.cx = len(t.input)
- }
- case actNextHistory:
- if t.history != nil {
- t.history.override(string(t.input))
- t.input = []rune(t.history.next())
- t.cx = len(t.input)
- }
- case actMouse:
- me := event.MouseEvent
- mx, my := me.X, me.Y
- if me.S != 0 {
- // Scroll
- if t.merger.Length() > 0 {
- if t.multi && me.Mod {
- toggle()
- }
- t.vmove(me.S)
- req(reqList)
+ case actAccept:
+ req(reqClose)
+ case actClearScreen:
+ req(reqRedraw)
+ case actUnixLineDiscard:
+ if t.cx > 0 {
+ t.yanked = copySlice(t.input[:t.cx])
+ t.input = t.input[t.cx:]
+ t.cx = 0
}
- } else if mx >= t.marginInt[3] && mx < C.MaxX()-t.marginInt[1] &&
- my >= t.marginInt[0] && my < C.MaxY()-t.marginInt[2] {
- mx -= t.marginInt[3]
- my -= t.marginInt[0]
- mx = util.Constrain(mx-len(t.prompt), 0, len(t.input))
- if !t.reverse {
- my = t.maxHeight() - my - 1
+ case actUnixWordRubout:
+ if t.cx > 0 {
+ t.rubout("\\s\\S")
}
- min := 2 + len(t.header)
- if t.inlineInfo {
- min--
+ case actBackwardKillWord:
+ if t.cx > 0 {
+ t.rubout("[^[:alnum:]][[:alnum:]]")
}
- if me.Double {
- // Double-click
- if my >= min {
- if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
- req(reqClose)
- }
- }
- } else if me.Down {
- if my == 0 && mx >= 0 {
- // Prompt
- t.cx = mx
- } else if my >= min {
- // List
- if t.vset(t.offset+my-min) && t.multi && me.Mod {
+ case actYank:
+ suffix := copySlice(t.input[t.cx:])
+ t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
+ t.cx += len(t.yanked)
+ case actPageUp:
+ t.vmove(t.maxItems() - 1)
+ req(reqList)
+ case actPageDown:
+ t.vmove(-(t.maxItems() - 1))
+ req(reqList)
+ case actBackwardWord:
+ t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
+ case actForwardWord:
+ t.cx += findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
+ case actKillWord:
+ ncx := t.cx +
+ findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
+ if ncx > t.cx {
+ t.yanked = copySlice(t.input[t.cx:ncx])
+ t.input = append(t.input[:t.cx], t.input[ncx:]...)
+ }
+ case actKillLine:
+ if t.cx < len(t.input) {
+ t.yanked = copySlice(t.input[t.cx:])
+ t.input = t.input[:t.cx]
+ }
+ case actRune:
+ prefix := copySlice(t.input[:t.cx])
+ t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
+ t.cx++
+ case actPreviousHistory:
+ if t.history != nil {
+ t.history.override(string(t.input))
+ t.input = []rune(t.history.previous())
+ t.cx = len(t.input)
+ }
+ case actNextHistory:
+ if t.history != nil {
+ t.history.override(string(t.input))
+ t.input = []rune(t.history.next())
+ t.cx = len(t.input)
+ }
+ case actMouse:
+ me := event.MouseEvent
+ mx, my := me.X, me.Y
+ if me.S != 0 {
+ // Scroll
+ if t.merger.Length() > 0 {
+ if t.multi && me.Mod {
toggle()
}
+ t.vmove(me.S)
req(reqList)
}
+ } else if mx >= t.marginInt[3] && mx < C.MaxX()-t.marginInt[1] &&
+ my >= t.marginInt[0] && my < C.MaxY()-t.marginInt[2] {
+ mx -= t.marginInt[3]
+ my -= t.marginInt[0]
+ mx = util.Constrain(mx-len(t.prompt), 0, len(t.input))
+ if !t.reverse {
+ my = t.maxHeight() - my - 1
+ }
+ min := 2 + len(t.header)
+ if t.inlineInfo {
+ min--
+ }
+ if me.Double {
+ // Double-click
+ if my >= min {
+ if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
+ return doAction(t.keymap[C.DoubleClick])
+ }
+ }
+ } else if me.Down {
+ if my == 0 && mx >= 0 {
+ // Prompt
+ t.cx = mx
+ } else if my >= min {
+ // List
+ if t.vset(t.offset+my-min) && t.multi && me.Mod {
+ toggle()
+ }
+ req(reqList)
+ }
+ }
}
}
+ return true
+ }
+ if !doAction(action) {
+ continue
}
changed := string(previousInput) != string(t.input)
t.mutex.Unlock() // Must be unlocked before touching reqBox