m/fzf
1
0
mirror of https://github.com/junegunn/fzf.git synced 2025-11-09 11:53:47 -05:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Junegunn Choi
b2773d5bc3 Add --freeze-right=N option to keep the rightmost N fields visible 2025-11-09 19:41:58 +09:00
Junegunn Choi
5e2d96d5e6 Add --freeze-left=N option to keep the leftmost N fields visible 2025-11-09 17:24:45 +09:00
phanium
e659b46ff5 feat: append spinner in the end when --info=inline (#4567)
Test:
  go run main.go --query "$(seq 100)" --info inline --border < <(sleep 60)
  go run main.go --query "$(seq 100)" --info inline --info-command 'echo hello' --border < <(sleep 60)

Close #4344
Close #619

Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
2025-11-09 10:44:27 +09:00
Junegunn Choi
991c36453c [man] Add --gutter-raw
Close #4579
2025-11-08 17:50:25 +09:00
junegunn
4d563c6dfa Deploying to master from @ junegunn/fzf@5cb695744f 🚀 2025-11-02 00:02:16 +00:00
Koichi Murase
5cb695744f [bash,zsh] Fix the version check for mawk (#4574)
We have been checking the mawk version by extracting <x>, <y>, <z>,
and <d> part from "mawk <x>.<y>.<z> <d>" in the output of the "mawk -W
version" and testing <x>, <y>, <z>, and <d> using an arithmetic
evalaution.  However, <d> is ensured to be an integer only in "x.y.z
>= 1.3.4".  Otherwise, it may cause a syntax error in the arithmetic
evaluation.  The mawk started to include the date as an integer in the
<d> position only from mawk-1.3.3-20090721.  We should first check
that "x.y.z >= 1.3.4" and then check the value of "d".  In case, "mawk
-W version" produces a completely different text, we should also
redirect stderr of the arithmetic commands to /dev/null.
2025-10-31 21:14:41 +09:00
Junegunn Choi
c1b259c042 0.66.1 2025-10-26 15:11:51 +09:00
junegunn
1a0371e2c7 Deploying to master from @ junegunn/fzf@aa259fdc19 🚀 2025-10-26 00:02:16 +00:00
Junegunn Choi
aa259fdc19 Fix regression in --no-color / NO_COLOR theme
Fix #4561
2025-10-21 19:49:43 +09:00
junegunn
b852dc8a56 Deploying to master from @ junegunn/fzf@a0cabe021d 🚀 2025-10-19 00:02:19 +00:00
Junegunn Choi
a0cabe021d Fix bug preventing 'ctrl-h' from being bound to an action
Fix #4556
2025-10-15 12:16:09 +09:00
18 changed files with 334 additions and 164 deletions

View File

@@ -1,6 +1,30 @@
CHANGELOG CHANGELOG
========= =========
0.67.0
------
- Added `--freeze-left=N` option to keep the leftmost N columns visible.
```sh
# Keeps the file name column fixed and always visible
git grep --line-number --color=always -- '' |
fzf --ansi --delimiter : --freeze-left 1
# Used with --keep-right
git grep --line-number --color=always -- '' |
fzf --ansi --delimiter : --freeze-left 1 --keep-right
```
- Also added `--freeze-right=N` option to keep the rightmost N columns visible.
```sh
fd | fzf --freeze-right 1 --delimiter /
fd | fzf --freeze-left 1 --freeze-right 1 --delimiter /
```
0.66.1
------
- Bug fixes
- Fixed a bug preventing 'ctrl-h' from being bound to an action (#4556)
- Fixed `--no-color` / `NO_COLOR` theme (#4561)
0.66.0 0.66.0
------ ------
@@ -35,10 +59,10 @@ This version introduces many new features centered around the new "raw" mode.
This version introduces a new "raw" mode (named so because it shows the list This version introduces a new "raw" mode (named so because it shows the list
"unfiltered"). In raw mode, non-matching items stay in their original positions, "unfiltered"). In raw mode, non-matching items stay in their original positions,
but appear dimmed. This allows you see surrounding items of a match and better but appear dimmed. This allows you to see the surrounding items of a match and
understand the context of it. You can enable raw mode by default with `--raw`, better understand the context of it. You can enable raw mode by default with
but it's often more useful when toggled dynamically with the `toggle-raw` `--raw`, but it's often more useful when toggled dynamically with the
action. `toggle-raw` action.
```sh ```sh
tree | fzf --reverse --bind alt-r:toggle-raw tree | fzf --reverse --bind alt-r:toggle-raw

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
set -u set -u
version=0.66.0 version=0.66.1
auto_completion= auto_completion=
key_bindings= key_bindings=
update_config=2 update_config=2

View File

@@ -1,4 +1,4 @@
$version="0.66.0" $version="0.66.1"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition $fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf\-tmux 1 "Oct 2025" "fzf 0.66.0" "fzf\-tmux - open fzf in tmux split pane" .TH fzf\-tmux 1 "Oct 2025" "fzf 0.66.1" "fzf\-tmux - open fzf in tmux split pane"
.SH NAME .SH NAME
fzf\-tmux - open fzf in tmux split pane fzf\-tmux - open fzf in tmux split pane

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Oct 2025" "fzf 0.66.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Oct 2025" "fzf 0.66.1" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -629,9 +629,16 @@ Render empty lines between each item
The given string will be repeated to draw a horizontal line on each gap The given string will be repeated to draw a horizontal line on each gap
(default: '┈' or '\-' depending on \fB\-\-no\-unicode\fR). (default: '┈' or '\-' depending on \fB\-\-no\-unicode\fR).
.TP .TP
.BI "\-\-freeze\-left=" "N"
Number of fields to freeze on the left.
.TP
.BI "\-\-freeze\-right=" "N"
Number of fields to freeze on the right.
.TP
.B "\-\-keep\-right" .B "\-\-keep\-right"
Keep the right end of the line visible when it's too long. Effective only when Keep the right end of the line visible when it's too long. Effective only when
the query string is empty. the query string is empty. Use \fB\-\-freeze\-right=1\fR instead if you want
the last field to be always visible even with a non-empty query.
.TP .TP
.BI "\-\-scroll\-off=" "LINES" .BI "\-\-scroll\-off=" "LINES"
Number of screen lines to keep above or below when scrolling to the top or to Number of screen lines to keep above or below when scrolling to the top or to
@@ -651,6 +658,9 @@ Label characters for \fBjump\fR mode.
.BI "\-\-gutter=" "CHAR" .BI "\-\-gutter=" "CHAR"
Character used for the gutter column (default: '▌' unless \fB\-\-no\-unicode\fR is given) Character used for the gutter column (default: '▌' unless \fB\-\-no\-unicode\fR is given)
.TP .TP
.BI "\-\-gutter\-raw=" "CHAR"
Character used for the gutter column in raw mode (default: '▖' unless \fB\-\-no\-unicode\fR is given)
.TP
.BI "\-\-pointer=" "STR" .BI "\-\-pointer=" "STR"
Pointer to the current line (default: '▌' or '>' depending on \fB\-\-no\-unicode\fR) Pointer to the current line (default: '▌' or '>' depending on \fB\-\-no\-unicode\fR)
.TP .TP

View File

@@ -26,7 +26,10 @@ __fzf_exec_awk() {
# version >= 1.3.4 # version >= 1.3.4
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && ((d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004)) && __fzf_awk=mawk [[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
# Note: macOS awk has a quirk that it stops processing at all when it sees # Note: macOS awk has a quirk that it stops processing at all when it sees

View File

@@ -51,7 +51,10 @@ __fzf_exec_awk() {
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk > /dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && ((d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004)) && __fzf_awk=mawk [[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"

View File

@@ -115,7 +115,10 @@ __fzf_exec_awk() {
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk > /dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && ((d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004)) && __fzf_awk=mawk [[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"

View File

@@ -38,7 +38,10 @@ __fzf_exec_awk() {
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk > /dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && ((d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004)) && __fzf_awk=mawk [[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"

View File

@@ -58,7 +58,10 @@ __fzf_exec_awk() {
elif command -v mawk > /dev/null 2>&1; then elif command -v mawk > /dev/null 2>&1; then
local n x y z d local n x y z d
IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null) IFS=' .' read -r n x y z d <<< $(command mawk -W version 2> /dev/null)
[[ $n == mawk ]] && ((d >= 20230302 && (x * 1000 + y) * 1000 + z >= 1003004)) && __fzf_awk=mawk [[ $n == mawk ]] &&
(((x * 1000 + y) * 1000 + z >= 1003004)) 2> /dev/null &&
((d >= 20230302)) 2> /dev/null &&
__fzf_awk=mawk
fi fi
fi fi
LC_ALL=C exec "$__fzf_awk" "$@" LC_ALL=C exec "$__fzf_awk" "$@"

View File

@@ -104,6 +104,8 @@ Usage: fzf [options]
--gap[=N] Render empty lines between each item --gap[=N] Render empty lines between each item
--gap-line[=STR] Draw horizontal line on each gap using the string --gap-line[=STR] Draw horizontal line on each gap using the string
(default: '┈' or '-') (default: '┈' or '-')
--freeze-left=N Number of fields to freeze on the left
--freeze-right=N Number of fields to freeze on the right
--keep-right Keep the right end of the line visible on overflow --keep-right Keep the right end of the line visible on overflow
--scroll-off=LINES Number of screen lines to keep above or below when --scroll-off=LINES Number of screen lines to keep above or below when
scrolling to the top or to the bottom (default: 0) scrolling to the top or to the bottom (default: 0)
@@ -562,6 +564,8 @@ type Options struct {
Case Case Case Case
Normalize bool Normalize bool
Nth []Range Nth []Range
FreezeLeft int
FreezeRight int
WithNth func(Delimiter) func([]Token, int32) string WithNth func(Delimiter) func([]Token, int32) string
AcceptNth func(Delimiter) func([]Token, int32) string AcceptNth func(Delimiter) func([]Token, int32) string
Delimiter Delimiter Delimiter Delimiter
@@ -673,9 +677,10 @@ func defaultPreviewOpts(command string) previewOpts {
} }
func defaultOptions() *Options { func defaultOptions() *Options {
var theme *tui.ColorTheme var theme, baseTheme *tui.ColorTheme
if os.Getenv("NO_COLOR") != "" { if os.Getenv("NO_COLOR") != "" {
theme = tui.NoColorTheme theme = tui.NoColorTheme
baseTheme = tui.NoColorTheme
} else { } else {
theme = tui.EmptyTheme theme = tui.EmptyTheme
} }
@@ -703,6 +708,7 @@ func defaultOptions() *Options {
Ansi: false, Ansi: false,
Mouse: true, Mouse: true,
Theme: theme, Theme: theme,
BaseTheme: baseTheme,
Black: false, Black: false,
Bold: true, Bold: true,
MinHeight: -10, MinHeight: -10,
@@ -2693,6 +2699,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.Nth, err = splitNth(str); err != nil { if opts.Nth, err = splitNth(str); err != nil {
return err return err
} }
case "--freeze-left":
if opts.FreezeLeft, err = nextInt("number of fields required"); err != nil {
return err
}
case "--freeze-right":
if opts.FreezeRight, err = nextInt("number of fields required"); err != nil {
return err
}
case "--with-nth": case "--with-nth":
str, err := nextString("nth expression required") str, err := nextString("nth expression required")
if err != nil { if err != nil {
@@ -3336,6 +3350,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
return errors.New("empty jump labels") return errors.New("empty jump labels")
} }
if opts.FreezeLeft < 0 || opts.FreezeRight < 0 {
return errors.New("number of fields to freeze must be a non-negative integer")
}
if validateJumpLabels { if validateJumpLabels {
for _, r := range opts.JumpLabels { for _, r := range opts.JumpLabels {
if r < 32 || r > 126 { if r < 32 || r > 126 {

View File

@@ -331,6 +331,8 @@ type Terminal struct {
scrollbar string scrollbar string
previewScrollbar string previewScrollbar string
ansi bool ansi bool
freezeLeft int
freezeRight int
nthAttr tui.Attr nthAttr tui.Attr
nth []Range nth []Range
nthCurrent []Range nthCurrent []Range
@@ -800,7 +802,6 @@ func defaultKeymap() map[tui.Event][]*action {
add(tui.CtrlD, actDeleteCharEof) add(tui.CtrlD, actDeleteCharEof)
add(tui.CtrlE, actEndOfLine) add(tui.CtrlE, actEndOfLine)
add(tui.CtrlF, actForwardChar) add(tui.CtrlF, actForwardChar)
add(tui.CtrlH, actBackwardDeleteChar)
add(tui.Backspace, actBackwardDeleteChar) add(tui.Backspace, actBackwardDeleteChar)
add(tui.CtrlBackspace, actBackwardDeleteChar) add(tui.CtrlBackspace, actBackwardDeleteChar)
add(tui.Tab, actToggleDown) add(tui.Tab, actToggleDown)
@@ -1051,6 +1052,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
footer: opts.Footer, footer: opts.Footer,
header0: opts.Header, header0: opts.Header,
ansi: opts.Ansi, ansi: opts.Ansi,
freezeLeft: opts.FreezeLeft,
freezeRight: opts.FreezeRight,
nthAttr: opts.Theme.Nth.Attr, nthAttr: opts.Theme.Nth.Attr,
nth: opts.Nth, nth: opts.Nth,
nthCurrent: opts.Nth, nthCurrent: opts.Nth,
@@ -2982,6 +2985,11 @@ func (t *Terminal) printInfoImpl() {
} else { } else {
outputPrinter(t.window, maxWidth) outputPrinter(t.window, maxWidth)
} }
if t.infoStyle == infoInline && outputLen < maxWidth-1 && t.reading {
t.window.Print(" ")
printSpinner()
outputLen += 2
}
if t.infoStyle == infoInlineRight { if t.infoStyle == infoInlineRight {
if t.separatorLen > 0 { if t.separatorLen > 0 {
@@ -3521,6 +3529,33 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
} }
allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr, hidden) allOffsets := result.colorOffsets(charOffsets, nthOffsets, t.theme, colBase, colMatch, t.nthAttr, hidden)
// Determine split offset for horizontal scrolling with freeze
splitOffset1 := -1
splitOffset2 := -1
if t.hscroll && !t.wrap {
var tokens []Token
if t.freezeLeft > 0 || t.freezeRight > 0 {
tokens = Tokenize(item.text.ToString(), t.delimiter)
}
// 0 1 2| 3| 4 5
// ----- ---
if t.freezeLeft > 0 {
if len(tokens) > 0 {
token := tokens[util.Min(t.freezeLeft, len(tokens))-1]
splitOffset1 = int(token.prefixLength) + token.text.Length() - token.text.TrailingWhitespaces()
}
}
if t.freezeRight > 0 {
index := len(tokens) - t.freezeRight - 1
if index == t.freezeLeft-1 {
splitOffset2 = 0
} else if index >= t.freezeLeft {
token := tokens[index]
splitOffset2 = int(token.prefixLength) + token.text.Length()
}
}
}
maxLines := 1 maxLines := 1
if t.canSpanMultiLines() { if t.canSpanMultiLines() {
maxLines = maxLineNum - lineNum + 1 maxLines = maxLineNum - lineNum + 1
@@ -3590,16 +3625,24 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
break break
} }
} }
splitOffsetLeft := 0
if splitOffset1 >= 0 && splitOffset1 > from && splitOffset1 < from+len(line) {
splitOffsetLeft = splitOffset1 - from
}
splitOffsetRight := -1
if splitOffset2 >= 0 && splitOffset2 >= from && splitOffset2 < from+len(line) {
splitOffsetRight = splitOffset2 - from
}
from += len(line) from += len(line)
if lineOffset < skipLines { if lineOffset < skipLines {
continue continue
} }
actualLineOffset := lineOffset - skipLines actualLineOffset := lineOffset - skipLines
var maxe int var maxEnd int
for _, offset := range offsets { for _, offset := range offsets {
if offset.match { if offset.match {
maxe = util.Max(maxe, int(offset.offset[1])) maxEnd = util.Max(maxEnd, int(offset.offset[1]))
} }
} }
@@ -3663,12 +3706,39 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
wrapped = true wrapped = true
} }
displayWidth = t.displayWidthWithLimit(line, 0, maxWidth) frozenLeft := line[:splitOffsetLeft]
middle := line[splitOffsetLeft:]
frozenRight := []rune{}
if splitOffsetRight >= splitOffsetLeft {
middle = line[splitOffsetLeft:splitOffsetRight]
frozenRight = line[splitOffsetRight:]
}
displayWidthSum := 0
todo := [3]func(){}
for fidx, runes := range [][]rune{frozenLeft, frozenRight, middle} {
if len(runes) == 0 {
continue
}
shift := 0
maxe := maxEnd
offs := make([]colorOffset, len(offsets))
for idx := range offsets {
offs[idx] = offsets[idx]
if fidx == 1 && splitOffsetRight > 0 {
shift = splitOffsetRight
} else if fidx == 2 && splitOffsetLeft > 0 {
shift = splitOffsetLeft
}
offs[idx].offset[0] -= int32(shift)
offs[idx].offset[1] -= int32(shift)
}
maxe -= shift
displayWidth = t.displayWidthWithLimit(runes, 0, maxWidth)
if !t.wrap && displayWidth > maxWidth { if !t.wrap && displayWidth > maxWidth {
ellipsis, ellipsisWidth := util.Truncate(t.ellipsis, maxWidth/2) ellipsis, ellipsisWidth := util.Truncate(t.ellipsis, maxWidth/2)
maxe = util.Constrain(maxe+util.Min(maxWidth/2-ellipsisWidth, t.hscrollOff), 0, len(line)) maxe = util.Constrain(maxe+util.Min(maxWidth/2-ellipsisWidth, t.hscrollOff), 0, len(runes))
transformOffsets := func(diff int32, rightTrim bool) { transformOffsets := func(diff int32, rightTrim bool) {
for idx, offset := range offsets { for idx, offset := range offs {
b, e := offset.offset[0], offset.offset[1] b, e := offset.offset[0], offset.offset[1]
el := int32(len(ellipsis)) el := int32(len(ellipsis))
b += el - diff b += el - diff
@@ -3677,55 +3747,71 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
if rightTrim { if rightTrim {
e = util.Min32(e, int32(maxWidth-ellipsisWidth)) e = util.Min32(e, int32(maxWidth-ellipsisWidth))
} }
offsets[idx].offset[0] = b offs[idx].offset[0] = b
offsets[idx].offset[1] = util.Max32(b, e) offs[idx].offset[1] = util.Max32(b, e)
} }
} }
if t.hscroll { if t.hscroll {
if t.keepRight && pos == nil { if fidx == 1 || fidx == 2 && t.keepRight && pos == nil {
trimmed, diff := t.trimLeft(line, maxWidth, ellipsisWidth) trimmed, diff := t.trimLeft(runes, maxWidth, ellipsisWidth)
transformOffsets(diff, false) transformOffsets(diff, false)
line = append(ellipsis, trimmed...) runes = append(ellipsis, trimmed...)
} else if !t.overflow(line[:maxe], maxWidth-ellipsisWidth) { } else if fidx == 0 || !t.overflow(runes[:maxe], maxWidth-ellipsisWidth) {
// Stri.. // Stri..
line, _ = t.trimRight(line, maxWidth-ellipsisWidth) runes, _ = t.trimRight(runes, maxWidth-ellipsisWidth)
line = append(line, ellipsis...) runes = append(runes, ellipsis...)
} else { } else {
// Stri.. // Stri..
rightTrim := false rightTrim := false
if t.overflow(line[maxe:], ellipsisWidth) { if t.overflow(runes[maxe:], ellipsisWidth) {
line = append(line[:maxe], ellipsis...) runes = append(runes[:maxe], ellipsis...)
rightTrim = true rightTrim = true
} }
// ..ri.. // ..ri..
var diff int32 var diff int32
line, diff = t.trimLeft(line, maxWidth, ellipsisWidth) runes, diff = t.trimLeft(runes, maxWidth, ellipsisWidth)
// Transform offsets // Transform offsets
transformOffsets(diff, rightTrim) transformOffsets(diff, rightTrim)
line = append(ellipsis, line...) runes = append(ellipsis, runes...)
} }
} else { } else {
line, _ = t.trimRight(line, maxWidth-ellipsisWidth) runes, _ = t.trimRight(runes, maxWidth-ellipsisWidth)
line = append(line, ellipsis...) runes = append(runes, ellipsis...)
for idx, offset := range offsets { for idx, offset := range offs {
offsets[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-len(ellipsis))) offs[idx].offset[0] = util.Min32(offset.offset[0], int32(maxWidth-len(ellipsis)))
offsets[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth)) offs[idx].offset[1] = util.Min32(offset.offset[1], int32(maxWidth))
} }
} }
displayWidth = t.displayWidthWithLimit(line, 0, displayWidth) displayWidth = t.displayWidthWithLimit(runes, 0, displayWidth)
} }
displayWidthSum += displayWidth
if maxWidth > 0 { if maxWidth > 0 {
color := colBase color := colBase
if hidden { if hidden {
color = color.WithFg(t.theme.Nomatch) color = color.WithFg(t.theme.Nomatch)
} }
t.printColoredString(t.window, line, offsets, color) todo[fidx] = func() {
t.printColoredString(t.window, runes, offs, color)
}
} else {
break
}
maxWidth -= displayWidth
}
if todo[0] != nil {
todo[0]()
}
if todo[2] != nil {
todo[2]()
}
if todo[1] != nil {
todo[1]()
} }
if postTask != nil { if postTask != nil {
postTask(actualLineNum, displayWidth, wasWrapped, forceRedraw, lbg) postTask(actualLineNum, displayWidthSum, wasWrapped, forceRedraw, lbg)
} else { } else {
t.markOtherLine(actualLineNum) t.markOtherLine(actualLineNum)
} }

View File

@@ -16,7 +16,7 @@ func _() {
_ = x[CtrlE-5] _ = x[CtrlE-5]
_ = x[CtrlF-6] _ = x[CtrlF-6]
_ = x[CtrlG-7] _ = x[CtrlG-7]
_ = x[CtrlH-8] _ = x[CtrlBackspace-8]
_ = x[Tab-9] _ = x[Tab-9]
_ = x[CtrlJ-10] _ = x[CtrlJ-10]
_ = x[CtrlK-11] _ = x[CtrlK-11]
@@ -99,75 +99,74 @@ func _() {
_ = x[CtrlRight-88] _ = x[CtrlRight-88]
_ = x[CtrlHome-89] _ = x[CtrlHome-89]
_ = x[CtrlEnd-90] _ = x[CtrlEnd-90]
_ = x[CtrlBackspace-91] _ = x[CtrlDelete-91]
_ = x[CtrlDelete-92] _ = x[CtrlPageUp-92]
_ = x[CtrlPageUp-93] _ = x[CtrlPageDown-93]
_ = x[CtrlPageDown-94] _ = x[Alt-94]
_ = x[Alt-95] _ = x[CtrlAlt-95]
_ = x[CtrlAlt-96] _ = x[CtrlAltUp-96]
_ = x[CtrlAltUp-97] _ = x[CtrlAltDown-97]
_ = x[CtrlAltDown-98] _ = x[CtrlAltLeft-98]
_ = x[CtrlAltLeft-99] _ = x[CtrlAltRight-99]
_ = x[CtrlAltRight-100] _ = x[CtrlAltHome-100]
_ = x[CtrlAltHome-101] _ = x[CtrlAltEnd-101]
_ = x[CtrlAltEnd-102] _ = x[CtrlAltBackspace-102]
_ = x[CtrlAltBackspace-103] _ = x[CtrlAltDelete-103]
_ = x[CtrlAltDelete-104] _ = x[CtrlAltPageUp-104]
_ = x[CtrlAltPageUp-105] _ = x[CtrlAltPageDown-105]
_ = x[CtrlAltPageDown-106] _ = x[CtrlShiftUp-106]
_ = x[CtrlShiftUp-107] _ = x[CtrlShiftDown-107]
_ = x[CtrlShiftDown-108] _ = x[CtrlShiftLeft-108]
_ = x[CtrlShiftLeft-109] _ = x[CtrlShiftRight-109]
_ = x[CtrlShiftRight-110] _ = x[CtrlShiftHome-110]
_ = x[CtrlShiftHome-111] _ = x[CtrlShiftEnd-111]
_ = x[CtrlShiftEnd-112] _ = x[CtrlShiftDelete-112]
_ = x[CtrlShiftDelete-113] _ = x[CtrlShiftPageUp-113]
_ = x[CtrlShiftPageUp-114] _ = x[CtrlShiftPageDown-114]
_ = x[CtrlShiftPageDown-115] _ = x[CtrlAltShiftUp-115]
_ = x[CtrlAltShiftUp-116] _ = x[CtrlAltShiftDown-116]
_ = x[CtrlAltShiftDown-117] _ = x[CtrlAltShiftLeft-117]
_ = x[CtrlAltShiftLeft-118] _ = x[CtrlAltShiftRight-118]
_ = x[CtrlAltShiftRight-119] _ = x[CtrlAltShiftHome-119]
_ = x[CtrlAltShiftHome-120] _ = x[CtrlAltShiftEnd-120]
_ = x[CtrlAltShiftEnd-121] _ = x[CtrlAltShiftDelete-121]
_ = x[CtrlAltShiftDelete-122] _ = x[CtrlAltShiftPageUp-122]
_ = x[CtrlAltShiftPageUp-123] _ = x[CtrlAltShiftPageDown-123]
_ = x[CtrlAltShiftPageDown-124] _ = x[Invalid-124]
_ = x[Invalid-125] _ = x[Fatal-125]
_ = x[Fatal-126] _ = x[BracketedPasteBegin-126]
_ = x[BracketedPasteBegin-127] _ = x[BracketedPasteEnd-127]
_ = x[BracketedPasteEnd-128] _ = x[Mouse-128]
_ = x[Mouse-129] _ = x[DoubleClick-129]
_ = x[DoubleClick-130] _ = x[LeftClick-130]
_ = x[LeftClick-131] _ = x[RightClick-131]
_ = x[RightClick-132] _ = x[SLeftClick-132]
_ = x[SLeftClick-133] _ = x[SRightClick-133]
_ = x[SRightClick-134] _ = x[ScrollUp-134]
_ = x[ScrollUp-135] _ = x[ScrollDown-135]
_ = x[ScrollDown-136] _ = x[SScrollUp-136]
_ = x[SScrollUp-137] _ = x[SScrollDown-137]
_ = x[SScrollDown-138] _ = x[PreviewScrollUp-138]
_ = x[PreviewScrollUp-139] _ = x[PreviewScrollDown-139]
_ = x[PreviewScrollDown-140] _ = x[Resize-140]
_ = x[Resize-141] _ = x[Change-141]
_ = x[Change-142] _ = x[BackwardEOF-142]
_ = x[BackwardEOF-143] _ = x[Start-143]
_ = x[Start-144] _ = x[Load-144]
_ = x[Load-145] _ = x[Focus-145]
_ = x[Focus-146] _ = x[One-146]
_ = x[One-147] _ = x[Zero-147]
_ = x[Zero-148] _ = x[Result-148]
_ = x[Result-149] _ = x[Jump-149]
_ = x[Jump-150] _ = x[JumpCancel-150]
_ = x[JumpCancel-151] _ = x[ClickHeader-151]
_ = x[ClickHeader-152] _ = x[ClickFooter-152]
_ = x[ClickFooter-153] _ = x[Multi-153]
_ = x[Multi-154]
} }
const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteShiftHomeShiftEndShiftPageUpShiftPageDownF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltDeleteAltHomeAltEndAltPageUpAltPageDownAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltShiftDeleteAltShiftHomeAltShiftEndAltShiftPageUpAltShiftPageDownCtrlUpCtrlDownCtrlLeftCtrlRightCtrlHomeCtrlEndCtrlBackspaceCtrlDeleteCtrlPageUpCtrlPageDownAltCtrlAltCtrlAltUpCtrlAltDownCtrlAltLeftCtrlAltRightCtrlAltHomeCtrlAltEndCtrlAltBackspaceCtrlAltDeleteCtrlAltPageUpCtrlAltPageDownCtrlShiftUpCtrlShiftDownCtrlShiftLeftCtrlShiftRightCtrlShiftHomeCtrlShiftEndCtrlShiftDeleteCtrlShiftPageUpCtrlShiftPageDownCtrlAltShiftUpCtrlAltShiftDownCtrlAltShiftLeftCtrlAltShiftRightCtrlAltShiftHomeCtrlAltShiftEndCtrlAltShiftDeleteCtrlAltShiftPageUpCtrlAltShiftPageDownInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderClickFooterMulti" const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlBackspaceTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteShiftHomeShiftEndShiftPageUpShiftPageDownF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltDeleteAltHomeAltEndAltPageUpAltPageDownAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltShiftDeleteAltShiftHomeAltShiftEndAltShiftPageUpAltShiftPageDownCtrlUpCtrlDownCtrlLeftCtrlRightCtrlHomeCtrlEndCtrlDeleteCtrlPageUpCtrlPageDownAltCtrlAltCtrlAltUpCtrlAltDownCtrlAltLeftCtrlAltRightCtrlAltHomeCtrlAltEndCtrlAltBackspaceCtrlAltDeleteCtrlAltPageUpCtrlAltPageDownCtrlShiftUpCtrlShiftDownCtrlShiftLeftCtrlShiftRightCtrlShiftHomeCtrlShiftEndCtrlShiftDeleteCtrlShiftPageUpCtrlShiftPageDownCtrlAltShiftUpCtrlAltShiftDownCtrlAltShiftLeftCtrlAltShiftRightCtrlAltShiftHomeCtrlAltShiftEndCtrlAltShiftDeleteCtrlAltShiftPageUpCtrlAltShiftPageDownInvalidFatalBracketedPasteBeginBracketedPasteEndMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeaderClickFooterMulti"
var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 157, 173, 182, 191, 199, 208, 214, 220, 228, 230, 234, 238, 243, 247, 250, 256, 263, 272, 281, 291, 302, 311, 319, 330, 343, 345, 347, 349, 351, 353, 355, 357, 359, 361, 364, 367, 370, 382, 387, 394, 401, 409, 418, 425, 431, 440, 451, 461, 473, 485, 498, 512, 524, 535, 549, 565, 571, 579, 587, 596, 604, 611, 624, 634, 644, 656, 659, 666, 675, 686, 697, 709, 720, 730, 746, 759, 772, 787, 798, 811, 824, 838, 851, 863, 878, 893, 910, 924, 940, 956, 973, 989, 1004, 1022, 1040, 1060, 1067, 1072, 1091, 1108, 1113, 1124, 1133, 1143, 1153, 1164, 1172, 1182, 1191, 1202, 1217, 1234, 1240, 1246, 1257, 1262, 1266, 1271, 1274, 1278, 1284, 1288, 1298, 1309, 1320, 1325} var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 52, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 143, 152, 165, 181, 190, 199, 207, 216, 222, 228, 236, 238, 242, 246, 251, 255, 258, 264, 271, 280, 289, 299, 310, 319, 327, 338, 351, 353, 355, 357, 359, 361, 363, 365, 367, 369, 372, 375, 378, 390, 395, 402, 409, 417, 426, 433, 439, 448, 459, 469, 481, 493, 506, 520, 532, 543, 557, 573, 579, 587, 595, 604, 612, 619, 629, 639, 651, 654, 661, 670, 681, 692, 704, 715, 725, 741, 754, 767, 782, 793, 806, 819, 833, 846, 858, 873, 888, 905, 919, 935, 951, 968, 984, 999, 1017, 1035, 1055, 1062, 1067, 1086, 1103, 1108, 1119, 1128, 1138, 1148, 1159, 1167, 1177, 1186, 1197, 1212, 1229, 1235, 1241, 1252, 1257, 1261, 1266, 1269, 1273, 1279, 1283, 1293, 1304, 1315, 1320}
func (i EventType) String() string { func (i EventType) String() string {
if i < 0 || i >= EventType(len(_EventType_index)-1) { if i < 0 || i >= EventType(len(_EventType_index)-1) {

View File

@@ -371,12 +371,10 @@ func (r *FullscreenRenderer) GetChar() Event {
} }
case rune(tcell.KeyCtrlH): case rune(tcell.KeyCtrlH):
switch { switch {
case ctrl:
return keyfn('h')
case alt: case alt:
return Event{AltBackspace, 0, nil} return Event{AltBackspace, 0, nil}
case none, shift: case ctrl, none, shift:
return Event{Backspace, 0, nil} return keyfn('h')
} }
} }
case tcell.KeyCtrlI: case tcell.KeyCtrlI:

View File

@@ -113,18 +113,18 @@ func TestGetCharEventKey(t *testing.T) {
{giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBackspace, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyBS, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled {giveKey{tcell.KeyCtrlH, 0, tcell.ModNone}, wantKey{Invalid, 0, nil}}, // fabricated, unhandled
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{Backspace, 0, nil}}, // actual "Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModNone}, wantKey{CtrlBackspace, 0, nil}}, // actual "Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Alt+Backspace" keystroke
{giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke {giveKey{tcell.KeyDEL, rune(tcell.KeyDEL), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{Backspace, 0, nil}}, // actual "Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModShift | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // actual "Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke {giveKey{tcell.KeyCtrlH, 0, tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAltBackspace, 0, nil}}, // actual "Ctrl+Shift+Alt+Backspace" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Alt+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt}, wantKey{AltBackspace, 0, nil}}, // fabricated "Ctrl+Alt+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlH, 0, nil}}, // actual "Ctrl+Shift+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModShift}, wantKey{CtrlBackspace, 0, nil}}, // actual "Ctrl+Shift+H" keystroke
{giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'h', nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke {giveKey{tcell.KeyCtrlH, rune(tcell.KeyCtrlH), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{AltBackspace, 0, nil}}, // fabricated "Ctrl+Shift+Alt+H" keystroke
// section 4: (Alt+Shift)+Key(Up|Down|Left|Right) // section 4: (Alt+Shift)+Key(Up|Down|Left|Right)
{giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}}, {giveKey{tcell.KeyUp, 0, tcell.ModNone}, wantKey{Up, 0, nil}},

View File

@@ -43,7 +43,7 @@ const (
CtrlE CtrlE
CtrlF CtrlF
CtrlG CtrlG
CtrlH CtrlBackspace
Tab Tab
CtrlJ CtrlJ
CtrlK CtrlK
@@ -137,7 +137,6 @@ const (
CtrlRight CtrlRight
CtrlHome CtrlHome
CtrlEnd CtrlEnd
CtrlBackspace
CtrlDelete CtrlDelete
CtrlPageUp CtrlPageUp
CtrlPageDown CtrlPageDown
@@ -296,8 +295,9 @@ func (a ColorAttr) IsColorDefined() bool {
} }
func (a ColorAttr) IsAttrDefined() bool { func (a ColorAttr) IsAttrDefined() bool {
return a.Attr != AttrUndefined return a.Attr&^BoldForce != AttrUndefined
} }
func (a ColorAttr) IsUndefined() bool { func (a ColorAttr) IsUndefined() bool {
return !a.IsColorDefined() && !a.IsAttrDefined() return !a.IsColorDefined() && !a.IsAttrDefined()
} }
@@ -1157,12 +1157,12 @@ func InitTheme(theme *ColorTheme, baseTheme *ColorTheme, boldify bool, forceBlac
// e.g. fzf --delimiter / --nth -1 --color fg:dim,nth:regular // e.g. fzf --delimiter / --nth -1 --color fg:dim,nth:regular
current := theme.Current current := theme.Current
if !baseTheme.Colored && current.IsUndefined() { if !baseTheme.Colored && current.IsUndefined() {
current.Attr = Reverse current.Attr |= Reverse
} }
theme.Current = theme.Fg.Merge(o(baseTheme.Current, current)) theme.Current = theme.Fg.Merge(o(baseTheme.Current, current))
currentMatch := theme.CurrentMatch currentMatch := theme.CurrentMatch
if !baseTheme.Colored && currentMatch.IsUndefined() { if !baseTheme.Colored && currentMatch.IsUndefined() {
currentMatch.Attr = Reverse | Underline currentMatch.Attr |= Reverse | Underline
} }
theme.CurrentMatch = o(baseTheme.CurrentMatch, currentMatch) theme.CurrentMatch = o(baseTheme.CurrentMatch, currentMatch)
theme.Spinner = o(baseTheme.Spinner, theme.Spinner) theme.Spinner = o(baseTheme.Spinner, theme.Spinner)

View File

@@ -1190,6 +1190,26 @@ class TestCore < TestInteractive
tmux.until { |lines| assert lines.any_include?('9999␊10000') } tmux.until { |lines| assert lines.any_include?('9999␊10000') }
end end
def test_freeze_left_keep_right
tmux.send_keys %[seq 10000 | #{FZF} --read0 --delimiter "\n" --freeze-left 3 --keep-right --ellipsis XX --no-multi-line --bind space:toggle-multi-line], :Enter
tmux.until { |lines| assert_match(/^> 1␊2␊3XX.*10000␊$/, lines[-3]) }
tmux.send_keys '5'
tmux.until { |lines| assert_match(/^> 1␊2␊3␊4␊5␊.*XX$/, lines[-3]) }
tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('> 1') }
tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('1␊2␊3␊4␊5␊') }
end
def test_freeze_right
tmux.send_keys %[seq 10000 | tr "\n" ' ' | #{FZF} --freeze-left 3 --freeze-right 3 --ellipsis XX], :Enter
tmux.until { |lines| assert_match(/XX9998 9999 10000$/, lines[-3]) }
tmux.send_keys "'1000"
tmux.until { |lines| assert_match(/^> 1 2 3XX.*XX9998 9999 10000$/,lines[-3]) }
tmux.send_keys :Space
tmux.until { |lines| assert lines.any_include?('> 1') }
end
def test_backward_eof def test_backward_eof
tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter tmux.send_keys "echo foo | #{FZF} --bind 'backward-eof:reload(seq 100)'", :Enter
tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 } tmux.until { |lines| lines.item_count == 1 && lines.match_count == 1 }