结合ivy来使用bm-bookmarks

我一直都在用 bm 来在多个buffer中的固定位置进行跳转。

最开始的时候,是直接使用bm提供的show,next等函数来进行跳转。

然后我发现了 helm-bm 可以利用helm来进行交互式的选择。

但是,我发现其实这样的效率也不是特别高。毕竟有时候,如果bm的bookmark太多时,只看那一行的内容是不太好确定跳转过去的是自己需要的位置。

然后我发现swiper-all这个函数,可以在搜索buffer的字符的时候,提供预览的功能。我感觉这个功能很实用。

网上搜了一下,在 reddit 上有人实现了一个简单的版本。 只是这个版本和helm-bm一样,没有提供预览的功能。于是我在它的基础上, 添加了update-fn ,这样就可以在选择的时候先查看一下是否需要跳到这里:

(defun bm-counsel-get-list (bookmark-overlays)
  (-map (lambda (bm)
          (with-current-buffer (overlay-buffer bm)
            (let* ((line (replace-regexp-in-string "\n$" "" (buffer-substring (overlay-start bm)
                                                                              (overlay-end bm))))
                   ;; line numbers start on 1
                   (line-num (+ 1 (count-lines (point-min) (overlay-start bm))))
                   (name (format "%s:%d - %s" (buffer-name) line-num line)))

              `(,name . ,bm))))
        bookmark-overlays))

(defun counsel-bm-update-input ()
  "Update fn for counsel-bm."
  (with-ivy-window
    (when (> (length (ivy-state-current ivy-last)) 0)
      (let* ((chosen (ivy-state-current ivy-last))
             (bookmark (gethash chosen bm-hash-table)))
        (if chosen
            (save-restriction
              (with-ivy-window
                (switch-to-buffer (overlay-buffer bookmark))
                (bm-goto bookmark)))
          nil)))))

(defun counsel-bm (&optional initial-input)
  "Use ivy to select bm bookmarks.
It has the ability to preview the bookmarks like `swiper-all'."
  (interactive)
  (let* ((bm-list (bm-counsel-get-list (bm-overlays-lifo-order t)))
         (bm-hash-table (make-hash-table :test 'equal))
         (search-list (-map (lambda (bm) (car bm)) bm-list)))

    (-each bm-list (lambda (bm)
                     (puthash (car bm) (cdr bm) bm-hash-table)))

    (if search-list
        (ivy-read "Find bookmark: "
                  search-list
                  :keymap counsel-describe-map

                  :action (lambda (chosen)
                            (let ((bookmark (gethash chosen bm-hash-table)))
                              (switch-to-buffer (overlay-buffer bookmark))
                              (bm-goto bookmark)))

                  :update-fn #'counsel-bm-update-input

                  :initial-input initial-input
                  :caller 'counsel-bm
                  )
      (message "%s" "No bookmark now."))))

(defun counsel-bm-from-isearch ()
  "Invoke `counsel-bmr' from isearch."
  (interactive)
  (let ((query (if isearch-regexp
                   isearch-string
                 (regexp-quote isearch-string))))
    (isearch-exit)
    (counsel-bm query)))

(provide 'counsel-bm)

还有一个counsel-bm-from-isearch,可以在isearch搜索的时候,或者完成isearch搜索后调用。我是把它绑定到了isearch-mode-map上:

(define-key isearch-mode-map (kbd "<M-return>") 'counsel-bm-from-isearch)

设置好bm的bookmark后,直接使用counsel-bm就可以了。效果如下图:

counsel-bm-example
Counsel Bm Example

如果你觉得本文不错,欢迎 donate