A lightweight modular framework for integrating fzf with Bash
- π€ Does not conflict with
bash_completionβ works via separate key bindings without interceptingTab - π¦ Modular architecture β load only the modules you need
- π― Context-dependent β fzf activates only for supported commands
- β‘ Does not interfere with tab completion β standard
Tabworks as usual
fzf_gently.bash:
- Uses separate key bindings (e.g.,
Ctrl+Space,Ctrl+F) - Checks the current command in
READLINE_LINEbefore activation - Allows using
Tabfor standardbash_completionwithout modifications - Activates fzf only when the cursor is in the context of a supported command
# Example: you are typing a command
git checkout fea<TAB> # β Standard bash_completion works
git checkout fea<C-Space> # β fzf opens for branch selectionThe core of fzf_gently.bash is the fzf_gently__any_f function. It implements short-circuit evaluation (like logical OR):
fzf_gently__any_f \
fzf_gently__fzf_git_branch \
fzf_gently__fzf_flatpak_app \
fzf_gently__fzf_cdHow it works:
- Executes functions in order from first to last
- Stops immediately when any function returns exit code
0(success) - Returns
0if at least one function succeeded - Returns
1only if all functions failed
Each module follows this pattern:
fzf_gently__fzf_module_name() {
# Check if current command matches this module's pattern
if [[ "$READLINE_LINE" =~ ^pattern$ ]]; then
# Launch fzf and set result to READLINE_LINE
return 0 # Success - stop processing other modules
else
return 1 # Not our command - try next module
fi
}Example flow for git checkout <C-f>:
-
fzf_gently__fzf_git_branchchecks: "Does this matchgit checkout?" β Yes! β Launches fzf β Returns0β Stop -
fzf_gently__fzf_flatpak_appnever runs (short-circuit) -
fzf_gently__fzf_cdnever runs (short-circuit)
Example flow for flatpak run <C-f>:
-
fzf_gently__fzf_git_branchchecks: "Does this match?" β No β Returns1β Continue -
fzf_gently__fzf_flatpak_appchecks: "Does this match?" β Yes! β Launches fzf β Returns0β Stop -
fzf_gently__fzf_cdnever runs
This approach ensures only the first matching module handles the command.
The fzf_gently___cmd_matches function simplifies pattern matching for command parsing:
# Basic command without subcommands
fzf_gently___cmd_matches "cd" "" prefix query || return 1
# Matches: "cd <query>"
# Command with multiple subcommands (OR logic)
fzf_gently___cmd_matches "git" "checkout branch" prefix query || return 1
# Matches: "git checkout <query>" or "git branch <query>"Parameters:
cmdβ main command name (e.g., "git", "cd")subcmdsβ space-separated subcommands (e.g., "checkout branch", empty string for none)prefix_varβ nameref variable to store matched prefix (cmd + subcmd)query_varβ nameref variable to store user input after the command
Returns:
0if READLINE_LINE matches the pattern (variables set)1if no match (continue to next module)
- Bash 4.0+
- fzf
# Clone the repository
git clone https://github.com/FRiMN/fzf_gently.bash.git ~/.config/fzf_gently.bashAdd to ~/.bashrc:
# Load fzf_gently code
source ~/.config/fzf_gently.bash/init.sh
# Activate all fzf_gently context-dependent functionality
bind -x '"\C-f": fzf_gently__all_context_dependent'
# Activate fzf_gently history functionality
bind -x '"\C-r": fzf_gently__fzf_history'Add to ~/.bashrc after the source:
# Activate fzf_gently for the current command (main binding)
bind -x '"\C-f": fzf_gently__all_context_dependent'
# Or use alternative combinations:
bind -x '"\C-@": fzf_gently__all_context_dependent' # Ctrl+Space
bind -x '"\C-t": fzf_gently__all_context_dependent' # Ctrl+T
# Individual modules (optional)
bind -x '"\C-g": fzf_gently__fzf_git_branch' # Git only
bind -x '"\C-r": fzf_gently__fzf_history' # Command historyYou can create your own function with a custom set of modules:
_fzf_smart() {
fzf_gently__any_f \
fzf_gently__fzf_git_branch \
fzf_gently__fzf_flatpak_app \
fzf_gently__fzf_cd
}
bind -x '"\C-f": _fzf_smart'Important: fzf_gently__any_f must always be the first argument, followed by the list of modules you want to check. The function will try each module in order and stop at the first one that matches the current command.
| Module | Description | Activation Command |
|---|---|---|
cd |
Interactive directory selection | cd <C-f> |
git_branch |
Git branch selection | git checkout <C-f> or git branch <C-f> |
flatpak_app |
Flatpak application selection | flatpak run <C-f> |
history |
Command history search | At any time |
Note: The
historymodule is not included infzf_gently__all_context_dependent()and must be bound separately (e.g., toCtrl+R).
# ~/.config/fzf_gently.bash/modules/my_module.sh
fzf_gently__fzf_my_feature() {
local prefix query selected
# Check if the current line matches our command
fzf_gently___cmd_matches "mycommand" "" prefix query || return 1
# Launch fzf with the query pre-filled
selected=$(my_data_source | \
fzf --prompt='Select: ' -1 --query="${query}" \
--height=40% --border) && \
fzf_gently___fzf_set_readline "$prefix" "$selected"
}fzf_gently__fzf_docker() {
local prefix query selected
# Matches: "docker start <query>" or "docker stop <query>"
fzf_gently___cmd_matches "docker" "start stop" prefix query || return 1
selected=$(docker ps --format '{{.Names}}' | \
fzf --prompt='Container: ' -1 --query="${query}" \
--height=40% --border) && \
fzf_gently___fzf_set_readline "$prefix" "$selected"
}Key points:
- Always declare
local prefix query selectedat the start - Use
fzf_gently___cmd_matchesto parse READLINE_LINE - Return 1 immediately if no match (
|| return 1) - Use
fzf_gently___fzf_set_readlineto set the result
Add to init.sh:
fzf_gently__all_context_dependent() {
fzf_gently__any_f \
fzf_gently__fzf_my_feature \ # β Your new module
fzf_gently__fzf_docker \
fzf_gently__fzf_git_branch \
fzf_gently__fzf_flatpak_app \
fzf_gently__fzf_cd
}MIT License - see file LICENSE
- junegunn/fzf β amazing fuzzy finder
- Bash Community β for the powerful readline mechanism