summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ADVANCED.md5
-rw-r--r--CHANGELOG.md3
-rw-r--r--shell/completion.bash26
-rw-r--r--shell/completion.zsh26
-rw-r--r--src/terminal.go75
-rw-r--r--src/tui/light.go14
-rw-r--r--src/tui/tcell.go20
-rw-r--r--src/tui/tui.go36
8 files changed, 151 insertions, 54 deletions
diff --git a/ADVANCED.md b/ADVANCED.md
index 2636b15f..69c738a9 100644
--- a/ADVANCED.md
+++ b/ADVANCED.md
@@ -529,7 +529,10 @@ TRANSFORMER='
# Otherwise, if the query does not end with a space,
# restart ripgrep and reload the list
elif ! [[ $FZF_QUERY =~ \ $ ]]; then
- echo "reload:sleep 0.1; $RG_PREFIX \"${words[0]}\" || true"
+ pat=${words[0]}
+ echo "reload:sleep 0.1; $RG_PREFIX \"$pat\" || true"
+ else
+ echo search:
fi
'
fzf --ansi --disabled --query "$INITIAL_QUERY" \
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8aae63db..3126bdaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,7 @@ CHANGELOG
echo "$FZF_CLICK_HEADER_WORD> "
)'
```
+ - `kill` completion for bash and zsh were updated to use this feature
- Added `search(...)` and `transform-search(...)` action to trigger an fzf search with an arbitrary query string. This can be used to extend the search syntax of fzf. In the following example, fzf will use the first word of the query to trigger ripgrep search, and use the rest of the query to perform fzf search within the result.
```sh
TRANSFORMER='
@@ -40,6 +41,8 @@ CHANGELOG
# restart ripgrep and reload the list
elif ! [[ $FZF_QUERY =~ \ $ ]]; then
echo "reload:rg --column --color=always --smart-case \"${words[0]}\""
+ else
+ echo search:
fi
'
fzf --ansi --disabled \
diff --git a/shell/completion.bash b/shell/completion.bash
index aa15b8ca..0c53e8e6 100644
--- a/shell/completion.bash
+++ b/shell/completion.bash
@@ -409,8 +409,32 @@ _fzf_complete_kill() {
}
_fzf_proc_completion() {
+ local transformer
+ transformer='
+ if [[ $FZF_KEY =~ ctrl|alt|shift ]] && [[ -n $FZF_NTH ]]; then
+ nths=( $(tr , " " <<< "$FZF_NTH") )
+ new_nths=()
+ found=0
+ for nth in ${nths[@]}; do
+ if [[ $nth = $FZF_CLICK_HEADER_NTH ]]; then
+ found=1
+ else
+ new_nths+=($nth)
+ fi
+ done
+ [[ $found = 0 ]] && new_nths+=($FZF_CLICK_HEADER_NTH)
+ new_nths=$(echo ${new_nths[@]} | tr " " ,)
+ echo "change-nth($new_nths)+change-prompt($new_nths> )"
+ else
+ if [[ $FZF_NTH = $FZF_CLICK_HEADER_NTH ]]; then
+ echo "change-nth()+change-prompt(> )"
+ else
+ echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"
+ fi
+ fi
+ '
_fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \
- --bind 'click-header:transform:echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"' -- "$@" < <(
+ --bind "click-header:transform:$transformer" -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox
command ps --everyone --full --windows # For cygwin
diff --git a/shell/completion.zsh b/shell/completion.zsh
index 9979933e..1199ac54 100644
--- a/shell/completion.zsh
+++ b/shell/completion.zsh
@@ -290,8 +290,32 @@ _fzf_complete_unalias() {
}
_fzf_complete_kill() {
+ local transformer
+ transformer='
+ if [[ $FZF_KEY =~ ctrl|alt|shift ]] && [[ -n $FZF_NTH ]]; then
+ nths=( $(tr , " " <<< "$FZF_NTH") )
+ new_nths=()
+ found=0
+ for nth in ${nths[@]}; do
+ if [[ $nth = $FZF_CLICK_HEADER_NTH ]]; then
+ found=1
+ else
+ new_nths+=($nth)
+ fi
+ done
+ [[ $found = 0 ]] && new_nths+=($FZF_CLICK_HEADER_NTH)
+ new_nths=$(echo ${new_nths[@]} | tr " " ,)
+ echo "change-nth($new_nths)+change-prompt($new_nths> )"
+ else
+ if [[ $FZF_NTH = $FZF_CLICK_HEADER_NTH ]]; then
+ echo "change-nth()+change-prompt(> )"
+ else
+ echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"
+ fi
+ fi
+ '
_fzf_complete -m --header-lines=1 --no-preview --wrap --color fg:dim,nth:regular \
- --bind 'click-header:transform:echo "change-nth($FZF_CLICK_HEADER_NTH)+change-prompt($FZF_CLICK_HEADER_WORD> )"' -- "$@" < <(
+ --bind "click-header:transform:$transformer" -- "$@" < <(
command ps -eo user,pid,ppid,start,time,command 2> /dev/null ||
command ps -eo user,pid,ppid,time,args 2> /dev/null || # For BusyBox
command ps --everyone --full --windows # For cygwin
diff --git a/src/terminal.go b/src/terminal.go
index 56b70e9b..5ff9f8de 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -4620,6 +4620,7 @@ func (t *Terminal) Loop() error {
pbarDragging := false
pborderDragging := -1
wasDown := false
+ pmx, pmy := -1, -1
needBarrier := true
// If an action is bound to 'start', we're going to process it before reading
@@ -5422,25 +5423,30 @@ func (t *Terminal) Loop() error {
case actMouse:
me := event.MouseEvent
mx, my := me.X, me.Y
- clicked := !wasDown && me.Down
+ click := !wasDown && me.Down
+ clicked := wasDown && !me.Down && (mx == pmx && my == pmy)
wasDown = me.Down
+ if click {
+ pmx, pmy = mx, my
+ }
if !me.Down {
barDragging = false
pbarDragging = false
pborderDragging = -1
previewDraggingPos = -1
+ pmx, pmy = -1, -1
}
// Scrolling
if me.S != 0 {
if t.window.Enclose(my, mx) && t.merger.Length() > 0 {
evt := tui.ScrollUp
- if me.Mod {
+ if me.Mod() {
evt = tui.SScrollUp
}
if me.S < 0 {
evt = tui.ScrollDown
- if me.Mod {
+ if me.Mod() {
evt = tui.SScrollDown
}
}
@@ -5456,7 +5462,7 @@ func (t *Terminal) Loop() error {
}
// Preview dragging
- if me.Down && (previewDraggingPos >= 0 || clicked && t.hasPreviewWindow() && t.pwindow.Enclose(my, mx)) {
+ if me.Down && (previewDraggingPos >= 0 || click && t.hasPreviewWindow() && t.pwindow.Enclose(my, mx)) {
if previewDraggingPos > 0 {
scrollPreviewBy(previewDraggingPos - my)
}
@@ -5466,7 +5472,7 @@ func (t *Terminal) Loop() error {
// Preview scrollbar dragging
headerLines := t.activePreviewOpts.headerLines
- pbarDragging = me.Down && (pbarDragging || clicked && t.hasPreviewWindow() && my >= t.pwindow.Top()+headerLines && my < t.pwindow.Top()+t.pwindow.Height() && mx == t.pwindow.Left()+t.pwindow.Width())
+ pbarDragging = me.Down && (pbarDragging || click && t.hasPreviewWindow() && my >= t.pwindow.Top()+headerLines && my < t.pwindow.Top()+t.pwindow.Height() && mx == t.pwindow.Left()+t.pwindow.Width())
if pbarDragging {
effectiveHeight := t.pwindow.Height() - headerLines
numLines := len(t.previewer.lines) - headerLines
@@ -5483,7 +5489,7 @@ func (t *Terminal) Loop() error {
}
// Preview border dragging (resizing)
- if pborderDragging < 0 && clicked && t.hasPreviewWindow() {
+ if pborderDragging < 0 && click && t.hasPreviewWindow() {
switch t.activePreviewOpts.position {
case posUp:
if t.pborder.Enclose(my, mx) && my == t.pborder.Top()+t.pborder.Height()-1 {
@@ -5564,9 +5570,7 @@ func (t *Terminal) Loop() error {
}
// Inside the header window
- // TODO: Should we trigger this on mouse up instead?
- // Should we still trigger it when the position has changed from the down event?
- if t.headerVisible && t.headerWindow != nil && t.headerWindow.Enclose(my, mx) {
+ if clicked && t.headerVisible && t.headerWindow != nil && t.headerWindow.Enclose(my, mx) {
mx -= t.headerWindow.Left() + t.headerIndent(t.headerBorderShape)
my -= t.headerWindow.Top()
if mx < 0 {
@@ -5580,7 +5584,7 @@ func (t *Terminal) Loop() error {
return doActions(actionsFor(tui.ClickHeader))
}
- if t.headerVisible && t.headerLinesWindow != nil && t.headerLinesWindow.Enclose(my, mx) {
+ if clicked && t.headerVisible && t.headerLinesWindow != nil && t.headerLinesWindow.Enclose(my, mx) {
mx -= t.headerLinesWindow.Left() + t.headerIndent(t.headerLinesShape)
my -= t.headerLinesWindow.Top()
if mx < 0 {
@@ -5616,7 +5620,7 @@ func (t *Terminal) Loop() error {
}
// Scrollbar dragging
- barDragging = me.Down && (barDragging || clicked && my >= min && mx == t.window.Width()-1)
+ barDragging = me.Down && (barDragging || click && my >= min && mx == t.window.Width()-1)
if barDragging {
barLength, barStart := t.getScrollbar()
if barLength > 0 {
@@ -5649,7 +5653,6 @@ func (t *Terminal) Loop() error {
return doActions(actionsFor(tui.DoubleClick))
}
}
- break
}
if me.Down {
@@ -5661,40 +5664,40 @@ func (t *Terminal) Loop() error {
t.vset(cy)
req(reqList)
evt := tui.RightClick
- if me.Mod {
+ if me.Mod() {
evt = tui.SRightClick
}
if me.Left {
evt = tui.LeftClick
- if me.Mod {
+ if me.Mod() {
evt = tui.SLeftClick
}
}
return doActions(actionsFor(evt))
- } else if t.headerVisible && t.headerWindow == nil {
- // Header
- // TODO: Should we trigger this on mouse up instead?
- numLines := t.visibleHeaderLinesInList()
- lineOffset := 0
- if t.inputWindow == nil && !t.headerFirst {
- // offset for info line
- if t.noSeparatorLine() {
- lineOffset = 1
- } else {
- lineOffset = 2
- }
+ }
+ }
+ if clicked && t.headerVisible && t.headerWindow == nil {
+ // Header
+ numLines := t.visibleHeaderLinesInList()
+ lineOffset := 0
+ if t.inputWindow == nil && !t.headerFirst {
+ // offset for info line
+ if t.noSeparatorLine() {
+ lineOffset = 1
+ } else {
+ lineOffset = 2
}
- my -= lineOffset
- mx -= t.pointerLen + t.markerLen
- if my >= 0 && my < numLines && mx >= 0 {
- if t.layout == layoutReverse {
- t.clickHeaderLine = my + 1
- } else {
- t.clickHeaderLine = numLines - my
- }
- t.clickHeaderColumn = mx + 1
- return doActions(actionsFor(tui.ClickHeader))
+ }
+ my -= lineOffset
+ mx -= t.pointerLen + t.markerLen
+ if my >= 0 && my < numLines && mx >= 0 {
+ if t.layout == layoutReverse {
+ t.clickHeaderLine = my + 1
+ } else {
+ t.clickHeaderLine = numLines - my
}
+ t.clickHeaderColumn = mx + 1
+ return doActions(actionsFor(tui.ClickHeader))
}
}
case actReload, actReloadSync:
diff --git a/src/tui/light.go b/src/tui/light.go
index 56e9ae0b..54c38c18 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -626,15 +626,13 @@ func (r *LightRenderer) mouseSequence(sz *int) Event {
// middle := t & 0b1
left := t&0b11 == 0
-
- // shift := t & 0b100
- // ctrl := t & 0b1000
- mod := t&0b1100 > 0
-
- drag := t&0b100000 > 0
+ ctrl := t&0b10000 > 0
+ alt := t&0b01000 > 0
+ shift := t&0b00100 > 0
+ drag := t&0b100000 > 0 // 32
if scroll != 0 {
- return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, ctrl, alt, shift}}
}
double := false
@@ -658,7 +656,7 @@ func (r *LightRenderer) mouseSequence(sz *int) Event {
}
}
}
- return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, ctrl, alt, shift}}
}
func (r *LightRenderer) smcup() {
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 3c3e13fc..3738214a 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -266,7 +266,11 @@ func (r *FullscreenRenderer) GetChar() Event {
// so mouse click is three consecutive events, but the first and last are indistinguishable from movement events (with released buttons)
// dragging has same structure, it only repeats the middle (main) event appropriately
x, y := ev.Position()
- mod := ev.Modifiers() != 0
+
+ mod := ev.Modifiers()
+ ctrl := (mod & tcell.ModCtrl) > 0
+ alt := (mod & tcell.ModAlt) > 0
+ shift := (mod & tcell.ModShift) > 0
// since we dont have mouse down events (unlike LightRenderer), we need to track state in prevButton
prevButton, button := _prevMouseButton, ev.Buttons()
@@ -275,9 +279,9 @@ func (r *FullscreenRenderer) GetChar() Event {
switch {
case button&tcell.WheelDown != 0:
- return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, -1, false, false, false, ctrl, alt, shift}}
case button&tcell.WheelUp != 0:
- return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, +1, false, false, false, ctrl, alt, shift}}
case button&tcell.Button1 != 0:
double := false
if !drag {
@@ -300,9 +304,9 @@ func (r *FullscreenRenderer) GetChar() Event {
}
}
// fire single or double click event
- return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, 0, true, !double, double, ctrl, alt, shift}}
case button&tcell.Button2 != 0:
- return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, mod}}
+ return Event{Mouse, 0, &MouseEvent{y, x, 0, false, true, false, ctrl, alt, shift}}
default:
// double and single taps on Windows don't quite work due to
// the console acting on the events and not allowing us
@@ -311,7 +315,11 @@ func (r *FullscreenRenderer) GetChar() Event {
down := left || button&tcell.Button3 != 0
double := false
- return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, mod}}
+ // No need to report mouse movement events when no button is pressed
+ if drag {
+ return Event{Invalid, 0, nil}
+ }
+ return Event{Mouse, 0, &MouseEvent{y, x, 0, left, down, double, ctrl, alt, shift}}
}
// process keyboard:
diff --git a/src/tui/tui.go b/src/tui/tui.go
index eab5ad7e..fe8fc243 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -150,6 +150,10 @@ func (e Event) Comparable() Event {
}
func (e Event) KeyName() string {
+ if me := e.MouseEvent; me != nil {
+ return me.Name()
+ }
+
if e.Type >= Invalid {
return ""
}
@@ -367,7 +371,37 @@ type MouseEvent struct {
Left bool
Down bool
Double bool
- Mod bool
+ Ctrl bool
+ Alt bool
+ Shift bool
+}
+
+func (e MouseEvent) Mod() bool {
+ return e.Ctrl || e.Alt || e.Shift
+}
+
+func (e MouseEvent) Name() string {
+ name := ""
+ if e.Down {
+ return name
+ }
+
+ if e.Ctrl {
+ name += "ctrl-"
+ }
+ if e.Alt {
+ name += "alt-"
+ }
+ if e.Shift {
+ name += "shift-"
+ }
+ if e.Double {
+ name += "double-"
+ }
+ if !e.Left {
+ name += "right-"
+ }
+ return name + "click"
}
type BorderShape int