Add toggle-header option

Close #3358
This commit is contained in:
Junegunn Choi 2023-07-25 22:11:15 +09:00
parent c0435fdff4
commit f83491274f
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
5 changed files with 68 additions and 14 deletions

View File

@ -1,7 +1,7 @@
CHANGELOG CHANGELOG
========= =========
0.42.1 0.43.0
------ ------
- `--listen` server can be secured by setting `$FZF_API_KEY` environment - `--listen` server can be secured by setting `$FZF_API_KEY` environment
variable. variable.
@ -14,6 +14,7 @@ CHANGELOG
# Client # Client
curl localhost:6266 -H "x-api-key: $FZF_API_KEY" -d 'change-query(yo)' curl localhost:6266 -H "x-api-key: $FZF_API_KEY" -d 'change-query(yo)'
``` ```
- Added `toggle-header` action
0.42.0 0.42.0
------ ------

View File

@ -1137,6 +1137,7 @@ A key or an event can be bound to one or more of the following actions.
\fBtoggle\fR (\fIright-click\fR) \fBtoggle\fR (\fIright-click\fR)
\fBtoggle-all\fR (toggle all matches) \fBtoggle-all\fR (toggle all matches)
\fBtoggle+down\fR \fIctrl-i (tab)\fR \fBtoggle+down\fR \fIctrl-i (tab)\fR
\fBtoggle-header\fR
\fBtoggle-in\fR (\fB--layout=reverse*\fR ? \fBtoggle+up\fR : \fBtoggle+down\fR) \fBtoggle-in\fR (\fB--layout=reverse*\fR ? \fBtoggle+up\fR : \fBtoggle+down\fR)
\fBtoggle-out\fR (\fB--layout=reverse*\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR) \fBtoggle-out\fR (\fB--layout=reverse*\fR ? \fBtoggle+down\fR : \fBtoggle+up\fR)
\fBtoggle-preview\fR \fBtoggle-preview\fR

View File

