【问题标题】:How can I emulate Vim's * search in GNU Emacs?如何在 GNU Emacs 中模拟 Vim 的 * 搜索?
【发布时间】:2010-10-10 00:50:32
【问题描述】:

在 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))

在启动 isearch 之前,它会倒退到单词的开头。我已经将它绑定到 C-+,它很容易在我的键盘上输入并且类似于 *,所以当我输入 C-+ C-w 时,它会从单词的开头复制到搜索迷你缓冲区。

但是,这仍然不完美。理想情况下,它将正则表达式搜索"\&lt;" word "\&gt;" 以不显示部分匹配项(搜索单词“bar”不应匹配“foobar”,仅匹配“bar”)。我尝试使用 search-forward-regexp 和 concat'ing \ 但这不会包含在文件中,不会突出显示匹配项并且通常很蹩脚。 isearch-* 函数似乎是最好的选择,但这些函数在编写脚本时表现不佳。

有什么想法吗?任何人都可以对 elisp 提供任何改进吗?还是我忽略了其他一些方法?

【问题讨论】:

  • 是的,Emacs 允许您连接到 isearch 来执行此操作,但 highlight-symbols 包更好。我发现让符号成为我当前思维过程的中心非常有帮助。此外,您可以获得一个键搜索,并且您的点相对于符号的开头保持不变。

标签: vim emacs elisp dot-emacs


【解决方案1】:

Mastering Emacs 博客的 Mickey 重新引入了一个很酷的“Smart Scan”库,它提供了 M-nM-p 的全局绑定,用于在缓冲区中导航光标下的符号。不影响搜索寄存器,因此它不是 * 的替代品,而是导航问题的一个聪明且可用的替代品。

【讨论】:

    【解决方案2】:

    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 的行为,你可以将这两个命令绑定在同一个按键下。

    【讨论】:

      【解决方案3】:

      内置命令 M-b C-s C-w 怎么样(单词开头,搜索,单词搜索)

      【讨论】:

      • 它真的不如一个键,是吗? :-)
      【解决方案4】:
      ;这是我的版本:模拟 Visual Studio/Windows 键绑定
      ; C-F3 - 开始搜索该点的单词
      ; F3 向前搜索,Shift F3 向后搜索
      
      (setq my-search-wrap nil)
      
      (defun my-search-func (dir)
        (交互的)
        (let* ((text (car search-ring)) newpoint)
              (当我的搜索换行
                   (goto-char (if (= dir 1) (point-min) (point-max)))
                   (setq my-search-wrap nil))
              (setq newpoint (search-forward text nil t dir))
              (如果新点
                (set-mark (if (= dir 1) (- newpoint (length text))
                               (+ newpoint (长度文本))))
                (消息“搜索失败:%s”文本)(叮)
                (setq my-search-wrap 文本))))
      
      (defun my-search-fwd () (交互式) (my-search-func 1))
      (defun my-search-bwd () (交互式) (my-search-func -1))
      
      (defun yank-thing-into-search ()
         (交互的)
         (让((文本(如果标记激活
                (缓冲区子字符串无属性(区域开始)(区域结束))
                       (或(当前词)“”))))
           (when (> (length text) 0) (isearch-update-ring text) (setq my-search-wrap nil)
                  (我的搜索转发))))
      (global-set-key (kbd "") 'my-search-fwd) ; Visual Studio 之类的搜索键
      (global-set-key (kbd "") 'my-search-bwd)
      (global-set-key (kbd "") 'yank-thing-into-search)
      
      

      【讨论】:

        【解决方案5】:

        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)
        

        【讨论】:

          【解决方案6】:

          根据您对我的第一个答案的反馈,这个怎么样:

          (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)
          

          【讨论】:

          • 如果文件中没有比当前匹配的匹配项,我会死。回溯和重现指令here.
          • 上运行时失败。
          • 第一次运行良好,但重复使用时出错:Wrong type argument: arrayp, nil
          • 这与内置的isearch-forward-symbol-at-point有何不同?
          【解决方案7】:

          有很多方法可以做到这一点:

          http://www.emacswiki.org/emacs/SearchAtPoint

          【讨论】:

          • JimBlandy 的解决方案非常接近,但它实际上并没有“搜索”,它在搜索界面上进行攻击。您不能跳转到上一个/下一个,并且不会将单词存储在缓冲区中。啊!
          【解决方案8】:

          有了这个,你应该可以在 isearch 模式下执行 C-*。

          (定义键 isearch-mode-map [?\C-*] 'kmk-isearch-yank-thing) (defun kmk-isearch-yank-thing () “将下一件事从缓冲区拉到搜索字符串中。” (交互的) (let ((string (regexp-quote (thing-at-point 'word)))) (setq isearch-string (连接 isearch 字符串“\\”) isearch 消息 (连接 isearch 消息 (mapconcat 'isearch-text-char-description 细绳 ””)) ;;不要在反向搜索中移动光标。 isearch-yank-flag t)) (setq isearch-regexp t isearch-word nil isearch-success t isearch-adjusted t) (isearch-搜索和更新))

          【讨论】:

          • 这与我在某一时刻所拥有的非常相似(在功能上,如果不是形式上的话) - 问题是 isearch 不能很好地使用多种用途。在再次运行 C-* 之前没有办法“清理” isearch。你最终得到了 search1search2search3 全部神秘连接。
          【解决方案9】:

          我没有尝试过,但是有一些代码 here 叫做 Grep-O-Matic。

          【讨论】:

            猜你喜欢
            • 2010-11-29
            • 2010-09-13
            • 2010-10-01
            • 2010-11-24
            • 2013-08-13
            • 1970-01-01
            • 2013-07-31
            • 2011-11-09
            • 2013-08-20
            相关资源
            最近更新 更多