123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- # 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}" == 'true' ]] && 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}" == true ]] &&
- (( ${__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
|