├── HACKING.md
├── README.md
├── colemak_keys.sh.example
├── deer
└── screenshots
├── 2017-08-03-215440_640x364_scrot.png
└── 2017-08-03-215509_640x364_scrot.png
/HACKING.md:
--------------------------------------------------------------------------------
1 | # WARNING: MAY BE OUTDATED, SORRY #
2 |
3 | In this file I'll try to explain some design decisions and how `deer` works.
4 |
5 | VARIABLES
6 | =========
7 |
8 | PATHS
9 | -----
10 |
11 | **DEER_DIRNAME**
12 |
13 | The full path to the current directory, without the trailing slash.
14 |
15 | **DEER_BASENAME**
16 |
17 | An associative array mapping the dirnames to the files or directories
18 | focuesed there.
19 |
20 | **DEER_BASENAME[$DEER_DIRNAME]**
21 |
22 | The name of the currently selected file or directory.
23 |
24 | Due to the special case of the root directory (`/`), simply using
25 | `$DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]` to get the full path to the selected
26 | directory is not sufficient as it would result in a double slash.
27 | Currently I use `${DEER_DIRNAME%/}/$DEER_BASENAME[$DEER_DIRNAME]` to eliminate one
28 | slash in that corner case. It complicates the code in a few places but
29 | it's more maintainable than keeping the slash in `DEER_BASENAME` and
30 | concatenating dirname with basename without the additional slash in
31 | between (it was the case up until recently).
32 |
33 | OTHERS
34 | ------
35 |
36 | **DEER_FILTER**
37 |
38 | An associative array mapping the dirnames to the filter to be applied
39 | there. It's modified in the function `deer-enter`. If the directory
40 | does not exist in that map, it's value should be assumed to be `*`.
41 | The prefered notation is quite ugly but it works:
42 | `${DEER_FILTER[$DEER_DIRNAME]:-'*'}`
43 |
44 | **OLD_LBUFFER and OLD_RBUFFER**
45 |
46 | The line buffer stored to restore the `LBUFFER` and `RBUFFER` contents
47 | later (in the `deer-restore` function) after adding the selected files
48 | to them.
49 |
50 | **PREDISPLAY**
51 |
52 | The built-in variable used to display the preview of the left side of
53 | the buffer (`OLD_LBUFFER`).
54 |
55 | **POSTDISPLAY**
56 |
57 | Used to display the status information. Currently only displays the
58 | applied filter if any.
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | deer
2 | ====
3 |
4 | [](https://gitter.im/Vifon/deer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5 |
6 | DESCRIPTION
7 | -----------
8 |
9 | `deer` is a file navigator for [zsh](http://zsh.sourceforge.net/)
10 | heavily inspired by [ranger](http://ranger.nongnu.org/).
11 |
12 | **WHY**
13 |
14 | I've created `deer` because I really like to use `ranger` as an
15 | extension of my shell to quickly navigate the
16 | directories. Unfortunately, its startup time (even though short) is
17 | sometimes cumbersome. `deer` implements the very basic ranger-like
18 | file navigation + some basic operations on the commandline, like
19 | inserting the selected path (in various ways), which makes it fast to
20 | launch and ideal for the task. Not using the whole terminal can be
21 | viewed as an another feature.
22 |
23 | Pros:
24 | * Launches much faster.
25 | * Better shell integration.
26 | * Retains the terminal contents and only uses a small part of the terminal.
27 |
28 | Cons:
29 | * Offers only a small subset of `ranger's` features.
30 | * Needs `zsh`.
31 |
32 | [](screenshots/2017-08-03-215440_640x364_scrot.png)
33 | [](screenshots/2017-08-03-215509_640x364_scrot.png)
34 |
35 | USAGE
36 | -----
37 |
38 | To launch `deer`, press `alt+k`.
39 |
40 | You can supply a numeric argument (`alt-number`) to go up the
41 | appropriate number of directory levels upon start.
42 |
43 | If you activate `deer` with the cursor on a path, it will start in there.
44 |
45 | **KEYS AND FUNCTIONS**
46 |
47 | These functions can be bound custom keys (the default is in the
48 | parentheses):
49 |
50 | * `down` (j) -- One item down.
51 | * `page_down` (J) -- Five items down.
52 | * `up` (k) -- One item up.
53 | * `page_up` (K) -- Five items up.
54 | * `enter` (l) -- Enter the selected directory.
55 | * `leave` (h) -- Leave the current directory (one directory up).
56 | * `next_parent` (]) -- One item down in the left column.
57 | * `prev_parent` ([) -- One item up in the left column.
58 | * `search` (/) -- Select the first file matching the given pattern.
59 | * `filter` (f) -- Shows only files matching the given pattern.
60 | * `toggle_hidden` (H) -- Show/hide the hidden files.
61 | * `quit` (q) -- Exit `deer`.
62 | * `append_path` (a) -- Insert the current path and leave the cursor on its right.
63 | * `append_abs_path` (A) -- Absolute path version.
64 | * `insert_path` (i) -- Insert the current path and leave the cursor on its left.
65 | * `insert_abs_path` (I) -- Absolute path version.
66 | * `multi_insert_dwim` (s) -- Insert the current path, add a smart separator using the last character before the cursor (unless it's an opening brace, then use a comma), move the cursor down and don't quit yet.
67 | * `multi_insert_abs` (S) -- Insert the current absolute path and don't quit yet.
68 | * `chdir` (c) -- `cd` into the current directory and quit.
69 | * `chdir_selected` (C) -- `cd` into the selected directory and quit.
70 | * `rifle` (r) -- Run `rifle(1)` on the selected file.
71 | * `edit` (e) -- Run `$EDITOR` on the selected file (default to vim).
72 |
73 | To bind a function to a different key, add something like this to your
74 | `.zshrc`:
75 |
76 | ```
77 | typeset -Ag DEER_KEYS
78 | DEER_KEYS[function]=key
79 | ```
80 |
81 | The `DEER_KEYS` variable is an associative array holding the keys
82 | associated with functions. One function may be bound to only one key
83 | (meaning the previous one is overwritten).
84 |
85 | INSTALLATION
86 | ------------
87 |
88 | **First method**
89 |
90 | I assume you have `~/.fpath` added to your `$FPATH` variable here. If
91 | you don't, either add it or use the second installation method. If you
92 | use some other directory, modify the commands below accordingly.
93 |
94 | Copy the `deer` main file to `~/.fpath/deer` and make sure it gets
95 | autoloaded in your `zshrc`:
96 |
97 | ```
98 | autoload -U deer
99 | ```
100 |
101 | Adding these lines will make the script available to the line editor, and bind
102 | it to `alt+k` respectively:
103 |
104 | ```
105 | zle -N deer
106 | bindkey '\ek' deer
107 | ```
108 |
109 | **Second method**
110 |
111 | Alternatively, you can directly source the file `deer`, and bind the
112 | initialization function to `alt+k` as follows:
113 |
114 | ```
115 | source /path/to/deer.sh
116 | zle -N deer
117 | bindkey '\ek' deer
118 | ```
119 |
120 | **Third method**
121 |
122 | With a plugin manager. Tested with
123 | [antigen](https://github.com/zsh-users/antigen),
124 | [zgen](https://github.com/tarjoilija/zgen) and
125 | [zplug](https://github.com/zplug/zplug):
126 |
127 | - antigen:
128 |
129 | ```
130 | antigen bundle Vifon/deer
131 | antigen apply
132 | autoload -U deer
133 | zle -N deer
134 | bindkey '\ek' deer
135 | ```
136 |
137 | - zgen:
138 |
139 | ```
140 | zgen load Vifon/deer
141 | zgen save
142 | autoload -U deer
143 | zle -N deer
144 | bindkey '\ek' deer
145 | ```
146 |
147 | - zplug:
148 |
149 | ```
150 | zplug "vifon/deer", use:deer
151 | zle -N deer
152 | bindkey '\ek' deer
153 | ```
154 |
155 | CONFIGURATION
156 | -------------
157 |
158 | By default, `deer` will use 22 lines of your terminal. This is configurable
159 | with the `zstyle` mechanism. Drop a line like this in `zshrc` to adjust this
160 | setting:
161 |
162 | ```
163 | zstyle ':deer:' height 35
164 | ```
165 |
166 | Show the hidden files by default:
167 |
168 | ```
169 | zstyle :deer: show_hidden yes
170 | ```
171 |
172 | To customize the keys used by `deer`, you may use the following code:
173 |
174 | ```
175 | typeset -Ag DEER_KEYS # Prepare the associative table.
176 | DEER_KEYS[action_name]=key
177 | ```
178 |
179 | Users of the Colemak keyboard layout may use the included
180 | `colemak_keys.sh.example` file to adjust the default keys to Colemak:
181 |
182 | ```
183 | source colemak_keys.sh.example.
184 | ```
185 |
186 | KNOWN ISSUES
187 | ------------
188 |
189 | _These are the issues that I'm aware of, along with the reason for why
190 | I've decided not to fix them for now. If you think any of them is a
191 | dealbreaker, open a ticket on Github and I'll see what I can do._
192 |
193 | **Slashes are replaced with division slashes (U+2215) in file previews**
194 |
195 | This is an ugly workaround, and the issue is with the way the output is
196 | formatted (slash is used as a separator for `paste(1)` and
197 | `column(1)` as it cannot appear in the filename).
198 |
199 | **The ../ directory is not correctly shown when completing the
200 | previously typed path**
201 |
202 | The shown path is created by deleting the preexisting prefix from the
203 | absolute path. It greatly simplifies the code and makes it easier to
204 | maintain (it's already quite messy in some places).
205 |
206 | **Buggy behavior in the root directory (/)**
207 |
208 | The root directory handling is quite tricky as it's the only directory
209 | where going up and down does not keep you in the same directory. I've
210 | concluded that that directory itself is used very rarely and most of
211 | the bugs are not fatal.
212 |
213 | FAQ
214 | ---
215 |
216 | **Can you add colors?**
217 |
218 | Unfortunately, no. It's a limitation of the underlying `zle` (zsh
219 | line editor) and I cannot do much about it.
220 |
221 | **Why are there so many strange features and their variations?**
222 |
223 | At first I add new features taylored for myself. Later I plan to
224 | review these features and clean them up. If you need something else,
225 | please leave a feature request or add it yourself if you know how.
226 |
227 | **The key binding system is ugly**
228 |
229 | Yes, it is. I plan to replace it with a proper keymap but for now it
230 | should suffice.
231 |
232 | RELATED PROJECTS
233 | ----------------
234 |
235 | [lscd](https://github.com/hut/lscd) from the author of the original
236 | ranger is a minimal file browser written in a POSIX shell (with only a
237 | few necessary bashisms).
238 |
239 | [blscd](https://github.com/D630/blscd) is a Bash fork of `lscd` which
240 | is very similar to ranger.
241 |
242 | SEE ALSO
243 | --------
244 |
245 | ranger(1), zsh(1)
246 |
247 | AUTHOR
248 | ------
249 |
250 | Wojciech 'vifon' Siewierski < wojciech dot siewierski at gmail dot com >
251 |
252 | COPYRIGHT
253 | ---------
254 |
255 | Copyright (C) 2014-2015 Wojciech Siewierski
256 |
257 | This program is free software: you can redistribute it and/or modify
258 | it under the terms of the GNU General Public License as published by
259 | the Free Software Foundation, either version 3 of the License, or
260 | (at your option) any later version.
261 |
262 | This program is distributed in the hope that it will be useful,
263 | but WITHOUT ANY WARRANTY; without even the implied warranty of
264 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
265 | GNU General Public License for more details.
266 |
267 | You should have received a copy of the GNU General Public License
268 | along with this program. If not, see .
269 |
--------------------------------------------------------------------------------
/colemak_keys.sh.example:
--------------------------------------------------------------------------------
1 | # Keys for the Colemak keyboard layout users.
2 | # Courtesy of Tadeusz 'tadzik' Sośnierz.
3 |
4 | typeset -Ag DEER_KEYS
5 |
6 | DEER_KEYS[down]=n
7 | DEER_KEYS[page_down]=N
8 | DEER_KEYS[up]=e
9 | DEER_KEYS[page_up]=E
10 | DEER_KEYS[enter]=i
11 |
--------------------------------------------------------------------------------
/deer:
--------------------------------------------------------------------------------
1 | # -*- mode: shell-script -*-
2 | # vim: set ft=zsh :
3 | #########################################################################
4 | # Copyright (C) 2014-2015 Wojciech Siewierski #
5 | # #
6 | # This program is free software: you can redistribute it and/or modify #
7 | # it under the terms of the GNU General Public License as published by #
8 | # the Free Software Foundation, either version 3 of the License, or #
9 | # (at your option) any later version. #
10 | # #
11 | # This program is distributed in the hope that it will be useful, #
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 | # GNU General Public License for more details. #
15 | # #
16 | # You should have received a copy of the GNU General Public License #
17 | # along with this program. If not, see . #
18 | #########################################################################
19 |
20 | zstyle -s ":deer:" height DEER_HEIGHT || DEER_HEIGHT=22
21 | zstyle -b ":deer:" show_hidden DEER_SHOW_HIDDEN
22 |
23 |
24 | typeset -Ag DEER_KEYS
25 | function ()
26 | {
27 | while [ -n "$2" ]; do
28 | DEER_KEYS[$1]=${DEER_KEYS[$1]:-$2}
29 | shift 2
30 | done
31 | } down j \
32 | page_down J \
33 | up k \
34 | page_up K \
35 | enter l \
36 | leave h \
37 | next_parent ']' \
38 | prev_parent '[' \
39 | search / \
40 | filter f \
41 | toggle_hidden H \
42 | quit q \
43 | append_path a \
44 | append_abs_path A \
45 | insert_path i \
46 | insert_abs_path I \
47 | multi_insert_dwim s \
48 | multi_insert_abs S \
49 | chdir c \
50 | chdir_selected C \
51 | rifle r \
52 | edit e \
53 |
54 |
55 | # Select the Nth next file. Pass a negative argument for the previous file.
56 | deer-move()
57 | {
58 | local FILES MOVEMENT INDEX
59 | MOVEMENT=$1
60 |
61 | FILES=($DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-/:t)
62 | $DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-^/:t))
63 |
64 | INDEX=${(k)FILES[(re)$DEER_BASENAME[$DEER_DIRNAME]]}
65 |
66 | if (( INDEX+MOVEMENT <= 0 )); then
67 | DEER_BASENAME[$DEER_DIRNAME]=$FILES[1]
68 | elif (( INDEX+MOVEMENT > $#FILES )); then
69 | DEER_BASENAME[$DEER_DIRNAME]=$FILES[$#FILES]
70 | else
71 | DEER_BASENAME[$DEER_DIRNAME]=$FILES[$INDEX+$MOVEMENT]
72 | fi
73 | }
74 |
75 | # Select the first visible directory (or file if there are no
76 | # directories) in the current directory. Useful when changing the file
77 | # filter.
78 | deer-refocus()
79 | {
80 | local TMP
81 | TMP=($DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-/:t)
82 | $DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-^/:t))
83 | DEER_BASENAME[$DEER_DIRNAME]=$TMP[1]
84 |
85 | [ -n "$DEER_BASENAME[$DEER_DIRNAME]" ] # Return if there were any files at all.
86 | }
87 |
88 | # Enter the selected directory
89 | deer-enter()
90 | {
91 | # Abort if there is no file focused at all or if it is not a
92 | # directory.
93 | [ -n "$DEER_BASENAME[$DEER_DIRNAME]" -a \
94 | -d "$DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]" ] || return
95 |
96 | DEER_DIRNAME=${DEER_DIRNAME%/}/$DEER_BASENAME[$DEER_DIRNAME]
97 |
98 | if [ -z $DEER_BASENAME[$DEER_DIRNAME] ]; then
99 | deer-refocus
100 | fi
101 | }
102 |
103 | # Move to the parent directory
104 | deer-leave()
105 | {
106 | [ $DEER_DIRNAME = / ] && return
107 | DEER_BASENAME[$DEER_DIRNAME:h]=$DEER_DIRNAME:t
108 | DEER_DIRNAME=$DEER_DIRNAME:h
109 | }
110 |
111 | # Display a given prompt, read a string and save it into $BUFFER.
112 | deer-prompt()
113 | {
114 | BUFFER=""
115 | PREDISPLAY="$1/ "
116 | POSTDISPLAY=""
117 |
118 | local region_highlight
119 | region_highlight=("P0 $#1 fg=green")
120 | zle recursive-edit
121 | }
122 |
123 | # Read a pattern and select the first matching file.
124 | deer-search()
125 | {
126 | deer-prompt "search"
127 |
128 | local TMP
129 | TMP=($DEER_DIRNAME/${~BUFFER}${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-:t))
130 | [ -n "$TMP[1]" ] && DEER_BASENAME[$DEER_DIRNAME]=$TMP[1]
131 | }
132 |
133 | # Read a pattern and use it as a new filter.
134 | deer-filter()
135 | {
136 | deer-prompt "filter"
137 |
138 | if [ -n "$BUFFER" ] && [[ ! $BUFFER == *\** ]]; then
139 | BUFFER=*$BUFFER*
140 | fi
141 |
142 | deer-apply-filter $BUFFER || deer-apply-filter
143 | }
144 |
145 | deer-apply-filter()
146 | {
147 | DEER_FILTER[$DEER_DIRNAME]=$1
148 | deer-refocus
149 | }
150 |
151 | # Draw an arrow pointing to the selected file.
152 | deer-mark-file-list()
153 | {
154 | local MARKED=$1
155 | shift
156 |
157 | print -l -- "$@" \
158 | | grep -Fx -B5 -A$DEER_HEIGHT -- "$MARKED" \
159 | | perl -pe 'BEGIN{$name = shift}
160 | if ($name."\n" eq $_) {
161 | $_="-> $_"
162 | } else {
163 | $_=" $_"
164 | }' -- "$MARKED"
165 | }
166 |
167 | # Draw the file lists in the form of Miller columns.
168 | deer-refresh()
169 | {
170 | local FILES PREVIEW PARENTFILES OUTPUT REL_DIRNAME
171 | local SEPARATOR="------"
172 |
173 | PREDISPLAY=$OLD_LBUFFER
174 | REL_DIRNAME=${${DEER_DIRNAME%/}#$DEER_STARTDIR}/
175 | [ -n "$DEER_STARTDIR" ] && REL_DIRNAME=${REL_DIRNAME#/}
176 | LBUFFER=$REL_DIRNAME$DEER_BASENAME[$DEER_DIRNAME]
177 | RBUFFER=""
178 | local TMP_FILTER
179 | TMP_FILTER=${DEER_FILTER[$DEER_DIRNAME]}
180 | POSTDISPLAY=${TMP_FILTER:+ filt:$TMP_FILTER}
181 | region_highlight=("P0 $#PREDISPLAY fg=black,bold"
182 | "0 $#REL_DIRNAME fg=blue,bold"
183 | "$#BUFFER $[$#BUFFER+$#POSTDISPLAY] fg=yellow,bold")
184 |
185 |
186 | FILES=($DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-/:t)
187 | $SEPARATOR
188 | $DEER_DIRNAME/${~DEER_FILTER[$DEER_DIRNAME]:-'*'}(N$DEER_GLOBFLAGS-^/:t))
189 | PARENTFILES=($DEER_DIRNAME:h/${~DEER_FILTER[$DEER_DIRNAME:h]:-'*'}(N$DEER_GLOBFLAGS-/:t))
190 |
191 | local IFS=$'\n'
192 | FILES=($(deer-mark-file-list "$DEER_BASENAME[$DEER_DIRNAME]" $FILES))
193 | PARENTFILES=($(deer-mark-file-list "$DEER_DIRNAME:t" $PARENTFILES))
194 | unset IFS
195 |
196 | FILES=(${(F)FILES[1,$DEER_HEIGHT]})
197 | PARENTFILES=(${(F)PARENTFILES[1,$DEER_HEIGHT]})
198 |
199 |
200 | if [ -f $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] ]; then
201 | if file $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] | grep -Fq text; then
202 | PREVIEW="--- Preview: ---"$'\n'$(head -n$DEER_HEIGHT $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME])
203 |
204 | # Replace '/' with '∕' (division slash, U+2215) to allow using it as a
205 | # paste(1)/column(1) separator.
206 | PREVIEW=${PREVIEW//\//∕}
207 | else
208 | PREVIEW="--- Binary file, preview unavailable ---"
209 | fi
210 | else
211 | # I'm really sorry about what you see below.
212 | # It basically means: PREVIEW=(directories separator files)
213 | PREVIEW=($DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]/${~DEER_FILTER[$DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]]:-'*'}(N$DEER_GLOBFLAGS-/:t)
214 | $SEPARATOR
215 | $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]/${~DEER_FILTER[$DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]]:-'*'}(N$DEER_GLOBFLAGS-^/:t))
216 | PREVIEW=${(F)PREVIEW[1,$DEER_HEIGHT]}
217 | fi
218 |
219 | OUTPUT="$(paste -d/ <(<<< $PARENTFILES \
220 | | awk '{print substr($0,1,16)}') \
221 | <(<<< $FILES) \
222 | <(<<< $PREVIEW) \
223 | | sed 's,/, / ,g' \
224 | | column -t -s/ 2> /dev/null \
225 | | awk -v width=$COLUMNS '{print substr($0,1,width-1)}')"
226 | zle -M -- $OUTPUT
227 | zle -R
228 | }
229 |
230 | # Run `deer-add' with the same arguments, restore the shell state and
231 | # then exit.
232 | deer-restore()
233 | {
234 | deer-add "$@"
235 | PREDISPLAY=""
236 | POSTDISPLAY=""
237 | region_highlight=()
238 | LBUFFER=$OLD_LBUFFER
239 | RBUFFER=$OLD_RBUFFER
240 | zle reset-prompt
241 | zle -M ""
242 | }
243 |
244 | # Add the given string before or after the cursor.
245 | deer-add()
246 | {
247 | case $1 in
248 | --append)
249 | OLD_LBUFFER+=$2
250 | shift 2
251 | ;;
252 | --insert)
253 | OLD_RBUFFER=$2$OLD_RBUFFER
254 | shift 2
255 | ;;
256 | esac
257 | }
258 |
259 | # Get the quoted relative path from the absolute unquoted path.
260 | deer-get-relative()
261 | {
262 | local TMP
263 | TMP=${1:-${DEER_DIRNAME%/}/$DEER_BASENAME[$DEER_DIRNAME]}
264 | TMP="`python -c '
265 | import sys, os
266 | print(os.path.relpath(sys.argv[1], sys.argv[2]))
267 | ' $TMP ${DEER_STARTDIR:-$PWD}`"
268 | print -R $TMP:q
269 | }
270 |
271 | # Tries to guess a directory to start in from the current argument.
272 | deer-set-initial-directory()
273 | {
274 | autoload -U split-shell-arguments modify-current-argument
275 | local REPLY REPLY2 reply
276 | local DIRECTORY
277 |
278 | ((--CURSOR))
279 | split-shell-arguments
280 | ((++CURSOR))
281 |
282 | # Find the longest existing directory path in the current argument.
283 | DEER_STARTDIR=${(Q)${${reply[$REPLY]%%[[:space:]]#}:a}%/}
284 | while [ -n "$DEER_STARTDIR" -a \
285 | ! -d "$DEER_STARTDIR" ]; do
286 | DEER_STARTDIR=${DEER_STARTDIR%/*}
287 | done
288 |
289 | DEER_DIRNAME=${DEER_STARTDIR:-$PWD}
290 | }
291 |
292 | # The main entry function.
293 | deer-launch()
294 | {
295 | emulate -L zsh
296 | setopt extended_glob
297 | local DEER_DIRNAME DEER_STARTDIR DEER_GLOBFLAGS
298 | local -A DEER_FILTER DEER_BASENAME
299 | local REPLY OLD_LBUFFER OLD_RBUFFER
300 |
301 | local GREP_OPTIONS
302 | GREP_OPTIONS=""
303 |
304 | OLD_LBUFFER=$LBUFFER
305 | OLD_RBUFFER=$RBUFFER
306 |
307 | deer-set-initial-directory
308 |
309 | if [ "$DEER_SHOW_HIDDEN" = yes ]; then
310 | DEER_GLOBFLAGS=D
311 | else
312 | DEER_GLOBFLAGS=""
313 | fi
314 |
315 | if [ -n "$NUMERIC" ]; then
316 | for i in {1..$NUMERIC}; do
317 | deer-leave
318 | done
319 | else
320 | # Don't change cwd but initialize the variables.
321 | deer-leave
322 | deer-enter
323 | fi
324 |
325 | deer-refresh
326 | while read -k; do
327 | case $REPLY in
328 | # Movement
329 | $DEER_KEYS[up])
330 | deer-move -1
331 | deer-refresh
332 | ;;
333 | $DEER_KEYS[page_up])
334 | deer-move -5
335 | deer-refresh
336 | ;;
337 | $DEER_KEYS[down])
338 | deer-move 1
339 | deer-refresh
340 | ;;
341 | $DEER_KEYS[page_down])
342 | deer-move 5
343 | deer-refresh
344 | ;;
345 | $DEER_KEYS[enter])
346 | deer-enter
347 | deer-refresh
348 | ;;
349 | $DEER_KEYS[leave])
350 | deer-leave
351 | deer-refresh
352 | ;;
353 | $DEER_KEYS[next_parent])
354 | deer-leave
355 | deer-move 1
356 | deer-enter
357 | deer-refresh
358 | ;;
359 | $DEER_KEYS[prev_parent])
360 | deer-leave
361 | deer-move -1
362 | deer-enter
363 | deer-refresh
364 | ;;
365 | # Search
366 | $DEER_KEYS[search])
367 | deer-search
368 | deer-refresh
369 | ;;
370 | # Filter
371 | $DEER_KEYS[filter])
372 | deer-filter
373 | deer-refresh
374 | ;;
375 | $DEER_KEYS[toggle_hidden])
376 | if [ -z $DEER_GLOBFLAGS ]; then
377 | DEER_GLOBFLAGS="D" # show hidden files
378 | else
379 | DEER_GLOBFLAGS=""
380 | fi
381 | # make sure the focus is on a visible file
382 | DEER_BASENAME[$DEER_DIRNAME]=
383 | deer-leave
384 | deer-enter
385 | deer-refresh
386 | ;;
387 | # Quit
388 | $DEER_KEYS[quit])
389 | deer-restore
390 | break
391 | ;;
392 | # Insert the path and quit.
393 | $DEER_KEYS[append_path])
394 | deer-restore --append "`deer-get-relative` "
395 | break
396 | ;;
397 | $DEER_KEYS[append_abs_path])
398 | deer-restore --append "${${DEER_DIRNAME%/}:q}/${DEER_BASENAME[$DEER_DIRNAME]:q} "
399 | break
400 | ;;
401 | $DEER_KEYS[insert_path])
402 | deer-restore --insert " `deer-get-relative`"
403 | break
404 | ;;
405 | $DEER_KEYS[insert_abs_path])
406 | deer-restore --insert " ${${DEER_DIRNAME%/}:q}/${DEER_BASENAME[$DEER_DIRNAME]:q}"
407 | break
408 | ;;
409 | # Insert the path and don't quit yet.
410 | $DEER_KEYS[multi_insert_dwim])
411 | if [ "$OLD_LBUFFER[-1]" = "/" ]; then
412 | OLD_LBUFFER+="{"
413 | fi
414 | # replacement used to insert ',' instead of '{' as a separator in {foo,bar,...} lists
415 | deer-add --append "`deer-get-relative`"${${OLD_LBUFFER[-1]/\{/,}:- }
416 | deer-move 1
417 | deer-refresh
418 | ;;
419 | # Insert the absolute path and don't quit yet.
420 | $DEER_KEYS[multi_insert_abs])
421 | deer-add --append " ${${DEER_DIRNAME%/}:q}/${DEER_BASENAME[$DEER_DIRNAME]:q}"
422 | deer-move 1
423 | deer-refresh
424 | ;;
425 | # Quit and change the shell's current directory to the selected one.
426 | $DEER_KEYS[chdir])
427 | deer-leave
428 | ;&
429 | $DEER_KEYS[chdir_selected])
430 | if [[ -d $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] && \
431 | -x $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] ]]; then
432 | cd -- $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]
433 | deer-restore
434 | break
435 | fi
436 | ;;
437 | $DEER_KEYS[edit])
438 | if [[ -f $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] ]]; then
439 | "${EDITOR:-vim}" $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]
440 | fi
441 | ;;
442 | # See rifle(1) manpage (included with ranger(1)).
443 | $DEER_KEYS[rifle])
444 | if [[ -f $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME] ]]; then
445 | rifle $DEER_DIRNAME/$DEER_BASENAME[$DEER_DIRNAME]
446 | fi
447 | ;;
448 | # Arrow keys
449 | $'\e')
450 | read -k
451 | case $REPLY in
452 | '[')
453 | read -k
454 | case $REPLY in
455 | 'A')
456 | deer-move -1
457 | deer-refresh
458 | ;;
459 | 'B')
460 | deer-move 1
461 | deer-refresh
462 | ;;
463 | 'C')
464 | deer-enter
465 | deer-refresh
466 | ;;
467 | 'D')
468 | deer-leave
469 | deer-refresh
470 | ;;
471 | esac
472 | ;;
473 | esac
474 | ;;
475 | esac
476 | done
477 | }
478 |
479 | if zle; then
480 | deer-launch
481 | else
482 | deer()
483 | {
484 | deer-launch "$@"
485 | }
486 | fi
487 |
--------------------------------------------------------------------------------
/screenshots/2017-08-03-215440_640x364_scrot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vifon/deer/da9b86e98d712f4feadfd96070ddb4c99bd29e29/screenshots/2017-08-03-215440_640x364_scrot.png
--------------------------------------------------------------------------------
/screenshots/2017-08-03-215509_640x364_scrot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vifon/deer/da9b86e98d712f4feadfd96070ddb4c99bd29e29/screenshots/2017-08-03-215509_640x364_scrot.png
--------------------------------------------------------------------------------