Allow walking multiple root directories (#4109)

Co-authored-by: Martin Sabathier <martin.sabathier.ext@corys.fr>
Co-authored-by: Junegunn Choi <junegunn.c@gmail.com>
This commit is contained in:
msabathier 2024-11-25 11:25:30 +01:00 committed by GitHub
parent ac3e24c99c
commit bee80a730f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 11 deletions

View File

@ -1005,8 +1005,8 @@ Determines the behavior of the built-in directory walker that is used when
.br .br
.TP .TP
.B "\-\-walker\-root=DIR" .B "\-\-walker\-root=DIR [...]"
The root directory from which to start the built-in directory walker. List of directory names to start the built-in directory walker.
The default value is the current working directory. The default value is the current working directory.
.TP .TP

View File

@ -145,7 +145,7 @@ Usage: fzf [options]
Directory traversal (Only used when $FZF_DEFAULT_COMMAND is not set) Directory traversal (Only used when $FZF_DEFAULT_COMMAND is not set)
--walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden) --walker=OPTS [file][,dir][,follow][,hidden] (default: file,follow,hidden)
--walker-root=DIR Root directory from which to start walker (default: .) --walker-root=DIR [...] List of directories to walk (default: .)
--walker-skip=DIRS Comma-separated list of directory names to skip --walker-skip=DIRS Comma-separated list of directory names to skip
(default: .git,node_modules) (default: .git,node_modules)
@ -490,7 +490,7 @@ type Options struct {
Unsafe bool Unsafe bool
ClearOnExit bool ClearOnExit bool
WalkerOpts walkerOpts WalkerOpts walkerOpts
WalkerRoot string WalkerRoot []string
WalkerSkip []string WalkerSkip []string
Version bool Version bool
Help bool Help bool
@ -594,7 +594,7 @@ func defaultOptions() *Options {
Unsafe: false, Unsafe: false,
ClearOnExit: true, ClearOnExit: true,
WalkerOpts: walkerOpts{file: true, hidden: true, follow: true}, WalkerOpts: walkerOpts{file: true, hidden: true, follow: true},
WalkerRoot: ".", WalkerRoot: []string{"."},
WalkerSkip: []string{".git", "node_modules"}, WalkerSkip: []string{".git", "node_modules"},
Help: false, Help: false,
Version: false} Version: false}
@ -626,6 +626,28 @@ func optionalNextString(args []string, i *int) (bool, string) {
return false, "" return false, ""
} }
func isDir(path string) bool {
stat, err := os.Stat(path)
return err == nil && stat.IsDir()
}
func nextDirs(args []string, i *int) ([]string, error) {
dirs := []string{}
for *i < len(args)-1 {
arg := args[*i+1]
if isDir(arg) {
dirs = append(dirs, arg)
*i++
} else {
break
}
}
if len(dirs) == 0 {
return nil, errors.New("no directory specified")
}
return dirs, nil
}
func atoi(str string) (int, error) { func atoi(str string) (int, error) {
num, err := strconv.Atoi(str) num, err := strconv.Atoi(str)
if err != nil { if err != nil {
@ -2487,7 +2509,7 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
return err return err
} }
case "--walker-root": case "--walker-root":
if opts.WalkerRoot, err = nextString(allArgs, &i, "directory required"); err != nil { if opts.WalkerRoot, err = nextDirs(allArgs, &i); err != nil {
return err return err
} }
case "--walker-skip": case "--walker-skip":
@ -2685,7 +2707,11 @@ func parseOptions(index *int, opts *Options, allArgs []string) error {
return err return err
} }
} else if match, value := optString(arg, "--walker-root="); match { } else if match, value := optString(arg, "--walker-root="); match {
opts.WalkerRoot = value if !isDir(value) {
return errors.New("not a directory: " + value)
}
dirs, _ := nextDirs(allArgs, &i)
opts.WalkerRoot = append([]string{value}, dirs...)
} else if match, value := optString(arg, "--walker-skip="); match { } else if match, value := optString(arg, "--walker-skip="); match {
opts.WalkerSkip = filterNonEmpty(strings.Split(value, ",")) opts.WalkerSkip = filterNonEmpty(strings.Split(value, ","))
} else if match, value := optString(arg, "--hscroll-off="); match { } else if match, value := optString(arg, "--hscroll-off="); match {

View File

@ -120,7 +120,7 @@ func (r *Reader) readChannel(inputChan chan string) bool {
} }
// ReadSource reads data from the default command or from standard input // ReadSource reads data from the default command or from standard input
func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, initCmd string, initEnv []string, readyChan chan bool) { func (r *Reader) ReadSource(inputChan chan string, roots []string, opts walkerOpts, ignores []string, initCmd string, initEnv []string, readyChan chan bool) {
r.startEventPoller() r.startEventPoller()
var success bool var success bool
signalReady := func() { signalReady := func() {
@ -137,7 +137,7 @@ func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts,
cmd := os.Getenv("FZF_DEFAULT_COMMAND") cmd := os.Getenv("FZF_DEFAULT_COMMAND")
if len(cmd) == 0 { if len(cmd) == 0 {
signalReady() signalReady()
success = r.readFiles(root, opts, ignores) success = r.readFiles(roots, opts, ignores)
} else { } else {
success = r.readFromCommand(cmd, initEnv, signalReady) success = r.readFromCommand(cmd, initEnv, signalReady)
} }
@ -265,7 +265,7 @@ func trimPath(path string) string {
return byteString(bytes) return byteString(bytes)
} }
func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool { func (r *Reader) readFiles(roots []string, opts walkerOpts, ignores []string) bool {
conf := fastwalk.Config{ conf := fastwalk.Config{
Follow: opts.follow, Follow: opts.follow,
// Use forward slashes when running a Windows binary under WSL or MSYS // Use forward slashes when running a Windows binary under WSL or MSYS
@ -301,7 +301,11 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
} }
return nil return nil
} }
return fastwalk.Walk(&conf, root, fn) == nil noerr := true
for _, root := range roots {
noerr = noerr && (fastwalk.Walk(&conf, root, fn) == nil)
}
return noerr
} }
func (r *Reader) readFromCommand(command string, environ []string, signalReady func()) bool { func (r *Reader) readFromCommand(command string, environ []string, signalReady func()) bool {