Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prefer current directory #360

Closed
3 of 9 tasks
pushcx opened this issue Apr 27, 2017 · 7 comments
Closed
3 of 9 tasks

Prefer current directory #360

pushcx opened this issue Apr 27, 2017 · 7 comments

Comments

@pushcx
Copy link

pushcx commented Apr 27, 2017

  • Category
    • Question
    • Bug
    • Suggestion
  • OS
    • Linux
    • macOS
    • Windows
    • Etc.
  • Vim
    • Vim
    • Neovim

Is it possible to make fzf prefer files in the current directory like ctrl-p does? I'm working on a Django project which has a couple dozen "apps". Each app is a directory, and they all have an admin.py, models.py, tasks.py, etc. Because they often interact, the standard way to use vim is to launch it in the parent directory so you can fuzzy-find down into all the children.

However, if the current buffer is foo/models.py and I fuzzy-find for adm, about 95% of the time I want foo/admin.py. Unfortunately, I always get the admin.py of the app with the shortest name. I never noticed ctrl-p was doing this, it "just worked" - when I wanted another dir, I could navigate up/down to it.

I can see this is the --tiebreak defaulting to length, but none of the other options solve this. I know I could hack together vimscript to search only the directory of the current file, but that'd break the 5% of the time I do actually want the file in another directory.

I've read up and down the man page, but I can't see any way to provide this hint. Is there something I'm missing?

@junegunn
Copy link
Owner

Is it possible to make fzf prefer files in the current directory like ctrl-p does?

"current directory" usually means "current working directory", but it looks like you are referring to the directory of the current file, right? Anyway, I don't know much about ctrlp, but it doesn't look like it behaves that way. Do you have custom settings?

Since fzf is a general-purpose filter, it does not know the context of the search, what each entry means. So there's no way to tell it to prefer files in a certain directory. One can rearrange the input so that those files appear first and set --tiebreak=index.

Alternatively I'd suggest that you define a separate command or mapping for starting :Files command with a directory argument like so

nnoremap <silent> <Leader><Leader> :Files <C-R>=expand('%:h')<CR><CR>

@jonhoo
Copy link

jonhoo commented Sep 17, 2018

@junegunn's solution sadly doesn't really solve the problem: it just gives a limited-scope fuzzy-find. Specifically, like @pushcx, I don't want to constrain the search to the directory of the current file (I may indeed want a file that's located elsewhere), I just want to prefer locations that are "closer" to some target directory. Now, @junegunn is right that this is probably not something fzf.vim should do directly; it should instead ensure that the file list that gets input to fzf is sorted using something that sorts by proximity to a given path (e.g., by piping the output of FZF_DEFAULT_COMMAND through a proximity-sort-program).

I do think there's a decent argument for writing such a program and then having a straightforward way to enable its use by fzf.vim though. It leads to a much better developer experience, especially in large projects. It could even be that this should be implemented as a tiebreak option for fzf like --tiebreak proximity=/path, though I think that's an implementation detail.

jonhoo added a commit to jonhoo/fzf that referenced this issue Sep 17, 2018
This patch adds a new type of `tiebreak`: `proximity=path`. It works by
ranking equally-scored candidates by their proximity to the given path
argument, where proximity is defined as the number of shared leading
path segments when split by the OS path separator.

Consider the following simple file hierarchy:

    test.txt
    bar/test.txt
    bar/main.txt
    misc/test.txt

Where a user is currently in the context of `bar/main.txt`. This could
be for a number of reasons, such as it being the file that is currently
open in their editor (see junegunn/fzf.vim#360 and
junegunn/fzf.vim#492). They now want to open `bar/test.txt`. If they
invoke `fzf` and type, well, any search, the `by_length` scoring will
make the `test.txt` file in the root the "best" candidate, as would
`by_start`. `by_end` would propose `misc/test.txt`. `by_score` would
also likely suggest `test.txt` in the root. None of these take into
account the user's current location.

With this patch, the user can invoke `fzf --tiebreak path=bar/main.txt`,
which will cause it to rank files that share a prefix segment (`bar/` in
this case) to rank higher. Thus, when the user types, say, `t`,
`bar/test.txt` will immediately be the top recommendation. In editor
context, the user's editor should probably automatically add this flag
based on the user's current file.

The flag also allows for more natural search in deeper hierarchies. For
example, if the user is in `foobar/controller/admin.rb` here:

    baz/controller/admin.rb
    baz/views/admin.rb
    foobar/controller/admin.rb
    foobar/views/admin.rb

And wants to quickly edit the view for that application (i.e.,
`foobar/views/admin.rb`), then

    fzf --tiebreak path=foobar/controller/admin.rb

will rank `foobar/views/admin.rb` higher than `baz/views/admin.rb` for a
search of `admin.rb`, which is what the user most likely expected.

Note that contrary to other suggestions like only listing files at or
below the user's current directory, this patch does *not* limit the
user's ability to search beyond the scope of their current context
(e.g., they *can* get to `baz/controller/admin.rb` if they so wish).
@jonhoo
Copy link

jonhoo commented Sep 17, 2018

I proposed an addition to fzf in junegunn/fzf#1380 which would let us implement this pretty trivially in fzf.vim (I think).

@jonhoo
Copy link

jonhoo commented Sep 18, 2018

This can now be achieved using proximity-sort as a filter before passing files to fzf along with --tiebreak=index. The .vimrc config blurb included in the proximity-sort README provides exactly the semantics I wanted at least!

@webberwang
Copy link

nnoremap <leader>t :call fzf#vim#files(FindRootDirectory())<cr>

I use the FindRootDirectory() plugin to limit the search scope within the project root. You can configure how project root is defined in the plugin.

@jonhoo
Copy link

jonhoo commented Mar 31, 2020

@webberwang That's not quite the same as what this issue was about. OP (and I) wanted a way to prefer files that are close to the current directory, without limiting matches to the current directory.

@RTS5227
Copy link

RTS5227 commented Sep 6, 2021

I've found a solution to that:

nnoremap <expr> <silent><leader>lr ":Files<CR>".expand('%:h')

Go up to the parent dir using Ctrl-W

export FZF_DEFAULT_OPTS='--bind "ctrl-w:backward-kill-word"'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants