# Emacs-based bookmark system autoload colors && colors zmodload -F zsh/stat b:zstat zmodload zsh/datetime zmodload zsh/mapfile local __bm_bookmark_cache=() let __bm_last_read_time=-1 function __bm_offset_to_row_col { let off="${2}" let row=1 let col=0 for line in "${(@f)mapfile[${1}]}"; do let len="${#line}" if (( off > len )); then off='off - (len + 1)' if (( off <= 0 )); then (( len == 0 )) && col=0 || col='len - 1' break fi row+=1 else col='off' off=0 break; fi done <"${1}" if (( off > 0 )); then row+=-1 fi printf '%d\0%d' "${row}" "${col}" } 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 } BM_BOOKMARK_PATH="${BM_BOOKMARK_PATH:-"$(__bm_find_user_emacs_dir)"}" function __bm_hash_dirs { # First empty the hash table hash -dr # Then add the hash dirs for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do local name="${__bm_bookmark_cache[${i}]://=/}" hash -d "${name}=${__bm_bookmark_cache[${i} + 2]}" done } function __bm_update_bookmark_list { __bm_last_read_time="${EPOCHSECONDS}" local args local script case "${BM_MODE}" in 'daemon') script=$(<<'EOF' (progn (require 'server) (dolist (entry (server-eval-at "server" '(when (boundp 'bookmark-alist) bookmark-alist))) (let ((path (alist-get 'filename (cdr entry) "")) (pos (alist-get 'position (cdr entry) 1))) (princ (format "%s\0%s\0%s\0%s\0" (car entry) path (expand-file-name path) pos))))) EOF ) ;; ''|'emacs') args="--insert=${BM_BOOKMARK_PATH}" script=$(<<'EOF' (progn (dolist (entry (read (current-buffer))) (let ((path (alist-get 'filename (cdr entry) "")) (pos (alist-get 'position (cdr entry) 1))) (princ (format "%s\0%s\0%s\0%s\0" (car entry) path (expand-file-name path) pos))))) EOF ) ;; *) printf 'Unknown value for $BM_MODE: "%s"\n' "${BM_MODE}" return 1 ;; esac __bm_bookmark_cache=(${(0)"$(command emacs --batch ${args} --eval "${script}")"}) __bm_hash_dirs } function __bm_bookmark_location { local parts=(${(s:/:)"${2}"}) 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 "${__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 __bm_res __bm_bookmark_location __bm_res "${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:-0}" )) && ls || true elif [[ -e "${bm_loc}" ]]; then let offset="${__bm_res[2]}" local rowcol=(${(0)"$(__bm_offset_to_row_col "${bm_loc}" "${offset}")"}) ${=EDITOR} "+${rowcol[1]}:${rowcol[2]}" "${bm_loc}" 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 { (( "${CURRENT}" == 2 )) || return local arg="${(Q)words[${CURRENT}]}" if ! [[ "${arg}" == */* ]]; then for ((i = 1; i < ${#__bm_bookmark_cache}; i+=4)); do compadd -q -S '/' -- "${__bm_bookmark_cache[${i}]}" done else local __bm_res __bm_bookmark_location __bm_res "${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}"*; 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 local __bm_res if __bm_bookmark_location __bm_res "${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 local __bm_res __bm_bookmark_location __bm_res "${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 } __bm_update_bookmark_list else printf '\n' return 1 fi } function _bmrm { _arguments ':bookmark:_bookmarks' } compdef _bmrm bmrm function __bm_precmd_hook { # Auto reload if (( "${BM_AUTO_RELOAD:-0}" )) && (( ${__bm_last_read_time} < "$(zstat +mtime "${BM_BOOKMARK_PATH}")" )); then __bm_update_bookmark_list fi } (( ${precmd_functions[(I)__bm_precmd_hook]} )) || precmd_functions+=(__bm_precmd_hook) __bm_update_bookmark_list