#!/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 cache: # BEMOJI_CACHE_LOCATION=/path/to/my/recents/directory bm_cache_dir="${BEMOJI_CACHE_LOCATION:-${XDG_CACHE_HOME:-$HOME/.cache}}" bm_history_file="${bm_cache_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 ] [-p] [-P] [-D ]" 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 Choose from default lists to download." echo " Valid choices: all|none|emoji|math (multiple choices possible)." echo " -f 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 "$BEMOJI_DOWNLOAD_LIST" ]; then # Populate default lists if echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'none'; then printf "Not downloading a default emoji list.\n" return elif echo "$BEMOJI_DOWNLOAD_LIST" | grep -q -e 'all'; then dl_default_emoji dl_math_symbols return else if 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 fi if [ -n "$(find "$bm_db_location" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then dl_default_emoji fi } dl_default_emoji() { local emojis emojis=$(curl -sSL "https://unicode.org/Public/emoji/14.0/emoji-test.txt") printf "%s" "$emojis" | sed -ne 's/^.*; fully-qualified.*# \(\S*\) \S* \(.*$\)/\1 \2/gp' >"$bm_db_location/emojis.txt" printf "Downloaded default emoji set.\n" } dl_math_symbols() { curl -sSL "https://unicode.org/Public/math/latest/MathClassEx-15.txt" | grep -ve '^#' | cut -d';' -f3,7 | sed -e 's/;/ /' >"$bm_db_location/math.txt" printf "Downloaded math symbols set.\n" } 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_cache_dir" ]; then mkdir -p "$bm_cache_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