├── src ├── .gitkeep ├── grep-file-contents.sh ├── history-repeat.bash ├── locate.tmux ├── cd-dir-hidden.sh ├── history-repeat2.bash ├── mru.vim ├── z.bash ├── cd-dir-file.sh ├── open-file.sh ├── kill-process.sh ├── cd-dir.sh ├── git-checkout-branch.sh ├── git-checkout-commit.sh ├── tags-jump.vim ├── edit-file.sh ├── ctags-search.bash ├── tmux-session-select.sh ├── history-repeat3.bash ├── history-repeat-edit.bash ├── z1.bash ├── ag-results.vim ├── search-buffer-lines.vim ├── tmux-pane-switch.sh └── completion-cmdl.vim ├── INFO.csv ├── README.md └── fzf-contrib /src/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/grep-file-contents.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | grep --line-buffered --color=never -r "" * | fzf 4 | -------------------------------------------------------------------------------- /src/history-repeat.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # fh - repeat history 4 | fh() { 5 | eval $(([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s | sed 's/ *[0-9]* *//') 6 | } 7 | -------------------------------------------------------------------------------- /src/locate.tmux: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env tmux 2 | 3 | # fzf-locate 4 | bind-key -n 'M-l' run "tmux split-window -p 40 'tmux send-keys -t #{pane_id} \"$(locate / | fzf -m | paste -sd\\ -)\"'" 5 | -------------------------------------------------------------------------------- /src/cd-dir-hidden.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fda - including hidden directories 4 | fda() { 5 | local dir 6 | dir=$(find ${1:-.} -type d 2> /dev/null | fzf +m) && cd "$dir" 7 | } 8 | -------------------------------------------------------------------------------- /src/history-repeat2.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # fh - repeat history 4 | fh() { 5 | print -z $(([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s | sed 's/ *[0-9]* *//') 6 | } 7 | -------------------------------------------------------------------------------- /src/mru.vim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vim 2 | 3 | command! FZFMru call fzf#run({ 4 | \'source': v:oldfiles, 5 | \'sink' : 'e ', 6 | \'options' : '-m', 7 | \}) 8 | -------------------------------------------------------------------------------- /src/z.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | unalias z &>/dev/null 4 | z() { 5 | if [[ -z "$*" ]]; then 6 | cd "$(_z -l 2>&1 | sed -n 's/^[ 0-9.,]*//p' | fzf)" 7 | else 8 | _z "$@" 9 | fi 10 | } 11 | -------------------------------------------------------------------------------- /src/cd-dir-file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # cdf - cd into the directory of the selected file 4 | cdf() { 5 | local file 6 | local dir 7 | file=$(fzf +m -q "$1") && dir=$(dirname "$file") && cd "$dir" 8 | } 9 | -------------------------------------------------------------------------------- /src/open-file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Equivalent to above, but opens it with `open` command 4 | fo() { 5 | local file 6 | file=$(fzf --query="$1" --select-1 --exit-0) 7 | [ -n "$file" ] && open "$file" 8 | } 9 | -------------------------------------------------------------------------------- /src/kill-process.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fkill - kill process 4 | fkill() { 5 | pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}') 6 | 7 | if [ "x$pid" != "x" ] 8 | then 9 | kill -${1:-9} $pid 10 | fi 11 | } 12 | -------------------------------------------------------------------------------- /src/cd-dir.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fd - cd to selected directory 4 | fd() { 5 | local dir 6 | dir=$(find ${1:-*} -path '*/\.*' -prune \ 7 | -o -type d -print 2> /dev/null | fzf +m) && 8 | cd "$dir" 9 | } 10 | -------------------------------------------------------------------------------- /src/git-checkout-branch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fbr - checkout git branch 4 | fbr() { 5 | local branches branch 6 | branches=$(git branch) && 7 | branch=$(echo "$branches" | fzf +s +m) && 8 | git checkout $(echo "$branch" | sed "s/.* //") 9 | } 10 | -------------------------------------------------------------------------------- /src/git-checkout-commit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fco - checkout git commit 4 | fco() { 5 | local commits commit 6 | commits=$(git log --pretty=oneline --abbrev-commit --reverse) && 7 | commit=$(echo "$commits" | fzf +s +m -e) && 8 | git checkout $(echo "$commit" | sed "s/ .*//") 9 | } 10 | -------------------------------------------------------------------------------- /src/tags-jump.vim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vim 2 | 3 | function! TagCommand() 4 | return substitute('awk _!/^!/ { print \$1 }_ ', '_', "'", 'g') 5 | \ . join(tagfiles(), ' ') 6 | endfunction 7 | 8 | command! FZFTag call fzf#run({ 9 | \ 'source' : TagCommand(), 10 | \ 'sink' : 'tag', 11 | \ }) 12 | -------------------------------------------------------------------------------- /src/edit-file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fe [FUZZY PATTERN] - Open the selected file with the default editor 4 | # - Bypass fuzzy finder if there's only one match (--select-1) 5 | # - Exit if there's no match (--exit-0) 6 | fe() { 7 | local file 8 | file=$(fzf --query="$1" --select-1 --exit-0) 9 | [ -n "$file" ] && ${EDITOR:-vim} "$file" 10 | } 11 | -------------------------------------------------------------------------------- /src/ctags-search.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ftags - search ctags 4 | ftags() { 5 | local line 6 | [ -e tags ] && 7 | line=$( 8 | awk 'BEGIN { FS="\t" } !/^!/ {print toupper($4)"\t"$1"\t"$2"\t"$3}' tags | 9 | cut -c1-80 | fzf --nth=1,2 10 | ) && $EDITOR $(cut -f3 <<< "$line") -c "set nocst" \ 11 | -c "silent tag $(cut -f2 <<< "$line")" 12 | } 13 | -------------------------------------------------------------------------------- /src/tmux-session-select.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fs [FUZZY PATTERN] - Select selected tmux session 4 | # - Bypass fuzzy finder if there's only one match (--select-1) 5 | # - Exit if there's no match (--exit-0) 6 | fs() { 7 | local session 8 | session=$(tmux list-sessions -F "#{session_name}" | \ 9 | fzf --query="$1" --select-1 --exit-0) && 10 | tmux switch-client -t "$session" 11 | } 12 | -------------------------------------------------------------------------------- /src/history-repeat3.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # utility function used to write the command in the shell 4 | writecmd() { 5 | perl -e '$TIOCSTI = 0x5412; $l = ; $lc = $ARGV[0] eq "-run" ? "\n" : ""; $l =~ s/\s*$/$lc/; map { ioctl STDOUT, $TIOCSTI, $_; } split "", $l;' -- $1 6 | } 7 | 8 | # fh - repeat history 9 | fh() { 10 | ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s | sed -re 's/^\s*[0-9]+\s*//' | writecmd -run 11 | } 12 | -------------------------------------------------------------------------------- /src/history-repeat-edit.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # utility function used to write the command in the shell 4 | writecmd() { 5 | perl -e '$TIOCSTI = 0x5412; $l = ; $lc = $ARGV[0] eq "-run" ? "\n" : ""; $l =~ s/\s*$/$lc/; map { ioctl STDOUT, $TIOCSTI, $_; } split "", $l;' -- $1 6 | } 7 | 8 | # fhe - repeat history edit 9 | fhe() { 10 | ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s | sed -re 's/^\s*[0-9]+\s*//' | writecmd 11 | } 12 | -------------------------------------------------------------------------------- /src/z1.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | unalias z &>/dev/null 4 | z() { 5 | if [[ -z "$*" ]]; then 6 | cd "$(_z -l 2>&1 | sed -n 's/^[ 0-9.,]*//p' | fzf)" 7 | else 8 | _last_z_args="$@" 9 | _z "$@" 10 | fi 11 | } 12 | 13 | zz() { 14 | cd "$(_z -l 2>&1 | sed -n 's/^[ 0-9.,]*//p' | fzf -q $_last_z_args)" 15 | } 16 | # Since z is not very optimal located on a qwerty keyboard I have these aliased as j and jj 17 | 18 | alias j=z 19 | alias jj=zz 20 | -------------------------------------------------------------------------------- /src/ag-results.vim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vim 2 | 3 | add this to your .vimrc 4 | 5 | command! -nargs=1 AgFZF call fzf#run({ 6 | \'source': Arghandler(), 7 | \'sink' : function('AgHandler'), 8 | \'options' : '-m' 9 | \}) 10 | 11 | function! AgHandler(l) 12 | let keys = split(a:l,':') 13 | execute 'tabe +' . keys[-2] . ' ' . escape(keys[-1], ' ') 14 | endfunction 15 | 16 | function! Arghandler(l) 17 | return "ag -i " . a:l . " | sed 's@\\(.[^:]*\\):\\(.[^:]*\\):\\(.*\\)@\\3:\\2:\\1@' " 18 | endfunction 19 | -------------------------------------------------------------------------------- /src/search-buffer-lines.vim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vim 2 | 3 | command! FZFLines call fzf#run({ 4 | \ 'source': BuffersLines(), 5 | \ 'sink': function('LineHandler'), 6 | \ 'options': '--extended --nth=3..', 7 | \ 'tmux_height': '60%' 8 | \}) 9 | 10 | function! LineHandler(l) 11 | let keys = split(a:l, ':\t') 12 | exec 'buf ' . keys[0] 13 | exec keys[1] 14 | normal! ^zz 15 | endfunction 16 | 17 | function! BuffersLines() 18 | let res = [] 19 | for b in filter(range(1, bufnr('$')), 'buflisted(v:val)') 20 | call extend(res, map(getbufline(b,0,"$"), 'b . ":\t" . (v:key + 1) . ":\t" . v:val ')) 21 | endfor 22 | return res 23 | endfunction 24 | -------------------------------------------------------------------------------- /src/tmux-pane-switch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # ftpane - switch pane 4 | ftpane () { 5 | local panes current_window target target_window target_pane 6 | panes=$(tmux list-panes -s -F '#I:#P - #{pane_current_path} #{pane_current_command}') 7 | current_window=$(tmux display-message -p '#I') 8 | 9 | target=$(echo "$panes" | fzf) || return 10 | 11 | target_window=$(echo $target | awk 'BEGIN{FS=":|-"} {print$1}') 12 | target_pane=$(echo $target | awk 'BEGIN{FS=":|-"} {print$2}' | cut -c 1) 13 | 14 | if [[ $current_window -eq $target_window ]]; then 15 | tmux select-pane -t ${target_window}.${target_pane} 16 | else 17 | tmux select-pane -t ${target_window}.${target_pane} && 18 | tmux select-window -t $target_window 19 | fi 20 | } 21 | -------------------------------------------------------------------------------- /INFO.csv: -------------------------------------------------------------------------------- 1 | ag-results.vim|"vim"|"ag;searching"|"" 2 | cd-dir-file.sh|"sh"|""|"" 3 | cd-dir-hidden.sh|"sh"|""|"" 4 | cd-dir.sh|"sh"|""|"" 5 | completion-cmdl.vim|"vim"|"completion;commandline"|"" 6 | ctags-search.bash|""|""|"" 7 | edit-file.sh|"sh"|""|"" 8 | git-checkout-branch.sh|"sh"|""|"" 9 | git-checkout-commit.sh|"sh"|""|"" 10 | grep-file-contents.sh|"sh"|""|"" 11 | history-repeat-edit.bash|""|""|"" 12 | history-repeat.bash|""|""|"" 13 | history-repeat2.bash|""|""|"" 14 | history-repeat3.bash|""|""|"" 15 | kill-process.sh|"sh"|""|"" 16 | locate.tmux|"tmux"|"locate;search"|"" 17 | mru.vim|"vim"|""|"" 18 | open-file.sh|"sh"|""|"" 19 | search-buffer-lines.vim|"vim"|"search;buffer"|"" 20 | tags-jump.vim|"vim"|"tags;jumping"|"" 21 | tmux-pane-switch.sh|"sh"|""|"" 22 | tmux-session-select.sh|"sh"|""|"" 23 | z.bash|"bash"|""|"" 24 | z1.bash|""|""|"" 25 | -------------------------------------------------------------------------------- /src/completion-cmdl.vim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env vim 2 | 3 | cnoremap eGetCompletions() 4 | "add an extra at the end of this line to automatically accept the fzf-selected completions. 5 | 6 | function! Lister() 7 | call extend(g:FZF_Cmd_Completion_Pre_List,split(getcmdline(),'\(\\\zs\)\@\eLister()\\" 31 | let l:FZF_Cmd_Completion_List = g:FZF_Cmd_Completion_Pre_List[len(l:Prefix):-1] 32 | unlet g:FZF_Cmd_Completion_Pre_List 33 | if len(l:Prefix) > 0 && l:Prefix[0] =~ 34 | \ '^ed\=i\=t\=$\|^spl\=i\=t\=$\|^tabed\=i\=t\=$\|^arged\=i\=t\=$\|^vsp\=l\=i\=t\=$' 35 | "single-argument file commands 36 | return CmdLineDirComplete(l:Prefix, "",l:cmdline_list[-1]) 37 | elseif len(l:Prefix) > 0 && l:Prefix[0] =~ 38 | \ '^arg\=s\=$\|^ne\=x\=t\=$\|^sne\=x\=t\=$\|^argad\=d\=$' 39 | "multi-argument file commands 40 | return CmdLineDirComplete(l:Prefix, '--multi', l:cmdline_list[-1]) 41 | else 42 | return join(l:Prefix + fzf#run({ 43 | \'source':l:FZF_Cmd_Completion_List, 44 | \'options': '--select-1 --query='.shellescape(l:cmdline_list[-1]) 45 | \})) 46 | endif 47 | endfunction 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## fzf-contrib 2 | 3 | `fzf-contrib`(1) stores and organizes useful code snippets around the general-purpose fuzzy finder [fzf](https://github.com/junegunn/fzf). 4 | 5 | ### Project Structure 6 | 7 | #### fzf-contrib(5) repo 8 | 9 | ``` 10 | # tree -a -P "*" -n --noreport -L 20 --charset=ascii "$PWD" 11 | /home/user1/var/code/projects/fzf-contrib 12 | |-- INFO.csv 13 | |-- README.md 14 | |-- fzf-contrib 15 | `-- src 16 | |-- .gitkeep 17 | |-- ag-results.vim 18 | |-- cd-dir-file.sh 19 | |-- cd-dir-hidden.sh 20 | |-- cd-dir.sh 21 | |-- completion-cmdl.vim 22 | |-- ctags-search.bash 23 | |-- edit-file.sh 24 | |-- git-checkout-branch.sh 25 | |-- git-checkout-commit.sh 26 | |-- grep-file-contents.sh 27 | |-- history-repeat-edit.bash 28 | |-- history-repeat.bash 29 | |-- history-repeat2.bash 30 | |-- history-repeat3.bash 31 | |-- kill-process.sh 32 | |-- locate.tmux 33 | |-- mru.vim 34 | |-- open-file.sh 35 | |-- search-buffer-lines.vim 36 | |-- tags-jump.vim 37 | |-- tmux-pane-switch.sh 38 | |-- tmux-session-select.sh 39 | |-- z.bash 40 | `-- z1.bash 41 | ``` 42 | 43 | Any src file needs to have an accurate filename with a filename extension, and a shebang formed like this: 44 | 45 | ``` 46 | #!/usr/bin/env 47 | #!/usr/bin/env sh 48 | #!/usr/bin/env vim 49 | ``` 50 | 51 | Further, it should be recorded in the file called `INFO.csv`. The fields are: 52 | 53 | ``` 54 | name 55 | applications 56 | tags 57 | description 58 | ``` 59 | 60 | #### fzf-custom dir 61 | 62 | It is intended to use selected code snippets in a second directory, independently from the `fzf-contrib` repo. The environment variable `FZF_CONTRIB_DIR` points to `${XDG_DATA_HOME:-${HOME}/.local/share}/fzf-custom` by default: 63 | 64 | ``` 65 | # tree -a -P "*" -n --noreport -L 20 --charset=ascii "$PWD" 66 | /home/user1/share/fzf-custom 67 | |-- INFO.csv 68 | |-- LINKS.txt 69 | |-- rc 70 | | |-- .gitkeep 71 | | |-- bash 72 | | | |-- .gitkeep 73 | | | |-- z.bash 74 | | | `-- z1.bash 75 | | |-- fish 76 | | | `-- .gitkeep 77 | | |-- mksh 78 | | | `-- .gitkeep 79 | | |-- tmux 80 | | | `-- .gitkeep 81 | | |-- vim 82 | | | `-- .gitkeep 83 | | `-- zsh 84 | | `-- .gitkeep 85 | `-- src 86 | |-- ag-results.vim 87 | |-- cd-dir-file.sh 88 | |-- cd-dir-hidden.sh 89 | |-- cd-dir.sh 90 | |-- completion-cmdl.vim 91 | |-- ctags-search.bash 92 | |-- edit-file.sh 93 | |-- git-checkout-branch.sh 94 | |-- git-checkout-commit.sh 95 | |-- grep-file-contents.sh 96 | |-- history-repeat-edit.bash 97 | |-- history-repeat.bash 98 | |-- history-repeat2.bash 99 | |-- history-repeat3.bash 100 | |-- kill-process.sh 101 | |-- locate.tmux 102 | |-- mru.vim 103 | |-- open-file.sh 104 | |-- search-buffer-lines.vim 105 | |-- tags-jump.vim 106 | |-- tmux-pane-switch.sh 107 | |-- tmux-session-select.sh 108 | |-- z.bash 109 | `-- z1.bash 110 | ``` 111 | 112 | Files are stored in `src/` and will be manually loaded (hard linked) into several `rc/` subdirectories. `LINKS.txt` tracks all linking; it may be used if `fzf-custom` is a git repository and we need to relink into `rc/` in a git hook post script. 113 | 114 | ### fzf-contrib(1) command 115 | 116 | `fzf-contrib`(1) follows the Shell and Utilities portion of the POSIX specification. It is written to copy files from `fzf-contrib` to `fzf-custom` and to maintain several code snippets inside `fzf-custom`. 117 | 118 | #### Install 119 | 120 | * Do `git clone https://github.com/D630/fzf-contrib.git` 121 | * Copy the shell script `fzf-contrib` elsewhere into `` 122 | * `cd fzf-contrib/ && ./fzf-contrib init` 123 | 124 | #### Help 125 | 126 | ``` 127 | Usage: fzf-contrib [add|help|info|init|load|reload|uload|version] 128 | 129 | Subcommands: 130 | add [d=] Force a copy of a src file from 131 | to 132 | help Show this instruction 133 | info [] Output records from INFO.csv 134 | init Initialize local src dir from the 135 | ; copy its contents to 136 | 137 | load [a=] [d=] Hard link a src file from 138 | into 139 | 140 | reload Remove all hardlinks in 141 | and source . 142 | Use this command in a hook script like 143 | <.git/hooks/post-merge> 144 | uload [] [a=] Remove hard link from 145 | 146 | Arguments: 147 | Name of application and subdirectory in 148 | . Delimite apps with 149 | comma: 'zsh,mksh,bash' 150 | New name of the current script in 151 | 152 | Grep BRE pattern 153 | Basename of the script in the current src dir 154 | 155 | Environment variables: 156 | FZF_CONTRIB_DIR 157 | ${XDG_DATA_HOME:-${HOME}/.local/share}/fzf-custom 158 | ``` 159 | 160 | #### Examples 161 | 162 | To initialize `fzf-custom/` and add new snippets to `fzf-custom/src/`: 163 | 164 | ``` 165 | % cd fzf-contrib/ 166 | % ./fzf-contrib info | less 167 | % ./fzf-contrib init 168 | > Create '/home/user1/share/fzf-custom/src' 169 | > Copy '/home/user1/var/code/projects/fzf-contrib/INFO.csv' -> '/home/user1/share/fzf-custom/INFO.csv' 170 | > Copy '/home/user1/var/code/projects/fzf-contrib/src/ag-results.vim' -> '/home/user1/share/fzf-custom/src/ag-results.vim' 171 | > Copy '/home/user1/var/code/projects/fzf-contrib/src/cd-dir-file.sh' -> '/home/user1/share/fzf-custom/src/cd-dir-file.sh' 172 | > Copy '/home/user1/var/code/projects/fzf-contrib/src/cd-dir-hidden.sh' -> '/home/user1/share/fzf-custom/src/cd-dir-hidden.sh' 173 | > [...] 174 | % ./fzf-contrib info z.bash | less 175 | % ./fzf-contrib add z.bash 176 | > Copy '/home/user1/var/code/projects/fzf-contrib/src/z.bash' -> '/home/user1/share/fzf-custom/src/z.bash' 177 | % ./fzf-contrib add z.bash d=Z 178 | > Copy '/home/user1/var/code/projects/fzf-contrib/src/z.bash' -> '/home/user1/share/fzf-custom/src/Z' 179 | ``` 180 | 181 | Edit `z.bash`: 182 | 183 | ``` 184 | % cd 185 | % ls ~/share/fzf-custom/ 186 | > INFO.csv rc src 187 | % ~/share/fzf-custom/src/z.bash 188 | ``` 189 | 190 | Load `z.bash` based on shebang: 191 | 192 | ``` 193 | % fzf-contrib load z.bash 194 | > Create '/home/user1/share/fzf-custom/src/z.bash' -> '/home/user1/share/fzf-custom/rc/bash/z.bash' 195 | ``` 196 | 197 | Load `z.bash` based on shebang and rename it to `Z`: 198 | 199 | ``` 200 | % fzf-contrib load z.bash d=Z 201 | > Create '/home/user1/share/fzf-custom/src/z.bash' -> '/home/user1/share/fzf-custom/rc/bash/Z' 202 | ``` 203 | 204 | Load `kill-process.sh` based on shebang. Since `bash`(1), `mksh`(1) and `zsh`(1) are sh-compatible, the script is loaded three times: 205 | 206 | ``` 207 | % fzf-contrib load kill-process.sh 208 | > Create '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/bash/kill-process.sh' 209 | > Create '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/mksh/kill-process.sh' 210 | > Create '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/zsh/kill-process.sh' 211 | ``` 212 | 213 | Unload/Remove `kill-process.sh` from `fzf-custom/rc/{mksh,zsh}/`: 214 | 215 | ``` 216 | % fzf-contrib uload kill-process.sh a=mksh,zsh 217 | > Remove '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/mksh/kill-process.sh' 218 | > Remove '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/zsh/kill-process.sh' 219 | ``` 220 | 221 | Load `kill-process.sh` into `fzf-custom/rc/mksh/`: 222 | 223 | ``` 224 | % fzf-contrib load kill-process.sh a=mksh 225 | > Create '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/mksh/kill-process.sh' 226 | ``` 227 | 228 | Load `kill-process.sh` into `fzf-custom/rc/zsh/` and rename it into `KILL.zsh`: 229 | 230 | ``` 231 | % fzf-contrib load kill-process.sh a=zsh d=KILL.zsh 232 | > Create '/home/user1/share/fzf-custom/src/kill-process.sh' -> '/home/user1/share/fzf-custom/rc/zsh/KILL.zsh' 233 | ``` 234 | 235 | Unload/Remove all links inside `fzf-custom/rc/`: 236 | 237 | ``` 238 | % fzf-contrib uload 239 | > rm: remove regular file ‘/home/user1/share/fzf-custom/LINKS.txt’? 240 | > Remove '/home/user1/share/fzf-custom/LINKS.txt' 241 | > Remove '/home/user1/share/fzf-custom/rc/zsh/KILL.zsh' 242 | > Remove '/home/user1/share/fzf-custom/rc/mksh/kill-process.sh' 243 | > Remove '/home/user1/share/fzf-custom/rc/bash/kill-process.sh' 244 | > Remove '/home/user1/share/fzf-custom/rc/bash/z.bash' 245 | > Remove '/home/user1/share/fzf-custom/rc/bash/Z' 246 | ``` 247 | 248 | #### Using selected code snippets 249 | 250 | Source the scripts inside `fzf-custom/rc//` into your configuration file. For example, you may put the following into `.bashrc`: 251 | 252 | ```sh 253 | declare snippet= 254 | for snippet in "${FZF_CONTRIB_DIR}"/rc/bash/?* 255 | do 256 | source "$snippet" 257 | done 258 | unset -v snippet 259 | ``` 260 | 261 | #### To do 262 | 263 | - better `fzf-contrib info` 264 | - update `INFO.csv` after `fzf-contrib load` and `fzf-contrib uload` 265 | - nicer names of arguments 266 | - example how to use `fzf-contrib reload` 267 | 268 | ### Bugs & Requests 269 | 270 | Report it on https://github.com/D630/fzf-contrib/issues 271 | 272 | ### LICENCE 273 | 274 | MIT 275 | -------------------------------------------------------------------------------- /fzf-contrib: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # fzf-contrib (MIT License) 4 | # Copyright (c) 2015 D630, https://github.com/D630/fzf-contrib 5 | 6 | # -- CONFIG. 7 | 8 | set -e 9 | set -u 10 | readonly shells_sh="bash mksh zsh" 11 | readonly applications="${shells_sh} fish tmux vim" # See func __fzf_contrib_load() 12 | export LC_COLLATE="C" 13 | 14 | # -- FUNCTIONS. 15 | 16 | die () 17 | { 18 | printf "${0}/error/%s: %s\n" "$1" "$2" 1>&2; 19 | exit "$1" 20 | } 21 | 22 | msg () 23 | { 24 | printf "${0}: %s\n" "$*" 1>&2; 25 | } 26 | 27 | fzf_contrib__add () 28 | { 29 | local \ 30 | arg \ 31 | dest \ 32 | src="${1:? argument missing}"; 33 | 34 | for arg 35 | do 36 | case "$arg" in 37 | d=?*) 38 | readonly dest="${arg#d=}" 39 | esac 40 | shift 1 41 | done 42 | 43 | if 44 | test -d "${FZF_CONTRIB_ROOT}/.git" && 45 | GIT_DIR="${FZF_CONTRIB_ROOT}/.git" \ 46 | GIT_WORK_TREE="$FZF_CONTRIB_ROOT" \ 47 | command git rev-parse --git-dir 1>/dev/null 2>&1; 48 | then 49 | msg "Copying '${FZF_CONTRIB_ROOT}/src/${src}' -> '${FZF_CONTRIB_DIR}/src/${dest:-${src}}'" 50 | GIT_DIR="${FZF_CONTRIB_ROOT}/.git" \ 51 | GIT_WORK_TREE="$FZF_CONTRIB_ROOT" \ 52 | command git ls-files "src/${src}" \ 53 | | { 54 | IFS= read -r s; 55 | command cp -fp \ 56 | "${FZF_CONTRIB_ROOT}/${s}" \ 57 | "${FZF_CONTRIB_DIR}/src/${dest:-${src}}" 58 | } 59 | else 60 | die 84 "not a git repository/worktree: '${FZF_CONTRIB_ROOT}'" 61 | fi 62 | } 63 | 64 | fzf_contrib__help () 65 | { 66 | fzf_contrib__usage 67 | command cat <<'HELP' 68 | 69 | Subcommands: 70 | add [d=] Force a copy of a src file from 71 | to 72 | help Show this instruction 73 | info [] Output records from INFO.csv 74 | init Initialize local src dir from the 75 | ; copy its contents to 76 | 77 | load [a=] [d=] Hard link a src file from 78 | into 79 | 80 | reload Remove all hardlinks in 81 | and source . 82 | Use this command in a hook script like 83 | <.git/hooks/post-merge> 84 | uload [] [a=] Remove hard link from 85 | 86 | Arguments: 87 | Name of application and subdirectory in 88 | . Delimite apps with 89 | comma: 'zsh,mksh,bash' 90 | New name of the current script in 91 | 92 | Grep BRE pattern 93 | Basename of the script in the current src dir 94 | 95 | Environment variables: 96 | FZF_CONTRIB_DIR 97 | ${XDG_DATA_HOME:-${HOME}/.local/share}/fzf-custom 98 | HELP 99 | 100 | } 1>&2 101 | 102 | fzf_contrib__info () 103 | { 104 | local \ 105 | apps \ 106 | desc \ 107 | expr \ 108 | info_file \ 109 | name \ 110 | tags; 111 | 112 | : "${expr:=${1:? grep pattern missing}}" 113 | 114 | if 115 | test -f "${FZF_CONTRIB_ROOT}/INFO.csv" 116 | then 117 | info_file="${PWD}/INFO.csv" 118 | elif 119 | test -f "${FZF_CONTRIB_DIR}/INFO.csv" 120 | then 121 | info_file="${FZF_CONTRIB_DIR}/INFO.csv" 122 | else 123 | die 86 "could not locate an INFO.csv" 124 | fi 125 | 126 | msg "Using info file '${info_file}'" 127 | 128 | command grep -e "$1" "$info_file" \ 129 | | { 130 | while 131 | IFS='|' read -r name apps tags desc 132 | do 133 | printf "NAME %s\nAPPS %s\nTAGS %s\nDESC %s\n\n" "$name" "$apps" "$tags" "$desc" 134 | done 135 | } 136 | } 137 | 138 | fzf_contrib__init () 139 | if 140 | test -d "${FZF_CONTRIB_DIR}/src" 141 | then 142 | die 83 "dir exists: '${FZF_CONTRIB_DIR}/src'" 143 | elif 144 | test -d "${FZF_CONTRIB_ROOT}/.git" && 145 | GIT_DIR="${FZF_CONTRIB_ROOT}/.git" \ 146 | GIT_WORK_TREE="$FZF_CONTRIB_ROOT" \ 147 | command git rev-parse --git-dir 1>/dev/null 2>&1; 148 | then 149 | msg "Creating '${FZF_CONTRIB_DIR}'" 150 | command mkdir -p "$FZF_CONTRIB_DIR" 151 | msg "Copying '${FZF_CONTRIB_ROOT}/INFO.csv' -> '${FZF_CONTRIB_DIR}/INFO.csv'" 152 | command cp -i "${FZF_CONTRIB_ROOT}/INFO.csv" "${FZF_CONTRIB_DIR}/INFO.csv" 153 | msg "Copying '${FZF_CONTRIB_ROOT}/src/' -> '${FZF_CONTRIB_DIR}/src/'" 154 | GIT_DIR="${FZF_CONTRIB_ROOT}/.git" \ 155 | GIT_WORK_TREE="$FZF_CONTRIB_ROOT" \ 156 | command git ls-files src \ 157 | | command xargs -I '{}' install -D '{}' "${FZF_CONTRIB_DIR}/{}" 158 | else 159 | die 84 "not a git repository/worktree: '${FZF_CONTRIB_ROOT}'" 160 | fi 161 | 162 | fzf_contrib__load () 163 | { 164 | local \ 165 | apps= \ 166 | arg \ 167 | dest= \ 168 | rc \ 169 | shebang \ 170 | src="${1:? argument missing}"; 171 | 172 | for arg 173 | do 174 | case "$arg" in 175 | d=?*) 176 | readonly dest="${arg#d=}" 177 | ;; 178 | a=?*) 179 | readonly apps="${arg#a=}" 180 | esac 181 | shift 1 182 | done 183 | 184 | exec 9>>"${FZF_CONTRIB_DIR}/LINKS.txt" 185 | 186 | if 187 | test -n "$dest" -a -n "$apps" 188 | then 189 | for rc in $(IFS=, ; printf '%s ' ${apps}) 190 | do 191 | msg "Linking '${FZF_CONTRIB_DIR}/src/${src}' -> '${FZF_CONTRIB_DIR}/rc/${rc}/${dest}'" 192 | command ln -f "${FZF_CONTRIB_DIR}/src/${src}" "${FZF_CONTRIB_DIR}/rc/${rc}/${dest}" 193 | printf '%s\n' "command ln -f \"\${GIT_DIR}/src/${src}\" \"\${GIT_DIR}/rc/${rc}/${dest}\"" 1>&9; 194 | done 195 | elif 196 | test -z "$dest" -a -n "$apps" 197 | then 198 | for rc in $(IFS=, ; printf '%s ' ${apps}) 199 | do 200 | msg "Linking '${FZF_CONTRIB_DIR}/src/${src}' -> '${FZF_CONTRIB_DIR}/rc/${rc}/${src}'" 201 | command ln -f "${FZF_CONTRIB_DIR}/src/${src}" "${FZF_CONTRIB_DIR}/rc/${rc}/${src}" 202 | printf '%s\n' "command ln -f \"\${GIT_DIR}/src/${src}\" \"\${GIT_DIR}/rc/${rc}/${src}\"" 1>&9; 203 | done 204 | else 205 | read -r _ shebang < "${FZF_CONTRIB_DIR}/src/${src}"; 206 | case "$shebang" in 207 | sh) 208 | for rc in ${shells_sh} 209 | do 210 | msg "Linking '${FZF_CONTRIB_DIR}/src/${src}' -> '${FZF_CONTRIB_DIR}/rc/${rc}/${dest:-${src}}'" 211 | command ln -f "${FZF_CONTRIB_DIR}/src/${src}" "${FZF_CONTRIB_DIR}/rc/${rc}/${dest:-${src}}" 212 | printf '%s\n' "command ln -f \"\${GIT_DIR}/src/${src}\" \"\${GIT_DIR}/rc/${rc}/${dest:-${src}}\"" 1>&9; 213 | done 214 | ;; 215 | bash|fish|mksh|tmux|vim|zsh) 216 | msg "Linking '${FZF_CONTRIB_DIR}/src/${src}' -> '${FZF_CONTRIB_DIR}/rc/${shebang}/${dest:-${src}}'" 217 | command ln -f "${FZF_CONTRIB_DIR}/src/${src}" "${FZF_CONTRIB_DIR}/rc/${shebang}/${dest:-${src}}" 218 | printf '%s\n' "command ln -f \"\${GIT_DIR}/src/${src}\" \"\${GIT_DIR}/rc/${shebang}/${dest:-${src}}\"" 1>&9; 219 | ;; 220 | *) 221 | die 81 "unknown shebang: '${shebang}'" 222 | esac 223 | fi 224 | 225 | exec 9<&- 226 | } 227 | 228 | fzf_contrib__main () 229 | { 230 | local \ 231 | FZF_CONTRIB_DIR \ 232 | FZF_CONTRIB_ROOT; 233 | 234 | FZF_CONTRIB_DIR="${FZF_CONTRIB_DIR:-${XDG_DATA_HOME:-${HOME}/.local/share}/fzf-custom}" 235 | FZF_CONTRIB_ROOT="$( 236 | rl="$(command readlink -fn -- "$0")"; 237 | command dirname -- "$rl" 238 | )" 239 | 240 | readonly \ 241 | FZF_CONTRIB_DIR \ 242 | FZF_CONTRIB_ROOT; 243 | 244 | case "$1" in 245 | help|init|version) 246 | "fzf_contrib__${1}" 247 | exit "$?" 248 | ;; 249 | info) 250 | shift 1 251 | fzf_contrib__info "$@" 252 | exit "$?" 253 | ;; 254 | *) 255 | fzf_contrib__prepare 256 | esac 257 | 258 | local cmd="$1" 259 | shift 1 260 | 261 | case "$cmd" in 262 | add|load|uload|reload) 263 | "fzf_contrib__${cmd}" "$@" 264 | ;; 265 | *) 266 | fzf_contrib__usage 267 | esac 268 | } 269 | 270 | fzf_contrib__prepare () 271 | { 272 | if 273 | ! test -d "${FZF_CONTRIB_DIR}/src" 274 | then 275 | die 79 "dir is missing: '${FZF_CONTRIB_DIR}/src'. Initialized first?" 276 | fi 277 | 278 | local rc 279 | 280 | for rc in ${applications} 281 | do 282 | { 283 | command mkdir -p "${FZF_CONTRIB_DIR}/rc/${rc}" 284 | command printf '%s' > "${FZF_CONTRIB_DIR}/rc/${rc}/.gitkeep"; 285 | } & 286 | done 287 | wait 288 | 289 | command printf '%s' > "${FZF_CONTRIB_DIR}/rc/.gitkeep"; 290 | } 291 | 292 | fzf_contrib__reload () 293 | if 294 | test -d "${FZF_CONTRIB_DIR}/.git" && 295 | GIT_DIR="${FZF_CONTRIB_DIR}/.git" \ 296 | GIT_WORK_TREE="$FZF_CONTRIB_DIR" \ 297 | command git rev-parse --git-dir 1>/dev/null 2>&1; 298 | then 299 | if 300 | ! test -f "${FZF_CONTRIB_DIR}/LINKS.txt" 301 | then 302 | die 87 "LINKS.txt file missing: '${FZF_CONTRIB_DIR}/LINKS.txt'" 303 | fi 304 | fzf_contrib__unload_all 305 | local line 306 | #. "${FZF_CONTRIB_DIR}/LINKS.txt" 307 | while 308 | IFS= read -r line 309 | do 310 | msg "Invoking '${line}'" 311 | ${line} 312 | done < "${FZF_CONTRIB_DIR}/LINKS.txt" 313 | else 314 | die 84 "not a git repository/worktree: '${FZF_CONTRIB_DIR}'" 315 | fi 316 | 317 | fzf_contrib__unload_all () 318 | { 319 | command find -H "${FZF_CONTRIB_DIR}/rc" -type f -links +1 -print \ 320 | | { 321 | while 322 | IFS= read -r rc 323 | do 324 | msg "Removing '${rc}'" 325 | command rm -f "$rc" || :; 326 | done 327 | } 328 | } 329 | 330 | fzf_contrib__uload () 331 | { 332 | if 333 | test "$#" -eq 0 334 | then 335 | msg "Removing '${FZF_CONTRIB_DIR}/LINKS.txt'" 336 | command rm -f "${FZF_CONTRIB_DIR}/LINKS.txt" || :; 337 | fzf_contrib__unload_all 338 | return "$?" 339 | fi 340 | 341 | local \ 342 | apps= \ 343 | arg \ 344 | inode_src="$(command ls -i "${FZF_CONTRIB_DIR}/src/${1:? argument missing}")" \ 345 | rc \ 346 | src="${1:? argument missing}"; 347 | 348 | read -r inode_src _ <