一份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函数。
- isearch-toggle-regexp 在使用isearch搜索时(即按下C-s isearch-forward后)绑定到M-s r。 按下后,您的输入全部都会被以正则来匹配了。
- isearch-toggle-case-fold 默认绑到M-s c。默认isearch对大小写是类似于rg一样“smart”的。具体地说,如果用户全部输入小写,则不匹分大小写进行匹配,如果用户输入中包括大写,则精确匹配大小写。 再举个例子,默认情况下,默认foo可以匹配foo,Foo,FOO。输入Foo,只能匹配到Foo。打开这个选项后,就是case sensitive了,也就只能精确匹配了。个人认为,该选项用处不太大。
- isearch-toggle-word 默认绑定到M-s w。打开word匹配。直接举例:未打开以前,foo可以匹配foobar,foo。打开该选项后,foo只能匹配foo了,foobar就匹配不到了。 可以看出来,开启该选项后,isearch必须完全匹配一个完整地word。这个功能可以帮忙过滤很多杂项。
- isearch-toggle-symbol 默认绑定到M-s _ 。它和isearch-toggle-word的基本一样,不过它使isearch完全匹配一个symbol。 简单来说,symbol和word的区别:isearch-toggle-word是一个symbol,它包括isearch toggle和word三个word。
- isearch-toggle-char-fold 默认绑定到M-s '。这个我觉得应该很少用到。文档里面原话是这样的:
For instance, under character folding the letter ‘a’ matches all of its accented cousins like ‘ä’ and ‘á’, i.e.
。可以看到,开启该选项后,a就可以匹配到它的其它腔调(是这么翻译的吗?)。 - isearch-toggle-lax-whitespace 默认绑定到M-s SPC。开启该功能后,可以把输入中的空格当做一个固定的正则表达式,这个固定的正则表达式存在于search-whitespace-regexp变量中。关于这个功能,我在后面 空格的特殊用法 中进行详细说明。
- swiper-from-isearch 这是swiper提供的小函数,可以就当前isearch中输入的内容来调用swiper。我绑定到了C-return这个按键:
(define-key isearch-mode-map (kbd "<C-return>") 'swiper-from-isearch)
空格的特殊用法
使用swiper的时候,默认输入一个空格是可以匹配任意多个任意字符,其实就是相当于正则表达式中的 .* 。我个人觉得这个功能是非常方便的。 因为实际使用中专门搜空格的机会很少,每次输入.*很麻烦,输入空格就很方便。
isearch中自带就有这个功能。其实就是 上文 讲到的功能。
这里面有几个变量:
- isearch-lax-whitespace 设置这个变量为t, 表示isearch在literal搜索的时候,把每个空格都当成search-whitespace-regexp变量设置的正则表达式来匹配。
- isearch-regexp-lax-whitespace 和isearch-lax-whitespace类似,不过它控制正则表达式搜索的行为。
- search-whitespace-regexp 这个变量就存了开启lax-whitespace功能后,空格对应的正则表达式。默认值为 "\\s-+" 。 我理解它应该是匹配的至少一个“空字符”,这里的空字符就包括空格、制表符等等类似物。
所以按照我的习惯,我是这样设置的:
;; 这样可以在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帮我绑定到了右方向键。 我建议把这些非常非常常用的功能,一定要找个好按的键来绑。 这样使用起来才爽。