mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-11-25 14:17:40 +00:00
Add --header-first option to display header before prompt line
Close #2422
This commit is contained in:
parent
ffd8bef808
commit
7bff4661f6
@ -1,8 +1,12 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
0.27.4
|
0.28.0
|
||||||
------
|
------
|
||||||
|
- Added `--header-first` option to print header before the prompt line
|
||||||
|
```sh
|
||||||
|
fzf --header $'Welcome to fzf\n▔▔▔▔▔▔▔▔▔▔▔▔▔▔' --reverse --height 30% --border --header-first
|
||||||
|
```
|
||||||
- Added `--scroll-off=LINES` option (similar to `scrolloff` option of Vim)
|
- Added `--scroll-off=LINES` option (similar to `scrolloff` option of Vim)
|
||||||
- You can set it to a very large number so that the cursor stays in the
|
- You can set it to a very large number so that the cursor stays in the
|
||||||
middle of the screen while scrolling
|
middle of the screen while scrolling
|
||||||
|
@ -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 "Nov 2021" "fzf 0.27.4" "fzf - a command-line fuzzy finder"
|
.TH fzf 1 "Nov 2021" "fzf 0.28.0" "fzf - a command-line fuzzy finder"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
fzf - a command-line fuzzy finder
|
fzf - a command-line fuzzy finder
|
||||||
@ -299,6 +299,9 @@ are not affected by \fB--with-nth\fR. ANSI color codes are processed even when
|
|||||||
The first N lines of the input are treated as the sticky header. When
|
The first N lines of the input are treated as the sticky header. When
|
||||||
\fB--with-nth\fR is set, the lines are transformed just like the other
|
\fB--with-nth\fR is set, the lines are transformed just like the other
|
||||||
lines that follow.
|
lines that follow.
|
||||||
|
.TP
|
||||||
|
.B "--header-first"
|
||||||
|
Print header before the prompt line
|
||||||
.SS Display
|
.SS Display
|
||||||
.TP
|
.TP
|
||||||
.B "--ansi"
|
.B "--ansi"
|
||||||
|
@ -69,6 +69,7 @@ const usage = `usage: fzf [options]
|
|||||||
--marker=STR Multi-select marker (default: '>')
|
--marker=STR Multi-select marker (default: '>')
|
||||||
--header=STR String to print as header
|
--header=STR String to print as header
|
||||||
--header-lines=N The first N lines of the input are treated as header
|
--header-lines=N The first N lines of the input are treated as header
|
||||||
|
--header-first Print header before the prompt line
|
||||||
|
|
||||||
Display
|
Display
|
||||||
--ansi Enable processing of ANSI color codes
|
--ansi Enable processing of ANSI color codes
|
||||||
@ -225,6 +226,7 @@ type Options struct {
|
|||||||
History *History
|
History *History
|
||||||
Header []string
|
Header []string
|
||||||
HeaderLines int
|
HeaderLines int
|
||||||
|
HeaderFirst bool
|
||||||
Margin [4]sizeSpec
|
Margin [4]sizeSpec
|
||||||
Padding [4]sizeSpec
|
Padding [4]sizeSpec
|
||||||
BorderShape tui.BorderShape
|
BorderShape tui.BorderShape
|
||||||
@ -287,6 +289,7 @@ func defaultOptions() *Options {
|
|||||||
History: nil,
|
History: nil,
|
||||||
Header: make([]string, 0),
|
Header: make([]string, 0),
|
||||||
HeaderLines: 0,
|
HeaderLines: 0,
|
||||||
|
HeaderFirst: false,
|
||||||
Margin: defaultMargin(),
|
Margin: defaultMargin(),
|
||||||
Padding: defaultMargin(),
|
Padding: defaultMargin(),
|
||||||
Unicode: true,
|
Unicode: true,
|
||||||
@ -1427,6 +1430,10 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||||||
case "--header-lines":
|
case "--header-lines":
|
||||||
opts.HeaderLines = atoi(
|
opts.HeaderLines = atoi(
|
||||||
nextString(allArgs, &i, "number of header lines required"))
|
nextString(allArgs, &i, "number of header lines required"))
|
||||||
|
case "--header-first":
|
||||||
|
opts.HeaderFirst = true
|
||||||
|
case "--no-header-first":
|
||||||
|
opts.HeaderFirst = false
|
||||||
case "--preview":
|
case "--preview":
|
||||||
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
opts.Preview.command = nextString(allArgs, &i, "preview command required")
|
||||||
case "--no-preview":
|
case "--no-preview":
|
||||||
|
@ -140,6 +140,8 @@ type Terminal struct {
|
|||||||
printQuery bool
|
printQuery bool
|
||||||
history *History
|
history *History
|
||||||
cycle bool
|
cycle bool
|
||||||
|
headerFirst bool
|
||||||
|
headerLines int
|
||||||
header []string
|
header []string
|
||||||
header0 []string
|
header0 []string
|
||||||
ansi bool
|
ansi bool
|
||||||
@ -529,6 +531,8 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
paused: opts.Phony,
|
paused: opts.Phony,
|
||||||
strong: strongAttr,
|
strong: strongAttr,
|
||||||
cycle: opts.Cycle,
|
cycle: opts.Cycle,
|
||||||
|
headerFirst: opts.HeaderFirst,
|
||||||
|
headerLines: opts.HeaderLines,
|
||||||
header: header,
|
header: header,
|
||||||
header0: header,
|
header0: header,
|
||||||
ansi: opts.Ansi,
|
ansi: opts.Ansi,
|
||||||
@ -976,12 +980,23 @@ func (t *Terminal) updatePromptOffset() ([]rune, []rune) {
|
|||||||
return before, after
|
return before, after
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Terminal) promptLine() int {
|
||||||
|
if t.headerFirst {
|
||||||
|
max := t.window.Height() - 1
|
||||||
|
if !t.noInfoLine() {
|
||||||
|
max--
|
||||||
|
}
|
||||||
|
return util.Min(len(t.header0)+t.headerLines, max)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Terminal) placeCursor() {
|
func (t *Terminal) placeCursor() {
|
||||||
t.move(0, t.promptLen+t.queryLen[0], false)
|
t.move(t.promptLine(), t.promptLen+t.queryLen[0], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Terminal) printPrompt() {
|
func (t *Terminal) printPrompt() {
|
||||||
t.move(0, 0, true)
|
t.move(t.promptLine(), 0, true)
|
||||||
t.prompt()
|
t.prompt()
|
||||||
|
|
||||||
before, after := t.updatePromptOffset()
|
before, after := t.updatePromptOffset()
|
||||||
@ -1003,22 +1018,23 @@ func (t *Terminal) trimMessage(message string, maxWidth int) string {
|
|||||||
|
|
||||||
func (t *Terminal) printInfo() {
|
func (t *Terminal) printInfo() {
|
||||||
pos := 0
|
pos := 0
|
||||||
|
line := t.promptLine()
|
||||||
switch t.infoStyle {
|
switch t.infoStyle {
|
||||||
case infoDefault:
|
case infoDefault:
|
||||||
t.move(1, 0, true)
|
t.move(line+1, 0, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
duration := int64(spinnerDuration)
|
duration := int64(spinnerDuration)
|
||||||
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
idx := (time.Now().UnixNano() % (duration * int64(len(t.spinner)))) / duration
|
||||||
t.window.CPrint(tui.ColSpinner, t.spinner[idx])
|
t.window.CPrint(tui.ColSpinner, t.spinner[idx])
|
||||||
}
|
}
|
||||||
t.move(1, 2, false)
|
t.move(line+1, 2, false)
|
||||||
pos = 2
|
pos = 2
|
||||||
case infoInline:
|
case infoInline:
|
||||||
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1
|
||||||
if pos+len(" < ") > t.window.Width() {
|
if pos+len(" < ") > t.window.Width() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.move(0, pos, true)
|
t.move(line, pos, true)
|
||||||
if t.reading {
|
if t.reading {
|
||||||
t.window.CPrint(tui.ColSpinner, " < ")
|
t.window.CPrint(tui.ColSpinner, " < ")
|
||||||
} else {
|
} else {
|
||||||
@ -1061,11 +1077,20 @@ func (t *Terminal) printHeader() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
max := t.window.Height()
|
max := t.window.Height()
|
||||||
|
if t.headerFirst {
|
||||||
|
max--
|
||||||
|
if !t.noInfoLine() {
|
||||||
|
max--
|
||||||
|
}
|
||||||
|
}
|
||||||
var state *ansiState
|
var state *ansiState
|
||||||
for idx, lineStr := range t.header {
|
for idx, lineStr := range t.header {
|
||||||
line := idx + 2
|
line := idx
|
||||||
if t.noInfoLine() {
|
if !t.headerFirst {
|
||||||
line--
|
line++
|
||||||
|
if !t.noInfoLine() {
|
||||||
|
line++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if line >= max {
|
if line >= max {
|
||||||
continue
|
continue
|
||||||
@ -2644,7 +2669,7 @@ func (t *Terminal) Loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if me.Down {
|
} else if me.Down {
|
||||||
if my == 0 && mx >= 0 {
|
if my == t.promptLine() && mx >= 0 {
|
||||||
// Prompt
|
// Prompt
|
||||||
t.cx = mx + t.xoffset
|
t.cx = mx + t.xoffset
|
||||||
} else if my >= min {
|
} else if my >= min {
|
||||||
|
@ -2109,6 +2109,39 @@ class TestGoFZF < TestBase
|
|||||||
tmux.send_keys :Down
|
tmux.send_keys :Down
|
||||||
tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
|
tmux.until { |lines| assert_equal "> #{height + 1}", lines[height / 2].strip }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_header_first
|
||||||
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
expected = <<~OUTPUT
|
||||||
|
> 4
|
||||||
|
997/997
|
||||||
|
>
|
||||||
|
3
|
||||||
|
2
|
||||||
|
1
|
||||||
|
foobar
|
||||||
|
OUTPUT
|
||||||
|
|
||||||
|
assert_equal expected.chomp, lines.reverse.take(7).reverse.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_header_first_reverse
|
||||||
|
tmux.send_keys "seq 1000 | #{FZF} --header foobar --header-lines 3 --header-first --reverse --inline-info", :Enter
|
||||||
|
tmux.until do |lines|
|
||||||
|
expected = <<~OUTPUT
|
||||||
|
foobar
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
> < 997/997
|
||||||
|
> 4
|
||||||
|
OUTPUT
|
||||||
|
|
||||||
|
assert_equal expected.chomp, lines.take(6).join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module TestShell
|
module TestShell
|
||||||
|
Loading…
Reference in New Issue
Block a user