diff --git a/CHANGELOG.md b/CHANGELOG.md index 28db9dce..f690fbe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ CHANGELOG 0.42.0 ------ +- Added new info style: `--info=right` - Added new info style: `--info=inline-right` - Added new border style `thinblock` which uses symbols for legacy computing [one eighth block elements](https://en.wikipedia.org/wiki/Symbols_for_Legacy_Computing) diff --git a/src/options.go b/src/options.go index f04d7f20..87f39929 100644 --- a/src/options.go +++ b/src/options.go @@ -72,7 +72,8 @@ const usage = `usage: fzf [options] (default: 0 or center) --margin=MARGIN Screen margin (TRBL | TB,RL | T,RL,B | T,R,B,L) --padding=PADDING Padding inside border (TRBL | TB,RL | T,RL,B | T,R,B,L) - --info=STYLE Finder info style [default|hidden|inline[:SEPARATOR]|inline-right] + --info=STYLE Finder info style + [default|right|hidden|inline[:SEPARATOR]|inline-right] --separator=STR String to form horizontal separator on info line --no-separator Hide info line separator --scrollbar[=C1[C2]] Scrollbar character(s) (each for main and preview window) @@ -194,11 +195,16 @@ type infoStyle int const ( infoDefault infoStyle = iota + infoRight infoInline infoInlineRight infoHidden ) +func (s infoStyle) noExtraLine() bool { + return s == infoInline || s == infoInlineRight || s == infoHidden +} + type labelOpts struct { label string column int @@ -1377,6 +1383,8 @@ func parseInfoStyle(str string) (infoStyle, string) { switch str { case "default": return infoDefault, "" + case "right": + return infoRight, "" case "inline": return infoInline, defaultInfoSep case "inline-right": @@ -1388,7 +1396,7 @@ func parseInfoStyle(str string) (infoStyle, string) { if strings.HasPrefix(str, prefix) { return infoInline, strings.ReplaceAll(str[len(prefix):], "\n", " ") } - errorExit("invalid info style (expected: default|hidden|inline[:SEPARATOR]|inline-right)") + errorExit("invalid info style (expected: default|right|hidden|inline[:SEPARATOR]|inline-right)") } return infoDefault, "" } diff --git a/src/terminal.go b/src/terminal.go index 08ea253b..94f4c6cd 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -565,7 +565,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { if previewBox != nil && opts.Preview.aboveOrBelow() { effectiveMinHeight += 1 + borderLines(opts.Preview.border) } - if opts.InfoStyle != infoDefault { + if opts.InfoStyle.noExtraLine() { effectiveMinHeight-- } effectiveMinHeight += borderLines(opts.BorderShape) @@ -845,7 +845,7 @@ func (t *Terminal) parsePrompt(prompt string) (func(), int) { } func (t *Terminal) noInfoLine() bool { - return t.infoStyle != infoDefault + return t.infoStyle.noExtraLine() } func getScrollbar(total int, height int, offset int) (int, int) { @@ -1479,6 +1479,8 @@ func (t *Terminal) printInfo() { printSpinner() t.move(line+1, 2, false) pos = 2 + case infoRight: + t.move(line+1, 0, false) case infoInlineRight: pos = t.promptLen + t.queryLen[0] + t.queryLen[1] + 1 t.move(line, pos, true) @@ -1529,6 +1531,37 @@ func (t *Terminal) printInfo() { if t.failed != nil && t.count == 0 { output = fmt.Sprintf("[Command failed: %s]", *t.failed) } + + printSeparator := func(fillLength int, pad bool) { + // --------_ + if t.separatorLen > 0 { + t.separator(t.window, fillLength) + t.window.Print(" ") + } else if pad { + t.window.Print(strings.Repeat(" ", fillLength+1)) + } + } + if t.infoStyle == infoRight { + maxWidth := t.window.Width() + if t.reading { + // Need space for spinner and a margin column + maxWidth -= 2 + } + output = t.trimMessage(output, maxWidth) + fillLength := t.window.Width() - len(output) - 2 + if t.reading { + if fillLength >= 2 { + printSeparator(fillLength-2, true) + } + printSpinner() + t.window.Print(" ") + } else if fillLength >= 0 { + printSeparator(fillLength, true) + } + t.window.CPrint(tui.ColInfo, output) + return + } + if t.infoStyle == infoInlineRight { pos = util.Max(pos, t.window.Width()-util.StringWidth(output)-3) if pos >= t.window.Width() { @@ -1539,14 +1572,14 @@ func (t *Terminal) printInfo() { t.window.Print(" ") pos += 2 } + maxWidth := t.window.Width() - pos output = t.trimMessage(output, maxWidth) t.window.CPrint(tui.ColInfo, output) - fillLength := maxWidth - len(output) - 2 - if t.separatorLen > 0 && fillLength > 0 { + if fillLength > 0 { t.window.CPrint(tui.ColSeparator, " ") - t.separator(t.window, fillLength) + printSeparator(fillLength, false) } } diff --git a/test/test_go.rb b/test/test_go.rb index 5ac0eee0..0062cf11 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -2707,6 +2707,16 @@ class TestGoFZF < TestBase tmux.until { assert(_1[-2] == ' 1/100') } end + def test_info_right + tmux.send_keys "#{FZF} --info=right --separator x --bind 'start:reload:seq 100; sleep 10'", :Enter + tmux.until { assert_match(%r{xxx [⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏] 100/100}, _1[-2]) } + end + + def test_info_inline_right + tmux.send_keys "#{FZF} --info=inline-right --bind 'start:reload:seq 100; sleep 10'", :Enter + tmux.until { assert_match(%r{[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏] 100/100}, _1[-1]) } + end + def test_prev_next_selected tmux.send_keys 'seq 10 | fzf --multi --bind ctrl-n:next-selected,ctrl-p:prev-selected', :Enter tmux.until { |lines| assert_equal 10, lines.item_count }