├── LICENSE
├── README.md
├── fzf-extras.sh
└── fzf-extras.zsh
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2024 Andy Weidenbaum
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a
6 | copy of this software and associated documentation files (the "Software"),
7 | to deal in the Software without restriction, including without limitation
8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 | and/or sell copies of the Software, and to permit persons to whom the
10 | Software is furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included
13 | in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 | OTHER DEALINGS IN THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # fzf-extras
2 |
3 | Additional key bindings for fzf, primarily Bash.
4 |
5 | ## Usage
6 |
7 | ### Bash
8 |
9 | **directory**
10 |
11 | bash cmdline | description
12 | --- | ---
13 | `zd` | 'fuzzy-finder' + 'cd' = 'zd', the super function of `zdd`, `zda`, `zdr`, `zdf`, `zst`, `zz`
14 | `zdd` | cd into selected directory
15 | `zda` | cd into selected directory, including hidden directories
16 | `zdr` | cd into selected parent directory
17 | `zdf` | cd into directory of selected file
18 | `zst` | cd into directory from stack
19 | `zz` | cd into selectable 'frecency' directory
20 |
21 | **file**
22 |
23 | bash cmdline | description
24 | --- | ---
25 | `e [FUZZY PATTERN]` | Open 'frecency' files with `$EDITOR`
26 | `fe [FUZZY PATTERN]` | Open selected file with `$EDITOR`
27 | `fo` | Equivalent to `fe`, but opens file with `$OPENER` (default: `xdg-open`) if you press Ctrl+O
28 | `v` | Open selected files from `~/.viminfo` with `$EDITOR`
29 |
30 | **git**
31 |
32 | bash cmdline | description
33 | --- | ---
34 | `fbr` | Checkout Git branch (including remote branches)
35 | `fco` | Checkout Git branch/tag
36 | `fcoc` | Checkout Git commit
37 | `fcs` | Get Git commit SHA hash
38 | `fshow` | Git commit browser
39 | `fstash` | Git stash management (Enter to show stash contents, Ctrl+D to show diff of stash against current HEAD, Ctrl+B to check stash out as a branch, for easier merging)
40 | `fzf-gitlog-multi-widget` | Multi-selectable `git show`
41 | `fzf-gitlog-widget` | Git log browser
42 |
43 | **history**
44 |
45 | bash cmdline | description
46 | --- | ---
47 | `fh` | Select line from history, repeat without editing
48 | `fhe` | Select line from history, leave for editing
49 | `runcmd` | Utility function used to run shell command
50 | `writecmd` | Utility function used to write shell command
51 |
52 | **pid**
53 |
54 | bash cmdline | description
55 | --- | ---
56 | `fkill` | Select process to kill (alternatively, type `kill`˽Tab)
57 |
58 | **tags**
59 |
60 | bash cmdline | description
61 | --- | ---
62 | `ftags` | Search ctags
63 |
64 | **tmux**
65 |
66 | bash cmdline | description
67 | --- | ---
68 | `fs [FUZZY PATTERN]` | Select tmux session
69 | `ftpane` | Switch pane
70 |
71 | ### Zsh
72 |
73 | **file**
74 |
75 | zsh cmdline | description
76 | --- | ---
77 | Alt-i | Paste selected entry from `locate` output into command line
78 |
79 | ## Installation
80 |
81 | ### Install fzf-extras
82 |
83 | **Arch Linux**
84 |
85 | Install [aur/fzf-extras](https://aur.archlinux.org/packages/fzf-extras).
86 |
87 | **Manual**
88 |
89 | ```sh
90 | git clone https://github.com/atweiden/fzf-extras ~/.fzf-extras
91 | ```
92 |
93 | ### Configure fzf-extras
94 |
95 | To make use of function `fo`, consider setting the `$OPENER` environment
96 | variable. If `$OPENER` is unset, `fo` will attempt to open files with
97 | `xdg-open` when pressing Ctrl+O.
98 |
99 | **Arch Linux**
100 |
101 | ```sh
102 | cat >> ~/.bashrc <<'EOF'
103 | OPENER=mimeo
104 | EOF
105 | ```
106 |
107 | **macOS**
108 |
109 | ```sh
110 | cat >> ~/.bashrc <<'EOF'
111 | OPENER=open
112 | EOF
113 | ```
114 |
115 | ### Source fzf-extras
116 |
117 | **Arch Linux**
118 |
119 | ```sh
120 | # bash users only
121 | cat >> ~/.bashrc <<'EOF'
122 | [[ -e "/usr/share/fzf/fzf-extras.bash" ]] \
123 | && source /usr/share/fzf/fzf-extras.bash
124 | EOF
125 |
126 | # zsh users only
127 | cat >> ~/.zshrc <<'EOF'
128 | [[ -e "/usr/share/fzf/fzf-extras.zsh" ]] \
129 | && source /usr/share/fzf/fzf-extras.zsh
130 | EOF
131 | ```
132 |
133 | **Manual**
134 |
135 | ```sh
136 | # bash users only
137 | cat >> ~/.bashrc <<'EOF'
138 | [[ -e "$HOME/.fzf-extras/fzf-extras.sh" ]] \
139 | && source "$HOME/.fzf-extras/fzf-extras.sh"
140 | EOF
141 |
142 | # zsh users only
143 | cat >> ~/.zshrc <<'EOF'
144 | [[ -e "$HOME/.fzf-extras/fzf-extras.zsh" ]] \
145 | && source "$HOME/.fzf-extras/fzf-extras.zsh"
146 | EOF
147 | ```
148 |
149 | Zsh users should not be sourcing `fzf-extras.sh`.
150 |
151 | The lack of meaningful support for Zsh will be fixed pending suitable
152 | PRs from Zsh-using contributors. Note it is perfectly acceptable
153 | to duplicate code from `fzf-extras.sh` into `fzf-extras.zsh`. See:
154 | https://github.com/atweiden/fzf-extras/issues/12.
155 |
156 | ## Dependencies
157 |
158 | **Required**
159 |
160 | - [bash](https://www.gnu.org/software/bash/) or [zsh](https://www.zsh.org/)
161 | - [fzf](https://github.com/junegunn/fzf)
162 | - [tmux](https://github.com/tmux/tmux)
163 |
164 | **Optional**
165 |
166 | - [ctags](https://github.com/universal-ctags/ctags)
167 | - [fasd](https://github.com/clvv/fasd)
168 | - [git](https://git-scm.com/)
169 | - [mlocate](https://pagure.io/mlocate)
170 | - [vim](https://www.vim.org/)
171 | - [xdg-utils](https://www.freedesktop.org/wiki/Software/xdg-utils/)
172 |
173 | ## Sources
174 |
175 | - [fzf/wiki](https://github.com/junegunn/fzf/wiki)
176 | - [junegunn/dotfiles](https://github.com/junegunn/dotfiles)
177 |
178 | ## See Also
179 |
180 | - [DanielFGray/fzf-scripts](https://github.com/DanielFGray/fzf-scripts)
181 |
182 | ## License
183 |
184 | [MIT](LICENSE)
185 |
--------------------------------------------------------------------------------
/fzf-extras.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # -----------------------------------------------------------------------------
4 | # directory
5 | # -----------------------------------------------------------------------------
6 |
7 | # fzf --preview command for file and directory
8 | if type bat >/dev/null 2>&1; then
9 | FZF_PREVIEW_CMD='bat --color=always --plain --line-range :$FZF_PREVIEW_LINES {}'
10 | elif type pygmentize >/dev/null 2>&1; then
11 | FZF_PREVIEW_CMD='head -n $FZF_PREVIEW_LINES {} | pygmentize -g'
12 | else
13 | FZF_PREVIEW_CMD='head -n $FZF_PREVIEW_LINES {}'
14 | fi
15 |
16 | # zdd - cd to selected directory
17 | zdd() {
18 | local dir
19 | dir="$(
20 | find "${1:-.}" -path '*/\.*' -prune -o -type d -print 2> /dev/null \
21 | | fzf +m \
22 | --preview='tree -C {} | head -n $FZF_PREVIEW_LINES' \
23 | --preview-window='right:hidden:wrap' \
24 | --bind=ctrl-v:toggle-preview \
25 | --bind=ctrl-x:toggle-sort \
26 | --header='(view:ctrl-v) (sort:ctrl-x)' \
27 | )" || return
28 | cd "$dir" || return
29 | }
30 |
31 | # zda - including hidden directories
32 | zda() {
33 | local dir
34 | dir="$(
35 | find "${1:-.}" -type d 2> /dev/null \
36 | | fzf +m \
37 | --preview='tree -C {} | head -n $FZF_PREVIEW_LINES' \
38 | --preview-window='right:hidden:wrap' \
39 | --bind=ctrl-v:toggle-preview \
40 | --bind=ctrl-x:toggle-sort \
41 | --header='(view:ctrl-v) (sort:ctrl-x)' \
42 | )" || return
43 | cd "$dir" || return
44 | }
45 |
46 | # zdr - cd to selected parent directory
47 | zdr() {
48 | local dirs=()
49 | local parent_dir
50 |
51 | get_parent_dirs() {
52 | if [[ -d "$1" ]]; then dirs+=("$1"); else return; fi
53 | if [[ "$1" == '/' ]]; then
54 | for _dir in "${dirs[@]}"; do echo "$_dir"; done
55 | else
56 | get_parent_dirs "$(dirname "$1")"
57 | fi
58 | }
59 |
60 | parent_dir="$(
61 | get_parent_dirs "$(realpath "${1:-$PWD}")" \
62 | | fzf +m \
63 | --preview 'tree -C {} | head -n $FZF_PREVIEW_LINES' \
64 | --preview-window='right:hidden:wrap' \
65 | --bind=ctrl-v:toggle-preview \
66 | --bind=ctrl-x:toggle-sort \
67 | --header='(view:ctrl-v) (sort:ctrl-x)' \
68 | )" || return
69 |
70 | cd "$parent_dir" || return
71 | }
72 |
73 | # zst - cd into the directory from stack
74 | zst() {
75 | local dir
76 | dir="$(
77 | dirs \
78 | | sed 's#\s#\n#g' \
79 | | uniq \
80 | | sed "s#^~#$HOME#" \
81 | | fzf +s +m -1 -q "$*" \
82 | --preview='tree -C {} | head -n $FZF_PREVIEW_LINES' \
83 | --preview-window='right:hidden:wrap' \
84 | --bind=ctrl-v:toggle-preview \
85 | --bind=ctrl-x:toggle-sort \
86 | --header='(view:ctrl-v) (sort:ctrl-x)' \
87 | )"
88 | # check $dir exists for Ctrl-C interrupt
89 | # or change directory to $HOME (= no value cd)
90 | if [[ -d "$dir" ]]; then
91 | cd "$dir" || return
92 | fi
93 | }
94 |
95 | # zdf - cd into the directory of the selected file
96 | zdf() {
97 | local file
98 | file="$(fzf +m -q "$*" \
99 | --preview="${FZF_PREVIEW_CMD}" \
100 | --preview-window='right:hidden:wrap' \
101 | --bind=ctrl-v:toggle-preview \
102 | --bind=ctrl-x:toggle-sort \
103 | --header='(view:ctrl-v) (sort:ctrl-x)' \
104 | )"
105 | cd "$(dirname "$file")" || return
106 | }
107 |
108 | # zz - selectable cd to frecency directory
109 | zz() {
110 | local dir
111 |
112 | dir="$(
113 | fasd -dl \
114 | | fzf \
115 | --tac \
116 | --reverse \
117 | --select-1 \
118 | --no-sort \
119 | --no-multi \
120 | --tiebreak=index \
121 | --bind=ctrl-x:toggle-sort \
122 | --query "$*" \
123 | --preview='tree -C {} | head -n $FZF_PREVIEW_LINES' \
124 | --preview-window='right:hidden:wrap' \
125 | --bind=ctrl-v:toggle-preview \
126 | --bind=ctrl-x:toggle-sort \
127 | --header='(view:ctrl-v) (sort:ctrl-x)' \
128 | )" || return
129 |
130 | cd "$dir" || return
131 | }
132 |
133 | # zd - cd into selected directory with options
134 | # The super function of zdd, zda, zdr, zst, zdf, zz
135 | zd() {
136 | read -r -d '' helptext <'
546 | }
547 |
548 | # fh - repeat history
549 | fh() {
550 | ([[ -n "$ZSH_NAME" ]] && fc -l 1 || history) \
551 | | fzf +s --tac \
552 | | sed -re 's/^\s*[0-9]+\s*//' \
553 | | runcmd
554 | }
555 |
556 | # writecmd - utility function used to write the command in the shell
557 | writecmd() {
558 | perl -e 'ioctl STDOUT, 0x5412, $_ for split //, do { chomp($_ = <>); $_ }'
559 | }
560 |
561 | # fhe - repeat history edit
562 | fhe() {
563 | ([[ -n "$ZSH_NAME" ]] && fc -l 1 || history) \
564 | | fzf +s --tac \
565 | | sed -re 's/^\s*[0-9]+\s*//' \
566 | | writecmd
567 | }
568 |
569 |
570 | # -----------------------------------------------------------------------------
571 | # pid
572 | # -----------------------------------------------------------------------------
573 |
574 | # fkill - kill process
575 | fkill() {
576 | local pid
577 |
578 | pid="$(
579 | ps -ef \
580 | | sed 1d \
581 | | fzf -m \
582 | | awk '{print $2}'
583 | )" || return
584 |
585 | kill -"${1:-9}" "$pid"
586 | }
587 |
588 |
589 | # -----------------------------------------------------------------------------
590 | # tags
591 | # -----------------------------------------------------------------------------
592 |
593 | # ftags - search ctags
594 | ftags() {
595 | local line
596 |
597 | [[ -e 'tags' ]] || return
598 |
599 | line="$(
600 | awk 'BEGIN { FS="\t" } !/^!/ {print toupper($4)"\t"$1"\t"$2"\t"$3}' tags \
601 | | cut -c1-"$COLUMNS" \
602 | | fzf --nth=2 --tiebreak=begin
603 | )" || return
604 |
605 | "${EDITOR:-vim}" "$(cut -f3 <<< "$line")" \
606 | -c 'set nocst' \
607 | -c "silent tag \"$(cut -f2 <<< "$line")\""
608 | }
609 |
610 |
611 | # -----------------------------------------------------------------------------
612 | # tmux
613 | # -----------------------------------------------------------------------------
614 |
615 | # fs [FUZZY PATTERN] - Select selected tmux session
616 | # - Bypass fuzzy finder if there's only one match (--select-1)
617 | # - Exit if there's no match (--exit-0)
618 | fs() {
619 | local session
620 |
621 | session="$(
622 | tmux list-sessions -F "#{session_name}" \
623 | | fzf-tmux \
624 | --query="$1" \
625 | --select-1 \
626 | --exit-0
627 | )" || return
628 |
629 | tmux switch-client -t "$session"
630 | }
631 |
632 | # ftpane - switch pane (@george-b)
633 | ftpane() {
634 | local panes
635 | local current_window
636 | local current_pane
637 | local target
638 | local target_window
639 | local target_pane
640 |
641 | panes="$(
642 | tmux list-panes \
643 | -s \
644 | -F '#I:#P - #{pane_current_path} #{pane_current_command}'
645 | )"
646 | current_pane="$(tmux display-message -p '#I:#P')"
647 | current_window="$(tmux display-message -p '#I')"
648 |
649 | target="$(
650 | echo "$panes" \
651 | | grep -v "$current_pane" \
652 | | fzf +m --reverse
653 | )" || return
654 |
655 | target_window="$(
656 | echo "$target" \
657 | | awk 'BEGIN{FS=":|-"} {print$1}'
658 | )"
659 |
660 | target_pane="$(
661 | echo "$target" \
662 | | awk 'BEGIN{FS=":|-"} {print$2}' \
663 | | cut -c 1
664 | )"
665 |
666 | if [[ "$current_window" -eq "$target_window" ]]; then
667 | tmux select-pane -t "$target_window.$target_pane"
668 | else
669 | tmux select-pane -t "$target_window.$target_pane" \
670 | && tmux select-window -t "$target_window"
671 | fi
672 | }
673 |
674 | # vim: set filetype=sh foldmethod=marker foldlevel=0:
675 |
--------------------------------------------------------------------------------
/fzf-extras.zsh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env zsh
2 |
3 | if [[ $- =~ i ]]; then
4 |
5 | # ALT-I - Paste the selected entry from locate output into the command line
6 | fzf-locate-widget() {
7 | local selected
8 | if selected=$(locate / | fzf -q "$LBUFFER"); then
9 | LBUFFER=$selected
10 | fi
11 | zle redisplay
12 | }
13 | zle -N fzf-locate-widget
14 | bindkey '\ei' fzf-locate-widget
15 |
16 | fi
17 |
--------------------------------------------------------------------------------