# Emacs-based bookmark system # Enable color utilities autoload colors && colors autoload regexp-replace __bm_bookmark_cache=() local __bm_res=() 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 emacs -Q --batch --eval \ "(prin1 (progn (require 'server) (server-eval-at \"server\" '(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 { local parts=(${(s:/:)"${1}"}) local bm_name="${parts[1]}" local rest_arr=(${parts:1}) local rest="${(j:/:)rest_arr}" for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do if [[ "${bm_name}" = "${__bm_bookmark_cache[${i}]}" ]]; then __bm_res=("${__bm_bookmark_cache[${i} + 2]}" "${__bm_bookmark_cache[${i} + 3]}" "${rest}") return 0 fi done __bm_res=() 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 -q -S '/' "${__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 } __bm_bookmark_location "${1}" local bm_loc="${__bm_res[1]}" local target="${__bm_res[1]}/${__bm_res[3]}" if (( ${#__bm_res} != 0 )) && [[ -e "${bm_loc}" ]]; then if [[ -d "${target}" ]]; then cd "${target}" [[ "${BM_CWD_LS}" = '1' ]] && ls || true elif [[ -e "${target}" ]]; then ${=EDITOR} "${target}" else printf 'Bookmark exists, but trailing path doesn'"'"'t: "%s"\n' \ "${(q)__bm_res[3]}" return 1 fi else printf 'No such bookmark: "%s"\n' "${(q)1}" return 1 fi } function _bm { local arg="${(Q)words[${CURRENT}]}" if ! [[ "${arg}" == */* ]]; then _arguments '1::bookmark:_bookmarks' else __bm_bookmark_location "${arg}" if [[ -d "${__bm_res[1]}" ]]; then local parts=(${(s:/:)__bm_res[3]}) local bm="${${(s:/:)${arg}}[1]}" local subdir="${(j:/:)parts[1,-2]}" local search="${parts[${#parts}]}" if ((${#parts} > 0)) && [[ "${arg}" == */ ]]; then subdir="${subdir}/${search}" subdir="${subdir#/}" search="" fi local pre_path="${__bm_res[1]}/${subdir}/" local raw_arg="${words[${CURRENT}]}" local prefix="${${(s:/:)${raw_arg}}[1]}/${subdir}" if ! [[ -z "${subdir}" ]]; then prefix+='/' fi compset -P "${(b)prefix}" for file in "${pre_path}${search}"*; do compadd -W "${pre_path}" -f "${file:t}" done fi fi } 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] ' "${(q)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' "${(q)name}" \ || { printf '%s\n' "${res}"; return 1 } __bm_update_bookmark_list } 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] ' "${(q)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' "${(q)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