-
Notifications
You must be signed in to change notification settings - Fork 97
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
Integrate fzf-completion
with fzf-tab-complete
#65
Comments
I wanted to ask the same question since the day I discovered fzf-tab (an awesome project by the way!) I'll try to paraphrase what you are asking. Is it possible to hook For example, right now, when I type type This would have the obvious advantage over My knowledge of Zsh completion system is too rudimentary to know whether this is even remotely possible. |
I think this is a question about compsys, not fzf-tab. A simple solution is override the default completion functions/completer: _files() {
local files=($(fd --hidden --follow --type=f))
compadd -a -f files
}
_cd() {
local dirs=($(fd --hidden --follow --type=d))
compadd -a -f dirs
} Or if you just want to enable it for some specific commands: # save the original _files completer
autoload +X -Uz _files
functions[_files_orig]=$functions[_files]
_files() {
if zstyle -t ":completion:$curcontext" use-fd; then
local files=($(fd --hidden --follow --type=f))
compadd -a -f files
else
_files_orig
fi
}
zstyle ':completion:*:vim:*' use-fd true Hope this can help you. |
Thanks for the working code snippets! This is super helpful.
This won't take advantage of streaming mode in fzf, so it can result in noticeable freezes when hitting TAB. Breadth-first traversal of files piped into |
Umm, compsys itself doesn't support streaming mode, and nor does fzf-tab. To take advantage of streaming mode of fzf we should call it in advance. _files() {
compadd -f -- "$(fd --hidden --follow --type=f | fzf)"
} On the contrary, this can't take advantage of compsys&fzf-tab (eg. list-colors). |
Yes, that's what I've been trying to suggest.
Why not? If this is done from within fzf-tab, then it should be possible, no? |
Thanks, this is close to be very good already. This works with |
I don't think it's close to being solved. The problem is that |
I'm not trying to offer suggestions how this can be solved. I only know enough of the completion system to know that there is no easy solution. My goal is to interest @Aloxaf enough in this problem so that he solves it 😉 It would be fantastic if TAB behaved the same as it does now in fzf-tab except that file completions had all possible candidates preexpanded and streamed into fzf. |
Yes, that sounds great. |
For context, why did you revert #41? 🙂 |
Would it be possible to hook |
I can't reproduce this, but maybe you could add a flag to prevent _files from called multiple times? (umm, that may also be a little complex) And I come up with another rough approach, define a completer so that it will only be called one time: FZF_FILES=(vim exa)
_fzf_files() {
if (( $FZF_FILES[(I)$words[1]] )); then
compadd -f -- "$(fd --hidden --follow --type=f | fzf)"
return 0
else
return 1
fi
} |
That's what's fzf-tab-completion does. But normally compadd will receive a group of candidates at a time. If we want it to stream every single candidate to fzf, then we have to pass the candidate to compadd one by one, which will be slower. Like this: _files() {
fd --hidden --follow --type=f | {
while read line; do
compadd -f -- $line
done
}
} |
I figured out what caused fzf to appear multiple times, I had the following configs back from when I used prezto (seems like I can remove them now that I use fzf-tab):
|
Why isn't fzf-tab doing it? What's the downside?
If fzf-tab could display completion candidates pushed with The real question is whether fzf-tab can push completions from compadd directly to fzf in streaming mode. What are we going to lose if fzf-tab learns to do this? |
I didn't choose this way mainly for speed. Another is for some function like getting the common prefix of candidates or sort the candidates or group color. They need the whole candidates to calculate. Ummm, let me think, if fzf-tab need to support streaming mode.
Looks still attractive, especially when I found zsh is still lack of speed when trying to support list-colors. It might need another version of fzf-tab (fzf-tab-streaming.zsh?) to let users switch from them? |
💯 🚀 🏆 Give a shout if you need someone to test stuff or bounce ideas off.
How would this work if the first N candidates share a long prefix but the following candidates have a different first symbol?
I think this is fine to begin with. If your ideas w.r.t. streaming fzf-tab pan out, it might be worth it to add group support to fzf. Basically, allow headers in the middle and not only at the top. This could be useful to other users of fzf, not just to fzf-tab. |
I need the stream mode. If the input source is large, fzf-tab will hang. Such as |
What I crave most is git completion(such as git log), but the current response of fzf-tab makes me a bit uncomfortable. |
@kevinhwang91 I hacked some code that streams the |
@maximbaz et al I've implemented something along the lines of what's been discussed here. The implementation is very hacky but seems to work well. The implementation is currently coupled to zsh4humans/v3, which is very much bleeding edge and not recommended for serious use. Nonetheless, I'd be grateful if you tried it in a docker container and let me know if this works for you and what you would like changed. Some of the hacks could be rolled into fzf-tab. Others might become their own plugin. To try it in docker: docker run -v $HOME:/host -e TERM -e COLORTERM -w /host -it --rm debian /bin/bash -uec '
apt-get update
apt-get install -y curl zsh
[[ -e /host/.p10k.zsh ]] && cp /host/.p10k.zsh ~/
exec sh -c "$(curl -fsSL https://raw.githubusercontent.com/romkatv/zsh4humans/v3/install)"' Within the container, Tab works just like in fzf-tab. When it opens fzf, the first entries are the same as in stock fzf-tab.
When there are directories among entries, their content gets added at the bottom, recursively. This is the difference with the stock fzf-tab. Additional completion candidates get streamed into fzf but the top candidates are just like before. You can accept suggestions with Enter or Tab. The latter will immediately trigger another completion if the inserted completion is a directory. This is similar to "continuous trigger" in fzf-tab but works slightly differently. Binding to Tab is very natural. This binding is helpful when you do something like You can also accept file completions with Alt+Enter. This will simply insert the query from fzf as completion. This is nice when hitting Tab gives you lots of entries that all start with Recursive file completions are only smart enough to figure out whether you want just directories or everything. They currently don't support file patterns and ignore lists. (This can be added.) File completions are colorized, like in fzf-tab. I wrote a very fast colorized that processes 70-100k files per second on my machine. It also fixes a few bugs that existed in fzf-tab colorizer. (This part should be relatively easy to backport to fzf-tab.) You can also notice that there is no prompt flickering when completing, unlike in the stock fzf-tab. And also that autosuggestions and syntax highlighting are always correct and faster than normal. zsh4humans is a giant pile of hacks that does awful violence to plugins in order to integrate everything nicely. If things are confusing, look at If you want to try it without docker, backup your zsh startup files and run this: exec sh -c "$(curl -fsSL https://raw.githubusercontent.com/romkatv/zsh4humans/v3/install)"' |
At some point I also had the following implementation:
I tried it because I thought that automatically streaming recursive directory content would be bad when what you are looking for is just a single file completion. After playing around with automatic streaming I realized these reservations are unfounded. Things that really help with it:
|
Thanks @romkatv! In a very short summary, I love it! I tried everything you mentioned and it feels great. The only annoying thing is something you have mentioned, a non-recursive suggestions on the first |
@maximbaz Thanks for giving it a try and posting feedback.
I think I've confused you. When you press Tab, directories are traversed recursively right away. However, there is special logic w.r.t. directories starting with a dot.
Here's an example. Suppose you have this directory structure in your home directory:
If you type
You won't see these directories because they are nested inside hidden directories:
I have
Yeah, I remember that. I haven't found good UI for this yet but I have more tools now that I could use once I figure out how this feature should work. Intuitively, injecting frequently used files and directories isn't much different from injecting files and directories by recursing into suggested directories (this is what I've done here). The problem is that doing this blindly wrecks prefix matching and ranking. I could do it with an extra binding. E.g., instead of Tab you'd press Shift+Tab and it would offer files / directories (depending on the command) that you've used in the past. Here "used" can be quite general. Running any command while having |
Oh I see! Might I suggest an alternative and more precise approach, where instead of treating all hidden folders specially, just respect Regarding frequently used folders, I agree it's a complicated matter, I also don't have a clear idea in my mind... I do like that you are building more tools that will be useful for this feature 🙂 But yeah maybe lets leave this out for a moment. I have another idea or request, more relevant to the completions scope. Would it be possible to not show fzf when there is a single match? Imagine I'm typing I guess all I described might be a special case of #8, which I also agree with and which would be nice to have. That ticket explains everything, if there is a common prefix let the first |
I can add customization points that would allow you to specify that such and such directory should be ignored, while some other directory should always be recursively descended. For now, though, I'm trying to figure out what the default behavior should be so that it requires as little customization as possible.
Oh, that's a bug. Fixed.
This is a trade off. To decide whether to just complete
This is easy to do but here I'm not sure it'll be a win. If the first tab completes to a common file prefix, what are the chances that you will not press another tab immediately. I think they are very low. It does sometimes happen though, and for that there is alt+enter in fzf. If you just want to insert prefix, going through tab -> fzf pops up -> alt+enter is surely more work than plain tab, but it happens rarely. On the other hand, pressing tab twice where one would do isn't as annoying but would be very common. I'm undecided here, so no changing the code yet. There are also some middle ground solutions such as inserting the prefix if it's longer than N. |
Agree with I agree with everything else you wrote, if I can think of a compelling argument in favor of completing up to a common prefix I will let you know. I guess now I will be eagerly awaiting when you declare your work stable for day-to-day usage 🙂 If you will make it into plugins that would be simpler to choose from (instead of fully switching to zsh4humans) it would be nice, although I might just as well consider switching to zsh4humans. Thanks once again for everything you do 👍 |
I misread
I was thinking of The standard completion system respects
When zsh4hmans/v3 is ready, I'll open an issue for feedback and ping you on it. Your feedback was instrumental to several crucial p10k features and I'm hoping we can achieve similar synergy with z4h.
I don't know yet how it'll shape out but there are a few things I do know. zsh4humans will stay either way and it will keep providing "deep integration" (a.k.a. dirty hacks) for various plugins it uses. The value from this is just too high to pass on. Naive combination of zsh-syntax-highlighting + zsh-autosuggestions + fzf-tab + powerlevel10k results in many inefficiencies, minor bugs and rough edges. These are very difficult to overcome by changing individual projects because each project is meant to work in so many different environments and configurations. But within zsh4humans I can make those changes because I control the whole environment. E.g., I can remove I'm almost certain I'll send a few PRs to fzf-tab where improvements have no trade offs and make sense for all fzf-tab users. There is a 50/50 chance zsh4humans will keep using fzf-tab vs having an embedded stripped-down and modified version of it. There is a very small chance this version will be available as a separate plugin.
I'll try to win you over. v3 is the first version I'm using myself and I honestly think it's very good. I've hand-optimized and polished all basics and implemented several advanced features that bring shell experience on the whole new level. For example, you can set up P.S. If you'd like to follow up w.r.t. zsh4humans, please do it via zsh4humans repo. Let's keep this thread focused on fzf-tab. |
Using fzf with |
What an awesome solution! 👏🏼 |
Customized FZF fuzzy completion is a workaround to modify completion candidate list while working well with fzf-tab. FZF_COMPLETION_TRIGGER=''
_fzf_complete_git() {
ARGS="$@"
local branches
branches=$(git branch -vv --all)
if [[ $ARGS == 'git co'* ]]; then
_fzf_complete --reverse --multi -- "$@" < <(
echo $branches
)
else
eval "zle ${fzf_default_completion:-expand-or-complete}"
fi
}
_fzf_complete_git_post() {
awk '{print $1}'
} Now zsh can show the candidates from the command we configured when typing |
I just want to throw my own workaround into the pile. What I did is a cute hack to invoke the default fzf-completion widget with a double fzf-completion-notrigger() {
# disable trigger just this once
local FZF_COMPLETION_TRIGGER=""
# if fzf-completion can't come up with something, call fzf-tab-complete
# instead of the default completion widget (expand-or-complete).
#
# FIXME: triggers an infinite recursion on an empty prompt
# _zsh_autosuggest_highlight_reset:3: maximum nested function level reached; increase FUNCNEST?
#
#local fzf_default_completion='fzf-tab-complete'
fzf-completion "$@"
}
zle -N fzf-completion-notrigger
# Set an aggressive $KEYTIMEOUT to make usage of single <Tab> less miserable
KEYTIMEOUT=20
# Bind double <Tab>
bindkey '\t\t' fzf-completion-notrigger
# Bind Ctrl-Space in case I am unable to use double <Tab> due to a combination
# of the aggressive $KEYTIMEOUT on a slow link.
bindkey '^ ' fzf-completion-notrigger Note that if Anyway, this limitation makes sense — fzf-completion does not use compsys to generate the response, that's why it is able to invoke fzf in a streaming manner, but that's also why it will never be able to integrate with compsys (and, by extension, with fzf-tab). When fzf-completion runs, there is no compsys context, therefore it is fundamentally unable to determine whether it needs to do its thing or defer to fzf-tab. That's why it needs a human-based trigger. |
I have configured fzf completion to be smart when suggesting files and directories, it includes hidden folders, skips ignored files, and many more:
With the above,
vim <tab>
shows fzf with all the files I want to see, andcd <tab>
shows fzf with all the directories I want to see, including nested files and directories.The rest of the completion is rather broken, e.g.
man <tab>
orpass <tab>
also show fzf with the files starting from the current directory, just like withvim <tab>
.I would like to make
fzf-tab
kick in when this happens, i.e. for file and directory completions keep usingfzf-completion
because it knows well how to select files and directories, but for all the rest of the completions I wantfzf-tab-complete
to be executed.Is it possible to achieve this?
Alternatively, is it possible to teach
fzf-tab
which files and directories it will present me? I would be fine to stop using/usr/share/fzf/completion.zsh
iffzf-tab
can also be told to usefd --hidden --follow --type=f
for example.Let me know if this makes sense, and thanks! :)
The text was updated successfully, but these errors were encountered: