使用 hydra 实现简单版本的 keychord

hydra-wiki 上给出了使用 xf 调用 find-file 的方法

我做了简单的扩展:

(require 'hydra)

(defvar comma-hydra-timer nil)
;; 退出 hydra 的延迟时间
(defvar comma-hydra-delay 0.5)
(defvar comma-hydra-char ",")
(defvar-local comma-hydra-last-buffer-undo-list nil)
(defun comma-hydra-quit (&optional no-hydra-quit)
  "退出hydra, 清空 timer"
  (when comma-hydra-timer
    (cancel-timer comma-hydra-timer)
    (setq comma-hydra-timer nil))
  (unless no-hydra-quit
    (hydra-keyboard-quit)))

(defun comma-hydra-pre ()
  (comma-hydra-quit t)
  (setq comma-hydra-timer (timer-create))
  ;; 暂时储存 `buffer-undo-list'
  (setq comma-hydra-last-buffer-undo-list buffer-undo-list)
  (hydra-set-property 'comma-hydra :verbosity 0)
  (unless buffer-read-only
    (insert comma-hydra-char))
  ;; 开始计时
  (timer-set-time comma-hydra-timer
                  (timer-relative-time (current-time) comma-hydra-delay))
  (timer-set-function comma-hydra-timer 'comma-hydra-quit)
  (timer-activate comma-hydra-timer))

(defmacro comma-hydra-lambda-body ($cmd-or-keys)
  `(progn
     (unless buffer-read-only
       (ignore-errors
         (let ((inhibit-message t))
           (undo)
           ;; 恢复 `buffer-undo-list'
           (setq buffer-undo-list comma-hydra-last-buffer-undo-list))))
     (comma-hydra-quit t)
     ,(if (functionp $cmd-or-keys)
          `(call-interactively #',$cmd-or-keys)
        `(setq unread-command-events
               ',(mapcar (lambda (c) (cons t c))
                          (listify-key-sequence (kbd $cmd-or-keys)))))))

(defmacro comma-hydra-define ($char &rest $binds)
  (declare (indent 1))
  (let (form1 form2)
    (dolist (bind $binds)
      (let* ((key (car bind))
             (def (cadr bind))
             (fn (intern (format "comma-hydra/lambda-%s-and-exit" key))))
        (push `(,key (comma-hydra-lambda-body ,def)) form1)
        (push `(define-key map ,key ',def) form2)))
    `(progn
       (setq comma-hydra-char ,$char)
       (defhydra comma-hydra (:body-pre comma-hydra-pre :color blue)
         ,@form1)
       (setq comma-hydra/keymap
             (let ((map (make-sparse-keymap)))
               ,@form2
               map)))))

(defun comma-hydra-invoker ()
  (interactive)
  (if (or (bound-and-true-p iedit-mode)
          (bound-and-true-p multiple-cursors-mode)
          defining-kbd-macro
          executing-kbd-macro)
      (unless buffer-read-only (insert comma-hydra-char))
    (call-interactively 'comma-hydra/body)))

之后使用 `comma-hydra-define’

(comma-hydra-define ","
  ;; 快速输入 `,f' 调用 find-file, 否则插入 `,f'
  ("f" find-file)
  ;; 快速输入 `,p' 相当于输入了 "C-c p"
  ("p" "C-c p"))

(global-set-key "," #'comma-hydra-invoker)
2 个赞