Compare commits

..

24 Commits

Author SHA1 Message Date
Junegunn Choi
ff1687744d 0.56.0 2024-10-27 12:03:01 +09:00
junegunn
782c870fb2 Deploying to master from @ junegunn/fzf@71fad63829 🚀 2024-10-27 00:02:14 +00:00
Charlie Vieth
71fad63829 Update fastwalk to v1.0.9 to fix handling of disk root paths on Windows (#4063)
Fixes: https://github.com/junegunn/fzf/issues/4027
2024-10-25 23:57:46 +09:00
Junegunn Choi
d65c6101a8 walker: Do not treat '..' as a hidden entry
Thanks to @LangLangBart for the suggested fix

Fix #4048
2024-10-25 13:50:58 +09:00
junegunn
3c40b1bd51 Deploying to master from @ junegunn/fzf@90a8800bb5 🚀 2024-10-20 00:02:15 +00:00
Junegunn Choi
90a8800bb5 Avoid selecting an outdated merger from cache
We cache a merger for partial input as well, because it is automatically
invalidated as soon as the new data comes in.

However, there was a race condition where a cached merger for a partial
input is used even after the input stream was complete. This commit
fixes the problem.

Fix #4034
2024-10-16 00:45:12 +09:00
Thomas Martitz
97f1dae2d1 Use eval to evaluate $post variable as command. (#4023) 2024-10-15 18:00:27 +09:00
dependabot[bot]
e54ec05709 Bump crate-ci/typos from 1.24.1 to 1.26.0 (#4036)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.24.1 to 1.26.0.
- [Release notes](https://github.com/crate-ci/typos/releases)
- [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crate-ci/typos/compare/v1.24.1...v1.26.0)

---
updated-dependencies:
- dependency-name: crate-ci/typos
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-15 17:38:14 +09:00
Junegunn Choi
a24eb99679 Fix full line background in preview window 2024-10-15 17:35:11 +09:00
dependabot[bot]
ad113d00b7 Bump golang.org/x/term from 0.24.0 to 0.25.0 (#4031)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/term/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-15 16:56:37 +09:00
junegunn
7bd5884d12 Deploying to master from @ junegunn/fzf@c3505858a6 🚀 2024-10-13 00:02:13 +00:00
junegunn
c3505858a6 Deploying to master from @ junegunn/fzf@e76aa37fd4 🚀 2024-10-06 00:02:11 +00:00
Junegunn Choi
e76aa37fd4 Make RuboCop happy 2024-10-01 19:45:53 +09:00
Junegunn Choi
1a32220ca9 Add --gap option to put empty lines between items 2024-10-01 19:15:17 +09:00
Junegunn Choi
4161403a1d --tmux: Export bash functions
Fix #4001
2024-09-29 19:33:42 +09:00
junegunn
53bcdc4294 Deploying to master from @ junegunn/fzf@30a8ef28cd 🚀 2024-09-29 00:02:07 +00:00
Koichi Murase
30a8ef28cd [bash] Fix infinite retry loop for completion setting without "-F func" (#4004) 2024-09-23 19:16:59 +09:00
junegunn
855f90727a Deploying to master from @ junegunn/fzf@2191a44e36 🚀 2024-09-15 00:02:03 +00:00
Junegunn Choi
2191a44e36 Redraw/hide scroll offset when 'info' property is changed 2024-09-12 22:04:19 +09:00
Junegunn Choi
952276dc2d Add 'noinfo' option to hide scroll offset information in preview window
fzf --preview 'seq 1000' --preview-window noinfo

Close #2525
2024-09-12 18:31:14 +09:00
dependabot[bot]
2286edb329 Bump golang.org/x/term from 0.23.0 to 0.24.0 (#3991)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.23.0 to 0.24.0.
- [Commits](https://github.com/golang/term/compare/v0.23.0...v0.24.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 21:25:57 +09:00
junegunn
a0f28583e7 Deploying to master from @ junegunn/fzf@8af0af3400 🚀 2024-09-08 00:02:05 +00:00
Junegunn Choi
8af0af3400 Highlights
Close #3942
2024-09-01 16:20:25 +09:00
junegunn
769e5cbb2d Deploying to master from @ junegunn/fzf@fc69308057 🚀 2024-09-01 00:02:21 +00:00
21 changed files with 218 additions and 78 deletions

View File

@@ -7,4 +7,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: crate-ci/typos@v1.24.1
- uses: crate-ci/typos@v1.26.0

View File

@@ -1,6 +1,24 @@
CHANGELOG
=========
0.56.0
------
- Added `--gap[=N]` option to display empty lines between items.
- This can be useful to visually separate adjacent multi-line items.
```sh
# All bash functions, highlighted
declare -f | perl -0777 -pe 's/^}\n/}\0/gm' |
bat --plain --language bash --color always |
fzf --read0 --ansi --reverse --multi --highlight-line --gap
```
- Or just to make the list easier to read. For single-line items, you probably want to set `--color gutter:-1` as well to hide the gutter.
```sh
fzf --info inline-right --gap --color gutter:-1
```
- Added `noinfo` option to `--preview-window` to hide the scroll indicator in the preview window
- Bug fixes
- Thanks to @LangLangBart, @akinomyoga, and @charlievieth for fixing the bugs
0.55.0
------
_Release highlights: https://junegunn.github.io/fzf/releases/0.55.0/_

File diff suppressed because one or more lines are too long

6
go.mod
View File

@@ -1,13 +1,13 @@
module github.com/junegunn/fzf
require (
github.com/charlievieth/fastwalk v1.0.8
github.com/charlievieth/fastwalk v1.0.9
github.com/gdamore/tcell/v2 v2.7.4
github.com/junegunn/go-shellwords v0.0.0-20240813092932-a62c48c52e97
github.com/mattn/go-isatty v0.0.20
github.com/rivo/uniseg v0.4.7
golang.org/x/sys v0.24.0
golang.org/x/term v0.23.0
golang.org/x/sys v0.26.0
golang.org/x/term v0.25.0
)
require (

12
go.sum
View File

@@ -1,5 +1,5 @@
github.com/charlievieth/fastwalk v1.0.8 h1:uaoH6cAKSk73aK7aKXqs0+bL+J3Txzd3NGH8tRXgHko=
github.com/charlievieth/fastwalk v1.0.8/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
github.com/charlievieth/fastwalk v1.0.9 h1:Odb92AfoReO3oFBfDGT5J+nwgzQPF/gWAw6E6/lkor0=
github.com/charlievieth/fastwalk v1.0.9/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
@@ -36,14 +36,14 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=

View File

@@ -2,7 +2,7 @@
set -u
version=0.55.0
version=0.56.0
auto_completion=
key_bindings=
update_config=2

View File

@@ -1,4 +1,4 @@
$version="0.55.0"
$version="0.56.0"
$fzf_base=Split-Path -Parent $MyInvocation.MyCommand.Definition

View File

@@ -11,7 +11,7 @@ import (
"github.com/junegunn/fzf/src/protector"
)
var version = "0.55"
var version = "0.56"
var revision = "devel"
//go:embed shell/key-bindings.bash

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
THE SOFTWARE.
..
.TH fzf\-tmux 1 "Aug 2024" "fzf 0.55.0" "fzf\-tmux - open fzf in tmux split pane"
.TH fzf\-tmux 1 "Oct 2024" "fzf 0.56.0" "fzf\-tmux - open fzf in tmux split pane"
.SH NAME
fzf\-tmux - open fzf in tmux split pane

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
THE SOFTWARE.
..
.TH fzf 1 "Aug 2024" "fzf 0.55.0" "fzf - a command-line fuzzy finder"
.TH fzf 1 "Oct 2024" "fzf 0.56.0" "fzf - a command-line fuzzy finder"
.SH NAME
fzf - a command-line fuzzy finder
@@ -208,6 +208,9 @@ Indicator for wrapped lines. The default is '↳ ' or '> ' depending on
.B "\-\-no\-multi\-line"
Disable multi-line display of items when using \fB\-\-read0\fR
.TP
.BI "\-\-gap" "[=N]"
Render empty lines between each item
.TP
.B "\-\-keep\-right"
Keep the right end of the line visible when it's too long. Effective only when
the query string is empty.
@@ -756,7 +759,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]hidden][,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES][,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]"
.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)]"
.RS
.B POSITION: (default: right)
@@ -790,6 +793,9 @@ e.g.
* Cyclic scrolling is enabled with \fBcycle\fR flag.
* To hide the scroll offset information on the top right corner, specify
\fBnoinfo\fR.
* To change the style of the border of the preview window, specify one of
the options for \fB\-\-border\fR with \fBborder\-\fR prefix.
e.g. \fBborder\-rounded\fR (border with rounded edges, default),

View File

@@ -264,6 +264,7 @@ _fzf_handle_dynamic_completion() {
# _completion_loader may not have updated completion for the command
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
__fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
__fzf_orig_completion_get_orig_func "$cmd" || ret=1
# Update orig_complete by _fzf_orig_completion entry
[[ $orig_complete =~ ' -F '(_fzf_[^ ]+)' ' ]] &&
@@ -376,7 +377,7 @@ _fzf_complete() {
selected=$(
FZF_DEFAULT_OPTS=$(__fzf_defaults "--reverse" "${FZF_COMPLETION_OPTS-} $str_arg") \
FZF_DEFAULT_OPTS_FILE='' \
__fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | command tr '\n' ' ')
__fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | eval "$post" | command tr '\n' ' ')
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [[ -n "$selected" ]]; then
COMPREPLY=("$selected")

View File

@@ -102,7 +102,7 @@ func (m *Matcher) Loop() {
if !cacheCleared {
if count == prevCount {
// Look up mergerCache
if cached, found := m.mergerCache[patternString]; found {
if cached, found := m.mergerCache[patternString]; found && cached.final == request.final {
merger = cached
}
} else {

View File

@@ -56,6 +56,7 @@ Usage: fzf [options]
--wrap Enable line wrap
--wrap-sign=STR Indicator for wrapped lines
--no-multi-line Disable multi-line display of items when using --read0
--gap[=N] Render empty lines between each item
--keep-right Keep the right end of the line visible on overflow
--scroll-off=LINES Number of screen lines to keep above or below when
scrolling to the top or to the bottom (default: 0)
@@ -120,8 +121,8 @@ Usage: fzf [options]
--preview=COMMAND Command to preview highlighted line ({})
--preview-window=OPT Preview window layout (default: right:50%)
[up|down|left|right][,SIZE[%]]
[,[no]wrap][,[no]cycle][,[no]follow][,[no]hidden]
[,border-BORDER_OPT]
[,[no]wrap][,[no]cycle][,[no]follow][,[no]info]
[,[no]hidden][,border-BORDER_OPT]
[,+SCROLL[OFFSETS][/DENOM]][,~HEADER_LINES]
[,default][,<SIZE_THRESHOLD(ALTERNATIVE_LAYOUT)]
--preview-label=LABEL
@@ -271,6 +272,7 @@ type previewOpts struct {
wrap bool
cycle bool
follow bool
info bool
border tui.BorderShape
headerLines int
threshold int
@@ -386,7 +388,7 @@ func (a previewOpts) sameLayout(b previewOpts) bool {
}
func (a previewOpts) sameContentLayout(b previewOpts) bool {
return a.wrap == b.wrap && a.headerLines == b.headerLines
return a.wrap == b.wrap && a.headerLines == b.headerLines && a.info == b.info
}
func firstLine(s string) string {
@@ -472,6 +474,7 @@ type Options struct {
Header []string
HeaderLines int
HeaderFirst bool
Gap int
Ellipsis *string
Scrollbar *string
Margin [4]sizeSpec
@@ -508,7 +511,7 @@ func filterNonEmpty(input []string) []string {
}
func defaultPreviewOpts(command string) previewOpts {
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, tui.DefaultBorderShape, 0, 0, nil}
return previewOpts{command, posRight, sizeSpec{50, true}, "", false, false, false, false, true, tui.DefaultBorderShape, 0, 0, nil}
}
func defaultOptions() *Options {
@@ -578,6 +581,7 @@ func defaultOptions() *Options {
Header: make([]string, 0),
HeaderLines: 0,
HeaderFirst: false,
Gap: 0,
Ellipsis: nil,
Scrollbar: nil,
Margin: defaultMargin(),
@@ -1789,6 +1793,10 @@ func parsePreviewWindowImpl(opts *previewOpts, input string) error {
opts.follow = true
case "nofollow":
opts.follow = false
case "info":
opts.info = true
case "noinfo":
opts.info = false
default:
if headerRegex.MatchString(token) {
if opts.headerLines, err = atoi(token[1:]); err != nil {
@@ -2338,6 +2346,12 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
opts.HeaderFirst = true
case "--no-header-first":
opts.HeaderFirst = false
case "--gap":
if opts.Gap, err = optionalNumeric(allArgs, &i, 1); err != nil {
return err
}
case "--no-gap":
opts.Gap = 0
case "--ellipsis":
str, err := nextString(allArgs, &i, "ellipsis string required")
if err != nil {
@@ -2625,6 +2639,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
if opts.HeaderLines, err = atoi(value); err != nil {
return err
}
} else if match, value := optString(arg, "--gap="); match {
if opts.Gap, err = atoi(value); err != nil {
return err
}
} else if match, value := optString(arg, "--ellipsis="); match {
str := firstLine(value)
opts.Ellipsis = &str

View File

@@ -9,6 +9,7 @@ import (
"os/exec"
"os/signal"
"path/filepath"
"regexp"
"strings"
"time"
@@ -32,7 +33,7 @@ func fifo(name string) (string, error) {
return output, nil
}
func runProxy(commandPrefix string, cmdBuilder func(temp string) *exec.Cmd, opts *Options, withExports bool) (int, error) {
func runProxy(commandPrefix string, cmdBuilder func(temp string, needBash bool) (*exec.Cmd, error), opts *Options, withExports bool) (int, error) {
output, err := fifo("proxy-output")
if err != nil {
return ExitError, err
@@ -92,17 +93,28 @@ func runProxy(commandPrefix string, cmdBuilder func(temp string) *exec.Cmd, opts
// To ensure that the options are processed by a POSIX-compliant shell,
// we need to write the command to a temporary file and execute it with sh.
var exports []string
needBash := false
if withExports {
exports = os.Environ()
for idx, pairStr := range exports {
validIdentifier := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
for _, pairStr := range os.Environ() {
pair := strings.SplitN(pairStr, "=", 2)
exports[idx] = fmt.Sprintf("export %s=%s", pair[0], escapeSingleQuote(pair[1]))
if validIdentifier.MatchString(pair[0]) {
exports = append(exports, fmt.Sprintf("export %s=%s", pair[0], escapeSingleQuote(pair[1])))
} else if strings.HasPrefix(pair[0], "BASH_FUNC_") && strings.HasSuffix(pair[0], "%%") {
name := pair[0][10 : len(pair[0])-2]
exports = append(exports, name+pair[1])
exports = append(exports, "export -f "+name)
needBash = true
}
}
}
temp := WriteTemporaryFile(append(exports, command), "\n")
defer os.Remove(temp)
cmd := cmdBuilder(temp)
cmd, err := cmdBuilder(temp, needBash)
if err != nil {
return ExitError, err
}
cmd.Stderr = os.Stderr
intChan := make(chan os.Signal, 1)
defer close(intChan)

View File

@@ -9,7 +9,10 @@ import (
"golang.org/x/sys/unix"
)
func sh() (string, error) {
func sh(bash bool) (string, error) {
if bash {
return "bash", nil
}
return "sh", nil
}

View File

@@ -13,12 +13,16 @@ import (
var shPath atomic.Value
func sh() (string, error) {
func sh(bash bool) (string, error) {
if cached := shPath.Load(); cached != nil {
return cached.(string), nil
}
cmd := exec.Command("cygpath", "-w", "/usr/bin/sh")
name := "sh"
if bash {
name = "bash"
}
cmd := exec.Command("cygpath", "-w", "/usr/bin/"+name)
bytes, err := cmd.Output()
if err != nil {
return "", err
@@ -31,7 +35,7 @@ func sh() (string, error) {
func mkfifo(path string, mode uint32) (string, error) {
m := strconv.FormatUint(uint64(mode), 8)
sh, err := sh()
sh, err := sh(false)
if err != nil {
return path, err
}
@@ -43,7 +47,7 @@ func mkfifo(path string, mode uint32) (string, error) {
}
func withOutputPipe(output string, task func(io.ReadCloser)) error {
sh, err := sh()
sh, err := sh(false)
if err != nil {
return err
}
@@ -62,7 +66,7 @@ func withOutputPipe(output string, task func(io.ReadCloser)) error {
}
func withInputPipe(input string, task func(io.WriteCloser)) error {
sh, err := sh()
sh, err := sh(false)
if err != nil {
return err
}

View File

@@ -265,7 +265,7 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
isDir := de.IsDir()
if isDir || opts.follow && isSymlinkToDir(path, de) {
base := filepath.Base(path)
if !opts.hidden && base[0] == '.' {
if !opts.hidden && base[0] == '.' && base != ".." {
return filepath.SkipDir
}
for _, ignore := range ignores {

View File

@@ -245,6 +245,7 @@ type Terminal struct {
hscroll bool
hscrollOff int
scrollOff int
gap int
wordRubout string
wordNext string
cx int
@@ -825,6 +826,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
headerVisible: true,
headerFirst: opts.HeaderFirst,
headerLines: opts.HeaderLines,
gap: opts.Gap,
header: []string{},
header0: opts.Header,
ansi: opts.Ansi,
@@ -1136,15 +1138,23 @@ func (t *Terminal) wrapCols() int {
return util.Max(t.window.Width()-(t.pointerLen+t.markerLen+1), 1)
}
// Number of lines the item takes including the gap
func (t *Terminal) numItemLines(item *Item, atMost int) (int, bool) {
var numLines int
if !t.wrap && !t.multiLine {
return 1, false
numLines = 1 + t.gap
return numLines, numLines > atMost
}
var overflow bool
if !t.wrap && t.multiLine {
return item.text.NumLines(atMost)
numLines, overflow = item.text.NumLines(atMost)
} else {
var lines [][]rune
lines, overflow = item.text.Lines(t.multiLine, atMost, t.wrapCols(), t.wrapSignWidth, t.tabstop)
numLines = len(lines)
}
lines, overflow := item.text.Lines(t.multiLine, atMost, t.wrapCols(), t.wrapSignWidth, t.tabstop)
return len(lines), overflow
numLines += t.gap
return numLines, overflow || numLines > atMost
}
func (t *Terminal) itemLines(item *Item, atMost int) ([][]rune, bool) {
@@ -2050,6 +2060,21 @@ func (t *Terminal) printHeader() {
t.wrap = wrap
}
func (t *Terminal) canSpanMultiLines() bool {
return t.multiLine || t.wrap || t.gap > 0
}
func (t *Terminal) renderEmptyLine(line int, barRange [2]int) {
t.move(line, 0, true)
t.markEmptyLine(line)
// If the screen is not filled with the list in non-multi-line mode,
// scrollbar is not visible at all. But in multi-line mode, we may need
// to redraw the scrollbar character at the end.
if t.canSpanMultiLines() {
t.prevLines[line].hasBar = t.printBar(line, true, barRange)
}
}
func (t *Terminal) printList() {
t.constrain()
barLength, barStart := t.getScrollbar()
@@ -2070,14 +2095,7 @@ func (t *Terminal) printList() {
item := t.merger.Get(itemCount + t.offset)
line = t.printItem(item, line, maxy, itemCount, itemCount == t.cy-t.offset, barRange)
} else if !t.prevLines[line].empty {
t.move(line, 0, true)
t.markEmptyLine(line)
// If the screen is not filled with the list in non-multi-line mode,
// scrollbar is not visible at all. But in multi-line mode, we may need
// to redraw the scrollbar character at the end.
if t.multiLine || t.wrap {
t.prevLines[line].hasBar = t.printBar(line, true, barRange)
}
t.renderEmptyLine(line, barRange)
}
}
}
@@ -2125,9 +2143,6 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
prevLine.queryLen == newLine.queryLen &&
prevLine.result == newLine.result {
t.prevLines[line].hasBar = printBar(line, false)
if !t.multiLine && !t.wrap {
return line
}
return line + numLines - 1
}
@@ -2214,6 +2229,10 @@ func (t *Terminal) printItem(result Result, line int, maxLine int, index int, cu
}
finalLineNum = t.printHighlighted(result, base, match, false, true, line, maxLine, forceRedraw, preTask, postTask)
}
for i := 0; i < t.gap && finalLineNum < maxLine; i++ {
finalLineNum++
t.renderEmptyLine(finalLineNum, barRange)
}
return finalLineNum
}
@@ -2275,7 +2294,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
allOffsets := result.colorOffsets(charOffsets, t.theme, colBase, colMatch, current)
maxLines := 1
if t.multiLine || t.wrap {
if t.canSpanMultiLines() {
maxLines = maxLineNum - lineNum + 1
}
lines, overflow := t.itemLines(item, maxLines)
@@ -2285,7 +2304,7 @@ func (t *Terminal) printHighlighted(result Result, colBase tui.ColorPair, colMat
topCutoff := false
skipLines := 0
wrapped := false
if t.multiLine || t.wrap {
if t.canSpanMultiLines() {
// Cut off the upper lines in the 'default' layout
if t.layout == layoutDefault && !current && maxLines == numItemLines && overflow {
lines, _ = t.itemLines(item, math.MaxInt)
@@ -2507,7 +2526,7 @@ func (t *Terminal) renderPreviewSpinner() {
spin := t.previewer.spinner
if len(spin) > 0 || t.previewer.scrollable {
maxWidth := t.pwindow.Width()
if !t.previewer.scrollable {
if !t.previewer.scrollable || !t.previewOpts.info {
if maxWidth > 0 {
t.pwindow.Move(0, maxWidth-1)
t.pwindow.CPrint(tui.ColPreviewSpinner, spin)
@@ -2702,11 +2721,15 @@ Loop:
url = nil
t.pwindow.LinkEnd()
}
if ansi != nil {
lbg = ansi.lbg
} else {
lbg = -1
}
str, width := t.processTabs(trimmed, prefixWidth)
if width > prefixWidth {
prefixWidth = width
if t.theme.Colored && ansi != nil && ansi.colored() {
lbg = ansi.lbg
fillRet = t.pwindow.CFill(ansi.fg, ansi.bg, ansi.attr, str)
} else {
fillRet = t.pwindow.CFill(tui.ColPreview.Fg(), tui.ColPreview.Bg(), tui.AttrRegular, str)
@@ -2728,7 +2751,7 @@ Loop:
if unchanged && lineNo == 0 {
break
}
if lbg >= 0 {
if t.theme.Colored && lbg >= 0 {
fillRet = t.pwindow.CFill(-1, lbg, tui.AttrRegular,
strings.Repeat(" ", t.pwindow.Width()-t.pwindow.X())+"\n")
} else {
@@ -4875,7 +4898,7 @@ func (t *Terminal) constrain() {
for tries := 0; tries < maxLines; tries++ {
numItems := maxLines
// How many items can be fit on screen including the current item?
if (t.multiLine || t.wrap) && t.merger.Length() > 0 {
if t.canSpanMultiLines() && t.merger.Length() > 0 {
numItemsFound := 0
linesSum := 0
@@ -4930,12 +4953,12 @@ func (t *Terminal) constrain() {
for {
prevOffset := newOffset
numItems := t.merger.Length()
itemLines := 1
if (t.multiLine || t.wrap) && t.cy < numItems {
itemLines := 1 + t.gap
if t.canSpanMultiLines() && t.cy < numItems {
itemLines, _ = t.numItemLines(t.merger.Get(t.cy).item, maxLines)
}
linesBefore := t.cy - newOffset
if t.multiLine || t.wrap {
if t.canSpanMultiLines() {
linesBefore = 0
for i := newOffset; i < t.cy && i < numItems; i++ {
lines, _ := t.numItemLines(t.merger.Get(i).item, maxLines-linesBefore-itemLines)

View File

@@ -49,9 +49,12 @@ func runTmux(args []string, opts *Options) (int, error) {
tmuxArgs = append(tmuxArgs, "-w"+opts.Tmux.width.String())
tmuxArgs = append(tmuxArgs, "-h"+opts.Tmux.height.String())
return runProxy(argStr, func(temp string) *exec.Cmd {
sh, _ := sh()
return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, err := sh(needBash)
if err != nil {
return nil, err
}
tmuxArgs = append(tmuxArgs, sh, temp)
return exec.Command("tmux", tmuxArgs...)
return exec.Command("tmux", tmuxArgs...), nil
}, opts, true)
}

View File

@@ -44,11 +44,6 @@ func needWinpty(opts *Options) bool {
}
func runWinpty(args []string, opts *Options) (int, error) {
sh, err := sh()
if err != nil {
return ExitError, err
}
argStr := escapeSingleQuote(args[0])
for _, arg := range args[1:] {
argStr += " " + escapeSingleQuote(arg)
@@ -56,20 +51,30 @@ func runWinpty(args []string, opts *Options) (int, error) {
argStr += ` --no-winpty`
if isMintty345() {
return runProxy(argStr, func(temp string) *exec.Cmd {
return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, err := sh(needBash)
if err != nil {
return nil, err
}
cmd := exec.Command(sh, temp)
cmd.Env = append(os.Environ(), "MSYS=enable_pcon")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
return cmd, nil
}, opts, false)
}
return runProxy(argStr, func(temp string) *exec.Cmd {
return runProxy(argStr, func(temp string, needBash bool) (*exec.Cmd, error) {
sh, err := sh(needBash)
if err != nil {
return nil, err
}
cmd := exec.Command(sh, "-c", fmt.Sprintf(`winpty < /dev/tty > /dev/tty -- sh %q`, temp))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd
return cmd, nil
}, opts, false)
}

View File

@@ -3378,6 +3378,54 @@ class TestGoFZF < TestBase
assert_equal expected, result
end
end
def test_preview_window_noinfo
# │ 1 ││
tmux.send_keys %(#{FZF} --preview 'seq 1000' --preview-window top,noinfo --scrollbar --bind space:change-preview-window:info), :Enter
tmux.until do |lines|
assert lines[1]&.start_with?('│ 1')
assert lines[1]&.end_with?(' ││')
end
tmux.send_keys :Space
tmux.until do |lines|
assert lines[1]&.start_with?('│ 1')
assert lines[1]&.end_with?('1000││')
end
end
def test_gap
tmux.send_keys %(seq 100 | #{FZF} --gap --border --reverse), :Enter
block = <<~BLOCK
>
100/100
> 1
2
3
4
BLOCK
tmux.until { assert_block(block, _1) }
end
def test_gap_2
tmux.send_keys %(seq 100 | #{FZF} --gap=2 --border --reverse), :Enter
block = <<~BLOCK
>
100/100
> 1
2
3
BLOCK
tmux.until { assert_block(block, _1) }
end
end
module TestShell