mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-15 14:53:47 -05:00
Fix inconsistent placement of header-lines with border options
fzf displayed --header-lines inconsistently depending on the presence of borders: # --header and --header-lines co-located seq 10 | fzf --header-lines 3 --header "$(seq 101 103)" --header-first # --header and --header-lines separated seq 10 | fzf --header-lines 3 --header "$(seq 101 103)" --header-first --header-lines-border This commit fixes the inconsistency with the following logic: * If only one of --header or --header-lines is provided, --header-first applies to that single header. * If both are present, --header-first affects only the regular --header, not --header-lines.
This commit is contained in:
@@ -1001,7 +1001,8 @@ The first N lines of the input are treated as the sticky header. When
|
|||||||
lines that follow.
|
lines that follow.
|
||||||
.TP
|
.TP
|
||||||
.B "\-\-header\-first"
|
.B "\-\-header\-first"
|
||||||
Print header before the prompt line
|
Print header before the prompt line. When both normal header and header lines
|
||||||
|
(\fB\-\-header\-lines\fR) are present, this applies only to the normal header.
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-header\-border" [=STYLE]
|
.BI "\-\-header\-border" [=STYLE]
|
||||||
Draw border around the header section
|
Draw border around the header section
|
||||||
|
|||||||
@@ -3218,15 +3218,6 @@ func postProcessOptions(opts *Options) error {
|
|||||||
|
|
||||||
if opts.HeaderLinesShape == tui.BorderNone {
|
if opts.HeaderLinesShape == tui.BorderNone {
|
||||||
opts.HeaderLinesShape = tui.BorderPhantom
|
opts.HeaderLinesShape = tui.BorderPhantom
|
||||||
} else if opts.HeaderLinesShape == tui.BorderUndefined {
|
|
||||||
// In reverse-list layout, header lines should be at the top, while
|
|
||||||
// ordinary header should be at the bottom. So let's use a separate
|
|
||||||
// window for the header lines.
|
|
||||||
if opts.Layout == layoutReverseList {
|
|
||||||
opts.HeaderLinesShape = tui.BorderPhantom
|
|
||||||
} else {
|
|
||||||
opts.HeaderLinesShape = tui.BorderNone
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Pointer == nil {
|
if opts.Pointer == nil {
|
||||||
|
|||||||
125
src/terminal.go
125
src/terminal.go
@@ -1203,8 +1203,8 @@ func (t *Terminal) extraLines() int {
|
|||||||
extra += borderLines(t.headerBorderShape)
|
extra += borderLines(t.headerBorderShape)
|
||||||
}
|
}
|
||||||
extra += len(t.header0)
|
extra += len(t.header0)
|
||||||
if t.hasHeaderLinesWindow() {
|
if w, shape := t.determineHeaderLinesShape(); w {
|
||||||
extra += borderLines(t.headerLinesShape)
|
extra += borderLines(shape)
|
||||||
}
|
}
|
||||||
extra += t.headerLines
|
extra += t.headerLines
|
||||||
}
|
}
|
||||||
@@ -1770,7 +1770,46 @@ func (t *Terminal) hasHeaderWindow() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) hasHeaderLinesWindow() bool {
|
func (t *Terminal) hasHeaderLinesWindow() bool {
|
||||||
return t.headerVisible && t.headerLines > 0 && t.headerLinesShape.Visible()
|
w, _ := t.determineHeaderLinesShape()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) determineHeaderLinesShape() (bool, tui.BorderShape) {
|
||||||
|
if !t.headerVisible || t.headerLines == 0 {
|
||||||
|
return false, tui.BorderNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// --header-lines-border is set
|
||||||
|
if t.headerLinesShape != tui.BorderUndefined {
|
||||||
|
return true, t.headerLinesShape
|
||||||
|
}
|
||||||
|
|
||||||
|
// --header-lines-border is not set, determine if we should use
|
||||||
|
// the style of --header-border
|
||||||
|
shape := tui.BorderNone
|
||||||
|
if len(t.header0) == 0 {
|
||||||
|
shape = t.headerBorderShape
|
||||||
|
}
|
||||||
|
if shape == tui.BorderNone {
|
||||||
|
shape = tui.BorderPhantom
|
||||||
|
}
|
||||||
|
|
||||||
|
// --layout reverse-list is set
|
||||||
|
if t.layout == layoutReverseList {
|
||||||
|
return true, shape
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use header window instead
|
||||||
|
if len(t.header0) == 0 {
|
||||||
|
return false, t.headerBorderShape
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have both types of headers, and we want to separate the two
|
||||||
|
if t.headerFirst {
|
||||||
|
return true, shape
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, tui.BorderNone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
||||||
@@ -1853,7 +1892,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
shift := 0
|
shift := 0
|
||||||
shrink := 0
|
shrink := 0
|
||||||
hasHeaderWindow := t.hasHeaderWindow()
|
hasHeaderWindow := t.hasHeaderWindow()
|
||||||
hasHeaderLinesWindow := t.hasHeaderLinesWindow()
|
hasHeaderLinesWindow, headerLinesShape := t.determineHeaderLinesShape()
|
||||||
hasInputWindow := !t.inputless && (t.inputBorderShape.Visible() || hasHeaderWindow || hasHeaderLinesWindow)
|
hasInputWindow := !t.inputless && (t.inputBorderShape.Visible() || hasHeaderWindow || hasHeaderLinesWindow)
|
||||||
if hasInputWindow {
|
if hasInputWindow {
|
||||||
inputWindowHeight := 2
|
inputWindowHeight := 2
|
||||||
@@ -1889,7 +1928,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
|
|
||||||
headerLinesHeight := 0
|
headerLinesHeight := 0
|
||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow {
|
||||||
headerLinesHeight = util.Min(availableLines, borderLines(t.headerLinesShape)+t.headerLines)
|
headerLinesHeight = util.Min(availableLines, borderLines(headerLinesShape)+t.headerLines)
|
||||||
if t.layout != layoutDefault {
|
if t.layout != layoutDefault {
|
||||||
shift += headerLinesHeight
|
shift += headerLinesHeight
|
||||||
shrink += headerLinesHeight
|
shrink += headerLinesHeight
|
||||||
@@ -2147,23 +2186,33 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
if t.wborder == nil {
|
if t.wborder == nil {
|
||||||
w = t.window
|
w = t.window
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasInputWindow {
|
if hasInputWindow {
|
||||||
var btop int
|
var btop int
|
||||||
if hasHeaderWindow && t.headerFirst {
|
if (hasHeaderWindow || hasHeaderLinesWindow) && t.headerFirst {
|
||||||
if t.layout == layoutReverse {
|
switch t.layout {
|
||||||
btop = w.Top() - inputBorderHeight - headerLinesHeight
|
case layoutDefault:
|
||||||
} else if t.layout == layoutReverseList {
|
btop = w.Top() + w.Height()
|
||||||
|
// If both headers are present, the header lines are displayed with the list
|
||||||
|
if hasHeaderWindow && hasHeaderLinesWindow {
|
||||||
|
btop += headerLinesHeight
|
||||||
|
}
|
||||||
|
case layoutReverse:
|
||||||
|
btop = w.Top() - inputBorderHeight
|
||||||
|
if hasHeaderWindow && hasHeaderLinesWindow {
|
||||||
|
btop -= headerLinesHeight
|
||||||
|
}
|
||||||
|
case layoutReverseList:
|
||||||
btop = w.Top() + w.Height()
|
btop = w.Top() + w.Height()
|
||||||
} else {
|
|
||||||
btop = w.Top() + w.Height() + headerLinesHeight
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if t.layout == layoutReverse {
|
switch t.layout {
|
||||||
btop = w.Top() - shrink
|
case layoutDefault:
|
||||||
} else if t.layout == layoutReverseList {
|
|
||||||
btop = w.Top() + w.Height() + headerBorderHeight
|
|
||||||
} else {
|
|
||||||
btop = w.Top() + w.Height() + headerBorderHeight + headerLinesHeight
|
btop = w.Top() + w.Height() + headerBorderHeight + headerLinesHeight
|
||||||
|
case layoutReverse:
|
||||||
|
btop = w.Top() - shrink
|
||||||
|
case layoutReverseList:
|
||||||
|
btop = w.Top() + w.Height() + headerBorderHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shift := 0
|
shift := 0
|
||||||
@@ -2215,17 +2264,34 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
|
|||||||
// Set up header lines border
|
// Set up header lines border
|
||||||
if hasHeaderLinesWindow {
|
if hasHeaderLinesWindow {
|
||||||
var btop int
|
var btop int
|
||||||
if t.layout != layoutDefault {
|
// NOTE: We still have to handle --header-first here in case
|
||||||
btop = w.Top() - headerLinesHeight
|
// --header-lines-border is set. Can't we just use header window instead
|
||||||
|
// with the style? So we can display header label.
|
||||||
|
// fzf --header-lines 3 --header-label hello --header-border
|
||||||
|
// fzf --header-lines 3 --header-label hello --header-lines-border
|
||||||
|
headerFirst := t.headerFirst && len(t.header0) == 0
|
||||||
|
|
||||||
|
if headerFirst {
|
||||||
|
if t.layout == layoutDefault {
|
||||||
|
btop = w.Top() + w.Height() + inputBorderHeight
|
||||||
|
} else if t.layout == layoutReverse {
|
||||||
|
btop = w.Top() - headerLinesHeight - inputBorderHeight
|
||||||
|
} else {
|
||||||
|
btop = w.Top() - headerLinesHeight
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
btop = w.Top() + w.Height()
|
if t.layout != layoutDefault {
|
||||||
|
btop = w.Top() - headerLinesHeight
|
||||||
|
} else {
|
||||||
|
btop = w.Top() + w.Height()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t.headerLinesBorder = t.tui.NewWindow(
|
t.headerLinesBorder = t.tui.NewWindow(
|
||||||
btop,
|
btop,
|
||||||
w.Left(),
|
w.Left(),
|
||||||
w.Width(),
|
w.Width(),
|
||||||
headerLinesHeight, tui.WindowHeader, tui.MakeBorderStyle(t.headerLinesShape, t.unicode), true)
|
headerLinesHeight, tui.WindowHeader, tui.MakeBorderStyle(headerLinesShape, t.unicode), true)
|
||||||
t.headerLinesWindow = createInnerWindow(t.headerLinesBorder, t.headerLinesShape, tui.WindowHeader, 0)
|
t.headerLinesWindow = createInnerWindow(t.headerLinesBorder, headerLinesShape, tui.WindowHeader, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print border label
|
// Print border label
|
||||||
@@ -2621,12 +2687,14 @@ func (t *Terminal) resizeIfNeeded() bool {
|
|||||||
// Check if the header borders are used and header has changed
|
// Check if the header borders are used and header has changed
|
||||||
allHeaderLines := t.visibleHeaderLines()
|
allHeaderLines := t.visibleHeaderLines()
|
||||||
primaryHeaderLines := allHeaderLines
|
primaryHeaderLines := allHeaderLines
|
||||||
if t.headerLinesShape.Visible() {
|
if t.hasHeaderLinesWindow() {
|
||||||
primaryHeaderLines -= t.headerLines
|
primaryHeaderLines -= t.headerLines
|
||||||
}
|
}
|
||||||
if (t.headerBorderShape.Visible() || t.headerLinesShape.Visible()) &&
|
needHeaderLinesWindow := t.hasHeaderLinesWindow()
|
||||||
|
if (t.headerBorderShape.Visible() || t.hasHeaderLinesWindow()) &&
|
||||||
(t.headerWindow == nil && primaryHeaderLines > 0 || t.headerWindow != nil && primaryHeaderLines != t.headerWindow.Height()) ||
|
(t.headerWindow == nil && primaryHeaderLines > 0 || t.headerWindow != nil && primaryHeaderLines != t.headerWindow.Height()) ||
|
||||||
t.headerLinesShape.Visible() && (t.headerLinesWindow == nil && t.headerLines > 0 || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) {
|
needHeaderLinesWindow && (t.headerLinesWindow == nil || t.headerLinesWindow != nil && t.headerLines != t.headerLinesWindow.Height()) ||
|
||||||
|
!needHeaderLinesWindow && t.headerLinesWindow != nil {
|
||||||
t.printAll()
|
t.printAll()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -2640,14 +2708,14 @@ func (t *Terminal) printHeader() {
|
|||||||
|
|
||||||
t.withWindow(t.headerWindow, func() {
|
t.withWindow(t.headerWindow, func() {
|
||||||
var lines []string
|
var lines []string
|
||||||
if !t.headerLinesShape.Visible() {
|
if !t.hasHeaderLinesWindow() {
|
||||||
lines = t.header
|
lines = t.header
|
||||||
}
|
}
|
||||||
t.printHeaderImpl(t.headerWindow, t.headerBorderShape, t.header0, lines)
|
t.printHeaderImpl(t.headerWindow, t.headerBorderShape, t.header0, lines)
|
||||||
})
|
})
|
||||||
if t.headerLinesShape.Visible() {
|
if w, shape := t.determineHeaderLinesShape(); w {
|
||||||
t.withWindow(t.headerLinesWindow, func() {
|
t.withWindow(t.headerLinesWindow, func() {
|
||||||
t.printHeaderImpl(t.headerLinesWindow, t.headerLinesShape, nil, t.header)
|
t.printHeaderImpl(t.headerLinesWindow, shape, nil, t.header)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5863,7 +5931,8 @@ func (t *Terminal) Loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if clicked && 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)
|
_, shape := t.determineHeaderLinesShape()
|
||||||
|
mx -= t.headerLinesWindow.Left() + t.headerIndent(shape)
|
||||||
my -= t.headerLinesWindow.Top()
|
my -= t.headerLinesWindow.Top()
|
||||||
if mx < 0 {
|
if mx < 0 {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -39,11 +39,11 @@ class TestLayout < TestInteractive
|
|||||||
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
|
||||||
block = <<~OUTPUT
|
block = <<~OUTPUT
|
||||||
> 4
|
> 4
|
||||||
997/997
|
|
||||||
>
|
|
||||||
3
|
3
|
||||||
2
|
2
|
||||||
1
|
1
|
||||||
|
997/997
|
||||||
|
>
|
||||||
foobar
|
foobar
|
||||||
OUTPUT
|
OUTPUT
|
||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
@@ -53,10 +53,10 @@ class TestLayout < TestInteractive
|
|||||||
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
|
||||||
block = <<~OUTPUT
|
block = <<~OUTPUT
|
||||||
foobar
|
foobar
|
||||||
|
> < 997/997
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
> < 997/997
|
|
||||||
> 4
|
> 4
|
||||||
OUTPUT
|
OUTPUT
|
||||||
tmux.until { assert_block(block, it) }
|
tmux.until { assert_block(block, it) }
|
||||||
@@ -148,10 +148,10 @@ class TestLayout < TestInteractive
|
|||||||
│
|
│
|
||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
│ 2/2
|
|
||||||
│ >
|
|
||||||
│ 2
|
│ 2
|
||||||
│ 1
|
│ 1
|
||||||
|
│ 2/2
|
||||||
|
│ >
|
||||||
│ foo
|
│ foo
|
||||||
╰───────
|
╰───────
|
||||||
OUTPUT
|
OUTPUT
|
||||||
@@ -609,11 +609,11 @@ class TestLayout < TestInteractive
|
|||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
╰──────────
|
╰──────────
|
||||||
|
2
|
||||||
|
1
|
||||||
98/98 ─
|
98/98 ─
|
||||||
>
|
>
|
||||||
╭──────────
|
╭──────────
|
||||||
│ 2
|
|
||||||
│ 1
|
|
||||||
│ hello
|
│ hello
|
||||||
╰──────────
|
╰──────────
|
||||||
BLOCK
|
BLOCK
|
||||||
@@ -666,12 +666,12 @@ class TestLayout < TestInteractive
|
|||||||
│ 4
|
│ 4
|
||||||
│ > 3
|
│ > 3
|
||||||
╰──────────
|
╰──────────
|
||||||
|
98/98 ─
|
||||||
|
>
|
||||||
╔══════════
|
╔══════════
|
||||||
║ 2
|
║ 2
|
||||||
║ 1
|
║ 1
|
||||||
╚══════════
|
╚══════════
|
||||||
98/98 ─
|
|
||||||
>
|
|
||||||
BLOCK
|
BLOCK
|
||||||
tmux.until { assert_block(block1, it) }
|
tmux.until { assert_block(block1, it) }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user