如何在GNU Emacs中模仿Vim的*搜索功能?

33

Vim中,在普通模式下,* 键可以搜索光标下的单词。在GNU Emacs中,最接近的本地等效方式是:

C-s C-w

但这并不完全相同。它打开了增量搜索迷你缓冲区,并将从当前缓冲区光标到单词结尾的内容复制到其中。在Vim中,即使你在单词中间按*键,也会搜索整个单词。

我编写了一些Elisp代码来实现类似的功能:

(defun find-word-under-cursor (arg)
  (interactive "p")
  (if (looking-at "\\<") () (re-search-backward "\\<" (point-min)))
  (isearch-forward))

我设置了一个快捷键C-+,当我在单词上按下C-+ C-w时,它会退回到单词的开头并启动isearch来复制单词。但这并不完美。最好使用regexp搜索“\<”word“\>”来避免显示部分匹配(搜索单词“bar”不应与“foobar”匹配,而只应匹配“bar”本身)。我尝试使用search-forward-regexp和concat'ing \<>,但它不会在文件中换行,也不会突出显示匹配项,而且通常很糟糕。isearch-*函数似乎是最好的选择,但这些函数在脚本化时表现不佳。请问有什么想法吗?是否有人可以改进elisp的一点?或者我是否忽略了其他方法?


是的,Emacs允许您钩入isearch来完成这个操作,但highlight-symbols包更好。我发现将符号保持集中于当前思维过程非常有帮助。此外,您可以进行一键搜索,并且相对于符号开头,您的点会保持不变。 - event_jr
https://dev59.com/uHI-5IYBdhLWcg3wm5hG - Neil
自从 Emacs 24.4(于2014年10月20日发布)起,此函数现在可用作交互命令 isearch-forward-symbol-at-point,可以通过全局键序列 M-s . 调用。我已经在 https://dev59.com/NXRB5IYBdhLWcg3wj32c#75425861 上发布了一个答案。 - Susam Pal
10个回答

