diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2025-01-06 00:44:59 +0900 |
|---|---|---|
| committer | Junegunn Choi <junegunn.c@gmail.com> | 2025-01-06 00:44:59 +0900 |
| commit | 0e0b86834294e1befb9e1ce16239230aa40d1add (patch) | |
| tree | 4e8fec876561cdb810299c0126a838f27d7b9553 | |
| parent | a5beb08ed72215e8b015851ad70bf3beca526429 (diff) | |
| download | fzf-0e0b86834294e1befb9e1ce16239230aa40d1add.tar.gz | |
Add preview border style 'line'
It draws a single line between the preview window and the rest of the
interface. i.e. automatically choose between 'left', 'right', 'top', and
'bottom' depending on the position of the preview window.
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | man/man1/fzf.1 | 11 | ||||
| -rw-r--r-- | src/options.go | 37 | ||||
| -rw-r--r-- | src/terminal.go | 39 | ||||
| -rw-r--r-- | src/tui/tui.go | 9 |
5 files changed, 70 insertions, 27 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index e18790b8..e0b8e66e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ CHANGELOG - `change-header-label` - `transform-header-label` - Added `--preview-border[=STYLE]` as short for `--preview-window=border[-STYLE]` +- Added new preview border style `line` which draws a single separator line between the preview window and the rest of the interface - You can specify `border-native` to `--tmux` so that native tmux border is used instead of `--border`. This can be useful if you start a different program from inside the popup. ```sh fzf --tmux border-native --bind 'enter:execute:less {}' diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 16421c7b..cc80c1a4 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -775,7 +775,10 @@ e.g. .TP .BI "\-\-preview\-border" [=STYLE] -Short for \fB\-\-preview\-window=border\-STYLE\fR +Short for \fB\-\-preview\-window=border\-STYLE\fR. In addition to the other +styles, \fBline\fR style is also supported for preview border, which draws +a single separator line between the preview window and the rest of the +interface. .TP .BI "\-\-preview\-label" [=LABEL] @@ -812,7 +815,7 @@ default value 0 (or \fBcenter\fR) will put the label at the center of the border line. .TP -.BI "\-\-preview\-window=" "[POSITION][,SIZE[%]][,border\-BORDER_OPT][,[no]wrap][,[no]follow][,[no]cycle][,[no]info][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]" +.BI "\-\-preview\-window=" "[POSITION][,SIZE[%]][,border\-STYLE][,[no]wrap][,[no]follow][,[no]cycle][,[no]info][,[no]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]" .RS .B POSITION: (default: right) @@ -855,6 +858,10 @@ e.g. \fBborder\-rounded\fR (border with rounded edges, default), \fBborder\-sharp\fR (border with sharp edges), \fBborder\-left\fR, \fBborder\-none\fR, etc. +* In addition to the other border styles, \fBborder\-line\fR style is also +supported, which draws a single separator line between the preview window and +the rest of the interface. + * \fB[:+SCROLL[OFFSETS][/DENOM]]\fR determines the initial scroll offset of the preview window. diff --git a/src/options.go b/src/options.go index f6b7245f..7db7418b 100644 --- a/src/options.go +++ b/src/options.go @@ -146,10 +146,12 @@ Usage: fzf [options] --preview-window=OPT Preview window layout (default: right:50%) [up|down|left|right][,SIZE[%]] [,[no]wrap][,[no]cycle][,[no]follow][,[no]info] - [,[no]hidden][,border-BORDER_OPT] + [,[no]hidden][,border-STYLE] [,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES] [,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)] --preview-border[=STYLE] Short for --preview-window=border-STYLE + [rounded|sharp|bold|block|thinblock|double|horizontal|vertical| + top|bottom|left|right|line|none] (default: rounded) --preview-label=LABEL --preview-label-pos=N Same as --border-label and --border-label-pos, but for preview window @@ -313,6 +315,10 @@ func (o *previewOpts) Toggle() { o.hidden = !o.hidden } +func (o *previewOpts) HasBorderRight() bool { + return o.border.HasRight() || o.border == tui.BorderLine && o.position == posLeft +} + func defaultTmuxOptions(index int) *tmuxOptions { return &tmuxOptions{ position: posCenter, @@ -832,8 +838,13 @@ func processScheme(opts *Options) error { return nil } -func parseBorder(str string, optional bool) (tui.BorderShape, error) { +func parseBorder(str string, optional bool, allowLine bool) (tui.BorderShape, error) { switch str { + case "line": + if !allowLine { + return tui.BorderNone, errors.New("'line' is only allowed for preview border") + } + return tui.BorderLine, nil case "rounded": return tui.BorderRounded, nil case "sharp": @@ -1900,6 +1911,8 @@ func parsePreviewWindowImpl(opts *previewOpts, input string) error { opts.position = posRight case "rounded", "border", "border-rounded": opts.border = tui.BorderRounded + case "border-line": + opts.border = tui.BorderLine case "sharp", "border-sharp": opts.border = tui.BorderSharp case "border-bold": @@ -2501,7 +2514,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { case "--no-preview": opts.Preview.command = "" case "--preview-window": - str, err := nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-BORDER_OPT][,wrap][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]") + str, err := nextString(allArgs, &i, "preview window layout required: [up|down|left|right][,SIZE[%]][,border-STYLE][,wrap][,cycle][,hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default]") if err != nil { return err } @@ -2512,7 +2525,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { opts.Preview.border = tui.BorderNone case "--preview-border": hasArg, arg := optionalNextString(allArgs, &i) - if opts.Preview.border, err = parseBorder(arg, !hasArg); err != nil { + if opts.Preview.border, err = parseBorder(arg, !hasArg, true); err != nil { return err } case "--height": @@ -2537,12 +2550,12 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { opts.BorderShape = tui.BorderNone case "--border": hasArg, arg := optionalNextString(allArgs, &i) - if opts.BorderShape, err = parseBorder(arg, !hasArg); err != nil { + if opts.BorderShape, err = parseBorder(arg, !hasArg, false); err != nil { return err } case "--list-border": hasArg, arg := optionalNextString(allArgs, &i) - if opts.ListBorderShape, err = parseBorder(arg, !hasArg); err != nil { + if opts.ListBorderShape, err = parseBorder(arg, !hasArg, false); err != nil { return err } case "--no-list-border": @@ -2566,7 +2579,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { opts.HeaderBorderShape = tui.BorderNone case "--header-border": hasArg, arg := optionalNextString(allArgs, &i) - if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg); err != nil { + if opts.HeaderBorderShape, err = parseBorder(arg, !hasArg, false); err != nil { return err } case "--no-header-label": @@ -2587,7 +2600,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { opts.InputBorderShape = tui.BorderNone case "--input-border": hasArg, arg := optionalNextString(allArgs, &i) - if opts.InputBorderShape, err = parseBorder(arg, !hasArg); err != nil { + if opts.InputBorderShape, err = parseBorder(arg, !hasArg, false); err != nil { return err } case "--no-input-label": @@ -2738,15 +2751,15 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { } else if match, value := optString(arg, "-d", "--delimiter="); match { opts.Delimiter = delimiterRegexp(value) } else if match, value := optString(arg, "--border="); match { - if opts.BorderShape, err = parseBorder(value, false); err != nil { + if opts.BorderShape, err = parseBorder(value, false, false); err != nil { return err } } else if match, value := optString(arg, "--preview-border="); match { - if opts.Preview.border, err = parseBorder(value, false); err != nil { + if opts.Preview.border, err = parseBorder(value, false, true); err != nil { return err } } else if match, value := optString(arg, "--list-border="); match { - if opts.ListBorderShape, err = parseBorder(value, false); err != nil { + if opts.ListBorderShape, err = parseBorder(value, false, false); err != nil { return err } } else if match, value := optString(arg, "--list-label="); match { @@ -2756,7 +2769,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { return err } } else if match, value := optString(arg, "--input-border="); match { - if opts.InputBorderShape, err = parseBorder(value, false); err != nil { + if opts.InputBorderShape, err = parseBorder(value, false, false); err != nil { return err } } else if match, value := optString(arg, "--input-label="); match { diff --git a/src/terminal.go b/src/terminal.go index 764284a9..344246de 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -1513,7 +1513,7 @@ func (t *Terminal) minPreviewSize(opts *previewOpts) (int, int) { switch opts.position { case posLeft, posRight: - if len(t.scrollbar) > 0 && !opts.border.HasRight() { + if len(t.scrollbar) > 0 && !opts.HasBorderRight() { // Need a column to show scrollbar minPreviewWidth++ } @@ -1757,17 +1757,30 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) { createPreviewWindow := func(y int, x int, w int, h int) { pwidth := w pheight := h - previewBorder := tui.MakeBorderStyle(previewOpts.border, t.unicode) + shape := previewOpts.border + if shape == tui.BorderLine { + switch previewOpts.position { + case posUp: + shape = tui.BorderBottom + case posDown: + shape = tui.BorderTop + case posLeft: + shape = tui.BorderRight + case posRight: + shape = tui.BorderLeft + } + } + previewBorder := tui.MakeBorderStyle(shape, t.unicode) t.pborder = t.tui.NewWindow(y, x, w, h, tui.WindowPreview, previewBorder, false) - pwidth -= borderColumns(previewOpts.border, bw) - pheight -= borderLines(previewOpts.border) - if previewOpts.border.HasLeft() { + pwidth -= borderColumns(shape, bw) + pheight -= borderLines(shape) + if shape.HasLeft() { x += 1 + bw } - if previewOpts.border.HasTop() { + if shape.HasTop() { y += 1 } - if len(t.scrollbar) > 0 && !previewOpts.border.HasRight() { + if len(t.scrollbar) > 0 && !shape.HasRight() { // Need a column to show scrollbar pwidth -= 1 } @@ -1800,6 +1813,14 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) { if previewOpts.hidden { return } + // If none of the inner borders has the right side, but the outer border does, increase the width by 1 column + stickToRight = t.borderShape.HasRight() && + !previewOpts.HasBorderRight() && !t.listBorderShape.HasRight() && !t.inputBorderShape.HasRight() && + (!t.headerVisible || !t.headerBorderShape.HasRight() || t.visibleHeaderLines() == 0) + if stickToRight { + innerWidth++ + width++ + } maxPreviewLines := availableLines if t.wborder != nil { @@ -1870,7 +1891,7 @@ func (t *Terminal) resizeWindows(forcePreview bool, redrawBorder bool) { createPreviewWindow(marginInt[0], marginInt[3], pwidth, height) } else { // NOTE: fzf --preview 'cat {}' --preview-window border-left --border - stickToRight = !previewOpts.border.HasRight() && t.borderShape.HasRight() + stickToRight = !previewOpts.HasBorderRight() && t.borderShape.HasRight() if stickToRight { innerWidth++ width++ @@ -3187,7 +3208,7 @@ func (t *Terminal) renderPreviewScrollbar(yoff int, barLength int, barStart int) t.previewer.xw = xw } xshift := -1 - t.borderWidth - if !t.activePreviewOpts.border.HasRight() { + if !t.activePreviewOpts.HasBorderRight() { xshift = -1 } yshift := 1 diff --git a/src/tui/tui.go b/src/tui/tui.go index db846f75..2bd9caf9 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -358,6 +358,7 @@ type BorderShape int const ( BorderUndefined BorderShape = iota + BorderLine BorderNone BorderRounded BorderSharp @@ -375,7 +376,7 @@ const ( func (s BorderShape) HasLeft() bool { switch s { - case BorderNone, BorderRight, BorderTop, BorderBottom, BorderHorizontal: // No Left + case BorderNone, BorderLine, BorderRight, BorderTop, BorderBottom, BorderHorizontal: // No Left return false } return true @@ -383,7 +384,7 @@ func (s BorderShape) HasLeft() bool { func (s BorderShape) HasRight() bool { switch s { - case BorderNone, BorderLeft, BorderTop, BorderBottom, BorderHorizontal: // No right + case BorderNone, BorderLine, BorderLeft, BorderTop, BorderBottom, BorderHorizontal: // No right return false } return true @@ -391,7 +392,7 @@ func (s BorderShape) HasRight() bool { func (s BorderShape) HasTop() bool { switch s { - case BorderNone, BorderLeft, BorderRight, BorderBottom, BorderVertical: // No top + case BorderNone, BorderLine, BorderLeft, BorderRight, BorderBottom, BorderVertical: // No top return false } return true @@ -399,7 +400,7 @@ func (s BorderShape) HasTop() bool { func (s BorderShape) HasBottom() bool { switch s { - case BorderNone, BorderLeft, BorderRight, BorderTop, BorderVertical: // No bottom + case BorderNone, BorderLine, BorderLeft, BorderRight, BorderTop, BorderVertical: // No bottom return false } return true |
