xah-fly-keys 使用总结
我是从 evil 切换到 xah-fly-keys 的。 导致这个切换的原因我在之前刚使用xah-fly-keys的时候也简单写过一篇文章来记录 从evil到xah-fly-keys ,简单来说就是代码写多了手痛。
xah-fly-keys是 Xah Lee 根据函数的使用频率,重新设置的一套快捷键绑定方式。 而evil,是一个在Emacs中模拟vim操作的插件。
xah-fly-keys和evil对比
- 常用命令确实好按,这应该是xah-fly-keys的精髓。 evil模拟vim的按键方式一方面是很高效,另一方面,我觉得也是一种限制。其实有时候跳出vim的框框,用emacs的方式解决问题会更高效。
- xah-fly-keys使用很多emacs原生的命令,用了xah-fly-keys后,我才发现原来Emacs本生内置的功能就很强大。 比如以前基本不用的isearch-forward,之前主要使用swiper,但是swiper在buffer太大的时候速度是很慢的,同样的buffer使用isearch-forward速度简直像飞一样。 再比如rectangle-mark-mode, list-matching-lines, delete-matching-lines, delete-non-matching-lines, copy-to-register, append-to-register等等。
- xah-fly-keys的command模式中的按键绑定在每次从insert回到command模式时都重新绑定一次,可以保证command模式的按键几乎在任何地方都是一样的。
- xah-fly-keys的insert模式基本保留原来mode中的按键绑定。不会像evil一样完全覆盖掉原mode的按键绑定。 论坛里网友提醒,evil也有evil-emacs-state 不过我在使用evil时,大多数时候都是在normal和insert模式下切换。几乎没有用到emacs-state。 原来使用evil时,基本上大多数的major-mode我都需要重新定义一下按键绑定,因为evil默认把原来major mode中的很多绑定覆盖了。 比如dired-mode,magit-mode,info-mode,ibuffer-mode等等中的很多按键就会被evil覆盖。 而在xah-fly-keys中,从command切到insert模式后,原来模式中的按键绑定一般都在,所以在xah-fly-keys中,如果使用原来的major-mode按键,基本不需要再为每个mode自定义按键了。
- xah-fly-keys支持dvorak布局。
- xah-fly-keys使用者没有evil多。至少在我看来,键盘流们使用vim的按键方式的人还是占多数。 所以evil的使用者应该是比xah-fly-keys多的,特别是在 spacemacs 火了以后。 我也是先用的vim,然后在emacs中使用evil,到最近才开始使用xah-fly-keys的。
- 如果要根据不同的模式来定制不同的按键绑定,我觉得xah-fly-keys没有evil直观。 evil有各种模式对应的local-map,直接在不同的模式中定义对应的local-map就可以了。 xah-fly-keys就只有xah-fly-key-map,所以在每种模式中自定义就需要一点小技巧。 在 定制 一节中可以看到如何定制xah-fly-keys。
evil功能在xah-fly-keys中的对应
Evil | Feature | Xah-fly-keys Replacement |
---|---|---|
A |
光标移动到到行尾,进入insert模式 | ;f |
I |
光标移动到到行尾,进入insert模式 | hf |
C |
清除当前光标至行尾内容并进入insert模式 | SPC g f kill-line先删除当前光标到行尾内容,然后进入insert模式 |
f[char] |
在当前行找到[char]字符然后把光标移动到字符上 | 使用`isearch-forward'或者直接光标左右移动,u,o一个word一个word移动出很快 |
t |
在当前行找到[char]字符然后把光标移动到字符之前 | 使用`isearch-forward'或者直接光标左右移动,u,o一个word一个word移动出很快 |
dd |
删除当前行 | x(xah-cut-line-or-region) |
df[char] |
删除光标到最近一个[char]中的内容,包括char | 暂时选中删除,或者使用e,r删除word |
df[char] |
删除光标到最近一个[char]中的内容,不包括char | 暂时选中删除,或者使用e,r删除word |
vi",vi(,vi{ |
选中",() , {}这些包住的内容 | 我使用 thing-edit 直接选中都不需要了 |
Ctrl v | 矩形选择和编辑 | SPC o SPC(rectangle-mark-mode) |
% | 在相互匹配的括号之间跳转 | /(xah-goto-matching-bracket)括号,双引号,引号等等都要以跳转。 |
xah-fly-keys中一些好用的功能
- xah-delete-current-text-block默认绑定到g上。该命令以block为单位删除内容,其中的block是以空行为分格的。写代码的时候,相关性很强的内容都是写在一起的。直接用一个g就删除了,很方便。
- xah-select-block默认绑定到6上。以block为单位的选中。
- xah-shrink-whitespaces默认绑定到w上。如果当前光标下有多个空格或者一个回车,把它们减少为一个空格。如有当前光标只有一个空格,删除该空格。如果当前光标下没有空格,插入一个空格。
- xah-toggle-letter-case默认绑定到b上。进行大小写字母切换的,可以在大写、小写,首字母大写这几种中切换。还可以选中一个区域对整个区域进行大小写修改。
- xah-show-kill-ring默认绑定为Space t。最近复制剪切过的所有记录都可以查到。
- xah-copy-file-path默认绑定到Space i g。拷贝当前文件的路径,在文件buffer中可以使用。在dired-mode中也可以使用,很方便。
- xah-open-file-at-cursor默认绑定到Space i f。打开当前光标下的文件,相对路径和绝对路径都支持,还支持http,还支持选择对应的行号。
-
xah-fly-keys的command-mode默认大写字母,特殊字符都没有绑定,所以,有些时候如果只需要输入特殊字符或者单个大写字母的时候,是不需要进入insert模式的。
比如输入
ABC"*{}()^$#@!
等。 - xah-delete-backward-char-or-bracket-text默认绑定在d上,我直接绑到 ⌫ Backspace 上了。 正常情况删除一个字符,如果光标前是一组括号,比如 (),<>,{},"", 等,删除整个括号,包括整个括号中的内容,并把这些内容都放到kill-ring中。 个人感觉这个应该是和xah-forward-right-bracket配合使用。这个函数就是直接把光标移动到下一个右括号后面,我把它绑定到了Space .上。
- emacs_navigating_keys_for_brackets 里面讲到 xah-forward-quote-smart和xah-backward-quote两个函数。前面的smart函数能跳到以""包住的内容的开头。 我写C时,这个功能跳字符串很方便。
定制xah-fly-keys
根据模式定制按键绑定
command模式下的按键大部分都是根据使用频率来制定的,最常用的都绑定在一个按键上,大部份都在leader-key后再敲两下。
具体的定制方法 杀哥写的: xah-fly-keys_customization讲得很清楚了。
我最需要的功能是根据不同的模式把command模式下的一些按键绑定到不同的功能。比如我常用的 SPC SPC 按键,我就喜欢在org-mode和c-mode中绑定不同的按键。
在和Xah咨询后,终于找到正确的方法:定义一个函数,该函数在不同的模式中使用 call-interactively 来调用来同的功能。比如我的f8就是绑定到这个函数的:
(defun peng-xah-fly-dot-key () "key `.'" (interactive) (cond ((eq major-mode 'c-mode) (call-interactively 'hydra-cscope/body)) ((eq major-mode 'Info-mode) (call-interactively 'hydra-info/body)) ((eq major-mode 'dired-mode) (call-interactively 'hydra-dired/body)) ((eq major-mode 'mhtml-mode) (call-interactively 'hydra-html/body)) ((eq major-mode 'picture-mode) (call-interactively 'hydra-artist-mode-menu/body)) ((eq major-mode 'artist-mode) (call-interactively 'hydra-artist-mode-menu/body)) ((eq major-mode 'ibuffer-mode) (call-interactively 'hydra-ibuffer-main/body)) (t nil)))
xah-fly-keys中有两个很重要的函数:xah-fly-command-mode-init 和 xah-fly-insert-mode-init。 前者在进入command mode时调用,后者在进入insert mode时调用。 command mode中绑定到一个按键的函数都在 xah-fly-command-mode-init 中重新绑定。
所以,直接使用define-key来修改xah-fly-key-map中的绑定是不行的,因为下次进入command-mode时, xah-fly-command-mode-init 又会修改这个map。
最直接的方式就是改 xah-fly-command-mode-init 函数。我就是把这块代码拷贝出来直接在后面添加我的自定义绑定的。
;; redefine command init (defun xah-fly-command-mode-init () "Set command mode keys. Version 2017-01-21" (interactive) (xah-fly--define-keys xah-fly-key-map '( ;; IMPORTANT, These keys are all dvorak keys ("~" . nil) (":" . nil) ("SPC" . xah-fly-leader-key-map) ("DEL" . xah-fly-leader-key-map) ;; ("'" . xah-reformat-lines) ("," . xah-shrink-whitespaces) ("-" . xah-cycle-hyphen-underscore-space) ("." . xah-backward-kill-word) (";" . xah-comment-dwim) ("/" . hippie-expand) ("\\" . nil) ("=" . xah-forward-equal-sign) ("[" . xah-backward-punct ) ("]" . xah-forward-punct) ("1" . xah-extend-selection) ("2" . xah-select-line) ("3" . delete-other-windows) ("4" . split-window-below) ("5" . delete-char) ("6" . xah-select-block) ("7" . xah-select-line) ("8" . xah-extend-selection) ("9" . xah-select-text-in-quote) ("0" . xah-pop-local-mark-ring) ("a" . execute-extended-command) ("b" . isearch-forward) ("c" . previous-line) ("d" . xah-beginning-of-line-or-block) ("e" . xah-delete-backward-char-or-bracket-text) ("f" . undo) ("g" . backward-word) ("h" . backward-char) ("i" . xah-delete-current-text-block) ("j" . xah-copy-line-or-region) ("k" . xah-paste-or-paste-previous) ("l" . xah-fly-insert-mode-activate-space-before) ("m" . xah-backward-left-bracket) ("n" . forward-char) ("o" . open-line) ("p" . xah-kill-word) ("q" . xah-cut-line-or-region) ("r" . forward-word) ("s" . xah-end-of-line-or-block) ("t" . next-line) ("u" . xah-fly-insert-mode-activate) ("v" . xah-forward-right-bracket) ("w" . xah-next-window-or-frame) ("x" . xah-toggle-letter-case) ("y" . set-mark-command) ("z" . xah-goto-matching-bracket))) (define-key xah-fly-key-map (kbd "a") (if (fboundp 'counsel-M-x) 'counsel-M-x 'execute-extended-command )) ;; add by pengpengxp the key here is 'qwerty' keys. (define-key xah-fly-key-map (kbd "C-a") 'peng-xah-fly-C-a-key) (define-key xah-fly-key-map (kbd "C-e") 'peng-xah-fly-C-e-key) (define-key xah-fly-key-map (kbd "C-n") 'next-line) (define-key xah-fly-key-map (kbd "C-p") 'previous-line) (define-key xah-fly-key-map (kbd "<C-tab>") '(lambda () (interactive) (switch-to-buffer (other-buffer)))) (define-key xah-fly-key-map (kbd "C-`") 'counsel-ibuffer) (define-key xah-fly-key-map (kbd "<C-S-tab>") '(lambda () (interactive) (switch-to-buffer (other-buffer)))) (define-key xah-fly-key-map (kbd "<home>") 'beginning-of-defun) (define-key xah-fly-key-map (kbd "<end>") 'end-of-defun) (define-key xah-fly-key-map (kbd "C-o") 'counsel-find-file) (define-key xah-fly-key-map (kbd "DEL") 'peng-xah-fly-backspace-key) (define-key xah-fly-key-map (kbd "<backspace>") 'peng-xah-fly-backspace-key) (define-key xah-fly-key-map (kbd "m") 'ivy-switch-buffer) (define-key xah-fly-key-map (kbd "p") 'peng-counsel-git-fast) (define-key xah-fly-key-map (kbd "d") 'counsel-find-file) (define-key xah-fly-key-map (kbd "s") peng-thing-edit-map) (define-key xah-fly-key-map (kbd "x") 'peng-xah-fly-x-key) (define-key xah-fly-key-map (kbd "0") 'xah-open-file-fast-ivy) (define-key xah-fly-key-map (kbd "1") 'peng-yank-symbol-name) (define-key xah-fly-key-map (kbd "2") 'peng-replace-current-symbol-with-kill-ring) (define-key xah-fly-key-map (kbd ".") 'peng-xah-fly-dot-key) (define-key xah-fly-key-map (kbd "`") 'xref-pop-marker-stack) (define-key xah-fly-key-map (kbd "<f8>") 'peng-xah-fly-f8-key) (progn (setq xah-fly-insert-state-q nil ) (modify-all-frames-parameters (list (cons 'cursor-type 'box)))) (setq mode-line-front-space "C") (force-mode-line-update) ;; )
如果你觉得本文不错,欢迎 donate 哦:)