Initial commit
This commit is contained in:
589
references/fzf-workflows.md
Normal file
589
references/fzf-workflows.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# fzf - Interactive Selection Workflows
|
||||
|
||||
## Overview
|
||||
|
||||
`fzf` is a command-line fuzzy finder that enables interactive selection from lists. It's a powerful tool for building interactive workflows.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
# Pipe any list to fzf
|
||||
ls | fzf
|
||||
|
||||
# Multi-select with Tab
|
||||
ls | fzf -m
|
||||
|
||||
# Use selected result
|
||||
SELECTED=$(ls | fzf)
|
||||
echo "You selected: $SELECTED"
|
||||
```
|
||||
|
||||
## Preview Windows
|
||||
|
||||
### Basic Preview
|
||||
|
||||
```bash
|
||||
# Preview with cat
|
||||
fd | fzf --preview 'cat {}'
|
||||
|
||||
# Preview with bat (syntax highlighting)
|
||||
fd | fzf --preview 'bat --color=always {}'
|
||||
|
||||
# Preview with line numbers
|
||||
fd | fzf --preview 'bat --color=always --line-range :500 {}'
|
||||
```
|
||||
|
||||
### Preview Window Configuration
|
||||
|
||||
```bash
|
||||
# Position and size
|
||||
fzf --preview 'cat {}' --preview-window=right:50%
|
||||
fzf --preview 'cat {}' --preview-window=up:40%
|
||||
fzf --preview 'cat {}' --preview-window=down:30%
|
||||
fzf --preview 'cat {}' --preview-window=left:50%
|
||||
|
||||
# Hidden by default (toggle with ctrl-/)
|
||||
fzf --preview 'cat {}' --preview-window=hidden
|
||||
|
||||
# Wrap text
|
||||
fzf --preview 'cat {}' --preview-window=wrap
|
||||
|
||||
# No wrap (default)
|
||||
fzf --preview 'cat {}' --preview-window=nowrap
|
||||
```
|
||||
|
||||
### Advanced Previews
|
||||
|
||||
```bash
|
||||
# Preview with highlighted search term
|
||||
rg --line-number 'pattern' | \
|
||||
fzf --delimiter : \
|
||||
--preview 'bat --color=always {1} --highlight-line {2}'
|
||||
|
||||
# Preview JSON with jq
|
||||
fd -e json | fzf --preview 'jq --color-output . {}'
|
||||
|
||||
# Preview images (requires chafa/imgcat)
|
||||
fd -e png -e jpg | fzf --preview 'chafa {}'
|
||||
|
||||
# Preview directories
|
||||
fd -t d | fzf --preview 'ls -la {}'
|
||||
|
||||
# Preview with tree
|
||||
fd -t d | fzf --preview 'tree -L 2 {}'
|
||||
|
||||
# Multi-level preview (file type detection)
|
||||
fd | fzf --preview '[[ -f {} ]] && bat --color=always {} || tree -L 1 {}'
|
||||
```
|
||||
|
||||
## Keybindings
|
||||
|
||||
### Default Keybindings
|
||||
|
||||
```
|
||||
ctrl-j / ctrl-n / down : Move down
|
||||
ctrl-k / ctrl-p / up : Move up
|
||||
enter : Select and exit
|
||||
tab : Mark item (multi-select)
|
||||
shift-tab : Unmark item
|
||||
ctrl-c / esc : Exit without selection
|
||||
ctrl-/ : Toggle preview window
|
||||
```
|
||||
|
||||
### Custom Keybindings
|
||||
|
||||
```bash
|
||||
# Copy to clipboard (macOS)
|
||||
fzf --bind 'ctrl-y:execute-silent(echo {} | pbcopy)'
|
||||
|
||||
# Copy to clipboard (Linux)
|
||||
fzf --bind 'ctrl-y:execute-silent(echo {} | xclip -selection clipboard)'
|
||||
|
||||
# Open in editor
|
||||
fzf --bind 'ctrl-e:execute($EDITOR {})'
|
||||
|
||||
# Open in editor and exit
|
||||
fzf --bind 'ctrl-e:execute($EDITOR {})+abort'
|
||||
|
||||
# Delete file with confirmation
|
||||
fzf --bind 'ctrl-d:execute(rm -i {})'
|
||||
|
||||
# Multiple bindings
|
||||
fzf --bind 'ctrl-e:execute($EDITOR {})' \
|
||||
--bind 'ctrl-y:execute-silent(echo {} | pbcopy)' \
|
||||
--bind 'ctrl-d:execute(rm -i {})'
|
||||
```
|
||||
|
||||
### Advanced Keybinding Actions
|
||||
|
||||
```bash
|
||||
# Reload results
|
||||
fzf --bind 'ctrl-r:reload(fd)'
|
||||
|
||||
# Change preview
|
||||
fzf --preview 'bat {}' \
|
||||
--bind 'ctrl-/:change-preview-window(hidden|)'
|
||||
|
||||
# Toggle between preview styles
|
||||
fzf --preview 'bat --color=always {}' \
|
||||
--bind 'ctrl-t:change-preview(tree -L 2 {})'
|
||||
|
||||
# Accept and execute
|
||||
fzf --bind 'enter:execute(echo Selected: {})+accept'
|
||||
```
|
||||
|
||||
## Search Syntax
|
||||
|
||||
### Fuzzy Matching
|
||||
|
||||
```
|
||||
# Basic fuzzy search
|
||||
abc # Matches files containing 'a', 'b', 'c' in order
|
||||
# Examples: abc, aXXbXXc, axbxc
|
||||
|
||||
# Exact match (single quote)
|
||||
'abc # Matches files containing exact string "abc"
|
||||
|
||||
# Prefix match (caret)
|
||||
^abc # Matches files starting with "abc"
|
||||
|
||||
# Suffix match (dollar)
|
||||
abc$ # Matches files ending with "abc"
|
||||
|
||||
# Exact match (both)
|
||||
^abc$ # Matches exactly "abc"
|
||||
```
|
||||
|
||||
### Logical Operators
|
||||
|
||||
```
|
||||
# AND (space)
|
||||
abc def # Contains both "abc" AND "def"
|
||||
|
||||
# OR (pipe)
|
||||
abc|def # Contains "abc" OR "def"
|
||||
|
||||
# NOT (exclamation)
|
||||
abc !def # Contains "abc" but NOT "def"
|
||||
|
||||
# Combination
|
||||
abc def|ghi !jkl
|
||||
# Contains "abc" AND ("def" OR "ghi") but NOT "jkl"
|
||||
```
|
||||
|
||||
### Field Selection
|
||||
|
||||
```bash
|
||||
# Search specific field (delimiter-separated)
|
||||
# -d : Set delimiter
|
||||
# -n : Select field number (1-indexed)
|
||||
# --with-nth : Display specific fields
|
||||
|
||||
# Example: search only filenames in paths
|
||||
fd | fzf -d / --with-nth -1
|
||||
|
||||
# Example: search only line numbers
|
||||
rg --line-number 'pattern' | fzf -d : --with-nth 2
|
||||
```
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### File Navigation
|
||||
|
||||
```bash
|
||||
# Find and edit file
|
||||
fe() {
|
||||
local file
|
||||
file=$(fd --type f | fzf --preview 'bat --color=always {}')
|
||||
[[ -n "$file" ]] && $EDITOR "$file"
|
||||
}
|
||||
|
||||
# Find and cd to directory
|
||||
fcd() {
|
||||
local dir
|
||||
dir=$(fd --type d | fzf --preview 'tree -L 1 {}')
|
||||
[[ -n "$dir" ]] && cd "$dir"
|
||||
}
|
||||
|
||||
# Find file with content preview
|
||||
ff() {
|
||||
local file
|
||||
file=$(rg --files-with-matches "${1:-.}" | \
|
||||
fzf --preview "rg --color=always --context 5 '$1' {}")
|
||||
[[ -n "$file" ]] && $EDITOR "$file"
|
||||
}
|
||||
```
|
||||
|
||||
### Code Search
|
||||
|
||||
```bash
|
||||
# Search content and edit
|
||||
fzf_grep() {
|
||||
local line
|
||||
line=$(rg --line-number "$1" | \
|
||||
fzf --delimiter : \
|
||||
--preview 'bat --color=always {1} --highlight-line {2}' \
|
||||
--preview-window +{2}-/2)
|
||||
|
||||
if [[ -n "$line" ]]; then
|
||||
local file=$(echo "$line" | cut -d: -f1)
|
||||
local lineno=$(echo "$line" | cut -d: -f2)
|
||||
$EDITOR "+$lineno" "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
# Find function definition and edit
|
||||
find_function() {
|
||||
local result
|
||||
result=$(ast-grep --pattern "def $1(\$\$\$):" | \
|
||||
fzf --preview 'bat --color=always {1}')
|
||||
|
||||
if [[ -n "$result" ]]; then
|
||||
local file=$(echo "$result" | cut -d: -f1)
|
||||
$EDITOR "$file"
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
### Git Integration
|
||||
|
||||
```bash
|
||||
# Interactive git add
|
||||
fga() {
|
||||
git status --short | \
|
||||
fzf -m --preview 'git diff --color=always {2}' | \
|
||||
awk '{print $2}' | \
|
||||
xargs git add
|
||||
}
|
||||
|
||||
# Interactive git checkout
|
||||
fgco() {
|
||||
git branch --all | \
|
||||
grep -v HEAD | \
|
||||
sed 's/^[* ]*//' | \
|
||||
sed 's#remotes/origin/##' | \
|
||||
sort -u | \
|
||||
fzf --preview 'git log --oneline --color=always {}' | \
|
||||
xargs git checkout
|
||||
}
|
||||
|
||||
# Interactive git log
|
||||
fgl() {
|
||||
git log --oneline --color=always | \
|
||||
fzf --ansi --preview 'git show --color=always {1}' | \
|
||||
awk '{print $1}' | \
|
||||
xargs git show
|
||||
}
|
||||
|
||||
# Interactive git diff
|
||||
fgd() {
|
||||
git diff --name-only | \
|
||||
fzf --preview 'git diff --color=always {}' | \
|
||||
xargs git diff
|
||||
}
|
||||
```
|
||||
|
||||
### Process Management
|
||||
|
||||
```bash
|
||||
# Interactive kill
|
||||
fkill() {
|
||||
local pid
|
||||
pid=$(ps aux | \
|
||||
sed 1d | \
|
||||
fzf -m --header='Select process to kill' | \
|
||||
awk '{print $2}')
|
||||
|
||||
if [[ -n "$pid" ]]; then
|
||||
echo "$pid" | xargs kill -${1:-9}
|
||||
fi
|
||||
}
|
||||
|
||||
# Interactive lsof (find what's using a port)
|
||||
fport() {
|
||||
local port
|
||||
port=$(lsof -i -P -n | \
|
||||
sed 1d | \
|
||||
fzf --header='Select connection' | \
|
||||
awk '{print $2}')
|
||||
|
||||
[[ -n "$port" ]] && echo "PID: $port"
|
||||
}
|
||||
```
|
||||
|
||||
### Docker Integration
|
||||
|
||||
```bash
|
||||
# Interactive container selection
|
||||
fdocker() {
|
||||
docker ps -a | \
|
||||
sed 1d | \
|
||||
fzf --header='Select container' | \
|
||||
awk '{print $1}'
|
||||
}
|
||||
|
||||
# Interactive container logs
|
||||
fdlogs() {
|
||||
local container
|
||||
container=$(fdocker)
|
||||
[[ -n "$container" ]] && docker logs -f "$container"
|
||||
}
|
||||
|
||||
# Interactive container exec
|
||||
fdexec() {
|
||||
local container
|
||||
container=$(fdocker)
|
||||
[[ -n "$container" ]] && docker exec -it "$container" /bin/bash
|
||||
}
|
||||
```
|
||||
|
||||
### Package Management
|
||||
|
||||
```bash
|
||||
# Interactive npm script runner
|
||||
fnpm() {
|
||||
local script
|
||||
script=$(cat package.json | \
|
||||
jq -r '.scripts | keys[]' | \
|
||||
fzf --preview 'jq -r ".scripts.{}" package.json')
|
||||
|
||||
[[ -n "$script" ]] && npm run "$script"
|
||||
}
|
||||
|
||||
# Interactive pip package info
|
||||
fpip() {
|
||||
pip list | \
|
||||
fzf --preview 'pip show {1}'
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Files
|
||||
|
||||
```bash
|
||||
# Edit config file
|
||||
fconf() {
|
||||
local file
|
||||
file=$(fd -H -t f \
|
||||
-e conf -e config -e json -e yaml -e yml -e toml -e ini \
|
||||
. "$HOME/.config" | \
|
||||
fzf --preview 'bat --color=always {}')
|
||||
|
||||
[[ -n "$file" ]] && $EDITOR "$file"
|
||||
}
|
||||
|
||||
# Edit dotfile
|
||||
fdot() {
|
||||
local file
|
||||
file=$(fd -H -t f '^\..*' "$HOME" --max-depth 1 | \
|
||||
fzf --preview 'bat --color=always {}')
|
||||
|
||||
[[ -n "$file" ]] && $EDITOR "$file"
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Configurations
|
||||
|
||||
### Color Schemes
|
||||
|
||||
```bash
|
||||
# Monokai
|
||||
export FZF_DEFAULT_OPTS='
|
||||
--color=fg:#f8f8f2,bg:#272822,hl:#66d9ef
|
||||
--color=fg+:#f8f8f2,bg+:#44475a,hl+:#66d9ef
|
||||
--color=info:#a6e22e,prompt:#f92672,pointer:#f92672
|
||||
--color=marker:#f92672,spinner:#a6e22e,header:#6272a4
|
||||
'
|
||||
|
||||
# Nord
|
||||
export FZF_DEFAULT_OPTS='
|
||||
--color=fg:#e5e9f0,bg:#3b4252,hl:#81a1c1
|
||||
--color=fg+:#e5e9f0,bg+:#434c5e,hl+:#81a1c1
|
||||
--color=info:#eacb8a,prompt:#bf616a,pointer:#b48ead
|
||||
--color=marker:#a3be8b,spinner:#b48ead,header:#a3be8b
|
||||
'
|
||||
|
||||
# Tokyo Night
|
||||
export FZF_DEFAULT_OPTS='
|
||||
--color=fg:#c0caf5,bg:#1a1b26,hl:#bb9af7
|
||||
--color=fg+:#c0caf5,bg+:#292e42,hl+:#7dcfff
|
||||
--color=info:#7aa2f7,prompt:#7dcfff,pointer:#7dcfff
|
||||
--color=marker:#9ece6a,spinner:#9ece6a,header:#9ece6a
|
||||
'
|
||||
```
|
||||
|
||||
### Layout Options
|
||||
|
||||
```bash
|
||||
# Reverse layout (prompt at top)
|
||||
fzf --reverse
|
||||
|
||||
# Full screen
|
||||
fzf --height=100%
|
||||
|
||||
# 40% of screen
|
||||
fzf --height=40%
|
||||
|
||||
# Border
|
||||
fzf --border
|
||||
fzf --border=rounded
|
||||
fzf --border=sharp
|
||||
|
||||
# Inline info
|
||||
fzf --inline-info
|
||||
|
||||
# No info
|
||||
fzf --no-info
|
||||
```
|
||||
|
||||
### Performance Options
|
||||
|
||||
```bash
|
||||
# Limit results
|
||||
fzf --select-1 # Auto-select if only one match
|
||||
fzf --exit-0 # Exit if no match
|
||||
|
||||
# Algorithm
|
||||
fzf --algo=v1 # Faster, less accurate
|
||||
fzf --algo=v2 # Default, balanced
|
||||
|
||||
# History
|
||||
fzf --history=/tmp/fzf-history
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```bash
|
||||
# Default command
|
||||
export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git'
|
||||
|
||||
# Default options
|
||||
export FZF_DEFAULT_OPTS='
|
||||
--height 40%
|
||||
--layout=reverse
|
||||
--border
|
||||
--inline-info
|
||||
--preview "bat --color=always --line-range :500 {}"
|
||||
--bind ctrl-/:toggle-preview
|
||||
'
|
||||
|
||||
# ctrl-t command (file widget)
|
||||
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
|
||||
export FZF_CTRL_T_OPTS="
|
||||
--preview 'bat --color=always --line-range :500 {}'
|
||||
--bind 'ctrl-/:toggle-preview'
|
||||
"
|
||||
|
||||
# alt-c command (directory widget)
|
||||
export FZF_ALT_C_COMMAND='fd --type d --hidden --exclude .git'
|
||||
export FZF_ALT_C_OPTS="
|
||||
--preview 'tree -L 1 {}'
|
||||
"
|
||||
```
|
||||
|
||||
## Shell Integration
|
||||
|
||||
### Bash/Zsh Key Bindings
|
||||
|
||||
```bash
|
||||
# Add to ~/.bashrc or ~/.zshrc
|
||||
source /usr/share/doc/fzf/examples/key-bindings.bash # Bash
|
||||
source /usr/share/doc/fzf/examples/key-bindings.zsh # Zsh
|
||||
|
||||
# Or via package manager
|
||||
eval "$(fzf --bash)" # Bash
|
||||
eval "$(fzf --zsh)" # Zsh
|
||||
```
|
||||
|
||||
Default bindings:
|
||||
|
||||
- `ctrl-t`: Paste selected files
|
||||
- `ctrl-r`: Paste from command history
|
||||
- `alt-c`: cd into selected directory
|
||||
|
||||
### Custom Shell Functions
|
||||
|
||||
```bash
|
||||
# Add to ~/.bashrc or ~/.zshrc
|
||||
|
||||
# fh - repeat history
|
||||
fh() {
|
||||
print -z $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | \
|
||||
fzf +s --tac | \
|
||||
sed -E 's/ *[0-9]*\*? *//' | \
|
||||
sed -E 's/\\/\\\\/g')
|
||||
}
|
||||
|
||||
# fbr - checkout git branch
|
||||
fbr() {
|
||||
local branches branch
|
||||
branches=$(git branch -vv) &&
|
||||
branch=$(echo "$branches" | fzf +m) &&
|
||||
git checkout $(echo "$branch" | awk '{print $1}' | sed "s/.* //")
|
||||
}
|
||||
|
||||
# fshow - git commit browser
|
||||
fshow() {
|
||||
git log --graph --color=always \
|
||||
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
|
||||
fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \
|
||||
--bind "ctrl-m:execute:
|
||||
(grep -o '[a-f0-9]\{7\}' | head -1 |
|
||||
xargs -I % sh -c 'git show --color=always % | less -R') << 'FZF-EOF'
|
||||
{}
|
||||
FZF-EOF"
|
||||
}
|
||||
```
|
||||
|
||||
## Tips and Tricks
|
||||
|
||||
### Performance
|
||||
|
||||
1. Use fd instead of find for FZF_DEFAULT_COMMAND
|
||||
2. Limit preview size with --line-range
|
||||
3. Use --select-1 and --exit-0 for automation
|
||||
4. Cache results for repeated searches
|
||||
|
||||
### User Experience
|
||||
|
||||
1. Always provide preview when possible
|
||||
2. Use semantic keybindings (ctrl-e for edit)
|
||||
3. Provide helpful --header messages
|
||||
4. Use --multi for batch operations
|
||||
5. Toggle preview with ctrl-/ for large files
|
||||
|
||||
### Integration
|
||||
|
||||
```bash
|
||||
# Combine with xargs
|
||||
fd -e py | fzf -m | xargs wc -l
|
||||
|
||||
# Combine with while read
|
||||
fd | fzf -m | while read file; do
|
||||
echo "Processing: $file"
|
||||
# Process file
|
||||
done
|
||||
|
||||
# Combine with command substitution
|
||||
vim $(fzf)
|
||||
cd $(fd -t d | fzf)
|
||||
```
|
||||
|
||||
### Scripting
|
||||
|
||||
```bash
|
||||
# Exit code check
|
||||
if result=$(fd | fzf); then
|
||||
echo "Selected: $result"
|
||||
else
|
||||
echo "No selection or cancelled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Multi-select results
|
||||
selected=$(fd | fzf -m)
|
||||
if [[ -n "$selected" ]]; then
|
||||
echo "$selected" | while read file; do
|
||||
process "$file"
|
||||
done
|
||||
fi
|
||||
```
|
||||
Reference in New Issue
Block a user