bemoji/bemoji
Marty Oehme 8beb28b2b9
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
🦊 BREAKING: Use new XDG Base specification for state (#5)
Switched the history from using the XDG_CACHE_HOME directory by default
to use XDG_STATE_HOME by default.

This makes sense since cache can (and should be prepared to) be wiped at
any moment and the program functionality should not be hindered by this.
Since we need to retain history through such wipes the newly introduced
state directory is the perfect match for keeping the history file in.

This does constitute a breaking change for existing histories which need
to be moved to the new directory if they made use of the old cache
directory.

Concurrent with this we are renaming `XDG_CACHE_LOCATION` environment
variable to `XDG_HISTORY_LOCATION` so this is a second breaking change
for those using a custom location for their histories.

This change attempts to make the naming scheme coherent and remove some
left-over naming cruft from the old location being the cache directory.

This provides one of the larger changes to the program so far.

Fixes #5.
2022-11-03 15:40:47 +01:00

227 lines
7 KiB
Bash
Executable file

#!/usr/bin/env bash
bm_version=0.2.0
# Emoji default database location
bm_db_location=${BEMOJI_DB_LOCATION:-"${XDG_DATA_HOME:-$HOME/.local/share}/bemoji"}
# Setting custom emoji list file location:
# BEMOJI_CUSTOM_LIST=/my/location/emojis.txt
# Setting custom recent emoji history:
# BEMOJI_HISTORY_LOCATION=/path/to/my/recents/directory
bm_state_dir="${BEMOJI_HISTORY_LOCATION:-${XDG_STATE_HOME:-$HOME/.local/state}}"
bm_history_file="${bm_state_dir}/bemoji-history.txt"
# Command to run after user chooses an emoji
bm_default_cmd="$BEMOJI_DEFAULT_CMD"
# Newline after echo
bm_echo_newline=${BEMOJI_ECHO_NEWLINE:-true}
# Do not save choices
bm_private_mode=${BEMOJI_PRIVATE_MODE:-false}
# Do not sort results
bm_ignore_recent=${BEMOJI_IGNORE_RECENT:-false}
# Report usage
usage() {
echo "Usage: $(basename "$0") [-t | -c | -e] [-f <filepath> ] [-p] [-P] [-D <choices>]" 1>&2
echo
echo "A simple emoji picker. Runs on bemenu/wofi/rofi/dmenu by default."
echo "Invoked without arguments sends the picked emoji to the clipboard."
echo
echo " Command options (can be combined):"
echo " -t Simulate typing the emoji choice with the keyboard."
echo " -c Send emoji choice to the clipboard. (default)"
echo " -e Only echo out the picked emoji."
echo ""
echo " Other options:"
echo " -n Do not print a newline after the picked emoji."
echo " -p Do not save picked emoji to recent history."
echo " -P Do not order emoji by recently used."
echo " -D <choice> Choose specific default lists to download if none found locally."
echo " Valid choices: all|none|emoji|math."
echo " -f <filepath> Use a custom emoji database. Can be a url which will be retrieved."
echo " -v Display current program version and directory configuration."
echo " -h Show this help."
echo
exit "$1"
}
version() {
printf "v%s\ndatabase=%s\nhistory=%s\n" "$bm_version" "$bm_db_location" "$bm_history_file"
exit
}
# Get Options
while getopts ":f:D:tcenpPhv" o; do
case "${o}" in
f) BEMOJI_CUSTOM_LIST="${OPTARG}" ;;
t) bm_cmds+=(_typer) ;;
c) bm_cmds+=(_clipper) ;;
e) bm_cmds+=(cat) ;;
n) bm_echo_newline=false ;;
D) BEMOJI_DOWNLOAD_LIST="${OPTARG}" ;;
p) bm_private_mode=true ;;
P) bm_ignore_recent=true ;;
h) usage 0 ;;
v) version ;;
*) usage 1 ;;
esac
done
prepare_db() {
# Create list directory
if [ ! -d "$bm_db_location" ]; then
mkdir -p "$bm_db_location"
fi
if [ -n "$(find "$bm_db_location" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
# Populate default lists
if echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'none'; then
printf "No emoji list found, but set to not download any default lists."
exit 1
elif echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'all'; then
dl_default_emoji
dl_math_symbols
return
fi
if [ -z "$BEMOJI_DOWNLOAD_LIST" ] || echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'emoji'; then
dl_default_emoji
fi
if echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'math'; then
dl_math_symbols
fi
fi
}
dl_default_emoji() {
curl -sSL "https://unicode.org/Public/emoji/14.0/emoji-test.txt" |
sed -ne 's/^.*; fully-qualified.*# \(\S*\) \S* \(.*$\)/\1 \2/gp' >"$bm_db_location/emojis.txt"
printf "Downloaded default emoji set."
}
dl_math_symbols() {
curl -sSL "https://unicode.org/Public/math/latest/MathClassEx-15.txt" |
grep -ve '^#' | cut -d';' -f3,7 --output-delimiter=' ' >"$bm_db_location/math.txt"
printf "Downloaded math symbols set."
}
gather_emojis() {
if [ -n "$BEMOJI_CUSTOM_LIST" ] && [ -f "$BEMOJI_CUSTOM_LIST" ]; then
result=$(cat "$BEMOJI_CUSTOM_LIST")
elif [ -n "$BEMOJI_CUSTOM_LIST" ] && curl -fsSI "$BEMOJI_CUSTOM_LIST" >/dev/null 2>&1; then
result=$(curl -sSL "$BEMOJI_CUSTOM_LIST")
else
result=$(cat "$bm_db_location"/*.txt)
fi
if [ "$bm_ignore_recent" = true ]; then
printf "%s" "$result"
else
printf "%s\n%s" "$(get_most_recent)" "$result" | cat -n - | sort -uk2 | sort -n | cut -f2-
fi
}
get_most_recent() {
recent_file="$bm_history_file"
if [ ! -f "$recent_file" ]; then
touch "$recent_file"
fi
# TODO improve this messy line
sed -e '/^$/d' "$recent_file" |
sort |
uniq -c |
sort -rn |
sed -e 's/^\s*//' |
cut -d' ' -f2-
}
add_to_recent() {
if [ -z "$1" ]; then return; fi
if [ ! -d "$bm_state_dir" ]; then
mkdir -p "$bm_state_dir"
fi
echo "$1" >>"$bm_history_file"
}
# Set default clipboard util
_clipper() {
if [ -n "$BEMOJI_CLIP_CMD" ]; then
# shellcheck disable=SC2068
${BEMOJI_CLIP_CMD[@]}
elif [ -n "$WAYLAND_DISPLAY" ] && command -v wl-copy >/dev/null 2>&1; then
wl-copy
elif [ -n "$DISPLAY" ] && command -v xclip >/dev/null 2>&1; then
xclip -selection clipboard
elif [ -n "$DISPLAY" ] && command -v xsel >/dev/null 2>&1; then
xsel -b
else
printf "No suitable clipboard tool found."
exit 1
fi
}
# Set default typing uti
_typer() {
totype=$(cat -)
if [ -n "$BEMOJI_TYPE_CMD" ]; then
# shellcheck disable=SC2068
${BEMOJI_TYPE_CMD[@]}
elif [ -n "$WAYLAND_DISPLAY" ] && command -v wtype >/dev/null 2>&1; then
wtype -s 30 "$totype"
elif [ -n "$DISPLAY" ] && command -v xdotool >/dev/null 2>&1; then
xdotool type --delay 30 "$totype"
else
printf "No suitable typing tool found."
exit 1
fi
}
# Set default picker util
_picker() {
if [ -n "$BEMOJI_PICKER_CMD" ]; then
# shellcheck disable=SC2068
${BEMOJI_PICKER_CMD[@]}
elif command -v bemenu >/dev/null 2>&1; then
bemenu -p 🔍 -i -l 20
elif command -v wofi >/dev/null 2>&1; then
wofi -p 🔍 -i --show dmenu
elif command -v rofi >/dev/null 2>&1; then
rofi -p 🔍 -i -dmenu --kb-custom-1 "Alt+1" --kb-custom-2 "Alt+2"
elif command -v dmenu >/dev/null 2>&1; then
dmenu -p 🔍 -i -l 20
else
printf "No suitable picker tool found."
exit 1
fi
}
[ -n "$BEMOJI_CUSTOM_LIST" ] || prepare_db
result=$(gather_emojis | _picker)
exit_value="$?"
[ "$bm_private_mode" = true ] || add_to_recent "$result"
result=$(echo "$result" | grep -o '^\S\+' | tr -d '\n')
case "$exit_value" in
1)
exit
;;
0)
if [ ${#bm_cmds[@]} -eq 0 ]; then
if [ -n "$bm_default_cmd" ]; then
# shellcheck disable=SC2068
echo "$result" | ${bm_default_cmd[@]}
exit
fi
bm_cmds+=(_clipper)
fi
for cmd in "${bm_cmds[@]}"; do
[ "$bm_echo_newline" = true ] && echo_opts= || echo_opts=-n
echo $echo_opts "$result" | "$cmd"
done
;;
10)
echo "$result" | _clipper
;;
11)
echo "$result" | _typer
;;
esac
exit