From 28bae624a4b6ec45858d363cbf0610b5a0dd11df Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Wed, 13 Sep 2023 22:17:29 +0200 Subject: [PATCH] mimimum zsh plugins --- .gitignore | 2 + .local/share/fzf/completion.bash | 381 +++++++++++++ .local/share/fzf/completion.zsh | 324 +++++++++++ .local/share/fzf/key-bindings.bash | 102 ++++ .local/share/fzf/key-bindings.zsh | 121 ++++ .local/share/nnn/plugins/.cbcp | 51 ++ .local/share/nnn/plugins/.iconlookup | 428 ++++++++++++++ .local/share/nnn/plugins/.nmv | 180 ++++++ .local/share/nnn/plugins/.nnn-plugin-helper | 38 ++ .local/share/nnn/plugins/.ntfy | 22 + .local/share/nnn/plugins/README.md | 378 +++++++++++++ .local/share/nnn/plugins/autojump | 74 +++ .local/share/nnn/plugins/boom | 50 ++ .local/share/nnn/plugins/bulknew | 32 ++ .local/share/nnn/plugins/cdpath | 56 ++ .local/share/nnn/plugins/chksum | 75 +++ .local/share/nnn/plugins/cmusq | 80 +++ .local/share/nnn/plugins/diffs | 62 ++ .local/share/nnn/plugins/dragdrop | 77 +++ .local/share/nnn/plugins/dups | 70 +++ .local/share/nnn/plugins/finder | 89 +++ .local/share/nnn/plugins/fixname | 75 +++ .local/share/nnn/plugins/fzcd | 89 +++ .local/share/nnn/plugins/fzhist | 40 ++ .local/share/nnn/plugins/fzopen | 83 +++ .local/share/nnn/plugins/fzplug | 59 ++ .local/share/nnn/plugins/getplugs | 70 +++ .local/share/nnn/plugins/gitroot | 15 + .local/share/nnn/plugins/gpgd | 28 + .local/share/nnn/plugins/gpge | 44 ++ .local/share/nnn/plugins/gsconnect | 21 + .local/share/nnn/plugins/gutenread | 49 ++ .local/share/nnn/plugins/imgresize | 31 + .local/share/nnn/plugins/imgur | 597 ++++++++++++++++++++ .local/share/nnn/plugins/imgview | 113 ++++ .local/share/nnn/plugins/ipinfo | 13 + .local/share/nnn/plugins/kdeconnect | 24 + .local/share/nnn/plugins/launch | 42 ++ .local/share/nnn/plugins/mimelist | 15 + .local/share/nnn/plugins/moclyrics | 40 ++ .local/share/nnn/plugins/mocq | 89 +++ .local/share/nnn/plugins/mp3conv | 41 ++ .local/share/nnn/plugins/mtpmount | 76 +++ .local/share/nnn/plugins/nbak | 75 +++ .local/share/nnn/plugins/nmount | 55 ++ .local/share/nnn/plugins/nuke | 555 ++++++++++++++++++ .local/share/nnn/plugins/oldbigfile | 16 + .local/share/nnn/plugins/openall | 49 ++ .local/share/nnn/plugins/organize | 62 ++ .local/share/nnn/plugins/pdfread | 30 + .local/share/nnn/plugins/preview-tabbed | 211 +++++++ .local/share/nnn/plugins/preview-tui | 481 ++++++++++++++++ .local/share/nnn/plugins/pskill | 35 ++ .local/share/nnn/plugins/renamer | 45 ++ .local/share/nnn/plugins/ringtone | 36 ++ .local/share/nnn/plugins/rsynccp | 26 + .local/share/nnn/plugins/splitjoin | 52 ++ .local/share/nnn/plugins/suedit | 16 + .local/share/nnn/plugins/togglex | 21 + .local/share/nnn/plugins/umounttree | 52 ++ .local/share/nnn/plugins/upload | 45 ++ .local/share/nnn/plugins/wallpaper | 37 ++ .local/share/nnn/plugins/x2sel | 62 ++ .local/share/nnn/plugins/xdgdefault | 53 ++ .local/share/nnn/quitcd/quitcd.bash_zsh | 30 + .local/share/nnn/quitcd/quitcd.csh | 17 + .local/share/nnn/quitcd/quitcd.elv | 41 ++ .local/share/nnn/quitcd/quitcd.fish | 36 ++ 68 files changed, 6484 insertions(+) create mode 100644 .local/share/fzf/completion.bash create mode 100644 .local/share/fzf/completion.zsh create mode 100644 .local/share/fzf/key-bindings.bash create mode 100644 .local/share/fzf/key-bindings.zsh create mode 100755 .local/share/nnn/plugins/.cbcp create mode 100755 .local/share/nnn/plugins/.iconlookup create mode 100755 .local/share/nnn/plugins/.nmv create mode 100644 .local/share/nnn/plugins/.nnn-plugin-helper create mode 100755 .local/share/nnn/plugins/.ntfy create mode 100644 .local/share/nnn/plugins/README.md create mode 100755 .local/share/nnn/plugins/autojump create mode 100755 .local/share/nnn/plugins/boom create mode 100755 .local/share/nnn/plugins/bulknew create mode 100755 .local/share/nnn/plugins/cdpath create mode 100755 .local/share/nnn/plugins/chksum create mode 100755 .local/share/nnn/plugins/cmusq create mode 100755 .local/share/nnn/plugins/diffs create mode 100755 .local/share/nnn/plugins/dragdrop create mode 100755 .local/share/nnn/plugins/dups create mode 100755 .local/share/nnn/plugins/finder create mode 100755 .local/share/nnn/plugins/fixname create mode 100755 .local/share/nnn/plugins/fzcd create mode 100755 .local/share/nnn/plugins/fzhist create mode 100755 .local/share/nnn/plugins/fzopen create mode 100755 .local/share/nnn/plugins/fzplug create mode 100755 .local/share/nnn/plugins/getplugs create mode 100755 .local/share/nnn/plugins/gitroot create mode 100755 .local/share/nnn/plugins/gpgd create mode 100755 .local/share/nnn/plugins/gpge create mode 100755 .local/share/nnn/plugins/gsconnect create mode 100755 .local/share/nnn/plugins/gutenread create mode 100755 .local/share/nnn/plugins/imgresize create mode 100755 .local/share/nnn/plugins/imgur create mode 100755 .local/share/nnn/plugins/imgview create mode 100755 .local/share/nnn/plugins/ipinfo create mode 100755 .local/share/nnn/plugins/kdeconnect create mode 100755 .local/share/nnn/plugins/launch create mode 100755 .local/share/nnn/plugins/mimelist create mode 100755 .local/share/nnn/plugins/moclyrics create mode 100755 .local/share/nnn/plugins/mocq create mode 100755 .local/share/nnn/plugins/mp3conv create mode 100755 .local/share/nnn/plugins/mtpmount create mode 100755 .local/share/nnn/plugins/nbak create mode 100755 .local/share/nnn/plugins/nmount create mode 100755 .local/share/nnn/plugins/nuke create mode 100755 .local/share/nnn/plugins/oldbigfile create mode 100755 .local/share/nnn/plugins/openall create mode 100755 .local/share/nnn/plugins/organize create mode 100755 .local/share/nnn/plugins/pdfread create mode 100755 .local/share/nnn/plugins/preview-tabbed create mode 100755 .local/share/nnn/plugins/preview-tui create mode 100755 .local/share/nnn/plugins/pskill create mode 100755 .local/share/nnn/plugins/renamer create mode 100755 .local/share/nnn/plugins/ringtone create mode 100755 .local/share/nnn/plugins/rsynccp create mode 100755 .local/share/nnn/plugins/splitjoin create mode 100755 .local/share/nnn/plugins/suedit create mode 100755 .local/share/nnn/plugins/togglex create mode 100755 .local/share/nnn/plugins/umounttree create mode 100755 .local/share/nnn/plugins/upload create mode 100755 .local/share/nnn/plugins/wallpaper create mode 100755 .local/share/nnn/plugins/x2sel create mode 100755 .local/share/nnn/plugins/xdgdefault create mode 100644 .local/share/nnn/quitcd/quitcd.bash_zsh create mode 100644 .local/share/nnn/quitcd/quitcd.csh create mode 100644 .local/share/nnn/quitcd/quitcd.elv create mode 100644 .local/share/nnn/quitcd/quitcd.fish diff --git a/.gitignore b/.gitignore index 4f8d624..e148913 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ !.zsh/** !.tmux.conf !.gitconfig +!.local +!.local/** diff --git a/.local/share/fzf/completion.bash b/.local/share/fzf/completion.bash new file mode 100644 index 0000000..51687a0 --- /dev/null +++ b/.local/share/fzf/completion.bash @@ -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 diff --git a/.local/share/fzf/completion.zsh b/.local/share/fzf/completion.zsh new file mode 100644 index 0000000..807d7b3 --- /dev/null +++ b/.local/share/fzf/completion.zsh @@ -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 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** +__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' +} diff --git a/.local/share/fzf/key-bindings.bash b/.local/share/fzf/key-bindings.bash new file mode 100644 index 0000000..d83f9d3 --- /dev/null +++ b/.local/share/fzf/key-bindings.bash @@ -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 diff --git a/.local/share/fzf/key-bindings.zsh b/.local/share/fzf/key-bindings.zsh new file mode 100644 index 0000000..458a3a4 --- /dev/null +++ b/.local/share/fzf/key-bindings.zsh @@ -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' +} diff --git a/.local/share/nnn/plugins/.cbcp b/.local/share/nnn/plugins/.cbcp new file mode 100755 index 0000000..70f9b75 --- /dev/null +++ b/.local/share/nnn/plugins/.cbcp @@ -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 diff --git a/.local/share/nnn/plugins/.iconlookup b/.local/share/nnn/plugins/.iconlookup new file mode 100755 index 0000000..d48d676 --- /dev/null +++ b/.local/share/nnn/plugins/.iconlookup @@ -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 diff --git a/.local/share/nnn/plugins/.nmv b/.local/share/nnn/plugins/.nmv new file mode 100755 index 0000000..37e887d --- /dev/null +++ b/.local/share/nnn/plugins/.nmv @@ -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 diff --git a/.local/share/nnn/plugins/.nnn-plugin-helper b/.local/share/nnn/plugins/.nnn-plugin-helper new file mode 100644 index 0000000..a0377ac --- /dev/null +++ b/.local/share/nnn/plugins/.nnn-plugin-helper @@ -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 $? +} diff --git a/.local/share/nnn/plugins/.ntfy b/.local/share/nnn/plugins/.ntfy new file mode 100755 index 0000000..2a61478 --- /dev/null +++ b/.local/share/nnn/plugins/.ntfy @@ -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 diff --git a/.local/share/nnn/plugins/README.md b/.local/share/nnn/plugins/README.md new file mode 100644 index 0000000..bb27d05 --- /dev/null +++ b/.local/share/nnn/plugins/README.md @@ -0,0 +1,378 @@ +

nnn plugins

+ +

+

read ebooks with plugin gutenread (Android)

+ +## 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 selection1] | Lang | Dependencies | +| --- | --- | --- | --- | +| [autojump](autojump) | Navigate to dir/path | sh | [jump](https://github.com/gsamokovarov/jump)/autojump/
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,
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,
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
[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,
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 (;) 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 ;f, `fzopen` can be run with ;o and so on... The key vs. plugin pairs are shown in the help and config screen. + +Alternatively, combine with Alt (i.e. Alt+key). + +To pick and run an unassigned plugin, press Enter (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 ;x can be used to make a file executable, ;g can be used to the git log of a git project directory, ;s 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 ;s and ;n. + +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 `(<->)` 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_. diff --git a/.local/share/nnn/plugins/autojump b/.local/share/nnn/plugins/autojump new file mode 100755 index 0000000..6a24a52 --- /dev/null +++ b/.local/share/nnn/plugins/autojump @@ -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 diff --git a/.local/share/nnn/plugins/boom b/.local/share/nnn/plugins/boom new file mode 100755 index 0000000..aab466f --- /dev/null +++ b/.local/share/nnn/plugins/boom @@ -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 diff --git a/.local/share/nnn/plugins/bulknew b/.local/share/nnn/plugins/bulknew new file mode 100755 index 0000000..64331e4 --- /dev/null +++ b/.local/share/nnn/plugins/bulknew @@ -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" diff --git a/.local/share/nnn/plugins/cdpath b/.local/share/nnn/plugins/cdpath new file mode 100755 index 0000000..663cb26 --- /dev/null +++ b/.local/share/nnn/plugins/cdpath @@ -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 diff --git a/.local/share/nnn/plugins/chksum b/.local/share/nnn/plugins/chksum new file mode 100755 index 0000000..5fa91cb --- /dev/null +++ b/.local/share/nnn/plugins/chksum @@ -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 diff --git a/.local/share/nnn/plugins/cmusq b/.local/share/nnn/plugins/cmusq new file mode 100755 index 0000000..fbaa485 --- /dev/null +++ b/.local/share/nnn/plugins/cmusq @@ -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 diff --git a/.local/share/nnn/plugins/diffs b/.local/share/nnn/plugins/diffs new file mode 100755 index 0000000..0464781 --- /dev/null +++ b/.local/share/nnn/plugins/diffs @@ -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 diff --git a/.local/share/nnn/plugins/dragdrop b/.local/share/nnn/plugins/dragdrop new file mode 100755 index 0000000..812f970 --- /dev/null +++ b/.local/share/nnn/plugins/dragdrop @@ -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 + diff --git a/.local/share/nnn/plugins/dups b/.local/share/nnn/plugins/dups new file mode 100755 index 0000000..27c1807 --- /dev/null +++ b/.local/share/nnn/plugins/dups @@ -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/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 "$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 diff --git a/.local/share/nnn/plugins/fixname b/.local/share/nnn/plugins/fixname new file mode 100755 index 0000000..4047152 --- /dev/null +++ b/.local/share/nnn/plugins/fixname @@ -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 diff --git a/.local/share/nnn/plugins/fzcd b/.local/share/nnn/plugins/fzcd new file mode 100755 index 0000000..125092c --- /dev/null +++ b/.local/share/nnn/plugins/fzcd @@ -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 diff --git a/.local/share/nnn/plugins/fzhist b/.local/share/nnn/plugins/fzhist new file mode 100755 index 0000000..111bc22 --- /dev/null +++ b/.local/share/nnn/plugins/fzhist @@ -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 diff --git a/.local/share/nnn/plugins/fzopen b/.local/share/nnn/plugins/fzopen new file mode 100755 index 0000000..17b0585 --- /dev/null +++ b/.local/share/nnn/plugins/fzopen @@ -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 diff --git a/.local/share/nnn/plugins/fzplug b/.local/share/nnn/plugins/fzplug new file mode 100755 index 0000000..11dcf7f --- /dev/null +++ b/.local/share/nnn/plugins/fzplug @@ -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 diff --git a/.local/share/nnn/plugins/getplugs b/.local/share/nnn/plugins/getplugs new file mode 100755 index 0000000..361a605 --- /dev/null +++ b/.local/share/nnn/plugins/getplugs @@ -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 diff --git a/.local/share/nnn/plugins/gitroot b/.local/share/nnn/plugins/gitroot new file mode 100755 index 0000000..4428d1e --- /dev/null +++ b/.local/share/nnn/plugins/gitroot @@ -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 diff --git a/.local/share/nnn/plugins/gpgd b/.local/share/nnn/plugins/gpgd new file mode 100755 index 0000000..44d5c0f --- /dev/null +++ b/.local/share/nnn/plugins/gpgd @@ -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 diff --git a/.local/share/nnn/plugins/gpge b/.local/share/nnn/plugins/gpge new file mode 100755 index 0000000..69016a9 --- /dev/null +++ b/.local/share/nnn/plugins/gpge @@ -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 diff --git a/.local/share/nnn/plugins/gsconnect b/.local/share/nnn/plugins/gsconnect new file mode 100755 index 0000000..f45f3d3 --- /dev/null +++ b/.local/share/nnn/plugins/gsconnect @@ -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 diff --git a/.local/share/nnn/plugins/gutenread b/.local/share/nnn/plugins/gutenread new file mode 100755 index 0000000..036ff35 --- /dev/null +++ b/.local/share/nnn/plugins/gutenread @@ -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 diff --git a/.local/share/nnn/plugins/imgresize b/.local/share/nnn/plugins/imgresize new file mode 100755 index 0000000..351fe71 --- /dev/null +++ b/.local/share/nnn/plugins/imgresize @@ -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 diff --git a/.local/share/nnn/plugins/imgur b/.local/share/nnn/plugins/imgur new file mode 100755 index 0000000..16d21bc --- /dev/null +++ b/.local/share/nnn/plugins/imgur @@ -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'> <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 _ diff --git a/.local/share/nnn/plugins/imgview b/.local/share/nnn/plugins/imgview new file mode 100755 index 0000000..d8cc247 --- /dev/null +++ b/.local/share/nnn/plugins/imgview @@ -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 diff --git a/.local/share/nnn/plugins/ipinfo b/.local/share/nnn/plugins/ipinfo new file mode 100755 index 0000000..4ff6f41 --- /dev/null +++ b/.local/share/nnn/plugins/ipinfo @@ -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 _ diff --git a/.local/share/nnn/plugins/kdeconnect b/.local/share/nnn/plugins/kdeconnect new file mode 100755 index 0000000..5f63d8b --- /dev/null +++ b/.local/share/nnn/plugins/kdeconnect @@ -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 diff --git a/.local/share/nnn/plugins/launch b/.local/share/nnn/plugins/launch new file mode 100755 index 0000000..d666cc5 --- /dev/null +++ b/.local/share/nnn/plugins/launch @@ -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 diff --git a/.local/share/nnn/plugins/mimelist b/.local/share/nnn/plugins/mimelist new file mode 100755 index 0000000..ccfe05a --- /dev/null +++ b/.local/share/nnn/plugins/mimelist @@ -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" diff --git a/.local/share/nnn/plugins/moclyrics b/.local/share/nnn/plugins/moclyrics new file mode 100755 index 0000000..2f69807 --- /dev/null +++ b/.local/share/nnn/plugins/moclyrics @@ -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 diff --git a/.local/share/nnn/plugins/mocq b/.local/share/nnn/plugins/mocq new file mode 100755 index 0000000..038ecc9 --- /dev/null +++ b/.local/share/nnn/plugins/mocq @@ -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 diff --git a/.local/share/nnn/plugins/mp3conv b/.local/share/nnn/plugins/mp3conv new file mode 100755 index 0000000..029f544 --- /dev/null +++ b/.local/share/nnn/plugins/mp3conv @@ -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 diff --git a/.local/share/nnn/plugins/mtpmount b/.local/share/nnn/plugins/mtpmount new file mode 100755 index 0000000..d6feea0 --- /dev/null +++ b/.local/share/nnn/plugins/mtpmount @@ -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 + diff --git a/.local/share/nnn/plugins/nbak b/.local/share/nnn/plugins/nbak new file mode 100755 index 0000000..f9cb644 --- /dev/null +++ b/.local/share/nnn/plugins/nbak @@ -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" diff --git a/.local/share/nnn/plugins/nmount b/.local/share/nnn/plugins/nmount new file mode 100755 index 0000000..e92fd5d --- /dev/null +++ b/.local/share/nnn/plugins/nmount @@ -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 diff --git a/.local/share/nnn/plugins/nuke b/.local/share/nnn/plugins/nuke new file mode 100755 index 0000000..b3eafc8 --- /dev/null +++ b/.local/share/nnn/plugins/nuke @@ -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 diff --git a/.local/share/nnn/plugins/oldbigfile b/.local/share/nnn/plugins/oldbigfile new file mode 100755 index 0000000..0a21527 --- /dev/null +++ b/.local/share/nnn/plugins/oldbigfile @@ -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 _ diff --git a/.local/share/nnn/plugins/openall b/.local/share/nnn/plugins/openall new file mode 100755 index 0000000..5a7941f --- /dev/null +++ b/.local/share/nnn/plugins/openall @@ -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 diff --git a/.local/share/nnn/plugins/organize b/.local/share/nnn/plugins/organize new file mode 100755 index 0000000..fb70aaf --- /dev/null +++ b/.local/share/nnn/plugins/organize @@ -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 "$@" diff --git a/.local/share/nnn/plugins/pdfread b/.local/share/nnn/plugins/pdfread new file mode 100755 index 0000000..1e889be --- /dev/null +++ b/.local/share/nnn/plugins/pdfread @@ -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 diff --git a/.local/share/nnn/plugins/preview-tabbed b/.local/share/nnn/plugins/preview-tabbed new file mode 100755 index 0000000..d1354bc --- /dev/null +++ b/.local/share/nnn/plugins/preview-tabbed @@ -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 diff --git a/.local/share/nnn/plugins/preview-tui b/.local/share/nnn/plugins/preview-tui new file mode 100755 index 0000000..1426327 --- /dev/null +++ b/.local/share/nnn/plugins/preview-tui @@ -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 diff --git a/.local/share/nnn/plugins/pskill b/.local/share/nnn/plugins/pskill new file mode 100755 index 0000000..e01c739 --- /dev/null +++ b/.local/share/nnn/plugins/pskill @@ -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 diff --git a/.local/share/nnn/plugins/renamer b/.local/share/nnn/plugins/renamer new file mode 100755 index 0000000..51c586e --- /dev/null +++ b/.local/share/nnn/plugins/renamer @@ -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 diff --git a/.local/share/nnn/plugins/ringtone b/.local/share/nnn/plugins/ringtone new file mode 100755 index 0000000..c18f255 --- /dev/null +++ b/.local/share/nnn/plugins/ringtone @@ -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 diff --git a/.local/share/nnn/plugins/rsynccp b/.local/share/nnn/plugins/rsynccp new file mode 100755 index 0000000..902f9e7 --- /dev/null +++ b/.local/share/nnn/plugins/rsynccp @@ -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 diff --git a/.local/share/nnn/plugins/splitjoin b/.local/share/nnn/plugins/splitjoin new file mode 100755 index 0000000..5ba081b --- /dev/null +++ b/.local/share/nnn/plugins/splitjoin @@ -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 diff --git a/.local/share/nnn/plugins/suedit b/.local/share/nnn/plugins/suedit new file mode 100755 index 0000000..21dddf4 --- /dev/null +++ b/.local/share/nnn/plugins/suedit @@ -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 diff --git a/.local/share/nnn/plugins/togglex b/.local/share/nnn/plugins/togglex new file mode 100755 index 0000000..e42d65a --- /dev/null +++ b/.local/share/nnn/plugins/togglex @@ -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 diff --git a/.local/share/nnn/plugins/umounttree b/.local/share/nnn/plugins/umounttree new file mode 100755 index 0000000..011d74d --- /dev/null +++ b/.local/share/nnn/plugins/umounttree @@ -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 diff --git a/.local/share/nnn/plugins/upload b/.local/share/nnn/plugins/upload new file mode 100755 index 0000000..4948587 --- /dev/null +++ b/.local/share/nnn/plugins/upload @@ -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 _ diff --git a/.local/share/nnn/plugins/wallpaper b/.local/share/nnn/plugins/wallpaper new file mode 100755 index 0000000..2017fbd --- /dev/null +++ b/.local/share/nnn/plugins/wallpaper @@ -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 diff --git a/.local/share/nnn/plugins/x2sel b/.local/share/nnn/plugins/x2sel new file mode 100755 index 0000000..70104d7 --- /dev/null +++ b/.local/share/nnn/plugins/x2sel @@ -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" diff --git a/.local/share/nnn/plugins/xdgdefault b/.local/share/nnn/plugins/xdgdefault new file mode 100755 index 0000000..cf64a2f --- /dev/null +++ b/.local/share/nnn/plugins/xdgdefault @@ -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 diff --git a/.local/share/nnn/quitcd/quitcd.bash_zsh b/.local/share/nnn/quitcd/quitcd.bash_zsh new file mode 100644 index 0000000..2450b79 --- /dev/null +++ b/.local/share/nnn/quitcd/quitcd.bash_zsh @@ -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 +} diff --git a/.local/share/nnn/quitcd/quitcd.csh b/.local/share/nnn/quitcd/quitcd.csh new file mode 100644 index 0000000..72bd357 --- /dev/null +++ b/.local/share/nnn/quitcd/quitcd.csh @@ -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"' diff --git a/.local/share/nnn/quitcd/quitcd.elv b/.local/share/nnn/quitcd/quitcd.elv new file mode 100644 index 0000000..95c47a6 --- /dev/null +++ b/.local/share/nnn/quitcd/quitcd.elv @@ -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 + } +} diff --git a/.local/share/nnn/quitcd/quitcd.fish b/.local/share/nnn/quitcd/quitcd.fish new file mode 100644 index 0000000..a8c3dc4 --- /dev/null +++ b/.local/share/nnn/quitcd/quitcd.fish @@ -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