summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2023-01-16 01:22:02 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2023-01-16 01:26:39 +0900
commit0c5956c43c1604d8e4364f08369dc4c58295ef3c (patch)
tree1c7a4601c7b2b108febefdde6cab9117ebe144f4
parent1c83b3969185393f0427463a39ef1523c273c16c (diff)
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`.
-rw-r--r--CHANGELOG.md9
-rw-r--r--man/man1/fzf.18
-rw-r--r--src/options.go4
-rw-r--r--src/terminal.go35
-rw-r--r--src/tui/dummy.go3
-rw-r--r--src/tui/light.go19
-rw-r--r--src/tui/tcell.go17
-rw-r--r--src/tui/tui.go1
8 files changed, 66 insertions, 30 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04b276f2..f259cfb5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -88,6 +88,10 @@ CHANGELOG
# a will put 'alpha' on the prompt, ctrl-b will 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
- fzf will always execute the preview command if the command template
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
this version, fzf will resume following if the user scrolls the window
to the bottom.
-- Added color name `preview-label` for `--preview-label` (defaults to `label`
- for `--border-label`)
+ - Default border style on Windows is changed to `sharp` because some
+ Windows terminals are not capable of displaying `rounded` border
+ characters correctly.
- Minor bug fixes and improvements
0.35.1
diff --git a/man/man1/fzf.1 b/man/man1/fzf.1
index 75856d07..6d44b461 100644
--- a/man/man1/fzf.1
+++ b/man/man1/fzf.1
@@ -230,6 +230,10 @@ Draw border around the finder
.BR none
.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
.BI "--border-label" [=LABEL]
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.
.br
-.B * border-rounded (default)
+.B * border-rounded (default on non-Windows platforms)
.br
-.B * border-sharp
+.B * border-sharp (default on Windows)
.br
.B * border-bold
.br
diff --git a/src/options.go b/src/options.go
index fa51cc35..fd160367 100644
--- a/src/options.go
+++ b/src/options.go
@@ -314,7 +314,7 @@ type Options struct {
}
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 {
@@ -543,7 +543,7 @@ func parseBorder(str string, optional bool) tui.BorderShape {
return tui.BorderNone
default:
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)")
}
diff --git a/src/terminal.go b/src/terminal.go
index 8cd9a143..41b24f4e 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -210,6 +210,7 @@ type Terminal struct {
window tui.Window
pborder tui.Window
pwindow tui.Window
+ borderWidth int
count int
progress int
hasLoadActions bool
@@ -604,6 +605,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
unicode: opts.Unicode,
listenPort: opts.ListenPort,
borderShape: opts.BorderShape,
+ borderWidth: 1,
borderLabel: nil,
borderLabelOpts: opts.BorderLabel,
previewLabel: nil,
@@ -666,8 +668,11 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
}
t.separator, t.separatorLen = t.ansiLabelPrinter(bar, &tui.ColSeparator, true)
}
+ if t.unicode {
+ t.borderWidth = runewidth.RuneWidth('│')
+ }
if opts.Scrollbar == nil {
- if t.unicode {
+ if t.unicode && t.borderWidth == 1 {
t.scrollbar = "│"
} else {
t.scrollbar = "|"
@@ -968,20 +973,21 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
paddingInt[idx] = sizeSpecToInt(idx, sizeSpec)
}
+ bw := t.borderWidth
extraMargin := [4]int{} // TRBL
for idx, sizeSpec := range t.margin {
switch t.borderShape {
case tui.BorderHorizontal:
extraMargin[idx] += 1 - idx%2
case tui.BorderVertical:
- extraMargin[idx] += 2 * (idx % 2)
+ extraMargin[idx] += (1 + bw) * (idx % 2)
case tui.BorderTop:
if idx == 0 {
extraMargin[idx]++
}
case tui.BorderRight:
if idx == 1 {
- extraMargin[idx] += 2
+ extraMargin[idx] += 1 + bw
}
case tui.BorderBottom:
if idx == 2 {
@@ -989,10 +995,10 @@ func (t *Terminal) adjustMarginAndPadding() (int, int, [4]int, [4]int) {
}
case tui.BorderLeft:
if idx == 3 {
- extraMargin[idx] += 2
+ extraMargin[idx] += 1 + bw
}
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]
}
@@ -1106,6 +1112,7 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
return
}
hasThreshold := previewOpts.threshold > 0 && previewOpts.alternative != nil
+ bw := t.borderWidth
createPreviewWindow := func(y int, x int, w int, h int) {
pwidth := w
pheight := h
@@ -1118,15 +1125,15 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
t.pborder = t.tui.NewWindow(y, x, w, h, true, previewBorder)
switch previewOpts.border {
case tui.BorderSharp, tui.BorderRounded, tui.BorderBold, tui.BorderDouble:
- pwidth -= 4
+ pwidth -= (1 + bw) * 2
pheight -= 2
- x += 2
+ x += 1 + bw
y += 1
case tui.BorderLeft:
- pwidth -= 2
- x += 2
+ pwidth -= 1 + bw
+ x += 1 + bw
case tui.BorderRight:
- pwidth -= 2
+ pwidth -= 1 + bw
case tui.BorderTop:
pheight -= 1
y += 1
@@ -1136,8 +1143,8 @@ func (t *Terminal) resizeWindows(forcePreview bool) {
pheight -= 2
y += 1
case tui.BorderVertical:
- pwidth -= 4
- x += 2
+ pwidth -= (1 + bw) * 2
+ x += 1 + bw
}
if len(t.scrollbar) > 0 && !previewOpts.border.HasRight() {
// 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 {
t.previewer.bar = make([]bool, height)
}
- xshift := -2
+ xshift := -1 - t.borderWidth
if !t.previewOpts.border.HasRight() {
xshift = -1
}
@@ -1846,7 +1853,7 @@ func (t *Terminal) renderPreviewScrollbar(yoff int, barLength int, barStart int)
// Avoid unnecessary redraws
bar := i >= yoff+barStart && i < yoff+barStart+barLength
- if bar == t.previewer.bar[i] {
+ if bar == t.previewer.bar[i] && !t.tui.NeedScrollbarRedraw() {
continue
}
diff --git a/src/tui/dummy.go b/src/tui/dummy.go
index adecd6fc..7a02a8af 100644
--- a/src/tui/dummy.go
+++ b/src/tui/dummy.go
@@ -8,6 +8,8 @@ func HasFullscreenRenderer() bool {
return false
}
+var DefaultBorderShape BorderShape = BorderRounded
+
func (a Attr) Merge(b Attr) Attr {
return a | b
}
@@ -32,6 +34,7 @@ func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {}
func (r *FullscreenRenderer) Pause(bool) {}
func (r *FullscreenRenderer) Resume(bool, bool) {}
func (r *FullscreenRenderer) Clear() {}
+func (r *FullscreenRenderer) NeedScrollbarRedraw() bool { return false }
func (r *FullscreenRenderer) Refresh() {}
func (r *FullscreenRenderer) Close() {}
diff --git a/src/tui/light.go b/src/tui/light.go
index c41de480..578118b7 100644
--- a/src/tui/light.go
+++ b/src/tui/light.go
@@ -651,6 +651,10 @@ func (r *LightRenderer) Clear() {
r.flush()
}
+func (r *LightRenderer) NeedScrollbarRedraw() bool {
+ return false
+}
+
func (r *LightRenderer) RefreshWindows(windows []Window) {
r.flush()
}
@@ -743,13 +747,14 @@ func (w *LightWindow) drawBorderHorizontal(top, bottom bool) {
if w.preview {
color = ColPreviewBorder
}
+ hw := runewidth.RuneWidth(w.border.horizontal)
if top {
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 {
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 {
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++ {
w.Move(y, 0)
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.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) {
diff --git a/src/tui/tcell.go b/src/tui/tcell.go
index 82d72995..f1f18e24 100644
--- a/src/tui/tcell.go
+++ b/src/tui/tcell.go
@@ -17,6 +17,8 @@ func HasFullscreenRenderer() bool {
return true
}
+var DefaultBorderShape BorderShape = BorderSharp
+
func asTcellColor(color Color) tcell.Color {
if color == colDefault {
return tcell.ColorDefault
@@ -187,6 +189,10 @@ func (r *FullscreenRenderer) Clear() {
_screen.Clear()
}
+func (r *FullscreenRenderer) NeedScrollbarRedraw() bool {
+ return true
+}
+
func (r *FullscreenRenderer) Refresh() {
// noop
}
@@ -686,15 +692,16 @@ func (w *TcellWindow) drawBorder() {
style = w.normal.style()
}
+ hw := runewidth.RuneWidth(w.borderStyle.horizontal)
switch shape {
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)
}
}
switch shape {
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)
}
}
@@ -707,14 +714,14 @@ func (w *TcellWindow) drawBorder() {
switch shape {
case BorderRounded, BorderSharp, BorderBold, BorderDouble, BorderVertical, BorderRight:
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 {
case BorderRounded, BorderSharp, BorderBold, BorderDouble:
_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(right-1, bot-1, w.borderStyle.bottomRight, nil, style)
+ _screen.SetContent(right-runewidth.RuneWidth(w.borderStyle.bottomRight), bot-1, w.borderStyle.bottomRight, nil, style)
}
}
diff --git a/src/tui/tui.go b/src/tui/tui.go
index c6a65d82..5a86453b 100644
--- a/src/tui/tui.go
+++ b/src/tui/tui.go
@@ -410,6 +410,7 @@ type Renderer interface {
RefreshWindows(windows []Window)
Refresh()
Close()
+ NeedScrollbarRedraw() bool
GetChar() Event