Rewrite snippy utility
It now handle: * multiline {clipboard} * cli {cursor} handling * gui {cursor} handling for multiline snippets
This commit is contained in:
parent
9bc10ed984
commit
0437577983
142
snippy
142
snippy
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/bash
|
||||||
# video demo at: http://www.youtube.com/watch?v=90xoathBYfk
|
# video demo at: http://www.youtube.com/watch?v=90xoathBYfk
|
||||||
|
|
||||||
# augmented by barbuk: https://github.com/BarbUk/dotfiles/blob/master/bin/snippy
|
# augmented by barbuk: https://github.com/BarbUk/dotfiles/blob/master/bin/snippy
|
||||||
# . restore current clipboard
|
# . restore current clipboard
|
||||||
# . {clipboard} placeholder to use current clipboard in snippet
|
# . {clipboard} placeholder to use current clipboard in snippet
|
||||||
# . {cursor} placeholder to place the cursor
|
# . {cursor} placeholder to place the cursor
|
||||||
|
# . go left to the correct position for cli and gui paste
|
||||||
|
# . go up for block snippet for gui paste
|
||||||
# . ##noparse header in snippet to not parse
|
# . ##noparse header in snippet to not parse
|
||||||
# . execute command begining by $
|
# . execute command begining by $
|
||||||
#
|
#
|
||||||
@ -42,16 +44,20 @@
|
|||||||
# GUIPASTE="xdotool key ctrl+v"
|
# GUIPASTE="xdotool key ctrl+v"
|
||||||
# CLIPASTE="xdotool key ctrl+shift+v"
|
# CLIPASTE="xdotool key ctrl+shift+v"
|
||||||
|
|
||||||
DIR=${HOME}/.snippy
|
readonly snippets_directory=${HOME}/.snippy
|
||||||
MENU_ENGINE="rofi"
|
readonly MENU_ENGINE="rofi"
|
||||||
DMENU_ARGS='-dmenu -i -sort'
|
readonly DMENU_ARGS='-dmenu -i -sort'
|
||||||
|
|
||||||
# if nothing happens, try "xdotool click 2", "xdotool key ctrl+v" or "xdotool key ctrl+shift+v"
|
# if nothing happens, try "xdotool click 2", "xdotool key ctrl+v" or "xdotool key ctrl+shift+v"
|
||||||
GUIPASTE="xdotool key ctrl+v"
|
readonly GUIPASTE="xdotool key ctrl+v"
|
||||||
CLIPASTE="xdotool key ctrl+shift+v"
|
readonly CLIPASTE="xdotool key ctrl+shift+v"
|
||||||
|
|
||||||
TMPFILE="/tmp/.snippy.tmp"; :>$TMPFILE
|
readonly MENU_ARGS=${DMENU_ARGS}
|
||||||
|
readonly cursor_placeholder="{cursor}"
|
||||||
|
readonly clipboard_placeholder="{clipboard}"
|
||||||
|
|
||||||
MENU_ARGS=${DMENU_ARGS}
|
readonly tmpfile=$(mktemp)
|
||||||
|
trap 'rm -f \$tmpfile' EXIT HUP INT TRAP TERM
|
||||||
|
|
||||||
# smarty like template engine which executes inline bash in (bashdown) strings (replaces variables with values e.g.)
|
# smarty like template engine which executes inline bash in (bashdown) strings (replaces variables with values e.g.)
|
||||||
# @link http://github.com/coderofsalvation/bashdown
|
# @link http://github.com/coderofsalvation/bashdown
|
||||||
@ -59,81 +65,121 @@ MENU_ARGS=${DMENU_ARGS}
|
|||||||
# @example: echo 'hi $NAME it is $(date)' | bashdown
|
# @example: echo 'hi $NAME it is $(date)' | bashdown
|
||||||
# fetches a document and interprets bashsyntax in string (bashdown) templates
|
# fetches a document and interprets bashsyntax in string (bashdown) templates
|
||||||
# @param string - string with bash(down) syntax (usually surrounded by ' quotes instead of ")
|
# @param string - string with bash(down) syntax (usually surrounded by ' quotes instead of ")
|
||||||
bashdown(){
|
bashdown() {
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
line="$(eval "printf -- \"$( printf "%s" "$line" | sed 's/"/\\"/g')\"")";
|
line="$(eval "printf -- \"$( printf "%s" "$line" | sed 's/"/\\"/g')\"")";
|
||||||
echo -e "$line"
|
echo -e "$line"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
is_gui(){
|
# Detect if focused app is a terminal or a gui
|
||||||
|
is_gui() {
|
||||||
name="$(xprop -id "$(xdotool getwindowfocus)" WM_CLASS | cut -d'"' -f2 | tr '[:upper:]' '[:lower:]')"
|
name="$(xprop -id "$(xdotool getwindowfocus)" WM_CLASS | cut -d'"' -f2 | tr '[:upper:]' '[:lower:]')"
|
||||||
[[ "$name" =~ term|tilda|kitty|alacritty ]] && return 1
|
[[ "$name" =~ term|tilda|kitty|alacritty ]] && return 1
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
run(){
|
# Find the index of a string in a string
|
||||||
cd "${DIR}" || exit
|
strindex() {
|
||||||
|
x="${1%%$2*}"
|
||||||
|
[[ "$x" = "$1" ]] && echo -1 || echo "${#x}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move the cursor up or left
|
||||||
|
move_cursor() {
|
||||||
|
local key=$1
|
||||||
|
local count=$2
|
||||||
|
local keys="End "
|
||||||
|
if [[ $count -gt 0 ]]; then
|
||||||
|
until [ "$count" -eq 0 ]; do
|
||||||
|
keys+="${key} "
|
||||||
|
((count-=1))
|
||||||
|
done
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
xdotool key --delay 0.1 $keys
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
cd "${snippets_directory}" || exit
|
||||||
|
|
||||||
|
local current_clipboard cursor_line cursor_line_position cursor_line cursor_position cursor_line_lenght
|
||||||
|
current_clipboard=$(xsel -b)
|
||||||
|
|
||||||
# Use the filenames in the snippy directory as menu entries.
|
# Use the filenames in the snippy directory as menu entries.
|
||||||
# Get the menu selection from the user.
|
# Get the menu selection from the user.
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
FILE=$(find -L . -type f | grep -v '^\.$' | sed 's!\.\/!!' | grep -vE '\.git/|\.gitconfig|\.gitkeep' | ${MENU_ENGINE} ${MENU_ARGS} -p '❯ ')
|
snippet=$( find -L . -type f \
|
||||||
# just return if nothing was selected
|
| grep -v '^\.$' \
|
||||||
[[ -z "${FILE}" ]] && return 1
|
| sed 's!\.\/!!' \
|
||||||
|
| grep -vE '\.git/|\.gitconfig|\.gitkeep' \
|
||||||
|
| ${MENU_ENGINE} ${MENU_ARGS} -p '❯ ')
|
||||||
|
|
||||||
if [ -f "${DIR}/${FILE}" ]; then
|
# just return if nothing was selected
|
||||||
|
[[ -z "${snippet}" ]] && return 1
|
||||||
|
|
||||||
|
if [ -f "${snippets_directory}/${snippet}" ]; then
|
||||||
# Put the contents of the selected file into the paste buffer.
|
# Put the contents of the selected file into the paste buffer.
|
||||||
# don't parse file with the ##noparse header
|
# don't parse file with the ##noparse header
|
||||||
if grep -qE "^##noparse" "${FILE}"; then
|
if grep -qE "^##noparse" "${snippets_directory}/${snippet}"; then
|
||||||
content="$(tail -n +2 "${DIR}/${FILE}")"
|
content="$( tail -n +2 "${snippets_directory}/${snippet}" )"
|
||||||
else
|
else
|
||||||
content="$(bashdown < "${DIR}/${FILE}")"
|
content="$( bashdown < "${snippets_directory}/${snippet}" )"
|
||||||
fi
|
fi
|
||||||
printf "%s" "$content" > $TMPFILE
|
|
||||||
else [[ ${FILE} =~ ^$ ]]
|
# replace {clipboard} by the clipboard content
|
||||||
${FILE##*$} 2>/dev/null > $TMPFILE # execute as bashcommand
|
# use awk to handle correctly multiline clipboard
|
||||||
|
printf "%s" "$content" | awk \
|
||||||
|
-v clipboard="$current_clipboard" \
|
||||||
|
-v placeholder="$clipboard_placeholder" \
|
||||||
|
'{ gsub(placeholder, clipboard); print }' > "$tmpfile"
|
||||||
|
|
||||||
|
else [[ ${snippet} =~ ^$ ]]
|
||||||
|
${snippet##*$} 2>/dev/null > "$tmpfile" # execute as bashcommand
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local current_clip cursor
|
# define cursor position and line at 0, we don't need to go up or left if there is no {cursor} placeholder
|
||||||
current_clip=$(xsel -b)
|
cursor_line_position=0
|
||||||
# define cursor at 0, we don't need to go up if there is no {cursor}
|
cursor_position=0
|
||||||
cursor=0
|
|
||||||
|
|
||||||
# replace {clipboard} by the cliboard comtent
|
|
||||||
sed -i -e "s~{clipboard}~${current_clip}~g" "$TMPFILE"
|
|
||||||
|
|
||||||
# Check if there is a {cursor} placeholder
|
# Check if there is a {cursor} placeholder
|
||||||
if grep -qF '{cursor}' "$TMPFILE";then
|
if grep -qF $cursor_placeholder "$tmpfile"; then
|
||||||
local file_lines
|
# retrieve the line number of the cursor placeholder
|
||||||
|
cursor_line_position=$(grep -n "$cursor_placeholder" "$tmpfile" | cut -d: -f1)
|
||||||
# retrieve the line
|
# retrieve the line
|
||||||
cursor=$(grep -n '{cursor}' "$TMPFILE" | cut -d: -f1)
|
cursor_line=$(grep $cursor_placeholder "$tmpfile")
|
||||||
# calculate snippet total lines
|
# calculate snippet total lines
|
||||||
file_lines=$(wc -l < "$TMPFILE")
|
file_lines=$(wc -l < "$tmpfile")
|
||||||
# determine the number of line to go
|
# determine the number of line to go up
|
||||||
cursor=$((file_lines - cursor))
|
cursor_line_position=$(( file_lines - cursor_line_position + 1 ))
|
||||||
|
# Extract cursor position
|
||||||
|
cursor_position=$(strindex "$cursor_line" $cursor_placeholder)
|
||||||
|
# total cursor line lenght
|
||||||
|
cursor_line_lenght=${#cursor_line}
|
||||||
|
# Compute the final cursor position ( 8 is the lenght of the placeholder {cursor} )
|
||||||
|
cursor_position=$(( cursor_line_lenght - cursor_position - 8 ))
|
||||||
# remove the placeholder
|
# remove the placeholder
|
||||||
sed -i -e "s/{cursor}//g" "$TMPFILE"
|
sed -i -e "s/$cursor_placeholder//g" "$tmpfile"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
xsel -b --input < "$TMPFILE"
|
# Copy snippet in clipboard
|
||||||
|
xsel -b --input < "$tmpfile"
|
||||||
|
|
||||||
# Paste into the current application.
|
# Paste into the current application.
|
||||||
if is_gui; then
|
if is_gui; then
|
||||||
${GUIPASTE}
|
# We need a little pause to handle the time to focus on the window
|
||||||
|
sleep 0.1
|
||||||
|
|
||||||
if [[ $cursor -gt 0 ]]; then
|
${GUIPASTE}
|
||||||
local keyup
|
move_cursor "Up" $cursor_line_position
|
||||||
until [ $cursor -eq 0 ]; do
|
|
||||||
keyup+="Up "
|
|
||||||
((cursor-=1))
|
|
||||||
# shellcheck disable=SC2086
|
|
||||||
xdotool key --delay -0.1 $keyup
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
${CLIPASTE}
|
${CLIPASTE}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -ne "$current_clip" | xsel -b --input
|
move_cursor "Left" $cursor_position
|
||||||
|
|
||||||
|
# Restore current clipboard
|
||||||
|
echo -ne "$current_clipboard" | xsel -b --input
|
||||||
}
|
}
|
||||||
|
|
||||||
run
|
run
|
||||||
|
Loading…
Reference in New Issue
Block a user