aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/preview-tui
blob: fafee764660bfcaaf358141eb05c788349729865 (plain) (blame)
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