├── .gitignore ├── LICENSE ├── README.md ├── fzf-z.plugin.zsh ├── fzfz └── recentdirs.sh /.gitignore: -------------------------------------------------------------------------------- 1 | z.sh 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andrew Ferrier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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 THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fzf-z 2 | 3 | **Note**: Personally I am no longer using this plugin actively, so I have archived it. 4 | If you're interested in taking it over, please let me know. 5 | 6 | --- 7 | 8 | This plugin was originally inspired as a mashup between 9 | [fzf](https://github.com/junegunn/fzf), and oh-my-zsh's [z 10 | plugin](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/z), 11 | which allows you to track recently and commonly used directories. The *z* 12 | plugin does a great job of allowing you to switch between frequently-used 13 | directories just by typing `z *somedirectorysubstring*`, but it doesn't really 14 | easily allow you to browse those directories, with partial-string search. This 15 | plugin was invented to solve that problem, using `fzf` as a front-end. Since 16 | then, it's been extended to support [fasd](https://github.com/clvv/fasd) and 17 | [autojump](https://github.com/wting/autojump), other 'frecency' plugins, as 18 | alternatives to `z`. 19 | 20 | ## Installation 21 | 22 | You can install `fzf-z` like any other `zsh` plugin. If you're not familiar 23 | with `zsh` plugins, using a plugin manager is the easiest way to install one. 24 | You can find information on some popular choices 25 | [here](https://wiki.archlinux.org/index.php/Zsh#Plugin_managers). `oh-my-zsh` 26 | and other configuration frameworks do not by themselves allow you to add custom 27 | plugins such as `fzf-z`; you'll likely need a plugin manager in addition. 28 | 29 | ### Pre-requisites 30 | 31 | You must have one of these installed: 32 | 33 | * The [z 34 | plugin](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/z). 35 | 36 | * The [fasd](https://github.com/clvv/fasd) tool (my personal choice, and recommended 37 | if you are not already using one of these tools). 38 | 39 | * The [autojump](https://github.com/wting/autojump) tool. 40 | 41 | These tools must be in your `$PATH`. These have to be installed irrespective 42 | of how you use `fzf-z`. 43 | 44 | You must also have [fzf](https://github.com/junegunn/fzf) installed. You can 45 | set the full path to `fzf` binary with environment variable `FZF_BIN_PATH`, or 46 | it uses the one found in your `$PATH`. 47 | 48 | *Note*: When you first use `fzf-z`, if you have configured 49 | `FZFZ_RECENT_DIRS_TOOL` to use `z` (which is the default), it will dynamically 50 | download `z.sh` for its own internal use. You still need to have the [z 51 | plugin](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/z) 52 | installed anyway. 53 | 54 | ## Sources of information 55 | 56 | Since the original version, I've extended `fzf-z` to support other sources of 57 | information about the directories you might be interested in, which are all 58 | mixed into the same list delivered through `fzf`. In priority order (the order 59 | in which they are shown in `fzf`, first to last): 60 | 61 | 1. Directories *under* the current directory. The number of these shown in 62 | `fzf` is limited by the `FZFZ_SUBDIR_LIMIT` environment variable, which 63 | defaults to 50. If you don't want those to be shown, simply set this to 64 | `0`. 65 | 66 | 1. Recently used dirs. By default, these are provided by the `z` command from 67 | the z plugin (the original purpose of this plugin). The order shown is the 68 | order given by `z -l`. However, if you want to use `fasd` (preferred) or 69 | `autojump` instead, set `FZFZ_RECENT_DIRS_TOOL` to `fasd` or `autojump` 70 | respectively. 71 | 72 | 1. All subdirectories in all directories listed in the `FZFZ_EXTRA_DIRS` 73 | environment variables. These directories are space-separated, so for 74 | example: 75 | 76 | `export FZFZ_EXTRA_DIRS="~/MyDocuments '~/Desktop/Some Other Stuff'"` 77 | 78 | ## Ways to use fzf-z 79 | 80 | ### As a zsh plugin 81 | 82 | Treat this plugin like any other zsh plugin and install using a [zsh plugin 83 | manager](https://github.com/unixorn/awesome-zsh-plugins#frameworks). For 84 | example: 85 | 86 | Once the plugin is installed, simply hit `` on the zsh command-line, 87 | and it will bring up a list of directories according to the sources of 88 | information listed above. Select one, perhaps typing to filter the list, and 89 | hit Enter - the path to the selected directory will be inserted into the 90 | command line. If you started with an empty command line, and you have the 91 | `AUTO_CD` zsh option turned on you'll change to that directory instantly. 92 | 93 | This is similar to the default **Ctrl-T** binding already provided by the 94 | [fzf zsh key-bindings 95 | file](https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh). At 96 | the moment, this plugin doesn't allow the **Ctrl-G** keybinding to be 97 | customized, but you can change by simply forking the plugin and editing the 98 | file if you want. 99 | 100 | ### As a command 101 | 102 | *New*: this plugin repository also now includes `fzfz` as a standalone command 103 | (although it depends on the provided script `recentdirs.sh` also). You can run this 104 | as an alternative to using this as a plugin, and it will print the selected 105 | directory to stdout, which you can use to embed this in other tools. 106 | 107 | ## Customizing and Options 108 | 109 | If you set the `FZFZ_EXCLUDE_PATTERN` environment variable to a regex (matched 110 | with `egrep`) it will exclude any directory which matches it from appearing in 111 | the subdirectory results (it isn't applied to the `z`/`fasd`/`autojump` 112 | results, since it's assumed any directory you've navigated to before is one 113 | you might be interested in). By default this variable is set to filter out 114 | anything in a `.git` directory. 115 | 116 | You can also set `FZFZ_EXTRA_OPTS` to add any additional options you like to 117 | the `fzf` command - for example, `-e` will turn exact matching on by default. 118 | 119 | By default, fzf-z will filter out duplicates in its list so directories found 120 | via multiple methods don't appear twice; however, this does slow it down. If 121 | you don't care about that and want to speed it up, set 122 | `FZFZ_UNIQUIFIER="cat"`. 123 | 124 | If you want to change the preview command used by fzfz (currently `tree` by 125 | default if it's installed, or `ls` if not), set `FZFZ_PREVIEW_COMMAND` to 126 | something like `ls {}` (`{}` is replaced with the directory currently 127 | selected). 128 | 129 | ## Performance 130 | 131 | If it's installed and in your `PATH`, `fzf-z` will use 132 | [fd](https://github.com/sharkdp/fd). If not, it'll fall back to `find`, which 133 | is slower. The behaviour is slightly differently also; `fd` will exclude files 134 | ignored by `.gitignore` or similar, which `find` will not do, so you will get 135 | less results. Generally, this is what you want, though. 136 | -------------------------------------------------------------------------------- /fzf-z.plugin.zsh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | # 3 | # Based on https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh 4 | # (MIT licensed, as of 2016-05-05). 5 | 6 | FZFZ_SCRIPT_PATH=${0:a:h} 7 | 8 | __fzfz() { 9 | $FZFZ_SCRIPT_PATH/fzfz | while read item; do 10 | printf '%q ' "$item" 11 | done 12 | echo 13 | } 14 | 15 | fzfz-dir-widget() { 16 | local shouldAccept=$(should-accept-line) 17 | LBUFFER="${LBUFFER}$(__fzfz)" 18 | local ret=$? 19 | zle redisplay 20 | typeset -f zle-line-init >/dev/null && zle zle-line-init 21 | if [[ $ret -eq 0 && -n "$BUFFER" && -n "$shouldAccept" ]]; then 22 | zle .accept-line 23 | fi 24 | return $ret 25 | } 26 | 27 | # Accept the line if the buffer was empty before invoking the file widget, and 28 | # the `auto_cd` option is set. 29 | should-accept-line() { 30 | if [[ ${#${(z)BUFFER}} -eq 0 && -o auto_cd ]]; then 31 | echo "true"; 32 | fi 33 | } 34 | 35 | zle -N fzfz-dir-widget 36 | bindkey -M viins -r '^G' 37 | bindkey -M vicmd -r '^G' 38 | bindkey -M emacs -r '^G' 39 | 40 | bindkey -M viins '^G' fzfz-dir-widget 41 | bindkey -M vicmd '^G' fzfz-dir-widget 42 | bindkey -M emacs '^G' fzfz-dir-widget 43 | -------------------------------------------------------------------------------- /fzfz: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env zsh 2 | # 3 | # These options are intended to be user-customizable if needed; you can 4 | # override them by exporting them from your ~/.zshrc. See README for more 5 | # details. 6 | 7 | FZF_BIN_PATH=${FZF_BIN_PATH:="fzf"} 8 | FZFZ_EXCLUDE_PATTERN=${FZFZ_EXCLUDE_PATTERN:="\/.git"} 9 | FZFZ_EXTRA_OPTS=${FZFZ_EXTRA_OPTS:=""} 10 | FZFZ_UNIQUIFIER=${FZFZ_UNIQUIFIER:="awk '!seen[\$0]++' 2>&1"} 11 | FZFZ_SUBDIR_LIMIT=${FZFZ_SUBDIR_LIMIT:=50} 12 | 13 | if ! (( ${+FZFZ_PREVIEW_COMMAND} )); then 14 | command -v exa >/dev/null 2>&1 15 | if [ $? -eq 0 ]; then 16 | FZFZ_PREVIEW_COMMAND='exa --level 2 --tree --color=always --group-directories-first {}' 17 | else 18 | command -v tree >/dev/null 2>&1 19 | if [ $? -eq 0 ]; then 20 | FZFZ_PREVIEW_COMMAND='tree -C -L 2 -x --noreport --dirsfirst {}' 21 | else 22 | FZFZ_PREVIEW_COMMAND='ls -1 -R {}' 23 | fi 24 | fi 25 | fi 26 | 27 | # ***** 28 | 29 | SCRIPT_PATH="${0:A:h}" 30 | 31 | if [[ $OSTYPE == darwin* && -z $(whence tac) ]]; then 32 | REVERSER='tail -r' 33 | else 34 | REVERSER='tac' 35 | fi 36 | 37 | if type fd &>/dev/null; then 38 | FIND_PREFIX="fd --color=never --hidden . " 39 | FIND_POSTFIX=" --type directory" 40 | FIND_REMOVE_SURPLUS="cat" 41 | else 42 | FIND_PREFIX="find " 43 | FIND_POSTFIX=" -type d" 44 | # find includes the current directory, so we remove the first line from 45 | # the output. 46 | FIND_REMOVE_SURPLUS="tail -n +2" 47 | fi 48 | 49 | if (($+FZFZ_EXCLUDE_PATTERN)); then 50 | if type gegrep &>/dev/null; then 51 | EXCLUDER="gegrep -v '$FZFZ_EXCLUDE_PATTERN'" 52 | else 53 | EXCLUDER="egrep -v '$FZFZ_EXCLUDE_PATTERN'" 54 | fi 55 | else 56 | EXCLUDER="cat" 57 | fi 58 | 59 | # EXCLUDER is applied directly only to searches that need it (i.e. not 60 | # `z`). That improvements performance, and makes sure that the 61 | # FZFZ_SUBDIR_LIMIT is applied on the post-excluded list. 62 | 63 | if (($+FZFZ_EXTRA_DIRS)); then 64 | EXTRA_DIRS="{ $FIND_PREFIX $FZFZ_EXTRA_DIRS $FIND_POSTFIX && $FIND_PID=${!} | ($EXCLUDER; kill -9 $FIND_PID) } 2> /dev/null" 65 | else 66 | EXTRA_DIRS="{ true }" 67 | fi 68 | 69 | if (($FZFZ_SUBDIR_LIMIT == 0)); then 70 | SUBDIRS="{ true }" 71 | else 72 | SUBDIRS="{ $FIND_PREFIX '$PWD' $FIND_POSTFIX | $EXCLUDER | head -n $(($FZFZ_SUBDIR_LIMIT+1)) | $FIND_REMOVE_SURPLUS }" 73 | fi 74 | 75 | RECENT_DIRS="{ $SCRIPT_PATH/recentdirs.sh }" 76 | RECENTLY_USED_DIRS="{ $RECENT_DIRS | $REVERSER | sed 's/^[[:digit:].]*[[:space:]]*//' }" 77 | 78 | FZF_COMMAND="${FZF_BIN_PATH} --height ${FZF_TMUX_HEIGHT:-40%} ${FZFZ_EXTRA_OPTS} --no-sort --tiebreak=end,index -m --preview='$FZFZ_PREVIEW_COMMAND | head -\$LINES'" 79 | 80 | COMMAND="{ $RECENTLY_USED_DIRS ; $SUBDIRS ; $EXTRA_DIRS; } | $FZFZ_UNIQUIFIER | $FZF_COMMAND" 81 | eval $COMMAND 82 | -------------------------------------------------------------------------------- /recentdirs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | FZFZ_RECENT_DIRS_TOOL=${FZFZ_RECENT_DIRS_TOOL:="z"} 7 | 8 | SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 9 | 10 | if [[ $FZFZ_RECENT_DIRS_TOOL == "z" ]]; then 11 | if [ ! -f "$SCRIPT_PATH/z.sh" ]; then 12 | >&2 echo "Locally-cached copy of z.sh not found, downloading..." 13 | curl https://raw.githubusercontent.com/rupa/z/master/z.sh > "$SCRIPT_PATH/z.sh" 14 | fi 15 | 16 | source "$SCRIPT_PATH/z.sh" 17 | _z -l 2>&1 && exit 0 || exit 0 18 | elif [[ $FZFZ_RECENT_DIRS_TOOL == "autojump" ]]; then 19 | if [[ $OSTYPE == darwin* && -z $(whence tac) ]]; then 20 | REVERSER='tail -r' 21 | else 22 | REVERSER='tac' 23 | fi 24 | autojump -s | $REVERSER | tail +8 | $REVERSER | awk '{print $2}' 25 | elif [[ $FZFZ_RECENT_DIRS_TOOL == "fasd" ]]; then 26 | fasd -dl 2>&1 && exit 0 || exit 0 27 | else 28 | echo "Unrecognized recent dirs tool '$FZFZ_RECENT_DIRS_TOOL', please set \$FZFZ_RECENT_DIRS_TOOL correctly." 29 | exit 1 30 | fi 31 | --------------------------------------------------------------------------------