Add more --border options; default changed to "rounded"

--border option now takes an optional argument that defines the style

  - rounded (new default)
  - sharp
  - horizontal (previous default)
This commit is contained in:
Junegunn Choi
2020-03-05 20:15:15 +09:00
parent 99f1e02766
commit d9b1211191
6 changed files with 150 additions and 90 deletions

View File

@@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
.. ..
.TH fzf 1 "Feb 2020" "fzf 0.21.0" "fzf - a command-line fuzzy finder" .TH fzf 1 "Mar 2020" "fzf 0.21.0" "fzf - a command-line fuzzy finder"
.SH NAME .SH NAME
fzf - a command-line fuzzy finder fzf - a command-line fuzzy finder
@@ -178,8 +178,16 @@ Choose the layout (default: default)
A synonym for \fB--layout=reverse\fB A synonym for \fB--layout=reverse\fB
.TP .TP
.B "--border" .BI "--border" [=STYLE]
Draw border above and below the finder Draw border around the finder
.br
.BR rounded " Border with rounded corners (default)"
.br
.BR sharp " Border with sharp corners"
.br
.BR horizontal " Horizontal lines above and below the finder"
.br
.TP .TP
.B "--no-unicode" .B "--no-unicode"

View File

@@ -57,7 +57,8 @@ const usage = `usage: fzf [options]
--min-height=HEIGHT Minimum height when --height is given in percent --min-height=HEIGHT Minimum height when --height is given in percent
(default: 10) (default: 10)
--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[=STYLE] Draw border around the finder
[rounded|sharp|horizontal] (default: rounded)
--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)
--info=STYLE Finder info style [default|inline|hidden] --info=STYLE Finder info style [default|inline|hidden]
--prompt=STR Input prompt (default: '> ') --prompt=STR Input prompt (default: '> ')
@@ -212,7 +213,7 @@ type Options struct {
Header []string Header []string
HeaderLines int HeaderLines int
Margin [4]sizeSpec Margin [4]sizeSpec
Bordered bool BorderShape tui.BorderShape
Unicode bool Unicode bool
Tabstop int Tabstop int
ClearOnExit bool ClearOnExit bool
@@ -301,12 +302,12 @@ func nextString(args []string, i *int, message string) string {
return args[*i] return args[*i]
} }
func optionalNextString(args []string, i *int) string { func optionalNextString(args []string, i *int) (bool, string) {
if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") { if len(args) > *i+1 && !strings.HasPrefix(args[*i+1], "-") && !strings.HasPrefix(args[*i+1], "+") {
*i++ *i++
return args[*i] return true, args[*i]
} }
return "" return false, ""
} }
func atoi(str string) int { func atoi(str string) int {
@@ -400,6 +401,23 @@ func parseAlgo(str string) algo.Algo {
return algo.FuzzyMatchV2 return algo.FuzzyMatchV2
} }
func parseBorder(str string, optional bool) tui.BorderShape {
switch str {
case "rounded":
return tui.BorderRounded
case "sharp":
return tui.BorderSharp
case "horizontal":
return tui.BorderHorizontal
default:
if optional && str == "" {
return tui.BorderRounded
}
errorExit("invalid border style (expected: rounded|sharp|horizontal)")
}
return tui.BorderNone
}
func parseKeyChords(str string, message string) map[int]string { func parseKeyChords(str string, message string) map[int]string {
if len(str) == 0 { if len(str) == 0 {
errorExit(message) errorExit(message)
@@ -1098,7 +1116,7 @@ func parseOptions(opts *Options, allArgs []string) {
case "--bind": case "--bind":
parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required")) parseKeymap(opts.Keymap, nextString(allArgs, &i, "bind expression required"))
case "--color": case "--color":
spec := optionalNextString(allArgs, &i) _, spec := optionalNextString(allArgs, &i)
if len(spec) == 0 { if len(spec) == 0 {
opts.Theme = tui.EmptyTheme() opts.Theme = tui.EmptyTheme()
} else { } else {
@@ -1246,9 +1264,10 @@ func parseOptions(opts *Options, allArgs []string) {
case "--no-margin": case "--no-margin":
opts.Margin = defaultMargin() opts.Margin = defaultMargin()
case "--no-border": case "--no-border":
opts.Bordered = false opts.BorderShape = tui.BorderNone
case "--border": case "--border":
opts.Bordered = true hasArg, arg := optionalNextString(allArgs, &i)
opts.BorderShape = parseBorder(arg, !hasArg)
case "--no-unicode": case "--no-unicode":
opts.Unicode = false opts.Unicode = false
case "--unicode": case "--unicode":
@@ -1273,6 +1292,8 @@ func parseOptions(opts *Options, allArgs []string) {
opts.Filter = &value opts.Filter = &value
} else if match, value := optString(arg, "-d", "--delimiter="); match { } else if match, value := optString(arg, "-d", "--delimiter="); match {
opts.Delimiter = delimiterRegexp(value) opts.Delimiter = delimiterRegexp(value)
} else if match, value := optString(arg, "--border="); match {
opts.BorderShape = parseBorder(value, false)
} else if match, value := optString(arg, "--prompt="); match { } else if match, value := optString(arg, "--prompt="); match {
opts.Prompt = value opts.Prompt = value
} else if match, value := optString(arg, "--pointer="); match { } else if match, value := optString(arg, "--pointer="); match {

View File

@@ -100,7 +100,7 @@ type Terminal struct {
margin [4]sizeSpec margin [4]sizeSpec
strong tui.Attr strong tui.Attr
unicode bool unicode bool
bordered bool borderShape tui.BorderShape
cleanExit bool cleanExit bool
border tui.Window border tui.Window
window tui.Window window tui.Window
@@ -370,9 +370,9 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
effectiveMinHeight *= 2 effectiveMinHeight *= 2
} }
if opts.InfoStyle != infoDefault { if opts.InfoStyle != infoDefault {
effectiveMinHeight -= 1 effectiveMinHeight--
} }
if opts.Bordered { if opts.BorderShape != tui.BorderNone {
effectiveMinHeight += 2 effectiveMinHeight += 2
} }
return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight)) return util.Min(termHeight, util.Max(maxHeight, effectiveMinHeight))
@@ -391,62 +391,62 @@ func NewTerminal(opts *Options, eventBox *util.EventBox) *Terminal {
spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`} spinner = []string{`-`, `\`, `|`, `/`, `-`, `\`, `|`, `/`}
} }
t := Terminal{ t := Terminal{
initDelay: delay, initDelay: delay,
infoStyle: opts.InfoStyle, infoStyle: opts.InfoStyle,
spinner: spinner, spinner: spinner,
queryLen: [2]int{0, 0}, queryLen: [2]int{0, 0},
layout: opts.Layout, layout: opts.Layout,
fullscreen: fullscreen, fullscreen: fullscreen,
hscroll: opts.Hscroll, hscroll: opts.Hscroll,
hscrollOff: opts.HscrollOff, hscrollOff: opts.HscrollOff,
wordRubout: wordRubout, wordRubout: wordRubout,
wordNext: wordNext, wordNext: wordNext,
cx: len(input), cx: len(input),
cy: 0, cy: 0,
offset: 0, offset: 0,
xoffset: 0, xoffset: 0,
yanked: []rune{}, yanked: []rune{},
input: input, input: input,
multi: opts.Multi, multi: opts.Multi,
sort: opts.Sort > 0, sort: opts.Sort > 0,
toggleSort: opts.ToggleSort, toggleSort: opts.ToggleSort,
delimiter: opts.Delimiter, delimiter: opts.Delimiter,
expect: opts.Expect, expect: opts.Expect,
keymap: opts.Keymap, keymap: opts.Keymap,
pressed: "", pressed: "",
printQuery: opts.PrintQuery, printQuery: opts.PrintQuery,
history: opts.History, history: opts.History,
margin: opts.Margin, margin: opts.Margin,
unicode: opts.Unicode, unicode: opts.Unicode,
bordered: opts.Bordered, borderShape: opts.BorderShape,
cleanExit: opts.ClearOnExit, cleanExit: opts.ClearOnExit,
strong: strongAttr, strong: strongAttr,
cycle: opts.Cycle, cycle: opts.Cycle,
header: header, header: header,
header0: header, header0: header,
ansi: opts.Ansi, ansi: opts.Ansi,
tabstop: opts.Tabstop, tabstop: opts.Tabstop,
reading: true, reading: true,
failed: nil, failed: nil,
jumping: jumpDisabled, jumping: jumpDisabled,
jumpLabels: opts.JumpLabels, jumpLabels: opts.JumpLabels,
printer: opts.Printer, printer: opts.Printer,
printsep: opts.PrintSep, printsep: opts.PrintSep,
merger: EmptyMerger, merger: EmptyMerger,
selected: make(map[int32]selectedItem), selected: make(map[int32]selectedItem),
reqBox: util.NewEventBox(), reqBox: util.NewEventBox(),
preview: opts.Preview, preview: opts.Preview,
previewer: previewer{"", 0, 0, previewBox != nil && !opts.Preview.hidden, false}, previewer: previewer{"", 0, 0, previewBox != nil && !opts.Preview.hidden, false},
previewBox: previewBox, previewBox: previewBox,
eventBox: eventBox, eventBox: eventBox,
mutex: sync.Mutex{}, mutex: sync.Mutex{},
suppress: true, suppress: true,
slab: util.MakeSlab(slab16Size, slab32Size), slab: util.MakeSlab(slab16Size, slab32Size),
theme: opts.Theme, theme: opts.Theme,
startChan: make(chan bool, 1), startChan: make(chan bool, 1),
killChan: make(chan int), killChan: make(chan int),
tui: renderer, tui: renderer,
initFunc: func() { renderer.Init() }} initFunc: func() { renderer.Init() }}
t.prompt, t.promptLen = t.processTabs([]rune(opts.Prompt), 0) t.prompt, t.promptLen = t.processTabs([]rune(opts.Prompt), 0)
t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0) t.pointer, t.pointerLen = t.processTabs([]rune(opts.Pointer), 0)
t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0) t.marker, t.markerLen = t.processTabs([]rune(opts.Marker), 0)
@@ -595,8 +595,11 @@ func (t *Terminal) resizeWindows() {
} else { } else {
marginInt[idx] = int(sizeSpec.size) marginInt[idx] = int(sizeSpec.size)
} }
if t.bordered && idx%2 == 0 { switch t.borderShape {
marginInt[idx] += 1 case tui.BorderHorizontal:
marginInt[idx] += 1 - idx%2
case tui.BorderRounded, tui.BorderSharp:
marginInt[idx] += 1 + idx%2
} }
} }
adjust := func(idx1 int, idx2 int, max int, min int) { adjust := func(idx1 int, idx2 int, max int, min int) {
@@ -636,18 +639,26 @@ func (t *Terminal) resizeWindows() {
width := screenWidth - marginInt[1] - marginInt[3] width := screenWidth - marginInt[1] - marginInt[3]
height := screenHeight - marginInt[0] - marginInt[2] height := screenHeight - marginInt[0] - marginInt[2]
if t.bordered { switch t.borderShape {
case tui.BorderHorizontal:
t.border = t.tui.NewWindow( t.border = t.tui.NewWindow(
marginInt[0]-1, marginInt[0]-1,
marginInt[3], marginInt[3],
width, width,
height+2, height+2,
false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode)) false, tui.MakeBorderStyle(tui.BorderHorizontal, t.unicode))
case tui.BorderRounded, tui.BorderSharp:
t.border = t.tui.NewWindow(
marginInt[0]-1,
marginInt[3]-2,
width+4,
height+2,
false, tui.MakeBorderStyle(t.borderShape, t.unicode))
} }
noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode) noBorder := tui.MakeBorderStyle(tui.BorderNone, t.unicode)
if previewVisible { if previewVisible {
createPreviewWindow := func(y int, x int, w int, h int) { createPreviewWindow := func(y int, x int, w int, h int) {
previewBorder := tui.MakeBorderStyle(tui.BorderAround, t.unicode) previewBorder := tui.MakeBorderStyle(tui.BorderRounded, t.unicode)
if !t.preview.border { if !t.preview.border {
previewBorder = tui.MakeTransparentBorder() previewBorder = tui.MakeTransparentBorder()
} }
@@ -1146,7 +1157,7 @@ func (t *Terminal) refresh() {
t.placeCursor() t.placeCursor()
if !t.suppress { if !t.suppress {
windows := make([]tui.Window, 0, 4) windows := make([]tui.Window, 0, 4)
if t.bordered { if t.borderShape != tui.BorderNone {
windows = append(windows, t.border) windows = append(windows, t.border)
} }
if t.hasPreviewWindow() { if t.hasPreviewWindow() {

View File

@@ -105,6 +105,7 @@ type LightRenderer struct {
type LightWindow struct { type LightWindow struct {
renderer *LightRenderer renderer *LightRenderer
colored bool colored bool
preview bool
border BorderStyle border BorderStyle
top int top int
left int left int
@@ -681,6 +682,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
w := &LightWindow{ w := &LightWindow{
renderer: r, renderer: r,
colored: r.theme != nil, colored: r.theme != nil,
preview: preview,
border: borderStyle, border: borderStyle,
top: top, top: top,
left: left, left: left,
@@ -704,7 +706,7 @@ func (r *LightRenderer) NewWindow(top int, left int, width int, height int, prev
func (w *LightWindow) drawBorder() { func (w *LightWindow) drawBorder() {
switch w.border.shape { switch w.border.shape {
case BorderAround: case BorderRounded, BorderSharp:
w.drawBorderAround() w.drawBorderAround()
case BorderHorizontal: case BorderHorizontal:
w.drawBorderHorizontal() w.drawBorderHorizontal()
@@ -720,16 +722,20 @@ func (w *LightWindow) drawBorderHorizontal() {
func (w *LightWindow) drawBorderAround() { func (w *LightWindow) drawBorderAround() {
w.Move(0, 0) w.Move(0, 0)
w.CPrint(ColPreviewBorder, AttrRegular, color := ColBorder
if w.preview {
color = ColPreviewBorder
}
w.CPrint(color, AttrRegular,
string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.topRight)) string(w.border.topLeft)+repeat(w.border.horizontal, w.width-2)+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(ColPreviewBorder, AttrRegular, string(w.border.vertical)) w.CPrint(color, AttrRegular, string(w.border.vertical))
w.CPrint(ColPreviewBorder, AttrRegular, repeat(' ', w.width-2)) w.CPrint(color, AttrRegular, repeat(' ', w.width-2))
w.CPrint(ColPreviewBorder, AttrRegular, string(w.border.vertical)) w.CPrint(color, AttrRegular, string(w.border.vertical))
} }
w.Move(w.height-1, 0) w.Move(w.height-1, 0)
w.CPrint(ColPreviewBorder, AttrRegular, w.CPrint(color, AttrRegular,
string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight)) string(w.border.bottomLeft)+repeat(w.border.horizontal, w.width-2)+string(w.border.bottomRight))
} }

View File

@@ -28,6 +28,7 @@ type Attr tcell.Style
type TcellWindow struct { type TcellWindow struct {
color bool color bool
preview bool
top int top int
left int left int
width int width int
@@ -418,6 +419,7 @@ func (r *FullscreenRenderer) NewWindow(top int, left int, width int, height int,
} }
return &TcellWindow{ return &TcellWindow{
color: r.theme != nil, color: r.theme != nil,
preview: preview,
top: top, top: top,
left: left, left: left,
width: width, width: width,
@@ -591,7 +593,7 @@ func (w *TcellWindow) drawBorder() {
var style tcell.Style var style tcell.Style
if w.color { if w.color {
if w.borderStyle.shape == BorderAround { if w.preview {
style = ColPreviewBorder.style() style = ColPreviewBorder.style()
} else { } else {
style = ColBorder.style() style = ColBorder.style()
@@ -605,7 +607,7 @@ func (w *TcellWindow) drawBorder() {
_screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style) _screen.SetContent(x, bot-1, w.borderStyle.horizontal, nil, style)
} }
if w.borderStyle.shape == BorderAround { if w.borderStyle.shape != BorderHorizontal {
for y := top; y < bot; y++ { for y := top; y < bot; y++ {
_screen.SetContent(left, y, w.borderStyle.vertical, nil, style) _screen.SetContent(left, y, w.borderStyle.vertical, nil, style)
_screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style) _screen.SetContent(right-1, y, w.borderStyle.vertical, nil, style)

View File

@@ -210,7 +210,8 @@ type BorderShape int
const ( const (
BorderNone BorderShape = iota BorderNone BorderShape = iota
BorderAround BorderRounded
BorderSharp
BorderHorizontal BorderHorizontal
) )
@@ -228,14 +229,25 @@ type BorderCharacter int
func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle { func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
if unicode { if unicode {
if shape == BorderRounded {
return BorderStyle{
shape: shape,
horizontal: '─',
vertical: '│',
topLeft: '╭',
topRight: '╮',
bottomLeft: '╰',
bottomRight: '╯',
}
}
return BorderStyle{ return BorderStyle{
shape: shape, shape: shape,
horizontal: '─', horizontal: '─',
vertical: '│', vertical: '│',
topLeft: '', topLeft: '',
topRight: '', topRight: '',
bottomLeft: '', bottomLeft: '',
bottomRight: '', bottomRight: '',
} }
} }
return BorderStyle{ return BorderStyle{
@@ -251,7 +263,7 @@ func MakeBorderStyle(shape BorderShape, unicode bool) BorderStyle {
func MakeTransparentBorder() BorderStyle { func MakeTransparentBorder() BorderStyle {
return BorderStyle{ return BorderStyle{
shape: BorderAround, shape: BorderRounded,
horizontal: ' ', horizontal: ' ',
vertical: ' ', vertical: ' ',
topLeft: ' ', topLeft: ' ',