一份isearch的使用总结

事情的起因是这样的:本来我是使用swiper的,不得不承认,swiper真是很好用。 但是,在遇到buffer比较大的时候,每次调用swiper总是要卡那么1到2秒,比如使用package-list-packages命令生成的*Package* buffer中使用swiper,启动那一下就会卡到让我可以思考下人生。。

一般这种时候我就直接使用原生的isearch,速度是真的快!

另外,现在我使用 xah-fly-keys , 它默认在command mode中把n这个非常好用的按键绑定到了isearch-forward,其实我一直在纠结要不要把这个按键绑成swiper,swiper有这个问题后,我就一直没换。

综上所述,我决定折腾一下原生的isearch。

基本介绍

默认的isearch-forward函数是literal的,也就是用户输入什么就匹配什么,没有正则解释,没有转义,完全literal。这样的好处就是,想搜啥就是啥,不用考虑太多。其实默认用它就可以了。

当然,完全可以开启正则匹配等功能,下面就说说这几个toggle函数。

空格的特殊用法

使用swiper的时候,默认输入一个空格是可以匹配任意多个任意字符,其实就是相当于正则表达式中的 .* 。我个人觉得这个功能是非常方便的。 因为实际使用中专门搜空格的机会很少,每次输入.*很麻烦,输入空格就很方便。

isearch中自带就有这个功能。其实就是 上文 讲到的功能。

这里面有几个变量:

所以按照我的习惯,我是这样设置的:

;; 这样可以在literal的isearch中,把空格直接当成正则里面的.*匹
(setq isearch-lax-whitespace t)
(setq search-whitespace-regexp ".*")
;; 在搜正则时不开启这个功能,空格就是空格
(setq isearch-regexp-lax-whitespace nil)

我默认是没有开启正则搜索的,这样设置后,我每次调用isearch后,基本都是一个单词,一个空格,再一个单词…… 几下就找到想要的了。 如果真的需要搜一行中的空格(虽然我几乎没有遇到过),可以使用M-s SPC (isearch-toggle-lax-whitespace) 在这次搜索中关闭这个功能就好了。 而在真正需要搜正则的时候,我是关闭这个功能的。

自动wrap

isearch在搜索到buffer末尾时,会先报一次fail,下次再按C-s的时候才又会从头开始。我不喜欢这个功能。 我希望它直接就越过末尾从头开始搜索。我在 这里 找到答案:

(defadvice isearch-search (after isearch-no-fail activate)
  (unless isearch-success
    (ad-disable-advice 'isearch-search 'after 'isearch-no-fail)
    (ad-activate 'isearch-search)
    (isearch-repeat (if isearch-forward 'forward))
    (ad-enable-advice 'isearch-search 'after 'isearch-no-fail)
    (ad-activate 'isearch-search)))

重新绑定删除键

isearch默认删除键(DEL)是绑定的isearch-delete-char,我不喜欢。 isearch-del-char这个函数才是我想要的,所以我重新绑定了一下:

(define-key isearch-mode-map (kbd "DEL") isearch-del-char)
。至于这两者的区别,isearch-delete-char会在你多次使用C-s在后面几个匹配项中跳转后,一个一个的返回,然后才是删字符,这导致有些时候感觉删除键老是删不掉isearch中输入的字符。 而isearch-del-char是真正的删字符。 这两者的区别,解释起来麻烦,直接试下就清楚了。

奇怪的C-g

isearch-abort。默认绑定到C-g。有时你会发现,需要按下两次C-g才可以完全退出isearch。 原因就是它默认执行的是isearch-abort函数,如果输入在buffer中没有匹要到任何内容,默认情况下,isearch会把输入中未匹配成功的部分标红。 两种情况,如果匹配成功,isearch-abort可以直接退出isearch。 如果匹配不成功,第一次执行isearch-abort会先把最后未匹配的内容全部删除,此时再按一次isearch-abort才会退出isearch。 想要一次性退出isearch,应该是执行isearch-cancel函数。我绑定到了C-c:

(define-key isearch-mode-map (kbd "C-c") isearch-cancel)

减少输入

isearch-yank-word-or-char。默认isearch是增量式的搜索的。 比如,如果输入is就已经定位到isearch时,执行这个函数就可以把当前word的后面的几个字符arch自动加入到isearch中。这样免得你敲太多。 类似的还在isearch-yank-line,isearch-yank-char,iseach-yank-kill等等。这些功能我用得不太多。

编辑isearch输入

isearch-edit-string 默认绑定到M-e。使用它可以编辑当前的isearch输入。 如果不使用这个功能,你会发现你是对这些输入编辑不了的,只能一个一个字符的删除。这个功能我也用得不多。

一键回到原来位置重新执行isearch

peng-rerun-isearch 我自己写了一个rerun的函数,因为我常遇到,搜到一半发现完全搜错了,想重新搜,我不想使用M-e编辑当前输入,也不想先cancel再调用isearch。 我只想rerun。

;; 这是参考abo-abo的代码来做的
;; `https://oremacs.com/2015/07/16/callback-quit/'
(defmacro isearch-quit-and-run (&rest body)
  "Quit the minibuffer and run BODY afterwards."
  (declare (indent 0))
  `(progn
     (put 'quit 'error-message "")
     (run-at-time nil nil
                  (lambda ()
                    (put 'quit 'error-message "Quit")
                    (with-demoted-errors "Error: %S"
                      ,@body)))
     (isearch-cancel)))

(defun peng-rerun-isearch ()
  "rerun isearch from the original place."
  (interactive)
  (isearch-quit-and-run
    (isearch-forward)))
这个rerun函数还是需要一点技巧的,我参考了 abo-abo的用法

isearch-occur

这个功能看个人爱好了。我hack了一次这个功能。我的想法是在每次输入字符的时候都自动的调用一下isearch-occur。这样基本就可以达到和swiper差不多的效果。 最后我也成功了,代码还保存在 我的github上 ,感兴的朋友可以看下,不过感觉还是不太好用。 一个是occur这个buffer跳来跳去感觉不爽。 另一个,有了swiper-from-isearch,也不太用得到了。

其实原先我的想法是,使用一个线程来异步执行isearch-occur,这样再大的buffer也不至于卡住了。 但是还是想法太简单了。emacs实在没有完全实现多线程,我理解可能有点类似于python中的一把大锁,实际执行的只有一个线程。 大buffer的时候,还是会卡住的。 多线程相关的,irc的朋友推荐了两个链接: Threads NoThreading 后面这个链接的irc讨论我没太看明白。后面有时间再看下。

这个问题,我也在Emacs-china上发了一个 问题

isearch搜中文

直接使用拼音首字母来拼中文,由论坛 徐春阳 同学提供: pinyin-search.el

我的建议

最近用起来isearch速度飞快,不过我是用的n就直接唤起isearch,非常的方便。然后我不通过C-s来搜下一个匹配项。 工作时间,我的键盘是 smartyao 。我绑定到了我键盘上左边大拇指位置。 一个很好按的键。 正常键盘上,xah-fly-keys帮我绑定到了右方向键。 我建议把这些非常非常常用的功能,一定要找个好按的键来绑。 这样使用起来才爽。