Add --info=STYLE [default|inline|hidden]

Close #1738
This commit is contained in:
Junegunn Choi 2019-11-15 00:39:29 +09:00
parent 168453da71
commit d2fa470165
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
5 changed files with 84 additions and 29 deletions

View File

@ -39,6 +39,10 @@ CHANGELOG
# usage: _fzf_setup_completion path|dir COMMANDS... # usage: _fzf_setup_completion path|dir COMMANDS...
_fzf_setup_completion path git kubectl _fzf_setup_completion path git kubectl
``` ```
- Info line style can be changed by `--info=STYLE`
- `--info=default`
- `--info=inline` (same as old `--inline-info`)
- `--info=hidden`
- When you transform the input with `--with-nth`, the trailing white spaces - When you transform the input with `--with-nth`, the trailing white spaces
are removed. are removed.
- `ctrl-\`, `ctrl-]`, `ctrl-^`, and `ctrl-/` can now be used with `--bind` - `ctrl-\`, `ctrl-]`, `ctrl-^`, and `ctrl-/` can now be used with `--bind`

View File

@ -212,8 +212,21 @@ e.g.
fzf --margin 1,5%\fR fzf --margin 1,5%\fR
.RE .RE
.TP .TP
.B "--inline-info" .BI "--info=" "STYLE"
Display finder info inline with the query Determines the display style of finder info.
.br
.BR default " Display on the next line to the prompt"
.br
.BR inline " Display on the same line"
.br
.BR hidden " Do not display finder info"
.br
.TP
.B "--no-info"
A synonym for \fB--info=hidden\fB
.TP .TP
.BI "--prompt=" "STR" .BI "--prompt=" "STR"
Input prompt (default: '> ') Input prompt (default: '> ')

View File

@ -57,7 +57,7 @@ const usage = `usage: fzf [options]
--layout=LAYOUT Choose layout: [default|reverse|reverse-list] --layout=LAYOUT Choose layout: [default|reverse|reverse-list]
--border Draw border above and below the finder --border Draw border above and below the finder
--margin=MARGIN Screen margin (TRBL / TB,RL / T,RL,B / T,R,B,L) --margin=MARGIN Screen margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
--inline-info Display finder info inline with the query --info=STYLE Finder info style [default|inline|hidden]
--prompt=STR Input prompt (default: '> ') --prompt=STR Input prompt (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
@ -142,6 +142,14 @@ const (
layoutReverseList layoutReverseList
) )
type infoStyle int
const (
infoDefault infoStyle = iota
infoInline
infoHidden
)
type previewOpts struct { type previewOpts struct {
command string command string
position windowPosition position windowPosition
@ -177,7 +185,7 @@ type Options struct {
Hscroll bool Hscroll bool
HscrollOff int HscrollOff int
FileWord bool FileWord bool
InlineInfo bool InfoStyle infoStyle
JumpLabels string JumpLabels string
Prompt string Prompt string
Query string Query string
@ -230,7 +238,7 @@ func defaultOptions() *Options {
Hscroll: true, Hscroll: true,
HscrollOff: 10, HscrollOff: 10,
FileWord: false, FileWord: false,
InlineInfo: false, InfoStyle: infoDefault,
JumpLabels: defaultJumpLabels, JumpLabels: defaultJumpLabels,
Prompt: "> ", Prompt: "> ",
Query: "", Query: "",
@ -904,6 +912,20 @@ func parseLayout(str string) layoutType {
return layoutDefault return layoutDefault
} }
func parseInfoStyle(str string) infoStyle {
switch str {
case "default":
return infoDefault
case "inline":
return infoInline
case "hidden":
return infoHidden
default:
errorExit("invalid info style (expected: default / inline / hidden)")
}
return infoDefault
}
func parsePreviewWindow(opts *previewOpts, input string) { func parsePreviewWindow(opts *previewOpts, input string) {
// Default // Default
opts.position = posRight opts.position = posRight
@ -1109,10 +1131,15 @@ func parseOptions(opts *Options, allArgs []string) {
opts.FileWord = true opts.FileWord = true
case "--no-filepath-word": case "--no-filepath-word":
opts.FileWord = false opts.FileWord = false
case "--info":
opts.InfoStyle = parseInfoStyle(
nextString(allArgs, &i, "info style required"))
case "--no-info":
opts.InfoStyle = infoHidden
case "--inline-info": case "--inline-info":
opts.InlineInfo = true opts.InfoStyle = infoInline
case "--no-inline-info": case "--no-inline-info":
opts.InlineInfo = false opts.InfoStyle = infoDefault
case "--jump-labels": case "--jump-labels":
opts.JumpLabels = nextString(allArgs, &i, "label characters required") opts.JumpLabels = nextString(allArgs, &i, "label characters required")
validateJumpLabels = true validateJumpLabels = true
@ -1220,6 +1247,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.MinHeight = atoi(value) opts.MinHeight = atoi(value)
} else if match, value := optString(arg, "--layout="); match { } else if match, value := optString(arg, "--layout="); match {
opts.Layout = parseLayout(value) opts.Layout = parseLayout(value)
} else if match, value := optString(arg, "--info="); match {
opts.InfoStyle = parseInfoStyle(value)
} else if match, value := optString(arg, "--toggle-sort="); match { } else if match, value := optString(arg, "--toggle-sort="); match {
parseToggleSort(opts.Keymap, value) parseToggleSort(opts.Keymap, value)
} else if match, value := optString(arg, "--expect="); match { } else if match, value := optString(arg, "--expect="); match {

View File

@ -60,7 +60,7 @@ var emptyLine = itemLine{}
// Terminal represents terminal input/output // Terminal represents terminal input/output
type Terminal struct { type Terminal struct {
initDelay time.Duration initDelay time.Duration
inlineInfo bool infoStyle infoStyle
prompt string prompt string
promptLen int promptLen int
queryLen [2]int queryLen [2]int
@ -361,7 +361,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) { if previewBox != nil && (opts.Preview.position == posUp || opts.Preview.position == posDown) {
effectiveMinHeight *= 2 effectiveMinHeight *= 2
} }
if opts.InlineInfo { if opts.InfoStyle != infoDefault {
effectiveMinHeight -= 1 effectiveMinHeight -= 1
} }
if opts.Bordered { if opts.Bordered {
@ -380,7 +380,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
} }
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
inlineInfo: opts.InlineInfo, infoStyle: opts.InfoStyle,
queryLen: [2]int{0, 0}, queryLen: [2]int{0, 0},
layout: opts.Layout, layout: opts.Layout,
fullscreen: fullscreen, fullscreen: fullscreen,
@ -438,6 +438,10 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
return &t return &t
} }
func (t *Terminal) noInfoLine() bool {
return t.infoStyle != infoDefault
}
// Input returns current query string // Input returns current query string
func (t *Terminal) Input() []rune { func (t *Terminal) Input() []rune {
t.mutex.Lock() t.mutex.Lock()
@ -672,7 +676,7 @@ func (t *Terminal) move(y int, x int, clear bool) {
y = h - y - 1 y = h - y - 1
case layoutReverseList: case layoutReverseList:
n := 2 + len(t.header) n := 2 + len(t.header)
if t.inlineInfo { if t.noInfoLine() {
n-- n--
} }
if y < n { if y < n {
@ -725,7 +729,17 @@ func (t *Terminal) printPrompt() {
func (t *Terminal) printInfo() { func (t *Terminal) printInfo() {
pos := 0 pos := 0
if t.inlineInfo { switch t.infoStyle {
case infoDefault:
t.move(1, 0, true)
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
t.window.CPrint(tui.ColSpinner, t.strong, _spinner[idx])
}
t.move(1, 2, false)
pos = 2
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
@ -737,15 +751,8 @@ func (t *Terminal) printInfo() {
t.window.CPrint(tui.ColPrompt, t.strong, " < ") t.window.CPrint(tui.ColPrompt, t.strong, " < ")
} }
pos += len(" < ") pos += len(" < ")
} else { case infoHidden:
t.move(1, 0, true) return
if t.reading {
duration := int64(spinnerDuration)
idx := (time.Now().UnixNano() % (duration * int64(len(_spinner)))) / duration
t.window.CPrint(tui.ColSpinner, t.strong, _spinner[idx])
}
t.move(1, 2, false)
pos = 2
} }
found := t.merger.Length() found := t.merger.Length()
@ -787,7 +794,7 @@ func (t *Terminal) printHeader() {
var state *ansiState var state *ansiState
for idx, lineStr := range t.header { for idx, lineStr := range t.header {
line := idx + 2 line := idx + 2
if t.inlineInfo { if t.noInfoLine() {
line-- line--
} }
if line >= max { if line >= max {
@ -816,7 +823,7 @@ func (t *Terminal) printList() {
i = maxy - 1 - j i = maxy - 1 - j
} }
line := i + 2 + len(t.header) line := i + 2 + len(t.header)
if t.inlineInfo { if t.noInfoLine() {
line-- line--
} }
if i < count { if i < count {
@ -1590,9 +1597,6 @@ func (t *Terminal) Loop() {
} }
exit := func(getCode func() int) { exit := func(getCode func() int) {
if !t.cleanExit && t.fullscreen && t.inlineInfo {
t.placeCursor()
}
t.tui.Close() t.tui.Close()
code := getCode() code := getCode()
if code <= exitNoMatch && t.history != nil { if code <= exitNoMatch && t.history != nil {
@ -1613,7 +1617,7 @@ func (t *Terminal) Loop() {
switch req { switch req {
case reqPrompt: case reqPrompt:
t.printPrompt() t.printPrompt()
if t.inlineInfo { if t.noInfoLine() {
t.printInfo() t.printInfo()
} }
case reqInfo: case reqInfo:
@ -1995,7 +1999,7 @@ func (t *Terminal) Loop() {
my -= t.window.Top() my -= t.window.Top()
mx = util.Constrain(mx-t.promptLen, 0, len(t.input)) mx = util.Constrain(mx-t.promptLen, 0, len(t.input))
min := 2 + len(t.header) min := 2 + len(t.header)
if t.inlineInfo { if t.noInfoLine() {
min-- min--
} }
h := t.window.Height() h := t.window.Height()
@ -2151,7 +2155,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.header) max := t.window.Height() - 2 - len(t.header)
if t.inlineInfo { if t.noInfoLine() {
max++ max++
} }
return util.Max(max, 0) return util.Max(max, 0)

View File

@ -1516,6 +1516,11 @@ class TestGoFZF < TestBase
tmux.until { |lines| lines[-1] == prompt } tmux.until { |lines| lines[-1] == prompt }
end end
def test_info_hidden
tmux.send_keys 'seq 10 | fzf --info=hidden', :Enter
tmux.until { |lines| lines[-2] == '> 1' }
end
def test_change_top def test_change_top
tmux.send_keys %(seq 1000 | #{FZF} --bind change:top), :Enter tmux.send_keys %(seq 1000 | #{FZF} --bind change:top), :Enter
tmux.until { |lines| lines.match_count == 1000 } tmux.until { |lines| lines.match_count == 1000 }