joplin-snippy/snippy
BarbUk f0a5ab1273 Handle clipboard replace only for snippet with a clipboard placeholder
Current clipboard is saved after bashdown template.
Bashdown command can now work with the clipboard
2018-12-27 15:38:55 +04:00

195 lines
6.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/bash
# video demo at: http://www.youtube.com/watch?v=90xoathBYfk
# augmented by barbuk: https://github.com/BarbUk/dotfiles/blob/master/bin/snippy
# . restore current clipboard
# . {clipboard} placeholder to use current clipboard in snippet
# . {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
# . execute command begining by $
#
# augmented by "opennomad": https://gist.github.com/opennomad/15c4d624dce99066a82d
# originally written by "mhwombat": https://bbs.archlinux.org/viewtopic.php?id=71938&p=2
# Based on "snippy" by "sessy"
# (https://bbs.archlinux.org/viewtopic.php?id=71938)
#
# You will also need "dmenu", "zenity", "xsel", "sponge" and "xdotool". Get them from your linux
# distro in the usual way.
#
# To use:
# 1. Create the directory ~/.snippy
#
# 2. Create a file in that directory for each snippet that you want.
# The filename will be used as a menu item, so you might want to
# omit the file extension when you name the file.
#
# TIP: If you have a lot of snippets, you can organise them into
# subdirectories under ~/.snippy.
#
# TIP: The contents of the file will be pasted asis, so if you
# don't want a newline at the end when the text is pasted, don't
# put one in the file.
#
# 3. Bind a convenient key combination to this script.
#
# TIP: If you're using XMonad, add something like this to xmonad.hs
# ((mod4Mask, xK_s), spawn "/path/to/snippy")
#
# 4. Set variables in ~/.snippy.conf. for example
# MENU_ENGINE="dmenu"
# DMENU_ARGS=' -p snippy -fn arial -sf green'
# ZENITY_ARGS='--list --hide-header --column=Snippet --text=snippy'
# GUIPASTE="xdotool key ctrl+v"
# CLIPASTE="xdotool key ctrl+shift+v"
readonly snippets_directory=${HOME}/.snippy
readonly MENU_ENGINE="rofi"
readonly DMENU_ARGS='-dmenu -i -sort'
# if nothing happens, try "xdotool click 2", "xdotool key ctrl+v" or "xdotool key ctrl+shift+v"
readonly GUIPASTE="xdotool key ctrl+v"
readonly CLIPASTE="xdotool key ctrl+shift+v"
readonly MENU_ARGS=${DMENU_ARGS}
readonly cursor_placeholder="{cursor}"
readonly clipboard_placeholder="{clipboard}"
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.)
# @link http://github.com/coderofsalvation/bashdown
# @dependancies: sed cat
# @example: echo 'hi $NAME it is $(date)' | bashdown
# fetches a document and interprets bashsyntax in string (bashdown) templates
# @param string - string with bash(down) syntax (usually surrounded by ' quotes instead of ")
bashdown() {
while IFS= read -r line; do
line="$(eval "printf -- \"$( printf "%s" "$line" | sed 's/"/\\"/g')\"")";
echo -e "$line"
done
}
# 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" =~ term|tilda|kitty|alacritty ]] && return 1
return 0
}
# Find the index of a string in a string
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
# Use the filenames in the snippy directory as menu entries.
# Get the menu selection from the user.
# shellcheck disable=SC2086
snippet=$( find -L . -type f \
| grep -v '^\.$' \
| sed 's!\.\/!!' \
| grep -vE '\.git/|\.gitconfig|\.gitkeep' \
| ${MENU_ENGINE} ${MENU_ARGS} -p ' ')
# 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.
# don't parse file with the ##noparse header
if grep -qE "^##noparse" "${snippets_directory}/${snippet}"; then
content="$( tail -n +2 "${snippets_directory}/${snippet}" )"
else
content="$( bashdown < "${snippets_directory}/${snippet}" )"
fi
printf "%s" "$content" > "$tmpfile"
else [[ ${snippet} =~ ^$ ]]
${snippet##*$} 2>/dev/null > "$tmpfile" # execute as bashcommand
fi
# save current clipboard
current_clipboard=$(xsel -b)
# replace {clipboard} by the clipboard content
# use awk to handle correctly multiline clipboard
if grep -q "$clipboard_placeholder" "$tmpfile"; then
awk \
-v clipboard="$current_clipboard" \
-v placeholder="$clipboard_placeholder" \
'{ gsub(placeholder, clipboard); print }' "$tmpfile" | sponge "$tmpfile"
# remove last EOL
perl -pi -e 'chomp if eof' "$tmpfile"
fi
# define cursor position and line at 0, we don't need to go up or left if there is no {cursor} placeholder
cursor_line_position=0
cursor_position=0
# Check if there is a {cursor} placeholder
if grep -qF $cursor_placeholder "$tmpfile"; then
# retrieve the line number of the cursor placeholder
cursor_line_position=$(grep -n "$cursor_placeholder" "$tmpfile" | cut -d: -f1)
# retrieve the line
cursor_line=$(grep $cursor_placeholder "$tmpfile")
# calculate snippet total lines
file_lines=$(wc -l < "$tmpfile")
# determine the number of line to go up
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
sed -i -e "s/$cursor_placeholder//g" "$tmpfile"
fi
# Copy snippet in clipboard
xsel -b --input < "$tmpfile"
# Paste into the current application.
if is_gui; then
# We need a little pause to handle the time to focus on the window
sleep 0.1
${GUIPASTE}
move_cursor "Up" $cursor_line_position
else
${CLIPASTE}
fi
move_cursor "Left" $cursor_position
# Restore current clipboard
echo -ne "$current_clipboard" | xsel -b --input
}
run