From 75972d59a8c772028bd6c57bc1c18c993bf1967a Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Fri, 29 Mar 2019 02:11:03 +0900 Subject: [PATCH] Add --no-unicode option to draw borders in ASCII characters Close ##1533 --- man/man1/fzf.1 | 5 +++++ src/options.go | 6 ++++++ src/terminal.go | 19 +++++++++++-------- src/tui/light.go | 26 ++++++++++++++------------ src/tui/tcell.go | 32 ++++++++++++++++---------------- src/tui/tui.go | 37 +++++++++++++++++++++++++++++++++++-- 6 files changed, 87 insertions(+), 38 deletions(-) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 860c26e..9ff44c4 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -174,6 +174,11 @@ A synonym for \fB--layout=reverse\fB .TP .B "--border" Draw border above and below the finder + +.TP +.B "--no-unicode" +Use ASCII characters instead of Unicode box drawing characters to draw border + .TP .BI "--margin=" MARGIN Comma-separated expression for margins around the finder. diff --git a/src/options.go b/src/options.go index f538259..59cdccc 100644 --- a/src/options.go +++ b/src/options.go @@ -195,6 +195,7 @@ type Options struct { HeaderLines int Margin [4]sizeSpec Bordered bool + Unicode bool Tabstop int ClearOnExit bool Version bool @@ -244,6 +245,7 @@ func defaultOptions() *Options { Header: make([]string, 0), HeaderLines: 0, Margin: defaultMargin(), + Unicode: true, Tabstop: 8, ClearOnExit: true, Version: false} @@ -1152,6 +1154,10 @@ func parseOptions(opts *Options, allArgs []string) { opts.Bordered = false case "--border": opts.Bordered = true + case "--no-unicode": + opts.Unicode = false + case "--unicode": + opts.Unicode = true case "--margin": opts.Margin = parseMargin( nextString(allArgs, &i, "margin required (TRBL / TB,RL / T,RL,B / T,R,B,L)")) diff --git a/src/terminal.go b/src/terminal.go index 8b65e91..1741b4d 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -88,6 +88,7 @@ type Terminal struct { tabstop int margin [4]sizeSpec strong tui.Attr + unicode bool bordered bool cleanExit bool border tui.Window @@ -391,6 +392,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal { printQuery: opts.PrintQuery, history: opts.History, margin: opts.Margin, + unicode: opts.Unicode, bordered: opts.Bordered, cleanExit: opts.ClearOnExit, strong: strongAttr, @@ -600,11 +602,12 @@ func (t *Terminal) resizeWindows() { marginInt[0]-1, marginInt[3], width, - height+2, tui.BorderHorizontal) + height+2, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode)) } + noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode) if previewVisible { createPreviewWindow := func(y int, x int, w int, h int) { - t.pborder = t.tui.NewWindow(y, x, w, h, tui.BorderAround) + t.pborder = t.tui.NewWindow(y, x, w, h, tui.MakeBorderStyle(tui.BorderAround, t.unicode)) pwidth := w - 4 // ncurses auto-wraps the line when the cursor reaches the right-end of // the window. To prevent unintended line-wraps, we use the width one @@ -612,28 +615,28 @@ func (t *Terminal) resizeWindows() { if !t.preview.wrap && t.tui.DoesAutoWrap() { pwidth += 1 } - t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, tui.BorderNone) + t.pwindow = t.tui.NewWindow(y+1, x+2, pwidth, h-2, noBorder) } switch t.preview.position { case posUp: pheight := calculateSize(height, t.preview.size, minHeight, 3) t.window = t.tui.NewWindow( - marginInt[0]+pheight, marginInt[3], width, height-pheight, tui.BorderNone) + marginInt[0]+pheight, marginInt[3], width, height-pheight, noBorder) createPreviewWindow(marginInt[0], marginInt[3], width, pheight) case posDown: pheight := calculateSize(height, t.preview.size, minHeight, 3) t.window = t.tui.NewWindow( - marginInt[0], marginInt[3], width, height-pheight, tui.BorderNone) + marginInt[0], marginInt[3], width, height-pheight, noBorder) createPreviewWindow(marginInt[0]+height-pheight, marginInt[3], width, pheight) case posLeft: pwidth := calculateSize(width, t.preview.size, minWidth, 5) t.window = t.tui.NewWindow( - marginInt[0], marginInt[3]+pwidth, width-pwidth, height, tui.BorderNone) + marginInt[0], marginInt[3]+pwidth, width-pwidth, height, noBorder) createPreviewWindow(marginInt[0], marginInt[3], pwidth, height) case posRight: pwidth := calculateSize(width, t.preview.size, minWidth, 5) t.window = t.tui.NewWindow( - marginInt[0], marginInt[3], width-pwidth, height, tui.BorderNone) + marginInt[0], marginInt[3], width-pwidth, height, noBorder) createPreviewWindow(marginInt[0], marginInt[3]+width-pwidth, pwidth, height) } } else { @@ -641,7 +644,7 @@ func (t *Terminal) resizeWindows() { marginInt[0], marginInt[3], width, - height, tui.BorderNone) + height, noBorder) } for i := 0; i < t.window.Height(); i++ { t.window.MoveAndClear(i, 0) diff --git a/src/tui/light.go b/src/tui/light.go index edeb621..2c2dc00 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -163,9 +163,9 @@ func (r *LightRenderer) findOffset() (row int, col int) { return -1, -1 } -func repeat(s string, times int) string { +func repeat(r rune, times int) string { if times > 0 { - return strings.Repeat(s, times) + return strings.Repeat(string(r), times) } return "" } @@ -676,7 +676,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, bord } func (w *LightWindow) drawBorder() { - switch w.border { + switch w.border.shape { case BorderAround: w.drawBorderAround() case BorderHorizontal: @@ -686,22 +686,24 @@ func (w *LightWindow) drawBorder() { func (w *LightWindow) drawBorderHorizontal() { w.Move(0, 0) - w.CPrint(ColBorder, AttrRegular, repeat("─", w.width)) + w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width)) w.Move(w.height-1, 0) - w.CPrint(ColBorder, AttrRegular, repeat("─", w.width)) + w.CPrint(ColBorder, AttrRegular, repeat(w.border.horizontal, w.width)) } func (w *LightWindow) drawBorderAround() { w.Move(0, 0) - w.CPrint(ColBorder, AttrRegular, "┌"+repeat("─", w.width-2)+"┐") + w.CPrint(ColBorder, AttrRegular, + string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight)) for y := 1; y < w.height-1; y++ { w.Move(y, 0) - w.CPrint(ColBorder, AttrRegular, "│") - w.cprint2(colDefault, w.bg, AttrRegular, repeat(" ", w.width-2)) - w.CPrint(ColBorder, AttrRegular, "│") + w.CPrint(ColBorder, AttrRegular, string(w.border.vertical)) + w.cprint2(colDefault, w.bg, AttrRegular, repeat(' ', w.width-2)) + w.CPrint(ColBorder, AttrRegular, string(w.border.vertical)) } w.Move(w.height-1, 0) - w.CPrint(ColBorder, AttrRegular, "└"+repeat("─", w.width-2)+"┘") + w.CPrint(ColBorder, AttrRegular, + string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight)) } func (w *LightWindow) csi(code string) { @@ -762,7 +764,7 @@ func (w *LightWindow) MoveAndClear(y int, x int) { w.Move(y, x) // We should not delete preview window on the right // csi("K") - w.Print(repeat(" ", w.width-x)) + w.Print(repeat(' ', w.width-x)) w.Move(y, x) } @@ -858,7 +860,7 @@ func wrapLine(input string, prefixLength int, max int, tabstop int) []wrappedLin width += w str := string(r) if r == '\t' { - str = repeat(" ", w) + str = repeat(' ', w) } if prefixLength+width <= max { line += str diff --git a/src/tui/tcell.go b/src/tui/tcell.go index d337385..098e8a1 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -61,12 +61,8 @@ func (w *TcellWindow) Refresh() { } w.lastX = 0 w.lastY = 0 - switch w.borderStyle { - case BorderAround: - w.drawBorder(true) - case BorderHorizontal: - w.drawBorder(false) - } + + w.drawBorder() } func (w *TcellWindow) FinishFill() { @@ -570,7 +566,11 @@ func (w *TcellWindow) CFill(fg Color, bg Color, a Attr, str string) FillReturn { return w.fillString(str, NewColorPair(fg, bg), a) } -func (w *TcellWindow) drawBorder(around bool) { +func (w *TcellWindow) drawBorder() { + if w.borderStyle.shape == BorderNone { + return + } + left := w.left right := left + w.width top := w.top @@ -584,19 +584,19 @@ func (w *TcellWindow) drawBorder(around bool) { } for x := left; x < right; x++ { - _screen.SetContent(x, top, tcell.RuneHLine, nil, style) - _screen.SetContent(x, bot-1, tcell.RuneHLine, nil, style) + _screen.SetContent(x, top, w.borderStyle.horizontal, nil, style) + _screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style) } - if around { + if w.borderStyle.shape == BorderAround { for y := top; y < bot; y++ { - _screen.SetContent(left, y, tcell.RuneVLine, nil, style) - _screen.SetContent(right-1, y, tcell.RuneVLine, nil, style) + _screen.SetContent(left, y, w.borderStyle.vertical, nil, style) + _screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style) } - _screen.SetContent(left, top, tcell.RuneULCorner, nil, style) - _screen.SetContent(right-1, top, tcell.RuneURCorner, nil, style) - _screen.SetContent(left, bot-1, tcell.RuneLLCorner, nil, style) - _screen.SetContent(right-1, bot-1, tcell.RuneLRCorner, nil, style) + _screen.SetContent(left, top, w.borderStyle.topLeft, nil, style) + _screen.SetContent(right-1, 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) } } diff --git a/src/tui/tui.go b/src/tui/tui.go index 5d0035d..be5bd26 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -201,14 +201,47 @@ type MouseEvent struct { Mod bool } -type BorderStyle int +type BorderShape int const ( - BorderNone BorderStyle = iota + BorderNone BorderShape = iota BorderAround BorderHorizontal ) +type BorderStyle struct { + shape BorderShape + horizontal rune + vertical rune + topLeft rune + topRight rune + bottomLeft rune + bottomRight rune +} + +func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle { + if unicode { + return BorderStyle{ + shape: shape, + horizontal: '─', + vertical: '│', + topLeft: '┌', + topRight: '┐', + bottomLeft: '└', + bottomRight: '┘', + } + } + return BorderStyle{ + shape: shape, + horizontal: '-', + vertical: '|', + topLeft: '+', + topRight: '+', + bottomLeft: '+', + bottomRight: '+', + } +} + type Renderer interface { Init() Pause(clear bool)