mirror of https://github.com/Llewellynvdm/fzf.git
parent
ec20dfe312
commit
5cd6f1d064
|
@ -12,6 +12,14 @@ CHANGELOG
|
|||
# Send actions to the server
|
||||
curl -XPOST localhost:6266 -d 'reload(seq 100)+change-prompt(hundred> )'
|
||||
```
|
||||
- Added scrollbar on the main search window
|
||||
```sh
|
||||
# Hide scrollbar
|
||||
fzf --no-scrollbar
|
||||
|
||||
# Customize scrollbar
|
||||
fzf --scrollbar ┆ --color scrollbar:blue
|
||||
```
|
||||
- New event
|
||||
- Added `load` event that is triggered when the input stream is complete
|
||||
and the initial processing of the list is complete.
|
||||
|
|
|
@ -356,6 +356,15 @@ ANSI color codes are supported.
|
|||
Do not display horizontal separator on the info line. A synonym for
|
||||
\fB--separator=''\fB
|
||||
|
||||
.TP
|
||||
.BI "--scrollbar=" "CHAR"
|
||||
Use the given character to render scrollbar. (default: '▏' or ':' depending on
|
||||
\fB--no-unicode\fR).
|
||||
|
||||
.TP
|
||||
.B "--no-scrollbar"
|
||||
Do not display scrollbar. A synonym for \fB--scrollbar=''\fB
|
||||
|
||||
.TP
|
||||
.BI "--prompt=" "STR"
|
||||
Input prompt (default: '> ')
|
||||
|
|
|
@ -73,6 +73,8 @@ const usage = `usage: fzf [options]
|
|||
--info=STYLE Finder info style [default|inline|hidden]
|
||||
--separator=STR String to form horizontal separator on info line
|
||||
--no-separator Hide info line separator
|
||||
--scrollbar[=CHAR] Scrollbar character
|
||||
--no-scrollbar Hide scrollbar
|
||||
--prompt=STR Input prompt (default: '> ')
|
||||
--pointer=STR Pointer to the current line (default: '>')
|
||||
--marker=STR Multi-select marker (default: '>')
|
||||
|
@ -290,6 +292,7 @@ type Options struct {
|
|||
HeaderLines int
|
||||
HeaderFirst bool
|
||||
Ellipsis string
|
||||
Scrollbar *string
|
||||
Margin [4]sizeSpec
|
||||
Padding [4]sizeSpec
|
||||
BorderShape tui.BorderShape
|
||||
|
@ -359,6 +362,7 @@ func defaultOptions() *Options {
|
|||
HeaderLines: 0,
|
||||
HeaderFirst: false,
|
||||
Ellipsis: "..",
|
||||
Scrollbar: nil,
|
||||
Margin: defaultMargin(),
|
||||
Padding: defaultMargin(),
|
||||
Unicode: true,
|
||||
|
@ -847,6 +851,8 @@ func parseTheme(defaultTheme *tui.ColorTheme, str string) *tui.ColorTheme {
|
|||
mergeAttr(&theme.Border)
|
||||
case "separator":
|
||||
mergeAttr(&theme.Separator)
|
||||
case "scrollbar":
|
||||
mergeAttr(&theme.Scrollbar)
|
||||
case "label":
|
||||
mergeAttr(&theme.BorderLabel)
|
||||
case "preview-label":
|
||||
|
@ -1570,6 +1576,16 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||
case "--no-separator":
|
||||
nosep := ""
|
||||
opts.Separator = &nosep
|
||||
case "--scrollbar":
|
||||
given, bar := optionalNextString(allArgs, &i)
|
||||
if given {
|
||||
opts.Scrollbar = &bar
|
||||
} else {
|
||||
opts.Scrollbar = nil
|
||||
}
|
||||
case "--no-scrollbar":
|
||||
noBar := ""
|
||||
opts.Scrollbar = &noBar
|
||||
case "--jump-labels":
|
||||
opts.JumpLabels = nextString(allArgs, &i, "label characters required")
|
||||
validateJumpLabels = true
|
||||
|
@ -1739,6 +1755,8 @@ func parseOptions(opts *Options, allArgs []string) {
|
|||
opts.InfoStyle = parseInfoStyle(value)
|
||||
} else if match, value := optString(arg, "--separator="); match {
|
||||
opts.Separator = &value
|
||||
} else if match, value := optString(arg, "--scrollbar="); match {
|
||||
opts.Scrollbar = &value
|
||||
} else if match, value := optString(arg, "--toggle-sort="); match {
|
||||
parseToggleSort(opts.Keymap, value)
|
||||
} else if match, value := optString(arg, "--expect="); match {
|
||||
|
@ -1845,6 +1863,11 @@ func postProcessOptions(opts *Options) {
|
|||
if !opts.Version && !tui.IsLightRendererSupported() && opts.Height.size > 0 {
|
||||
errorExit("--height option is currently not supported on this platform")
|
||||
}
|
||||
|
||||
if opts.Scrollbar != nil && runewidth.StringWidth(*opts.Scrollbar) > 1 {
|
||||
errorExit("scrollbar display width should be 1")
|
||||
}
|
||||
|
||||
// Default actions for CTRL-N / CTRL-P when --history is set
|
||||
if opts.History != nil {
|
||||
if _, prs := opts.Keymap[tui.CtrlP.AsEvent()]; !prs {
|
||||
|
|
|
@ -97,6 +97,7 @@ type itemLine struct {
|
|||
label string
|
||||
queryLen int
|
||||
width int
|
||||
bar bool
|
||||
result Result
|
||||
}
|
||||
|
||||
|
@ -161,6 +162,7 @@ type Terminal struct {
|
|||
header []string
|
||||
header0 []string
|
||||
ellipsis string
|
||||
scrollbar string
|
||||
ansi bool
|
||||
tabstop int
|
||||
margin [4]sizeSpec
|
||||
|
@ -632,6 +634,15 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||
}
|
||||
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
||||
}
|
||||
if opts.Scrollbar == nil {
|
||||
if t.unicode {
|
||||
t.scrollbar = "▏" // Left one eighth block
|
||||
} else {
|
||||
t.scrollbar = "|"
|
||||
}
|
||||
} else {
|
||||
t.scrollbar = *opts.Scrollbar
|
||||
}
|
||||
|
||||
_, t.hasLoadActions = t.keymap[tui.Load.AsEvent()]
|
||||
|
||||
|
@ -763,6 +774,27 @@ func (t *Terminal) noInfoLine() bool {
|
|||
return t.infoStyle != infoDefault
|
||||
}
|
||||
|
||||
func (t *Terminal) getScrollbar() (int, int) {
|
||||
total := t.merger.Length()
|
||||
if total == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
maxItems := t.maxItems()
|
||||
barLength := util.Max(1, maxItems*maxItems/total)
|
||||
if total <= maxItems {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
var barStart int
|
||||
if total == maxItems {
|
||||
barStart = 0
|
||||
} else {
|
||||
barStart = (maxItems - barLength) * t.offset / (total - maxItems)
|
||||
}
|
||||
return barLength, barStart
|
||||
}
|
||||
|
||||
// Input returns current query string
|
||||
func (t *Terminal) Input() (bool, []rune) {
|
||||
t.mutex.Lock()
|
||||
|
@ -1349,6 +1381,7 @@ func (t *Terminal) printHeader() {
|
|||
|
||||
func (t *Terminal) printList() {
|
||||
t.constrain()
|
||||
barLength, barStart := t.getScrollbar()
|
||||
|
||||
maxy := t.maxItems()
|
||||
count := t.merger.Length() - t.offset
|
||||
|
@ -1362,7 +1395,7 @@ func (t *Terminal) printList() {
|
|||
line--
|
||||
}
|
||||
if i < count {
|
||||
t.printItem(t.merger.Get(i+t.offset), line, i, i == t.cy-t.offset)
|
||||
t.printItem(t.merger.Get(i+t.offset), line, i, i == t.cy-t.offset, i >= barStart && i < barStart+barLength)
|
||||
} else if t.prevLines[i] != emptyLine {
|
||||
t.prevLines[i] = emptyLine
|
||||
t.move(line, 0, true)
|
||||
|
@ -1370,7 +1403,7 @@ func (t *Terminal) printList() {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
||||
func (t *Terminal) printItem(result Result, line int, i int, current bool, bar bool) {
|
||||
item := result.item
|
||||
_, selected := t.selected[item.Index()]
|
||||
label := ""
|
||||
|
@ -1386,7 +1419,7 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
|||
|
||||
// Avoid unnecessary redraw
|
||||
newLine := itemLine{current: current, selected: selected, label: label,
|
||||
result: result, queryLen: len(t.input), width: 0}
|
||||
result: result, queryLen: len(t.input), width: 0, bar: bar}
|
||||
prevLine := t.prevLines[i]
|
||||
if prevLine.current == newLine.current &&
|
||||
prevLine.selected == newLine.selected &&
|
||||
|
@ -1426,6 +1459,12 @@ func (t *Terminal) printItem(result Result, line int, i int, current bool) {
|
|||
if fillSpaces > 0 {
|
||||
t.window.Print(strings.Repeat(" ", fillSpaces))
|
||||
}
|
||||
if len(t.scrollbar) > 0 && bar != prevLine.bar {
|
||||
t.move(line, t.window.Width()-1, true)
|
||||
if bar {
|
||||
t.window.CPrint(tui.ColScrollbar, t.scrollbar)
|
||||
}
|
||||
}
|
||||
t.prevLines[i] = newLine
|
||||
}
|
||||
|
||||
|
@ -2999,8 +3038,9 @@ func (t *Terminal) Loop() {
|
|||
}
|
||||
} else if t.window.Enclose(my, mx) {
|
||||
mx -= t.window.Left()
|
||||
my -= t.window.Top()
|
||||
bar := mx == t.window.Width()-1
|
||||
mx = util.Constrain(mx-t.promptLen, 0, len(t.input))
|
||||
my -= t.window.Top()
|
||||
min := 2 + len(t.header)
|
||||
if t.noInfoLine() {
|
||||
min--
|
||||
|
@ -3016,7 +3056,20 @@ func (t *Terminal) Loop() {
|
|||
my = h - my - 1
|
||||
}
|
||||
}
|
||||
if me.Double {
|
||||
if bar && my >= min {
|
||||
barLength, barStart := t.getScrollbar()
|
||||
if barLength > 0 {
|
||||
maxItems := t.maxItems()
|
||||
if newBarStart := util.Constrain(my-min-barLength/2, 0, maxItems-barLength); newBarStart != barStart {
|
||||
total := t.merger.Length()
|
||||
prevOffset := t.offset
|
||||
// barStart = (maxItems - barLength) * t.offset / (total - maxItems)
|
||||
t.offset = int(math.Ceil(float64(newBarStart) * float64(total-maxItems) / float64(maxItems-barLength)))
|
||||
t.cy = t.offset + t.cy - prevOffset
|
||||
req(reqList)
|
||||
}
|
||||
}
|
||||
} else if me.Double {
|
||||
// Double-click
|
||||
if my >= min {
|
||||
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
|
||||
|
|
|
@ -176,6 +176,7 @@ func (r *LightRenderer) Init() {
|
|||
|
||||
if r.mouse {
|
||||
r.csi("?1000h")
|
||||
r.csi("?1002h")
|
||||
r.csi("?1006h")
|
||||
}
|
||||
r.csi(fmt.Sprintf("%dA", r.MaxY()-1))
|
||||
|
@ -569,12 +570,14 @@ func (r *LightRenderer) mouseSequence(sz *int) Event {
|
|||
// ctrl := t & 0b1000
|
||||
mod := t&0b1100 > 0
|
||||
|
||||
drag := t&0b100000 > 0
|
||||
|
||||
if scroll != 0 {
|
||||
return Event{Mouse, 0, &MouseEvent{y, x, scroll, false, false, false, mod}}
|
||||
}
|
||||
|
||||
double := false
|
||||
if down {
|
||||
if down && !drag {
|
||||
now := time.Now()
|
||||
if !left { // Right double click is not allowed
|
||||
r.clickY = []int{}
|
||||
|
|
|
@ -269,6 +269,7 @@ type ColorTheme struct {
|
|||
Selected ColorAttr
|
||||
Header ColorAttr
|
||||
Separator ColorAttr
|
||||
Scrollbar ColorAttr
|
||||
Border ColorAttr
|
||||
BorderLabel ColorAttr
|
||||
PreviewLabel ColorAttr
|
||||
|
@ -466,6 +467,7 @@ var (
|
|||
ColInfo ColorPair
|
||||
ColHeader ColorPair
|
||||
ColSeparator ColorPair
|
||||
ColScrollbar ColorPair
|
||||
ColBorder ColorPair
|
||||
ColPreview ColorPair
|
||||
ColPreviewBorder ColorPair
|
||||
|
@ -490,6 +492,7 @@ func EmptyTheme() *ColorTheme {
|
|||
Selected: ColorAttr{colUndefined, AttrUndefined},
|
||||
Header: ColorAttr{colUndefined, AttrUndefined},
|
||||
Separator: ColorAttr{colUndefined, AttrUndefined},
|
||||
Scrollbar: ColorAttr{colUndefined, AttrUndefined},
|
||||
Border: ColorAttr{colUndefined, AttrUndefined},
|
||||
BorderLabel: ColorAttr{colUndefined, AttrUndefined},
|
||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||
|
@ -517,6 +520,7 @@ func NoColorTheme() *ColorTheme {
|
|||
Selected: ColorAttr{colDefault, AttrRegular},
|
||||
Header: ColorAttr{colDefault, AttrRegular},
|
||||
Separator: ColorAttr{colDefault, AttrRegular},
|
||||
Scrollbar: ColorAttr{colDefault, AttrRegular},
|
||||
Border: ColorAttr{colDefault, AttrRegular},
|
||||
BorderLabel: ColorAttr{colDefault, AttrRegular},
|
||||
Disabled: ColorAttr{colDefault, AttrRegular},
|
||||
|
@ -549,6 +553,7 @@ func init() {
|
|||
Selected: ColorAttr{colMagenta, AttrUndefined},
|
||||
Header: ColorAttr{colCyan, AttrUndefined},
|
||||
Separator: ColorAttr{colBlack, AttrUndefined},
|
||||
Scrollbar: ColorAttr{colBlack, AttrUndefined},
|
||||
Border: ColorAttr{colBlack, AttrUndefined},
|
||||
BorderLabel: ColorAttr{colWhite, AttrUndefined},
|
||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||
|
@ -573,6 +578,7 @@ func init() {
|
|||
Selected: ColorAttr{168, AttrUndefined},
|
||||
Header: ColorAttr{109, AttrUndefined},
|
||||
Separator: ColorAttr{59, AttrUndefined},
|
||||
Scrollbar: ColorAttr{59, AttrUndefined},
|
||||
Border: ColorAttr{59, AttrUndefined},
|
||||
BorderLabel: ColorAttr{145, AttrUndefined},
|
||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||
|
@ -597,6 +603,7 @@ func init() {
|
|||
Selected: ColorAttr{168, AttrUndefined},
|
||||
Header: ColorAttr{31, AttrUndefined},
|
||||
Separator: ColorAttr{145, AttrUndefined},
|
||||
Scrollbar: ColorAttr{145, AttrUndefined},
|
||||
Border: ColorAttr{145, AttrUndefined},
|
||||
BorderLabel: ColorAttr{59, AttrUndefined},
|
||||
Disabled: ColorAttr{colUndefined, AttrUndefined},
|
||||
|
@ -645,6 +652,7 @@ func initTheme(theme *ColorTheme, baseTheme *ColorTheme, forceBlack bool) {
|
|||
theme.PreviewFg = o(theme.Fg, theme.PreviewFg)
|
||||
theme.PreviewBg = o(theme.Bg, theme.PreviewBg)
|
||||
theme.PreviewLabel = o(theme.BorderLabel, theme.PreviewLabel)
|
||||
theme.Scrollbar = o(theme.Separator, theme.Scrollbar)
|
||||
|
||||
initPalette(theme)
|
||||
}
|
||||
|
@ -677,6 +685,7 @@ func initPalette(theme *ColorTheme) {
|
|||
ColInfo = pair(theme.Info, theme.Bg)
|
||||
ColHeader = pair(theme.Header, theme.Bg)
|
||||
ColSeparator = pair(theme.Separator, theme.Bg)
|
||||
ColScrollbar = pair(theme.Scrollbar, theme.Bg)
|
||||
ColBorder = pair(theme.Border, theme.Bg)
|
||||
ColBorderLabel = pair(theme.BorderLabel, theme.Bg)
|
||||
ColPreviewLabel = pair(theme.PreviewLabel, theme.PreviewBg)
|
||||
|
|
|
@ -23,7 +23,7 @@ DEFAULT_TIMEOUT = 10
|
|||
FILE = File.expand_path(__FILE__)
|
||||
BASE = File.expand_path('..', __dir__)
|
||||
Dir.chdir(BASE)
|
||||
FZF = "FZF_DEFAULT_OPTS= FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf"
|
||||
FZF = "FZF_DEFAULT_OPTS=--no-scrollbar FZF_DEFAULT_COMMAND= #{BASE}/bin/fzf"
|
||||
|
||||
def wait
|
||||
since = Time.now
|
||||
|
@ -65,7 +65,7 @@ class Shell
|
|||
end
|
||||
|
||||
def fish
|
||||
UNSETS.map { |v| v + '= ' }.join + 'fish'
|
||||
UNSETS.map { |v| v + '= ' }.join + ' FZF_DEFAULT_OPTS=--no-scrollbar fish'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -2908,7 +2908,7 @@ class TestFish < TestBase
|
|||
end
|
||||
|
||||
def new_shell
|
||||
tmux.send_keys 'env FZF_TMUX=1 fish', :Enter
|
||||
tmux.send_keys 'env FZF_TMUX=1 FZF_DEFAULT_OPTS=--no-scrollbar fish', :Enter
|
||||
tmux.send_keys 'function fish_prompt; end; clear', :Enter
|
||||
tmux.until { |lines| assert_empty lines }
|
||||
end
|
||||
|
@ -2927,6 +2927,8 @@ unset <%= UNSETS.join(' ') %>
|
|||
unset $(env | sed -n /^_fzf_orig/s/=.*//p)
|
||||
unset $(declare -F | sed -n "/_fzf/s/.*-f //p")
|
||||
|
||||
export FZF_DEFAULT_OPTS=--no-scrollbar
|
||||
|
||||
# Setup fzf
|
||||
# ---------
|
||||
if [[ ! "$PATH" == *<%= BASE %>/bin* ]]; then
|
||||
|
|
Loading…
Reference in New Issue