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

Could Ctrl-T override .gitignore when starting from an ignored path? #2450

Closed
5 of 10 tasks
githorse opened this issue Apr 19, 2021 · 8 comments
Closed
5 of 10 tasks

Could Ctrl-T override .gitignore when starting from an ignored path? #2450

githorse opened this issue Apr 19, 2021 · 8 comments

Comments

@githorse
Copy link

  • I have read through the manual page (man fzf)
  • I have the latest version of fzf
  • I have searched through the existing issues

Info

  • OS
    • Linux
    • Mac OS X
    • Windows
    • Etc.
  • Shell
    • bash
    • zsh
    • fish

Problem / Steps to reproduce

This is not a bug in fzf, but it's a non-optimal UX. I notice when I'm in my project root and I type

$ ls node_modules<Ctrl-T>my-node-module

I see no matches. If instead I do

$ cd node_modules; ls<Ctrl-T>my-node-module

I see the expected files. This is obviously because my FZF_CTRL_T_COMMAND is ag -g "", and ag is ignoring stuff in my .gitignore, which includes node_modules/.

Thing is, that's usually exactly what I want. But clearly when I type $ ls node_modules/ I do want to search in node_modules/. Is there any way this could be made to work, either with some fancier ag options (or a different command altogether), or by some modification to fzf (doing cd node_modules behind the scenes, perhaps)?

@junegunn
Copy link
Owner

@githorse
Copy link
Author

githorse commented Apr 19, 2021

It works! (In Bash; not in zsh using the oh-my-zsh fzf plugin, but that's a separate issue I'll have to look into.) I guess I'm a little confused why we have two different ways to do this (unless it's to solve this exact issue) -- is there more information on the difference between the two methods?

The built-in fuzzy-completion has the same root issue, though; either the underlying command uses .gitignore and so can't ever see into node_modules/, or it doesn't and shows me results from node_modules/ when I don't want them.

I could of course just remember to use **<Tab> when I want to see all files and <Ctrl-T> otherwise. But I'm a dreamer! I just added

[[ -d "${LBUFFER##* }" ]] && cd "${LBUFFER##* }"

to __fsel() in keybindings.zsh and it seems to do what I want, except that unsurprisingly it breaks on directory names with spaces. My zsh-fu is weak (nonexistent) and I'm not sure if there's a way to fix that without doing some annoying parsing for quotes and backslashes.

Actually, the cd thing seems like maybe a good idea even apart from the .gitignore problem, because otherwise I might be indexing a ton of files I don't need. Consider:

$ pwd
/
$ ls /usr/share/<Ctrl-T>

This takes forever because it indexes everything under / -- but presumably I don't want anything except what's in /usr/share/, which is a much smaller list.

Of course there may be unforeseen consequences with this I haven't considered.

@githorse
Copy link
Author

Here's some progress towards a more reliable version:

get_start_dir() { echo "${@[-1]}"; }
dir="$(eval get_start_dir "$(echo "$LBUFFER" | rev | awk -F'\\|\\||&&|;' '{print $1}' | rev)")"
[[ -d "$dir" ]] && cd "$dir"

This works by using a function to figure out what the final word on the command line is, respecting quotes and backslashes to escape whitespace. The awk stuff parses the LBUFFER correctly when using multiple commands on the same line connected with ||, && or ; (e.g. $ source ~/.zshrc; ls node_modules<Ctrl-T>). Don't love the eval here. There must be a better way...

@junegunn
Copy link
Owner

junegunn commented Apr 20, 2021

I guess I'm a little confused why we have two different ways to do this (unless it's to solve this exact issue) -- is there more information on the difference between the two methods?

CTRL-T is the simpler one that works without any context information, so we can easily configure its behavior with a static $FZF_CTRL_T_COMMAND variable.

Fuzzy completion is a more generic version with much more complexity. Its behavior is determined by what you have typed so far on the command line and the position of your cursor; cd /foo/bar/**<tab> is different from ls /foo/bar/**<tab> or kill **<tab> or ssh **<tab> (CTRL-T would work the same in all cases). So we need to use functions to customize how it works: https://github.com/junegunn/fzf#settings

  • _fzf_compgen_path and fzf_compgen_dir for generating the candidate list for the basic path and directory completion
  • _fzf_comprun for using different options of fzf depending on the command
  • _fzf_complete_COMMANDNAME for custom completion for COMMANDNAME command
    • _fzf_complete_COMMANDNAME_post for post-processing the output of fzf

@githorse
Copy link
Author

githorse commented Apr 21, 2021

Gotcha. Thanks for the explanation. Looks like the magic sauce I was looking for to handle whitespace is in _fzf_extract_command in completion.zsh. However, I notice that this also doesn't seem to respect ;, &&, and ||, right? In other words, if I do

$ source ~/.zshrc; cd **<tab>

It thinks my command is source, not cd. Admittedly, a corner case. But should _fzf_extract_command be updated to parse only the rightmost command? (Using. e.g. echo "$LBUFFER" | rev | awk -F'\\|\\||&&|;' '{print $1}' | rev or some more robust zsh magic).

(This is getting slightly bit off-topic; I could open a separate issue for this if you agree.)

@junegunn
Copy link
Owner

It's a known issue: #1981 #1992 #2130

@githorse
Copy link
Author

OK, thanks. Will let those other issues sort out the command thing then.

Let's label this property, where $ ls root/some_gitignored_dir/** includes results under root/some_gitignored_dir but $ ls root/** does not, "directory-aware search". I would say this is a desirable property but some might disagree. Certainly it's what I personally expected to happen.

Some observations/questions:

  • The default _fzf_compgen_path() and _fzf_compgen_dir() do not exhibit directory-aware search; however, the examples from the README settings (e.g. fd --hidden --follow --exclude ".git" . "$1") actually do work. Obviously I can just make those changes locally. But would you consider a PR updating the default commands to also do directory-aware search?
  • It seems that completion is "better" than Ctrl-T because of the context-sensitive stuff. Is there something Ctrl-T has that completion doesn't? I can't think of use case where I would not prefer the context-sensitive search (given that it falls back to all files for an unknown command), but I'm assuming there's a good reason they work differently.
  • Given that completion seems superior in most respects, I'd rather use that (after replacing _fzf_compgen_(path|dir)() as above). But my fingers prefer <Ctrl-T> to **<Tab>. Is there any way I can locally hook up the keybinding ^T to the completion code instead?

@junegunn
Copy link
Owner

The default _fzf_compgen_path() and _fzf_compgen_dir() do not exhibit directory-aware search

The prefix (e.g. ~/foo/ when you type vim ~/foo/bar**<tab>) is passed to the function as $1, so they use the information to only list files in that directory.

Is there something Ctrl-T has that completion doesn't?

  • Unlike on zsh, on bash, completion must be defined for each and every command. So if you have a new command and you haven't configured the fuzzy completion for it, foobar **<tab> simply will not work, while CTRL-T will always work.

  • Sometimes you don't want context-sensitivity. ssh **<tab> will list host names and IP addresses, but there are cases where you just want to grab a file path and CTRL-T comes in handy. ssh -i <CTRL-T>

  • And like I said above, it's easier to grasp and to configure. That's a big advantage for the new users and those who don't have time to learn how to configure fuzzy completion.

Is there any way I can locally hook up the keybinding ^T to the completion code instead?

https://github.com/junegunn/fzf/wiki/Configuring-fuzzy-completion#dedicated-completion-key (zsh only)

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

2 participants