1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
#!/usr/bin/env sh
# Description: Terminal based file previewer
#
# Note: This plugin needs a "NNN_FIFO" to work. See man.
#
# Dependencies:
# - Supports 3 independent methods to preview with:
# - tmux (>=3.0), or
# - kitty with allow_remote_control on, or
# - $TERMINAL set to a terminal (it's xterm by default).
# - less or $PAGER
# - tree or exa or ls
# - mediainfo or file
# - unzip
# - tar
# - man
# - optional: bat for code syntax highlighting
# - optional: kitty terminal or catimg for images
# - optional: scope.sh file viewer from ranger.
# To use:
# 1. drop scope.sh executable in $PATH
# 2. set/export $USE_SCOPE as 1
# - optional: pistol file viewer (https://github.com/doronbehar/pistol).
# To use:
# 1. install pistol
# 2. set/export $USE_PISTOL as 1
#
# Usage:
# You need to set a NNN_FIFO path and a key for the plugin with NNN_PLUG,
# then start `nnn`:
#
# $ nnn -a
#
# or
#
# $ NNN_FIFO=/tmp/nnn.fifo nnn
#
# Then in `nnn`, launch the `preview-tui` plugin.
#
# If you provide the same NNN_FIFO to all nnn instances, there will be a
# single common preview window. I you provide different FIFO path (e.g.
# with -a), they will be independent.
#
# The previews will be shown in a tmux split. If that isn't possible, it
# will try to use a kitty terminal split. And as a final fallback, a
# different terminal window will be used ($TERMINAL).
#
# Tmux and kitty users can configure $SPLIT to either "h" or "v" to set a
# 'h'orizontal split or a 'v'ertical split (as in, the line that splits the
# windows will be horizontal or vertical).
#
# Kitty users need `allow_remote_control` set to `yes`. To customize the
# window split, `enabled_layouts` has to be set to `all` or `splits` (the
# former is the default value). This terminal is also able to show images
# without extra dependencies.
#
# Shell: POSIX compliant
# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero
SPLIT="$SPLIT" # you can set a permanent split here
TERMINAL="$TERMINAL" # same goes for the terminal
USE_SCOPE="${USE_SCOPE:-0}"
USE_PISTOL="${USE_PISTOL:-0}"
PAGER="${PAGER:-less -R}"
if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then
TERMINAL=tmux
elif [ -n "$KITTY_WINDOW_ID" ] && kitty @ ls >/dev/null 2>&1; then
TERMINAL=kitty
else
TERMINAL="${TERMINAL:-xterm}"
fi
if [ -z "$SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then
SPLIT='h'
elif [ "$SPLIT" != 'h' ]; then
SPLIT='v'
fi
exists() {
command -v "$1" >/dev/null 2>&1
}
fifo_pager() {
cmd="$1"
shift
# We use a FIFO to access $PAGER PID in jobs control
tmpfifopath="${TMPDIR:-/tmp}/nnn-preview-tui-fifo.$$"
mkfifo "$tmpfifopath" || return
$PAGER < "$tmpfifopath" &
(
exec > "$tmpfifopath"
"$cmd" "$@" &
)
rm "$tmpfifopath"
}
# Binary file: show file info inside the pager
print_bin_info() {
printf -- "-------- \033[1;31mBinary file\033[0m --------\n"
if exists mediainfo; then
mediainfo "$1" 2>/dev/null
else
file -b "$1"
fi
}
preview_file () {
kill %- %+ 2>/dev/null && wait %- %+ 2>/dev/null
clear
# Trying to use pistol if it's available.
if [ "$USE_PISTOL" -ne 0 ] && exists pistol; then
fifo_pager pistol "$1"
return
fi
# Trying to use scope.sh if it's available.
if [ "$USE_SCOPE" -ne 0 ] && exists scope.sh; then
fifo_pager scope.sh "$1" "$cols" "$lines" "$(mktemp -d)" \
"True" 2>/dev/null
return
fi
# Detecting the exact type of the file: the encoding, mime type, and
# extension in lowercase.
encoding="$(file -Lb --mime-encoding -- "$1")"
mimetype="$(file -Lb --mime-type -- "$1")"
ext="${1##*.}"
if [ -n "$ext" ]; then
ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')"
fi
lines=$(($(tput lines)-1))
cols=$(tput cols)
# Otherwise, falling back to the defaults.
if [ -d "$1" ]; then
cd "$1" || return
if exists tree; then
fifo_pager tree -L 3 -F
elif exists exa; then
fifo_pager exa -G --colour=always 2>/dev/null
else
fifo_pager ls --color=always
fi
elif [ "$encoding" = "binary" ]; then
if [ "${mimetype%%/*}" = "image" ] ; then
if [ "$TERMINAL" = "kitty" ]; then
# Kitty terminal users can use the native image preview method.
kitty +kitten icat --silent --transfer-mode=stream --stdin=no \
"$1" &
elif exists catimg; then
catimg "$1"
elif exists viu; then
viu -t "$1"
else
fifo_pager print_bin_info "$1"
fi
elif [ "$mimetype" = "application/zip" ] ; then
fifo_pager unzip -l "$1"
elif [ "$ext" = "gz" ] || [ "$ext" = "bz2" ] ; then
fifo_pager tar -tvf "$1"
else
fifo_pager print_bin_info "$1"
fi
elif [ "$mimetype" = "text/troff" ] ; then
fifo_pager man -Pcat -l "$1"
else
if exists bat; then
fifo_pager bat --paging=never --decorations=always --color=always \
"$1" 2>/dev/null
else
$PAGER "$1" &
fi
fi
}
if [ "$PREVIEW_MODE" ] ; then
if [ ! -r "$NNN_FIFO" ] ; then
echo "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')" >&2
read -r
exit 1
fi
preview_file "$1"
# use cat instead of 'exec <' to avoid issues with dash shell
# shellcheck disable=SC2002
cat "$NNN_FIFO" |\
while read -r selection ; do
preview_file "$selection"
done
# Restoring the previous layout for kitty users. This will only work for
# kitty >= 0.18.0.
if [ "$TERMINAL" = "kitty" ]; then
kitty @ last-used-layout --no-response >/dev/null 2>&1
fi
exit 0
fi
if [ "$TERMINAL" = "tmux" ]; then
# tmux splits are inverted
if [ "$SPLIT" = "v" ]; then SPLIT="h"; else SPLIT="v"; fi
tmux split-window -e "NNN_FIFO=$NNN_FIFO" -e "PREVIEW_MODE=1" -d"$SPLIT" "$0" "$1"
elif [ "$TERMINAL" = "kitty" ]; then
# Setting the layout for the new window. It will be restored after the
# script ends.
kitty @ goto-layout splits >/dev/null
# Trying to use kitty's integrated window management as the split window.
# All environmental variables that will be used in the new window must
# be explicitly passed.
kitty @ launch --no-response --title "nnn preview" --keep-focus \
--cwd "$PWD" --env "PATH=$PATH" --env "NNN_FIFO=$NNN_FIFO" \
--env "PREVIEW_MODE=1" --env "PAGER=$PAGER" \
--env "USE_SCOPE=$USE_SCOPE" --env "SPLIT=$SPLIT" \
--env "USE_PISTOL=$USE_PISTOL" \
--location "${SPLIT}split" "$0" "$1" >/dev/null
else
PREVIEW_MODE=1 $TERMINAL -e "$0" "$1" &
fi
|