mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2025-01-23 15:18:29 +00:00
Better support for Windows terminals
* Default border style on Windows is changed to `sharp` because some Windows terminals are not capable of displaying `rounded` border characters correctly. * If your terminal emulator renders each box-drawing character with 2 columns, set `RUNEWIDTH_EASTASIAN` environment variable to `1`.
This commit is contained in:
parent
1c83b39691
commit
0c5956c43c
@ -88,6 +88,10 @@ CHANGELOG
|
|||||||
# a will put 'alpha' on the prompt, ctrl-b will put 'bravo'
|
# a will put 'alpha' on the prompt, ctrl-b will put 'bravo'
|
||||||
fzf --bind 'a:put+put(lpha),ctrl-b:put(bravo)'
|
fzf --bind 'a:put+put(lpha),ctrl-b:put(bravo)'
|
||||||
```
|
```
|
||||||
|
- Added color name `preview-label` for `--preview-label` (defaults to `label`
|
||||||
|
for `--border-label`)
|
||||||
|
- Better support for (Windows) terminals where each box-drawing character
|
||||||
|
takes 2 columns. Set `RUNEWIDTH_EASTASIAN` environment variable to `1`.
|
||||||
- Behavior changes
|
- Behavior changes
|
||||||
- fzf will always execute the preview command if the command template
|
- fzf will always execute the preview command if the command template
|
||||||
contains `{q}` even when it's empty. If you prefer the old behavior,
|
contains `{q}` even when it's empty. If you prefer the old behavior,
|
||||||
@ -114,8 +118,9 @@ CHANGELOG
|
|||||||
when the user manually scrolls the window, the following stops. With
|
when the user manually scrolls the window, the following stops. With
|
||||||
this version, fzf will resume following if the user scrolls the window
|
this version, fzf will resume following if the user scrolls the window
|
||||||
to the bottom.
|
to the bottom.
|
||||||
- Added color name `preview-label` for `--preview-label` (defaults to `label`
|
- Default border style on Windows is changed to `sharp` because some
|
||||||
for `--border-label`)
|
Windows terminals are not capable of displaying `rounded` border
|
||||||
|
characters correctly.
|
||||||
- Minor bug fixes and improvements
|
- Minor bug fixes and improvements
|
||||||
|
|
||||||
0.35.1
|
0.35.1
|
||||||
|
@ -230,6 +230,10 @@ Draw border around the finder
|
|||||||
.BR none
|
.BR none
|
||||||
.br
|
.br
|
||||||
|
|
||||||
|
If you use a terminal emulator where box-drawing characters take 2 columns,
|
||||||
|
try setting \fBRUNEWIDTH_EASTASIAN\fR to \fB1\fR. If the border is still
|
||||||
|
not properly rendered, set \fB--no-unicode\fR.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "--border-label" [=LABEL]
|
.BI "--border-label" [=LABEL]
|
||||||
Label to print on the horizontal border line. Should be used with one of the
|
Label to print on the horizontal border line. Should be used with one of the
|
||||||
@ -568,9 +572,9 @@ Label to print on the horizontal border line of the preview window.
|
|||||||
Should be used with one of the following \fB--preview-window\fR options.
|
Should be used with one of the following \fB--preview-window\fR options.
|
||||||
|
|
||||||
.br
|
.br
|
||||||
.B * border-rounded (default)
|
.B * border-rounded (default on non-Windows platforms)
|
||||||
.br
|
.br
|
||||||
.B * border-sharp
|
.B * border-sharp (default on Windows)
|
||||||
.br
|
.br
|
||||||
.B * border-bold
|
.B * border-bold
|
||||||
.br
|
.br
|
||||||
|
@ -314,7 +314,7 @@ type Options struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func defaultPreviewOpts(command string) previewOpts {
|
func defaultPreviewOpts(command string) previewOpts {
|
||||||
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.BorderRounded, 0, 0, nil}
|
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.DefaultBorderShape, 0, 0, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultOptions() *Options {
|
func defaultOptions() *Options {
|
||||||
@ -543,7 +543,7 @@ func parseBorder(str string, optional bool) tui.BorderShape {
|
|||||||
return tui.BorderNone
|
return tui.BorderNone
|
||||||
default:
|
default:
|
||||||
if optional && str == "" {
|
if optional && str == "" {
|
||||||
return tui.BorderRounded
|
return tui.DefaultBorderShape
|
||||||
}
|
}
|
||||||
errorExit("invalid border style (expected: rounded|sharp|bold|double|horizontal|vertical|top|bottom|left|right|none)")
|
errorExit("invalid border style (expected: rounded|sharp|bold|double|horizontal|vertical|top|bottom|left|right|none)")
|
||||||
}
|
}
|
||||||
|
@ -210,6 +210,7 @@ type Terminal struct {
|
|||||||
window tui.Window
|
window tui.Window
|
||||||
pborder tui.Window
|
pborder tui.Window
|
||||||
pwindow tui.Window
|
pwindow tui.Window
|
||||||
|
borderWidth int
|
||||||
count int
|
count int
|
||||||
progress int
|
progress int
|
||||||
hasLoadActions bool
|
hasLoadActions bool
|
||||||
@ -604,6 +605,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
unicode: opts.Unicode,
|
unicode: opts.Unicode,
|
||||||
listenPort: opts.ListenPort,
|
listenPort: opts.ListenPort,
|
||||||
borderShape: opts.BorderShape,
|
borderShape: opts.BorderShape,
|
||||||
|
borderWidth: 1,
|
||||||
borderLabel: nil,
|
borderLabel: nil,
|
||||||
borderLabelOpts: opts.BorderLabel,
|
borderLabelOpts: opts.BorderLabel,
|
||||||
previewLabel: nil,
|
previewLabel: nil,
|
||||||
@ -666,8 +668,11 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
|
|||||||
}
|
}
|
||||||
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
|
||||||
}
|
}
|
||||||
|
if t.unicode {
|
||||||
|
t.borderWidth = runewidth.RuneWidth('│')
|
||||||
|
}
|
||||||
if opts.Scrollbar == nil {
|
if opts.Scrollbar == nil {
|
||||||
if t.unicode {
|
if t.unicode && t.borderWidth == 1 {
|
||||||
t.scrollbar = "│"
|
t.scrollbar = "│"
|
||||||
} else {
|
} else {
|
||||||
t.scrollbar = "|"
|
t.scrollbar = "|"
|
||||||
@ -968,20 +973,21 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
|||||||
paddingInt[idx] = sizeSpecToInt(idx, sizeSpec)
|
paddingInt[idx] = sizeSpecToInt(idx, sizeSpec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bw := t.borderWidth
|
||||||
extraMargin := [4]int{} // TRBL
|
extraMargin := [4]int{} // TRBL
|
||||||
for idx, sizeSpec := range t.margin {
|
for idx, sizeSpec := range t.margin {
|
||||||
switch t.borderShape {
|
switch t.borderShape {
|
||||||
case tui.BorderHorizontal:
|
case tui.BorderHorizontal:
|
||||||
extraMargin[idx] += 1 - idx%2
|
extraMargin[idx] += 1 - idx%2
|
||||||
case tui.BorderVertical:
|
case tui.BorderVertical:
|
||||||
extraMargin[idx] += 2 * (idx % 2)
|
extraMargin[idx] += (1 + bw) * (idx % 2)
|
||||||
case tui.BorderTop:
|
case tui.BorderTop:
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
extraMargin[idx]++
|
extraMargin[idx]++
|
||||||
}
|
}
|
||||||
case tui.BorderRight:
|
case tui.BorderRight:
|
||||||
if idx == 1 {
|
if idx == 1 {
|
||||||
extraMargin[idx] += 2
|
extraMargin[idx] += 1 + bw
|
||||||
}
|
}
|
||||||
case tui.BorderBottom:
|
case tui.BorderBottom:
|
||||||
if idx == 2 {
|
if idx == 2 {
|
||||||
@ -989,10 +995,10 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
|
|||||||
}
|
}
|
||||||
case tui.BorderLeft:
|
case tui.BorderLeft:
|
||||||
if idx == 3 {
|
if idx == 3 {
|
||||||
extraMargin[idx] += 2
|
extraMargin[idx] += 1 + bw
|
||||||
}
|
}
|
||||||
case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
case tui.BorderRounded, tui.BorderSharp, tui.BorderBold, tui.BorderDouble:
|
||||||
extraMargin[idx] += 1 + idx%2
|
extraMargin[idx] += 1 + bw*(idx%2)
|
||||||
}
|
}
|
||||||
marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx]
|
marginInt[idx] = sizeSpecToInt(idx, sizeSpec) + extraMargin[idx]
|
||||||
}
|
}
|
||||||
@ -1106,6 +1112,7 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
|
hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
|
||||||
|
bw := t.borderWidth
|
||||||
createPreviewWindow := func(y int, x int, w int, h int) {
|
createPreviewWindow := func(y int, x int, w int, h int) {
|
||||||
pwidth := w
|
pwidth := w
|
||||||
pheight := h
|
pheight := h
|
||||||
@ -1118,15 +1125,15 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
|
|||||||
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
|
||||||
switch previewOpts.border {
|
switch previewOpts.border {
|
||||||
case tui.BorderSharp, tui.BorderRounded, tui.BorderBold, tui.BorderDouble:
|
case tui.BorderSharp, tui.BorderRounded, tui.BorderBold, tui.BorderDouble:
|
||||||
pwidth -= 4
|
pwidth -= (1 + bw) * 2
|
||||||
pheight -= 2
|
pheight -= 2
|
||||||
x += 2
|
x += 1 + bw
|
||||||
y += 1
|
y += 1
|
||||||
case tui.BorderLeft:
|
case tui.BorderLeft:
|
||||||
pwidth -= 2
|
pwidth -= 1 + bw
|
||||||
x += 2
|
x += 1 + bw
|
||||||
case tui.BorderRight:
|
case tui.BorderRight:
|
||||||
pwidth -= 2
|
pwidth -= 1 + bw
|
||||||
case tui.BorderTop:
|
case tui.BorderTop:
|
||||||
pheight -= 1
|
pheight -= 1
|
||||||
y += 1
|
y += 1
|
||||||
@ -1136,8 +1143,8 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
|
|||||||
pheight -= 2
|
pheight -= 2
|
||||||
y += 1
|
y += 1
|
||||||
case tui.BorderVertical:
|
case tui.BorderVertical:
|
||||||
pwidth -= 4
|
pwidth -= (1 + bw) * 2
|
||||||
x += 2
|
x += 1 + bw
|
||||||
}
|
}
|
||||||
if len(t.scrollbar) > 0 && !previewOpts.border.HasRight() {
|
if len(t.scrollbar) > 0 && !previewOpts.border.HasRight() {
|
||||||
// Need a column to show scrollbar
|
// Need a column to show scrollbar
|
||||||
@ -1832,7 +1839,7 @@ func (t *Terminal) renderPreviewScrollbar(yoff int, barLength int, barStart int)
|
|||||||
if len(t.previewer.bar) != height {
|
if len(t.previewer.bar) != height {
|
||||||
t.previewer.bar = make([]bool, height)
|
t.previewer.bar = make([]bool, height)
|
||||||
}
|
}
|
||||||
xshift := -2
|
xshift := -1 - t.borderWidth
|
||||||
if !t.previewOpts.border.HasRight() {
|
if !t.previewOpts.border.HasRight() {
|
||||||
xshift = -1
|
xshift = -1
|
||||||
}
|
}
|
||||||
@ -1846,7 +1853,7 @@ func (t *Terminal) renderPreviewScrollbar(yoff int, barLength int, barStart int)
|
|||||||
|
|
||||||
// Avoid unnecessary redraws
|
// Avoid unnecessary redraws
|
||||||
bar := i >= yoff+barStart && i < yoff+barStart+barLength
|
bar := i >= yoff+barStart && i < yoff+barStart+barLength
|
||||||
if bar == t.previewer.bar[i] {
|
if bar == t.previewer.bar[i] && !t.tui.NeedScrollbarRedraw() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ func HasFullscreenRenderer() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DefaultBorderShape BorderShape = BorderRounded
|
||||||
|
|
||||||
func (a Attr) Merge(b Attr) Attr {
|
func (a Attr) Merge(b Attr) Attr {
|
||||||
return a | b
|
return a | b
|
||||||
}
|
}
|
||||||
@ -32,6 +34,7 @@ func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
|
|||||||
func (r *FullscreenRenderer) Pause(bool) {}
|
func (r *FullscreenRenderer) Pause(bool) {}
|
||||||
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
func (r *FullscreenRenderer) Resume(bool, bool) {}
|
||||||
func (r *FullscreenRenderer) Clear() {}
|
func (r *FullscreenRenderer) Clear() {}
|
||||||
|
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
|
||||||
func (r *FullscreenRenderer) Refresh() {}
|
func (r *FullscreenRenderer) Refresh() {}
|
||||||
func (r *FullscreenRenderer) Close() {}
|
func (r *FullscreenRenderer) Close() {}
|
||||||
|
|
||||||
|
@ -651,6 +651,10 @@ func (r *LightRenderer) Clear() {
|
|||||||
r.flush()
|
r.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *LightRenderer) NeedScrollbarRedraw() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (r *LightRenderer) RefreshWindows(windows []Window) {
|
func (r *LightRenderer) RefreshWindows(windows []Window) {
|
||||||
r.flush()
|
r.flush()
|
||||||
}
|
}
|
||||||
@ -743,13 +747,14 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
|
hw := runewidth.RuneWidth(w.border.horizontal)
|
||||||
if top {
|
if top {
|
||||||
w.Move(0, 0)
|
w.Move(0, 0)
|
||||||
w.CPrint(color, repeat(w.border.horizontal, w.width))
|
w.CPrint(color, repeat(w.border.horizontal, w.width/hw))
|
||||||
}
|
}
|
||||||
if bottom {
|
if bottom {
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(color, repeat(w.border.horizontal, w.width))
|
w.CPrint(color, repeat(w.border.horizontal, w.width/hw))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,15 +785,19 @@ func (w *LightWindow) drawBorderAround() {
|
|||||||
if w.preview {
|
if w.preview {
|
||||||
color = ColPreviewBorder
|
color = ColPreviewBorder
|
||||||
}
|
}
|
||||||
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight))
|
hw := runewidth.RuneWidth(w.border.horizontal)
|
||||||
|
vw := runewidth.RuneWidth(w.border.vertical)
|
||||||
|
tcw := runewidth.RuneWidth(w.border.topLeft) + runewidth.RuneWidth(w.border.topRight)
|
||||||
|
bcw := runewidth.RuneWidth(w.border.bottomLeft) + runewidth.RuneWidth(w.border.bottomRight)
|
||||||
|
w.CPrint(color, string(w.border.topLeft)+repeat(w.border.horizontal, (w.width-tcw)/hw)+string(w.border.topRight))
|
||||||
for y := 1; y < w.height-1; y++ {
|
for y := 1; y < w.height-1; y++ {
|
||||||
w.Move(y, 0)
|
w.Move(y, 0)
|
||||||
w.CPrint(color, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
w.CPrint(color, repeat(' ', w.width-2))
|
w.CPrint(color, repeat(' ', w.width-vw*2))
|
||||||
w.CPrint(color, string(w.border.vertical))
|
w.CPrint(color, string(w.border.vertical))
|
||||||
}
|
}
|
||||||
w.Move(w.height-1, 0)
|
w.Move(w.height-1, 0)
|
||||||
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
|
w.CPrint(color, string(w.border.bottomLeft)+repeat(w.border.horizontal, (w.width-bcw)/hw)+string(w.border.bottomRight))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *LightWindow) csi(code string) {
|
func (w *LightWindow) csi(code string) {
|
||||||
|
@ -17,6 +17,8 @@ func HasFullscreenRenderer() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DefaultBorderShape BorderShape = BorderSharp
|
||||||
|
|
||||||
func asTcellColor(color Color) tcell.Color {
|
func asTcellColor(color Color) tcell.Color {
|
||||||
if color == colDefault {
|
if color == colDefault {
|
||||||
return tcell.ColorDefault
|
return tcell.ColorDefault
|
||||||
@ -187,6 +189,10 @@ func (r *FullscreenRenderer) Clear() {
|
|||||||
_screen.Clear()
|
_screen.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *FullscreenRenderer) NeedScrollbarRedraw() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (r *FullscreenRenderer) Refresh() {
|
func (r *FullscreenRenderer) Refresh() {
|
||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
@ -686,15 +692,16 @@ func (w *TcellWindow) drawBorder() {
|
|||||||
style = w.normal.style()
|
style = w.normal.style()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hw := runewidth.RuneWidth(w.borderStyle.horizontal)
|
||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderTop:
|
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderTop:
|
||||||
for x := left; x < right; x++ {
|
for x := left; x <= right-hw; x += hw {
|
||||||
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
_screen.SetContent(x, top, w.borderStyle.horizontal, nil, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderBottom:
|
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderHorizontal, BorderBottom:
|
||||||
for x := left; x < right; x++ {
|
for x := left; x <= right-hw; x += hw {
|
||||||
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -707,14 +714,14 @@ func (w *TcellWindow) drawBorder() {
|
|||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
|
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
|
||||||
for y := top; y < bot; y++ {
|
for y := top; y < bot; y++ {
|
||||||
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)
|
_screen.SetContent(right-hw, y, w.borderStyle.vertical, nil, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch shape {
|
switch shape {
|
||||||
case BorderRounded, BorderSharp, BorderBold, BorderDouble:
|
case BorderRounded, BorderSharp, BorderBold, BorderDouble:
|
||||||
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
_screen.SetContent(left, top, w.borderStyle.topLeft, nil, style)
|
||||||
_screen.SetContent(right-1, top, w.borderStyle.topRight, nil, style)
|
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.topRight), top, w.borderStyle.topRight, nil, style)
|
||||||
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
_screen.SetContent(left, bot-1, w.borderStyle.bottomLeft, nil, style)
|
||||||
_screen.SetContent(right-1, bot-1, w.borderStyle.bottomRight, nil, style)
|
_screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,6 +410,7 @@ type Renderer interface {
|
|||||||
RefreshWindows(windows []Window)
|
RefreshWindows(windows []Window)
|
||||||
Refresh()
|
Refresh()
|
||||||
Close()
|
Close()
|
||||||
|
NeedScrollbarRedraw() bool
|
||||||
|
|
||||||
GetChar() Event
|
GetChar() Event
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user