14
根据您对我的第一个答案的反馈,这个怎么样:
(defun my-isearch-word-at-point ()
  (interactive)
  (call-interactively 'isearch-forward-regexp))

(defun my-isearch-yank-word-hook ()
  (when (equal this-command 'my-isearch-word-at-point)
    (let ((string (concat "\\<"
                          (buffer-substring-no-properties
                           (progn (skip-syntax-backward "w_") (point))
                           (progn (skip-syntax-forward "w_") (point)))
                          "\\>")))
      (if (and isearch-case-fold-search
               (eq 'not-yanks search-upper-case))
          (setq string (downcase string)))
      (setq isearch-string string
            isearch-message
            (concat isearch-message
                    (mapconcat 'isearch-text-char-description
                               string ""))
            isearch-yank-flag t)
      (isearch-search-and-update))))

(add-hook 'isearch-mode-hook 'my-isearch-yank-word-hook)

如果当前文件中没有匹配项,程序会停止运行。回溯和复现指令在这里 - mgalgs
这个在<word>上运行失败。 - Ian Kelling
第一次使用时它运行良好,但重复使用时会出现错误:Wrong type argument: arrayp, nil - Dfr
2
这与内置的isearch-forward-symbol-at-point有什么不同? - paldepind

7

highlight symbol 是一个 Emacs 扩展程序,提供了这个功能。特别是,推荐使用 .emacsrc 设置:

(require 'highlight-symbol)

(global-set-key [(control f3)] 'highlight-symbol-at-point)
(global-set-key [f3] 'highlight-symbol-next)
(global-set-key [(shift f3)] 'highlight-symbol-prev)

允许跳转到当前点的下一个符号(F3),跳转到上一个符号(Shift + F3)或突出显示与光标下方匹配的符号(Ctrl + F3)。如果光标位于单词中间,这些命令将继续正常工作。

与vim的超级星号不同,突出显示符号和在符号之间跳转绑定到两个不同的命令。我个人不介意分离,但如果您想精确匹配vim的行为,可以将这两个命令绑定在同一按键下。


5

JimBlandy的解决方案非常接近,但实际上它并没有“搜索”,而是在搜索界面上进行了黑客攻击。您无法跳转到上一个/下一个,并且不会将单词存储在缓冲区中。唉! - richq

3

scottfrazer的答案对我很有效,但对于以“_”(或其他非单词字符)结尾的单词似乎不起作用?我发现轻量级符号模式的代码在不同版本的emacs中使用了不同的单词边界正则表达式,这为我解决了问题。以下是修改后的代码:

(defconst my-isearch-rx-start
  (if (< emacs-major-version 22)
      "\\<"
    "\\_<")
  "Start-of-symbol regular expression marker.")

(defconst my-isearch-rx-end
  (if (< emacs-major-version 22)
      "\\>"
    "\\_>")
  "End-of-symbol regular expression marker.")

(defun my-isearch-word-at-point ()
  (interactive)
  (call-interactively 'isearch-forward-regexp))

(defun my-isearch-yank-word-hook ()
  (when (equal this-command 'my-isearch-word-at-point)
    (let ((string (concat my-isearch-rx-start
                          (buffer-substring-no-properties
                           (progn (skip-syntax-backward "w_") (point))
                           (progn (skip-syntax-forward "w_") (point)))
                          my-isearch-rx-end)))
      (if (and isearch-case-fold-search
               (eq 'not-yanks search-upper-case))
          (setq string (downcase string)))
      (setq isearch-string string
            isearch-message
            (concat isearch-message
                    (mapconcat 'isearch-text-char-description
                               string ""))
            isearch-yank-flag t)
      (isearch-search-and-update))))

(add-hook 'isearch-mode-hook 'my-isearch-yank-word-hook)

2
如何使用内置命令 M-b C-s C-w(单词开头,搜索,单词搜索)?

1
这并不像只需一个按键那么好,是吗? :-) - richq

2

来自Mastering Emacs博客的米奇重新介绍了一个很酷的“Smart Scan”库,它提供了全局绑定的M-nM-p,用于在缓冲区中导航光标下的符号。它不影响搜索寄存器,因此不能完全替代*,但是它是一个巧妙且可用的导航问题替代品。


0
;这是我的版本:模拟Visual Studio/Windows键绑定
; C-F3 - 开始搜索光标所在的单词
; F3向前搜索,Shift F3向后搜索
(setq my-search-wrap nil)
(defun my-search-func (dir) (interactive) (let* ((text (car search-ring)) newpoint) (when my-search-wrap (goto-char (if (= dir 1) (point-min) (point-max))) (setq my-search-wrap nil)) (setq newpoint (search-forward text nil t dir)) (if newpoint (set-mark (if (= dir 1) (- newpoint (length text)) (+ newpoint (length text)))) (message "搜索失败: %s" text) (ding) (setq my-search-wrap text))))
(defun my-search-fwd () (interactive) (my-search-func 1)) (defun my-search-bwd () (interactive) (my-search-func -1))
(defun yank-thing-into-search () (interactive) (let ((text (if mark-active (buffer-substring-no-properties (region-beginning)(region-end)) (or (current-word) "")))) (when (> (length text) 0) (isearch-update-ring text) (setq my-search-wrap nil) (my-search-fwd)))) (global-set-key (kbd "") 'my-search-fwd) ; 类似于Visual Studio的搜索键 (global-set-key (kbd "") 'my-search-bwd) (global-set-key (kbd "") 'yank-thing-into-search)

0

我没有尝试过,但这里有一些代码在这里叫做Grep-O-Matic。


0

使用此代码,您可以在isearch模式下执行C-*

(define-key isearch-mode-map [?\C-*] 'kmk-isearch-yank-thing)
(defun kmk-isearch-yank-thing () "从缓冲区中提取下一个内容到搜索字符串中。" (interactive) (let ((string (regexp-quote (thing-at-point 'word)))) (setq isearch-string (concat isearch-string "\\") isearch-message (concat isearch-message (mapconcat 'isearch-text-char-description string "")) ;; 不要在反向搜索中移动光标。 isearch-yank-flag t)) (setq isearch-regexp t isearch-word nil isearch-success t isearch-adjusted t) (isearch-search-and-update))

这与我曾经拥有的某个功能相当相似(如果不是形式上的话)- 问题在于多次使用与isearch不兼容。没有办法在再次运行C-*之前“清理”isearch。你最终会发现search1search2search3全部神秘地连接在一起。 - richq

0

自从 Emacs 24.4 版本以来,使用全局键序列 M-s . 可以搜索光标下的 符号

以下是有关此键序列及其调用的函数的更多信息,可通过 Emacs 的描述系统(C-h k M-s .)获得:

M-s . runs the command isearch-forward-symbol-at-point (found in
global-map), which is an interactive compiled Lisp function in
‘isearch.el’.

It is bound to M-s ., <menu-bar> <edit> <search> <i-search>
<isearch-forward-symbol-at-point>.

(isearch-forward-symbol-at-point &optional ARG)

Do incremental search forward for a symbol found near point.
Like ordinary incremental search except that the symbol found at point
is added to the search string initially as a regexp surrounded
by symbol boundary constructs \_< and \_>.
See the command ‘isearch-forward-symbol’ for more information.
With a prefix argument, search for ARGth symbol forward if ARG is
positive, or search for ARGth symbol backward if ARG is negative.

  Probably introduced at or before Emacs version 24.4.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接