mimimum zsh plugins
This commit is contained in:
parent
dfd0ebf8c7
commit
28bae624a4
|
@ -13,3 +13,5 @@
|
||||||
!.zsh/**
|
!.zsh/**
|
||||||
!.tmux.conf
|
!.tmux.conf
|
||||||
!.gitconfig
|
!.gitconfig
|
||||||
|
!.local
|
||||||
|
!.local/**
|
||||||
|
|
|
@ -0,0 +1,381 @@
|
||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ completion.bash
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX (default: 0)
|
||||||
|
# - $FZF_TMUX_OPTS (default: empty)
|
||||||
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
|
if [[ $- =~ i ]]; then
|
||||||
|
|
||||||
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
|
if ! declare -f _fzf_compgen_path > /dev/null; then
|
||||||
|
_fzf_compgen_path() {
|
||||||
|
echo "$1"
|
||||||
|
command find -L "$1" \
|
||||||
|
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
||||||
|
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! declare -f _fzf_compgen_dir > /dev/null; then
|
||||||
|
_fzf_compgen_dir() {
|
||||||
|
command find -L "$1" \
|
||||||
|
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
||||||
|
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
# To redraw line after fzf closes (printf '\e[5n')
|
||||||
|
bind '"\e[0n": redraw-current-line' 2> /dev/null
|
||||||
|
|
||||||
|
__fzf_comprun() {
|
||||||
|
if [[ "$(type -t _fzf_comprun 2>&1)" = function ]]; then
|
||||||
|
_fzf_comprun "$@"
|
||||||
|
elif [[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; }; then
|
||||||
|
shift
|
||||||
|
fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- "$@"
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
fzf "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_orig_completion() {
|
||||||
|
local l comp f cmd
|
||||||
|
while read -r l; do
|
||||||
|
if [[ "$l" =~ ^(.*\ -F)\ *([^ ]*).*\ ([^ ]*)$ ]]; then
|
||||||
|
comp="${BASH_REMATCH[1]}"
|
||||||
|
f="${BASH_REMATCH[2]}"
|
||||||
|
cmd="${BASH_REMATCH[3]}"
|
||||||
|
[[ "$f" = _fzf_* ]] && continue
|
||||||
|
printf -v "_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}" "%s" "${comp} %s ${cmd} #${f}"
|
||||||
|
if [[ "$l" = *" -o nospace "* ]] && [[ ! "${__fzf_nospace_commands-}" = *" $cmd "* ]]; then
|
||||||
|
__fzf_nospace_commands="${__fzf_nospace_commands-} $cmd "
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_opts_completion() {
|
||||||
|
local cur prev opts
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
|
opts="
|
||||||
|
-x --extended
|
||||||
|
-e --exact
|
||||||
|
--algo
|
||||||
|
-i +i
|
||||||
|
-n --nth
|
||||||
|
--with-nth
|
||||||
|
-d --delimiter
|
||||||
|
+s --no-sort
|
||||||
|
--tac
|
||||||
|
--tiebreak
|
||||||
|
-m --multi
|
||||||
|
--no-mouse
|
||||||
|
--bind
|
||||||
|
--cycle
|
||||||
|
--no-hscroll
|
||||||
|
--jump-labels
|
||||||
|
--height
|
||||||
|
--literal
|
||||||
|
--reverse
|
||||||
|
--margin
|
||||||
|
--inline-info
|
||||||
|
--prompt
|
||||||
|
--pointer
|
||||||
|
--marker
|
||||||
|
--header
|
||||||
|
--header-lines
|
||||||
|
--ansi
|
||||||
|
--tabstop
|
||||||
|
--color
|
||||||
|
--no-bold
|
||||||
|
--history
|
||||||
|
--history-size
|
||||||
|
--preview
|
||||||
|
--preview-window
|
||||||
|
-q --query
|
||||||
|
-1 --select-1
|
||||||
|
-0 --exit-0
|
||||||
|
-f --filter
|
||||||
|
--print-query
|
||||||
|
--expect
|
||||||
|
--sync"
|
||||||
|
|
||||||
|
case "${prev}" in
|
||||||
|
--tiebreak)
|
||||||
|
COMPREPLY=( $(compgen -W "length begin end index" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--color)
|
||||||
|
COMPREPLY=( $(compgen -W "dark light 16 bw" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--history)
|
||||||
|
COMPREPLY=()
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$cur" =~ ^-|\+ ]]; then
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- "$cur") )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_handle_dynamic_completion() {
|
||||||
|
local cmd orig_var orig ret orig_cmd orig_complete
|
||||||
|
cmd="$1"
|
||||||
|
shift
|
||||||
|
orig_cmd="$1"
|
||||||
|
orig_var="_fzf_orig_completion_$cmd"
|
||||||
|
orig="${!orig_var-}"
|
||||||
|
orig="${orig##*#}"
|
||||||
|
if [[ -n "$orig" ]] && type "$orig" > /dev/null 2>&1; then
|
||||||
|
$orig "$@"
|
||||||
|
elif [[ -n "${_fzf_completion_loader-}" ]]; then
|
||||||
|
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
|
||||||
|
_completion_loader "$@"
|
||||||
|
ret=$?
|
||||||
|
# _completion_loader may not have updated completion for the command
|
||||||
|
if [[ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]]; then
|
||||||
|
__fzf_orig_completion < <(complete -p "$orig_cmd" 2> /dev/null)
|
||||||
|
if [[ "${__fzf_nospace_commands-}" = *" $orig_cmd "* ]]; then
|
||||||
|
eval "${orig_complete/ -F / -o nospace -F }"
|
||||||
|
else
|
||||||
|
eval "$orig_complete"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return $ret
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_generic_path_completion() {
|
||||||
|
local cur base dir leftover matches trigger cmd
|
||||||
|
cmd="${COMP_WORDS[0]}"
|
||||||
|
if [[ $cmd == \\* ]]; then
|
||||||
|
cmd="${cmd:1}"
|
||||||
|
fi
|
||||||
|
cmd="${cmd//[^A-Za-z0-9_=]/_}"
|
||||||
|
COMPREPLY=()
|
||||||
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
if [[ "$cur" == *"$trigger" ]]; then
|
||||||
|
base=${cur:0:${#cur}-${#trigger}}
|
||||||
|
eval "base=$base"
|
||||||
|
|
||||||
|
dir=
|
||||||
|
[[ $base = *"/"* ]] && dir="$base"
|
||||||
|
while true; do
|
||||||
|
if [[ -z "$dir" ]] || [[ -d "$dir" ]]; then
|
||||||
|
leftover=${base/#"$dir"}
|
||||||
|
leftover=${leftover/#\/}
|
||||||
|
[[ -z "$dir" ]] && dir='.'
|
||||||
|
[[ "$dir" != "/" ]] && dir="${dir/%\//}"
|
||||||
|
matches=$(eval "$1 $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $2" __fzf_comprun "$4" -q "$leftover" | while read -r item; do
|
||||||
|
printf "%q " "${item%$3}$3"
|
||||||
|
done)
|
||||||
|
matches=${matches% }
|
||||||
|
[[ -z "$3" ]] && [[ "${__fzf_nospace_commands-}" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
|
||||||
|
if [[ -n "$matches" ]]; then
|
||||||
|
COMPREPLY=( "$matches" )
|
||||||
|
else
|
||||||
|
COMPREPLY=( "$cur" )
|
||||||
|
fi
|
||||||
|
printf '\e[5n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
dir=$(dirname "$dir")
|
||||||
|
[[ "$dir" =~ /$ ]] || dir="$dir"/
|
||||||
|
done
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
shift
|
||||||
|
_fzf_handle_dynamic_completion "$cmd" "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete() {
|
||||||
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in "${!args[@]}"; do
|
||||||
|
if [[ "${args[$i]}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local cur selected trigger cmd post
|
||||||
|
post="$(caller 0 | awk '{print $2}')_post"
|
||||||
|
type -t "$post" > /dev/null 2>&1 || post=cat
|
||||||
|
|
||||||
|
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
|
||||||
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
if [[ "$cur" == *"$trigger" ]]; then
|
||||||
|
cur=${cur:0:${#cur}-${#trigger}}
|
||||||
|
|
||||||
|
selected=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $str_arg" __fzf_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
|
||||||
|
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
|
||||||
|
if [[ -n "$selected" ]]; then
|
||||||
|
COMPREPLY=("$selected")
|
||||||
|
else
|
||||||
|
COMPREPLY=("$cur")
|
||||||
|
fi
|
||||||
|
printf '\e[5n'
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
_fzf_handle_dynamic_completion "$cmd" "${rest[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_path_completion() {
|
||||||
|
__fzf_generic_path_completion _fzf_compgen_path "-m" "" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deprecated. No file only completion.
|
||||||
|
_fzf_file_completion() {
|
||||||
|
_fzf_path_completion "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_dir_completion() {
|
||||||
|
__fzf_generic_path_completion _fzf_compgen_dir "" "/" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_kill() {
|
||||||
|
_fzf_proc_completion "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_proc_completion() {
|
||||||
|
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||||
|
command ps -ef | sed 1d
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_proc_completion_post() {
|
||||||
|
awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_host_completion() {
|
||||||
|
_fzf_complete +m -- "$@" < <(
|
||||||
|
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \
|
||||||
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||||
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_var_completion() {
|
||||||
|
_fzf_complete -m -- "$@" < <(
|
||||||
|
declare -xp | sed -En 's|^declare [^ ]+ ([^=]+).*|\1|p'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_alias_completion() {
|
||||||
|
_fzf_complete -m -- "$@" < <(
|
||||||
|
alias | sed -En 's|^alias ([^=]+).*|\1|p'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# fzf options
|
||||||
|
complete -o default -F _fzf_opts_completion fzf
|
||||||
|
# fzf-tmux is a thin fzf wrapper that has only a few more options than fzf
|
||||||
|
# itself. As a quick improvement we take fzf's completion. Adding the few extra
|
||||||
|
# fzf-tmux specific options (like `-w WIDTH`) are left as a future patch.
|
||||||
|
complete -o default -F _fzf_opts_completion fzf-tmux
|
||||||
|
|
||||||
|
d_cmds="${FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
|
||||||
|
a_cmds="
|
||||||
|
awk cat diff diff3
|
||||||
|
emacs emacsclient ex file ftp g++ gcc gvim head hg java
|
||||||
|
javac ld less more mvim nvim patch perl python ruby
|
||||||
|
sed sftp sort source tail tee uniq vi view vim wc xdg-open
|
||||||
|
basename bunzip2 bzip2 chmod chown curl cp dirname du
|
||||||
|
find git grep gunzip gzip hg jar
|
||||||
|
ln ls mv open rm rsync scp
|
||||||
|
svn tar unzip zip"
|
||||||
|
|
||||||
|
# Preserve existing completion
|
||||||
|
__fzf_orig_completion < <(complete -p $d_cmds $a_cmds 2> /dev/null)
|
||||||
|
|
||||||
|
if type _completion_loader > /dev/null 2>&1; then
|
||||||
|
_fzf_completion_loader=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
__fzf_defc() {
|
||||||
|
local cmd func opts orig_var orig def
|
||||||
|
cmd="$1"
|
||||||
|
func="$2"
|
||||||
|
opts="$3"
|
||||||
|
orig_var="_fzf_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
|
||||||
|
orig="${!orig_var-}"
|
||||||
|
if [[ -n "$orig" ]]; then
|
||||||
|
printf -v def "$orig" "$func"
|
||||||
|
eval "$def"
|
||||||
|
else
|
||||||
|
complete -F "$func" $opts "$cmd"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Anything
|
||||||
|
for cmd in $a_cmds; do
|
||||||
|
__fzf_defc "$cmd" _fzf_path_completion "-o default -o bashdefault"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Directory
|
||||||
|
for cmd in $d_cmds; do
|
||||||
|
__fzf_defc "$cmd" _fzf_dir_completion "-o nospace -o dirnames"
|
||||||
|
done
|
||||||
|
|
||||||
|
unset cmd d_cmds a_cmds
|
||||||
|
|
||||||
|
_fzf_setup_completion() {
|
||||||
|
local kind fn cmd
|
||||||
|
kind=$1
|
||||||
|
fn=_fzf_${1}_completion
|
||||||
|
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
|
||||||
|
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
__fzf_orig_completion < <(complete -p "$@" 2> /dev/null)
|
||||||
|
for cmd in "$@"; do
|
||||||
|
case "$kind" in
|
||||||
|
dir) __fzf_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
|
||||||
|
var) __fzf_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
|
||||||
|
alias) __fzf_defc "$cmd" "$fn" "-a" ;;
|
||||||
|
*) __fzf_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Environment variables / Aliases / Hosts / Process
|
||||||
|
_fzf_setup_completion 'var' export unset
|
||||||
|
_fzf_setup_completion 'alias' unalias
|
||||||
|
_fzf_setup_completion 'host' ssh telnet
|
||||||
|
_fzf_setup_completion 'proc' kill
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,324 @@
|
||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ completion.zsh
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX (default: 0)
|
||||||
|
# - $FZF_TMUX_OPTS (default: '-d 40%')
|
||||||
|
# - $FZF_COMPLETION_TRIGGER (default: '**')
|
||||||
|
# - $FZF_COMPLETION_OPTS (default: empty)
|
||||||
|
|
||||||
|
# Both branches of the following `if` do the same thing -- define
|
||||||
|
# __fzf_completion_options such that `eval $__fzf_completion_options` sets
|
||||||
|
# all options to the same values they currently have. We'll do just that at
|
||||||
|
# the bottom of the file after changing options to what we prefer.
|
||||||
|
#
|
||||||
|
# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
|
||||||
|
# *must* be quoted in order to prevent alias expansion. In addition, code must
|
||||||
|
# be written in a way works with any set of zsh options. This is very tricky, so
|
||||||
|
# careful when you change it.
|
||||||
|
#
|
||||||
|
# Start by loading the builtin zsh/parameter module. It provides `options`
|
||||||
|
# associative array that stores current shell options.
|
||||||
|
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
|
||||||
|
# This is the fast branch and it gets taken on virtually all Zsh installations.
|
||||||
|
#
|
||||||
|
# ${(kv)options[@]} expands to array of keys (option names) and values ("on"
|
||||||
|
# or "off"). The subsequent expansion# with (j: :) flag joins all elements
|
||||||
|
# together separated by spaces. __fzf_completion_options ends up with a value
|
||||||
|
# like this: "options=(shwordsplit off aliases on ...)".
|
||||||
|
__fzf_completion_options="options=(${(j: :)${(kv)options[@]}})"
|
||||||
|
else
|
||||||
|
# This branch is much slower because it forks to get the names of all
|
||||||
|
# zsh options. It's possible to eliminate this fork but it's not worth the
|
||||||
|
# trouble because this branch gets taken only on very ancient or broken
|
||||||
|
# zsh installations.
|
||||||
|
() {
|
||||||
|
# That `()` above defines an anonymous function. This is essentially a scope
|
||||||
|
# for local parameters. We use it to avoid polluting global scope.
|
||||||
|
'local' '__fzf_opt'
|
||||||
|
__fzf_completion_options="setopt"
|
||||||
|
# `set -o` prints one line for every zsh option. Each line contains option
|
||||||
|
# name, some spaces, and then either "on" or "off". We just want option names.
|
||||||
|
# Expansion with (@f) flag splits a string into lines. The outer expansion
|
||||||
|
# removes spaces and everything that follow them on every line. __fzf_opt
|
||||||
|
# ends up iterating over option names: shwordsplit, aliases, etc.
|
||||||
|
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
|
||||||
|
if [[ -o "$__fzf_opt" ]]; then
|
||||||
|
# Option $__fzf_opt is currently on, so remember to set it back on.
|
||||||
|
__fzf_completion_options+=" -o $__fzf_opt"
|
||||||
|
else
|
||||||
|
# Option $__fzf_opt is currently off, so remember to set it back off.
|
||||||
|
__fzf_completion_options+=" +o $__fzf_opt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# The value of __fzf_completion_options here looks like this:
|
||||||
|
# "setopt +o shwordsplit -o aliases ..."
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable the default zsh options (those marked with <Z> in `man zshoptions`)
|
||||||
|
# but without `aliases`. Aliases in functions are expanded when functions are
|
||||||
|
# defined, so if we disable aliases here, we'll be sure to have no pesky
|
||||||
|
# aliases in any of our functions. This way we won't need prefix every
|
||||||
|
# command with `command` or to quote every word to defend against global
|
||||||
|
# aliases. Note that `aliases` is not the only option that's important to
|
||||||
|
# control. There are several others that could wreck havoc if they are set
|
||||||
|
# to values we don't expect. With the following `emulate` command we
|
||||||
|
# sidestep this issue entirely.
|
||||||
|
'emulate' 'zsh' '-o' 'no_aliases'
|
||||||
|
|
||||||
|
# This brace is the start of try-always block. The `always` part is like
|
||||||
|
# `finally` in lesser languages. We use it to *always* restore user options.
|
||||||
|
{
|
||||||
|
|
||||||
|
# Bail out if not interactive shell.
|
||||||
|
[[ -o interactive ]] || return 0
|
||||||
|
|
||||||
|
# To use custom commands instead of find, override _fzf_compgen_{path,dir}
|
||||||
|
if ! declare -f _fzf_compgen_path > /dev/null; then
|
||||||
|
_fzf_compgen_path() {
|
||||||
|
echo "$1"
|
||||||
|
command find -L "$1" \
|
||||||
|
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
|
||||||
|
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! declare -f _fzf_compgen_dir > /dev/null; then
|
||||||
|
_fzf_compgen_dir() {
|
||||||
|
command find -L "$1" \
|
||||||
|
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
|
||||||
|
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
|
||||||
|
__fzf_comprun() {
|
||||||
|
if [[ "$(type _fzf_comprun 2>&1)" =~ function ]]; then
|
||||||
|
_fzf_comprun "$@"
|
||||||
|
elif [ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; }; then
|
||||||
|
shift
|
||||||
|
if [ -n "${FZF_TMUX_OPTS-}" ]; then
|
||||||
|
fzf-tmux ${(Q)${(Z+n+)FZF_TMUX_OPTS}} -- "$@"
|
||||||
|
else
|
||||||
|
fzf-tmux -d ${FZF_TMUX_HEIGHT:-40%} -- "$@"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
fzf "$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract the name of the command. e.g. foo=1 bar baz**<tab>
|
||||||
|
__fzf_extract_command() {
|
||||||
|
local token tokens
|
||||||
|
tokens=(${(z)1})
|
||||||
|
for token in $tokens; do
|
||||||
|
token=${(Q)token}
|
||||||
|
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
|
||||||
|
echo "$token"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "${tokens[1]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_generic_path_completion() {
|
||||||
|
local base lbuf cmd compgen fzf_opts suffix tail dir leftover matches
|
||||||
|
base=$1
|
||||||
|
lbuf=$2
|
||||||
|
cmd=$(__fzf_extract_command "$lbuf")
|
||||||
|
compgen=$3
|
||||||
|
fzf_opts=$4
|
||||||
|
suffix=$5
|
||||||
|
tail=$6
|
||||||
|
|
||||||
|
setopt localoptions nonomatch
|
||||||
|
eval "base=$base"
|
||||||
|
[[ $base = *"/"* ]] && dir="$base"
|
||||||
|
while [ 1 ]; do
|
||||||
|
if [[ -z "$dir" || -d ${dir} ]]; then
|
||||||
|
leftover=${base/#"$dir"}
|
||||||
|
leftover=${leftover/#\/}
|
||||||
|
[ -z "$dir" ] && dir='.'
|
||||||
|
[ "$dir" != "/" ] && dir="${dir/%\//}"
|
||||||
|
matches=$(eval "$compgen $(printf %q "$dir")" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-}" __fzf_comprun "$cmd" ${(Q)${(Z+n+)fzf_opts}} -q "$leftover" | while read item; do
|
||||||
|
item="${item%$suffix}$suffix"
|
||||||
|
echo -n "${(q)item} "
|
||||||
|
done)
|
||||||
|
matches=${matches% }
|
||||||
|
if [ -n "$matches" ]; then
|
||||||
|
LBUFFER="$lbuf$matches$tail"
|
||||||
|
fi
|
||||||
|
zle reset-prompt
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
dir=$(dirname "$dir")
|
||||||
|
dir=${dir%/}/
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_path_completion() {
|
||||||
|
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_path \
|
||||||
|
"-m" "" " "
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_dir_completion() {
|
||||||
|
__fzf_generic_path_completion "$1" "$2" _fzf_compgen_dir \
|
||||||
|
"" "/" ""
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_feed_fifo() (
|
||||||
|
command rm -f "$1"
|
||||||
|
mkfifo "$1"
|
||||||
|
cat <&0 > "$1" &
|
||||||
|
)
|
||||||
|
|
||||||
|
_fzf_complete() {
|
||||||
|
setopt localoptions ksh_arrays
|
||||||
|
# Split arguments around --
|
||||||
|
local args rest str_arg i sep
|
||||||
|
args=("$@")
|
||||||
|
sep=
|
||||||
|
for i in {0..${#args[@]}}; do
|
||||||
|
if [[ "${args[$i]-}" = -- ]]; then
|
||||||
|
sep=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$sep" ]]; then
|
||||||
|
str_arg=
|
||||||
|
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
|
||||||
|
args=("${args[@]:0:$sep}")
|
||||||
|
else
|
||||||
|
str_arg=$1
|
||||||
|
args=()
|
||||||
|
shift
|
||||||
|
rest=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
local fifo lbuf cmd matches post
|
||||||
|
fifo="${TMPDIR:-/tmp}/fzf-complete-fifo-$$"
|
||||||
|
lbuf=${rest[0]}
|
||||||
|
cmd=$(__fzf_extract_command "$lbuf")
|
||||||
|
post="${funcstack[1]}_post"
|
||||||
|
type $post > /dev/null 2>&1 || post=cat
|
||||||
|
|
||||||
|
_fzf_feed_fifo "$fifo"
|
||||||
|
matches=$(FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_COMPLETION_OPTS-} $str_arg" __fzf_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
|
||||||
|
if [ -n "$matches" ]; then
|
||||||
|
LBUFFER="$lbuf$matches"
|
||||||
|
fi
|
||||||
|
command rm -f "$fifo"
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_telnet() {
|
||||||
|
_fzf_complete +m -- "$@" < <(
|
||||||
|
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
|
||||||
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_ssh() {
|
||||||
|
_fzf_complete +m -- "$@" < <(
|
||||||
|
setopt localoptions nonomatch
|
||||||
|
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?%]') \
|
||||||
|
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
|
||||||
|
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
|
||||||
|
awk '{if (length($2) > 0) {print $2}}' | sort -u
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_export() {
|
||||||
|
_fzf_complete -m -- "$@" < <(
|
||||||
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_unset() {
|
||||||
|
_fzf_complete -m -- "$@" < <(
|
||||||
|
declare -xp | sed 's/=.*//' | sed 's/.* //'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_unalias() {
|
||||||
|
_fzf_complete +m -- "$@" < <(
|
||||||
|
alias | sed 's/=.*//'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_kill() {
|
||||||
|
_fzf_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
|
||||||
|
command ps -ef | sed 1d
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf_complete_kill_post() {
|
||||||
|
awk '{print $2}'
|
||||||
|
}
|
||||||
|
|
||||||
|
fzf-completion() {
|
||||||
|
local tokens cmd prefix trigger tail matches lbuf d_cmds
|
||||||
|
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
|
||||||
|
|
||||||
|
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
|
||||||
|
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
|
||||||
|
tokens=(${(z)LBUFFER})
|
||||||
|
if [ ${#tokens} -lt 1 ]; then
|
||||||
|
zle ${fzf_default_completion:-expand-or-complete}
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmd=$(__fzf_extract_command "$LBUFFER")
|
||||||
|
|
||||||
|
# Explicitly allow for empty trigger.
|
||||||
|
trigger=${FZF_COMPLETION_TRIGGER-'**'}
|
||||||
|
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
|
||||||
|
|
||||||
|
# When the trigger starts with ';', it becomes a separate token
|
||||||
|
if [[ ${LBUFFER} = *"${tokens[-2]-}${tokens[-1]}" ]]; then
|
||||||
|
tokens[-2]="${tokens[-2]-}${tokens[-1]}"
|
||||||
|
tokens=(${tokens[0,-2]})
|
||||||
|
fi
|
||||||
|
|
||||||
|
lbuf=$LBUFFER
|
||||||
|
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
|
||||||
|
|
||||||
|
# Trigger sequence given
|
||||||
|
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
|
||||||
|
d_cmds=(${=FZF_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
|
||||||
|
|
||||||
|
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
|
||||||
|
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
|
||||||
|
|
||||||
|
if eval "type _fzf_complete_${cmd} > /dev/null"; then
|
||||||
|
prefix="$prefix" eval _fzf_complete_${cmd} ${(q)lbuf}
|
||||||
|
zle reset-prompt
|
||||||
|
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
|
||||||
|
_fzf_dir_completion "$prefix" "$lbuf"
|
||||||
|
else
|
||||||
|
_fzf_path_completion "$prefix" "$lbuf"
|
||||||
|
fi
|
||||||
|
# Fall back to default completion
|
||||||
|
else
|
||||||
|
zle ${fzf_default_completion:-expand-or-complete}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -z "$fzf_default_completion" ] && {
|
||||||
|
binding=$(bindkey '^I')
|
||||||
|
[[ $binding =~ 'undefined-key' ]] || fzf_default_completion=$binding[(s: :w)2]
|
||||||
|
unset binding
|
||||||
|
}
|
||||||
|
|
||||||
|
zle -N fzf-completion
|
||||||
|
bindkey '^I' fzf-completion
|
||||||
|
|
||||||
|
} always {
|
||||||
|
# Restore the original options.
|
||||||
|
eval $__fzf_completion_options
|
||||||
|
'unset' '__fzf_completion_options'
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ key-bindings.bash
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX_OPTS
|
||||||
|
# - $FZF_CTRL_T_COMMAND
|
||||||
|
# - $FZF_CTRL_T_OPTS
|
||||||
|
# - $FZF_CTRL_R_OPTS
|
||||||
|
# - $FZF_ALT_C_COMMAND
|
||||||
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
|
# Key bindings
|
||||||
|
# ------------
|
||||||
|
__fzf_select__() {
|
||||||
|
local cmd opts
|
||||||
|
cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
|
-o -type f -print \
|
||||||
|
-o -type d -print \
|
||||||
|
-o -type l -print 2> /dev/null | cut -b3-"}"
|
||||||
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-} -m"
|
||||||
|
eval "$cmd" |
|
||||||
|
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) "$@" |
|
||||||
|
while read -r item; do
|
||||||
|
printf '%q ' "$item" # escape special chars
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $- =~ i ]]; then
|
||||||
|
|
||||||
|
__fzfcmd() {
|
||||||
|
[[ -n "${TMUX_PANE-}" ]] && { [[ "${FZF_TMUX:-0}" != 0 ]] || [[ -n "${FZF_TMUX_OPTS-}" ]]; } &&
|
||||||
|
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||||
|
}
|
||||||
|
|
||||||
|
fzf-file-widget() {
|
||||||
|
local selected="$(__fzf_select__ "$@")"
|
||||||
|
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
|
||||||
|
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_cd__() {
|
||||||
|
local cmd opts dir
|
||||||
|
cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
|
-o -type d -print 2> /dev/null | cut -b3-"}"
|
||||||
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore --reverse ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-} +m"
|
||||||
|
dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="$opts" $(__fzfcmd)) && printf 'builtin cd -- %q' "$dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzf_history__() {
|
||||||
|
local output opts script
|
||||||
|
opts="--height ${FZF_TMUX_HEIGHT:-40%} --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} -n2..,.. --scheme=history --bind=ctrl-r:toggle-sort ${FZF_CTRL_R_OPTS-} +m --read0"
|
||||||
|
script='BEGIN { getc; $/ = "\n\t"; $HISTCOUNT = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCOUNT - $. . "\t$_" if !$seen{$_}++'
|
||||||
|
output=$(
|
||||||
|
builtin fc -lnr -2147483648 |
|
||||||
|
last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e "$script" |
|
||||||
|
FZF_DEFAULT_OPTS="$opts" $(__fzfcmd) --query "$READLINE_LINE"
|
||||||
|
) || return
|
||||||
|
READLINE_LINE=${output#*$'\t'}
|
||||||
|
if [[ -z "$READLINE_POINT" ]]; then
|
||||||
|
echo "$READLINE_LINE"
|
||||||
|
else
|
||||||
|
READLINE_POINT=0x7fffffff
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Required to refresh the prompt after fzf
|
||||||
|
bind -m emacs-standard '"\er": redraw-current-line'
|
||||||
|
|
||||||
|
bind -m vi-command '"\C-z": emacs-editing-mode'
|
||||||
|
bind -m vi-insert '"\C-z": emacs-editing-mode'
|
||||||
|
bind -m emacs-standard '"\C-z": vi-editing-mode'
|
||||||
|
|
||||||
|
if (( BASH_VERSINFO[0] < 4 )); then
|
||||||
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
|
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
|
||||||
|
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
|
||||||
|
|
||||||
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
|
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__fzf_history__)"\e\C-e\er"'
|
||||||
|
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
|
||||||
|
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
|
||||||
|
else
|
||||||
|
# CTRL-T - Paste the selected file path into the command line
|
||||||
|
bind -m emacs-standard -x '"\C-t": fzf-file-widget'
|
||||||
|
bind -m vi-command -x '"\C-t": fzf-file-widget'
|
||||||
|
bind -m vi-insert -x '"\C-t": fzf-file-widget'
|
||||||
|
|
||||||
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
|
bind -m emacs-standard -x '"\C-r": __fzf_history__'
|
||||||
|
bind -m vi-command -x '"\C-r": __fzf_history__'
|
||||||
|
bind -m vi-insert -x '"\C-r": __fzf_history__'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ALT-C - cd into the selected directory
|
||||||
|
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
|
||||||
|
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
|
||||||
|
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,121 @@
|
||||||
|
# ____ ____
|
||||||
|
# / __/___ / __/
|
||||||
|
# / /_/_ / / /_
|
||||||
|
# / __/ / /_/ __/
|
||||||
|
# /_/ /___/_/ key-bindings.zsh
|
||||||
|
#
|
||||||
|
# - $FZF_TMUX_OPTS
|
||||||
|
# - $FZF_CTRL_T_COMMAND
|
||||||
|
# - $FZF_CTRL_T_OPTS
|
||||||
|
# - $FZF_CTRL_R_OPTS
|
||||||
|
# - $FZF_ALT_C_COMMAND
|
||||||
|
# - $FZF_ALT_C_OPTS
|
||||||
|
|
||||||
|
# Key bindings
|
||||||
|
# ------------
|
||||||
|
|
||||||
|
# The code at the top and the bottom of this file is the same as in completion.zsh.
|
||||||
|
# Refer to that file for explanation.
|
||||||
|
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
|
||||||
|
__fzf_key_bindings_options="options=(${(j: :)${(kv)options[@]}})"
|
||||||
|
else
|
||||||
|
() {
|
||||||
|
__fzf_key_bindings_options="setopt"
|
||||||
|
'local' '__fzf_opt'
|
||||||
|
for __fzf_opt in "${(@)${(@f)$(set -o)}%% *}"; do
|
||||||
|
if [[ -o "$__fzf_opt" ]]; then
|
||||||
|
__fzf_key_bindings_options+=" -o $__fzf_opt"
|
||||||
|
else
|
||||||
|
__fzf_key_bindings_options+=" +o $__fzf_opt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
'emulate' 'zsh' '-o' 'no_aliases'
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
[[ -o interactive ]] || return 0
|
||||||
|
|
||||||
|
# CTRL-T - Paste the selected file path(s) into the command line
|
||||||
|
__fsel() {
|
||||||
|
local cmd="${FZF_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
|
-o -type f -print \
|
||||||
|
-o -type d -print \
|
||||||
|
-o -type l -print 2> /dev/null | cut -b3-"}"
|
||||||
|
setopt localoptions pipefail no_aliases 2> /dev/null
|
||||||
|
local item
|
||||||
|
eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_CTRL_T_OPTS-}" $(__fzfcmd) -m "$@" | while read item; do
|
||||||
|
echo -n "${(q)item} "
|
||||||
|
done
|
||||||
|
local ret=$?
|
||||||
|
echo
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
__fzfcmd() {
|
||||||
|
[ -n "${TMUX_PANE-}" ] && { [ "${FZF_TMUX:-0}" != 0 ] || [ -n "${FZF_TMUX_OPTS-}" ]; } &&
|
||||||
|
echo "fzf-tmux ${FZF_TMUX_OPTS:--d${FZF_TMUX_HEIGHT:-40%}} -- " || echo "fzf"
|
||||||
|
}
|
||||||
|
|
||||||
|
fzf-file-widget() {
|
||||||
|
LBUFFER="${LBUFFER}$(__fsel)"
|
||||||
|
local ret=$?
|
||||||
|
zle reset-prompt
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
zle -N fzf-file-widget
|
||||||
|
bindkey -M emacs '^T' fzf-file-widget
|
||||||
|
bindkey -M vicmd '^T' fzf-file-widget
|
||||||
|
bindkey -M viins '^T' fzf-file-widget
|
||||||
|
|
||||||
|
# ALT-C - cd into the selected directory
|
||||||
|
fzf-cd-widget() {
|
||||||
|
local cmd="${FZF_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
|
||||||
|
-o -type d -print 2> /dev/null | cut -b3-"}"
|
||||||
|
setopt localoptions pipefail no_aliases 2> /dev/null
|
||||||
|
local dir="$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse --bind=ctrl-z:ignore ${FZF_DEFAULT_OPTS-} ${FZF_ALT_C_OPTS-}" $(__fzfcmd) +m)"
|
||||||
|
if [[ -z "$dir" ]]; then
|
||||||
|
zle redisplay
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
zle push-line # Clear buffer. Auto-restored on next prompt.
|
||||||
|
BUFFER="builtin cd -- ${(q)dir}"
|
||||||
|
zle accept-line
|
||||||
|
local ret=$?
|
||||||
|
unset dir # ensure this doesn't end up appearing in prompt expansion
|
||||||
|
zle reset-prompt
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
zle -N fzf-cd-widget
|
||||||
|
bindkey -M emacs '\ec' fzf-cd-widget
|
||||||
|
bindkey -M vicmd '\ec' fzf-cd-widget
|
||||||
|
bindkey -M viins '\ec' fzf-cd-widget
|
||||||
|
|
||||||
|
# CTRL-R - Paste the selected command from history into the command line
|
||||||
|
# EDIT BY plex: removed --scheme-history , as it causes errors on some debian based systems as it seems.
|
||||||
|
fzf-history-widget() {
|
||||||
|
local selected num
|
||||||
|
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
|
||||||
|
selected=( $(fc -rl 1 | awk '{ cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) print $0 }' |
|
||||||
|
FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} ${FZF_DEFAULT_OPTS-} -n2..,.. --bind=ctrl-r:toggle-sort,ctrl-z:ignore ${FZF_CTRL_R_OPTS-} --query=${(qqq)LBUFFER} +m" $(__fzfcmd)) )
|
||||||
|
local ret=$?
|
||||||
|
if [ -n "$selected" ]; then
|
||||||
|
num=$selected[1]
|
||||||
|
if [ -n "$num" ]; then
|
||||||
|
zle vi-fetch-history -n $num
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
zle reset-prompt
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
zle -N fzf-history-widget
|
||||||
|
bindkey -M emacs '^R' fzf-history-widget
|
||||||
|
bindkey -M vicmd '^R' fzf-history-widget
|
||||||
|
bindkey -M viins '^R' fzf-history-widget
|
||||||
|
|
||||||
|
} always {
|
||||||
|
eval $__fzf_key_bindings_options
|
||||||
|
'unset' '__fzf_key_bindings_options'
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Copy selection to system clipboard as newline-separated entries
|
||||||
|
# Dependencies:
|
||||||
|
# - tr
|
||||||
|
# - xclip/xsel (Linux)
|
||||||
|
# - pbcopy (macOS)
|
||||||
|
# - termux-clipboard-set (Termux)
|
||||||
|
# - clip.exe (WSL)
|
||||||
|
# - clip (Cygwin)
|
||||||
|
# - wl-copy (Wayland)
|
||||||
|
# - clipboard (Haiku)
|
||||||
|
#
|
||||||
|
# Limitation: breaks if a filename has newline in it
|
||||||
|
#
|
||||||
|
# Note: For a space-separated list:
|
||||||
|
# xargs -0 < "$SELECTION"
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
[ -s "$selection" ] || { echo "plugin .cbcp error: empty selection" >&2 ; exit 1; }
|
||||||
|
|
||||||
|
if type xsel >/dev/null 2>&1; then
|
||||||
|
# Linux
|
||||||
|
tr '\0' '\n' < "$selection" | xsel -bi
|
||||||
|
elif type xclip >/dev/null 2>&1; then
|
||||||
|
# Linux
|
||||||
|
tr '\0' '\n' < "$selection" | xclip -sel clip
|
||||||
|
elif type pbcopy >/dev/null 2>&1; then
|
||||||
|
# macOS
|
||||||
|
tr '\0' '\n' < "$selection" | pbcopy
|
||||||
|
elif type termux-clipboard-set >/dev/null 2>&1; then
|
||||||
|
# Termux
|
||||||
|
tr '\0' '\n' < "$selection" | termux-clipboard-set
|
||||||
|
elif type clip.exe >/dev/null 2>&1; then
|
||||||
|
# WSL
|
||||||
|
tr '\0' '\n' < "$selection" | clip.exe
|
||||||
|
elif type clip >/dev/null 2>&1; then
|
||||||
|
# Cygwin
|
||||||
|
tr '\0' '\n' < "$selection" | clip
|
||||||
|
elif type wl-copy >/dev/null 2>&1; then
|
||||||
|
# Wayland
|
||||||
|
tr '\0' '\n' < "$selection" | wl-copy
|
||||||
|
elif type clipboard >/dev/null 2>&1; then
|
||||||
|
# Haiku
|
||||||
|
tr '\0' '\n' < "$selection" | clipboard --stdin
|
||||||
|
fi
|
|
@ -0,0 +1,428 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Print icons in front of list of directories/files
|
||||||
|
|
||||||
|
# Dependencies: awk
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
# 1. Set colors and/or icons to your liking
|
||||||
|
# 2. Pipe any directory listing to iconlookup and it will output prepended icons
|
||||||
|
# 3. preview-tui uses the script to prepend icon to directory listings
|
||||||
|
# 4. Aditionally you can consider adding it to your PATH and/or FZF_DEFAULT_COMMAND to
|
||||||
|
# make it work with various fzf plugins (make sure you also add --ansi to your FZF_DEFAULT_OPTS)
|
||||||
|
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
|
||||||
|
# Author: Luuk van Baal (https://github.com/luukvbaal/iconlookup)
|
||||||
|
|
||||||
|
icon_lookup() {
|
||||||
|
awk 'BEGIN {
|
||||||
|
# Set your ANSI colorscheme below (https://en.wikipedia.org/wiki/ANSI_escape_code#Colors).
|
||||||
|
# Default uses standard nnn icon colors, 8 and 24-bit nord themes are commented out.
|
||||||
|
colordepth=8 #colordepth=8 #colordepth=24
|
||||||
|
color_dirtxt=39 #color_dirtxt=111 #color_dirtxt="129;161;193"
|
||||||
|
color_filetxt=15 #color_filetxt=111 #color_filetxt="129;161;193"
|
||||||
|
color_default=39 #color_default=111 #color_default="129;161;193"
|
||||||
|
color_video=93 #color_video=110 #color_video="136;192;208"
|
||||||
|
color_audio=220 #color_audio=150 #color_audio="163;190;140"
|
||||||
|
color_image=82 #color_image=150 #color_image="163;190;140"
|
||||||
|
color_docs=202 #color_docs=173 #color_docs="208;135;112"
|
||||||
|
color_archive=209 #color_archive=179 #color_archive="235;203;139"
|
||||||
|
color_c=81 #color_c=150 #color_c="163;190;140"
|
||||||
|
color_java=32 #color_java=139 #color_java="180;142;173"
|
||||||
|
color_js=47 #color_js=109 #color_js="143;188;187"
|
||||||
|
color_react=39 #color_react=111 #color_react="129;161;193"
|
||||||
|
color_css=199 #color_css=110 #color_css="136;192;208"
|
||||||
|
color_python=227 #color_python=68 #color_python="94;129;172"
|
||||||
|
color_lua=19 #color_lua=167 #color_lua="191;97;106"
|
||||||
|
color_document=15 #color_document=173 #color_document="208;135;112"
|
||||||
|
color_fsharp=31 #color_fsharp=179 #color_fsharp="180;142;173"
|
||||||
|
color_ruby=160 #color_ruby=150 #color_ruby="163;190;140"
|
||||||
|
color_scala=196 #color_scala=139 #color_scala="143;188;187"
|
||||||
|
color_shell=47 #color_shell=109 #color_shell="143;188;187"
|
||||||
|
color_vim=28 #color_vim=109 #color_vim="143;188;187"
|
||||||
|
|
||||||
|
# icons[][1] contains icon and icons[][2] contains color
|
||||||
|
icons["directory"][1] = ""; icons["directory"][2] = color_default
|
||||||
|
icons["file"][1] = ""; icons["file"][2] = color_default
|
||||||
|
icons["exec"][1] = ""; icons["exec"][2] = color_default
|
||||||
|
icons["manual"][1] = ""; icons["manual"][2] = color_docs
|
||||||
|
icons["pipe"][1] = "ﳣ"; icons["pipe"][2] = color_default
|
||||||
|
icons["socket"][1] = "ﳧ"; icons["socket"][2] = color_default
|
||||||
|
icons["door"][1] = "➡"; icons["door"][2] = color_default
|
||||||
|
|
||||||
|
# top level and common icons
|
||||||
|
icons[".git"][1] = ""; icons[".git"][2] = color_default
|
||||||
|
icons["desktop"][1] = "ﲾ"; icons["desktop"][2] = color_default
|
||||||
|
icons["briefcase"][1] = ""; icons["briefcase"][2] = color_default
|
||||||
|
icons["document"][1] = ""; icons["document"][2] = color_default
|
||||||
|
icons["downloads"][1] = ""; icons["downloads"][2] = color_default
|
||||||
|
icons["music"][1] = ""; icons["music"][2] = color_default
|
||||||
|
icons["musicfile"][1] = ""; icons["musicfile"][2] = color_audio
|
||||||
|
icons["pictures"][1] = ""; icons["pictures"][2] = color_default
|
||||||
|
icons["picturefile"][1] = ""; icons["picturefile"][2] = color_image
|
||||||
|
icons["public"][1] = ""; icons["public"][2] = color_default
|
||||||
|
icons["templates"][1] = "陼"; icons["templates"][2] = color_default
|
||||||
|
icons["videos"][1] = ""; icons["videos"][2] = color_default
|
||||||
|
icons["videofile"][1] = "ﳜ"; icons["videofile"][2] = color_video
|
||||||
|
icons["changelog"][1] = ""; icons["changelog"][2] = color_docs
|
||||||
|
icons["configure"][1] = ""; icons["configure"][2] = color_default
|
||||||
|
icons["license"][1] = ""; icons["license"][2] = color_docs
|
||||||
|
icons["makefile"][1] = ""; icons["makefile"][2] = color_default
|
||||||
|
icons["archive"][1] = ""; icons["archive"][2] = color_archive
|
||||||
|
icons["script"][1] = ""; icons["script"][2] = color_shell
|
||||||
|
icons["cplusplus"][1] = ""; icons["cplusplus"][2] = color_c
|
||||||
|
icons["java"][1] = ""; icons["java"][2] = color_java
|
||||||
|
icons["clojure"][1] = ""; icons["clojure"][2] = color_default
|
||||||
|
icons["js"][1] = ""; icons["js"][2] = color_js
|
||||||
|
icons["linux"][1] = ""; icons["linux"][2] = color_default
|
||||||
|
icons["fsharp"][1] = ""; icons["fsharp"][2] = color_fsharp
|
||||||
|
icons["ruby"][1] = ""; icons["ruby"][2] = color_ruby
|
||||||
|
icons["c"][1] = ""; icons["c"][2] = color_c
|
||||||
|
icons["chess"][1] = ""; icons["chess"][2] = color_default
|
||||||
|
icons["haskell"][1] = ""; icons["haskell"][2] = color_vim
|
||||||
|
icons["html"][1] = ""; icons["html"][2] = color_default
|
||||||
|
icons["react"][1] = ""; icons["react"][2] = color_react
|
||||||
|
icons["python"][1] = ""; icons["python"][2] = color_python
|
||||||
|
icons["database"][1] = ""; icons["database"][2] = color_default
|
||||||
|
icons["worddoc"][1] = ""; icons["worddoc"][2] = color_document
|
||||||
|
icons["playlist"][1] = "蘿"; icons["playlist"][2] = color_audio
|
||||||
|
icons["opticaldisk"][1] = ""; icons["opticaldisk"][2] = color_archive
|
||||||
|
|
||||||
|
# numbers
|
||||||
|
icons["1"][1] = icons["manual"][1]; icons["1"][2] = icons["manual"][2]
|
||||||
|
icons["7z"][1] = icons["archive"][1]; icons["7z"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# a
|
||||||
|
icons["a"][1] = icons["manual"][1]; icons["a"][2] = icons["manual"][2]
|
||||||
|
icons["apk"][1] = icons["archive"][1]; icons["apk"][2] = icons["archive"][2]
|
||||||
|
icons["asm"][1] = icons["file"][1]; icons["asm"][2] = icons["file"][2]
|
||||||
|
icons["aup"][1] = icons["musicfile"][1]; icons["aup"][2] = icons["musicfile"][2]
|
||||||
|
icons["avi"][1] = icons["videofile"][1]; icons["avi"][2] = icons["videofile"][2]
|
||||||
|
|
||||||
|
# b
|
||||||
|
icons["bat"][1] = icons["script"][1]; icons["bat"][2] = icons["script"][2]
|
||||||
|
icons["bin"][1] = ""; icons["bin"][2] = color_default
|
||||||
|
icons["bmp"][1] = icons["picturefile"][1]; icons["bmp"][2] = icons["picturefile"][2]
|
||||||
|
icons["bz2"][1] = icons["archive"][1]; icons["bz2"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# c
|
||||||
|
icons["cplusplus"][1] = icons["cplusplus"][1]; icons["cplusplus"][2] = icons["cplusplus"][2]
|
||||||
|
icons["cabal"][1] = icons["haskell"][1]; icons["cab"][2] = icons["haskell"][2]
|
||||||
|
icons["cab"][1] = icons["archive"][1]; icons["cab"][2] = icons["archive"][2]
|
||||||
|
icons["cbr"][1] = icons["archive"][1]; icons["cbr"][2] = icons["archive"][2]
|
||||||
|
icons["cbz"][1] = icons["archive"][1]; icons["cbz"][2] = icons["archive"][2]
|
||||||
|
icons["cc"][1] = icons["cplusplus"][1]; icons["cc"][2] = icons["cplusplus"][2]
|
||||||
|
icons["class"][1] = icons["java"][1]; icons["class"][2] = icons["java"][2]
|
||||||
|
icons["clj"][1] = icons["clojure"][1]; icons["clj"][2] = icons["clojure"][2]
|
||||||
|
icons["cljc"][1] = icons["clojure"][1]; icons["cljc"][2] = icons["clojure"][2]
|
||||||
|
icons["cljs"][1] = icons["clojure"][1]; icons["cljs"][2] = icons["clojure"][2]
|
||||||
|
icons["cmake"][1] = icons["makefile"][1]; icons["cmake"][2] = icons["makefile"][2]
|
||||||
|
icons["coffee"][1] = ""; icons["coffee"][2] = color_default
|
||||||
|
icons["conf"][1] = icons["configure"][1]; icons["conf"][2] = icons["configure"][2]
|
||||||
|
icons["cpio"][1] = icons["archive"][1]; icons["cpio"][2] = icons["archive"][2]
|
||||||
|
icons["cpp"][1] = icons["cplusplus"][1]; icons["cpp"][2] = icons["cplusplus"][2]
|
||||||
|
icons["css"][1] = ""; icons["css"][2] = color_css
|
||||||
|
icons["cue"][1] = icons["playlist"][1]; icons["cue"][2] = icons["playlist"][2]
|
||||||
|
icons["cvs"][1] = icons["configure"][1]; icons["cvs"][2] = icons["configure"][2]
|
||||||
|
icons["cxx"][1] = icons["cplusplus"][1]; icons["cxx"][2] = icons["cplusplus"][2]
|
||||||
|
|
||||||
|
# d
|
||||||
|
icons["db"][1] = icons["database"][1]; icons["db"][2] = icons["database"][2]
|
||||||
|
icons["deb"][1] = ""; icons["deb"][2] = color_archive
|
||||||
|
icons["diff"][1] = ""; icons["diff"][2] = color_default
|
||||||
|
icons["dll"][1] = icons["script"][1]; icons["dll"][2] = icons["script"][2]
|
||||||
|
icons["doc"][1] = icons["worddoc"][1]; icons["doc"][2] = icons["worddoc"][2]
|
||||||
|
icons["docx"][1] = icons["worddoc"][1]; icons["docx"][2] = icons["worddoc"][2]
|
||||||
|
|
||||||
|
# e
|
||||||
|
icons["ejs"][1] = icons["js"][1]; icons["ejs"][2] = icons["js"][2]
|
||||||
|
icons["elf"][1] = icons["linux"][1]; icons["elf"][2] = icons["linux"][2]
|
||||||
|
icons["epub"][1] = icons["manual"][1]; icons["epub"][2] = icons["manual"][2]
|
||||||
|
icons["exe"][1] = icons["exec"][1]; icons["exe"][2] = icons["exec"][2]
|
||||||
|
|
||||||
|
# f
|
||||||
|
icons["fsharp"][1] = icons["fsharp"][1]; icons["fsharp"][2] = icons["fsharp"][2]
|
||||||
|
icons["flac"][1] = icons["musicfile"][1]; icons["flac"][2] = icons["musicfile"][2]
|
||||||
|
icons["fen"][1] = icons["chess"][1]; icons["fen"][2] = icons["chess"][2]
|
||||||
|
icons["flv"][1] = icons["videofile"][1]; icons["flv"][2] = icons["videofile"][2]
|
||||||
|
icons["fs"][1] = icons["fsharp"][1]; icons["fs"][2] = icons["fsharp"][2]
|
||||||
|
icons["fsi"][1] = icons["fsharp"][1]; icons["fsi"][2] = icons["fsharp"][2]
|
||||||
|
icons["fsscript"][1] = icons["fsharp"][1]; icons["fsscript"][2] = icons["fsharp"][2]
|
||||||
|
icons["fsx"][1] = icons["fsharp"][1]; icons["fsx"][2] = icons["fsharp"][2]
|
||||||
|
|
||||||
|
# g
|
||||||
|
icons["gem"][1] = icons["ruby"][1]; icons["gem"][2] = icons["ruby"][2]
|
||||||
|
icons["gif"][1] = icons["picturefile"][1]; icons["gif"][2] = icons["picturefile"][2]
|
||||||
|
icons["go"][1] = "ﳑ"; icons["go"][2] = color_default
|
||||||
|
icons["gz"][1] = icons["archive"][1]; icons["gz"][2] = icons["archive"][2]
|
||||||
|
icons["gzip"][1] = icons["archive"][1]; icons["gzip"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# h
|
||||||
|
icons["h"][1] = icons["c"][1]; icons["h"][2] = icons["c"][2]
|
||||||
|
icons["hh"][1] = icons["cplusplus"][1]; icons["hh"][2] = icons["cplusplus"][2]
|
||||||
|
icons["hpp"][1] = icons["cplusplus"][1]; icons["hpp"][2] = icons["cplusplus"][2]
|
||||||
|
icons["hs"][1] = icons["haskell"][1]; icons["hs"][2] = icons["haskell"][2]
|
||||||
|
icons["htaccess"][1] = icons["configure"][1]; icons["htaccess"][2] = icons["configure"][2]
|
||||||
|
icons["htpasswd"][1] = icons["configure"][1]; icons["htpasswd"][2] = icons["configure"][2]
|
||||||
|
icons["htm"][1] = icons["html"][1]; icons["htm"][2] = icons["html"][2]
|
||||||
|
icons["hxx"][1] = icons["cplusplus"][1]; icons["hxx"][2] = icons["cplusplus"][2]
|
||||||
|
|
||||||
|
# i
|
||||||
|
icons["ico"][1] = icons["picturefile"][1]; icons["ico"][2] = icons["picturefile"][2]
|
||||||
|
icons["img"][1] = icons["opticaldisk"][1]; icons["img"][2] = icons["opticaldisk"][2]
|
||||||
|
icons["ini"][1] = icons["configure"][1]; icons["ini"][2] = icons["configure"][2]
|
||||||
|
icons["iso"][1] = icons["opticaldisk"][1]; icons["iso"][2] = icons["opticaldisk"][2]
|
||||||
|
|
||||||
|
# j
|
||||||
|
icons["jar"][1] = icons["java"][1]; icons["jar"][2] = icons["java"][2]
|
||||||
|
icons["java"][1] = icons["java"][1]; icons["java"][2] = icons["java"][2]
|
||||||
|
icons["jl"][1] = icons["configure"][1]; icons["jl"][2] = icons["configure"][2]
|
||||||
|
icons["jpeg"][1] = icons["picturefile"][1]; icons["jpeg"][2] = icons["picturefile"][2]
|
||||||
|
icons["jpg"][1] = icons["picturefile"][1]; icons["jpg"][2] = icons["picturefile"][2]
|
||||||
|
icons["json"][1] = "ﬥ"; icons["json"][2] = color_js
|
||||||
|
icons["jsx"][1] = icons["react"][1]; icons["jsx"][2] = icons["react"][2]
|
||||||
|
|
||||||
|
# k
|
||||||
|
|
||||||
|
# l
|
||||||
|
icons["lha"][1] = icons["archive"][1]; icons["lha"][2] = icons["archive"][2]
|
||||||
|
icons["lhs"][1] = icons["haskell"][1]; icons["lhs"][2] = icons["haskell"][2]
|
||||||
|
icons["ilog"][1] = icons["document"][1]; icons["ilog"][2] = icons["document"][2]
|
||||||
|
icons["lua"][1] = ""; icons["lua"][2] = color_lua
|
||||||
|
icons["lzh"][1] = icons["archive"][1]; icons["lzh"][2] = icons["archive"][2]
|
||||||
|
icons["lzma"][1] = icons["archive"][1]; icons["lzma"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# m
|
||||||
|
icons["m"][1] = "ﴜ"; icons["mat"][2] = color_c
|
||||||
|
icons["m4a"][1] = icons["musicfile"][1]; icons["m4a"][2] = icons["musicfile"][2]
|
||||||
|
icons["m4v"][1] = icons["videofile"][1]; icons["m4v"][2] = icons["videofile"][2]
|
||||||
|
icons["mat"][1] = ""; icons["mat"][2] = color_c
|
||||||
|
icons["markdown"][1] = ""; icons["markdown"][2] = color_docs
|
||||||
|
icons["md"][1] = ""; icons["md"][2] = color_docs
|
||||||
|
icons["mk"][1] = icons["makefile"][1]; icons["mk"][2] = icons["makefile"][2]
|
||||||
|
icons["mkv"][1] = icons["videofile"][1]; icons["mkv"][2] = icons["videofile"][2]
|
||||||
|
icons["mov"][1] = icons["videofile"][1]; icons["mov"][2] = icons["videofile"][2]
|
||||||
|
icons["mp3"][1] = icons["musicfile"][1]; icons["mp3"][2] = icons["musicfile"][2]
|
||||||
|
icons["mp4"][1] = icons["videofile"][1]; icons["mp4"][2] = icons["videofile"][2]
|
||||||
|
icons["mpeg"][1] = icons["videofile"][1]; icons["mpeg"][2] = icons["videofile"][2]
|
||||||
|
icons["mpg"][1] = icons["videofile"][1]; icons["mpg"][2] = icons["videofile"][2]
|
||||||
|
icons["msi"][1] = ""; icons["msi"][2] = color_default
|
||||||
|
|
||||||
|
# n
|
||||||
|
icons["nix"][1] = ""; icons["nix"][2] = color_fsharp
|
||||||
|
|
||||||
|
# o
|
||||||
|
icons["o"][1] = icons["manual"][1]; icons["o"][2] = icons["manual"][2]
|
||||||
|
icons["ogg"][1] = icons["musicfile"][1]; icons["ogg"][2] = icons["musicfile"][2]
|
||||||
|
icons["odownload"][1] = icons["download"][1]; icons["odownload"][2] = icons["download"][2]
|
||||||
|
icons["out"][1] = icons["linux"][1]; icons["out"][2] = icons["linux"][2]
|
||||||
|
|
||||||
|
# p
|
||||||
|
icons["part"][1] = icons["download"][1]; icons["part"][2] = icons["download"][2]
|
||||||
|
icons["patch"][1] = icons["diff"][1]; icons["patch"][2] = icons["diff"][2]
|
||||||
|
icons["pdf"][1] = ""; icons["pdf"][2] = color_docs
|
||||||
|
icons["pgn"][1] = icons["chess"][1]; icons["pgn"][2] = icons["chess"][2]
|
||||||
|
icons["php"][1] = ""; icons["php"][2] = color_default
|
||||||
|
icons["png"][1] = icons["picturefile"][1]; icons["png"][2] = icons["picturefile"][2]
|
||||||
|
icons["ppt"][1] = ""; icons["ppt"][2] = color_default
|
||||||
|
icons["pptx"][1] = ""; icons["pptx"][2] = color_default
|
||||||
|
icons["psb"][1] = ""; icons["psb"][2] = color_default
|
||||||
|
icons["psd"][1] = ""; icons["psd"][2] = color_default
|
||||||
|
icons["py"][1] = icons["python"][1]; icons["py"][2] = icons["python"][2]
|
||||||
|
icons["pyc"][1] = icons["python"][1]; icons["pyc"][2] = icons["python"][2]
|
||||||
|
icons["pyd"][1] = icons["python"][1]; icons["pyd"][2] = icons["python"][2]
|
||||||
|
icons["pyo"][1] = icons["python"][1]; icons["pyo"][2] = icons["python"][2]
|
||||||
|
|
||||||
|
# q
|
||||||
|
|
||||||
|
# r
|
||||||
|
icons["rar"][1] = icons["archive"][1]; icons["rar"][2] = icons["archive"][2]
|
||||||
|
icons["rc"][1] = icons["configure"][1]; icons["rc"][2] = icons["configure"][2]
|
||||||
|
icons["rom"][1] = ""; icons["rom"][2] = color_default
|
||||||
|
icons["rpm"][1] = icons["archive"][1]; icons["rpm"][2] = icons["archive"][2]
|
||||||
|
icons["rss"][1] = "參"; icons["rss"][2] = color_default
|
||||||
|
icons["rtf"][1] = ""; icons["rtf"][2] = color_default
|
||||||
|
|
||||||
|
# s
|
||||||
|
icons["sass"][1] = ""; icons["sass"][2] = color_css
|
||||||
|
icons["scss"][1] = ""; icons["scss"][2] = color_css
|
||||||
|
icons["so"][1] = icons["manual"][1]; icons["so"][2] = icons["manual"][2]
|
||||||
|
icons["scala"][1] = ""; icons["scala"][2] = color_scala
|
||||||
|
icons["sh"][1] = icons["script"][1]; icons["sh"][2] = icons["script"][2]
|
||||||
|
icons["slim"][1] = icons["script"][1]; icons["slim"][2] = icons["script"][2]
|
||||||
|
icons["sln"][1] = ""; icons["sln"][2] = color_default
|
||||||
|
icons["sql"][1] = icons["database"][1]; icons["sql"][2] = icons["database"][2]
|
||||||
|
icons["srt"][1] = ""; icons["srt"][2] = color_default
|
||||||
|
icons["isub"][1] = ""; icons["isub"][2] = color_default
|
||||||
|
icons["svg"][1] = icons["picturefile"][1]; icons["svg"][2] = icons["picturefile"][2]
|
||||||
|
|
||||||
|
# t
|
||||||
|
icons["tar"][1] = icons["archive"][1]; icons["tar"][2] = icons["archive"][2]
|
||||||
|
icons["tex"][1] = ""; icons["tex"][2] = color_default
|
||||||
|
icons["tgz"][1] = icons["archive"][1]; icons["tgz"][2] = icons["archive"][2]
|
||||||
|
icons["ts"][1] = ""; icons["ts"][2] = color_js
|
||||||
|
icons["tsx"][1] = icons["react"][1]; icons["tsx"][2] = icons["react"][2]
|
||||||
|
icons["txt"][1] = icons["document"][1]; icons["txt"][2] = icons["document"][2]
|
||||||
|
icons["txz"][1] = icons["archive"][1]; icons["txz"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# u
|
||||||
|
|
||||||
|
# v
|
||||||
|
icons["vid"][1] = icons["videofile"][1]; icons["vid"][2] = icons["videofile"][2]
|
||||||
|
icons["vim"][1] = ""; icons["vim"][2] = color_vim
|
||||||
|
icons["vimrc"][1] = ""; icons["vimrc"][2] = color_vim
|
||||||
|
icons["vtt"][1] = ""; icons["vtt"][2] = color_default
|
||||||
|
# w
|
||||||
|
icons["wav"][1] = icons["musicfile"][1]; icons["wav"][2] = icons["musicfile"][2]
|
||||||
|
icons["webm"][1] = icons["videofile"][1]; icons["webm"][2] = icons["videofile"][2]
|
||||||
|
icons["wma"][1] = icons["videofile"][1]; icons["wma"][2] = icons["videofile"][2]
|
||||||
|
icons["wmv"][1] = icons["videofile"][1]; icons["wmv"][2] = icons["videofile"][2]
|
||||||
|
|
||||||
|
# x
|
||||||
|
icons["xbps"][1] = icons["archive"][1]; icons["xbps"][2] = color_archive
|
||||||
|
icons["xcf"][1] = icons["picturefile"][1]; icons["xcf"][2] = color_image
|
||||||
|
icons["xhtml"][1] = icons["html"][1]; icons["xhtml"][2] = icons["html"][2]
|
||||||
|
icons["xls"][1] = ""; icons["xls"][2] = color_default
|
||||||
|
icons["xlsx"][1] = ""; icons["xlsx"][2] = color_default
|
||||||
|
icons["xml"][1] = icons["html"][1]; icons["xml"][2] = icons["html"][2]
|
||||||
|
icons["xz"][1] = icons["archive"][1]; icons["xz"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
# y
|
||||||
|
icons["yaml"][1] = icons["configure"][1]; icons["yaml"][2] = icons["configure"][2]
|
||||||
|
icons["yml"][1] = icons["configure"][1]; icons["yml"][2] = icons["configure"][2]
|
||||||
|
# z
|
||||||
|
icons["zip"][1] = icons["archive"][1]; icons["zip"][2] = icons["archive"][2]
|
||||||
|
icons["zsh"][1] = icons["script"][1]; icons["zsh"][2] = icons["script"][2]
|
||||||
|
icons["zst"][1] = icons["archive"][1]; icons["zst"][2] = icons["archive"][2]
|
||||||
|
|
||||||
|
FS = "."
|
||||||
|
limit = ENVIRON["limit"]
|
||||||
|
switch (colordepth) {
|
||||||
|
case "4":
|
||||||
|
escape="\033["
|
||||||
|
break;
|
||||||
|
case "8":
|
||||||
|
escape="\033[38;5;"
|
||||||
|
break;
|
||||||
|
case "24":
|
||||||
|
escape="\033[38;2;"
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bstr = ENVIRON["beforestr"]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# dont print cwd . and leading ./ from tree -f
|
||||||
|
if ($0 ~/^\.$/)
|
||||||
|
next
|
||||||
|
ent = ($0 ~/^\.\//) ? substr($0, 3, length($0) - 2) : $0
|
||||||
|
ext = $NF
|
||||||
|
|
||||||
|
# Print icons, set color and bold directories by using ansi escape codes
|
||||||
|
if (ext in icons)
|
||||||
|
printcolor(icons[ext][1], icons[ext][2], color_filetxt, ent, "10")
|
||||||
|
else
|
||||||
|
switch (substr(ent, length(ent), 1)) {
|
||||||
|
case "/":
|
||||||
|
printcolor(icons["directory"][1], color_default, color_dirtxt, ent, "1")
|
||||||
|
break;
|
||||||
|
case "*":
|
||||||
|
printcolor(icons["exe"][1], color_default, color_filetxt, ent, "10")
|
||||||
|
break;
|
||||||
|
case "|":
|
||||||
|
printcolor(icons["pipe"][1], color_default, color_filetxt, ent, "10")
|
||||||
|
break;
|
||||||
|
case "=":
|
||||||
|
printcolor(icons["socket"][1], color_default, color_filetxt, ent, "10")
|
||||||
|
break;
|
||||||
|
case ">":
|
||||||
|
printcolor(icons["door"][1], color_default, color_filetxt, ent, "10")
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printcolor(icons["file"][1], color_default, color_filetxt, ent, "10")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function printcolor(i, c, d, n, b) {
|
||||||
|
if (limit != "" && length(n) + 2 > limit)
|
||||||
|
n = substr(n, 1, limit - 2)
|
||||||
|
printf "\033[0m"
|
||||||
|
printf "%s%s%s;%sm%s %s%sm%s\n", bstr, escape, c, b, i, escape, d, n
|
||||||
|
}'
|
||||||
|
printf '\033[0m'
|
||||||
|
}
|
||||||
|
|
||||||
|
print_begin() {
|
||||||
|
printf '%s\n' "$1" | sed 's/\\n/\n/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
print_end() {
|
||||||
|
printf '%s\n' "$1" | sed 's/\\n/\n/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
print_help() {
|
||||||
|
printf 'Icon Lookup\n
|
||||||
|
Usage:
|
||||||
|
iconlookup [options]
|
||||||
|
iconlookup [-bBe] [string]
|
||||||
|
iconlookup -l [number]
|
||||||
|
iconlookup (-h | --help)
|
||||||
|
|
||||||
|
Prepend icons to list of files based on extension or appended indicator by ls/tree "-F" flag ("/" for directory, "*" for executable etc.)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h --help -? Show this screen.
|
||||||
|
-b --before Prepend str before icon.
|
||||||
|
-B --begin Prepend str before output.
|
||||||
|
-e --end Append str after output.
|
||||||
|
-l --limit Limit line length to [number] characters.'
|
||||||
|
}
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
case $1 in
|
||||||
|
-h|-\?|--help)
|
||||||
|
print_help
|
||||||
|
exit ;;
|
||||||
|
-B|--begin)
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
print_begin "$2"
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
-e|--end)
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
end=1
|
||||||
|
endstr="$2"
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
-b|--before)
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
export beforestr="$2"
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
-l|--limit)
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
export limit="$2"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf 'ERROR: "--limit" requires a non-empty option argument.\n'
|
||||||
|
exit
|
||||||
|
fi ;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break ;;
|
||||||
|
-?*)
|
||||||
|
printf 'WARNING: Unknown option ignored: %s\n' "$1" ;;
|
||||||
|
*) break ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -t 0 ]; then
|
||||||
|
[ -n "$beforestr" ] && limit="$((limit - ${#beforestr}))"
|
||||||
|
icon_lookup
|
||||||
|
else
|
||||||
|
printf 'ERROR: no data provided...\nExpecting a directory listing in stdin\n'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$end" ]; then
|
||||||
|
print_end "$endstr"
|
||||||
|
fi
|
|
@ -0,0 +1,180 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description: An almost fully POSIX compliant batch file renamer
|
||||||
|
#
|
||||||
|
# Note: nnn auto-detects and invokes this plugin if available
|
||||||
|
# Whitespace is used as delimiter for read.
|
||||||
|
# The plugin doesn't support filenames with leading or trailing whitespace
|
||||||
|
# To use NNN_LIST your shell must support readlink(1)
|
||||||
|
#
|
||||||
|
# Capabilities:
|
||||||
|
# 1. Basic file rename
|
||||||
|
# 2. Detects order change
|
||||||
|
# 3. Can move files
|
||||||
|
# 4. Can remove files
|
||||||
|
# 5. Switch number pairs to swap filenames
|
||||||
|
#
|
||||||
|
# Shell: bash
|
||||||
|
# Author: KlzXS
|
||||||
|
|
||||||
|
EDITOR="${EDITOR:-vi}"
|
||||||
|
TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
NNN_INCLUDE_HIDDEN="${NNN_INCLUDE_HIDDEN:-0}"
|
||||||
|
VERBOSE="${VERBOSE:-0}"
|
||||||
|
RECURSIVE="${RECURSIVE:-0}"
|
||||||
|
|
||||||
|
case "$NNN_TRASH" in
|
||||||
|
1)
|
||||||
|
RM_UTIL="trash-put" ;;
|
||||||
|
2)
|
||||||
|
RM_UTIL="gio trash" ;;
|
||||||
|
*)
|
||||||
|
RM_UTIL="rm -ri" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
exit_status=0
|
||||||
|
|
||||||
|
dst_file=$(mktemp "$TMPDIR/.nnnXXXXXX")
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "Rename 'c'urrent / 's'election? "
|
||||||
|
read -r resp
|
||||||
|
|
||||||
|
if ! [ "$resp" = "c" ] && ! [ "$resp" = "s" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
arr=$(tr '\0' '\n' < "$selection")
|
||||||
|
else
|
||||||
|
findcmd="find . ! -name ."
|
||||||
|
|
||||||
|
if [ "$RECURSIVE" -eq 0 ]; then
|
||||||
|
findcmd="$findcmd -prune"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$NNN_INCLUDE_HIDDEN" -eq 0 ]; then
|
||||||
|
findcmd="$findcmd ! -name \".*\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NNN_LIST" ]; then
|
||||||
|
findcmd="$findcmd -print"
|
||||||
|
else
|
||||||
|
findcmd="$findcmd -printf "'"'"$NNN_LIST/%P\n"'"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
arr=$(eval "$findcmd" | sort)
|
||||||
|
fi
|
||||||
|
|
||||||
|
lines=$(printf "%s\n" "$arr" | wc -l)
|
||||||
|
width=${#lines}
|
||||||
|
|
||||||
|
printf "%s" "$arr" | awk '{printf("%'"${width}"'d %s\n", NR, $0)}' > "$dst_file"
|
||||||
|
|
||||||
|
items=("~")
|
||||||
|
while IFS='' read -r line; do
|
||||||
|
if [ -n "$NNN_LIST" ]; then
|
||||||
|
line=$(readlink "$line" || printf "%s" "$line")
|
||||||
|
fi
|
||||||
|
|
||||||
|
items+=("$line");
|
||||||
|
done < <(printf "%s\n" "$arr")
|
||||||
|
|
||||||
|
$EDITOR "$dst_file"
|
||||||
|
|
||||||
|
while read -r num name; do
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
if [ -z "$num" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s: unable to parse line, aborting\n" "$0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if $num is an integer
|
||||||
|
if [ ! "$num" -eq "$num" ] 2> /dev/null; then
|
||||||
|
printf "%s: unable to parse line, aborting\n" "$0"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
src=${items[$num]}
|
||||||
|
|
||||||
|
if [ -z "$src" ]; then
|
||||||
|
printf "%s: unknown item number %s\n" "$0" "$num" > /dev/stderr
|
||||||
|
continue
|
||||||
|
elif [ "$name" != "$src" ]; then
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e "$src" ] && [ ! -L "$src" ]; then
|
||||||
|
printf "%s: %s does not exit\n" "$0" "$src" > /dev/stderr
|
||||||
|
|
||||||
|
unset "items[$num]"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# handle swaps
|
||||||
|
if [ -e "$name" ] || [ -L "$name" ]; then
|
||||||
|
tmp="$name~"
|
||||||
|
c=0
|
||||||
|
|
||||||
|
while [ -e "$tmp" ] || [ -L "$tmp" ]; do
|
||||||
|
c=$((c+1))
|
||||||
|
tmp="$tmp~$c"
|
||||||
|
done
|
||||||
|
|
||||||
|
if mv "$name" "$tmp"; then
|
||||||
|
if [ "$VERBOSE" -ne 0 ]; then
|
||||||
|
printf "'%s' -> '%s'\n" "$name" "$tmp"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr
|
||||||
|
exit_status=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for key in "${!items[@]}"; do
|
||||||
|
if [ "${items[$key]}" = "$name" ]; then
|
||||||
|
items[$key]="$tmp"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir=$(dirname "$name")
|
||||||
|
if [ ! -d "$dir" ] && ! mkdir -p "$dir"; then
|
||||||
|
printf "%s: failed to create directory tree %s\n" "$0" "$dir" > /dev/stderr
|
||||||
|
exit_status=1
|
||||||
|
elif ! mv -i "$src" "$name"; then
|
||||||
|
printf "%s: failed to rename %s to %s: %s\n" "$0" "$name" "$tmp" "$!" > /dev/stderr
|
||||||
|
exit_status=1
|
||||||
|
else
|
||||||
|
if [ -d "$name" ]; then
|
||||||
|
for key in "${!items[@]}"; do
|
||||||
|
items[$key]=$(printf "%s" "${items[$key]}" | sed "s|^$src\(\$\|\/\)|$name\1|")
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$VERBOSE" -ne 0 ]; then
|
||||||
|
printf "'%s' => '%s'\n" "$src" "$name"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
true
|
||||||
|
if [ "$VERBOSE" -ne 0 ]; then
|
||||||
|
printf "'%s' -> '%s'\n" "$src" "$name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset "items[$num]"
|
||||||
|
done <"$dst_file"
|
||||||
|
|
||||||
|
unset "items[0]"
|
||||||
|
for item in "${items[@]}"; do
|
||||||
|
$RM_UTIL "$item"
|
||||||
|
done
|
||||||
|
|
||||||
|
rm "$dst_file"
|
||||||
|
exit $exit_status
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Helper script for plugins
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Anna Arad
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
export selection
|
||||||
|
|
||||||
|
## Set CUR_CTX to 1 to open directory in current context
|
||||||
|
CUR_CTX=0
|
||||||
|
export CUR_CTX
|
||||||
|
|
||||||
|
## Ask nnn to switch to directory $1 in context $2.
|
||||||
|
## If $2 is not provided, the function asks explicitly.
|
||||||
|
nnn_cd () {
|
||||||
|
dir="$1"
|
||||||
|
|
||||||
|
if [ -z "$NNN_PIPE" ]; then
|
||||||
|
echo "No pipe file found" 1>&2
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
context=$2
|
||||||
|
elif [ $CUR_CTX -ne 1 ]; then
|
||||||
|
printf "Choose context 1-4 (blank for current): "
|
||||||
|
read -r context
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s" "${context:-0}c$dir" > "$NNN_PIPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_exists () {
|
||||||
|
type "$1" > /dev/null 2>&1
|
||||||
|
echo $?
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Show a notification
|
||||||
|
#
|
||||||
|
# Details: nnn invokes this plugin to show notification when a cp/mv/rm operation is complete.
|
||||||
|
#
|
||||||
|
# Dependencies: notify-send (Ubuntu)/ntfy (https://github.com/dschep/ntfy)/osascript (macOS)/notify (Haiku)
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Anna Arad
|
||||||
|
|
||||||
|
OS="$(uname)"
|
||||||
|
|
||||||
|
if type notify-send >/dev/null 2>&1; then
|
||||||
|
notify-send nnn "Done!"
|
||||||
|
elif [ "$OS" = "Darwin" ]; then
|
||||||
|
osascript -e 'display notification "Done!" with title "nnn"'
|
||||||
|
elif type ntfy >/dev/null 2>&1; then
|
||||||
|
ntfy -t nnn send "Done!"
|
||||||
|
elif [ "$OS" = "Haiku" ]; then
|
||||||
|
notify --title "nnn" "Done!"
|
||||||
|
fi
|
|
@ -0,0 +1,378 @@
|
||||||
|
<h1 align="center">nnn plugins</h1>
|
||||||
|
|
||||||
|
<p align="center"><img src="https://i.imgur.com/SpT0L2W.png" /></p>
|
||||||
|
<p align="center"><i>read ebooks with plugin gutenread (Android)</i></p>
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Plugins extend the capabilities of `nnn`. They are _executable_ scripts (or binaries) `nnn` can communicate with and trigger. This mechanism fits perfectly with the fundamental design to keep the core file manager lean and fast, by delegating repetitive (but not necessarily file manager-specific) tasks to the plugins which can be run with custom hotkeys.
|
||||||
|
|
||||||
|
`nnn` is _**language-agnostic**_ when it comes to plugins. You can write a plugin in any (scripting) language you are comfortable in!
|
||||||
|
|
||||||
|
## List of plugins
|
||||||
|
|
||||||
|
| Plugin (a-z) | Description [Clears selection<sup>1</sup>] | Lang | Dependencies |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [autojump](autojump) | Navigate to dir/path | sh | [jump](https://github.com/gsamokovarov/jump)/autojump/<br>zoxide/z/[z.lua](https://github.com/skywind3000/z.lua) |
|
||||||
|
| [boom](boom) | Play random music from dir | sh | [moc](http://moc.daper.net/) |
|
||||||
|
| [bulknew](bulknew) | Create multiple files/dirs at once | bash | sed, xargs, mktemp |
|
||||||
|
| [cdpath](cdpath) | `cd` to the directory from `CDPATH` | sh | fzf |
|
||||||
|
| [chksum](chksum) | Create and verify checksums [✓] | sh | md5sum,<br>sha256sum |
|
||||||
|
| [cmusq](cmusq) | Queue/play files/dirs in cmus player [✓] | sh | cmus, pgrep |
|
||||||
|
| [diffs](diffs) | Diff for selection (limited to 2 for directories) [✓] | sh | vimdiff, mktemp |
|
||||||
|
| [dragdrop](dragdrop) | Drag/drop files from/into nnn | sh | [dragon](https://github.com/mwh/dragon) |
|
||||||
|
| [dups](dups) | List non-empty duplicate files in current dir | bash | find, md5sum,<br>sort uniq xargs |
|
||||||
|
| [finder](finder) | Run custom find command (**stored in histfile**) and list | sh | - |
|
||||||
|
| [fixname](fixname) | Clean filename to be more shell-friendly [✓] | bash | sed |
|
||||||
|
| [fzcd](fzcd) | Fuzzy search multiple dirs (or `$PWD`) and visit file | sh | fzf, (find) |
|
||||||
|
| [fzhist](fzhist) | Fuzzy-select a cmd from history, edit in `$EDITOR` and run | sh | fzf, mktemp |
|
||||||
|
| [fzopen](fzopen) | Fuzzy find file(s) in subtree to edit/open/pick | sh | fzf, xdg-open/open |
|
||||||
|
| [fzplug](fzplug) | Fuzzy find, preview and run other plugins | sh | fzf |
|
||||||
|
| [getplugs](getplugs) | Update plugins to installed `nnn` version | sh | curl |
|
||||||
|
| [gitroot](gitroot) | Cd to the root of current git repo | sh | git |
|
||||||
|
| [gpge](gpge) | Encrypt/decrypt files using GPG [✓] | sh | gpg |
|
||||||
|
| [gutenread](gutenread) | Browse, download, read from Project Gutenberg | sh | curl, unzip, w3m<br>[epr](https://github.com/wustho/epr) (optional) |
|
||||||
|
| [gsconnect](gsconnect) | GNOME's implementation of kdeconnect [✓] | sh | gsconnect |
|
||||||
|
| [imgresize](imgresize) | Batch resize images in dir to screen resolution | sh | [imgp](https://github.com/jarun/imgp) |
|
||||||
|
| [imgur](imgur) | Upload an image to imgur (from [imgur-screenshot](https://github.com/jomo/imgur-screenshot)) | bash | - |
|
||||||
|
| [imgview](imgview) | View (thumbnail)images, set wallpaper, [rename](https://github.com/jarun/nnn/wiki/Basic-use-cases#browse-rename-images) and [more](https://wiki.archlinux.org/index.php/Sxiv#Assigning_keyboard_shortcuts)| sh | _see in-file docs_ |
|
||||||
|
| [ipinfo](ipinfo) | Fetch external IP address and whois information | sh | curl, whois |
|
||||||
|
| [kdeconnect](kdeconnect) | Send selected files to an Android device [✓] | sh | kdeconnect-cli |
|
||||||
|
| [launch](launch) | GUI application launcher | sh | fzf |
|
||||||
|
| [mimelist](mimelist) | List files by mime in subtree | sh | - |
|
||||||
|
| [moclyrics](moclyrics) | Show lyrics of the track playing in moc | sh | [ddgr](https://github.com/jarun/ddgr), [moc](http://moc.daper.net/) |
|
||||||
|
| [mocq](mocq) | Queue/play selection/dir/file in moc [✓] | sh | [moc](http://moc.daper.net/) |
|
||||||
|
| [mp3conv](mp3conv) | Extract audio from multimedia as mp3 | sh | ffmpeg |
|
||||||
|
| [mtpmount](mtpmount) | Toggle mount of MTP device (eg. Android) | sh | gvfs-mtp |
|
||||||
|
| [nbak](nbak) | Backs up `nnn` config | sh | tar, awk, mktemp |
|
||||||
|
| [nmount](nmount) | Toggle mount status of a device as normal user | sh | pmount, udisks2 |
|
||||||
|
| [nuke](nuke) | Sample file opener (CLI-only by default) | sh | _see in-file docs_ |
|
||||||
|
| [oldbigfile](oldbigfile) | List large files by access time | sh | find, sort |
|
||||||
|
| [openall](openall) | Open selected files together or one by one [✓] | bash | - |
|
||||||
|
| [organize](organize) | Auto-organize files in directories by file type [✓] | sh | file |
|
||||||
|
| [pdfread](pdfread) | Read a PDF or text file aloud | sh | pdftotext, mpv,<br>pico2wave |
|
||||||
|
| [preview-tabbed](preview-tabbed) | Preview files with Tabbed/xembed | bash | _see in-file docs_ |
|
||||||
|
| [preview-tui](preview-tui) | Preview with Tmux/kitty/[QuickLook](https://github.com/QL-Win/QuickLook)/xterm/`$TERMINAL` | sh | _see in-file docs_ |
|
||||||
|
| [pskill](pskill) | Fuzzy list by name and kill process or zombie | sh | fzf, ps, sudo/doas |
|
||||||
|
| [renamer](renamer) | Batch rename selection or files in dir [✓] | sh | [qmv](https://www.nongnu.org/renameutils/)/[vidir](https://joeyh.name/code/moreutils/) |
|
||||||
|
| [ringtone](ringtone) | Create a variable bitrate mp3 ringtone from file | sh | date, ffmpeg |
|
||||||
|
| [rsynccp](rsynccp) | Gives copy-paste verbose progress percentage [✓] | sh | rsync |
|
||||||
|
| [splitjoin](splitjoin) | Split file or join selection [✓] | sh | split, cat |
|
||||||
|
| [suedit](suedit) | Edit file using superuser permissions | sh | sudoedit/sudo/doas |
|
||||||
|
| [togglex](togglex) | Toggle executable mode for selection [✓] | sh | chmod |
|
||||||
|
| [umounttree](umounttree) | Unmount a remote mountpoint from within | sh | fusermount |
|
||||||
|
| [upload](upload) | Upload to Firefox Send or ix.io (text) or file.io (bin) | sh | [ffsend](https://github.com/timvisee/ffsend), curl, jq, tr |
|
||||||
|
| [wallpaper](wall) | Set wallpaper or change colorscheme | sh | nitrogen/pywal |
|
||||||
|
| [x2sel](x2sel) | Copy file list from system clipboard to selection | sh | _see in-file docs_ |
|
||||||
|
| [xdgdefault](xdgdefault) | Set the default app for the hovered file type | sh | xdg-utils, fzf/dmenu |
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
1. A plugin has to explicitly request `nnn` to clear the selection e.g. after operating on the selected files.
|
||||||
|
2. Files starting with a dot in the `plugins` directory are internal files and should not be used as plugins.
|
||||||
|
|
||||||
|
### Table of contents
|
||||||
|
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [Skip directory refresh after running a plugin](#skip-directory-refresh-after-running-a-plugin)
|
||||||
|
- [Running commands as plugin](#running-commands-as-plugin)
|
||||||
|
- [Skip user confirmation after command execution](#skip-user-confirmation-after-command-execution)
|
||||||
|
- [Run a GUI app as plugin](#run-a-gui-app-as-plugin)
|
||||||
|
- [Page non-interactive command output](#page-non-interactive-command-output)
|
||||||
|
- [Some useful key-command examples](#some-useful-key-command-examples)
|
||||||
|
- [Access level of plugins](#access-level-of-plugins)
|
||||||
|
- [Create your own plugins](#create-your-own-plugins)
|
||||||
|
- [Send data to `nnn`](#send-data-to-nnn)
|
||||||
|
- [Get notified on file hover](#get-notified-on-file-hover)
|
||||||
|
- [Examples](#examples)
|
||||||
|
- [Contributing plugins](#contributing-plugins)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The following command installs or updates (after backup) all plugins:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -Ls https://raw.githubusercontent.com/jarun/nnn/master/plugins/getplugs | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Plugins are installed to `${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Set environment variable `NNN_PLUG` to assign keybinds and invoke plugins directly using the plugin shortcut (<kbd>;</kbd>) followed by the assigned key character. E.g., with the below config:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='f:finder;o:fzopen;p:mocq;d:diffs;t:nmount;v:imgview'
|
||||||
|
```
|
||||||
|
|
||||||
|
plugin `finder` can be invoked with the keybind <kbd>;f</kbd>, `fzopen` can be run with <kbd>;o</kbd> and so on... The key vs. plugin pairs are shown in the help and config screen.
|
||||||
|
|
||||||
|
Alternatively, combine with <kbd>Alt</kbd> (i.e. <kbd>Alt+key</kbd>).
|
||||||
|
|
||||||
|
To pick and run an unassigned plugin, press <kbd>Enter</kbd> (to _enter_ the plugin dir) at the plugin prompt.
|
||||||
|
|
||||||
|
To run a plugin at startup, use the option `-P` followed by the plugin key.
|
||||||
|
|
||||||
|
If the plugins list gets too long, try breaking them up into sections:
|
||||||
|
|
||||||
|
```
|
||||||
|
NNN_PLUG_PERSONAL='g:personal/convert2zoom;p:personal/echo'
|
||||||
|
NNN_PLUG_WORK='j:work/prettyjson;d:work/foobar'
|
||||||
|
NNN_PLUG_INLINE='e:!go run $nnn*'
|
||||||
|
NNN_PLUG_DEFAULT='1:ipinfo;p:preview-tui;o:fzz;b:nbak'
|
||||||
|
NNN_PLUG="$NNN_PLUG_PERSONAL;$NNN_PLUG_WORK;$NNN_PLUG_DEFAULT;$NNN_PLUG_INLINE"
|
||||||
|
export NNN_PLUG
|
||||||
|
```
|
||||||
|
|
||||||
|
Note:
|
||||||
|
- `'g:personal/convert2zoom'` will look in the personal sub-folder inside the plugin folder.
|
||||||
|
- `'b:boom;b:bulknew` will result in only the first definition of *b* (`b:boom`) being used.
|
||||||
|
- A keybinding definition of more than 1 character will prevent nnn from starting.
|
||||||
|
|
||||||
|
|
||||||
|
#### Skip directory refresh after running a plugin [`-`]
|
||||||
|
|
||||||
|
`nnn` refreshes the directory after running a plugin to reflect any changes by the plugin. To disable this add a `-` before the plugin name:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='p:-plugin'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running commands as plugin [`!`]
|
||||||
|
|
||||||
|
To assign keys to arbitrary non-background cli commands and invoke like plugins, add `!` (underscore) before the command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='x:!chmod +x $nnn;g:!git log;s:!smplayer $nnn'
|
||||||
|
```
|
||||||
|
|
||||||
|
Now <kbd>;x</kbd> can be used to make a file executable, <kbd>;g</kbd> can be used to the git log of a git project directory, <kbd>;s</kbd> can be used to preview a partially downloaded media file.
|
||||||
|
|
||||||
|
#### Skip user confirmation after command execution [`*`]
|
||||||
|
|
||||||
|
`nnn` waits for user confirmation (the prompt `Press Enter to continue`) after it executes a command as plugin (unlike plugins which can add a `read` to wait). To skip this, add a `*` after the command.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='s:!smplayer $nnn*;n:-!vim /home/vaio/Dropbox/Public/synced_note*'
|
||||||
|
```
|
||||||
|
|
||||||
|
Now there will be no prompt after <kbd>;s</kbd> and <kbd>;n</kbd>.
|
||||||
|
|
||||||
|
Note: Do not use `*` with programs those run and exit e.g. cat.
|
||||||
|
|
||||||
|
#### Run a GUI app as plugin [`&`]
|
||||||
|
|
||||||
|
To run a GUI app as plugin, add a `&` after `!`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='m:-!&mousepad $nnn'
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `$nnn` must be the last argument in this case.
|
||||||
|
|
||||||
|
#### Page non-interactive command output [`|`]
|
||||||
|
|
||||||
|
To show the output of run-and-exit commands which do not need user input, add `|` (pipe) after `!`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export NNN_PLUG='m:-!|mediainfo $nnn;t:-!|tree -ps;l:-!|ls -lah --group-directories-first'
|
||||||
|
```
|
||||||
|
|
||||||
|
This option is incompatible with `&` (terminal output is masked for GUI programs) and ignores `*` (output is already paged for user).
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
1. Use single quotes for `$NNN_PLUG` so `$nnn` is not interpreted
|
||||||
|
2. `$nnn` must be the last argument (if used) to run a _command as plugin_
|
||||||
|
3. (_Again_) add `!` before the command
|
||||||
|
4. To disable directory refresh after running a _command as plugin_, prefix with `-!`
|
||||||
|
|
||||||
|
#### Some useful key-command examples
|
||||||
|
|
||||||
|
| Key:Command | Description |
|
||||||
|
|---|---|
|
||||||
|
| `c:!convert $nnn png:- \| xclip -sel clipboard -t image/png*` | Copy image to clipboard |
|
||||||
|
| `e:-!sudo -E vim $nnn*` | Edit file as root in vim |
|
||||||
|
| `g:-!git diff` | Show git diff |
|
||||||
|
| `h:-!hx $nnn*` | Open hovered file in [hx](https://github.com/krpors/hx) hex editor |
|
||||||
|
| `k:-!fuser -kiv $nnn*` | Interactively kill process(es) using hovered file |
|
||||||
|
| `l:-!git log` | Show git log |
|
||||||
|
| `n:-!vi /home/user/Dropbox/dir/note*` | Take quick notes in a synced file/dir of notes |
|
||||||
|
| `p:-!less -iR $nnn*` | Page through hovered file in less |
|
||||||
|
| `s:-!&smplayer -minigui $nnn` | Play hovered media file, even unfinished download |
|
||||||
|
| `x:!chmod +x $nnn` | Make the hovered file executable |
|
||||||
|
| `y:-!sync*` | Flush cached writes |
|
||||||
|
|
||||||
|
## Access level of plugins
|
||||||
|
|
||||||
|
When `nnn` executes a plugin, it does the following:
|
||||||
|
- Changes to the directory where the plugin is to be run (`$PWD` pointing to the active directory)
|
||||||
|
- Passes three arguments to the script:
|
||||||
|
1. `$1`: The hovered file's name.
|
||||||
|
2. `$2`: The working directory (might differ from `$PWD` in case of symlinked paths; non-canonical).
|
||||||
|
3. `$3`: The picker mode output file (`-` for stdout) if `nnn` is executed as a file picker.
|
||||||
|
- Sets the environment variable `NNN_PIPE` used to control `nnn` active directory.
|
||||||
|
- Sets the environment variable `NNN_INCLUDE_HIDDEN` to `1` if hidden files are active, `0` otherwise.
|
||||||
|
- Exports the [special variables](https://github.com/jarun/nnn/wiki/Concepts#special-variables).
|
||||||
|
|
||||||
|
Plugins can also read the `.selection` file in the config directory.
|
||||||
|
|
||||||
|
## Create your own plugins
|
||||||
|
|
||||||
|
Plugins can be written in any scripting language. However, POSIX-compliant shell scripts runnable in `sh` are preferred.
|
||||||
|
|
||||||
|
**Make the file executable**. Drop it in the plugin directory. Optionally add a hotkey in `$NNN_PLUG` for frequent usage.
|
||||||
|
|
||||||
|
#### Send data to `nnn`
|
||||||
|
`nnn` provides a mechanism for plugins to send data to `nnn` to control its active directory or invoke the list mode.
|
||||||
|
The way to do so is by writing to the pipe pointed by the environment variable `NNN_PIPE`.
|
||||||
|
The plugin should write a single string in the format `(<->)<ctxcode><opcode><data>` without a newline at the end. For example, `1c/etc`.
|
||||||
|
|
||||||
|
The optional `-` at the **beginning of the stream** instructs `nnn` to clear the selection.
|
||||||
|
In cases where the data transfer to `nnn` has to happen while the selection file is being read (e.g. in a loop), the plugin should
|
||||||
|
create a tmp copy of the selection file, inform `nnn` to clear the selection and then do the subsequent processing with the tmp file.
|
||||||
|
A paged [`|`] or GUI [`&`] cmd run as plugin cannot clear selection.
|
||||||
|
|
||||||
|
The `ctxcode` indicates the context to change the active directory of.
|
||||||
|
|
||||||
|
| Context code | Meaning |
|
||||||
|
|:---:| --- |
|
||||||
|
| `+` | smart context (next inactive else current) |
|
||||||
|
| `0` | current context |
|
||||||
|
| `1`-`4` | context number |
|
||||||
|
|
||||||
|
The `opcode` indicates the operation type.
|
||||||
|
|
||||||
|
| Opcode | Operation |
|
||||||
|
|:---:| --- |
|
||||||
|
| `c` | change directory |
|
||||||
|
| `l` | list files in list mode |
|
||||||
|
| `p` | picker file overwritten |
|
||||||
|
|
||||||
|
For convenience, we provided a helper script named `.nnn-plugin-helper` and a function named `nnn_cd` to ease this process. `nnn_cd` receives the path to change to as the first argument, and the context as an optional second argument.
|
||||||
|
If a context is not provided, it is asked for explicitly. To skip this and choose the current context, set the `CUR_CTX` variable in `.nnn-plugin-helper` (or in the specific plugin after sourcing `.nnn-plugin-helper`) to 1.
|
||||||
|
Usage examples can be found in the Examples section below.
|
||||||
|
|
||||||
|
#### Get notified on file hover
|
||||||
|
|
||||||
|
If `NNN_FIFO` is set, `nnn` will open it and write every hovered files. This can be used in plugins and external scripts, e.g. to implement file previews.
|
||||||
|
|
||||||
|
Don't forget to fork in the background to avoid blocking `nnn`.
|
||||||
|
|
||||||
|
For more details on configuration and usage of the preview plugins, visit [Live Previews](https://github.com/jarun/nnn/wiki/Live-previews).
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
There are many plugins provided by `nnn` which can be used as examples. Here are a few simple selected examples.
|
||||||
|
|
||||||
|
#### Show the git log of changes to the particular file along with the code for a quick and easy review.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
git log -p -- "$1"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Change to directory in clipboard using helper script
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
. $(dirname $0)/.nnn-plugin-helper
|
||||||
|
|
||||||
|
nnn_cd "$(xsel -ob)"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Change directory to the location of a link using helper script with specific context (current)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
. $(dirname $0)/.nnn-plugin-helper
|
||||||
|
|
||||||
|
nnn_cd "$(dirname $(readlink -fn $1))" 0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Change to arbitrary directory without helper script
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
printf "cd to: "
|
||||||
|
read -r dir
|
||||||
|
|
||||||
|
printf "%s" "0c$dir" > "$NNN_PIPE"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Send every hovered file to X selection
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
if [ -z "$NNN_FIFO" ] ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
while read FILE ; do
|
||||||
|
printf "%s" "$FILE" | xsel
|
||||||
|
done < "$NNN_FIFO" &
|
||||||
|
disown
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Quick `find` the first match in subtree and open in `nuke`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
|
||||||
|
|
||||||
|
printf "file name: "
|
||||||
|
read -r pattern
|
||||||
|
|
||||||
|
entry=$(find . -type f -iname "$pattern" -print -quit 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$entry" ]; then
|
||||||
|
"$NUKE" "$entry"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Quick find (using `fd`)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
printf "pattern: "
|
||||||
|
read -r pattern
|
||||||
|
|
||||||
|
if [ -n "$pattern" ]; then
|
||||||
|
printf "%s" "+l" > "$NNN_PIPE"
|
||||||
|
eval "fd -HI $pattern -0" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Quick grep (using `rg`)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
printf "pattern: "
|
||||||
|
read -r pattern
|
||||||
|
|
||||||
|
if [ -n "$pattern" ]; then
|
||||||
|
printf "%s" "+l" > "$NNN_PIPE"
|
||||||
|
eval "rg -l0 --hidden -S $pattern" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing plugins
|
||||||
|
|
||||||
|
1. Add informative sections like _Description_, _Notes_, _Dependencies_, _Shell_, _Author_ etc. in the plugin.
|
||||||
|
2. Add an entry in the table above. Note that the list is alphabetically ordered by plugin name.
|
||||||
|
3. Keep non-portable commands (like `notify-send`) commented so users from any other OS/DE aren't surprised.
|
||||||
|
4. The plugin file should be executable.
|
||||||
|
5. If your plugin stores data, use `${XDG_CACHE_HOME:-$HOME/.cache}/nnn`. Document it _in-file_.
|
|
@ -0,0 +1,74 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Navigate to directory using jump/autojump/zoxide/z
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - jump - https://github.com/gsamokovarov/jump
|
||||||
|
# - OR autojump - https://github.com/wting/autojump
|
||||||
|
# - OR zoxide - https://github.com/ajeetdsouza/zoxide
|
||||||
|
# - OR z - https://github.com/rupa/z (z requires fzf)
|
||||||
|
# - OR z (fish) - https://github.com/jethrokuan/z (z requires fzf)
|
||||||
|
# - OR z.lua - https://github.com/skywind3000/z.lua (z.lua can enhanced with fzf)
|
||||||
|
#
|
||||||
|
# Note: The dependencies STORE NAVIGATION PATTERNS
|
||||||
|
#
|
||||||
|
# to make z.lua work, you need to set $NNN_ZLUA to the path of script z.lua
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Marty Buchaus, Dave Snider, Tim Adler, Nick Waywood
|
||||||
|
|
||||||
|
if [ ! -p "$NNN_PIPE" ]; then
|
||||||
|
printf 'ERROR: NNN_PIPE is not set!'
|
||||||
|
read -r _
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if type jump >/dev/null 2>&1; then
|
||||||
|
printf "jump to : "
|
||||||
|
IFS= read -r line
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
odir="$(jump cd ${line})"
|
||||||
|
printf "%s" "0c$odir" > "$NNN_PIPE"
|
||||||
|
elif type autojump >/dev/null 2>&1; then
|
||||||
|
printf "jump to : "
|
||||||
|
read -r dir
|
||||||
|
odir="$(autojump "$dir")"
|
||||||
|
printf "%s" "0c$odir" > "$NNN_PIPE"
|
||||||
|
elif type zoxide >/dev/null 2>&1; then
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
odir="$(zoxide query -i --)"
|
||||||
|
printf "%s" "0c$odir" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
printf "jump to : "
|
||||||
|
read -r dir
|
||||||
|
odir="$(zoxide query -- "$dir")"
|
||||||
|
printf "%s" "0c$odir" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
elif type lua >/dev/null 2>&1 && [ -n "$NNN_ZLUA" ]; then
|
||||||
|
printf "jump to : "
|
||||||
|
read -r line
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
odir="$(lua "$NNN_ZLUA" -l "$line" | fzf --nth 2.. --reverse --inline-info --tac +s -e --height 35%)"
|
||||||
|
printf "%s" "0c$(echo "$odir" | awk '{print $2}')" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
odir="$(lua "$NNN_ZLUA" -e "$line")"
|
||||||
|
printf "%s" "0c$odir" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# rupa/z uses $_Z_DATA, jethrokuan/z (=port of z for fish) uses $Z_DATA
|
||||||
|
datafile="${_Z_DATA:-${Z_DATA:-$HOME/.z}}"
|
||||||
|
if type fzf >/dev/null 2>&1 && [ -f "$datafile" ]; then
|
||||||
|
# Read the data from z's file instead of calling
|
||||||
|
# z so the data doesn't need to be processed twice
|
||||||
|
sel=$(awk -F "|" '{print $1}' "$datafile" | fzf | awk '{$1=$1};1')
|
||||||
|
|
||||||
|
# NOTE: Uncomment this line and comment out the line above if
|
||||||
|
# you want to see the weightings of the dir's in the fzf pane
|
||||||
|
# sel=$(awk -F "|" '{printf "%s %s\n", $2, $1}' "$datafile" | fzf | sed 's/^[0-9,.]* *//' | awk '{$1=$1};1')
|
||||||
|
|
||||||
|
printf "%s" "0c$sel" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
printf "No supported autojump script [jump/autojump/zoxide/z (needs fzf)] found"
|
||||||
|
read -r _
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Play random music (MP3, FLAC, M4A, WEBM, WMA) from current dir.
|
||||||
|
#
|
||||||
|
# Dependencies: mocp (or custom)
|
||||||
|
#
|
||||||
|
# Note: You may want to set GUIPLAYER.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
GUIPLAYER="${GUIPLAYER:-""}"
|
||||||
|
NUMTRACKS="${NUMTRACKS:-100}"
|
||||||
|
|
||||||
|
if [ -n "$GUIPLAYER" ]; then
|
||||||
|
find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | shuf -n "$NUMTRACKS" | xargs -d "\n" "$GUIPLAYER" > /dev/null 2>&1 &
|
||||||
|
|
||||||
|
# detach the player
|
||||||
|
sleep 1
|
||||||
|
elif type mocp >/dev/null 2>&1; then
|
||||||
|
cmd=$(pgrep -x mocp 2>/dev/null)
|
||||||
|
ret=$cmd
|
||||||
|
|
||||||
|
if [ -z "$ret" ]; then
|
||||||
|
# start MOC server
|
||||||
|
mocp -S
|
||||||
|
mocp -o shuffle
|
||||||
|
else
|
||||||
|
# mocp running, check if it's playing
|
||||||
|
state=$(mocp -i | grep "State:" | cut -d' ' -f2)
|
||||||
|
if [ "$state" = 'PLAY' ]; then
|
||||||
|
# add up to 100 random audio files
|
||||||
|
find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clear MOC playlist
|
||||||
|
mocp -c
|
||||||
|
mocp -o shuffle
|
||||||
|
|
||||||
|
# add up to 100 random audio files
|
||||||
|
find . -type f \( -iname "*.mp3" -o -iname "*.flac" -o -iname "*.m4a" -o -iname "*.webm" -o -iname "*.wma" \) | head -n "$NUMTRACKS" | xargs -d "\n" mocp -a
|
||||||
|
|
||||||
|
# start playing
|
||||||
|
mocp -p
|
||||||
|
else
|
||||||
|
printf "moc missing"
|
||||||
|
read -r _
|
||||||
|
fi
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Allows for creation of multiple files/dirs simultaneously
|
||||||
|
# Creates a tmp file to write each entry in a separate line
|
||||||
|
#
|
||||||
|
# Note: Only relative paths are supported. Absolute paths are ignored
|
||||||
|
# Leading and trailing whitespace in path names is also ignored
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: KlzXS
|
||||||
|
|
||||||
|
EDITOR="${EDITOR:-vi}"
|
||||||
|
TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
|
||||||
|
printf "'f'ile / 'd'ir? "
|
||||||
|
read -r resp
|
||||||
|
|
||||||
|
if [ "$resp" = "f" ]; then
|
||||||
|
#shellcheck disable=SC2016
|
||||||
|
cmd='mkdir -p "$(dirname "{}")" && touch "{}"'
|
||||||
|
elif [ "$resp" = "d" ]; then
|
||||||
|
cmd='mkdir -p {}'
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX")
|
||||||
|
$EDITOR "$tmpfile"
|
||||||
|
|
||||||
|
sed "/^\//d" "$tmpfile" | xargs -n1 -I{} sh -c "$cmd"
|
||||||
|
|
||||||
|
rm "$tmpfile"
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: 'cd' to the directory from CDPATH
|
||||||
|
#
|
||||||
|
# Details: If the CDPATH environmet variable is not set, the default value of
|
||||||
|
# ${XDG_CONFIG_HOME:-$HOME/.config}/nnn/bookmarks will be used.
|
||||||
|
# You can create this directory and fill it with symbolic links to your
|
||||||
|
# favorite directories. It's a good idea to add it to CDPATH so that it
|
||||||
|
# could also be used from the command line outside of nnn.
|
||||||
|
# The fzf search is done on the directory basename (the first column).
|
||||||
|
#
|
||||||
|
# This plugin is an extended version of the bookmarks plugin.
|
||||||
|
# If you set your CDPATH to ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/bookmarks
|
||||||
|
# or to the value of BOOKMARKS_DIR, you can use it as a bookmarks replacement.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Yuri Kloubakov
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
# Get a list of (symbolic links to) directories for every element of CDPATH
|
||||||
|
get_dirs() {
|
||||||
|
IFS=':'
|
||||||
|
for path in $CDPATH; do
|
||||||
|
for entry in "$path"/*; do
|
||||||
|
if [ -d "$entry" ]; then
|
||||||
|
name=$(basename "$entry" | grep -o '^.\{1,24\}')
|
||||||
|
if [ -h "$entry" ]; then
|
||||||
|
slink=$(ls -dl -- "$entry")
|
||||||
|
entry=${slink#*" $entry -> "}
|
||||||
|
fi
|
||||||
|
printf "%-24s :%s\n" "${name}" "$entry"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
echo "$1"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "$CDPATH" ]; then
|
||||||
|
CDPATH="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/bookmarks"
|
||||||
|
[ -d "$CDPATH" ] || abort "CDPATH is not set and there is no \"$CDPATH\" directory"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir_list=$(get_dirs)
|
||||||
|
[ -n "$dir_list" ] || abort "There are no directories to choose from. Check your \"$CDPATH\"."
|
||||||
|
|
||||||
|
dir=$(echo "$dir_list" | fzf --nth=1 --delimiter=':' | awk -F: 'END { print $2 }')
|
||||||
|
if [ -n "$dir" ]; then
|
||||||
|
nnn_cd "$dir" 0
|
||||||
|
fi
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Create and verify checksums
|
||||||
|
#
|
||||||
|
# Note: On macOS, install the relevant checksum packages from Homebrew with:
|
||||||
|
# brew install coreutils
|
||||||
|
#
|
||||||
|
# Details:
|
||||||
|
# - selection: it will generate one file with the checksums and filenames
|
||||||
|
# (and with paths if they are in another directory)
|
||||||
|
# output checksum filename format: checksum_timestamp.checksum_type
|
||||||
|
# - file: if the file is a checksum, the plugin does the verification
|
||||||
|
# if the file is not a checksum, checksum will be generated for it
|
||||||
|
# the output checksum filename will be filename.checksum_type
|
||||||
|
# - directory: recursively calculates checksum for all the files in the dir
|
||||||
|
# the output checksum filename will be directory.checksum_type
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: ath3, Arun Prakash Jana
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
resp=f
|
||||||
|
chsum=md5
|
||||||
|
|
||||||
|
checksum_type()
|
||||||
|
{
|
||||||
|
echo "possible checksums: md5, sha1, sha224, sha256, sha384, sha512"
|
||||||
|
printf "create md5 (m), sha256 (s), sha512 (S) (or type one of the above checksums) [default=m]: "
|
||||||
|
read -r chsum_resp
|
||||||
|
for chks in md5 sha1 sha224 sha256 sha384 sha512
|
||||||
|
do
|
||||||
|
if [ "$chsum_resp" = "$chks" ]; then
|
||||||
|
chsum=$chsum_resp
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$chsum_resp" = "s" ]; then
|
||||||
|
chsum=sha256
|
||||||
|
elif [ "$chsum_resp" = "S" ]; then
|
||||||
|
chsum=sha512
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "work with selection (s) or current file (f) [default=f]: "
|
||||||
|
read -r resp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
checksum_type
|
||||||
|
sed 's|'"$PWD/"'||g' < "$selection" | xargs -0 -I{} ${chsum}sum {} > "checksum_$(date '+%Y%m%d%H%M').$chsum"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
elif [ -n "$1" ]; then
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
for chks in md5 sha1 sha224 sha256 sha384 sha512
|
||||||
|
do
|
||||||
|
if echo "$1" | grep -q \.${chks}$; then
|
||||||
|
${chks}sum -c < "$1"
|
||||||
|
read -r _
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
checksum_type
|
||||||
|
file=$(basename "$1").$chsum
|
||||||
|
${chsum}sum "$1" > "$file"
|
||||||
|
elif [ -d "$1" ]; then
|
||||||
|
checksum_type
|
||||||
|
file=$(basename "$1").$chsum
|
||||||
|
find "$1" -type f -exec ${chsum}sum "{}" + > "$file"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Add selection or hovered file/directory to cmus queue
|
||||||
|
#
|
||||||
|
# Dependencies: cmus, pgrep, xdotool (optional)
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. If adding selection, files/dirs are added in the same order they were selected in nnn
|
||||||
|
# 2. A new window will be opened if cmus is not running already, playback will start immediately
|
||||||
|
# 3. If cmus is already running, files will be appended to the queue with no forced playback
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# 1. Add cava and cmus-lyrics as optional dependencies
|
||||||
|
# 2. Start cava and/or cmus-lyrics in tmux or kitty panes next to cmus
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Kabouik
|
||||||
|
|
||||||
|
# (Optional) Set preferred terminal emulator for cmus if not set in your env,
|
||||||
|
# or leave commented out to use OS default
|
||||||
|
#TERMINAL="kitty"
|
||||||
|
|
||||||
|
if ! type cmus >/dev/null; then
|
||||||
|
printf "cmus missing"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
start_cmus() {
|
||||||
|
type xdotool >/dev/null && nnnwindow="$(xdotool getactivewindow)"
|
||||||
|
case "$TERMINAL" in
|
||||||
|
kitty | gnome-terminal | st)
|
||||||
|
nohup "$TERMINAL" -- cmus & ;;
|
||||||
|
havoc)
|
||||||
|
nohup "$TERMINAL" cmus & ;;
|
||||||
|
"")
|
||||||
|
nohup x-terminal-emulator -e cmus & ;;
|
||||||
|
*)
|
||||||
|
nohup "$TERMINAL" -e cmus & ;;
|
||||||
|
esac
|
||||||
|
# Give the new terminal some time to open
|
||||||
|
until cmus-remote -C; do sleep 0.1; done
|
||||||
|
[ -n "$nnnwindow" ] && xdotool windowactivate "$nnnwindow"
|
||||||
|
} >/dev/null 2>&1
|
||||||
|
|
||||||
|
fill_queue() {
|
||||||
|
if [ "$REPLY" = "s" ]; then
|
||||||
|
xargs < "$selection" -0 cmus-remote -q
|
||||||
|
elif [ -n "$1" ]; then
|
||||||
|
cmus-remote -q "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# If active selection,then ask what to do
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "Queue [s]election or [c]urrently hovered? [default=c]: "
|
||||||
|
read -r REPLY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If cmus is not running, start and play queue
|
||||||
|
if ! pgrep cmus >/dev/null; then
|
||||||
|
printf "cmus is not running, starting it in a new %s window.\n" "$TERMINAL"
|
||||||
|
start_cmus
|
||||||
|
fill_queue "$1"
|
||||||
|
cmus-remote -p
|
||||||
|
printf "Files added to cmus queue.\n"
|
||||||
|
else # Append to existing queue if cmus is already running
|
||||||
|
fill_queue "$1"
|
||||||
|
printf "Files appended to current cmus queue.\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Change view
|
||||||
|
cmus-remote -C "view 4"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Show diff of 2 directories or multiple files in vimdiff
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. vim may show the warning: 'Vim: Warning: Input is not from a terminal'
|
||||||
|
# press 'Enter' to ignore and proceed.
|
||||||
|
# 2. if only one file is in selection, the hovered file is considered as the
|
||||||
|
# second file to diff with
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Arun Prakash Jana, ath3
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
if type nvim >/dev/null 2>&1; then
|
||||||
|
diffcmd="nvim -d"
|
||||||
|
else
|
||||||
|
diffcmd="vimdiff +0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dirdiff() {
|
||||||
|
dir1=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$1")".XXXXXXXX)
|
||||||
|
dir2=$(mktemp "${TMPDIR:-/tmp}"/nnn-"$(basename "$2")".XXXXXXXX)
|
||||||
|
ls -A1 "$1" > "$dir1"
|
||||||
|
ls -A1 "$2" > "$dir2"
|
||||||
|
$diffcmd "$dir1" "$dir2"
|
||||||
|
rm "$dir1" "$dir2"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
arr=$(tr '\0' '\n' < "$selection")
|
||||||
|
if [ "$(echo "$arr" | wc -l)" -gt 1 ]; then
|
||||||
|
f1="$(echo "$arr" | sed -n '1p')"
|
||||||
|
f2="$(echo "$arr" | sed -n '2p')"
|
||||||
|
if [ -d "$f1" ] && [ -d "$f2" ]; then
|
||||||
|
dirdiff "$f1" "$f2"
|
||||||
|
else
|
||||||
|
# If xargs supports the -o option, use it to get rid of:
|
||||||
|
# Vim: Warning: Input is not from a terminal
|
||||||
|
# xargs -0 -o vimdiff < $selection
|
||||||
|
|
||||||
|
eval xargs -0 "$diffcmd" < "$selection"
|
||||||
|
fi
|
||||||
|
elif [ -n "$1" ]; then
|
||||||
|
f1="$(echo "$arr" | sed -n '1p')"
|
||||||
|
if [ -d "$f1" ] && [ -d "$1" ]; then
|
||||||
|
dirdiff "$f1" "$1"
|
||||||
|
elif [ -f "$f1" ] && [ -f "$1" ]; then
|
||||||
|
$diffcmd "$f1" "$1"
|
||||||
|
else
|
||||||
|
echo "cannot compare file with directory"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "needs at least 2 files or directories selected for comparison"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,77 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Open a Drag and drop window, to drop files onto other programs.
|
||||||
|
# Also provides drag and drop window for files.
|
||||||
|
#
|
||||||
|
# Dependencies: dragon - https://github.com/mwh/dragon
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. Files that are dropped will be added to nnn's selection
|
||||||
|
# Some web-based files will be downloaded to current dir
|
||||||
|
# with curl and it may overwrite some existing files
|
||||||
|
# 2. The user has to mm to clear nnn's selection first
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: 0xACE
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
resp=f
|
||||||
|
all=
|
||||||
|
if type dragon-drag-and-drop >/dev/null 2>&1; then
|
||||||
|
dnd="dragon-drag-and-drop"
|
||||||
|
elif type dragon-drop >/dev/null 2>&1; then
|
||||||
|
dnd="dragon-drop"
|
||||||
|
else
|
||||||
|
dnd="dragon"
|
||||||
|
fi
|
||||||
|
|
||||||
|
add_file ()
|
||||||
|
{
|
||||||
|
printf '%s\0' "$@" >> "$selection"
|
||||||
|
}
|
||||||
|
|
||||||
|
use_all ()
|
||||||
|
{
|
||||||
|
printf "mark --all (a) [default=none]: "
|
||||||
|
read -r resp
|
||||||
|
if [ "$resp" = "a" ]; then
|
||||||
|
all="--all"
|
||||||
|
else
|
||||||
|
all=""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "Drop file (r). Drag selection (s), Drag current directory (d) or drag current file (f) [default=f]: "
|
||||||
|
read -r resp
|
||||||
|
else
|
||||||
|
printf "Drop file (r). Drag current directory (d) or drag current file (f) [default=f]: "
|
||||||
|
read -r resp
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
resp=f
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
use_all
|
||||||
|
sed -z 's|'"$PWD/"'||g' < "$selection" | xargs -0 "$dnd" "$all" &
|
||||||
|
elif [ "$resp" = "d" ]; then
|
||||||
|
use_all
|
||||||
|
"$dnd" "$all" "$PWD/"* &
|
||||||
|
elif [ "$resp" = "r" ]; then
|
||||||
|
true > "$selection"
|
||||||
|
"$dnd" --print-path --target | while read -r f
|
||||||
|
do
|
||||||
|
if printf "%s" "$f" | grep '^\(https\?\|ftps\?\|s\?ftp\):\/\/' ; then
|
||||||
|
curl -LJO "$f"
|
||||||
|
add_file "$PWD/$(basename "$f")"
|
||||||
|
elif [ -e "$f" ]; then
|
||||||
|
add_file "$f"
|
||||||
|
fi
|
||||||
|
done &
|
||||||
|
else
|
||||||
|
if [ -n "$1" ] && [ -e "$1" ]; then
|
||||||
|
"$dnd" "$1" &
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: List non-empty duplicates in the current dir (based on size followed by MD5)
|
||||||
|
#
|
||||||
|
# Source: https://www.commandlinefu.com/commands/view/3555/find-duplicate-files-based-on-size-first-then-md5-hash
|
||||||
|
#
|
||||||
|
# Dependencies: find md5sum sort uniq xargs gsed
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. If the file size exceeds $size_digits digits the file will be misplaced
|
||||||
|
# 12 digits fit files up to 931GiB
|
||||||
|
# 2. Bash compatible required for mktemp
|
||||||
|
#
|
||||||
|
# Shell: Bash
|
||||||
|
# Authors: syssyphus, KlzXS
|
||||||
|
|
||||||
|
EDITOR="${EDITOR:-vi}"
|
||||||
|
TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
|
||||||
|
size_digits=12
|
||||||
|
tmpfile=$(mktemp "$TMPDIR/.nnnXXXXXX")
|
||||||
|
|
||||||
|
printf "\
|
||||||
|
## This is an overview of all duplicate files found.
|
||||||
|
## Comment out the files you wish to remove. You will be given an option to cancel.
|
||||||
|
## Lines with double comments (##) are ignored.
|
||||||
|
## You will have the option to remove the files with force or interactively.\n
|
||||||
|
" > "$tmpfile"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
find . -size +0 -type f -printf "%${size_digits}s %p\n" | sort -rn | uniq -w"${size_digits}" -D | sed -e '
|
||||||
|
s/^ \{0,12\}\([0-9]\{0,12\}\) \(.*\)$/printf "%s %s\\n" "$(md5sum "\2")" "d\1"/
|
||||||
|
' | tr '\n' '\0' | xargs -0 -n1 sh -c | sort | { uniq -w32 --all-repeated=separate; echo; } | sed -ne '
|
||||||
|
h
|
||||||
|
s/^\(.\{32\}\).* d\([0-9]*\)$/## md5sum: \1 size: \2 bytes/p
|
||||||
|
g
|
||||||
|
|
||||||
|
:loop
|
||||||
|
N
|
||||||
|
/.*\n$/!b loop
|
||||||
|
p' | sed -e 's/^.\{32\} \(.*\) d[0-9]*$/\1/' >> "$tmpfile"
|
||||||
|
|
||||||
|
"$EDITOR" "$tmpfile"
|
||||||
|
|
||||||
|
printf "Remove commented files? (yes/no) [default=n]: "
|
||||||
|
read -r commented
|
||||||
|
|
||||||
|
if [ "$commented" = "y" ]; then
|
||||||
|
sedcmd="/^##.*/d; /^[^#].*/d; /^$/d; s/^# *\(.*\)$/\1/"
|
||||||
|
else
|
||||||
|
printf "Press any key to exit"
|
||||||
|
read -r _
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Remove with force or interactive? (f/i) [default=i]: "
|
||||||
|
read -r force
|
||||||
|
|
||||||
|
if [ "$force" = "f" ]; then
|
||||||
|
#shellcheck disable=SC2016
|
||||||
|
sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -f "$0" "$@" </dev/tty'
|
||||||
|
else
|
||||||
|
#shellcheck disable=SC2016
|
||||||
|
sed -e "$sedcmd" "$tmpfile" | tr '\n' '\0' | xargs -0 -r sh -c 'rm -i "$0" "$@" </dev/tty'
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "$tmpfile"
|
||||||
|
|
||||||
|
printf "Press any key to exit"
|
||||||
|
read -r _
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description: Run custom search and list results in smart context
|
||||||
|
#
|
||||||
|
# Note: This plugin retains search history
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# Run plugin and enter e.g. "-size +10M" to list files in current
|
||||||
|
# directory larger than 10M. By default entered expressions are
|
||||||
|
# interpreted as arguments to find. Results have to be NUL
|
||||||
|
# terminated which is done by default for find. Alternatively one
|
||||||
|
# can prepend a '$' to run a custom search program such as fd or
|
||||||
|
# ripgrep. Entered expressions will be saved in history file to
|
||||||
|
# be listed as bookmarks and and can be entered by index and edited.
|
||||||
|
#
|
||||||
|
# Shell: Bash
|
||||||
|
# Author: Arun Prakash Jana, Luuk van Baal
|
||||||
|
TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
NNN_FINDHIST="${NNN_FINDHIST:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/finderbms}"
|
||||||
|
NNN_FINDHISTLEN="${NNN_FINDHISTLEN:-10000}"
|
||||||
|
|
||||||
|
printexamples() {
|
||||||
|
printf -- "-maxdepth 1 -name pattern
|
||||||
|
-maxdepth 1 -size +100M
|
||||||
|
\$fd -0 pattern
|
||||||
|
\$fd -0 -d 2 -S +100M
|
||||||
|
\$grep -rlZ pattern
|
||||||
|
\$rg -l0 pattern
|
||||||
|
\$fzf -m | tr '\\\n' '\\\0'\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printexprs() {
|
||||||
|
for ((i = "$1"; i < ${#fexprs[@]}; i++)); do
|
||||||
|
printf '%s\t%s\n' "$((i + 1))" "${fexprs[$i]}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
mapexpr() {
|
||||||
|
if [ "$fexpr" -eq "$fexpr" ] 2>/dev/null; then
|
||||||
|
fexpr=${fexprs[$((fexpr - 1))]}
|
||||||
|
read -r -e -p "Search expression: " -i "$fexpr" fexpr
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
readexpr() {
|
||||||
|
case "$fexpr" in
|
||||||
|
h) clear
|
||||||
|
printf "Examples:\n"
|
||||||
|
mapfile -t fexprs < <(printexamples)
|
||||||
|
printexprs 0
|
||||||
|
read -r -p "Search expression or index: " fexpr
|
||||||
|
mapexpr
|
||||||
|
[ -n "$fexpr" ] && readexpr ;;
|
||||||
|
\$*) cmd="${fexpr:1}" ;;
|
||||||
|
*) mapexpr && readexpr
|
||||||
|
cmd="find $fexpr -print0" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
clear
|
||||||
|
[ -f "$NNN_FINDHIST" ] || printexamples > "$NNN_FINDHIST"
|
||||||
|
|
||||||
|
mapfile -t fexprs < <(sort "$NNN_FINDHIST" | uniq -c | sort -nr | head -n5 |\
|
||||||
|
awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}')
|
||||||
|
printf "Most used search expressions:\n"
|
||||||
|
printexprs 0
|
||||||
|
|
||||||
|
mapfile -t -O"$i" fexprs < <(tac "$NNN_FINDHIST" | awk '!a[$0]++' | head -n5)
|
||||||
|
printf "Most recently used search expressions:\n"
|
||||||
|
printexprs "$i"
|
||||||
|
read -r -p "Search expression or index (h for help): " fexpr
|
||||||
|
|
||||||
|
mapexpr
|
||||||
|
|
||||||
|
if [ -n "$fexpr" ]; then
|
||||||
|
printf "+l" > "$NNN_PIPE"
|
||||||
|
while :; do
|
||||||
|
readexpr
|
||||||
|
eval "$cmd" > "$NNN_PIPE" && break
|
||||||
|
read -r -e -p "Search expression: " -i "$fexpr" fexpr
|
||||||
|
done
|
||||||
|
if [ -n "$fexpr" ]; then
|
||||||
|
tail -n"$NNN_FINDHISTLEN" "$NNN_FINDHIST" > "$TMPDIR/finderbms"
|
||||||
|
printf "%s\n" "$fexpr" >> "$TMPDIR/finderbms"
|
||||||
|
mv "$TMPDIR/finderbms" "$NNN_FINDHIST"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description: Clean filename or dirname (either hovered or selections)
|
||||||
|
# to be more shell-friendly. This script cleans
|
||||||
|
# non A-Za-z0-9._- characters.
|
||||||
|
# and replaces it with underscore (_).
|
||||||
|
#
|
||||||
|
# It supports cleaning single/double quote, newline,
|
||||||
|
# leading, trailing spaces.
|
||||||
|
#
|
||||||
|
# eg.
|
||||||
|
# to be continued (つづく).mp4 -> to_be_continued______.mp4
|
||||||
|
# [work] stuff.txt -> _work__stuff.txt
|
||||||
|
# home's server -> home_s_server
|
||||||
|
# qwe\trty -> __qwe_rty
|
||||||
|
#
|
||||||
|
# And if there are two almost similar filenames
|
||||||
|
# like: 'asd]f' and 'asd f' both will be renamed to 'asd_f',
|
||||||
|
# to avoid overwriting, the last file will be prepended by _.
|
||||||
|
# So they will be: 'asd_f' and '_asd_f'
|
||||||
|
#
|
||||||
|
# Dependencies: sed
|
||||||
|
#
|
||||||
|
# Shell: Bash
|
||||||
|
# Author: Benawi Adha
|
||||||
|
|
||||||
|
prompt=true
|
||||||
|
sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
# printf "%s" "$1" | sed -e 's/[^A-Za-z0-9._-]/_/g'
|
||||||
|
printf "%s" "$1" | sed 's/[^A-Za-z0-9._-]/_/g' | sed ':a;N;$!ba;s/\n/_/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -s "$sel" ]; then
|
||||||
|
targets=()
|
||||||
|
while IFS= read -r -d '' i || [ -n "$i" ]; do
|
||||||
|
targets+=( "$(basename "$i")" )
|
||||||
|
done < "$sel"
|
||||||
|
else
|
||||||
|
targets=("$1")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in "${targets[@]}"; do
|
||||||
|
printf "%s -> %s\n" "$i" "$(cleanup "$i")";
|
||||||
|
done
|
||||||
|
|
||||||
|
if $prompt; then
|
||||||
|
echo
|
||||||
|
printf "Proceed [Yn]? "
|
||||||
|
read -r input
|
||||||
|
case "$input" in
|
||||||
|
y|Y|'')
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Canceled"
|
||||||
|
exit
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in "${targets[@]}"; do
|
||||||
|
if [ "$i" != "$(cleanup "$i")" ]; then
|
||||||
|
tmp=''
|
||||||
|
if [ -e "$(cleanup "$i")" ]; then
|
||||||
|
tmp='_'
|
||||||
|
fi
|
||||||
|
mv "$i" "$tmp$(cleanup "$i")";
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Fuzzy search multiple locations read-in from a path-list file
|
||||||
|
# (or $PWD) and open the selected file's dir in a smart context.
|
||||||
|
# Dependencies: fzf, find (only for multi-location search)
|
||||||
|
#
|
||||||
|
# Details: Paths in list file should be newline-separated absolute paths.
|
||||||
|
# Paths can be file paths; the script will scan the parent dirs.
|
||||||
|
#
|
||||||
|
# The path-list file precedence is:
|
||||||
|
# - "$1" (the hovered file) if it exists, is plain-text and the
|
||||||
|
# first line points to an existing file
|
||||||
|
# - "$LIST" if set below
|
||||||
|
# - "$2" (the current directory) [mimics plugin fzcd behaviour]
|
||||||
|
#
|
||||||
|
# The path-list file can be generated easily:
|
||||||
|
# - pick the (file)paths in picker mode to path-list file
|
||||||
|
# - OR, edit selection in nnn and save as path-list file
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Anna Arad, Arun Prakash Jana, KlzXS
|
||||||
|
|
||||||
|
IFS="$(printf '\n\r')"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
CTX=+
|
||||||
|
LIST="${LIST:-""}"
|
||||||
|
|
||||||
|
if ! type fzf >/dev/null 2>&1; then
|
||||||
|
printf "fzf missing"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$1" ] && [ "$(file -b --mime-type "$1")" = 'text/plain' ] && [ -e "$(head -1 "$1")" ]; then
|
||||||
|
LIST="$1"
|
||||||
|
elif ! [ -s "$LIST" ]; then
|
||||||
|
sel=$(fzf)
|
||||||
|
# Show only the file and parent dir
|
||||||
|
# sel=$(fzf --delimiter / --with-nth=-2,-1 --tiebreak=begin --info=hidden)
|
||||||
|
|
||||||
|
LIST=''
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$LIST" ]; then
|
||||||
|
if type find >/dev/null 2>&1; then
|
||||||
|
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
|
||||||
|
|
||||||
|
while IFS= read -r path; do
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
printf "%s\n" "$path" >> "$tmpfile"
|
||||||
|
elif [ -f "$path" ]; then
|
||||||
|
printf "%s\n" "$(dirname "$path")" >> "$tmpfile"
|
||||||
|
fi
|
||||||
|
done < "$LIST"
|
||||||
|
|
||||||
|
sel=$(xargs -d '\n' < "$tmpfile" -I{} find {} -type f -printf "%H//%P\n" | sed '/.*\/\/\(\..*\|.*\/\..*\)/d; s:/\+:/:g' | fzf --delimiter / --tiebreak=begin --info=hidden)
|
||||||
|
# Alternative for 'fd'
|
||||||
|
# sel=$(xargs -d '\n' < "$tmpfile" fd . | fzf --delimiter / --tiebreak=begin --info=hidden)
|
||||||
|
|
||||||
|
rm "$tmpfile"
|
||||||
|
else
|
||||||
|
printf "find missing"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$sel" ]; then
|
||||||
|
if [ "$sel" = "." ] || { ! [ -d "$sel" ] && ! [ -f "$sel" ]; }; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the selected path returned by fzf command is absolute
|
||||||
|
case $sel in
|
||||||
|
/*) nnn_cd "$sel" "$CTX" ;;
|
||||||
|
*)
|
||||||
|
# Remove "./" prefix if it exists
|
||||||
|
sel="${sel#./}"
|
||||||
|
|
||||||
|
if [ "$PWD" = "/" ]; then
|
||||||
|
nnn_cd "/$sel" "$CTX"
|
||||||
|
else
|
||||||
|
nnn_cd "$PWD/$sel" "$CTX"
|
||||||
|
fi;;
|
||||||
|
esac
|
||||||
|
fi
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Fuzzy find a command from history,
|
||||||
|
# edit in $EDITOR and run as a command
|
||||||
|
#
|
||||||
|
# Note: Supports only bash and fish history
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
fuzzy=fzf
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
shellname="$(basename "$SHELL")"
|
||||||
|
|
||||||
|
if [ "$shellname" = "bash" ]; then
|
||||||
|
hist_file="$HOME/.bash_history"
|
||||||
|
entry="$("$fuzzy" < "$hist_file")"
|
||||||
|
elif [ "$shellname" = "fish" ]; then
|
||||||
|
hist_file="$HOME/.local/share/fish/fish_history"
|
||||||
|
entry="$(grep "\- cmd: " "$hist_file" | cut -c 8- | "$fuzzy")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$entry" ]; then
|
||||||
|
tmpfile=$(mktemp)
|
||||||
|
echo "$entry" >> "$tmpfile"
|
||||||
|
$EDITOR "$tmpfile"
|
||||||
|
|
||||||
|
if [ -s "$tmpfile" ]; then
|
||||||
|
$SHELL -c "$(cat "$tmpfile")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm "$tmpfile"
|
||||||
|
|
||||||
|
printf "Press any key to exit"
|
||||||
|
read -r _
|
||||||
|
fi
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Regular mode:
|
||||||
|
# Fuzzy find a file in directory subtree.
|
||||||
|
# Opens in $VISUAL or $EDITOR if text.
|
||||||
|
# Opens other type of files with xdg-open.
|
||||||
|
# Work only with a single file selected.
|
||||||
|
#
|
||||||
|
# Picker mode:
|
||||||
|
# If picker mode output file is passed, it
|
||||||
|
# will be overwritten with any picked files.
|
||||||
|
# Leaves untouched if no file is picked.
|
||||||
|
# Works with single/multiple files selected.
|
||||||
|
#
|
||||||
|
# Dependencies: fd/find, fzf/skim, xdg-open/open (on macOS)
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
|
||||||
|
USE_NUKE=0
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
cmd="$FZF_DEFAULT_COMMAND"
|
||||||
|
if type fd >/dev/null 2>&1; then
|
||||||
|
[ -z "$cmd" ] && cmd="fd -t f 2>/dev/null"
|
||||||
|
else
|
||||||
|
[ -z "$cmd" ] && cmd="find . -type f 2>/dev/null"
|
||||||
|
fi
|
||||||
|
entry="$(eval "$cmd" | fzf -m)"
|
||||||
|
# To show only the file name
|
||||||
|
# entry=$(find . -type f 2>/dev/null | fzf --delimiter / --with-nth=-1 --tiebreak=begin --info=hidden)
|
||||||
|
elif type sk >/dev/null 2>&1; then
|
||||||
|
entry=$(find . -type f 2>/dev/null | sk)
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for picker mode
|
||||||
|
if [ "$3" ]; then
|
||||||
|
if [ "$entry" ]; then
|
||||||
|
case "$entry" in
|
||||||
|
/*) fullpath="$entry" ;;
|
||||||
|
*) fullpath="$PWD/$entry" ;;
|
||||||
|
esac
|
||||||
|
if [ "-" = "$3" ]; then
|
||||||
|
printf "%s\n" "$fullpath"
|
||||||
|
else
|
||||||
|
printf "%s\n" "$fullpath" > "$3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tell `nnn` to clear its internal selection
|
||||||
|
printf "%s" "0p" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$USE_NUKE" -ne 0 ]; then
|
||||||
|
"$NUKE" "$entry"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Open the file (works for a single file only)
|
||||||
|
cmd_file=""
|
||||||
|
cmd_open=""
|
||||||
|
if uname | grep -q "Darwin"; then
|
||||||
|
cmd_file="file -bIL"
|
||||||
|
cmd_open="open"
|
||||||
|
else
|
||||||
|
cmd_file="file -biL"
|
||||||
|
cmd_open="xdg-open"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$($cmd_file "$entry")" in
|
||||||
|
*text*)
|
||||||
|
"${VISUAL:-$EDITOR}" "$entry" ;;
|
||||||
|
*)
|
||||||
|
$cmd_open "$entry" >/dev/null 2>&1 ;;
|
||||||
|
esac
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Fuzzy find and execute nnn plugins (and optionally,
|
||||||
|
# custom scripts located elsewhere).
|
||||||
|
# Description and details of plugins can be previewed
|
||||||
|
# from the fzf interface. Use `?` to toggle preview
|
||||||
|
# pane on and off, ^Up/^Dn to scroll.
|
||||||
|
#
|
||||||
|
# Dependencies: find, fzf, cat (or bat, if installed)
|
||||||
|
#
|
||||||
|
# Note: For better compatibility with as many nnn plugins as possible,
|
||||||
|
# fzplug will first execute the chosen script on the file hovered
|
||||||
|
# in nnn, and upon failure, try to run it with no target (i.e on
|
||||||
|
# an active selection, if present).
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Kabouik
|
||||||
|
|
||||||
|
# Optional scripts sources
|
||||||
|
|
||||||
|
# Leave blank or fill with the absolute path of a folder containing executable
|
||||||
|
# scripts other than nnn plugins (e.g., "$HOME/.local/share/nautilus/scripts",
|
||||||
|
# since there are numerous Nautilus script git repositories).
|
||||||
|
# Add extra variables if needed, make sure you call them in the find command.
|
||||||
|
|
||||||
|
#CUSTOMDIR1="$HOME/.local/share/nautilus/scripts"
|
||||||
|
CUSTOMDIR1=""
|
||||||
|
CUSTOMDIR2=""
|
||||||
|
|
||||||
|
nnnpluginsdir="$HOME/.config/nnn/plugins"
|
||||||
|
|
||||||
|
# Preview with bat if installed
|
||||||
|
if type bat >/dev/null; then
|
||||||
|
BAT="bat --terminal-width='$(tput cols)' --decorations=always --color=always --style='${BAT_STYLE:-header,numbers}'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
plugin=$(find "$nnnpluginsdir" "$CUSTOMDIR1" "$CUSTOMDIR2" \
|
||||||
|
-maxdepth 3 -perm -111 -type f 2>/dev/null | fzf --ansi --preview \
|
||||||
|
"${BAT:-cat} {}" --preview-window="right:66%:wrap" --delimiter / \
|
||||||
|
--with-nth -1 --bind="?:toggle-preview")
|
||||||
|
|
||||||
|
# Try running the script on the hovered file, and abort
|
||||||
|
# abort if no plugin was selected (ESC or ^C pressed).
|
||||||
|
err=0
|
||||||
|
if ! [ "$plugin" = "" ]; then
|
||||||
|
"$plugin" "$1" || err=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If attempt with hovered file fails, try without any target
|
||||||
|
# (nnn selections should still be passed to the script in that case)
|
||||||
|
if [ "$err" -eq "1" ]; then
|
||||||
|
clear && "$plugin" || err=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Abort and show error if both fail
|
||||||
|
if [ "$err" -eq "2" ]; then
|
||||||
|
sep="\n---\n"
|
||||||
|
printf "$sep""Failed to execute '%s'. See error above or try without fzfplug. Press return to continue. " "$plugin" && read -r _ && clear
|
||||||
|
fi
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Update nnn plugins to installed nnn version
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Arun Prakash Jana, KlzXS
|
||||||
|
|
||||||
|
CONFIG_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/
|
||||||
|
PLUGIN_DIR=${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins
|
||||||
|
|
||||||
|
merge () {
|
||||||
|
if type nvim >/dev/null 2>&1; then
|
||||||
|
nvim -d "$1" "$2"
|
||||||
|
else
|
||||||
|
vimdiff +0 "$1" "$2"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt () {
|
||||||
|
printf "%s\n" "Plugin $1 already exists and is different."
|
||||||
|
printf "Keep (k), merge (m), overwrite (o) [default: k]? "
|
||||||
|
read -r operation
|
||||||
|
|
||||||
|
if [ "$operation" = "m" ]; then
|
||||||
|
op="merge"
|
||||||
|
elif [ "$operation" = "o" ]; then
|
||||||
|
op="cp -vRf"
|
||||||
|
else
|
||||||
|
op="true"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$1" = "master" ] ; then
|
||||||
|
VER="master"
|
||||||
|
ARCHIVE_URL=https://github.com/jarun/nnn/archive/master.tar.gz
|
||||||
|
elif type nnn >/dev/null 2>&1; then
|
||||||
|
VER=$(nnn -V)
|
||||||
|
ARCHIVE_URL=https://github.com/jarun/nnn/releases/download/v"$VER"/nnn-v"$VER".tar.gz
|
||||||
|
else
|
||||||
|
echo "nnn is not installed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# backup any earlier plugins
|
||||||
|
if [ -d "$PLUGIN_DIR" ]; then
|
||||||
|
tar -C "$CONFIG_DIR" -czf "$CONFIG_DIR""plugins-$(date '+%Y%m%d%H%M').tar.gz" plugins/
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$PLUGIN_DIR"
|
||||||
|
cd "$CONFIG_DIR" || exit 1
|
||||||
|
curl -Ls "$ARCHIVE_URL" -o nnn-"$VER".tar.gz
|
||||||
|
tar -zxf nnn-"$VER".tar.gz
|
||||||
|
|
||||||
|
cd nnn-"$VER"/plugins || exit 1
|
||||||
|
|
||||||
|
# shellcheck disable=SC2044
|
||||||
|
# We do not use obnoxious names for plugins
|
||||||
|
for f in $(find . -maxdepth 1 \( ! -iname "." ! -iname "*.md" \)); do
|
||||||
|
if [ -f ../../plugins/"$f" ]; then
|
||||||
|
if [ "$(diff --brief "$f" ../../plugins/"$f")" ]; then
|
||||||
|
prompt "$f"
|
||||||
|
$op "$f" ../../plugins/
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cp -vRf "$f" ../../plugins/
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
cd ../.. || exit 1
|
||||||
|
|
||||||
|
rm -rf nnn-"$VER"/ nnn-"$VER".tar.gz
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: cd to the top level of the current git repository in the current context
|
||||||
|
# Dependencies: git
|
||||||
|
# Shell: sh
|
||||||
|
# Author: https://github.com/PatrickF1
|
||||||
|
|
||||||
|
root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
||||||
|
if [ -n "$root" ]; then
|
||||||
|
printf "%s" "0c$root" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
printf "Not in a git repository"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Decrypts selected files using gpg. The contents of the
|
||||||
|
# decrypted file are stored in a file with extension .dec
|
||||||
|
#
|
||||||
|
# Note: If an appropriate private key cannot be found gpg silently
|
||||||
|
# prints a message in the background and no files are written.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: KlzXS
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
printf "(s)election/(c)urrent? [default=c] "
|
||||||
|
read -r resp
|
||||||
|
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
files=$(tr '\0' '\n' < "$selection")
|
||||||
|
else
|
||||||
|
files=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s" "$files" | xargs -n1 -I{} gpg --decrypt --output "{}.dec" {}
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ "$resp" = "s" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Encrypts selected files using gpg. Can encrypt
|
||||||
|
# asymmetrically (key) or symmetrically (passphrase).
|
||||||
|
# If asymmetric encryption is chosen a key can be
|
||||||
|
# chosen from the list of capable public keys using fzf.
|
||||||
|
#
|
||||||
|
# Note: Symmetric encryption only works for a single (current) file as per gpg limitations
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: KlzXS
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
printf "(s)ymmetric, (a)symmetric? [default=a] "
|
||||||
|
read -r symmetry
|
||||||
|
|
||||||
|
if [ "$symmetry" = "s" ]; then
|
||||||
|
gpg --symmetric "$1"
|
||||||
|
else
|
||||||
|
printf "(s)election/(c)urrent? [default=c] "
|
||||||
|
read -r resp
|
||||||
|
|
||||||
|
if [ "$resp" = "s" ]; then
|
||||||
|
files=$(tr '\0' '\n' < "$selection")
|
||||||
|
else
|
||||||
|
files=$1
|
||||||
|
fi
|
||||||
|
|
||||||
|
keyids=$(gpg --list-public-keys --with-colons | grep -E "pub:(.*:){10}.*[eE].*:" | awk -F ":" '{print $5}')
|
||||||
|
|
||||||
|
#awk needs literal $10
|
||||||
|
#shellcheck disable=SC2016
|
||||||
|
keyuids=$(printf "%s" "$keyids" | xargs -n1 -I{} sh -c 'gpg --list-key --with-colons "{}" | grep "uid" | awk -F ":" '\''{printf "%s %s\n", "{}", $10}'\''')
|
||||||
|
|
||||||
|
recipient=$(printf "%s" "$keyuids" | fzf | awk '{print $1}')
|
||||||
|
|
||||||
|
printf "%s" "$files" | xargs -n1 gpg --encrypt --recipient "$recipient"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ "$resp" = "s" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#set -x
|
||||||
|
# Description: Send the selected (or hovered) files to your Android device using gsconnect daemon.js.
|
||||||
|
# GSConnect must be configured on the Android device and the PC.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Darukutsu
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
gsconnect=$HOME/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/daemon.js
|
||||||
|
ids=$($gsconnect -l)
|
||||||
|
|
||||||
|
for id in $ids; do
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
xargs -0 < "$selection" -I{} "$gsconnect" -d "$id" --share-file="{}"
|
||||||
|
# Clear selection
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
"$gsconnect" -d "$id" --share-file="$2/$1"
|
||||||
|
fi
|
||||||
|
done
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Browse Project Gutenberg catalogue by popularity, then download
|
||||||
|
# and read a book of your choice.
|
||||||
|
#
|
||||||
|
# Details: Set the variable EBOOK_ID to download in html format and read in w3m.
|
||||||
|
# Clear EBOOK_ID to browse available ebooks by popularity and set it to
|
||||||
|
# the ID once you find an interesting one.
|
||||||
|
# To download and read in epub format set READER to an epub reader like
|
||||||
|
# epr: https://github.com/wustho/epr
|
||||||
|
#
|
||||||
|
# More on EBOOK_ID:
|
||||||
|
# Wuthering Heights by Emily Brontë is at https://www.gutenberg.org/ebooks/768
|
||||||
|
# So EBOOK_ID would be 768
|
||||||
|
#
|
||||||
|
# Downloaded ebooks are at ${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
EBOOK_ID="${EBOOK_ID:-""}"
|
||||||
|
DIR="${XDG_CACHE_HOME:-$HOME/.cache}/nnn/gutenbooks/$EBOOK_ID"
|
||||||
|
BROWSE_LINK="https://www.gutenberg.org/ebooks/search/?sort_order=downloads"
|
||||||
|
BROWSER="${BROWSER:-w3m}"
|
||||||
|
READER="${READER:-""}"
|
||||||
|
|
||||||
|
if [ -n "$EBOOK_ID" ]; then
|
||||||
|
if [ ! -e "$DIR" ]; then
|
||||||
|
mkdir -p "$DIR"
|
||||||
|
cd "$DIR" || exit 1
|
||||||
|
|
||||||
|
if [ -z "$READER" ]; then
|
||||||
|
curl -L -O "https://www.gutenberg.org/files/$EBOOK_ID/$EBOOK_ID-h.zip"
|
||||||
|
unzip "$EBOOK_ID"-h.zip
|
||||||
|
else
|
||||||
|
curl -L -o "$EBOOK_ID".epub "https://www.gutenberg.org/ebooks/$EBOOK_ID.epub.noimages"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$DIR" ]; then
|
||||||
|
if [ -z "$READER" ]; then
|
||||||
|
"$BROWSER" "$DIR/$EBOOK_ID-h/$EBOOK_ID-h.htm"
|
||||||
|
else
|
||||||
|
"$READER" "$DIR/$EBOOK_ID.epub"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
"$BROWSER" "$BROWSE_LINK"
|
||||||
|
fi
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Resize images in a directory to screen resolution with imgp
|
||||||
|
#
|
||||||
|
# Dependencipes: imgp - https://github.com/jarun/imgp
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. Set res to avoid the desktop resolution prompt each time
|
||||||
|
# 2. MINSIZE is set to 1MB by default, adjust it if you want
|
||||||
|
# 3. imgp options used:
|
||||||
|
# a - adaptive mode
|
||||||
|
# c - convert PNG to JPG
|
||||||
|
# k - skip images matching specified hres/vres
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
# set resolution (e.g. 1920x1080)
|
||||||
|
res="${RESOLUTION}"
|
||||||
|
|
||||||
|
# set minimum image size (in bytes) to resize (default: 1MB)
|
||||||
|
MINSIZE="${MINSIZE:-1048576}"
|
||||||
|
|
||||||
|
if [ -z "$res" ]; then
|
||||||
|
printf "desktop resolution (hxv): "
|
||||||
|
read -r res
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$res" ] && [ -n "$MINSIZE" ]; then
|
||||||
|
imgp -ackx "$res" -s "$MINSIZE"
|
||||||
|
fi
|
|
@ -0,0 +1,597 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# The MIT License
|
||||||
|
#
|
||||||
|
# Copyright (c) jomo
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge,
|
||||||
|
# to any person obtaining a copy of this software and
|
||||||
|
# associated documentation files (the "Software"), to
|
||||||
|
# deal in the Software without restriction, including
|
||||||
|
# without limitation the rights to use, copy, modify,
|
||||||
|
# merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom
|
||||||
|
# the Software is furnished to do so,
|
||||||
|
# subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice
|
||||||
|
# shall be included in all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||||
|
# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
# https://github.com/jomo/imgur-screenshot
|
||||||
|
# https://help.imgur.com/hc/en-us/articles/209592766-Tools-for-Imgur
|
||||||
|
#
|
||||||
|
# Slightly modified for `nnn` integration
|
||||||
|
#
|
||||||
|
# Shell: Bash
|
||||||
|
# Description: Upload an image file to imgur
|
||||||
|
|
||||||
|
if [ "${1}" = "--debug" ]; then
|
||||||
|
echo "########################################"
|
||||||
|
echo "Enabling debug mode"
|
||||||
|
echo "Please remove credentials before pasting"
|
||||||
|
echo "########################################"
|
||||||
|
echo ""
|
||||||
|
uname -a
|
||||||
|
for arg in ${0} "${@}"; do
|
||||||
|
echo -n "'${arg}' "
|
||||||
|
done
|
||||||
|
echo -e "\n"
|
||||||
|
shift
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_version="v1.7.4"
|
||||||
|
|
||||||
|
function is_mac() {
|
||||||
|
uname | grep -q "Darwin"
|
||||||
|
}
|
||||||
|
|
||||||
|
### IMGUR-SCREENSHOT DEFAULT CONFIG ####
|
||||||
|
|
||||||
|
# You can override the config in ~/.config/imgur-screenshot/settings.conf
|
||||||
|
|
||||||
|
imgur_anon_id="ea6c0ef2987808e"
|
||||||
|
imgur_icon_path="${HOME}/Pictures/imgur.png"
|
||||||
|
|
||||||
|
imgur_acct_key=""
|
||||||
|
imgur_secret=""
|
||||||
|
login="false"
|
||||||
|
album_title=""
|
||||||
|
album_id=""
|
||||||
|
credentials_file="${HOME}/.config/imgur-screenshot/credentials.conf"
|
||||||
|
|
||||||
|
file_name_format="imgur-%Y_%m_%d-%H:%M:%S.png" # when using scrot, must end with .png!
|
||||||
|
file_dir="${HOME}/Pictures"
|
||||||
|
|
||||||
|
upload_connect_timeout="5"
|
||||||
|
upload_timeout="120"
|
||||||
|
upload_retries="1"
|
||||||
|
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
if is_mac; then
|
||||||
|
screenshot_select_command="screencapture -i %img"
|
||||||
|
screenshot_window_command="screencapture -iWa %img"
|
||||||
|
screenshot_full_command="screencapture %img"
|
||||||
|
open_command="open %url"
|
||||||
|
else
|
||||||
|
screenshot_select_command="scrot -s %img"
|
||||||
|
screenshot_window_command="scrot %img"
|
||||||
|
screenshot_full_command="scrot %img"
|
||||||
|
open_command="xdg-open %url"
|
||||||
|
fi
|
||||||
|
open="true"
|
||||||
|
|
||||||
|
mode="select"
|
||||||
|
edit_command="gimp %img"
|
||||||
|
edit="false"
|
||||||
|
exit_on_album_creation_fail="true"
|
||||||
|
|
||||||
|
log_file="${HOME}/.imgur-screenshot.log"
|
||||||
|
|
||||||
|
auto_delete=""
|
||||||
|
copy_url="true"
|
||||||
|
keep_file="true"
|
||||||
|
check_update="true"
|
||||||
|
|
||||||
|
# NOTICE: if you make changes here, also edit the docs at
|
||||||
|
# https://github.com/jomo/imgur-screenshot/wiki/Config
|
||||||
|
|
||||||
|
# You can override the config in ~/.config/imgur-screenshot/settings.conf
|
||||||
|
|
||||||
|
############## END CONFIG ##############
|
||||||
|
|
||||||
|
settings_path="${HOME}/.config/imgur-screenshot/settings.conf"
|
||||||
|
if [ -f "${settings_path}" ]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "${settings_path}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dependency check
|
||||||
|
if [ "${1}" = "--check" ]; then
|
||||||
|
(type grep &>/dev/null && echo "OK: found grep") || echo "ERROR: grep not found"
|
||||||
|
if is_mac; then
|
||||||
|
if type growlnotify &>/dev/null; then
|
||||||
|
echo "OK: found growlnotify"
|
||||||
|
elif type terminal-notifier &>/dev/null; then
|
||||||
|
echo "OK: found terminal-notifier"
|
||||||
|
else
|
||||||
|
echo "ERROR: growlnotify nor terminal-notifier found"
|
||||||
|
fi
|
||||||
|
(type screencapture &>/dev/null && echo "OK: found screencapture") || echo "ERROR: screencapture not found"
|
||||||
|
(type pbcopy &>/dev/null && echo "OK: found pbcopy") || echo "ERROR: pbcopy not found"
|
||||||
|
else
|
||||||
|
(type notify-send &>/dev/null && echo "OK: found notify-send") || echo "ERROR: notify-send (from libnotify-bin) not found"
|
||||||
|
(type scrot &>/dev/null && echo "OK: found scrot") || echo "ERROR: scrot not found"
|
||||||
|
(type xclip &>/dev/null && echo "OK: found xclip") || echo "ERROR: xclip not found"
|
||||||
|
fi
|
||||||
|
(type curl &>/dev/null && echo "OK: found curl") || echo "ERROR: curl not found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# notify <'ok'|'error'> <title> <text>
|
||||||
|
function notify() {
|
||||||
|
if is_mac; then
|
||||||
|
if type growlnotify &>/dev/null; then
|
||||||
|
growlnotify --icon "${imgur_icon_path}" --iconpath "${imgur_icon_path}" --title "${2}" --message "${3}"
|
||||||
|
else
|
||||||
|
terminal-notifier -appIcon "${imgur_icon_path}" -contentImage "${imgur_icon_path}" -title "imgur: ${2}" -message "${3}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ "${1}" = "error" ]; then
|
||||||
|
notify-send -a ImgurScreenshot -u critical -c "im.error" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
|
||||||
|
else
|
||||||
|
notify-send -a ImgurScreenshot -u low -c "transfer.complete" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function take_screenshot() {
|
||||||
|
echo "Please select area"
|
||||||
|
is_mac || sleep 0.1 # https://bbs.archlinux.org/viewtopic.php?pid=1246173#p1246173
|
||||||
|
|
||||||
|
cmd="screenshot_${mode}_command"
|
||||||
|
cmd=${!cmd//\%img/${1}}
|
||||||
|
|
||||||
|
if ! shot_err="$(${cmd} &>/dev/null)"; then #takes a screenshot with selection
|
||||||
|
echo "Failed to take screenshot '${1}': '${shot_err}'. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
|
||||||
|
notify error "Something went wrong :(" "Information has been logged"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_for_update() {
|
||||||
|
# exit non-zero on HTTP error, output only the body (no stats) but output errors, follow redirects, output everything to stdout
|
||||||
|
remote_version="$(curl --compressed -fsSL --stderr - "https://api.github.com/repos/jomo/imgur-screenshot/releases" | grep -Em 1 --color 'tag_name":\s*".*"' | cut -d '"' -f 4)"
|
||||||
|
if [ -n "$remote_version" ]; then
|
||||||
|
if [ ! "${current_version}" = "${remote_version}" ] && [ -n "${current_version}" ] && [ -n "${remote_version}" ]; then
|
||||||
|
echo "Update found!"
|
||||||
|
echo "Version ${remote_version} is available (You have ${current_version})"
|
||||||
|
notify ok "Update found" "Version ${remote_version} is available (You have ${current_version}). https://github.com/jomo/imgur-screenshot"
|
||||||
|
echo "Check https://github.com/jomo/imgur-screenshot/releases/${remote_version} for more info."
|
||||||
|
elif [ -z "${current_version}" ] || [ -z "${remote_version}" ]; then
|
||||||
|
echo "Invalid empty version string"
|
||||||
|
echo "Current (local) version: '${current_version}'"
|
||||||
|
echo "Latest (remote) version: '${remote_version}'"
|
||||||
|
else
|
||||||
|
echo "Version ${current_version} is up to date."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Failed to check for latest version: ${remote_version}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function check_oauth2_client_secrets() {
|
||||||
|
if [ -z "${imgur_acct_key}" ] || [ -z "${imgur_secret}" ]; then
|
||||||
|
echo "In order to upload to your account, register a new application at:"
|
||||||
|
echo "https://api.imgur.com/oauth2/addclient"
|
||||||
|
echo "Select 'OAuth 2 authorization without a callback URL'"
|
||||||
|
echo "Then, set the imgur_acct_key (Client ID) and imgur_secret in your config."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function load_access_token() {
|
||||||
|
token_expire_time=0
|
||||||
|
# check for saved access_token and its expiration date
|
||||||
|
if [ -f "${credentials_file}" ]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "${credentials_file}"
|
||||||
|
fi
|
||||||
|
current_time="$(date +%s)"
|
||||||
|
preemptive_refresh_time="$((10*60))"
|
||||||
|
expired="$((current_time > (token_expire_time - preemptive_refresh_time)))"
|
||||||
|
if [ -n "${refresh_token}" ]; then
|
||||||
|
# token already set
|
||||||
|
if [ "${expired}" -eq "0" ]; then
|
||||||
|
# token expired
|
||||||
|
refresh_access_token "${credentials_file}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
acquire_access_token "${credentials_file}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function acquire_access_token() {
|
||||||
|
check_oauth2_client_secrets
|
||||||
|
# prompt for a PIN
|
||||||
|
authorize_url="https://api.imgur.com/oauth2/authorize?client_id=${imgur_acct_key}&response_type=pin"
|
||||||
|
echo "Go to"
|
||||||
|
echo "${authorize_url}"
|
||||||
|
echo "and grant access to this application."
|
||||||
|
read -rp "Enter the PIN: " imgur_pin
|
||||||
|
|
||||||
|
if [ -z "${imgur_pin}" ]; then
|
||||||
|
echo "PIN not entered, exiting"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# exchange the PIN for access token and refresh token
|
||||||
|
response="$(curl --compressed -fsSL --stderr - \
|
||||||
|
-F "client_id=${imgur_acct_key}" \
|
||||||
|
-F "client_secret=${imgur_secret}" \
|
||||||
|
-F "grant_type=pin" \
|
||||||
|
-F "pin=${imgur_pin}" \
|
||||||
|
https://api.imgur.com/oauth2/token)"
|
||||||
|
save_access_token "${response}" "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function refresh_access_token() {
|
||||||
|
check_oauth2_client_secrets
|
||||||
|
token_url="https://api.imgur.com/oauth2/token"
|
||||||
|
# exchange the refresh token for access_token and refresh_token
|
||||||
|
if ! response="$(curl --compressed -fsSL --stderr - \
|
||||||
|
-F "client_id=${imgur_acct_key}" \
|
||||||
|
-F "client_secret=${imgur_secret}" \
|
||||||
|
-F "grant_type=refresh_token" \
|
||||||
|
-F "refresh_token=${refresh_token}" \
|
||||||
|
"${token_url}"
|
||||||
|
)"; then
|
||||||
|
# curl failed
|
||||||
|
handle_upload_error "${response}" "${token_url}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
save_access_token "${response}" "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function save_access_token() {
|
||||||
|
if ! grep -q "access_token" <<<"${1}"; then
|
||||||
|
# server did not send access_token
|
||||||
|
echo "Error: Something is wrong with your credentials:"
|
||||||
|
echo "${1}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
access_token="$(grep -Eo 'access_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
|
||||||
|
refresh_token="$(grep -Eo 'refresh_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
|
||||||
|
expires_in="$(grep -Eo 'expires_in":[0-9]*' <<<"${1}" | cut -d ':' -f 2)"
|
||||||
|
token_expire_time="$(( $(date +%s) + expires_in ))"
|
||||||
|
|
||||||
|
# create dir if not exist
|
||||||
|
mkdir -p "$(dirname "${2}")" 2>/dev/null
|
||||||
|
touch "${2}" && chmod 600 "${2}"
|
||||||
|
cat <<EOF > "${2}"
|
||||||
|
access_token="${access_token}"
|
||||||
|
refresh_token="${refresh_token}"
|
||||||
|
token_expire_time="${token_expire_time}"
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch_account_info() {
|
||||||
|
response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/account/me)"
|
||||||
|
if grep -Eq '"success":\s*true' <<<"${response}"; then
|
||||||
|
username="$(grep -Eo '"url":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
echo "Logged in as ${username}."
|
||||||
|
echo "https://${username}.imgur.com"
|
||||||
|
else
|
||||||
|
echo "Failed to fetch info: ${response}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete_image() {
|
||||||
|
response="$(curl --compressed -X DELETE -fsSL --stderr - -H "Authorization: Client-ID ${1}" "https://api.imgur.com/3/image/${2}")"
|
||||||
|
if grep -Eq '"success":\s*true' <<<"${response}"; then
|
||||||
|
echo "Image successfully deleted (delete hash: ${2})." >> "${3}"
|
||||||
|
else
|
||||||
|
echo "The Image could not be deleted: ${response}." >> "${3}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function upload_authenticated_image() {
|
||||||
|
echo "Uploading '${1}'..."
|
||||||
|
title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
|
||||||
|
if [ -n "${album_id}" ]; then
|
||||||
|
response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
|
||||||
|
else
|
||||||
|
response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# JSON parser premium edition (not really)
|
||||||
|
if grep -Eq '"success":\s*true' <<<"${response}"; then
|
||||||
|
img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
|
||||||
|
del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
|
||||||
|
if [ -n "${auto_delete}" ]; then
|
||||||
|
export -f delete_image
|
||||||
|
echo "Deleting image in ${auto_delete} seconds."
|
||||||
|
nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
|
||||||
|
fi
|
||||||
|
|
||||||
|
handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
|
||||||
|
else # upload failed
|
||||||
|
err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
test -z "${err_msg}" && err_msg="${response}"
|
||||||
|
handle_upload_error "${err_msg}" "${1}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function upload_anonymous_image() {
|
||||||
|
echo "Uploading '${1}'..."
|
||||||
|
title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
|
||||||
|
if [ -n "${album_id}" ]; then
|
||||||
|
response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" https://api.imgur.com/3/image)"
|
||||||
|
else
|
||||||
|
response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" https://api.imgur.com/3/image)"
|
||||||
|
fi
|
||||||
|
# JSON parser premium edition (not really)
|
||||||
|
if grep -Eq '"success":\s*true' <<<"${response}"; then
|
||||||
|
img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
|
||||||
|
del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
|
||||||
|
if [ -n "${auto_delete}" ]; then
|
||||||
|
export -f delete_image
|
||||||
|
echo "Deleting image in ${auto_delete} seconds."
|
||||||
|
nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
|
||||||
|
fi
|
||||||
|
|
||||||
|
handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
|
||||||
|
else # upload failed
|
||||||
|
err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
test -z "${err_msg}" && err_msg="${response}"
|
||||||
|
handle_upload_error "${err_msg}" "${1}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_upload_success() {
|
||||||
|
echo ""
|
||||||
|
echo "image link: ${1}"
|
||||||
|
echo "delete link: ${2}"
|
||||||
|
|
||||||
|
if [ "${copy_url}" = "true" ] && [ -z "${album_title}" ]; then
|
||||||
|
if is_mac; then
|
||||||
|
echo -n "${1}" | pbcopy
|
||||||
|
else
|
||||||
|
echo -n "${1}" | xclip -selection clipboard
|
||||||
|
fi
|
||||||
|
echo "URL copied to clipboard"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# print to log file: image link, image location, delete link
|
||||||
|
echo -e "${1}\t${3}\t${2}" >> "${log_file}"
|
||||||
|
|
||||||
|
notify ok "Upload done!" "${1}"
|
||||||
|
|
||||||
|
# if [ ! -z "${open_command}" ] && [ "${open}" = "true" ]; then
|
||||||
|
# open_cmd=${open_command//\%url/${1}}
|
||||||
|
# open_cmd=${open_cmd//\%img/${2}}
|
||||||
|
# echo "Opening '${open_cmd}'"
|
||||||
|
# eval "${open_cmd}"
|
||||||
|
# fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_upload_error() {
|
||||||
|
error="Upload failed: \"${1}\""
|
||||||
|
echo "${error}"
|
||||||
|
echo -e "Error\t${2}\t${error}" >> "${log_file}"
|
||||||
|
notify error "Upload failed :(" "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_album_creation_success() {
|
||||||
|
echo ""
|
||||||
|
echo "Album link: ${1}"
|
||||||
|
echo "Delete hash: ${2}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
notify ok "Album created!" "${1}"
|
||||||
|
|
||||||
|
if [ "${copy_url}" = "true" ]; then
|
||||||
|
if is_mac; then
|
||||||
|
echo -n "${1}" | pbcopy
|
||||||
|
else
|
||||||
|
echo -n "${1}" | xclip -selection clipboard
|
||||||
|
fi
|
||||||
|
echo "URL copied to clipboard"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# print to log file: album link, album title, delete hash
|
||||||
|
echo -e "${1}\t\"${3}\"\t${2}" >> "${log_file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_album_creation_error() {
|
||||||
|
error="Album creation failed: \"${1}\""
|
||||||
|
echo -e "Error\t${2}\t${error}" >> "${log_file}"
|
||||||
|
notify error "Album creation failed :(" "${1}"
|
||||||
|
if [ ${exit_on_album_creation_fail} ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ ${#} != 0 ]; do
|
||||||
|
case "${1}" in
|
||||||
|
-h | --help)
|
||||||
|
echo "usage: ${0} [--debug] [-c | --check | -v | -h | -u]"
|
||||||
|
echo " ${0} [--debug] [option]... [file]..."
|
||||||
|
echo ""
|
||||||
|
echo " --debug Enable debugging, must be first option"
|
||||||
|
echo " -h, --help Show this help, exit"
|
||||||
|
echo " -v, --version Show current version, exit"
|
||||||
|
echo " --check Check if all dependencies are installed, exit"
|
||||||
|
echo " -c, --connect Show connected imgur account, exit"
|
||||||
|
echo " -o, --open <true|false> Override 'open' config"
|
||||||
|
echo " -e, --edit <true|false> Override 'edit' config"
|
||||||
|
echo " -i, --edit-command <command> Override 'edit_command' config (include '%img'), sets --edit 'true'"
|
||||||
|
echo " -l, --login <true|false> Override 'login' config"
|
||||||
|
echo " -a, --album <album_title> Create new album and upload there"
|
||||||
|
echo " -A, --album-id <album_id> Override 'album_id' config"
|
||||||
|
echo " -k, --keep-file <true|false> Override 'keep_file' config"
|
||||||
|
echo " -d, --auto-delete <s> Automatically delete image after <s> seconds"
|
||||||
|
echo " -u, --update Check for updates, exit"
|
||||||
|
echo " file Upload file instead of taking a screenshot"
|
||||||
|
exit 0;;
|
||||||
|
-v | --version)
|
||||||
|
echo "${current_version}"
|
||||||
|
exit 0;;
|
||||||
|
-s | --select)
|
||||||
|
mode="select"
|
||||||
|
shift;;
|
||||||
|
-w | --window)
|
||||||
|
mode="window"
|
||||||
|
shift;;
|
||||||
|
-f | --full)
|
||||||
|
mode="full"
|
||||||
|
shift;;
|
||||||
|
-o | --open)
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
open="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-e | --edit)
|
||||||
|
edit="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-i | --edit-command)
|
||||||
|
edit_command="${2}"
|
||||||
|
edit="true"
|
||||||
|
shift 2;;
|
||||||
|
-l | --login)
|
||||||
|
login="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-c | --connect)
|
||||||
|
load_access_token
|
||||||
|
fetch_account_info
|
||||||
|
exit 0;;
|
||||||
|
-a | --album)
|
||||||
|
album_title="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-A | --album-id)
|
||||||
|
album_id="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-k | --keep-file)
|
||||||
|
keep_file="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-d | --auto-delete)
|
||||||
|
auto_delete="${2}"
|
||||||
|
shift 2;;
|
||||||
|
-u | --update)
|
||||||
|
check_for_update
|
||||||
|
exit 0;;
|
||||||
|
*)
|
||||||
|
upload_files=("${@}")
|
||||||
|
break;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${login}" = "true" ]; then
|
||||||
|
# load before changing directory
|
||||||
|
load_access_token
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ -n "${album_title}" ]; then
|
||||||
|
if [ "${login}" = "true" ]; then
|
||||||
|
response="$(curl -fsSL --stderr - \
|
||||||
|
-F "title=${album_title}" \
|
||||||
|
-H "Authorization: Bearer ${access_token}" \
|
||||||
|
https://api.imgur.com/3/album)"
|
||||||
|
else
|
||||||
|
response="$(curl -fsSL --stderr - \
|
||||||
|
-F "title=${album_title}" \
|
||||||
|
-H "Authorization: Client-ID ${imgur_anon_id}" \
|
||||||
|
https://api.imgur.com/3/album)"
|
||||||
|
fi
|
||||||
|
if grep -Eq '"success":\s*true' <<<"${response}"; then # Album creation successful
|
||||||
|
echo "Album '${album_title}' successfully created"
|
||||||
|
album_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
handle_album_creation_success "https://imgur.com/a/${album_id}" "${del_id}" "${album_title}"
|
||||||
|
|
||||||
|
if [ "${login}" = "false" ]; then
|
||||||
|
album_id="${del_id}"
|
||||||
|
fi
|
||||||
|
else # Album creation failed
|
||||||
|
err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
|
||||||
|
test -z "${err_msg}" && err_msg="${response}"
|
||||||
|
handle_album_creation_error "${err_msg}" "${album_title}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${upload_files[*]}" ]; then
|
||||||
|
upload_files[0]=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
for upload_file in "${upload_files[@]}"; do
|
||||||
|
|
||||||
|
if [ -z "${upload_file}" ]; then
|
||||||
|
cd "${file_dir}" || exit 1
|
||||||
|
|
||||||
|
# new filename with date
|
||||||
|
img_file="$(date +"${file_name_format}")"
|
||||||
|
take_screenshot "${img_file}"
|
||||||
|
else
|
||||||
|
# upload file instead of screenshot
|
||||||
|
img_file="${upload_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get full path
|
||||||
|
#cd "$(dirname "$(realpath "${img_file}")")"
|
||||||
|
#img_file="$(realpath "${img_file}")"
|
||||||
|
|
||||||
|
# check if file exists
|
||||||
|
if ! [ -f "${img_file}" ]; then
|
||||||
|
echo "file '${img_file}' doesn't exist !"
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# open image in editor if configured
|
||||||
|
if [ "${edit}" = "true" ]; then
|
||||||
|
edit_cmd=${edit_command//\%img/${img_file}}
|
||||||
|
echo "Opening editor '${edit_cmd}'"
|
||||||
|
if ! (eval "${edit_cmd}"); then
|
||||||
|
echo "Error for image '${img_file}': command '${edit_cmd}' failed, not uploading. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
|
||||||
|
notify error "Something went wrong :(" "Information has been logged"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${login}" = "true" ]; then
|
||||||
|
upload_authenticated_image "${img_file}"
|
||||||
|
else
|
||||||
|
upload_anonymous_image "${img_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# delete file if configured
|
||||||
|
if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then
|
||||||
|
echo "Deleting temp file ${file_dir}/${img_file}"
|
||||||
|
rm -rf "${img_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
if [ "${check_update}" = "true" ]; then
|
||||||
|
check_for_update
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r _
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Open hovered or current directory in image viewer.
|
||||||
|
# Generates media thumbnails with optional dependencies.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - imv (https://github.com/eXeC64/imv) or,
|
||||||
|
# - sxiv (https://github.com/muennich/sxiv) or,
|
||||||
|
# - nsxiv (https://codeberg.org/nsxiv/nsxiv) or,
|
||||||
|
# - ucollage (https://github.com/ckardaris/ucollage) or,
|
||||||
|
# - lsix (https://github.com/hackerb9/lsix), or
|
||||||
|
# - viu (https://github.com/atanunq/viu), or
|
||||||
|
# - catimg (https://github.com/posva/catimg), or
|
||||||
|
# - optional: ffmpeg for audio thumbnails (album art)
|
||||||
|
# - optional: ffmpegthumbnailer for video thumbnails
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana, Luuk van Baal
|
||||||
|
#
|
||||||
|
# Consider setting NNN_PREVIEWDIR to $XDG_CACHE_HOME/nnn/previews
|
||||||
|
# if you want to keep media thumbnails on disk between reboots.
|
||||||
|
NNN_PREVIEWDIR="${NNN_PREVIEWDIR:-${TMPDIR:-/tmp}/nnn/previews}"
|
||||||
|
|
||||||
|
exit_prompt() {
|
||||||
|
[ -n "$1" ] && printf "%s\n" "$1"
|
||||||
|
printf "%s" "Press any key to exit..."
|
||||||
|
cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg"
|
||||||
|
clear
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
make_thumbs() {
|
||||||
|
mkdir -p "$NNN_PREVIEWDIR$dir" || return
|
||||||
|
if [ "$1" = "viu" ] || [ "$1" = "catimg" ]; then
|
||||||
|
[ -d "$target" ] && exit_prompt "$1 can only display a single image"
|
||||||
|
mime="$(file -bL --mime-type -- "$target")"
|
||||||
|
case "$mime" in
|
||||||
|
audio/*) ffmpeg -i "$target" "$NNN_PREVIEWDIR$target.jpg" -y >/dev/null 2>&1
|
||||||
|
ret="$NNN_PREVIEWDIR/$target.jpg" ;;
|
||||||
|
video/*) ffmpegthumbnailer -i "$target" -o "$NNN_PREVIEWDIR$target.jpg" 2> /dev/null
|
||||||
|
ret="$NNN_PREVIEWDIR/$target.jpg" ;;
|
||||||
|
*) ret="$target" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
for file in "$dir"/*; do
|
||||||
|
if [ ! -f "$NNN_PREVIEWDIR$file.jpg" ]; then
|
||||||
|
case "$(file -bL --mime-type -- "$file")" in
|
||||||
|
audio/*) [ "$1" != "sxiv" ] &&
|
||||||
|
ffmpeg -i "$file" "$NNN_PREVIEWDIR$file.jpg" -y >/dev/null 2>&1 ;;
|
||||||
|
video/*) [ "$1" != "ucollage" ] &&
|
||||||
|
ffmpegthumbnailer -i "$file" -o "$NNN_PREVIEWDIR$file.jpg" 2> /dev/null ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for file in "$NNN_PREVIEWDIR$dir"/*; do
|
||||||
|
filename="$(basename "$file" .jpg)"
|
||||||
|
[ ! -e "$dir/$filename" ] && rm "$file" 2>/dev/null
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
listimages() {
|
||||||
|
find -L "$dir" "$NNN_PREVIEWDIR$dir" -maxdepth 1 -type f -print0 2>/dev/null | sort -z
|
||||||
|
}
|
||||||
|
|
||||||
|
view_files() {
|
||||||
|
[ -f "$target" ] && count="-n $(listimages | grep -a -m 1 -ZznF "$target" | cut -d: -f1)"
|
||||||
|
case "$1" in
|
||||||
|
nsxiv) listimages | xargs -0 nsxiv -a "${count:--t}" -- ;;
|
||||||
|
sxiv) listimages | xargs -0 sxiv -a "${count:--t}" -- ;;
|
||||||
|
imv*) listimages | xargs -0 "$1" "${count:-}" -- ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
target="$(readlink -f "$1")"
|
||||||
|
[ -d "$target" ] && dir="$target" || dir="${target%/*}"
|
||||||
|
if uname | grep -q "Darwin"; then
|
||||||
|
[ -f "$1" ] && open "$1" >/dev/null 2>&1 &
|
||||||
|
elif type lsix >/dev/null 2>&1; then
|
||||||
|
if [ -d "$target" ]; then
|
||||||
|
cd "$target" || exit_prompt
|
||||||
|
fi
|
||||||
|
make_thumbs lsix
|
||||||
|
clear
|
||||||
|
lsix
|
||||||
|
cd "$NNN_PREVIEWDIR$dir" && lsix
|
||||||
|
exit_prompt
|
||||||
|
elif type ucollage >/dev/null 2>&1; then
|
||||||
|
type ffmpeg >/dev/null 2>&1 && make_thumbs ucollage
|
||||||
|
UCOLLAGE_EXPAND_DIRS=1 ucollage "$dir" "$NNN_PREVIEWDIR$dir" || exit_prompt
|
||||||
|
elif type sxiv >/dev/null 2>&1; then
|
||||||
|
type ffmpegthumbnailer >/dev/null 2>&1 && make_thumbs sxiv
|
||||||
|
view_files sxiv >/dev/null 2>&1 &
|
||||||
|
elif type nsxiv >/dev/null 2>&1; then
|
||||||
|
type ffmpegthumbnailer >/dev/null 2>&1 && make_thumbs sxiv
|
||||||
|
view_files nsxiv >/dev/null 2>&1 &
|
||||||
|
elif type imv >/dev/null 2>&1; then
|
||||||
|
make_thumbs imv
|
||||||
|
view_files imv >/dev/null 2>&1 &
|
||||||
|
elif type imvr >/dev/null 2>&1; then
|
||||||
|
make_thumbs imv
|
||||||
|
view_files imvr >/dev/null 2>&1 &
|
||||||
|
elif type viu >/dev/null 2>&1; then
|
||||||
|
clear
|
||||||
|
make_thumbs viu
|
||||||
|
viu -n "$ret"
|
||||||
|
exit_prompt
|
||||||
|
elif type catimg >/dev/null 2>&1; then
|
||||||
|
make_thumbs catimg
|
||||||
|
catimg "$ret"
|
||||||
|
exit_prompt
|
||||||
|
else
|
||||||
|
exit_prompt "Please install sxiv/nsxiv/imv/viu/catimg/lsix."
|
||||||
|
fi
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Shows the external IP address and whois information. Useful over VPNs.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
IP=$(curl -s ifconfig.me)
|
||||||
|
|
||||||
|
whois "$IP"
|
||||||
|
echo your external IP address is "$IP"
|
||||||
|
|
||||||
|
read -r _
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Send the selected files to your Android device using kdeconnect-cli.
|
||||||
|
# kdeconnect must be configured on the Android device and the PC.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: juacq97
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
id=$(kdeconnect-cli -a --id-only | awk '{print $1}')
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
kdeconnect-cli -d "$id" --share "$(cat "$selection")"
|
||||||
|
|
||||||
|
# If you want a system notification, uncomment the next 3 lines.
|
||||||
|
#notify-send -a "Kdeconnect" "Sending $(cat "$selection")"
|
||||||
|
#else
|
||||||
|
#notify-send -a "Kdeconnect" "No file selected"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Independent POSIX-compliant GUI application launcher.
|
||||||
|
# Fuzzy find executables in $PATH and launch an application.
|
||||||
|
# stdin, stdout, stderr are suppressed so CLI tools exit silently.
|
||||||
|
#
|
||||||
|
# To configure launch as an independent app launcher add a keybind
|
||||||
|
# to open launch in a terminal e.g.,
|
||||||
|
#
|
||||||
|
# xfce4-terminal -e "${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/launch
|
||||||
|
#
|
||||||
|
# Dependencies: fzf
|
||||||
|
#
|
||||||
|
# Usage: launch [delay]
|
||||||
|
# delay is in seconds, if omitted launch waits for 1 sec
|
||||||
|
#
|
||||||
|
# Integration with nnn: launch is installed with other plugins, nnn picks it up.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
|
||||||
|
IFS=':'
|
||||||
|
|
||||||
|
get_selection() {
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
{ IFS=':'; ls -H $PATH; } | sort | fzf
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if selection=$( get_selection ); then
|
||||||
|
setsid "$selection" 2>/dev/null 1>/dev/null &
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
sleep "$1"
|
||||||
|
else
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Find and list files by mime type in smart context
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$(dirname "$0")"/.nnn-plugin-helper
|
||||||
|
|
||||||
|
printf "mime (e.g., video/audio/image): "
|
||||||
|
read -r mime
|
||||||
|
|
||||||
|
printf "%s" "+l" > "$NNN_PIPE"
|
||||||
|
find . | file -if- | grep "$mime" | awk -F: '{printf "%s\0", $1}' > "$NNN_PIPE"
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Fetches the lyrics of the track currently playing in MOC
|
||||||
|
#
|
||||||
|
# Dependencies: ddgr (https://github.com/jarun/ddgr)
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
# Check if MOC server is running
|
||||||
|
cmd=$(pgrep -x mocp 2>/dev/null)
|
||||||
|
ret=$cmd
|
||||||
|
if [ -z "$ret" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Grab the output
|
||||||
|
out="$(mocp -i)"
|
||||||
|
|
||||||
|
# Check if anything is playing
|
||||||
|
state=$(echo "$out" | grep "State:" | cut -d' ' -f2)
|
||||||
|
if ! [ "$state" = 'PLAY' ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try by Artist and Song Title first
|
||||||
|
ARTIST="$(echo "$out" | grep 'Artist:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')"
|
||||||
|
TITLE="$(echo "$out" | grep 'SongTitle:' | cut -d':' -f2 | sed 's/^[[:blank:]]*//;s/[[:blank:]]*$//')"
|
||||||
|
|
||||||
|
if [ -n "$ARTIST" ] && [ -n "$TITLE" ]; then
|
||||||
|
ddgr -w azlyrics.com --ducky "$ARTIST" "$TITLE"
|
||||||
|
else
|
||||||
|
# Try by file name
|
||||||
|
FILENAME="$(basename "$(echo "$out" | grep 'File:' | cut -d':' -f2)")"
|
||||||
|
FILENAME="$(echo "${FILENAME%%.*}" | tr -d -)"
|
||||||
|
|
||||||
|
if [ -n "$FILENAME" ]; then
|
||||||
|
ddgr -w azlyrics.com --ducky "$FILENAME"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Appends and optionally plays music in MOC
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - if selection is available, plays it, else plays the current file or directory
|
||||||
|
# - appends tracks and exits is MOC is running, else clears playlist and adds tracks
|
||||||
|
# - to let mocp shuffle tracks, set SHUFFLE=1
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Arun Prakash Jana, ath3
|
||||||
|
|
||||||
|
IFS="$(printf '\n\r')"
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
cmd=$(pgrep -x mocp 2>/dev/null)
|
||||||
|
ret=$cmd
|
||||||
|
|
||||||
|
SHUFFLE="${SHUFFLE:-0}"
|
||||||
|
|
||||||
|
mocp_add ()
|
||||||
|
{
|
||||||
|
if [ "$SHUFFLE" = 1 ]; then
|
||||||
|
if [ "$resp" = "y" ]; then
|
||||||
|
arr=$(tr '\0' '\n' < "$selection")
|
||||||
|
elif [ -n "$1" ]; then
|
||||||
|
arr="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for entry in $arr
|
||||||
|
do
|
||||||
|
if [ -d "$entry" ]; then
|
||||||
|
arr2=$arr2$(find "$entry" -type f \( ! -iname "*.m3u" ! -iname "*.pls" \))
|
||||||
|
elif echo "$entry" | grep -qv '\.m3u$\|\.pls$' ; then
|
||||||
|
arr2=$(printf "%s\n%s" "$entry" "$arr2")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
mocp -o shuffle
|
||||||
|
echo "$arr2" | xargs -d "\n" mocp -a
|
||||||
|
else
|
||||||
|
if [ "$resp" = "y" ]; then
|
||||||
|
xargs < "$selection" -0 mocp -a
|
||||||
|
else
|
||||||
|
mocp -a "$1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ! -s "$selection" ] && [ -z "$1" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$2" = "opener" ]; then
|
||||||
|
:
|
||||||
|
elif [ -s "$selection" ]; then
|
||||||
|
printf "Work with selection? Enter 'y' to confirm: "
|
||||||
|
read -r resp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ret" ]; then
|
||||||
|
# mocp not running
|
||||||
|
mocp -S
|
||||||
|
else
|
||||||
|
# mocp running, check if it's playing
|
||||||
|
state=$(mocp -i | grep "State:" | cut -d' ' -f2)
|
||||||
|
|
||||||
|
if [ "$state" = 'PLAY' ]; then
|
||||||
|
# add to playlist and exit
|
||||||
|
mocp_add "$1"
|
||||||
|
|
||||||
|
# uncomment the line below to show mocp interface after appending
|
||||||
|
# mocp
|
||||||
|
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clear selection and play
|
||||||
|
mocp -c
|
||||||
|
mocp_add "$1" "$resp"
|
||||||
|
mocp -p
|
||||||
|
|
||||||
|
# uncomment the line below to show mocp interface after appending
|
||||||
|
# mocp
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ "$resp" = "y" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Extract audio from multimedia files and convert to mp3
|
||||||
|
#
|
||||||
|
# Dependencies: ffmpeg compiled with libmp3lame audio codec support
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
outdir=_mp3files
|
||||||
|
|
||||||
|
handle_multimedia() {
|
||||||
|
mime="${1}"
|
||||||
|
file="${2}"
|
||||||
|
|
||||||
|
case "${mime}" in
|
||||||
|
audio/* | video/*)
|
||||||
|
ffmpeg -i "${file}" -vn -codec:a libmp3lame -q:a 2 "${outdir}/${file%.*}.mp3"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
printf "Process 'a'll in directory or 'c'urrent? "
|
||||||
|
read -r resp
|
||||||
|
|
||||||
|
if [ "$resp" = "a" ]; then
|
||||||
|
if ! [ -e "${outdir}" ]; then
|
||||||
|
mkdir "${outdir}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in *; do
|
||||||
|
if [ -f "${f}" ]; then
|
||||||
|
mimestr="$( file --dereference --brief --mime-type -- "${f}" )"
|
||||||
|
handle_multimedia "${mimestr}" "${f}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
elif [ "$resp" = "c" ] && [ -f "$1" ]; then
|
||||||
|
ffmpeg -i "${1}" -vn -codec:a libmp3lame -q:a 2 "${1%.*}.mp3"
|
||||||
|
fi
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Toggle mount of MTP device (eg. Android device)
|
||||||
|
# 'l' to list mountable devices
|
||||||
|
# 'n' integer associated to device to mount
|
||||||
|
# 'q'/'Return' exit
|
||||||
|
#
|
||||||
|
# Dependencies: gvfs-mtp
|
||||||
|
#
|
||||||
|
# Notes: The MTP device should be mounted at /run/user/$UID/gvfs.
|
||||||
|
# Put /run/user/$UID/gvfs to bookmark entries (NNN_BMS) for faster access.
|
||||||
|
# Make sure the device is unlocked when mounting.
|
||||||
|
#
|
||||||
|
# When doing copy-paste into MTP device, you will get an error like this:
|
||||||
|
# cp: preserving times for './gambar1.png': Operation not supported
|
||||||
|
# That just means the file is copied but timestamp won't be preserved.
|
||||||
|
# It's like doing `cp -p localfile.txt file-to-SMB.txt`.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Benawi Adha
|
||||||
|
|
||||||
|
prompt="Device number ('l' to list): "
|
||||||
|
|
||||||
|
IFS='
|
||||||
|
'
|
||||||
|
|
||||||
|
lsmtp () {
|
||||||
|
devs=$(gio mount -li | grep -e 'activation_root' | sed 's/\s*activation_root=//g')
|
||||||
|
c=1
|
||||||
|
printf "Devices list:\n"
|
||||||
|
for i in $devs; do
|
||||||
|
printf "%s %s\\n" "$c" "$i"
|
||||||
|
c=$(( c + 1 ))
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
lsmtp
|
||||||
|
printf "%s" "$prompt"
|
||||||
|
read -r input
|
||||||
|
|
||||||
|
while [ -n "$input" ]
|
||||||
|
do
|
||||||
|
if [ "$input" = "l" ]; then
|
||||||
|
lsmtp
|
||||||
|
elif [ "$input" = "q" ] || [ "$input" -eq 0 ]; then
|
||||||
|
exit
|
||||||
|
elif [ "$input" -le "$(printf '%s\n' "${devs}" | grep -c '^')" ]; then
|
||||||
|
# dev=$(printf "%s\n" "$devs" | cut -d$'\n' -f${input})
|
||||||
|
c=1
|
||||||
|
for i in $devs; do
|
||||||
|
dev=$i
|
||||||
|
if [ "$input" -eq $c ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
c=$(( c + 1 ))
|
||||||
|
done
|
||||||
|
|
||||||
|
if (gio mount -l | grep '^Mount([1-9]).*'"$dev" ) 1>/dev/null; then
|
||||||
|
if gio mount -u "${dev}"; then
|
||||||
|
printf "%s unmounted\n" "$dev"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if gio mount "${dev}"; then
|
||||||
|
printf "%s mounted to /run/user/\$UID/gvfs\n" "$dev"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
printf "Invalid input\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "%s" "$prompt"
|
||||||
|
read -r input
|
||||||
|
done
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Backup nnn configuration
|
||||||
|
# - config dir content
|
||||||
|
# - environment config
|
||||||
|
# - shell functions and aliases
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Léo Villeveygoux
|
||||||
|
|
||||||
|
nnn_aliases="n nnn"
|
||||||
|
|
||||||
|
outdir="nnn-$(whoami)@$(hostname)"
|
||||||
|
|
||||||
|
outfile="${outdir}.tar.bz2"
|
||||||
|
|
||||||
|
shellname="$(basename "$SHELL")"
|
||||||
|
|
||||||
|
conffile="config.txt"
|
||||||
|
|
||||||
|
configdir="${XDG_CONFIG_HOME:-$HOME/.config}/nnn"
|
||||||
|
|
||||||
|
workdir="$PWD"
|
||||||
|
|
||||||
|
tempdir="$(mktemp -d)"
|
||||||
|
|
||||||
|
mkdir "$tempdir/$outdir"
|
||||||
|
|
||||||
|
if [ ! -d "$tempdir" ]; then
|
||||||
|
echo "Can't create work directory." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$tempdir/$outdir" || exit 1
|
||||||
|
|
||||||
|
# Backing up config dir content
|
||||||
|
cp -r "$configdir" . || exit 1
|
||||||
|
|
||||||
|
# Environment config
|
||||||
|
env | sed "s/'/'\\\\''/" |\
|
||||||
|
awk '/^NNN_/{print "export '\''"$0"'\''"}' > "$conffile"
|
||||||
|
|
||||||
|
# Shell functions/aliases
|
||||||
|
case "$shellname" in
|
||||||
|
bash)
|
||||||
|
for name in $nnn_aliases ; do
|
||||||
|
if [ "$(bash -ic "type -t $name")" = "function" ] ; then
|
||||||
|
bash -ic "type $name" | tail -n+2 >> "$conffile"
|
||||||
|
elif bash -ic "alias $name" >/dev/null 2>&1 ; then
|
||||||
|
bash -ic "alias $name" >> "$conffile"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
zsh)
|
||||||
|
for name in $nnn_aliases ; do
|
||||||
|
if zsh -ic "functions $name" ; then
|
||||||
|
zsh -ic "functions $name" >> "$conffile"
|
||||||
|
elif zsh -ic "alias $name" ; then
|
||||||
|
echo alias "$(zsh -ic "alias $name")" >> "$conffile"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "Unknown shell, skipping alias/function checking." >&2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cd .. || exit 1
|
||||||
|
|
||||||
|
printf "Saving as '%s' ... " "$workdir/$outfile"
|
||||||
|
|
||||||
|
tar caf "$workdir/$outfile" "$outdir" && echo "Done" || echo "Failed"
|
||||||
|
|
||||||
|
cd "$workdir" && rm -rf "$tempdir"
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Toggle mount status of a device using pmount
|
||||||
|
# If the device is not mounted, it will be mounted.
|
||||||
|
# If the device is mounted, it will be unmounted and powered down.
|
||||||
|
#
|
||||||
|
# Dependencies: lsblk, pmount
|
||||||
|
#
|
||||||
|
# Usage: Runs `lsblk` on 'l', exits on 'Return`.
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - The script uses Linux-specific lsblk to list block devices. Alternatives:
|
||||||
|
# macOS: "diskutil list"
|
||||||
|
# BSD: "geom disk list"
|
||||||
|
# - The script uses udisksctl (from udisks2) to power down devices. This is also Linux-specific.
|
||||||
|
# Users on non-Linux platforms can comment it and use an alterntive to power-down disks.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
prompt="device name [e.g. sdXn] ('l'ist, 'q'uit): "
|
||||||
|
|
||||||
|
lsblk
|
||||||
|
|
||||||
|
printf "\nEnsure you aren't still in the mounted device.\n"
|
||||||
|
printf "%s" "$prompt"
|
||||||
|
read -r dev
|
||||||
|
|
||||||
|
while [ -n "$dev" ]
|
||||||
|
do
|
||||||
|
if [ "$dev" = "l" ]; then
|
||||||
|
lsblk
|
||||||
|
elif [ "$dev" = "q" ]; then
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
if grep -qs "$dev " /proc/mounts; then
|
||||||
|
sync
|
||||||
|
if pumount "$dev"
|
||||||
|
then
|
||||||
|
echo "$dev" unmounted.
|
||||||
|
if udisksctl power-off -b /dev/"$dev"
|
||||||
|
then
|
||||||
|
echo "$dev" ejected.
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
pmount "$dev"
|
||||||
|
echo "$dev" mounted to "$(lsblk -n /dev/"$dev" | rev | cut -d' ' -f1 | rev)".
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
printf "%s" "$prompt"
|
||||||
|
read -r dev
|
||||||
|
done
|
|
@ -0,0 +1,555 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Sample script to play files in apps by file type or mime
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Usage: nuke filepath
|
||||||
|
#
|
||||||
|
# Integration with nnn:
|
||||||
|
# 1. Export the required config:
|
||||||
|
# export NNN_OPENER=/absolute/path/to/nuke
|
||||||
|
# # Otherwise, if nuke is in $PATH
|
||||||
|
# # export NNN_OPENER=nuke
|
||||||
|
# 2. Run nnn with the program option to indicate a CLI opener
|
||||||
|
# nnn -c
|
||||||
|
# # The -c program option overrides option -e
|
||||||
|
# 3. nuke can use nnn plugins (e.g. mocq is used for audio), $PATH is updated.
|
||||||
|
#
|
||||||
|
# Details:
|
||||||
|
# Inspired by ranger's scope.sh, modified for usage with nnn.
|
||||||
|
#
|
||||||
|
# Guards against accidentally opening mime types like executables, shared libs etc.
|
||||||
|
#
|
||||||
|
# Tries to play 'file' (1st argument) in the following order:
|
||||||
|
# 1. by extension
|
||||||
|
# 2. by mime (image, video, audio, pdf)
|
||||||
|
# 3. by mime (other file types)
|
||||||
|
# 4. by mime (prompt and run executables)
|
||||||
|
#
|
||||||
|
# Modification tips:
|
||||||
|
# 1. Invokes CLI utilities by default. Set GUI to 1 to enable GUI apps.
|
||||||
|
# 2. PAGER is "less -R".
|
||||||
|
# 3. Start GUI apps in bg to unblock. Redirect stdout and strerr if required.
|
||||||
|
# 4. Some CLI utilities are piped to the $PAGER, to wait and quit uniformly.
|
||||||
|
# 5. If the output cannot be paged use "read -r _" to wait for user input.
|
||||||
|
# 6. On a DE, try 'xdg-open' or 'open' in handle_fallback() as last resort.
|
||||||
|
#
|
||||||
|
# Feel free to change the utilities to your favourites and add more mimes.
|
||||||
|
#
|
||||||
|
# Defaults:
|
||||||
|
# By extension (only the enabled ones):
|
||||||
|
# most archives: list with atool, bsdtar
|
||||||
|
# rar: list with unrar
|
||||||
|
# 7-zip: list with 7z
|
||||||
|
# pdf: zathura (GUI), pdftotext, mutool, exiftool
|
||||||
|
# audio: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool
|
||||||
|
# avi|mkv|mp4: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool
|
||||||
|
# log: vi
|
||||||
|
# torrent: rtorrent, transmission-show
|
||||||
|
# odt|ods|odp|sxw: odt2txt
|
||||||
|
# md: glow (https://github.com/charmbracelet/glow), lowdown (https://kristaps.bsd.lv/lowdown)
|
||||||
|
# htm|html|xhtml: w3m, lynx, elinks
|
||||||
|
# json: jq, python (json.tool module)
|
||||||
|
# Multimedia by mime:
|
||||||
|
# image/*: imv/sxiv/nsxiv (GUI), viu (https://github.com/atanunq/viu), img2txt, exiftool
|
||||||
|
# video/*: smplayer, mpv (GUI), ffmpegthumbnailer, mediainfo, exiftool
|
||||||
|
# audio/*: mocq (nnn plugin using MOC), mpv, media_client (Haiku), mediainfo, exiftool
|
||||||
|
# application/pdf: zathura (GUI), pdftotext, mutool, exiftool
|
||||||
|
# Other mimes:
|
||||||
|
# text/troff: man -l
|
||||||
|
# text/* | */xml: vi
|
||||||
|
# image/vnd.djvu): djvutxt, exiftool
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# 1. Adapt, test and enable all mimes
|
||||||
|
# 2. Clean-up the unnecessary exit codes
|
||||||
|
|
||||||
|
# set to 1 to enable GUI apps and/or BIN execution
|
||||||
|
GUI="${GUI:-0}"
|
||||||
|
BIN="${BIN:-0}"
|
||||||
|
|
||||||
|
set -euf -o noclobber -o noglob -o nounset
|
||||||
|
IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n
|
||||||
|
|
||||||
|
PATH=$PATH:"${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins"
|
||||||
|
IMAGE_CACHE_PATH="$(dirname "$1")"/.thumbs
|
||||||
|
|
||||||
|
FPATH="$1"
|
||||||
|
FNAME=$(basename "$1")
|
||||||
|
EDITOR="${VISUAL:-${EDITOR:-vi}}"
|
||||||
|
PAGER="${PAGER:-less -R}"
|
||||||
|
ext="${FNAME##*.}"
|
||||||
|
if [ -n "$ext" ]; then
|
||||||
|
ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
is_mac() {
|
||||||
|
uname | grep -q "Darwin"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_pdf() {
|
||||||
|
if [ "$GUI" -ne 0 ]; then
|
||||||
|
if is_mac; then
|
||||||
|
nohup open "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
elif type zathura >/dev/null 2>&1; then
|
||||||
|
nohup zathura "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
elif type pdftotext >/dev/null 2>&1; then
|
||||||
|
## Preview as text conversion
|
||||||
|
pdftotext -l 10 -nopgbrk -q -- "${FPATH}" - | eval "$PAGER"
|
||||||
|
elif type mutool >/dev/null 2>&1; then
|
||||||
|
mutool draw -F txt -i -- "${FPATH}" 1-10 | eval "$PAGER"
|
||||||
|
elif type exiftool >/dev/null 2>&1; then
|
||||||
|
exiftool "${FPATH}" | eval "$PAGER"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_audio() {
|
||||||
|
if type mocp >/dev/null 2>&1 && type mocq >/dev/null 2>&1; then
|
||||||
|
mocq "${FPATH}" "opener" >/dev/null 2>&1
|
||||||
|
elif type mpv >/dev/null 2>&1; then
|
||||||
|
mpv "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
elif type media_client >/dev/null 2>&1; then
|
||||||
|
media_client play "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
elif type mediainfo >/dev/null 2>&1; then
|
||||||
|
mediainfo "${FPATH}" | eval "$PAGER"
|
||||||
|
elif type exiftool >/dev/null 2>&1; then
|
||||||
|
exiftool "${FPATH}"| eval "$PAGER"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_video() {
|
||||||
|
if [ "$GUI" -ne 0 ]; then
|
||||||
|
if is_mac; then
|
||||||
|
nohup open "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
elif type smplayer >/dev/null 2>&1; then
|
||||||
|
nohup smplayer "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
elif type mpv >/dev/null 2>&1; then
|
||||||
|
nohup mpv "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
elif type ffmpegthumbnailer >/dev/null 2>&1; then
|
||||||
|
# Thumbnail
|
||||||
|
[ -d "${IMAGE_CACHE_PATH}" ] || mkdir "${IMAGE_CACHE_PATH}"
|
||||||
|
ffmpegthumbnailer -i "${FPATH}" -o "${IMAGE_CACHE_PATH}/${FNAME}.jpg" -s 0
|
||||||
|
viu -n "${IMAGE_CACHE_PATH}/${FNAME}.jpg" | eval "$PAGER"
|
||||||
|
elif type mediainfo >/dev/null 2>&1; then
|
||||||
|
mediainfo "${FPATH}" | eval "$PAGER"
|
||||||
|
elif type exiftool >/dev/null 2>&1; then
|
||||||
|
exiftool "${FPATH}"| eval "$PAGER"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# handle this extension and exit
|
||||||
|
handle_extension() {
|
||||||
|
case "${ext}" in
|
||||||
|
## Archive
|
||||||
|
a|ace|alz|arc|arj|bz|bz2|cab|cpio|deb|gz|jar|lha|lz|lzh|lzma|lzo|\
|
||||||
|
rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z|zip)
|
||||||
|
if type atool >/dev/null 2>&1; then
|
||||||
|
atool --list -- "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type bsdtar >/dev/null 2>&1; then
|
||||||
|
bsdtar --list --file "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
rar)
|
||||||
|
if type unrar >/dev/null 2>&1; then
|
||||||
|
## Avoid password prompt by providing empty password
|
||||||
|
unrar lt -p- -- "${FPATH}" | eval "$PAGER"
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
7z)
|
||||||
|
if type 7z >/dev/null 2>&1; then
|
||||||
|
## Avoid password prompt by providing empty password
|
||||||
|
7z l -p -- "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## PDF
|
||||||
|
pdf)
|
||||||
|
handle_pdf
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Audio
|
||||||
|
aac|flac|m4a|mid|midi|mpa|mp2|mp3|ogg|wav|wma)
|
||||||
|
handle_audio
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Video
|
||||||
|
avi|mkv|mp4)
|
||||||
|
handle_video
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Log files
|
||||||
|
log)
|
||||||
|
"$EDITOR" "${FPATH}"
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
## BitTorrent
|
||||||
|
torrent)
|
||||||
|
if type rtorrent >/dev/null 2>&1; then
|
||||||
|
rtorrent "${FPATH}"
|
||||||
|
exit 0
|
||||||
|
elif type transmission-show >/dev/null 2>&1; then
|
||||||
|
transmission-show -- "${FPATH}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## OpenDocument
|
||||||
|
odt|ods|odp|sxw)
|
||||||
|
if type odt2txt >/dev/null 2>&1; then
|
||||||
|
## Preview as text conversion
|
||||||
|
odt2txt "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Markdown
|
||||||
|
md)
|
||||||
|
if type glow >/dev/null 2>&1; then
|
||||||
|
glow -sdark "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type lowdown >/dev/null 2>&1; then
|
||||||
|
lowdown -Tterm "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
## HTML
|
||||||
|
htm|html|xhtml)
|
||||||
|
## Preview as text conversion
|
||||||
|
if type w3m >/dev/null 2>&1; then
|
||||||
|
w3m -dump "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type lynx >/dev/null 2>&1; then
|
||||||
|
lynx -dump -- "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type elinks >/dev/null 2>&1; then
|
||||||
|
elinks -dump "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
json)
|
||||||
|
if type jq >/dev/null 2>&1; then
|
||||||
|
jq --color-output . "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type python >/dev/null 2>&1; then
|
||||||
|
python -m json.tool -- "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# sets the variable abs_target, this should be faster than calling printf
|
||||||
|
abspath() {
|
||||||
|
case "$1" in
|
||||||
|
/*) abs_target="$1";;
|
||||||
|
*) abs_target="$PWD/$1";;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# storing the result to a tmp file is faster than calling listimages twice
|
||||||
|
listimages() {
|
||||||
|
find -L "///${1%/*}" -maxdepth 1 -type f -print0 |
|
||||||
|
grep -izZE '\.(jpe?g|png|gif|webp|tiff|bmp|ico|svg)$' |
|
||||||
|
sort -z | tee "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_dir() {
|
||||||
|
abspath "$2"
|
||||||
|
tmp="${TMPDIR:-/tmp}/nuke_$$"
|
||||||
|
trap 'rm -f $tmp' EXIT
|
||||||
|
count="$(listimages "$abs_target" | grep -a -m 1 -ZznF "$abs_target" | cut -d: -f1)"
|
||||||
|
|
||||||
|
if [ -n "$count" ]; then
|
||||||
|
if [ "$GUI" -ne 0 ]; then
|
||||||
|
xargs -0 nohup "$1" -n "$count" -- < "$tmp"
|
||||||
|
else
|
||||||
|
xargs -0 "$1" -n "$count" -- < "$tmp"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
shift
|
||||||
|
"$1" -- "$@" # fallback
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_multimedia() {
|
||||||
|
## Size of the preview if there are multiple options or it has to be
|
||||||
|
## rendered from vector graphics. If the conversion program allows
|
||||||
|
## specifying only one dimension while keeping the aspect ratio, the width
|
||||||
|
## will be used.
|
||||||
|
# local DEFAULT_SIZE="1920x1080"
|
||||||
|
|
||||||
|
mimetype="${1}"
|
||||||
|
case "${mimetype}" in
|
||||||
|
## SVG
|
||||||
|
# image/svg+xml|image/svg)
|
||||||
|
# convert -- "${FPATH}" "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# exit 1;;
|
||||||
|
|
||||||
|
## DjVu
|
||||||
|
# image/vnd.djvu)
|
||||||
|
# ddjvu -format=tiff -quality=90 -page=1 -size="${DEFAULT_SIZE}" \
|
||||||
|
# - "${IMAGE_CACHE_PATH}" < "${FPATH}" \
|
||||||
|
# && exit 6 || exit 1;;
|
||||||
|
|
||||||
|
## Image
|
||||||
|
image/*)
|
||||||
|
if [ "$GUI" -ne 0 ]; then
|
||||||
|
if is_mac; then
|
||||||
|
nohup open "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
elif type imv >/dev/null 2>&1; then
|
||||||
|
load_dir imv "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
elif type imvr >/dev/null 2>&1; then
|
||||||
|
load_dir imvr "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
elif type sxiv >/dev/null 2>&1; then
|
||||||
|
load_dir sxiv "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
elif type nsxiv >/dev/null 2>&1; then
|
||||||
|
load_dir nsxiv "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
elif type viu >/dev/null 2>&1; then
|
||||||
|
viu -n "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type img2txt >/dev/null 2>&1; then
|
||||||
|
img2txt --gamma=0.6 -- "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type exiftool >/dev/null 2>&1; then
|
||||||
|
exiftool "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# local orientation
|
||||||
|
# orientation="$( identify -format '%[EXIF:Orientation]\n' -- "${FPATH}" )"
|
||||||
|
## If orientation data is present and the image actually
|
||||||
|
## needs rotating ("1" means no rotation)...
|
||||||
|
# if [[ -n "$orientation" && "$orientation" != 1 ]]; then
|
||||||
|
## ...auto-rotate the image according to the EXIF data.
|
||||||
|
# convert -- "${FPATH}" -auto-orient "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# fi
|
||||||
|
|
||||||
|
## `w3mimgdisplay` will be called for all images (unless overridden
|
||||||
|
## as above), but might fail for unsupported types.
|
||||||
|
exit 7;;
|
||||||
|
|
||||||
|
## PDF
|
||||||
|
application/pdf)
|
||||||
|
handle_pdf
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Audio
|
||||||
|
audio/*)
|
||||||
|
handle_audio
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
## Video
|
||||||
|
video/*)
|
||||||
|
handle_video
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
# pdftoppm -f 1 -l 1 \
|
||||||
|
# -scale-to-x "${DEFAULT_SIZE%x*}" \
|
||||||
|
# -scale-to-y -1 \
|
||||||
|
# -singlefile \
|
||||||
|
# -jpeg -tiffcompression jpeg \
|
||||||
|
# -- "${FPATH}" "${IMAGE_CACHE_PATH%.*}" \
|
||||||
|
# && exit 6 || exit 1;;
|
||||||
|
|
||||||
|
|
||||||
|
## ePub, MOBI, FB2 (using Calibre)
|
||||||
|
# application/epub+zip|application/x-mobipocket-ebook|\
|
||||||
|
# application/x-fictionbook+xml)
|
||||||
|
# # ePub (using https://github.com/marianosimone/epub-thumbnailer)
|
||||||
|
# epub-thumbnailer "${FPATH}" "${IMAGE_CACHE_PATH}" \
|
||||||
|
# "${DEFAULT_SIZE%x*}" && exit 6
|
||||||
|
# ebook-meta --get-cover="${IMAGE_CACHE_PATH}" -- "${FPATH}" \
|
||||||
|
# >/dev/null && exit 6
|
||||||
|
# exit 1;;
|
||||||
|
|
||||||
|
## Font
|
||||||
|
# application/font*|application/*opentype)
|
||||||
|
# preview_png="/tmp/$(basename "${IMAGE_CACHE_PATH%.*}").png"
|
||||||
|
# if fontimage -o "${preview_png}" \
|
||||||
|
# --pixelsize "120" \
|
||||||
|
# --fontname \
|
||||||
|
# --pixelsize "80" \
|
||||||
|
# --text " ABCDEFGHIJKLMNOPQRSTUVWXYZ " \
|
||||||
|
# --text " abcdefghijklmnopqrstuvwxyz " \
|
||||||
|
# --text " 0123456789.:,;(*!?') ff fl fi ffi ffl " \
|
||||||
|
# --text " The quick brown fox jumps over the lazy dog. " \
|
||||||
|
# "${FPATH}";
|
||||||
|
# then
|
||||||
|
# convert -- "${preview_png}" "${IMAGE_CACHE_PATH}" \
|
||||||
|
# && rm "${preview_png}" \
|
||||||
|
# && exit 6
|
||||||
|
# else
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
# ;;
|
||||||
|
|
||||||
|
## Preview archives using the first image inside.
|
||||||
|
## (Very useful for comic book collections for example.)
|
||||||
|
# application/zip|application/x-rar|application/x-7z-compressed|\
|
||||||
|
# application/x-xz|application/x-bzip2|application/x-gzip|application/x-tar)
|
||||||
|
# local fn=""; local fe=""
|
||||||
|
# local zip=""; local rar=""; local tar=""; local bsd=""
|
||||||
|
# case "${mimetype}" in
|
||||||
|
# application/zip) zip=1 ;;
|
||||||
|
# application/x-rar) rar=1 ;;
|
||||||
|
# application/x-7z-compressed) ;;
|
||||||
|
# *) tar=1 ;;
|
||||||
|
# esac
|
||||||
|
# { [ "$tar" ] && fn=$(tar --list --file "${FPATH}"); } || \
|
||||||
|
# { fn=$(bsdtar --list --file "${FPATH}") && bsd=1 && tar=""; } || \
|
||||||
|
# { [ "$rar" ] && fn=$(unrar lb -p- -- "${FPATH}"); } || \
|
||||||
|
# { [ "$zip" ] && fn=$(zipinfo -1 -- "${FPATH}"); } || return
|
||||||
|
#
|
||||||
|
# fn=$(echo "$fn" | python -c "import sys; import mimetypes as m; \
|
||||||
|
# [ print(l, end='') for l in sys.stdin if \
|
||||||
|
# (m.guess_type(l[:-1])[0] or '').startswith('image/') ]" |\
|
||||||
|
# sort -V | head -n 1)
|
||||||
|
# [ "$fn" = "" ] && return
|
||||||
|
# [ "$bsd" ] && fn=$(printf '%b' "$fn")
|
||||||
|
#
|
||||||
|
# [ "$tar" ] && tar --extract --to-stdout \
|
||||||
|
# --file "${FPATH}" -- "$fn" > "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# fe=$(echo -n "$fn" | sed 's/[][*?\]/\\\0/g')
|
||||||
|
# [ "$bsd" ] && bsdtar --extract --to-stdout \
|
||||||
|
# --file "${FPATH}" -- "$fe" > "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# [ "$bsd" ] || [ "$tar" ] && rm -- "${IMAGE_CACHE_PATH}"
|
||||||
|
# [ "$rar" ] && unrar p -p- -inul -- "${FPATH}" "$fn" > \
|
||||||
|
# "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# [ "$zip" ] && unzip -pP "" -- "${FPATH}" "$fe" > \
|
||||||
|
# "${IMAGE_CACHE_PATH}" && exit 6
|
||||||
|
# [ "$rar" ] || [ "$zip" ] && rm -- "${IMAGE_CACHE_PATH}"
|
||||||
|
# ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_mime() {
|
||||||
|
mimetype="${1}"
|
||||||
|
case "${mimetype}" in
|
||||||
|
## Manpages
|
||||||
|
text/troff)
|
||||||
|
man -l "${FPATH}"
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
## Text
|
||||||
|
text/* | */xml)
|
||||||
|
"$EDITOR" "${FPATH}"
|
||||||
|
exit 0;;
|
||||||
|
## Syntax highlight
|
||||||
|
# if [[ "$( stat --printf='%s' -- "${FPATH}" )" -gt "${HIGHLIGHT_SIZE_MAX}" ]]; then
|
||||||
|
# exit 2
|
||||||
|
# fi
|
||||||
|
# if [[ "$( tput colors )" -ge 256 ]]; then
|
||||||
|
# local pygmentize_format='terminal256'
|
||||||
|
# local highlight_format='xterm256'
|
||||||
|
# else
|
||||||
|
# local pygmentize_format='terminal'
|
||||||
|
# local highlight_format='ansi'
|
||||||
|
# fi
|
||||||
|
# env HIGHLIGHT_OPTIONS="${HIGHLIGHT_OPTIONS}" highlight \
|
||||||
|
# --out-format="${highlight_format}" \
|
||||||
|
# --force -- "${FPATH}" && exit 5
|
||||||
|
# pygmentize -f "${pygmentize_format}" -O "style=${PYGMENTIZE_STYLE}"\
|
||||||
|
# -- "${FPATH}" && exit 5
|
||||||
|
# exit 2;;
|
||||||
|
|
||||||
|
## DjVu
|
||||||
|
image/vnd.djvu)
|
||||||
|
if type djvutxt >/dev/null 2>&1; then
|
||||||
|
## Preview as text conversion (requires djvulibre)
|
||||||
|
djvutxt "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
elif type exiftool >/dev/null 2>&1; then
|
||||||
|
exiftool "${FPATH}" | eval "$PAGER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_fallback() {
|
||||||
|
if [ "$GUI" -ne 0 ]; then
|
||||||
|
if type xdg-open >/dev/null 2>&1; then
|
||||||
|
nohup xdg-open "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
elif type open >/dev/null 2>&1; then
|
||||||
|
nohup open "${FPATH}" >/dev/null 2>&1 &
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo '----- File details -----' && file --dereference --brief -- "${FPATH}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_blocked() {
|
||||||
|
case "${MIMETYPE}" in
|
||||||
|
application/x-sharedlib)
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
application/x-shared-library-la)
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
application/x-executable)
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
application/x-shellscript)
|
||||||
|
exit 0;;
|
||||||
|
|
||||||
|
application/octet-stream)
|
||||||
|
exit 0;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_bin() {
|
||||||
|
case "${MIMETYPE}" in
|
||||||
|
application/x-executable|application/x-shellscript)
|
||||||
|
clear
|
||||||
|
echo '-------- Executable File --------' && file --dereference --brief -- "${FPATH}"
|
||||||
|
printf "Run executable (y/N/'a'rgs)? "
|
||||||
|
read -r answer
|
||||||
|
case "$answer" in
|
||||||
|
[Yy]* ) exec "${FPATH}";;
|
||||||
|
[Aa]* )
|
||||||
|
printf "args: "
|
||||||
|
read -r args
|
||||||
|
exec "${FPATH}" "$args";;
|
||||||
|
[Nn]* ) exit;;
|
||||||
|
esac
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
MIMETYPE="$( file -bL --mime-type -- "${FPATH}" )"
|
||||||
|
handle_extension
|
||||||
|
handle_multimedia "${MIMETYPE}"
|
||||||
|
handle_mime "${MIMETYPE}"
|
||||||
|
[ "$BIN" -ne 0 ] && [ -x "${FPATH}" ] && handle_bin
|
||||||
|
handle_blocked "${MIMETYPE}"
|
||||||
|
handle_fallback
|
||||||
|
|
||||||
|
exit 1
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: List files bigger than input size by ascending access date.
|
||||||
|
#
|
||||||
|
# Dependencies: find sort
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
printf "Min file size (MB): "
|
||||||
|
read -r size
|
||||||
|
|
||||||
|
find . -size +"$size"M -type f -printf '%A+ %s %p\n' | sort
|
||||||
|
|
||||||
|
echo "Press any key to exit"
|
||||||
|
read -r _
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description: Open selected files in nuke one by one or in oneshot
|
||||||
|
#
|
||||||
|
# Notes: 1. Opens the hovered file if the selection is empty
|
||||||
|
# 2. nuke is the default, set OPENER below for custom
|
||||||
|
# 3. Opener is invoked once for each file in a loop
|
||||||
|
# 4. Keep pressing "Enter" to open files one by one
|
||||||
|
#
|
||||||
|
# Shell: bash
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
OPENER="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
|
||||||
|
|
||||||
|
if [ -s "$sel" ]; then
|
||||||
|
targets=()
|
||||||
|
while IFS= read -r -d '' entry || [ -n "$entry" ]; do
|
||||||
|
targets+=( "$entry" )
|
||||||
|
done < "$sel"
|
||||||
|
|
||||||
|
elements=${#targets[@]}
|
||||||
|
|
||||||
|
if (( elements == 1 )); then
|
||||||
|
# If there's only one file selected, open without prompts
|
||||||
|
"$OPENER" "${targets[0]}"
|
||||||
|
else
|
||||||
|
printf "open [A]ll? "
|
||||||
|
read -r all
|
||||||
|
|
||||||
|
for ((index=0; index <= ${#targets[@]}; index++)); do
|
||||||
|
"$OPENER" "${targets[index]}"
|
||||||
|
if [ "$all" != "A" ] && (( index+1 < elements )); then
|
||||||
|
printf "press Enter to open '%s'\n" "${targets[index+1]}"
|
||||||
|
read -r -s -n 1 key
|
||||||
|
if [[ $key != "" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
elif [ -n "$1" ]; then
|
||||||
|
"$OPENER" "$1"
|
||||||
|
fi
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Organize files in directories by category
|
||||||
|
#
|
||||||
|
# Note: This plugin clears the selection as it changes the contents of the current dir
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: th3lusive
|
||||||
|
|
||||||
|
sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
organize() {
|
||||||
|
case "$(file -biL "$1")" in
|
||||||
|
*video*)
|
||||||
|
[ ! -d "Videos" ] && mkdir "Videos"
|
||||||
|
mv "$1" "Videos/$1"
|
||||||
|
printf "Moved %s to Videos\n" "$1" ;;
|
||||||
|
|
||||||
|
*audio*) [ ! -d "Audio" ] && mkdir "Audio"
|
||||||
|
mv "$1" "Audio/$1"
|
||||||
|
printf "Moved %s to Audio\n" "$1" ;;
|
||||||
|
|
||||||
|
*image*)
|
||||||
|
[ ! -d "Images" ] && mkdir "Images"
|
||||||
|
mv "$1" "Images/$1"
|
||||||
|
printf "Moved %s to Images\n" "$1" ;;
|
||||||
|
|
||||||
|
*pdf*|*document*|*epub*|*djvu*|*cb*)
|
||||||
|
[ ! -d "Documents" ] && mkdir "Documents"
|
||||||
|
mv "$1" "Documents/$1"
|
||||||
|
printf "Moved %s to Documents\n" "$1" ;;
|
||||||
|
|
||||||
|
*text*)
|
||||||
|
[ ! -d "Plaintext" ] && mkdir "Plaintext"
|
||||||
|
mv "$1" "Plaintext/$1"
|
||||||
|
printf "Moved %s to Plaintext\n" "$1" ;;
|
||||||
|
|
||||||
|
*tar*|*xz*|*compress*|*7z*|*rar*|*zip*)
|
||||||
|
[ ! -d "Archives" ] && mkdir "Archives"
|
||||||
|
mv "$1" "Archives/$1"
|
||||||
|
printf "Moved %s to Archives\n" "$1" ;;
|
||||||
|
|
||||||
|
*binary*)
|
||||||
|
[ ! -d "Binaries" ] && mkdir "Binaries"
|
||||||
|
mv "$1" "Binaries/$1"
|
||||||
|
printf "Moved %s to Binaries\n" "$1" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
for file in *
|
||||||
|
do
|
||||||
|
[ -f "$file" ] && organize "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -s "$sel" ] && [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Read a text or PDF file in British English
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
tmpf="$(basename "$1")"
|
||||||
|
tmpf="${TMPDIR:-/tmp}"/"${tmpf%.*}"
|
||||||
|
|
||||||
|
if [ "$(head -c 4 "$1")" = "%PDF" ]; then
|
||||||
|
# Convert using pdftotext
|
||||||
|
pdftotext -nopgbrk -layout "$1" - | sed 's/\xe2\x80\x8b//g' > "$tmpf".txt
|
||||||
|
|
||||||
|
pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$tmpf".txt)"
|
||||||
|
|
||||||
|
rm "$tmpf".txt
|
||||||
|
else
|
||||||
|
pico2wave -w "$tmpf".wav -l en-GB "$(tr '\n' ' ' < "$1")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# to jump around and note the time
|
||||||
|
mpv "$tmpf".wav
|
||||||
|
|
||||||
|
# flat read but better quality
|
||||||
|
# play -qV0 "$tmpf".wav treble 2 gain -l 2
|
||||||
|
|
||||||
|
rm "$tmpf".wav
|
||||||
|
fi
|
|
@ -0,0 +1,211 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Description: tabbed/xembed based file previewer
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - tabbed (https://tools.suckless.org/tabbed): xembed host
|
||||||
|
# - xterm (or urxvt or st) : xembed client for text-based preview
|
||||||
|
# - mpv (https://mpv.io): xembed client for video/audio
|
||||||
|
# - sxiv (https://github.com/muennich/sxiv) or,
|
||||||
|
# - nsxiv (https://codeberg.org/nsxiv/nsxiv) : xembed client for images
|
||||||
|
# - zathura (https://pwmt.org/projects/zathura): xembed client for PDF
|
||||||
|
# - nnn's nuke plugin for text preview and fallback
|
||||||
|
# nuke is a fallback for 'mpv', 'sxiv'/'nsxiv', and 'zathura', but has its
|
||||||
|
# own dependencies, see the script for more information
|
||||||
|
# - vim (or any editor/pager really)
|
||||||
|
# - file
|
||||||
|
# - mktemp
|
||||||
|
# - xdotool (optional, to keep main window focused)
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# - Install the dependencies. Then set a NNN_FIFO
|
||||||
|
# and set a key for the plugin, then start `nnn`:
|
||||||
|
# $ NNN_FIFO=/tmp/nnn.fifo nnn
|
||||||
|
# - Launch the plugin with the designated key from nnn
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# 1. This plugin needs a "NNN_FIFO" to work. See man.
|
||||||
|
# 2. If the same NNN_FIFO is used in multiple nnn instances, there will be one
|
||||||
|
# common preview window. With different FIFO paths, they will be independent.
|
||||||
|
#
|
||||||
|
# How it works:
|
||||||
|
# We use `tabbed` [1] as a xembed [2] host, to have a single window
|
||||||
|
# owning each previewer window. So each previewer must be a xembed client.
|
||||||
|
# For text previewers, this is not an issue, as there are a lot of
|
||||||
|
# xembed-able terminal emulator (we default to `xterm`, but examples are
|
||||||
|
# provided for `urxvt` and `st`). For graphic preview this can be trickier,
|
||||||
|
# but a few popular viewers are xembed-able, we use:
|
||||||
|
# - `mpv`: multimedia player, for video/audio preview
|
||||||
|
# - `sxiv`/`nsxiv`: image viewer
|
||||||
|
# - `zathura`: PDF viewer
|
||||||
|
# - but we always fallback to `nuke` plugin
|
||||||
|
#
|
||||||
|
# [1]: https://tools.suckless.org/tabbed/
|
||||||
|
# [2]: https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
|
||||||
|
#
|
||||||
|
# Shell: Bash (job control is weakly specified in POSIX)
|
||||||
|
# Author: Léo Villeveygoux
|
||||||
|
|
||||||
|
|
||||||
|
XDOTOOL_TIMEOUT=2
|
||||||
|
PAGER=${PAGER:-"vim -R"}
|
||||||
|
NUKE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/plugins/nuke"
|
||||||
|
|
||||||
|
|
||||||
|
if type xterm >/dev/null 2>&1 ; then
|
||||||
|
TERMINAL="xterm -into"
|
||||||
|
elif type urxvt >/dev/null 2>&1 ; then
|
||||||
|
TERMINAL="urxvt -embed"
|
||||||
|
elif type st >/dev/null 2>&1 ; then
|
||||||
|
TERMINAL="st -w"
|
||||||
|
else
|
||||||
|
echo "No xembed term found" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
term_nuke () {
|
||||||
|
# $1 -> $XID, $2 -> $FILE
|
||||||
|
$TERMINAL "$1" -e "$NUKE" "$2" &
|
||||||
|
}
|
||||||
|
|
||||||
|
start_tabbed () {
|
||||||
|
FIFO="$(mktemp -u)"
|
||||||
|
mkfifo "$FIFO"
|
||||||
|
|
||||||
|
tabbed > "$FIFO" &
|
||||||
|
|
||||||
|
jobs # Get rid of the "Completed" entries
|
||||||
|
|
||||||
|
TABBEDPID="$(jobs -p %%)"
|
||||||
|
|
||||||
|
if [ -z "$TABBEDPID" ] ; then
|
||||||
|
echo "Can't start tabbed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r XID < "$FIFO"
|
||||||
|
|
||||||
|
rm "$FIFO"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_viewer_pid () {
|
||||||
|
VIEWERPID="$(jobs -p %%)"
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_viewer () {
|
||||||
|
if [ -n "$VIEWERPID" ] && jobs -p | grep "$VIEWERPID" ; then
|
||||||
|
kill "$VIEWERPID"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
sigint_kill () {
|
||||||
|
kill_viewer
|
||||||
|
kill "$TABBEDPID"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
previewer_loop () {
|
||||||
|
unset -v NNN_FIFO
|
||||||
|
# mute from now
|
||||||
|
exec >/dev/null 2>&1
|
||||||
|
|
||||||
|
MAINWINDOW="$(xdotool getactivewindow)"
|
||||||
|
|
||||||
|
start_tabbed
|
||||||
|
trap sigint_kill SIGINT
|
||||||
|
|
||||||
|
xdotool windowactivate "$MAINWINDOW"
|
||||||
|
|
||||||
|
# Bruteforce focus stealing prevention method,
|
||||||
|
# works well in floating window managers like XFCE
|
||||||
|
# but make interaction with the preview window harder
|
||||||
|
# (uncomment to use):
|
||||||
|
#xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
|
||||||
|
|
||||||
|
while read -r FILE ; do
|
||||||
|
|
||||||
|
jobs # Get rid of the "Completed" entries
|
||||||
|
|
||||||
|
if ! jobs | grep tabbed ; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e "$FILE" ] ; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
kill_viewer
|
||||||
|
|
||||||
|
MIME="$(file -bL --mime-type "$FILE")"
|
||||||
|
|
||||||
|
case "$MIME" in
|
||||||
|
video/*)
|
||||||
|
if type mpv >/dev/null 2>&1 ; then
|
||||||
|
mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
|
||||||
|
else
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
audio/*)
|
||||||
|
if type mpv >/dev/null 2>&1 ; then
|
||||||
|
mpv --force-window=immediate --loop-file --wid="$XID" "$FILE" &
|
||||||
|
else
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
image/*)
|
||||||
|
if type sxiv >/dev/null 2>&1 ; then
|
||||||
|
sxiv -ae "$XID" "$FILE" &
|
||||||
|
elif type nsxiv >/dev/null 2>&1 ; then
|
||||||
|
nsxiv -ae "$XID" "$FILE" &
|
||||||
|
else
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
application/pdf)
|
||||||
|
if type zathura >/dev/null 2>&1 ; then
|
||||||
|
zathura -e "$XID" "$FILE" &
|
||||||
|
else
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
inode/directory)
|
||||||
|
$TERMINAL "$XID" -e nnn "$FILE" &
|
||||||
|
;;
|
||||||
|
text/*)
|
||||||
|
if [ -x "$NUKE" ] ; then
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
$TERMINAL "$XID" -e $PAGER "$FILE" &
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -x "$NUKE" ] ; then
|
||||||
|
term_nuke "$XID" "$FILE"
|
||||||
|
else
|
||||||
|
$TERMINAL "$XID" -e sh -c "file '$FILE' | $PAGER -" &
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
get_viewer_pid
|
||||||
|
|
||||||
|
# following lines are not needed with the bruteforce xdotool method
|
||||||
|
ACTIVE_XID="$(xdotool getactivewindow)"
|
||||||
|
if [ $((ACTIVE_XID == XID)) -ne 0 ] ; then
|
||||||
|
xdotool windowactivate "$MAINWINDOW"
|
||||||
|
else
|
||||||
|
timeout "$XDOTOOL_TIMEOUT" xdotool behave "$XID" focus windowactivate "$MAINWINDOW" &
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
kill "$TABBEDPID"
|
||||||
|
kill_viewer
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ! -r "$NNN_FIFO" ] ; then
|
||||||
|
echo "Can't read \$NNN_FIFO ('$NNN_FIFO')"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
previewer_loop < "$NNN_FIFO" &
|
||||||
|
disown
|
|
@ -0,0 +1,481 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Terminal based file previewer
|
||||||
|
#
|
||||||
|
# Note: This plugin needs a "NNN_FIFO" to work. See man.
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - Supports 5 independent methods to preview with:
|
||||||
|
# - tmux (>=3.0), or
|
||||||
|
# - kitty with allow_remote_control and listen_on set in kitty.conf, or
|
||||||
|
# - QuickLook on WSL (https://github.com/QL-Win/QuickLook), or
|
||||||
|
# - Windows Terminal (https://github.com/Microsoft/Terminal | https://aka.ms/terminal) with WSL, or
|
||||||
|
# - $TERMINAL set to a terminal (it's xterm by default).
|
||||||
|
# - less or $PAGER
|
||||||
|
# - tree or exa or ls
|
||||||
|
# - mediainfo or file
|
||||||
|
# - mktemp
|
||||||
|
# - unzip
|
||||||
|
# - tar
|
||||||
|
# - man
|
||||||
|
# - optional: bsdtar or atool for additional archive preview
|
||||||
|
# - optional: bat for code syntax highlighting
|
||||||
|
# - optional: ueberzug, kitty terminal, viu or catimg for images
|
||||||
|
# - optional: convert(ImageMagick) for playing gif preview (required for kitty image previews)
|
||||||
|
# - optional: ffmpegthumbnailer for video thumbnails (https://github.com/dirkvdb/ffmpegthumbnailer)
|
||||||
|
# - optional: ffmpeg for audio thumbnails
|
||||||
|
# - optional: libreoffce for opendocument/officedocument preview
|
||||||
|
# - optional: pdftoppm(poppler) for pdf thumbnails
|
||||||
|
# - optional: gnome-epub-thumbnailer for epub thumbnails (https://gitlab.gnome.org/GNOME/gnome-epub-thumbnailer)
|
||||||
|
# - optional: fontpreview for font preview (https://github.com/sdushantha/fontpreview)
|
||||||
|
# - optional: djvulibre for djvu
|
||||||
|
# - optional: glow or lowdown for markdown
|
||||||
|
# - optional: w3m or lynx or elinks for html
|
||||||
|
# - optional: set/export ICONLOOKUP as 1 to enable file icons in front of directory previews with .iconlookup
|
||||||
|
# Icons and colors are configureable in .iconlookup
|
||||||
|
# - optional: scope.sh file viewer from ranger.
|
||||||
|
# 1. drop scope.sh executable in $PATH
|
||||||
|
# 2. set/export $USE_SCOPE as 1
|
||||||
|
# - optional: pistol file viewer (https://github.com/doronbehar/pistol).
|
||||||
|
# 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 launch the `preview-tui` plugin in `nnn`.
|
||||||
|
#
|
||||||
|
# If you provide the same NNN_FIFO to all nnn instances, there will be a
|
||||||
|
# single common preview window. If 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 something similar to the following in their kitty.conf:
|
||||||
|
# - `allow_remote_control yes`
|
||||||
|
# - `listen_on unix:$TMPDIR/kitty`
|
||||||
|
# - `enabled_layouts splits` (optional)
|
||||||
|
# With ImageMagick installed, this terminal can use the icat kitten to display images.
|
||||||
|
# Refer to kitty documentation for further details.
|
||||||
|
#
|
||||||
|
# Iterm2 users are recommended to use viu to view images without getting pixelated.
|
||||||
|
#
|
||||||
|
# Windows Terminal users can set "Profile termination behavior" under "Profile > Advanced" settings
|
||||||
|
# to automaticaly close pane on quit when exit code is 0.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Todd Yamakawa, Léo Villeveygoux, @Recidiviste, Mario Ortiz Manero, Luuk van Baal, @WanderLanz
|
||||||
|
|
||||||
|
#SPLIT="$SPLIT" # you can set a permanent split here
|
||||||
|
#TERMINAL="$TERMINAL" # same goes for the terminal
|
||||||
|
SPLIT_SIZE="${SPLIT_SIZE:-50}" # split size in percentage for supported previewers
|
||||||
|
USE_SCOPE="${USE_SCOPE:-0}"
|
||||||
|
USE_PISTOL="${USE_PISTOL:-0}"
|
||||||
|
ICONLOOKUP="${ICONLOOKUP:-0}"
|
||||||
|
PAGER="${PAGER:-less -P?n -R}"
|
||||||
|
TMPDIR="${TMPDIR:-/tmp}"
|
||||||
|
BAT_STYLE="${BAT_STYLE:-numbers}"
|
||||||
|
BAT_THEME="${BAT_THEME:-ansi}"
|
||||||
|
# Consider setting NNN_PREVIEWDIR to $XDG_CACHE_HOME/nnn/previews if you want to keep previews on disk between reboots
|
||||||
|
NNN_PREVIEWDIR="${NNN_PREVIEWDIR:-$TMPDIR/nnn/previews}"
|
||||||
|
NNN_PREVIEWWIDTH="${NNN_PREVIEWWIDTH:-1920}"
|
||||||
|
NNN_PREVIEWHEIGHT="${NNN_PREVIEWHEIGHT:-1080}"
|
||||||
|
NNN_PARENT="${NNN_FIFO#*.}"
|
||||||
|
[ "$NNN_PARENT" -eq "$NNN_PARENT" ] 2>/dev/null || NNN_PARENT=""
|
||||||
|
FIFOPID="$TMPDIR/nnn-preview-tui-fifopid.$NNN_PARENT"
|
||||||
|
PREVIEWPID="$TMPDIR/nnn-preview-tui-pagerpid.$NNN_PARENT"
|
||||||
|
CURSEL="$TMPDIR/nnn-preview-tui-selection.$NNN_PARENT"
|
||||||
|
FIFO_UEBERZUG="$TMPDIR/nnn-preview-tui-ueberzug-fifo.$NNN_PARENT"
|
||||||
|
POSOFFSET="$TMPDIR/nnn-preview-tui-posoffset"
|
||||||
|
|
||||||
|
exists() { type "$1" >/dev/null 2>&1 ;}
|
||||||
|
pkill() { command pkill "$@" >/dev/null 2>&1 ;}
|
||||||
|
pidkill() { [ -f "$1" ] && kill "$(cat "$1")" >/dev/null 2>&1 ;}
|
||||||
|
prompt() { printf "%b" "$@"; cfg=$(stty -g); stty raw -echo; head -c 1; stty "$cfg" ;}
|
||||||
|
|
||||||
|
start_preview() {
|
||||||
|
[ "$PAGER" = "most" ] && PAGER="less -R"
|
||||||
|
|
||||||
|
if [ -e "${TMUX%%,*}" ] && tmux -V | grep -q '[ -][3456789]\.'; then
|
||||||
|
TERMINAL=tmux
|
||||||
|
elif [ -n "$KITTY_LISTEN_ON" ]; then
|
||||||
|
TERMINAL=kitty
|
||||||
|
elif [ -z "$TERMINAL" ] && [ "$TERM_PROGRAM" = "iTerm.app" ]; then
|
||||||
|
TERMINAL=iterm
|
||||||
|
elif [ -n "$WT_SESSION" ]; then
|
||||||
|
TERMINAL=winterm
|
||||||
|
else
|
||||||
|
TERMINAL="${TERMINAL:-xterm}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SPLIT" ] && [ $(($(tput lines) * 2)) -gt "$(tput cols)" ]; then
|
||||||
|
SPLIT='h'
|
||||||
|
elif [ "$SPLIT" != 'h' ]; then
|
||||||
|
SPLIT='v'
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$TERMINAL" in
|
||||||
|
tmux) # tmux splits are inverted
|
||||||
|
if [ "$SPLIT" = "v" ]; then DSPLIT="h"; else DSPLIT="v"; fi
|
||||||
|
tmux split-window -e "NNN_FIFO=$NNN_FIFO" -e "PREVIEW_MODE=1" -e "CURSEL=$CURSEL" \
|
||||||
|
-e "TMPDIR=$TMPDIR" -e "FIFOPID=$FIFOPID" -e "POSOFFSET=$POSOFFSET" \
|
||||||
|
-e "BAT_STYLE=$BAT_STYLE" -e "BAT_THEME=$BAT_THEME" -e "PREVIEWPID=$PREVIEWPID" \
|
||||||
|
-e "PAGER=$PAGER" -e "ICONLOOKUP=$ICONLOOKUP" -e "NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH" \
|
||||||
|
-e "USE_SCOPE=$USE_SCOPE" -e "SPLIT=$SPLIT" -e "USE_PISTOL=$USE_PISTOL" \
|
||||||
|
-e "NNN_PREVIEWDIR=$NNN_PREVIEWDIR" -e "NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT" \
|
||||||
|
-e "FIFO_UEBERZUG=$FIFO_UEBERZUG" -e "QLPATH=$2" -d"$DSPLIT" -p"$SPLIT_SIZE" "$0" "$1" ;;
|
||||||
|
kitty) # Setting the layout for the new window. It will be restored after the script ends.
|
||||||
|
kitty @ goto-layout splits
|
||||||
|
# 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 "TMPDIR=$TMPDIR" \
|
||||||
|
--env "USE_SCOPE=$USE_SCOPE" --env "SPLIT=$SPLIT" --env "TERMINAL=$TERMINAL"\
|
||||||
|
--env "PREVIEWPID=$PREVIEWPID" --env "FIFO_UEBERZUG=$FIFO_UEBERZUG" \
|
||||||
|
--env "ICONLOOKUP=$ICONLOOKUP" --env "NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT" \
|
||||||
|
--env "NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH" --env "NNN_PREVIEWDIR=$NNN_PREVIEWDIR" \
|
||||||
|
--env "USE_PISTOL=$USE_PISTOL" --env "BAT_STYLE=$BAT_STYLE" \
|
||||||
|
--env "BAT_THEME=$BAT_THEME" --env "FIFOPID=$FIFOPID" \
|
||||||
|
--env "CURSEL=$CURSEL" --location "${SPLIT}split" "$0" "$1" ;;
|
||||||
|
iterm)
|
||||||
|
command="$SHELL -c 'cd $PWD; \
|
||||||
|
PATH=\\\"$PATH\\\" NNN_FIFO=\\\"$NNN_FIFO\\\" PREVIEW_MODE=1 PAGER=\\\"$PAGER\\\" \
|
||||||
|
USE_SCOPE=\\\"$USE_SCOPE\\\" SPLIT=\\\"$SPLIT\\\" TERMINAL=\\\"$TERMINAL\\\" \
|
||||||
|
PREVIEWPID=\\\"$PREVIEWPID\\\" CURSEL=\\\"$CURSEL\\\" TMPDIR=\\\"$TMPDIR\\\" \
|
||||||
|
ICONLOOKUP=\\\"$ICONLOOKUP\\\" NNN_PREVIEWHEIGHT=\\\"$NNN_PREVIEWHEIGHT\\\" \
|
||||||
|
NNN_PREVIEWWIDTH=\\\"$NNN_PREVIEWWIDTH\\\" NNN_PREVIEWDIR=\\\"$NNN_PREVIEWDIR\\\" \
|
||||||
|
USE_PISTOL=\\\"$USE_PISTOL\\\" BAT_STYLE=\\\"$BAT_STYLE\\\" \
|
||||||
|
BAT_THEME=\\\"$BAT_THEME\\\" FIFOPID=\\\"$FIFOPID\\\" \\\"$0\\\" \\\"$1\\\"'"
|
||||||
|
if [ "$SPLIT" = "h" ]; then split="horizontally"; else split="vertically"; fi
|
||||||
|
osascript <<-EOF
|
||||||
|
tell application "iTerm"
|
||||||
|
tell current session of current window
|
||||||
|
split $split with default profile command "$command"
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
winterm)
|
||||||
|
if [ "$SPLIT" = "h" ]; then split="H"; else split="V"; fi
|
||||||
|
cmd.exe /c wt -w 0 sp -$split -s$((SPLIT_SIZE / 100)) bash -c "cd $PWD \; \
|
||||||
|
PATH='$PATH' NNN_FIFO=$NNN_FIFO PREVIEW_MODE=1 CURSEL=$CURSEL TMPDIR=$TMPDIR \
|
||||||
|
FIFOPID=$FIFOPID BAT_STYLE=$BAT_STYLE BAT_THEME=$BAT_THEME PREVIEWPID=$PREVIEWPID \
|
||||||
|
PAGER='$PAGER' ICONLOOKUP=$ICONLOOKUP NNN_PREVIEWWIDTH=$NNN_PREVIEWWIDTH \
|
||||||
|
USE_SCOPE=$USE_SCOPE SPLIT=$SPLIT USE_PISTOL=$USE_PISTOL \
|
||||||
|
NNN_PREVIEWDIR=$NNN_PREVIEWDIR NNN_PREVIEWHEIGHT=$NNN_PREVIEWHEIGHT \
|
||||||
|
FIFO_UEBERZUG=$FIFO_UEBERZUG QLPATH=$2 $0 $1" \; -w 0 mf previous
|
||||||
|
;;
|
||||||
|
*) if [ -n "$2" ]; then
|
||||||
|
QUICKLOOK=1 QLPATH="$2" PREVIEW_MODE=1 "$0" "$1" &
|
||||||
|
else
|
||||||
|
PREVIEWPID="$PREVIEWPID" CURSEL="$CURSEL" PREVIEW_MODE=1 \
|
||||||
|
FIFOPID="$FIFOPID" FIFO_UEBERZUG="$FIFO_UEBERZUG" $TERMINAL -e "$0" "$1" &
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_preview() {
|
||||||
|
if exists QuickLook.exe; then
|
||||||
|
QLPATH="QuickLook.exe"
|
||||||
|
elif exists Bridge.exe; then
|
||||||
|
QLPATH="Bridge.exe"
|
||||||
|
fi
|
||||||
|
if pidkill "$FIFOPID"; then
|
||||||
|
[ -p "$NNN_PPIPE" ] && printf "0" > "$NNN_PPIPE"
|
||||||
|
pidkill "$PREVIEWPID"
|
||||||
|
pkill -f "tail --follow $FIFO_UEBERZUG"
|
||||||
|
if [ -n "$QLPATH" ] && stat "$1"; then
|
||||||
|
f="$(wslpath -w "$1")" && "$QLPATH" "$f" &
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[ -p "$NNN_PPIPE" ] && printf "1" > "$NNN_PPIPE"
|
||||||
|
start_preview "$1" "$QLPATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fifo_pager() {
|
||||||
|
cmd="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
# We use a FIFO to access $PAGER PID in jobs control
|
||||||
|
tmpfifopath="$TMPDIR/nnn-preview-tui-fifo.$$"
|
||||||
|
mkfifo "$tmpfifopath" || return
|
||||||
|
|
||||||
|
$PAGER < "$tmpfifopath" &
|
||||||
|
printf "%s" "$!" > "$PREVIEWPID"
|
||||||
|
|
||||||
|
(
|
||||||
|
exec > "$tmpfifopath"
|
||||||
|
if [ "$cmd" = "pager" ]; then
|
||||||
|
if exists bat; then
|
||||||
|
bat --terminal-width="$cols" --decorations=always --color=always \
|
||||||
|
--paging=never --style="$BAT_STYLE" --theme="$BAT_THEME" "$@" &
|
||||||
|
else
|
||||||
|
$PAGER "$@" &
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
"$cmd" "$@" &
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
else
|
||||||
|
file -b "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_mime() {
|
||||||
|
case "$2" in
|
||||||
|
image/jpeg) image_preview "$cols" "$lines" "$1" ;;
|
||||||
|
image/gif) generate_preview "$cols" "$lines" "$1" "gif" ;;
|
||||||
|
image/vnd.djvu) generate_preview "$cols" "$lines" "$1" "djvu" ;;
|
||||||
|
image/*) generate_preview "$cols" "$lines" "$1" "image" ;;
|
||||||
|
video/*) generate_preview "$cols" "$lines" "$1" "video" ;;
|
||||||
|
audio/*) generate_preview "$cols" "$lines" "$1" "audio" ;;
|
||||||
|
application/font*|application/*opentype|font/*) generate_preview "$cols" "$lines" "$1" "font" ;;
|
||||||
|
*/*office*|*/*document*) generate_preview "$cols" "$lines" "$1" "office" ;;
|
||||||
|
application/zip) fifo_pager unzip -l "$1" ;;
|
||||||
|
text/troff)
|
||||||
|
if exists man; then
|
||||||
|
fifo_pager man -Pcat -l "$1"
|
||||||
|
else
|
||||||
|
fifo_pager pager "$1"
|
||||||
|
fi ;;
|
||||||
|
*) handle_ext "$1" "$3" "$4" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_ext() {
|
||||||
|
case "$2" in
|
||||||
|
epub) generate_preview "$cols" "$lines" "$1" "epub" ;;
|
||||||
|
pdf) generate_preview "$cols" "$lines" "$1" "pdf" ;;
|
||||||
|
gz|bz2) fifo_pager tar -tvf "$1" ;;
|
||||||
|
md) if exists glow; then
|
||||||
|
fifo_pager glow -s dark "$1"
|
||||||
|
elif exists lowdown; then
|
||||||
|
fifo_pager lowdown -Tterm "$1"
|
||||||
|
else
|
||||||
|
fifo_pager pager "$1"
|
||||||
|
fi ;;
|
||||||
|
htm|html|xhtml)
|
||||||
|
if exists w3m; then
|
||||||
|
fifo_pager w3m "$1"
|
||||||
|
elif exists lynx; then
|
||||||
|
fifo_pager lynx "$1"
|
||||||
|
elif exists elinks; then
|
||||||
|
fifo_pager elinks "$1"
|
||||||
|
else
|
||||||
|
fifo_pager pager "$1"
|
||||||
|
fi ;;
|
||||||
|
7z|a|ace|alz|arc|arj|bz|cab|cpio|deb|jar|lha|lz|lzh|lzma|lzo\
|
||||||
|
|rar|rpm|rz|t7z|tar|tbz|tbz2|tgz|tlz|txz|tZ|tzo|war|xpi|xz|Z)
|
||||||
|
if exists atool; then
|
||||||
|
fifo_pager atool -l "$1"
|
||||||
|
elif exists bsdtar; then
|
||||||
|
fifo_pager bsdtar -tvf "$1"
|
||||||
|
fi ;;
|
||||||
|
*) if [ "$3" = "bin" ]; then
|
||||||
|
fifo_pager print_bin_info "$1"
|
||||||
|
else
|
||||||
|
fifo_pager pager "$1"
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
preview_file() {
|
||||||
|
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"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use QuickLook if it's available.
|
||||||
|
if [ -n "$QUICKLOOK" ]; then
|
||||||
|
stat "$1" && f="$(wslpath -w "$1")" && "$QLPATH" "$f" &
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detecting the exact type of the file: the encoding, mime type, and extension in lowercase.
|
||||||
|
encoding="$(file -bL --mime-encoding -- "$1")"
|
||||||
|
mimetype="$(file -bL --mime-type -- "$1")"
|
||||||
|
ext="${1##*.}"
|
||||||
|
[ -n "$ext" ] && ext="$(printf "%s" "${ext}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
lines=$(tput lines)
|
||||||
|
cols=$(tput cols)
|
||||||
|
|
||||||
|
# Otherwise, falling back to the defaults.
|
||||||
|
if [ -d "$1" ]; then
|
||||||
|
cd "$1" || return
|
||||||
|
if [ "$ICONLOOKUP" -ne 0 ] && [ -f "$(dirname "$0")"/.iconlookup ]; then
|
||||||
|
[ "$SPLIT" = v ] && BSTR="\n"
|
||||||
|
# shellcheck disable=SC2012
|
||||||
|
ls -F --group-directories-first | head -n "$((lines - 3))" | "$(dirname "$0")"/.iconlookup -l "$cols" -B "$BSTR" -b " "
|
||||||
|
elif exists tree; then
|
||||||
|
fifo_pager tree --filelimit "$(find . -maxdepth 1 | wc -l)" -L 3 -C -F --dirsfirst --noreport
|
||||||
|
elif exists exa; then
|
||||||
|
exa -G --group-directories-first --colour=always
|
||||||
|
else
|
||||||
|
fifo_pager ls -F --group-directories-first --color=always
|
||||||
|
fi
|
||||||
|
elif [ "${encoding#*)}" = "binary" ]; then
|
||||||
|
handle_mime "$1" "$mimetype" "$ext" "bin"
|
||||||
|
else
|
||||||
|
handle_mime "$1" "$mimetype" "$ext"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_preview() {
|
||||||
|
if [ -n "$QLPATH" ] && stat "$3"; then
|
||||||
|
f="$(wslpath -w "$3")" && "$QLPATH" "$f" &
|
||||||
|
elif [ ! -f "$NNN_PREVIEWDIR/$3.jpg" ] || [ -n "$(find -L "$3" -newer "$NNN_PREVIEWDIR/$3.jpg")" ]; then
|
||||||
|
mkdir -p "$NNN_PREVIEWDIR/${3%/*}"
|
||||||
|
case $4 in
|
||||||
|
audio) ffmpeg -i "$3" -filter_complex "scale=iw*min(1\,min($NNN_PREVIEWWIDTH/iw\,ih)):-1" "$NNN_PREVIEWDIR/$3.jpg" -y ;;
|
||||||
|
epub) gnome-epub-thumbnailer "$3" "$NNN_PREVIEWDIR/$3.jpg" ;;
|
||||||
|
font) fontpreview -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" ;;
|
||||||
|
gif) if [ -p "$FIFO_UEBERZUG" ] && exists convert; then
|
||||||
|
frameprefix="$NNN_PREVIEWDIR/$3/${3##*/}"
|
||||||
|
if [ ! -d "$NNN_PREVIEWDIR/$3" ]; then
|
||||||
|
mkdir -p "$NNN_PREVIEWDIR/$3"
|
||||||
|
convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg" ||
|
||||||
|
MAGICK_TMPDIR="/tmp" convert -coalesce -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$3" "$frameprefix.jpg"
|
||||||
|
fi
|
||||||
|
frames=$(($(find "$NNN_PREVIEWDIR/$3" | wc -l) - 2))
|
||||||
|
[ $frames -lt 0 ] && return
|
||||||
|
while true; do
|
||||||
|
for i in $(seq 0 $frames); do
|
||||||
|
image_preview "$1" "$2" "$frameprefix-$i.jpg"
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
done &
|
||||||
|
printf "%s" "$!" > "$PREVIEWPID"
|
||||||
|
return
|
||||||
|
else
|
||||||
|
image_preview "$1" "$2" "$3"
|
||||||
|
return
|
||||||
|
fi ;;
|
||||||
|
image) if exists convert; then
|
||||||
|
convert "$3" -flatten -resize "$NNN_PREVIEWWIDTH"x"$NNN_PREVIEWHEIGHT"\> "$NNN_PREVIEWDIR/$3.jpg"
|
||||||
|
else
|
||||||
|
image_preview "$1" "$2" "$3" && return
|
||||||
|
fi ;;
|
||||||
|
office) libreoffice --convert-to jpg "$3" --outdir "$NNN_PREVIEWDIR/${3%/*}"
|
||||||
|
filename="$(printf "%s" "${3##*/}" | cut -d. -f1)"
|
||||||
|
mv "$NNN_PREVIEWDIR/${3%/*}/$filename.jpg" "$NNN_PREVIEWDIR/$3.jpg" ;;
|
||||||
|
pdf) pdftoppm -jpeg -f 1 -singlefile "$3" "$NNN_PREVIEWDIR/$3" ;;
|
||||||
|
djvu) ddjvu -format=ppm -page=1 "$3" "$NNN_PREVIEWDIR/$3.jpg" ;;
|
||||||
|
video) ffmpegthumbnailer -m -s0 -i "$3" -o "$NNN_PREVIEWDIR/$3.jpg" || rm "$NNN_PREVIEWDIR/$3.jpg" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
if [ -f "$NNN_PREVIEWDIR/$3.jpg" ]; then
|
||||||
|
image_preview "$1" "$2" "$NNN_PREVIEWDIR/$3.jpg"
|
||||||
|
else
|
||||||
|
fifo_pager print_bin_info "$3"
|
||||||
|
fi
|
||||||
|
} >/dev/null 2>&1
|
||||||
|
|
||||||
|
image_preview() {
|
||||||
|
clear
|
||||||
|
exec >/dev/tty
|
||||||
|
if [ "$TERMINAL" = "kitty" ]; then
|
||||||
|
# Kitty terminal users can use the native image preview method
|
||||||
|
kitty +kitten icat --silent --scale-up --place "$1"x"$2"@0x0 --transfer-mode=stream --stdin=no "$3" &
|
||||||
|
elif exists ueberzug; then
|
||||||
|
ueberzug_layer "$1" "$2" "$3" && return
|
||||||
|
elif exists catimg; then
|
||||||
|
catimg "$3" &
|
||||||
|
elif exists viu; then
|
||||||
|
viu -t "$3" &
|
||||||
|
else
|
||||||
|
fifo_pager print_bin_info "$3" && return
|
||||||
|
fi
|
||||||
|
printf "%s" "$!" > "$PREVIEWPID"
|
||||||
|
}
|
||||||
|
|
||||||
|
ueberzug_layer() {
|
||||||
|
[ -f "$POSOFFSET" ] && read -r x y < "$POSOFFSET"
|
||||||
|
printf '{"action": "add", "identifier": "nnn_ueberzug", "x": %d, "y": %d, "width": "%d", "height": "%d", "scaler": "fit_contain", "path": "%s"}\n'\
|
||||||
|
"${x:-0}" "${y:-0}" "$1" "$2" "$3" > "$FIFO_UEBERZUG"
|
||||||
|
}
|
||||||
|
|
||||||
|
ueberzug_remove() {
|
||||||
|
printf '{"action": "remove", "identifier": "nnn_ueberzug"}\n' > "$FIFO_UEBERZUG"
|
||||||
|
}
|
||||||
|
|
||||||
|
winch_handler() {
|
||||||
|
clear
|
||||||
|
pidkill "$PREVIEWPID"
|
||||||
|
if [ -p "$FIFO_UEBERZUG" ]; then
|
||||||
|
pkill -f "tail --follow $FIFO_UEBERZUG"
|
||||||
|
tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json &
|
||||||
|
fi
|
||||||
|
preview_file "$(cat "$CURSEL")"
|
||||||
|
}
|
||||||
|
|
||||||
|
preview_fifo() {
|
||||||
|
while read -r selection; do
|
||||||
|
if [ -n "$selection" ]; then
|
||||||
|
pidkill "$PREVIEWPID"
|
||||||
|
[ -p "$FIFO_UEBERZUG" ] && ueberzug_remove
|
||||||
|
[ "$selection" = "close" ] && break
|
||||||
|
preview_file "$selection"
|
||||||
|
printf "%s" "$selection" > "$CURSEL"
|
||||||
|
fi
|
||||||
|
done < "$NNN_FIFO"
|
||||||
|
sleep 0.1 # make sure potential preview by winch_handler is killed
|
||||||
|
pkill -P "$$"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$PREVIEW_MODE" ]; then
|
||||||
|
if [ "$TERMINAL" != "kitty" ] && exists ueberzug; then
|
||||||
|
mkfifo "$FIFO_UEBERZUG"
|
||||||
|
tail --follow "$FIFO_UEBERZUG" | ueberzug layer --silent --parser json &
|
||||||
|
fi
|
||||||
|
|
||||||
|
preview_file "$PWD/$1"
|
||||||
|
preview_fifo &
|
||||||
|
printf "%s" "$!" > "$FIFOPID"
|
||||||
|
printf "%s" "$PWD/$1" > "$CURSEL"
|
||||||
|
trap 'winch_handler; wait' WINCH
|
||||||
|
trap 'rm "$PREVIEWPID" "$CURSEL" "$FIFO_UEBERZUG" "$FIFOPID" "$POSOFFSET" 2>/dev/null' INT HUP EXIT
|
||||||
|
wait "$!" 2>/dev/null
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
if [ ! -r "$NNN_FIFO" ]; then
|
||||||
|
clear
|
||||||
|
prompt "No FIFO available! (\$NNN_FIFO='$NNN_FIFO')\nPlease read Usage in preview-tui."
|
||||||
|
elif [ "$KITTY_WINDOW_ID" ] && [ -z "$TMUX" ] && [ -z "$KITTY_LISTEN_ON" ]; then
|
||||||
|
clear
|
||||||
|
prompt "\$KITTY_LISTEN_ON not set!\nPlease read Usage in preview-tui."
|
||||||
|
else
|
||||||
|
toggle_preview "$1" &
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Fuzzy list and kill a (zombie) process by name
|
||||||
|
#
|
||||||
|
# Dependencies: fzf, ps
|
||||||
|
#
|
||||||
|
# Note: To kill a zombie process enter "zombie"
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
printf "Enter process name ['defunct' for zombies]: "
|
||||||
|
read -r psname
|
||||||
|
|
||||||
|
# shellcheck disable=SC2009
|
||||||
|
if [ -n "$psname" ]; then
|
||||||
|
if type sudo >/dev/null 2>&1; then
|
||||||
|
sucmd=sudo
|
||||||
|
elif type doas >/dev/null 2>&1; then
|
||||||
|
sucmd=doas
|
||||||
|
else
|
||||||
|
sucmd=: # noop
|
||||||
|
fi
|
||||||
|
|
||||||
|
if type fzf >/dev/null 2>&1; then
|
||||||
|
fuzzy=fzf
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmd="$(ps -ax | grep -iw "$psname" | "$fuzzy" | sed -e 's/^[ \t]*//' | cut -d' ' -f1)"
|
||||||
|
if [ -n "$cmd" ]; then
|
||||||
|
$sucmd kill -9 "$cmd"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Batch rename selection or current directory with qmv or vidir
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# - Try to mimic current batch rename functionality but with correct
|
||||||
|
# handling of edge cases by qmv or vidir.
|
||||||
|
# - Qmv opens with hidden files if no selection is used. Selected
|
||||||
|
# directories are shown.
|
||||||
|
# - Vidir don't show directories nor hidden files.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: José Neder
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
if type qmv >/dev/null 2>&1; then
|
||||||
|
batchrenamesel="qmv -fdo -da"
|
||||||
|
batchrename="qmv -fdo -a"
|
||||||
|
elif type vidir >/dev/null 2>&1; then
|
||||||
|
batchrenamesel="vidir"
|
||||||
|
batchrename="vidir"
|
||||||
|
else
|
||||||
|
printf "there is not batchrename program installed."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "rename selection? "
|
||||||
|
read -r resp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resp" = "y" ]; then
|
||||||
|
# -o flag is necessary for interactive editors
|
||||||
|
xargs -o -0 $batchrenamesel < "$selection"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
elif [ ! "$(LC_ALL=C ls -a)" = ".
|
||||||
|
.." ]; then
|
||||||
|
# On older systems that don't have ls -A
|
||||||
|
$batchrename
|
||||||
|
fi
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Create an mp3 ringtone out of an audio file in any format
|
||||||
|
# Needs user to provide start and end where to cut the file
|
||||||
|
# Input file audio.ext results in audio_ringtone.mp3
|
||||||
|
#
|
||||||
|
# Tip: To convert a complete media file, set start as 0 and
|
||||||
|
# the runtime of the file as end.
|
||||||
|
#
|
||||||
|
# Dependencies: date, ffmpeg
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
printf "start (hh:mm:ss): "
|
||||||
|
read -r start
|
||||||
|
st=$(date -d "$start" +%s) || exit 1
|
||||||
|
|
||||||
|
printf "end (hh:mm:ss): "
|
||||||
|
read -r end
|
||||||
|
et=$(date -d "$end" +%s) || exit 1
|
||||||
|
|
||||||
|
if [ "$st" -ge "$et" ]; then
|
||||||
|
printf "error: start >= end "
|
||||||
|
read -r _
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
interval=$(( et - st ))
|
||||||
|
|
||||||
|
outfile=$(basename "$1")
|
||||||
|
outfile="${outfile%.*}"_ringtone.mp3
|
||||||
|
|
||||||
|
ffmpeg -i "$1" -ss "$start" -t "$interval" -vn -sn -acodec libmp3lame -q:a 2 "$outfile"
|
||||||
|
fi
|
|
@ -0,0 +1,26 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Simple script to give copy-paste a progress percentage
|
||||||
|
# by utilizing rsync.
|
||||||
|
#
|
||||||
|
# LIMITATION: this won't work when pasting to MTP device.
|
||||||
|
#
|
||||||
|
# Dependencies: rsync
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Benawi Adha
|
||||||
|
|
||||||
|
sel=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
# Choose one of these two schemes by commenting
|
||||||
|
|
||||||
|
# more verbose
|
||||||
|
xargs -0 -I % rsync -ah --progress % "$PWD" < "$sel"
|
||||||
|
|
||||||
|
# less verbose
|
||||||
|
# xargs -0 -I % rsync -ah --info=progress2 % "$PWD" < "$sel"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
|
@ -0,0 +1,52 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Splits the file passed as argument or joins selection
|
||||||
|
#
|
||||||
|
# Note: Adds numeric suffix to split files
|
||||||
|
# Adds '.out suffix to the first file to be joined and saves as output file for join
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Arun Prakash Jana, ath3
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
resp=s
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
printf "press 's' (split current file) or 'j' (join selection): "
|
||||||
|
read -r resp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$resp" = "j" ]; then
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
arr=$(tr '\0' '\n' < "$selection")
|
||||||
|
if [ "$(echo "$arr" | wc -l)" -lt 2 ]; then
|
||||||
|
echo "joining needs at least 2 files"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
for entry in $arr
|
||||||
|
do
|
||||||
|
if [ -d "$entry" ]; then
|
||||||
|
echo "cant join directories"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
file="$(basename "$(echo "$arr" | sed -n '1p' | sed -e 's/[0-9][0-9]$//')")"
|
||||||
|
sort -z < "$selection" | xargs -0 -I{} cat {} > "${file}.out"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [ "$resp" = "s" ]; then
|
||||||
|
if [ -n "$1" ] && [ -f "$1" ]; then
|
||||||
|
# a single file is passed
|
||||||
|
printf "split size in MB: "
|
||||||
|
read -r size
|
||||||
|
|
||||||
|
if [ -n "$size" ]; then
|
||||||
|
split -d -b "$size"M "$1" "$1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Edit file as superuser
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Anna Arad
|
||||||
|
|
||||||
|
EDITOR="${EDITOR:-vim}"
|
||||||
|
|
||||||
|
if type sudo >/dev/null 2>&1; then
|
||||||
|
sudo -E "$EDITOR" "$1"
|
||||||
|
elif type sudoedit >/dev/null 2>&1; then
|
||||||
|
sudoedit -E "$1"
|
||||||
|
elif type doas >/dev/null 2>&1; then
|
||||||
|
doas "$EDITOR" "$1"
|
||||||
|
fi
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Toggles executable mode for selection
|
||||||
|
#
|
||||||
|
# Dependencies: chmod
|
||||||
|
#
|
||||||
|
# Note: Works _only_ with selection (nnn can toggle the mode for the hovered file)
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
xargs -0 -I {} sh -c 'if [ -x "{}" ] ; then chmod -x "{}" ; else chmod +x "{}" ; fi' < "$selection"
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
if [ -p "$NNN_PIPE" ]; then
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,52 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Autodetects a nnn remote mountpoint (mounted with `c`)
|
||||||
|
# from any of its subfolders and allows unmounting it
|
||||||
|
# from the subdir without navigating to the mountppoint
|
||||||
|
# or entering the remote name. Also works when hovering
|
||||||
|
# the mountpoint directly like vanilla `u`.
|
||||||
|
#
|
||||||
|
# Dependencies: fusermount
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Authors: Kabouik & 0xACE
|
||||||
|
#
|
||||||
|
# TODO:
|
||||||
|
# - Avoid lazy unmount by forcing nnn context to leave the subfolder before fusermount.
|
||||||
|
# Tried `printf "%s" "0c$m" > "$NNN_PIPE"` but it breaks the nnn interface, see #854.
|
||||||
|
|
||||||
|
err=0
|
||||||
|
m=$HOME/.config/nnn/mounts
|
||||||
|
if [ "$PWD" = "$m" ]; then
|
||||||
|
# Allow running the script on hovered directory if user is in ~/.config/nnn/mounts
|
||||||
|
d="$1"
|
||||||
|
else
|
||||||
|
d=$(dirname "$(readlink -f "$1")" | grep -oP "^$m\K.*" | cut -d"/" -f2)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test if user is within $m or a subdir, abort if not
|
||||||
|
if [ "$d" = "" ]; then
|
||||||
|
clear && printf "You are not in a remote folder mounted with nnn. Press return to continue. " && read -r _
|
||||||
|
else
|
||||||
|
# Test if $m/$d is a mountpoint and try unmounting if it is
|
||||||
|
mountpoint -q -- "$m/$d"
|
||||||
|
if [ "$?" -eq "1" ]; then
|
||||||
|
clear && printf "Parent '%s' is not a mountpoint. Press return to continue. " "$d" && read -r _
|
||||||
|
else
|
||||||
|
cd "$m" && fusermount -uq "$m/$d" || err=1
|
||||||
|
if [ "$err" -eq "0" ]; then
|
||||||
|
rmdir "$m/$d" && clear && printf "Parent '%s' unmounted." "$d"
|
||||||
|
else
|
||||||
|
clear && printf "Failed to unmount. Try lazy unmount? [Yy/Nn] " && read -r
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If unmount fails, offer lazy unmount
|
||||||
|
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
|
||||||
|
err=0
|
||||||
|
cd "$m" && fusermount -uqz "$m/$d" || err=1
|
||||||
|
if [ "$err" -eq "0" ]; then
|
||||||
|
rmdir "$m/$d" && clear && printf "Parent '%s' unmounted with lazy unmount. " "$d"
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Selections are uploaded using Firefox Send
|
||||||
|
# For single files:
|
||||||
|
# Upload to Firefox Send if ffsend is found, else
|
||||||
|
# Paste contents of a text a file http://ix.io
|
||||||
|
# Upload a binary file to file.io
|
||||||
|
#
|
||||||
|
# Dependencies: ffsend (https://github.com/timvisee/ffsend), curl, jq, tr
|
||||||
|
#
|
||||||
|
# Note: Binary file set to expire after a week
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Arun Prakash Jana
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
if [ -s "$selection" ]; then
|
||||||
|
if type ffsend >/dev/null 2>&1; then
|
||||||
|
# File name will be randomized foo.tar
|
||||||
|
xargs -0 < "$selection" ffsend u
|
||||||
|
else
|
||||||
|
printf "ffsend is required to upload selection."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear selection
|
||||||
|
printf "-" > "$NNN_PIPE"
|
||||||
|
else
|
||||||
|
if [ -n "$1" ] && [ -s "$1" ]; then
|
||||||
|
if type ffsend >/dev/null 2>&1; then
|
||||||
|
ffsend -fiq u "$1"
|
||||||
|
elif [ "$(mimetype --output-format %m "$1" | awk -F '/' '{print $1}')" = "text" ]; then
|
||||||
|
curl -F "f:1=@$1" ix.io
|
||||||
|
else
|
||||||
|
# Upload the file, show the download link and wait till user presses any key
|
||||||
|
curl -s -F "file=@$1" https://file.io/?expires=1w | jq '.link' | tr -d '"'
|
||||||
|
|
||||||
|
# To write download link to "$1".loc and exit
|
||||||
|
# curl -s -F "file=@$1" https://file.io/?expires=1w -o `basename "$1"`.loc
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf "empty file!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -r _
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Set the selected image as wallpaper.
|
||||||
|
# Uses nitrogen or pywal on X11, swww on wayland.
|
||||||
|
#
|
||||||
|
# Usage: Hover on an image and run the script to set it as wallpaper.
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: juacq97
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
if [ "$(file --mime-type "$1" | awk '{print $NF}' | awk -F '/' '{print $1}')" = "image" ]; then
|
||||||
|
if [ "$XDG_SESSION_TYPE" = "x11" ]; then
|
||||||
|
if type nitrogen >/dev/null 2>&1; then
|
||||||
|
nitrogen --set-zoom-fill --save "$1"
|
||||||
|
elif type wal >/dev/null 2>&1; then
|
||||||
|
wal -i "$1"
|
||||||
|
else
|
||||||
|
printf "nitrogen or pywal missing"
|
||||||
|
read -r _
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if type swww >/dev/null 2>&1; then
|
||||||
|
swww img "$1"
|
||||||
|
else
|
||||||
|
printf "swww missing"
|
||||||
|
read -r _
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If you want a system notification, uncomment the next 3 lines.
|
||||||
|
# notify-send -a "nnn" "Wallpaper changed!"
|
||||||
|
# else
|
||||||
|
# notify-send -a "nnn" "No image selected"
|
||||||
|
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Copy system clipboard newline-separated file list to selection
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - tr
|
||||||
|
# - xclip/xsel (Linux)
|
||||||
|
# - pbpaste (macOS)
|
||||||
|
# - termux-clipboard-get (Termux)
|
||||||
|
# - powershell (WSL)
|
||||||
|
# - cygwim's /dev/clipboard (Cygwin)
|
||||||
|
# - wl-paste (Wayland)
|
||||||
|
# - clipboard (Haiku)
|
||||||
|
#
|
||||||
|
# Note:
|
||||||
|
# - Limitation: breaks if a filename has newline in it
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: Léo Villeveygoux, after Arun Prakash Jana's .cbcp
|
||||||
|
|
||||||
|
IFS="$(printf '%b_' '\n')"; IFS="${IFS%_}" # protect trailing \n
|
||||||
|
|
||||||
|
selection=${NNN_SEL:-${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.selection}
|
||||||
|
|
||||||
|
getclip () {
|
||||||
|
if type xsel >/dev/null 2>&1; then
|
||||||
|
# Linux
|
||||||
|
xsel -bo
|
||||||
|
elif type xclip >/dev/null 2>&1; then
|
||||||
|
# Linux
|
||||||
|
xclip -sel clip -o
|
||||||
|
elif type pbpaste >/dev/null 2>&1; then
|
||||||
|
# macOS
|
||||||
|
pbpaste
|
||||||
|
elif type termux-clipboard-get >/dev/null 2>&1; then
|
||||||
|
# Termux
|
||||||
|
termux-clipboard-get
|
||||||
|
elif type powershell.exe >/dev/null 2>&1; then
|
||||||
|
# WSL
|
||||||
|
powershell.exe Get-Clipboard
|
||||||
|
elif [ -r /dev/clipboard ] ; then
|
||||||
|
# Cygwin
|
||||||
|
cat /dev/clipboard
|
||||||
|
elif type wl-paste >/dev/null 2>&1; then
|
||||||
|
# Wayland
|
||||||
|
wl-paste
|
||||||
|
elif type clipboard >/dev/null 2>&1; then
|
||||||
|
# Haiku
|
||||||
|
clipboard --print
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
CLIPBOARD=$(getclip)
|
||||||
|
|
||||||
|
# Check if clipboard actually contains a file list
|
||||||
|
for file in $CLIPBOARD ; do
|
||||||
|
if [ ! -e "$file" ] ; then
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "%s" "$CLIPBOARD" | tr '\n' '\0' > "$selection"
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Description: Sets the xdg-open's default application for the current entry's file
|
||||||
|
# type. ${XDG_DATA_DIRS} and ${XDG_DATA_HOME} are set to the recommended
|
||||||
|
# defaults if unset, as specified in XDG Base Directory Specification
|
||||||
|
# - http://specifications.freedesktop.org/basedir-spec/.
|
||||||
|
#
|
||||||
|
# Dependencies: xdg-utils, fzf or dmenu (GUI)
|
||||||
|
#
|
||||||
|
# Shell: POSIX compliant
|
||||||
|
# Author: lwnctd
|
||||||
|
|
||||||
|
# set to 1 to enable GUI apps
|
||||||
|
GUI="${GUI:-0}"
|
||||||
|
|
||||||
|
if [ "$GUI" -ne 0 ] && command -v dmenu > /dev/null 2>& 1; then
|
||||||
|
menu="dmenu -i -l 7"
|
||||||
|
elif command -v fzf > /dev/null 2>& 1; then
|
||||||
|
menu="fzf -e --tiebreak=begin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$1" ] || [ -z "$menu" ] > /dev/null 2>& 1; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ftype=$(xdg-mime query filetype "$2/$1")
|
||||||
|
|
||||||
|
if [ -z "$ftype" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dirs=${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
|
||||||
|
dirs=${dirs}:${XDG_DATA_HOME:-$HOME/.local/share}:
|
||||||
|
|
||||||
|
while [ -n "$dirs" ]; do
|
||||||
|
d=${dirs%%:*}
|
||||||
|
if [ -n "$d" ] && [ -d "$d"/applications ]; then
|
||||||
|
set -- "$@" "$d"/applications
|
||||||
|
fi
|
||||||
|
dirs=${dirs#*:}
|
||||||
|
done
|
||||||
|
|
||||||
|
app=$(find "$@" -iname '*.desktop' -exec grep '^Name=' {} + \
|
||||||
|
| sort -u -t ':' -k 1,1 \
|
||||||
|
| sed -e 's;..*/\(..*desktop\):Name=\(..*\);\2:\1;' \
|
||||||
|
| sort -t ':' -k 1,1 \
|
||||||
|
| column -t -s ':' -o "$(printf '\t')" \
|
||||||
|
| $menu \
|
||||||
|
| cut -f 2)
|
||||||
|
|
||||||
|
if [ -n "$app" ]; then
|
||||||
|
xdg-mime default "${app%%[[:blank:]]*}" "$ftype"
|
||||||
|
fi
|
|
@ -0,0 +1,30 @@
|
||||||
|
n ()
|
||||||
|
{
|
||||||
|
# Block nesting of nnn in subshells
|
||||||
|
if [[ "${NNNLVL:-0}" -ge 1 ]]; then
|
||||||
|
echo "nnn is already running"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set)
|
||||||
|
# If NNN_TMPFILE is set to a custom path, it must be exported for nnn to
|
||||||
|
# see. To cd on quit only on ^G, remove the "export" and make sure not to
|
||||||
|
# use a custom path, i.e. set NNN_TMPFILE *exactly* as follows:
|
||||||
|
# NNN_TMPFILE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.lastd"
|
||||||
|
export NNN_TMPFILE="${XDG_CONFIG_HOME:-$HOME/.config}/nnn/.lastd"
|
||||||
|
|
||||||
|
# Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn
|
||||||
|
# stty start undef
|
||||||
|
# stty stop undef
|
||||||
|
# stty lwrap undef
|
||||||
|
# stty lnext undef
|
||||||
|
|
||||||
|
# The backslash allows one to alias n to nnn if desired without making an
|
||||||
|
# infinitely recursive alias
|
||||||
|
\nnn "$@"
|
||||||
|
|
||||||
|
if [ -f "$NNN_TMPFILE" ]; then
|
||||||
|
. "$NNN_TMPFILE"
|
||||||
|
rm -f "$NNN_TMPFILE" > /dev/null
|
||||||
|
fi
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
# NOTE: set NNN_TMPFILE correctly if you use 'XDG_CONFIG_HOME'
|
||||||
|
|
||||||
|
# The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set)
|
||||||
|
# If NNN_TMPFILE is set to a custom path, it must be exported for nnn to see.
|
||||||
|
# To cd on quit only on ^G, set NNN_TMPFILE after the nnn invocation, and make
|
||||||
|
# sure not to use a custom path.
|
||||||
|
set NNN_TMPFILE=~/.config/nnn/.lastd
|
||||||
|
|
||||||
|
# Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn
|
||||||
|
# stty start undef
|
||||||
|
# stty stop undef
|
||||||
|
# stty lwrap undef
|
||||||
|
# stty lnext undef
|
||||||
|
|
||||||
|
# The backslash allows one to alias n to nnn if desired without making an
|
||||||
|
# infinitely recursive alias
|
||||||
|
alias n '\nnn; source "$NNN_TMPFILE"; rm -f "$NNN_TMPFILE"'
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Append this file to ~/.elvish/rc.elv (Elvish > 0.17.0)
|
||||||
|
|
||||||
|
use path
|
||||||
|
|
||||||
|
fn n {|@a|
|
||||||
|
# Block nesting of nnn in subshells
|
||||||
|
if (has-env NNNLVL) {
|
||||||
|
try {
|
||||||
|
if (>= $E:NNNLVL 1) {
|
||||||
|
echo "nnn is already running"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch e {
|
||||||
|
nop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set)
|
||||||
|
# If NNN_TMPFILE is set to a custom path, it must be exported for nnn to
|
||||||
|
# see.
|
||||||
|
if (has-env XDG_CONFIG_HOME) {
|
||||||
|
set-env NNN_TMPFILE $E:XDG_CONFIG_HOME/nnn/.lastd
|
||||||
|
} else {
|
||||||
|
set-env NNN_TMPFILE $E:HOME/.config/nnn/.lastd
|
||||||
|
}
|
||||||
|
|
||||||
|
# Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn
|
||||||
|
# stty start undef
|
||||||
|
# stty stop undef
|
||||||
|
# stty lwrap undef
|
||||||
|
# stty lnext undef
|
||||||
|
|
||||||
|
# The e: prefix allows one to alias n to nnn if desired without making an
|
||||||
|
# infinitely recursive alias
|
||||||
|
e:nnn $@a
|
||||||
|
|
||||||
|
if (path:is-regular $E:NNN_TMPFILE) {
|
||||||
|
eval (slurp < $E:NNN_TMPFILE)
|
||||||
|
rm $E:NNN_TMPFILE
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Rename this file to match the name of the function
|
||||||
|
# e.g. ~/.config/fish/functions/n.fish
|
||||||
|
# or, add the lines to the 'config.fish' file.
|
||||||
|
|
||||||
|
function n --wraps nnn --description 'support nnn quit and change directory'
|
||||||
|
# Block nesting of nnn in subshells
|
||||||
|
if test -n "$NNNLVL" -a "$NNNLVL" -ge 1
|
||||||
|
echo "nnn is already running"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# The behaviour is set to cd on quit (nnn checks if NNN_TMPFILE is set)
|
||||||
|
# If NNN_TMPFILE is set to a custom path, it must be exported for nnn to
|
||||||
|
# see. To cd on quit only on ^G, remove the "-x" from both lines below,
|
||||||
|
# without changing the paths.
|
||||||
|
if test -n "$XDG_CONFIG_HOME"
|
||||||
|
set -x NNN_TMPFILE "$XDG_CONFIG_HOME/nnn/.lastd"
|
||||||
|
else
|
||||||
|
set -x NNN_TMPFILE "$HOME/.config/nnn/.lastd"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Unmask ^Q (, ^V etc.) (if required, see `stty -a`) to Quit nnn
|
||||||
|
# stty start undef
|
||||||
|
# stty stop undef
|
||||||
|
# stty lwrap undef
|
||||||
|
# stty lnext undef
|
||||||
|
|
||||||
|
# The command function allows one to alias this function to `nnn` without
|
||||||
|
# making an infinitely recursive alias
|
||||||
|
command nnn $argv
|
||||||
|
|
||||||
|
if test -e $NNN_TMPFILE
|
||||||
|
source $NNN_TMPFILE
|
||||||
|
rm $NNN_TMPFILE
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue