使用 helm-ag, 绑定单个快捷键,实现在当前文件 -> 当前目录 -> 上一级目录 -> 再上一级目录 ... 中搜索。

这两天解决了一个大问题,和 helm 作者相关的讨论(你没看错,是 helm 作者,不是 helm-ag 作者),见 这里,虽然我其实也给 helm-ag 提了一个 issue, 但是这个作者,似乎不怎么维护了, 不抱什么希望它能修复它。

直接上源代码:

(setq helm-do-ag-on-current-directory-p nil)

(defun helm-quit-and-helm-do-ag-on-current-directory ()
  "Drop into `helm-do-ag' on DEFAULT-DIRECTORY from `helm'."
  (interactive)
  (setq helm-do-ag-on-current-directory-p t)
  (with-helm-alive-p
    (helm-run-after-exit #'helm-do-ag default-directory nil helm-pattern)))

(defun advice-up-on-level-corretly-when-run-helm-do-ag-this-file (orig-fun &rest command)
  "If start helm-ag with `helm-do-ag-this-file', `helm-ag--do-ag-up-one-level' not work,
we have to run `helm-do-ag' on DEFAULT-DIRECTORY first, then up one level function start to work."
  (if helm-do-ag-on-current-directory-p
      (apply orig-fun command)
      (helm-quit-and-helm-do-ag-on-current-directory)))

(advice-add #'helm-ag--do-ag-up-one-level :around #'advice-up-on-level-corretly-when-run-helm-do-ag-this-file)
(global-set-key [(control r)] 'helm-do-ag-this-file)
(define-key helm-do-ag-map [(control r)] 'helm-ag--do-ag-up-one-level)
(add-hook 'helm-quit-hook (lambda () (setq helm-do-ag-on-current-directory-p nil)))

实际效果是这样的。(假设绑定快捷键为 Ctrl+r)

  1. mark 你想 search 的关键字(或者,自动选择当前光标所在的 word/symbol, 这取决于你的 helm-ag 的 helm-ag-insert-at-point 变量设置。

  2. 第一次按下 Ctrl+r, 在当前文件中搜索这个关键字。

  1. 没找到? 再按一次 Ctrl+r, 在当前文件目录内查找。

  1. 还没找到?继续 Ctrl+r, 再上一级文件夹内继续查找。

  1. 重复 4, 直到找到你期望的结果。
2 个赞

稍微跑个题。

去年有段时间helm在macos上特别不稳定,可能和helm的timer有关,导致cpu非常高,就换成ivy+counsel了。虽然很喜欢helm-mini-buffer的效果,也不在乎helm的启动速度,但是还是换成ivy了。helm-ag换成counsel-rg和counsel-ag了。快捷键win+f、win+r绑定在当前目录和项目的root directory查询输入的字符串。

我是从 ivy 换回来的,虽然,也没全换回来 … 有些功能还是在用 ivy.

另外,上面的绑定方案特定于有时候查找没有 project root 标记的查询,才需要一级一级往上查,如果直接 root 查,我是用 helm-git-grep.el,绑定的其他快捷键。

支持!明天试一下

话说helm到底什么情况,之前不是说开发者放弃了吗,最近看到又活跃了

维护者人很 Nice, 用心解答了我很多疑问,而且很多并不是我问他的,他也会给建议,甚至这个 bug 根本跟 helm 自己主项目无关。

试试 all-the-icons-ivy-rich,比 helm-mini 更强大 :smile:

谢谢。一直在用doom-modeline、ivy-rich,也了解过楼主的all-the-icons-ivy-rich.el。出于性能原因,不想在mini-buffer里面显示那么多。

很怀念 helm-mini 里面把 buffers和recent files放到一起的布局,counsel里面 counsel-switch-buffer 和 counsel-buffer-or-recentf 是分开的,都不如 helm-mini 方便。counsel好像也有buffer和recent files一起的,不习惯,也没用。

这个功能我也经常用。用的是counsel-etags-grep-current-directory,不过可以用C-u N M-x counsel-etags-grep-current-directory来从N层父目录开始搜。

find-file-in-project 也有类似的find-file-in-current-directory命令。

all-the-icons-ivy-rich 不会比helm的性能差,显示也是可以自己定制的,包括icon。使用all-the-icons-ivy-rich后, ivy-switch-buffer 会显示当前buffer和recent files,颜色区分非常合理。建议可以尝试。

你的代码在我这边有点小问题:

  1. 标记搜索键字。
  2. helm-do-ag-this-file
  3. C-l, 成功切换至搜索当前目录。
  4. C-l, 询问 "Current directory might be the project root. Continue searching the parent directory? ",回答 y 之后搜索关键字丢失。

如果是 helm-do-ag 则第一次 C-l 无效,第二次 C-l 询问 yes/no 回答 y 搜索关键字同样会丢失。

test-1.el
⋊> emacsq.sh \
   -P helm,helm-ag \
   -M helm-mode --eval "
   (progn
     (toggle-debug-on-error)
     (require 'helm-config)
     (require 'helm-ag)

     (setq helm-do-ag-on-current-directory-p nil)

     (defun helm-quit-and-helm-do-ag-on-current-directory ()
       \"Drop into `helm-do-ag' on DEFAULT-DIRECTORY from `helm'.\"
       (interactive)
       (setq helm-do-ag-on-current-directory-p t)
       (with-helm-alive-p
        (helm-run-after-exit #'helm-do-ag default-directory nil helm-pattern)))

     (defun advice-up-on-level-corretly-when-run-helm-do-ag-this-file (orig-fun &rest command)
       \"If start helm-ag with `helm-do-ag-this-file', `helm-ag--do-ag-up-one-level' not work,
   we have to run `helm-do-ag' on DEFAULT-DIRECTORY first, then up one level function start to work.\"
       (if helm-do-ag-on-current-directory-p
           (apply orig-fun command)
         (helm-quit-and-helm-do-ag-on-current-directory)))

     (advice-add #'helm-ag--do-ag-up-one-level :around #'advice-up-on-level-corretly-when-run-helm-do-ag-this-file)
     (global-set-key (kbd \"C-c f\") 'helm-do-ag-this-file)
     (global-set-key (kbd \"C-c d\") 'helm-do-ag)
     (add-hook 'helm-quit-hook (lambda () (setq helm-do-ag-on-current-directory-p nil))))" -nw

我做了点修改:

  • 修复回答 y 关键字丢失的问题。
  • 修复 helm-do-ag 首次 C-l 无效的问题。
  • 把询问 yes/no 提前到首次 C-l
  • 重命名 advice 函数。

test-2.el
⋊> emacsq.sh \
   -P helm,helm-ag \
   -M helm-mode --eval "
   (progn
     (toggle-debug-on-error)
     (require 'helm-config)
     (require 'helm-ag)

     (setq helm-ag--do-ag-up-one-level-basedir nil)

     (defun helm-ag--do-ag-up-one-level@with-input ()
     \"Advice override `helm-ag--do-ag-up-one-level' to start helm ag with input.\"
     (let ((default-input helm-pattern))
       (if (or helm-ag--do-ag-up-one-level-basedir
               (y-or-n-p \"Current directory might be the project root.  \
   Continue searching the parent directory? \"))
           (with-helm-alive-p
             (helm-run-after-exit
              #'helm-do-ag
              (setq helm-ag--do-ag-up-one-level-basedir
                    (helm-aif helm-ag--do-ag-up-one-level-basedir
                        (helm-basedir (directory-file-name it))
                      (helm-aif helm-ag--default-target ;; search-this-file?
                          default-directory
                        (helm-basedir (directory-file-name default-directory)))))
              nil default-input)))))

     (advice-add #'helm-ag--do-ag-up-one-level :override #'helm-ag--do-ag-up-one-level@with-input)
     (add-hook 'helm-quit-hook (lambda () (setq helm-ag--do-ag-up-one-level-basedir nil)))

     (global-set-key (kbd \"C-c f\") 'helm-do-ag-this-file)
     (global-set-key (kbd \"C-c d\") 'helm-do-ag))" -nw

测试结果符合预期。

1 个赞

这个体验不错,我给我的 color-rg.el 也增加了一个命令 color-rg-rerun-parent-dir Add color-rg-rerun-parent-dir. · manateelazycat/color-rg@d2a0915 · GitHub

在当前目录没有搜索按 o 按键快速在上层目录上搜索,也可以直接按 O 修改搜索目录。

1 个赞

分享个粗制滥造的ivy版的

(defun counsel-rg+-remove-boundaries (text)
  (when (string-prefix-p "\\_<" text)
    (setq text (substring text 3))
    )
  (when (string-suffix-p "\\_>" text)
    (setq text (substring text 0 (- (length text) 3)))
    )
  text
  )

(defun counsel-rg+ (&optional initial-input initial-directory extra-rg-args rg-prompt)
  "Search upwards in the directory tree."
  (interactive)
  (let ((dir (file-name-directory (directory-file-name (or initial-directory default-directory))))
        (text (or ivy-text initial-input))
        )
    (ivy-quit-and-run
      (counsel-rg (counsel-rg+-remove-boundaries text) dir extra-rg-args rg-prompt)
      )
    )
  )

;; 我绑定了C-s到swiper,加上下面两个绑定,就可以用C-s搜索当前buffer以及上级目录了
;; (ivy-define-key counsel-ag-map (kbd "C-s") #'counsel-rg+)  ;; counsel-rg中也可以C-s搜上级目录了
;; (ivy-define-key swiper-map (kbd "C-s") #'counsel-rg+)
2 个赞

好东西阿 :innocent:谢谢楼主,正烦着helm-ag怎么在根目录查找,发现你这个更好用 :innocent:

这个需求我一般还是习惯用rg.el,改变一下目录就行了。

懒猫的 color-rg 也建议试试,好用的很, 这个功能他也加了。