From 5737da5897fce588178ded3e4e28425eef8a3611 Mon Sep 17 00:00:00 2001 From: Michal Trybus Date: Mon, 17 Mar 2025 18:53:53 +0100 Subject: [PATCH] Support negative preview window size Negative size adjusts the preview window to achieve desired item list size --- man/man1/fzf.1 | 3 +++ src/options.go | 13 +++++-------- src/options_test.go | 9 +++++++++ src/terminal.go | 8 +++++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index f4c8322cb57..819f881fff1 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -878,6 +878,9 @@ default until \fBtoggle\-preview\fR action is triggered. * If size is given as 0, preview window will not be visible, but fzf will still execute the command in the background. +* If size is -N (negative and % is not used), the preview window will occupy the +amount of space for which the entry list's size is N. + * Long lines are truncated by default. Line wrap can be enabled with \fBwrap\fR flag. diff --git a/src/options.go b/src/options.go index 1f431c45d51..4cfb0a20467 100644 --- a/src/options.go +++ b/src/options.go @@ -340,7 +340,7 @@ type previewOpts struct { } func (o *previewOpts) Visible() bool { - return o.size.size > 0 || o.alternative != nil && o.alternative.size.size > 0 + return o.size.size != 0 || o.alternative != nil && o.alternative.size.size > 0 } func (o *previewOpts) Toggle() { @@ -468,7 +468,7 @@ func parseLabelPosition(opts *labelOpts, arg string) error { } func (a previewOpts) aboveOrBelow() bool { - return a.size.size > 0 && (a.position == posUp || a.position == posDown) + return a.size.size != 0 && (a.position == posUp || a.position == posDown) } type previewOptsCompare int @@ -1878,14 +1878,14 @@ func parseSize(str string, maxPercent float64, label string) (sizeSpec, error) { } if val < 0 { - return spec, errors.New(label + " must be non-negative") + return spec, errors.New(label + " (with %) must be non-negative") } if val > maxPercent { return spec, fmt.Errorf("%s too large (max: %d%%)", label, int(maxPercent)) } } else { if strings.Contains(str, ".") { - return spec, errors.New(label + " (without %) must be a non-negative integer") + return spec, errors.New(label + " (without %) must be an integer") } i, err := atoi(str) @@ -1893,9 +1893,6 @@ func parseSize(str string, maxPercent float64, label string) (sizeSpec, error) { return spec, err } val = float64(i) - if val < 0 { - return spec, errors.New(label + " must be non-negative") - } } return sizeSpec{val, percent}, nil } @@ -1969,7 +1966,7 @@ func parsePreviewWindow(opts *previewOpts, input string) error { func parsePreviewWindowImpl(opts *previewOpts, input string) error { var err error tokenRegex := regexp.MustCompile(`[:,]*(<([1-9][0-9]*)\(([^)<]+)\)|[^,:]+)`) - sizeRegex := regexp.MustCompile("^[0-9]+%?$") + sizeRegex := regexp.MustCompile("^-?[0-9]+%?$") offsetRegex := regexp.MustCompile(`^(\+{(-?[0-9]+|n)})?([+-][0-9]+)*(-?/[1-9][0-9]*)?$`) headerRegex := regexp.MustCompile("^~(0|[1-9][0-9]*)$") tokens := tokenRegex.FindAllStringSubmatch(input, -1) diff --git a/src/options_test.go b/src/options_test.go index 5c9a789a274..2eb0188b179 100644 --- a/src/options_test.go +++ b/src/options_test.go @@ -434,6 +434,15 @@ func TestPreviewOpts(t *testing.T) { opts.Preview.size.size == 15) { t.Error(opts.Preview) } + opts = optsFor("--preview-window=up:-15:wrap:hidden") + if !(opts.Preview.command == "" && + opts.Preview.hidden == true && + opts.Preview.wrap == true && + opts.Preview.position == posUp && + opts.Preview.size.percent == false && + opts.Preview.size.size == -15) { + t.Error(opts.Preview) + } opts = optsFor("--preview=foo", "--preview-window=up", "--preview-window=default:70%") if !(opts.Preview.command == "foo" && opts.Preview.position == posRight && diff --git a/src/terminal.go b/src/terminal.go index 1d89d235308..0b6f7a3c30d 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -1622,7 +1622,13 @@ func calculateSize(base int, size sizeSpec, occupied int, minSize int) int { if size.percent { return util.Constrain(int(float64(base)*0.01*size.size), minSize, max) } - return util.Constrain(int(size.size)+minSize-1, minSize, max) + var unconstrained int + if size.size < 0 { + unconstrained = int(float64(base) + size.size) - occupied + 1 + } else { + unconstrained = int(size.size) + minSize - 1 + } + return util.Constrain(unconstrained, minSize, max) } func (t *Terminal) minPreviewSize(opts *previewOpts) (int, int) {