# Emacs-based bookmark system # Enable color utilities autoload colors && colors __bm_bookmark_cache=() function __bm_find_user_emacs_dir { [[ -f "${HOME}/.emacs.d/var/bookmark-default.el" ]] && { printf '%s' "${HOME}/.emacs.d/var/bookmark-default.el"; return 0 } [[ -f "${HOME}/.emacs.d/bookmark-default.el" ]] && { printf '%s' "${HOME}/.emacs.d/bookmark-default.el"; return 0 } [[ -f "${HOME}/.config/emacs/var/bookmark-default.el" ]] && { printf '%s' "${HOME}/.config/emacs/var/bookmark-default.el"; return 0 } [[ -f "${HOME}/.config/emacs/bookmark-default.el" ]] && { printf '%s' "${HOME}/.config/emacs/bookmark-default.el"; return 0 } [[ -f "${XDG_CONFIG_HOME}/emacs/var/bookmark-default.el" ]] && { printf '%s' "${XDG_CONFIG_HOME}/emacs/var/bookmark-default.el"; return 0 } [[ -f "${XDG_CONFIG_HOME}/emacs/bookmark-default.el" ]] && { printf '%s' "${XDG_CONFIG_HOME}/emacs/bookmark-default.el"; return 0 } printf 'Could not discover Emacs bookmark file! Please set $BM_MODE to "daemon" or define $BM_BOOKMARK_PATH!\n' return 1 } function __bm_update_bookmark_list { local quoted_output case "${BM_MODE}" in 'daemon') quoted_output=(${(z)${"$(command emacsclient --eval \ "(let ((out)) (dolist (entry bookmark-alist out) (let ((path (alist-get 'filename (cdr entry) "")) (pos (alist-get 'position (cdr entry) 1))) (setq out (append (list (car entry) path (expand-file-name path) pos) out)))))")":1:-1}}) ;; ''|'emacs') if ! [[ -v BM_BOOKMARK_PATH ]]; then local BM_BOOKMARK_PATH="$(__bm_find_user_emacs_dir)" fi quoted_output=(${(z)${"$(command emacs -Q --batch --eval \ "(prin1 (with-temp-buffer (insert-file-contents \"${BM_BOOKMARK_PATH:gs#\\#\\\\#:gs#\"#\\\"#}\") (require 'cl-lib) (let ((out)) (dolist (entry (read (current-buffer)) out) (let ((path (alist-get 'filename (cdr entry) "")) (pos (alist-get 'position (cdr entry) 1))) (setq out (append (list (car entry) path (expand-file-name path) pos) out)))))))))")":1:-1}}) ;; *) printf 'Unknown value for $BM_MODE: "%s"\n' "${BM_MODE}" return 1 ;; esac __bm_bookmark_cache=() for entry in ${quoted_output}; do __bm_bookmark_cache+="${(Q)entry}" done } function __bm_bookmark_location { for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do if [[ "${1}" = "${__bm_bookmark_cache[${i}]}" ]]; then printf '%s\n%d\n' "${__bm_bookmark_cache[${i} + 2]}" \ "${__bm_bookmark_cache[${i} + 3]}" return 0 fi done return 1 } function __bm_list_bookmarks { for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do local name="${__bm_bookmark_cache[${i}]}" local pretty_path="${__bm_bookmark_cache[${i} + 1]}" local abs_path="${__bm_bookmark_cache[${i} + 2]}" local print_color="" if [[ -d "${abs_path}" ]]; then print_color="${bold_color}${fg[blue]}" fi printf "${print_color}%s${reset_color} => %s\n" \ "${name}" "${pretty_path}" done } function _bookmarks { for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do compadd "${__bm_bookmark_cache[${i}]}" done } alias lsbm="__bm_update_bookmark_list && __bm_list_bookmarks" function bm { __bm_update_bookmark_list || \ { printf 'Updating bookmark list failed!\n'; return 1 } (( ${#} == 0 )) && { __bm_list_bookmarks; return } local loc=(${(f)"$(__bm_bookmark_location "${1}")"}) if [[ -d "${loc[1]}" ]]; then cd "${loc[1]}" [[ "${BM_CWD_LS}" = '1' ]] && ls || true elif [[ -e "${loc[1]}" ]]; then ${=EDITOR} "${loc[1]}" else printf 'No such bookmark: "%s"\n' "${1}" return 1 fi } function _bm { _arguments ':bookmark:_bookmarks' } compdef _bm bm function bmadd { if [[ "${1}" = '-h' ]]; then printf 'usage: bmadd [PATH] [NAME]\n' return 0 fi [[ "${1}" = '--' ]] && shift 1 local name local loc case "${#}" in 0) loc="${PWD}" name="${PWD:t}" ;; 1) loc="${1}" name="${1:t}" ;; *) loc="${1}" name="${2}" ;; esac __bm_update_bookmark_list if __bm_bookmark_location "${name}" >/dev/null; then printf 'Bookmark "%s" already exists. Overwrite it? [y/N] ' "${name}" read -q let ans=${?} printf '\n' (( ${ans} != 0 )) && return 1 fi local res="$(emacsclient --eval \ "(let* ((loc \"${loc:gs#\\#\\\\#:gs#\"#\\\"#}\") (name \"${name:gs#\\#\\\\#:gs#\"#\\\"#}\") (res (with-temp-buffer (set-visited-file-name loc t nil) (bookmark-set name) (set-buffer-modified-p nil))) (inhibit-message t)) (bookmark-save) res)")" [[ "${res}" = 'nil' ]] && printf 'Added bookmark "%s"\n' "${name}" \ || { printf '%s\n' "${res}"; return 1 } } function _bmadd { _arguments ':file:_files' ':name' } compdef _bmadd bmadd function bmrm { if [[ "${1}" = '-h' ]]; then printf 'usage: bmrm [NAME]\n' return 0 fi [[ "${1}" = '--' ]] && shift 1 if (( ${#} < 1 )); then printf 'usage: bmrm [NAME]\n' return 1 fi __bm_update_bookmark_list __bm_bookmark_location "${1}" >/dev/null || \ { printf 'No such bookmark: "%s"\n' "${1}"; return 1 } printf 'Really delete "%s"? [y/N] ' "${1}" if read -q; then printf '\n' local res="$(emacsclient --eval \ "(let* ((res (bookmark-delete \"${1:gs#\\#\\\\#:gs#\"#\\\"#}\")) (inhibit-message t)) (bookmark-save) res)")" [[ "${res}" = 'nil' ]] && printf 'Deleted bookmark "%s"\n' "${1}" \ || { printf '%s\n' "${res}"; return 1 } else printf '\n' return 1 fi } function _bmrm { _arguments ':bookmark:_bookmarks' } compdef _bmrm bmrm __bm_update_bookmark_list