Hydra 是个好东西


#1

感觉有点像 Vim 中的 ”模式“ 的概念,不过是可以自定义的


#2

是的, 我就把我常用的快捷键全部都设置到hydra里面了


#3

感觉有点像 transient state


#4

spacemacs的transient state就是在hydra上加了点东西


#5

以前只是知道这个插件,一直没用过。昨天弄了下,网上不知什么解释不清楚,对我的实际影响就是一个比 which keys 更好看的插件。用了这个应该可以不用 which-keys 了吧。虽然也有其他人有一些两者区别的解释。


#6

org章节间跳转

with hydra: C-c C-p n p n n n p …

without hydra: 一直按着modifier,很累


#7

四处搜刮了下代码,通过hydra基本实现了evil的功能,对于只需要光标移动的我来说,已经足够了。

大家可以试用下,并欢迎各位大神提供建议。

(defun hydra-refresh-mode-line (&optional state)
  "Refresh mode line tag."
  (when (listp mode-line-format)
    (setq hydra-mode-line-tag (propertize state
					  'face 'eyebrowse-mode-line-active
					  'mouse-face 'mode-line-highlight))
    ;; refresh mode line data structure
    ;; first remove hydra from mode-line
    (setq mode-line-format (delq 'hydra-mode-line-tag mode-line-format))
    (let ((mlpos mode-line-format)
          pred which where)
      ;; determine before/after which symbol the tag should be placed
      (cond
       ((eq hydra-mode-line-format 'before)
        (setq where 'after which 'mode-line-position))
       ((eq hydra-mode-line-format 'after)
        (setq where 'after which 'mode-line-modes))
       ((consp hydra-mode-line-format)
        (setq where (car hydra-mode-line-format)
              which (cdr hydra-mode-line-format))))
      ;; find the cons-cell of the symbol before/after which the tag
      ;; should be placed
      (while (and mlpos
                  (let ((sym (or (car-safe (car mlpos)) (car mlpos))))
                    (not (eq which sym))))
        (setq pred mlpos
              mlpos (cdr mlpos)))
      ;; put hydra tag at the right position in the mode line
      (cond
       ((not mlpos)) ;; position not found, so do not add the tag
       ((eq where 'before)
        (if pred
            (setcdr pred (cons 'hydra-mode-line-tag mlpos))
          (setq mode-line-format
                (cons 'hydra-mode-line-tag mode-line-format))))
       ((eq where 'after)
        (setcdr mlpos (cons 'hydra-mode-line-tag (cdr mlpos)))))
      (force-mode-line-update))))

(defhydra hydra-reading
  (:pre (progn (setq hydra-is-helpful nil) (overwrite-mode -1) (hydra-refresh-mode-line "  [N]  "))
	:before-exit (progn (setq hydra-is-helpful t) (hydra-refresh-mode-line "  [I]  "))
	:foreign-keys run
	:color amaranth
	:hint nil)
  " "
  ("!" shell-command)
  ("-" er/expand-region)
  ("%" view-jump-brace)
  ("/" (progn (toggle-org-hydra) (hydra-push '(hydra-reading/body))) :color teal)
  ("." (progn (call-interactively 'avy-goto-char-timer)))
  (":" (progn (call-interactively 'eval-expression)))
  (";d" delete-window)
  (";v" (progn (elfeed) (elfeed-update)) :color blue)
  (";e" eval-buffer)
  (";t" twit)
  (";s" (sx-tab-all-questions t "emacs"))
  ("C-c C-]" helm-bibtex :color blue)
  ("C-c a" modi/switch-to-scratch-and-back :color blue)
  ("C-c z" (call-interactively 'helm-org-dwim))
  ("C-M-S-i" (if org-src-mode (org-edit-src-exit) (call-interactively 'narrow-or-widen-dwim)) :color blue)
  ("<mouse-1>" mouse-set-point :color blue)
  ("<mouse-2>" helm-for-files)
  ("<mouse-3>" kill-this-buffer)
  ("<" beginning-of-buffer)
  (">" end-of-buffer)
  ("@" avy-goto-line)
  ("A" (progn (beginning-of-line) (indent-according-to-mode)) :color blue)
  ("D" kill-line :color blue)
  ("E" end-of-line :color blue)
  ("F" (progn (call-interactively 'avy-goto-word-1-backward-in-line)))
  ("H" (progn (ov-highlight/body) (hydra-push '(hydra-reading/body))) :color teal)
  ("I" (progn (forward-char 1)) :color blue)
  ("J" (progn (end-of-line) (newline-and-indent)) :color blue)
  ("K" (progn (beginning-of-line) (open-line 1) (indent-according-to-mode)) :color blue)
  ("N" next-user-buffer)
  ("P" previous-user-buffer)
  ("S" swiper-all)
  ("S-SPC" scroll-down)
  ("SPC" scroll-up)
  ("T" org-babel-tangle)
  ("U" word-example-in-sentence)
  ("V" (and (ignore-errors (other-window-for-scrolling) (scroll-other-window-down))))
  ("W" backward-word)
  ("X" (progn (kill-line 0)))
  ("Y" duplicate-line-or-region :color blue)
  ("[" org-backward-paragraph)
  ("]" org-forward-paragraph)
  ("a" (progn (beginning-of-line) (indent-according-to-mode)))
  ("b" (progn (ibuffer) (swiper)))
  ("c" (progn (overwrite-mode) (hydra-refresh-mode-line "  [C]  ")) :color blue)
  ("dd" kill-whole-line)
  ("dw" kill-word)
  ("df" zap-to-char :color blue)
  ("de" kill-line :color blue)
  ("da" (progn (kill-line 0) (indent-according-to-mode)) :color blue)
  ("dp" duplicate-line-or-region :color blue)
  ("e" end-of-line)
  ("f" (progn (call-interactively 'avy-goto-word-1-forward-in-line)))
  ("g" google-this)
  ("h" backward-char)
  ("i" nil)
  ("j" next-line)
  ("k" previous-line)
  ("l" forward-char)
  ("ma" magit-log-all :color blue)
  ("mc" magit-stage-all-and-commit :color blue)
  ("mg" magit-status :color blue)
  ("md" magit-diff :color blue)
  ("ml" magit-log-current :color blue)
  ("mt" git-timemachine :color blue)
  ("mw" mark-word)
  ("ms" mark-sexp)
  ("mp" mark-paragraph)
  ("n" (progn (ded/org-show-next-heading-tidily)))
  ("o" ace-link)
  ("p" (progn (ded/org-show-previous-heading-tidily)))
  ("r" undo-tree-redo)
  ("s" swiper)
  ("t" other-window)
  ("u" undo)
  ("v" (save-excursion (and (ignore-errors (other-window-for-scrolling)) (scroll-other-window))))
  ("w" forward-word)
  ("x" delete-char)
  ("y" yank))

(bind-key "<escape>" 'hydra-reading/body)

(defun avy-goto-word-1-backward-in-line (char &optional arg)
  (interactive (list (read-char "char: " t)
                     current-prefix-arg))
  (avy-goto-word-1 char arg (point-at-bol) (point) nil))

(defun avy-goto-word-1-forward-in-line (char &optional arg)
  (interactive (list (read-char "char: " t)
                     current-prefix-arg))
  (avy-goto-word-1 char arg (point) (point-at-eol) nil))

(defun view-jump-brace ()
  "Jump to correspondence parenthesis"
  (interactive)
  (let ((c (following-char))
        (p (preceding-char)))
    (if (eq (char-syntax c) 40) (forward-list)
      (if (eq (char-syntax p) 41) (backward-list)
        (backward-up-list)))))

(setq cursor-in-non-selected-windows nil)

(defvar hydra-stack nil)

(defun hydra-push (expr)
  (push `(lambda () ,expr) hydra-stack))

(defun hydra-pop ()
  (interactive)
  (let ((x (pop hydra-stack)))
    (when x
      (funcall x))))

目前有一个缺陷就是,我参照evil-mode-line的写法实现了在mode-line上的开关显示,但是尽管propertize了显示符号,但是显示仍然不成功。


#8

用evil的话主要还是用operator, motion text-object的组合拳, 还有那一堆evil-打头的插件(生态), 你如果只用基本的, evil的确是太重量级了一点


#9

之前尝试过Evil生态,但是对于自己太过于庞大。

同时,自己无法抛弃已经熟记的emacs快捷键,通过Hydra基本可以mix Evil和emacs的快捷键,配置也十分简单,用着很顺手。


#10

我也觉得evil过于庞大了, 而且某些快捷键实在不够高效, 又要自己改改改.

vim花费了太多键位在一些定位重复的按键上, 而且占着那些容易按的地方, 让我十分烦躁. 自己重新设置又十分麻烦(说白了就是vim历史包袱太重),导致一个键盘上看起来似乎满满当当的, 但其实大部分时间你并不会用到那些键. 更导致了你的leader只能放在;, 这种不太好按的地方.

比如

  • ft, we, 我不是很懂这两个有什么不得了的区别
  • 有了cc为什么还需要S? 我把S改成了s的功能, 把s空出来做leader, 得了, 又和evil-surround抢键位的问题, 但是我不想改了.
  • evil-ex我一年估计都用不到一次, 以前还觉得ex命令的s替换比较方便, 现在发现emacs自带一个query-replace完爆, 配合anzu食用更加, 连多光标功能都给我戒了.
  • 同理, 既生c, 何生r?
  • / # * n N不是我狠心, 但是swiper真的很好用.
  • 等等等等…

虽然evil有这么多槽点, 但是我现在还是被迫使用这东西, 我喜欢模式编辑的高效. 但是xah fly keys之流我又不喜欢他这种没有文本对象的设计, 除非有朝一日实在受不了写一个自己的简洁版evil了


#11

You can also use emacs keybind in insert mode of evil:

+(setcdr evil-insert-state-map nil)

+(define-key evil-insert-state-map (read-kbd-macro evil-toggle-key) 'evil-normal-state)

+(define-key evil-insert-state-map [escape] 'evil-normal-state)

#12

是这样的,我经常会在Normal mode误按Emacs的快捷键。这个时候就无解了。

上面的hydra里,即便是hydra里未定义的键,也同样工作。


#13

喜欢hydra,拯救了我的小拇指,因为hydra我早已不用在调换ctrl和caps lock键了


#14

哈哈, 我也是.


#15

用了大半年 hydra 写了 7-8 个 hydra menu,用的还是很频繁的,过最终还是决定改 用 keymap 和which-key

hydra 的问题主要有以下几点:

  • 手写 hydra menu 太磨人了

    取个简短的名字真难

  • 和原生的 emacs 特性割裂开来了

    如 ‘C-x k’ 看具体的按键绑定时,看到的是整个 menu 的信息,有点反直觉

  • 列表展示是横向的

    因为每一个项的宽度是不一样的,看起来比较累。个人比较习惯垂直展示列表并且左对齐, 看起来没那么累。

  • 按了其它字符会直接将该字符插入到当前 buffer 中

    有时候是下意识的就按错键了,结果导致当前 buffer 被插入一个字符,导致问题


#16

1: 列表展示是可以用hydra-is-helpful这个变量设置成不显示的. 我一般都不显示, 记不住的时候才显示出来看一下. 比如这样 (“M-h” (if hydra-is-helpful (setq hydra-is-helpful nil) (setq hydra-is-helpful t)) :exit nil)

2: 按其他字符的时候是提示一下, 还是插入一个字符, 又或者退出hydra, 是可以设置的. 通过对应的hydra的color来设置.


#17

所有字符必须都列出来,这是做不到的,对于未预期的字符就没招了


#18

你要的是hydra的:foreign-key吧, 仔细读一下hydra的README


#19

是的,看了一下 hydra 文档,应该可以通过 :foreign-key 解决问题 4


#20

which-key 配置

;; which-key
(use-package which-key
  :defer nil
  :delight
  :custom
  (which-key-idle-delay 0.3)
  (which-key-popup-type 'side-window)
  (which-key-side-window-location 'bottom)
  (which-key-show-docstrings t)
  (which-key-max-display-columns 1)
  (which-key-show-prefix nil)
  (which-key-side-window-max-height 8)
  (which-key-max-description-length 80)
  :config
  (which-key-mode t))

效果如下,跟 Ivy 有点接近