helpful.el 和我的配置


#1

helpful.el 应该不少人用,比自带的help功能强很多。唯一的痛点是,helpful不像help那样,永远只用一个window,并且还有back/forward按钮;而是每次都创建一个新buffer并占用一个新window,back/forward也没有。

我搞了一下,现在helpful只占用一个window并且顶端有back/forward按钮:

(use-package helpful
  :commands (helpful-callable
             helpful-variable
             helpful-key
             helpful-at-point)
  :config
  (setq helpful-max-buffers 5)
  ;; don't pop new window
  (setq helpful-switch-buffer-function
        (lambda (buf) (if-let ((window (display-buffer-reuse-mode-window buf '((mode . helpful-mode)))))
                          ;; ensure the helpful window is selected for `helpful-update'.
                          (select-window window)
                        ;; line above returns nil if no available window is found
                        (pop-to-buffer buf))))
  (defvar moon-helpful-history () "History of helpful, a list of buffers.")
  (advice-add #'helpful-update :around #'moon-helpful@helpful-update)
  (advice-add #'helpful--buffer :around (lambda (oldfunc &rest _)
                                          (let ((buf (apply oldfunc _)))
                                            (push buf moon-helpful-history)
                                            buf))))

(defun moon-helpful@helpful-update (oldfunc)
  "Insert back/forward buttons."
  (funcall oldfunc)
  (let ((inhibit-read-only t))
    (goto-char (point-min))
    (insert-text-button "Back"
                        'action (lambda (&rest _)
                                  (interactive)
                                  (moon-helpful-switch-to-buffer (current-buffer) 1)))
    (insert " / ")
    (insert-text-button "Forward"
                        'action (lambda (&rest _)
                                  (interactive)
                                  (moon-helpful-switch-to-buffer (current-buffer)  -1)))
    (insert "\n\n")))

(defun moon-helpful-switch-to-buffer (buffer &optional offset)
  "Jump to last SYMBOL in helpful history, offset by OFFSET."
  (interactive)
  (require 'seq)
  (require 'cl-lib)
  (setq moon-helpful-history (seq-remove (lambda (buf) (not (buffer-live-p buf))) moon-helpful-history))
  (cl-labels ((find-index (elt lst)
                          (let ((idx 0)
                                (len (length lst)))
                            (while (and (not (eq elt (nth idx lst)))
                                        (not (eq idx len)))
                              (setq idx (1+ idx)))
                            (if (eq idx len)
                                nil
                              idx))))
    (let ((idx (+ (or offset 0) (find-index buffer moon-helpful-history))))
      (if (or (>= idx (length moon-helpful-history))
              (< idx 0))
          (message "No further history.")
        (switch-to-buffer (nth idx moon-helpful-history))))))

#2

它能找到这个函数的所有调用非常有用(比如了解某个参数的使用方法),我比较喜欢用 El-search 达到这个目的,虽然需要自己输入麻烦些,但更酷、更灵活,比如查询所有叫 use-package 安装的包

(l ^ use-package __ :ensure)

#3

原来help-mode的设置长这样:

(use-package help-mode
  :config
  (bind-keys :map help-mode-map
             ("," . beginning-of-buffer)
             ("." . end-of-buffer)
             ("n" . next-line)
             ("p" . previous-line)
             ("h" . backward-char)
             ("l" . forward-char)
             ("j" . next-line)
             ("k" . previous-line)
             ("f" . help-go-forward)
             ("b" . help-go-back)))

参考大佬的配置加了个helpful的:

(use-package helpful
  :ensure t
  :preface
  (defun helpful-at-point-dwim ()
    (interactive)
    (let ((symbol (symbol-at-point)))
      (if symbol (helpful-symbol symbol)
        (call-interactively #'helpful-symbol))))

  :bind (([remap describe-variable] . helpful-variable)
         ([remap describe-function] . helpful-callable)
         ([remap describe-key] . helpful-key)
         :map emacs-lisp-mode-map
         ("C-c C-d" . helpful-at-point-dwim))

  :init
  (setq helpful-max-buffers 10)

  :config
  (defun helpful-reuse-window-function (buf)
    (if-let ((window (display-buffer-reuse-mode-window buf '((mode . helpful-mode)))))
        (select-window window)
      (pop-to-buffer buf)))
  (setq helpful-switch-buffer-function #'helpful-reuse-window-function)

  (defun helpful-previous-helpful-buffer ()
    (interactive)
    (let ((bufname (buffer-name)))
      (previous-buffer)
      (while (and (not (eq major-mode 'helpful-mode))
                  (not (string= (buffer-name) bufname)))
        (previous-buffer))))

  (defun helpful-next-helpful-buffer ()
    (interactive)
    (let ((bufname (buffer-name)))
      (next-buffer)
      (while (and (not (eq major-mode 'helpful-mode))
                  (not (string= (buffer-name) bufname)))
        (next-buffer))))

  (bind-keys :map helpful-mode-map
             ("," . beginning-of-buffer)
             ("." . end-of-buffer)
             ("b" . helpful-previous-helpful-buffer)
             ("f" . helpful-next-helpful-buffer)
             ("q" . delete-window)))

buffer的数量我是懒得理了。。反正都已经那么多。。


#4

helpful会顯示Emacs的C source code。因爲用到了cc-mode,觸發了我的c-mode-hook裏的(lsp)開啓了language server。有辦法禁止開language server嗎?


#5

感觉可以let-bound cc-mode-hook


#6

会么?我这里不会,试试下面这个

(add-hook 'c-mode-hook
          (defun foo ()
            (message-box "calling from c-mode-hook...")))

(helpful-symbol 'car)

实际上 helpful.el 已经禁用了 mode hook


#7

虽然没试过,但是感觉这个命令只能在最近的两个buffer之间跳转。因为你每次切换bufffer以后,切换到的的buffer都会被移到buffer-list的开头,这时候previous-buffer只会回到之前的buffer,而不是历史上的第三个。


试了一下,没有问题,是我傻了。previous-buffer这么智能的吗 otz

我把我的函数换成你的了,另外q我绑的是delete-window


#8

如果保留多个buffer的话,是只能delete window,不然关不掉~

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

改了一下,如果只有一个window,delete-window会删不掉:

(defun helpful-quit ()
    (interactive)
    (if (> (length (window-list)) 1)
        (delete-window)
      (bury-buffer)))

#9

大家用 helpful 慢不慢?


#10

慢,不过0.5到1秒还算可以


#11

不管有几个buffer历史,delete-window都应该直接关掉window的啊。