Skip to content

Conversation

@jonhoo
Copy link

@jonhoo jonhoo commented 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).

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).
@junegunn
Copy link
Owner

junegunn commented Sep 18, 2018

Interesting idea, though I'm not sure if I want to add options for specific types of input. Once we have an option for file paths, my argument for keeping fzf simple and rejecting options for other types of input – list of processes, URLs, songs and albums, or whatever we can imagine – becomes somewhat moot. And even for file paths, other users may have different opinions on how they should be sorted, by file size, by modification time, by extension, etc, and I don't think we can continue adding options for such different requirements. I agree that this is a common requirement, but I just don't want to make an exception.

As I commented in the linked issue, this particular case can be addressed by reordering the input and using --tiebreak=index.

(fd -p src/util; fd -E src/util) | fzf --tiebreak=index
function! s:list_cmd()
  let base = fnamemodify(expand('%'), ':h:.:S')
  return base == '.' ? 'fd -t f' : printf('fd -t f -p %s; fd -t f -E %s', base, base)
endfunction

command! -bang -nargs=? -complete=dir Files
  \ call fzf#vim#files(<q-args>, {'source': s:list_cmd(),
  \                               'options': '--tiebreak=index'}, <bang>0)

@jonhoo
Copy link
Author

jonhoo commented Sep 18, 2018

Mmm, that's a fair argument. And thanks for the explicit example config! Sadly the solution you propose doesn't actually solve this issue (at least for me), since it only gives a binary "close/not close" ranking, as opposed to being ranked by proximity. I guess the way to solve this without modifying fzf is to implement a separate program that takes a list of files as input and order them by their proximity to an argument. That way you could do:

$ fd -pFt f path/to/file | proximity-sort | fzf --tiebreak=index

I guess implementing proximity-sort is next on my TODO then :p

@jonhoo jonhoo closed this Sep 18, 2018
@junegunn
Copy link
Owner

Thanks for understanding :)

@jonhoo
Copy link
Author

jonhoo commented Sep 18, 2018

proximity-sort now exists here: https://github.com/jonhoo/proximity-sort
And the included .vimrc config blurb in the README there fixes junegunn/fzf.vim#360 🎉

@junegunn
Copy link
Owner

Cool, that was really fast!

@jonhoo
Copy link
Author

jonhoo commented Sep 18, 2018

What can I say — I really wanted this feature. It's been making me crazy over the past week while working on a paper in a repository with multiple papers that all have files like intro.tex, design.tex, eval.tex, etc.!

@edi9999 edi9999 mentioned this pull request Oct 15, 2018
15 tasks
@redreceipt
Copy link

proximity-sort now exists here: https://github.com/jonhoo/proximity-sort
And the included .vimrc config blurb in the README there fixes junegunn/fzf.vim#360 🎉

@jonhoo love it! How would I implement this into my .bashrc so fzf always uses this functionality?

@jonhoo
Copy link
Author

jonhoo commented Oct 15, 2018

It's a little tricky to have it be used on the command-line, because there is no "current" file. The README on https://github.com/jonhoo/proximity-sort has info about how you integrate it with vim though!

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

Successfully merging this pull request may close these issues.

3 participants