fish: Completion script rewrite (SHIFT-TAB)#4731
Conversation
|
I've rebased devel onto the latest master, so the CI error should resolve. EDIT: Hmm, it doesnt. Let me rebase your branch. |
There was a problem hiding this comment.
Pull request overview
Reworks the Fish integration to provide a new <shift-tab> completion implementation centered around a single fzf_complete function, while simplifying the shell script update workflow and adjusting related install/test configuration.
Changes:
- Replaces the Fish completion implementation with a new
fzf_completefunction and binds it to Shift-Tab (orbtabon older Fish). - Removes
shell/common.fishand inlines/adjusts shared Fish logic inshell/key-bindings.fish. - Simplifies
shell/update.shto only update Bash/Zsh scripts fromcommon.sh, and updates install/test/editorconfig accordingly.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
shell/completion.fish |
Rewritten Fish completion entrypoint (fzf_complete) + Shift-Tab binding, plus wildcard-expansion search path. |
shell/key-bindings.fish |
Removes generated include block and keeps required Fish helper functions inline. |
shell/common.fish |
Deleted (previous Fish shared include file). |
shell/update.sh |
Hardcodes common.sh inclusion and updates only Bash/Zsh targets. |
test/lib/common.fish |
Updates test environment variables for the new Fish completion behavior/options. |
install |
Updates the post-install Fish instruction to call fish_user_key_bindings. |
.editorconfig |
Applies shell-style indentation rules to *.fish files as well. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thanks. Some questions: It looks like this change breaks backward compatibility for the custom completion API, right? Could you explain the rationale behind it? Also, what happens to users who rely on the previous API for custom completions? e.g. function _fzf_complete_foo
function _fzf_complete_foo_post
awk '{print $NF}'
end
_fzf_complete --multi --reverse --header-lines=3 -- $argv < (ls -al | psub)
functions -e _fzf_complete_foo_post
endCould the new code detect old-style functions and still call them, at least with a warning? Maybe keep
I see the point, but I'd lean toward consistency and handle the |
|
Please take a look at the Copilot comments, but feel free to disregard irrelevant ones. Copilot often speaks nonsense. |
I just went with the most optimal implementation this time, since completion already works different than the other shells and it's a recent addition to fish. It is easy to maintain compatibility though: Rename the main function and custom post functions, and remove |
Option 1
Option 2Make What do you think? Although the fish completion is new, we neglected to mark it as experimental, so we should at least let the users know. |
Let's go with this. I can't imagine someone installing the latest version of fzf while still being stuck on a 4-year-old version of fish. We should mention it on the CHANGELOG though. |
Good idea, lets do that.
Great, now there are more commands and more command options available, as well as the command substitution syntax I might delay the changes a little because one of my dogs thew my laptop off the desk and now is dead (the laptop not the dog!). |
|
No rush, just wondering if this is close to landing so I can decide whether to put out a quick patch for #4737 in the meantime and make a new fzf release without this. |
|
I found some issues with In case that this doesn't make it to the new release, the patch below fixes #4737 for the current script: diff --git a/shell/completion.fish b/shell/completion.fish
index a1281c31..d9c74b79 100644
--- a/shell/completion.fish
+++ b/shell/completion.fish
@@ -214,6 +214,12 @@ function fzf_completion_setup
# Main completion function
function fzf-completion
+ # Restore the default shift-tab behavior on tab completions
+ if commandline --paging-mode
+ commandline -f complete-and-search
+ return
+ end
+
set -l -- tokens (__fzf_cmd_tokens)
set -l -- current_token (commandline -t)
set -l -- cmd_name $tokens[1] |
|
I see, thanks! If this can be completed within a week, I'd like to wait and include it in the next release. |
|
OK, good. I'll push the changes tomorrow (Monday) then - I'll try to fix an edge case where there is still an issue. |
6d86afc to
0805e51
Compare
|
I pushed the changes - please test. |
f7c0f4e to
9437db0
Compare
|
Thanks! I ran some basic tests and everything worked as expected.
And could you elaborate a bit more on |
The intention is for the descriptions to be searchable (and unless there is an option that I'm not aware of, it is not possible to dim the field), which I think is more useful, don't you agree?
The |
|
I see. I wasn't familiar with the term "expansion list", but I suppose fish users would know it. Sounds fine to me. By the way, this doesn't seem to work. Could you take a look? touch 'foo " bar'
git add <shift-tab>
# git add \"foo\ \\\"\ bar\"
# fatal: pathspec '"foo \" bar"' did not match any files |
This is an issue with the fish completion function of git. You will notice you get the same result with |
Ha, you're right! Anything else to decide? If not, we should update the README and CHANGELOG. |
I can't think of anything else. I just forgot to mention a couple of changes with the last commit (maybe worth mentioning in README):
Should I update the README in this PR, or do you prefer to make the changes yourself in |
Yeah, that's a good strategy. We could adopt it in bash and zsh as well in the future, though I wouldn't worry too much about it in practice.
Could you update here? I'll make the final edit when I merge your changes from devel to master. Thanks. |
|
Sorry for taking so long - there was an issue with the arguments passed to custom functions (it is now fixed). |
|
Thanks! Would you like to add an entry to the CHANGELOG.md as well? I think we should at least mention that the new version requirement of 3.4.0. |
Done. |
|
Thanks a lot! Merged to devel branch. I'll release a new version with your work this weekend. |
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [junegunn/fzf](https://github.com/junegunn/fzf) | minor | `v0.70.0` → `v0.71.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>junegunn/fzf (junegunn/fzf)</summary> ### [`v0.71.0`](https://github.com/junegunn/fzf/releases/tag/v0.71.0): 0.71.0 [Compare Source](junegunn/fzf@v0.70.0...v0.71.0) *Release highlights: <https://junegunn.github.io/fzf/releases/0.71.0/>* - Added `--popup` as a new name for `--tmux` with Zellij support - `--popup` starts fzf in a tmux popup or a Zellij floating pane - `--tmux` is now an alias for `--popup` - Requires tmux 3.3+ or Zellij 0.44+ - Cross-reload item identity with `--id-nth` - Added `--id-nth=NTH` to define item identity fields for cross-reload operations - When a `reload` is triggered with tracking enabled, fzf searches for the tracked item by its identity fields in the new list. - `--track --id-nth ..` tracks by the entire line - `--track --id-nth 1` tracks by the first field - `--track` without `--id-nth` retains the existing index-based tracking behavior - The UI is temporarily blocked (prompt dimmed, input disabled) until the item is found or loading completes. - Press `Escape` or `Ctrl-C` to cancel the blocked state without quitting - Info line shows `+T*` / `+t*` while searching - With `--multi`, selected items are preserved across `reload-sync` by matching their identity fields - Performance improvements - The search performance now scales linearly with the number of CPU cores, as we dropped static partitioning to allow better load balancing across threads. ``` === query: 'linux' === [all] baseline: 21.95ms current: 17.47ms (1.26x) matches: 179966 (12.79%) [1T] baseline: 179.63ms current: 180.53ms (1.00x) matches: 179966 (12.79%) [2T] baseline: 97.38ms current: 90.05ms (1.08x) matches: 179966 (12.79%) [4T] baseline: 53.83ms current: 44.77ms (1.20x) matches: 179966 (12.79%) [8T] baseline: 41.66ms current: 22.58ms (1.84x) matches: 179966 (12.79%) ``` - Improved the cache structure, reducing memory footprint per entry by 86x. - With the reduced per-entry cost, the cache now has broader coverage. - Shell integration improvements - bash: CTRL-R now supports multi-select and `shift-delete` to delete history entries ([#​4715](junegunn/fzf#4715)) - fish: - Improved command history (CTRL-R) ([#​4703](junegunn/fzf#4703)) ([@​bitraid](https://github.com/bitraid)) - Rewrite completion script (SHIFT-TAB) ([#​4731](junegunn/fzf#4731)) ([@​bitraid](https://github.com/bitraid)) - Increase minimum fish version requirement to 3.4.0 ([#​4731](junegunn/fzf#4731)) ([@​bitraid](https://github.com/bitraid)) - `GET /` HTTP endpoint now includes `positions` field in each match entry, providing the indices of matched characters for external highlighting ([#​4726](junegunn/fzf#4726)) - Allow adaptive height with negative value (`--height=~-HEIGHT`) ([#​4682](junegunn/fzf#4682)) - Bug fixes - `--walker=follow` no longer follows symlinks whose target is an ancestor of the walker root, avoiding severe resource exhaustion when a symlink points outside the tree (e.g. Wine's `z:` → `/`) ([#​4710](junegunn/fzf#4710)) - Fixed AWK tokenizer not treating a new line character as whitespace - Fixed `--{accept,with}-nth` removing trailing whitespaces with a non-default `--delimiter` - Fixed OSC8 hyperlinks being mangled when the URL contains unicode characters ([#​4707](junegunn/fzf#4707)) - Fixed `--with-shell` not handling quoted arguments correctly ([#​4709](junegunn/fzf#4709)) - Fixed child processes not being terminated on Windows ([#​4723](junegunn/fzf#4723)) ([@​pjeby](https://github.com/pjeby)) - Fixed preview scrollbar not rendered after `toggle-preview` - Fixed preview follow/scroll with long wrapped lines - Fixed tab width when `--frozen-left` is used - Fixed preview mouse events being processed when no preview window exists - zsh: Fixed history widget when `sh_glob` option is on ([#​4714](junegunn/fzf#4714)) ([@​EvanHahn](https://github.com/EvanHahn)) </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDQuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwNC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6bWlub3IiXX0=-->
|
This is not the reason that set -gx FZF_COMPLETION_OPTS '--nth=1' |
|
Alternatively, if you want to keep the descriptions search and mimic the native |


Contribution Policy
We do not accept pull requests generated primarily by AI without genuine understanding or real-world usage context.
All contributions are expected to demonstrate:
If these expectations are not met, we would prefer to implement the changes ourselves rather than spend time reviewing low-effort submissions.
Acknowledgement
The current completion script suffers from various issues:
\or$, and commands likeenv -i cmd. Also, due to an oversight incomplete, a single escaped backslash (\\) in command line crashes the shell.$work in the opposite way: Unescaped$does file completion while escaped$does variable name completion (instead of the other way around).tab) of the results and gives the wrong impression that by deleting the query the whole list is going to be displayed.This implementation is designed from the beginning as a replacement for the native
<shift-tab>functionality and is cleaner and more robust, while providing the additional ability of recognizing tokens that are wildcard expansion patterns, and perform search on the expanded lists. It consists of a single function (fzf_complete) of about 100 LOC, which can also be used by custom completion functions. The function is defined as a wrapper of fzf, so its command line options (same as fzf) can be auto completed. Since the script makes use of fish completion functions, it should be advised for users to create/update regular completion functions (which will work for bothtabandshift-tab), and create custom fzf completion functions only when needing to modify fzf options or want different results forshift-tab(for example, have no limit on the number of results for a command likegit checkout).Minimal custom function example:
Null terminated items from custom functions (that don't have custom post-functions) are supported, even though it doesn't make much sense for completion items to insert newlines (or other unescaped whitespace characters). For that reason, custom post completion functions are always expected to return line terminated items:
A more useful example that demonstrates the different ways the function can be used:
The README isn't updated yet. There are some things than must be decided:
The script uses the
--visible(-V) option ofstring length, and--escapeoption ofcomplete -C, both of which were introduced in fish v3.4.0. This makes the version requirement different than that ofkey-bindings.fish, which is v3.1b1. There are three different ways we can handle this:string length -V, and there were no complaints yet.key-bindings.fish. Akey-bindings-legacy.fishcould be left in the repository, so that users with old fish versions can still have a way to use the key bindings.The file names in completion lists (but not in glob lists) are escaped. This can be changed, but:
\tVariable:, which is not very elegant or 100% reliable) and not apply escaping in this case. It should also check after escaping if the token starts with unescaped~, and unescape the leading~from the result.completeseems to split the item before escaping, so names that contain tabs are still not handled correctly. This is something that the native fish completion also suffers from and will probably be fixed in the future.The environment variable
$FZF_EXPANSION_OPTSis used for customizing the options of glob expansion search, in addition to the usual$FZF_COMPLETION_OPTSfor setting completion options. The list in glob expansions is certain to be files or directories, so it makes sense to have separate options, but it is also necessary for accessing the selections because different templates must be used ({+}for expansions,{+r1}for completions). It would be possible to use a single variable though, if the names in both completion and expansion were escaped or unescaped, and also have\x00as delimiter for expansion (so the same template can be used).The custom post completion functions are named
_fzf_post_complete_COMMANDinstead of_fzf_compete_COMMAND_postthat is used for other shells. This is done for being able to handle binaries namedfooandfoo_post. If having the same function name as other shells is preferred, we can change it.The script tries to detect the command to be completed, by stripping any leading variable definitions and launcher commands (
builtin,command,doas,env,sudo) from the command line. If a custom function is defined for that command, the function is called and the (stripped) command line tokens are passed as arguments ($argv[1]is the command name,$argv[-1]is the current token). Any options of the launcher commands are removed, but in order to also support options that accept parameters and remove the parameters too, it would require much code (for something rarely used), whereas now a single regex is used. Let me know if this is OK.