@ -1115,6 +1115,8 @@ func parseActionList(masked string, original string, prevActions []*action, putA
appendAction(actToggleSearch) appendAction(actToggleSearch)
case "toggle-track": case "toggle-track":
appendAction(actToggleTrack) appendAction(actToggleTrack)
case "toggle-header":
appendAction(actToggleHeader)
case "track": case "track":
appendAction(actTrack) appendAction(actTrack)
case "select": case "select":

View File

@ -125,6 +125,7 @@ type eachLine struct {
} }
type itemLine struct { type itemLine struct {
offset int
current bool current bool
selected bool selected bool
label string label string
@ -192,6 +193,7 @@ type Terminal struct {
printQuery bool printQuery bool
history *History history *History
cycle bool cycle bool
headerVisible bool
headerFirst bool headerFirst bool
headerLines int headerLines int
header []string header []string
@ -341,6 +343,7 @@ const (
actToggleIn actToggleIn
actToggleOut actToggleOut
actToggleTrack actToggleTrack
actToggleHeader
actTrack actTrack
actDown actDown
actUp actUp
@ -628,6 +631,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
cleanExit: opts.ClearOnExit, cleanExit: opts.ClearOnExit,
paused: opts.Phony, paused: opts.Phony,
cycle: opts.Cycle, cycle: opts.Cycle,
headerVisible: true,
headerFirst: opts.HeaderFirst, headerFirst: opts.HeaderFirst,
headerLines: opts.HeaderLines, headerLines: opts.HeaderLines,
header: []string{}, header: []string{},
@ -734,9 +738,16 @@ func borderLines(shape tui.BorderShape) int {
return 0 return 0
} }
func (t *Terminal) visibleHeaderLines() int {
if !t.headerVisible {
return 0
}
return len(t.header0) + t.headerLines
}
// Extra number of lines needed to display fzf // Extra number of lines needed to display fzf
func (t *Terminal) extraLines() int { func (t *Terminal) extraLines() int {
extra := len(t.header0) + t.headerLines + 1 extra := t.visibleHeaderLines() + 1
if !t.noInfoLine() { if !t.noInfoLine() {
extra++ extra++
} }
@ -1386,7 +1397,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
case layoutDefault: case layoutDefault:
y = h - y - 1 y = h - y - 1
case layoutReverseList: case layoutReverseList:
n := 2 + len(t.header0) + len(t.header) n := 2 + t.visibleHeaderLines()
if t.noInfoLine() { if t.noInfoLine() {
n-- n--
} }
@ -1430,7 +1441,7 @@ func (t *Terminal) promptLine() int {
if !t.noInfoLine() { if !t.noInfoLine() {
max-- max--
} }
return util.Min(len(t.header0)+t.headerLines, max) return util.Min(t.visibleHeaderLines(), max)
} }
return 0 return 0
} }
@ -1583,7 +1594,7 @@ func (t *Terminal) printInfo() {
} }
func (t *Terminal) printHeader() { func (t *Terminal) printHeader() {
if len(t.header0)+len(t.header) == 0 { if t.visibleHeaderLines() == 0 {
return return
} }
max := t.window.Height() max := t.window.Height()
@ -1611,7 +1622,8 @@ func (t *Terminal) printHeader() {
text: util.ToChars([]byte(trimmed)), text: util.ToChars([]byte(trimmed)),
colors: colors} colors: colors}
t.move(line, 2, true) t.move(line, 0, true)
t.window.Print(" ")
t.printHighlighted(Result{item: item}, t.printHighlighted(Result{item: item},
tui.ColHeader, tui.ColHeader, false, false) tui.ColHeader, tui.ColHeader, false, false)
} }
@ -1628,13 +1640,13 @@ func (t *Terminal) printList() {
if t.layout == layoutDefault { if t.layout == layoutDefault {
i = maxy - 1 - j i = maxy - 1 - j
} }
line := i + 2 + len(t.header0) + len(t.header) line := i + 2 + t.visibleHeaderLines()
if t.noInfoLine() { if t.noInfoLine() {
line-- line--
} }
if i < count { if i < count {
t.printItem(t.merger.Get(i+t.offset), line, i, i == t.cy-t.offset, i >= barStart && i < barStart+barLength) t.printItem(t.merger.Get(i+t.offset), line, i, i == t.cy-t.offset, i >= barStart && i < barStart+barLength)
} else if t.prevLines[i] != emptyLine { } else if t.prevLines[i] != emptyLine || t.prevLines[i].offset != line {
t.prevLines[i] = emptyLine t.prevLines[i] = emptyLine
t.move(line, 0, true) t.move(line, 0, true)
} }
@ -1656,11 +1668,12 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b
} }
// Avoid unnecessary redraw // Avoid unnecessary redraw
newLine := itemLine{current: current, selected: selected, label: label, newLine := itemLine{offset: line, current: current, selected: selected, label: label,
result: result, queryLen: len(t.input), width: 0, bar: bar} result: result, queryLen: len(t.input), width: 0, bar: bar}
prevLine := t.prevLines[i] prevLine := t.prevLines[i]
forceRedraw := prevLine.offset != newLine.offset
printBar := func() { printBar := func() {
if len(t.scrollbar) > 0 && bar != prevLine.bar { if len(t.scrollbar) > 0 && (bar != prevLine.bar || forceRedraw) {
t.prevLines[i].bar = bar t.prevLines[i].bar = bar
t.move(line, t.window.Width()-1, true) t.move(line, t.window.Width()-1, true)
if bar { if bar {
@ -1669,7 +1682,8 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b
} }
} }
if prevLine.current == newLine.current && if !forceRedraw &&
prevLine.current == newLine.current &&
prevLine.selected == newLine.selected && prevLine.selected == newLine.selected &&
prevLine.label == newLine.label && prevLine.label == newLine.label &&
prevLine.queryLen == newLine.queryLen && prevLine.queryLen == newLine.queryLen &&
@ -1678,7 +1692,7 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool, bar b
return return
} }
t.move(line, 0, false) t.move(line, 0, forceRedraw)
if current { if current {
if len(label) == 0 { if len(label) == 0 {
t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty) t.window.CPrint(tui.ColCurrentCursorEmpty, t.pointerEmpty)
@ -3403,6 +3417,9 @@ func (t *Terminal) Loop() {
t.track = trackEnabled t.track = trackEnabled
} }
req(reqInfo) req(reqInfo)
case actToggleHeader:
t.headerVisible = !t.headerVisible
req(reqList, reqInfo, reqPrompt, reqHeader)
case actTrack: case actTrack:
if t.track == trackDisabled { if t.track == trackDisabled {
t.track = trackCurrent t.track = trackCurrent
@ -3485,7 +3502,7 @@ func (t *Terminal) Loop() {
// Translate coordinates // Translate coordinates
mx -= t.window.Left() mx -= t.window.Left()
my -= t.window.Top() my -= t.window.Top()
min := 2 + len(t.header0) + len(t.header) min := 2 + t.visibleHeaderLines()
if t.noInfoLine() { if t.noInfoLine() {
min-- min--
} }
@ -3757,7 +3774,7 @@ func (t *Terminal) vset(o int) bool {
} }
func (t *Terminal) maxItems() int { func (t *Terminal) maxItems() int {
max := t.window.Height() - 2 - len(t.header0) - len(t.header) max := t.window.Height() - 2 - t.visibleHeaderLines()
if t.noInfoLine() { if t.noInfoLine() {
max++ max++
} }

View File

@ -1213,6 +1213,39 @@ class TestGoFZF < TestBase
end end
end end
def test_toggle_header
tmux.send_keys "seq 4 | #{FZF} --header-lines 2 --header foo --bind space:toggle-header --header-first --height 10 --border", :Enter
before = <<~OUTPUT
4
> 3
2/2
>
2
1
foo
OUTPUT
tmux.until { assert_block(before, _1) }
tmux.send_keys :Space
after = <<~OUTPUT
4
> 3
2/2
>
OUTPUT
tmux.until { assert_block(after, _1) }
tmux.send_keys :Space
tmux.until { assert_block(before, _1) }
end
def test_cancel def test_cancel
tmux.send_keys "seq 10 | #{fzf('--bind 2:cancel')}", :Enter tmux.send_keys "seq 10 | #{fzf('--bind 2:cancel')}", :Enter
tmux.until { |lines| assert_equal ' 10/10', lines[-2] } tmux.until { |lines| assert_equal ' 10/10', lines[-2] }