Let inline sections take precedence over --header-first

--header-first previously was rejected with --header-border=inline or
--header-lines-border=inline. Now, inline placement wins: an inline
section stays inside the list frame, and --header-first only affects
non-inline sections (mainly the main --header).
This commit is contained in:
Junegunn Choi
2026-04-19 21:27:55 +09:00
parent abfa60b7d0
commit dacb87abca
4 changed files with 46 additions and 10 deletions
+5 -4
View File
@@ -1107,9 +1107,10 @@ shape that has both top and bottom segments (rounded / sharp / bold /
double / block / thinblock / horizontal) and falls back to \fBline\fR double / block / thinblock / horizontal) and falls back to \fBline\fR
otherwise. When the list border also has side segments, the separator otherwise. When the list border also has side segments, the separator
joins them with T-junctions; \fBhorizontal\fR has no side borders, so the joins them with T-junctions; \fBhorizontal\fR has no side borders, so the
separator is drawn without T-junction endpoints. Not compatible with separator is drawn without T-junction endpoints. Takes precedence over
\fB\-\-header\-first\fR, and when \fB\-\-header\-lines\fR is also set \fB\-\-header\-first\fR (the section stays inside the list frame), and
\fB\-\-header\-lines\-border\fR must also be \fBinline\fR. when \fB\-\-header\-lines\fR is also set \fB\-\-header\-lines\-border\fR
must also be \fBinline\fR.
.TP .TP
.BI "\-\-header\-label" [=LABEL] .BI "\-\-header\-label" [=LABEL]
@@ -1128,7 +1129,7 @@ a single separator line between the header lines and the list section.
\fBinline\fR style embeds the header lines inside the list border frame \fBinline\fR style embeds the header lines inside the list border frame
with a horizontal separator; it requires a \fB\-\-list\-border\fR shape with a horizontal separator; it requires a \fB\-\-list\-border\fR shape
that has both top and bottom segments, falls back to \fBline\fR that has both top and bottom segments, falls back to \fBline\fR
otherwise, and is not compatible with \fB\-\-header\-first\fR. otherwise.
.SS FOOTER .SS FOOTER
-3
View File
@@ -3619,9 +3619,6 @@ func validateOptions(opts *Options) error {
opts.Preview.border == tui.BorderInline { opts.Preview.border == tui.BorderInline {
return errors.New("inline border is only supported for --header-border, --header-lines-border, and --footer-border") return errors.New("inline border is only supported for --header-border, --header-lines-border, and --footer-border")
} }
if opts.HeaderFirst && (opts.HeaderBorderShape == tui.BorderInline || opts.HeaderLinesShape == tui.BorderInline) {
return errors.New("--header-first is not compatible with --header-border=inline or --header-lines-border=inline")
}
if opts.HeaderBorderShape == tui.BorderInline && if opts.HeaderBorderShape == tui.BorderInline &&
opts.HeaderLinesShape != tui.BorderInline && opts.HeaderLinesShape != tui.BorderInline &&
opts.HeaderLinesShape != tui.BorderUndefined && opts.HeaderLinesShape != tui.BorderUndefined &&
+5 -1
View File
@@ -2844,7 +2844,11 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) {
if hasInputWindow { if hasInputWindow {
var btop int var btop int
if (hasHeaderWindow || hasHeaderLinesWindow) && t.headerFirst { // Inline sections live inside the list frame, so they don't participate
// in --header-first repositioning; only non-inline sections do.
hasNonInlineHeader := hasHeaderWindow && t.headerBorderShape != tui.BorderInline
hasNonInlineHeaderLines := hasHeaderLinesWindow && headerLinesShape != tui.BorderInline
if (hasNonInlineHeader || hasNonInlineHeaderLines) && t.headerFirst {
switch t.layout { switch t.layout {
case layoutDefault: case layoutDefault:
btop = w.Top() + w.Height() btop = w.Top() + w.Height()
+36 -2
View File
@@ -1491,6 +1491,42 @@ class TestLayout < TestInteractive
tmux.send_keys 'Escape' tmux.send_keys 'Escape'
end end
# Inline takes precedence over --header-first: the main header stays
# inside the list frame instead of moving below the input.
def test_inline_header_border_overrides_header_first
tmux.send_keys %(seq 5 | #{FZF} --style full --header foo --header-first --header-border inline), :Enter
tmux.until do |lines|
foo_idx = lines.index { |l| l.match?(/\A│\s+foo\s+│\z/) }
input_idx = lines.index { |l| l.match?(/\A│\s+>\s+\d+\/\d+\s+│\z/) }
foo_idx && input_idx && foo_idx < input_idx
end
end
# With both sections present, --header-first still moves the main --header
# below the input while --header-lines-border=inline keeps header-lines
# inside the list frame.
def test_inline_header_lines_with_header_first_and_main_header
tmux.send_keys %(seq 5 | #{FZF} --style full --header foo --header-lines 1 --header-first --header-lines-border inline), :Enter
tmux.until do |lines|
one_idx = lines.index { |l| l.match?(/\A│\s+1\s+│\z/) }
foo_idx = lines.index { |l| l.match?(/\A│\s+foo\s+│\z/) }
input_idx = lines.index { |l| l.match?(/\A│\s+>\s+\d+\/\d+\s+│\z/) }
one_idx && foo_idx && input_idx && one_idx < input_idx && input_idx < foo_idx
end
end
# With no main --header, --header-first previously repositioned
# header-lines. Inline now takes precedence: header-lines stays inside
# the list frame.
def test_inline_header_lines_with_header_first_no_main_header
tmux.send_keys %(seq 5 | #{FZF} --style full --header-lines 1 --header-first --header-lines-border inline), :Enter
tmux.until do |lines|
one_idx = lines.index { |l| l.match?(/\A│\s+1\s+│\z/) }
input_idx = lines.index { |l| l.match?(/\A│\s+>\s+\d+\/\d+\s+│\z/) }
one_idx && input_idx && one_idx < input_idx
end
end
# Invalid inline combinations must be rejected at startup. # Invalid inline combinations must be rejected at startup.
def test_inline_rejected_on_unsupported_options def test_inline_rejected_on_unsupported_options
[ [
@@ -1498,8 +1534,6 @@ class TestLayout < TestInteractive
['--list-border=inline', 'inline border is only supported'], ['--list-border=inline', 'inline border is only supported'],
['--input-border=inline', 'inline border is only supported'], ['--input-border=inline', 'inline border is only supported'],
['--preview-window=border-inline --preview :', 'invalid preview window option: border-inline'], ['--preview-window=border-inline --preview :', 'invalid preview window option: border-inline'],
['--header-first --header-border=inline', '--header-first is not compatible'],
['--header-first --header-lines-border=inline --header-lines=1', '--header-first is not compatible'],
['--header-border=inline --header-lines-border=sharp --header-lines=1', ['--header-border=inline --header-lines-border=sharp --header-lines=1',
'--header-border=inline requires --header-lines-border to be inline or unset'] '--header-border=inline requires --header-lines-border to be inline or unset']
].each do |args, expected| ].each do |args, expected|