From bf515a3d3251c54d04cd8e2e9f422cdd3f789fb8 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 29 Jun 2024 17:11:09 +0900 Subject: [PATCH] Add --walker-path-sep=CHAR to use a different path separator This is needed when you run a Windows binary on WSL or zsh on Windows where forward slashes are expected. export FZF_DEFAULT_OPTS='--walker-path-sep /' Close #3859 --- CHANGELOG.md | 2 ++ man/man1/fzf.1 | 4 ++++ src/core.go | 4 ++-- src/options.go | 23 +++++++++++++++++++++++ src/reader.go | 17 +++++++++++++---- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31312ddd..4817c76f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ CHANGELOG --bind 'start:reload:sleep 1; ps -ef' \ --bind 'load:change-header:Loaded!' ``` +- Added `--walker-path-sep=CHAR` option to change the default path separator used by the built-in walker + - Needed when running a Windows binary on WSL or zsh on Windows where forward slashes are expected - Fixed mouse support on Windows - Fixed crash when using `--tiebreak=end` with very long items - zsh 5.0 compatibility (thanks to @LangLangBart) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index 14761438..873325dd 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -1008,6 +1008,10 @@ The default value is the current working directory. Comma-separated list of directory names to skip during the directory walk. The default value is \fB.git,node_modules\fR. +.TP +.B "\-\-walker\-path\-sep=CHAR" +Path separator to use (default: '/' on Unix, '\\' on Windows) + .SS Shell integration .TP .B "\-\-bash" diff --git a/src/core.go b/src/core.go index caada27c..1dc642f4 100644 --- a/src/core.go +++ b/src/core.go @@ -159,7 +159,7 @@ func Run(opts *Options) (int, error) { // reload or reload-sync action is bound to 'start' event, no need to start the reader eventBox.Set(EvtReadNone, nil) } else { - go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip) + go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, opts.WalkerSep) } } @@ -212,7 +212,7 @@ func Run(opts *Options) (int, error) { } return false }, eventBox, executor, opts.ReadZero, false) - reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip) + reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, opts.WalkerSep) } else { eventBox.Unwatch(EvtReadNew) eventBox.WaitFor(EvtReadFin) diff --git a/src/options.go b/src/options.go index d32daf10..58b5782b 100644 --- a/src/options.go +++ b/src/options.go @@ -147,6 +147,7 @@ Usage: fzf [options] --walker-root=DIR Root directory from which to start walker (default: .) --walker-skip=DIRS Comma-separated list of directory names to skip (default: .git,node_modules) + --walker-path-sep=CHAR Path separator to use (default: / on Unix, \ on Windows) Shell integration --bash Print script to set up Bash shell integration @@ -489,6 +490,7 @@ type Options struct { WalkerOpts walkerOpts WalkerRoot string WalkerSkip []string + WalkerSep byte Version bool Help bool CPUProfile string @@ -592,6 +594,7 @@ func defaultOptions() *Options { WalkerOpts: walkerOpts{file: true, hidden: true, follow: true}, WalkerRoot: ".", WalkerSkip: []string{".git", "node_modules"}, + WalkerSep: os.PathSeparator, Help: false, Version: false} } @@ -1904,6 +1907,14 @@ func parseMarkerMultiLine(str string) (*[3]string, error) { return &result, nil } +func parsePathSep(str string) (byte, error) { + bytes := []byte(str) + if len(bytes) != 1 { + return os.PathSeparator, errors.New("invalid path separator (expected: single-byte character)") + } + return bytes[0], nil +} + func parseOptions(index *int, opts *Options, allArgs []string) error { var err error var historyMax int @@ -2479,6 +2490,14 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { return err } opts.WalkerSkip = filterNonEmpty(strings.Split(str, ",")) + case "--walker-path-sep": + str, err := nextString(allArgs, &i, "path separator required") + if err != nil { + return err + } + if opts.WalkerSep, err = parsePathSep(str); err != nil { + return err + } case "--profile-cpu": if opts.CPUProfile, err = nextString(allArgs, &i, "file path required: cpu"); err != nil { return err @@ -2666,6 +2685,10 @@ func parseOptions(index *int, opts *Options, allArgs []string) error { opts.WalkerRoot = value } else if match, value := optString(arg, "--walker-skip="); match { opts.WalkerSkip = filterNonEmpty(strings.Split(value, ",")) + } else if match, value := optString(arg, "--walker-path-sep="); match { + if opts.WalkerSep, err = parsePathSep(value); err != nil { + return err + } } else if match, value := optString(arg, "--hscroll-off="); match { if opts.HscrollOff, err = atoi(value); err != nil { return err diff --git a/src/reader.go b/src/reader.go index f39f47f5..9d4fe160 100644 --- a/src/reader.go +++ b/src/reader.go @@ -111,7 +111,7 @@ func (r *Reader) readChannel(inputChan chan string) bool { } // ReadSource reads data from the default command or from standard input -func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string) { +func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, sep byte) { r.startEventPoller() var success bool if inputChan != nil { @@ -119,7 +119,7 @@ func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, } else if util.IsTty(os.Stdin) { cmd := os.Getenv("FZF_DEFAULT_COMMAND") if len(cmd) == 0 { - success = r.readFiles(root, opts, ignores) + success = r.readFiles(root, opts, ignores, sep) } else { // We can't export FZF_* environment variables to the default command success = r.readFromCommand(cmd, nil) @@ -233,9 +233,10 @@ func isSymlinkToDir(path string, de os.DirEntry) bool { return false } -func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool { +func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string, sep byte) bool { r.killed = false conf := fastwalk.Config{Follow: opts.follow} + replaceSep := sep != os.PathSeparator fn := func(path string, de os.DirEntry, err error) error { if err != nil { return nil @@ -254,7 +255,15 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool } } } - if ((opts.file && !isDir) || (opts.dir && isDir)) && r.pusher([]byte(path)) { + bytes := stringBytes(path) + if replaceSep { + for i, b := range bytes { + if b == os.PathSeparator { + bytes[i] = sep + } + } + } + if ((opts.file && !isDir) || (opts.dir && isDir)) && r.pusher(bytes) { atomic.StoreInt32(&r.event, int32(EvtReadNew)) } }