感觉有点像 Vim 中的 ”模式“ 的概念,不过是可以自定义的
是的, 我就把我常用的快捷键全部都设置到hydra里面了
感觉有点像 transient state
spacemacs的transient state就是在hydra上加了点东西
以前只是知道这个插件,一直没用过。昨天弄了下,网上不知什么解释不清楚,对我的实际影响就是一个比 which keys 更好看的插件。用了这个应该可以不用 which-keys 了吧。虽然也有其他人有一些两者区别的解释。
org章节间跳转
with hydra: C-c C-p n p n n n p …
without hydra: 一直按着modifier,很累
四处搜刮了下代码,通过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了显示符号,但是显示仍然不成功。
用evil的话主要还是用operator, motion text-object的组合拳, 还有那一堆evil-打头的插件(生态), 你如果只用基本的, evil的确是太重量级了一点
之前尝试过Evil生态,但是对于自己太过于庞大。
同时,自己无法抛弃已经熟记的emacs快捷键,通过Hydra基本可以mix Evil和emacs的快捷键,配置也十分简单,用着很顺手。
我也觉得evil过于庞大了, 而且某些快捷键实在不够高效, 又要自己改改改.
vim花费了太多键位在一些定位重复的按键上, 而且占着那些容易按的地方, 让我十分烦躁. 自己重新设置又十分麻烦(说白了就是vim历史包袱太重),导致一个键盘上看起来似乎满满当当的, 但其实大部分时间你并不会用到那些键. 更导致了你的leader只能放在;
和,
这种不太好按的地方.
比如
-
f
和t
,w
和e
, 我不是很懂这两个有什么不得了的区别 - 有了
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了
是这样的,我经常会在Normal mode误按Emacs的快捷键。这个时候就无解了。
上面的hydra里,即便是hydra里未定义的键,也同样工作。
喜欢hydra,拯救了我的小拇指,因为hydra我早已不用在调换ctrl和caps lock键了
哈哈, 我也是.
用了大半年 hydra
写了 7-8 个 hydra
menu,用的还是很频繁的,过最终还是决定改
用 keymap 和which-key
。
hydra
的问题主要有以下几点:
-
手写 hydra menu 太磨人了
取个简短的名字真难
-
和原生的 emacs 特性割裂开来了
如 ‘C-x k’ 看具体的按键绑定时,看到的是整个 menu 的信息,有点反直觉
-
列表展示是横向的
因为每一个项的宽度是不一样的,看起来比较累。个人比较习惯垂直展示列表并且左对齐, 看起来没那么累。
-
按了其它字符会直接将该字符插入到当前 buffer 中
有时候是下意识的就按错键了,结果导致当前 buffer 被插入一个字符,导致问题
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来设置.
所有字符必须都列出来,这是做不到的,对于未预期的字符就没招了
你要的是hydra的:foreign-key
吧, 仔细读一下hydra的README
是的,看了一下 hydra 文档,应该可以通过 :foreign-key
解决问题 4
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 有点接近
用在 artist-mode 中 挺好的