[求助] Emacs下怎么对文件夹下所有pdf文件进行grep操作

有时候需要对多个pdf文件进行文本搜索,选择文件后跳转到pdf指定页面。尝试过以下几个方案。

  1. consult + ripgrep-all, 代码来自这里
;; helper function for ripgrep-all
(defun consult--ripgrep-all (&optional dir initial)
  "Search with `rga' for files in DIR where the content matches a regexp.
The initial input is given by the INITIAL argument. See `consult-grep'
for more details."
  (interactive "P")
  (consult--grep "Ripgrep-all" #'consult--ripgrep-make-builder dir initial))

;;;###autoload
(defun consult-ripgrep-all ()
  ;; Bind to a key
  (interactive)
  (let ((consult-ripgrep-args "rga --null --line-buffered --color=never --max-columns=1000 --path-separator /   --smart-case --no-heading --line-number ."))
    (consult--ripgrep-all)))

搜索后的呈现形式还是挺清晰的,每个pdf文件分成一组,每个匹配一行,唯一的问题是按回车不支持跳转到pdf文件相应的页面。不知道怎么提取文件路径和页码信息利用embark实现pdf跳转。

  1. consult-recoll

Recoll 需要预先索引给定的文件夹,我通常知道在哪个文件夹搜索,如果索引所有含有pdf文件的文件夹,筛选搜索结果的时候不太方便。

  1. pdfgrep

这个支持pdf跳转到指定的页码,不过搜索结果的呈现形式不太好看,每个匹配一行,这一行里面前半部分是文件路径,后面是匹配的文本,但文件路径可以太长,后面的文本匹配部分容易看不清。但又不能隐藏文件名,因为需要配合文件名判断候选项。

  1. blink-search

blink-search有一个rga的后端,不过大佬的插件感觉用起来有门槛,在doom-emacs光安装就花了些功夫。

(package! blink-search
  :recipe (:host github
           :repo "manateelazycat/blink-search"
           :files ("*.py" "*.el" "core" "backend" “icons”)))

也有一些问题,搜索结果没有像consult那样分组,每一条匹配前半部分是文件路径,后半部分是匹配的文本,看起来不清晰,筛选也不太方便,consult按C+M+j可以直接跳到下一个分组。另外blink-search跳转到pdf好像有问题,如果候选项没有在第一屏显示,后面按C+n选中候选项,按回车好像无法跳转。

可以给blink-search报个issue,我有时间改进一下。

提了两个issue,大佬有空可以看一下。

需要安装pdf-tools, 运行M-x my-pdfgrep-in-directory, 只用了completing-read,所以可以和helm/ivy/counsult等框架自由搭配,

(defvar my-pdfgrep-program "pdfgrep"
  "Pdf grep program.")

(defvar my-pdfgrep-options "-H -n"
  "Pdf grep program options.")

(defvar my-pdfgrep-ignore-case t
  "Ignore case when grepping pdf.")

(defun my-pdfgrep-in-directory ()
  "Grep pdf files in current or specific directory."
  (interactive)
  (let* ((root (read-directory-name "Directory: "))
         (default-directory root)
         (case-fold-search t)
         (keyword (read-from-minibuffer "Keyword: " (thing-at-point 'symbol)))
         (cmd (format "%s %s %s -r %s"
                      my-pdfgrep-program
                      my-pdfgrep-options
                      (if my-pdfgrep-ignore-case "-i" "")
                      keyword))
         (lines (split-string (shell-command-to-string cmd) "[\r\n]+" t))
         path
         page
         text
         cands
         selected)
    (cond
     ((and lines (> (length lines) 0))
      (dolist (line lines)
        (when (string-match "^\\(.+\\.pdf\\):\\([0-9]+\\):\\(.*\\)$" line)
          (setq path (match-string 1 line))
          (setq page (match-string 2 line))
          (setq text (match-string 3 line))
          (push (cons (format "%s:%s: %s"
                              (file-name-base path)
                              page
                              (string-trim text))
                      (list path (string-to-number page) text))
                cands)))
      (setq cands (nreverse cands))
      (when (setq selected (completing-read (format "Grep in %s:" root)
                                            cands))
        (setq selected (cdr (assoc selected cands)))
        (find-file (nth 0 selected))
        (unless (featurep 'pdf-tools) (require 'pdf-tools))
        (pdf-view-goto-page (nth 1 selected))
        (pdf-isearch-hl-matches nil (pdf-isearch-search-page keyword) t)))

     (t
      (message "Found nothing.")))))

大佬都是从头自己写函数 :+1:,不过这个函数还没有达到我想要的效果,这个函数好像只是不显示文件夹路径,依然是文件名和匹配文本是一行,没有实现按文件分组,然后分组下只显示匹配的文本。我的pdf文件名字大多比较长,这种显示方式还是不太清晰。另外,这么函数好像不是异步的,对于文件较多的文件夹,需要等一下才能输出结果。

你应该要的是color-rg + rga 的效果

你说的 blink-search 跳转的问题已经修复了。

如果你要分组的话, 我觉得可以改造一下 color-rg.el 让其支持 rga, color-rg的交互模式应该是你期望的。

之前听过color-rg,不过以为侧重重构,尝试了一下,不知道加上rga是否合适。

另外说一下个人试用感受,“菜单栏”有一些快捷提示挺好的。文件之间的空行可能是刻意留的吧,不过我个人感觉有点浪费空间。另外,view mode的快捷键hl的设定对于我(evil用户)来说不太自然,jk对应上和下,没什么问题,hl对应左和右,但h绑定jump to next file, l绑定jump to previous file,感觉还是有点不太自然,相当于左(h)对应下(next file),右(l)对应上(previous file)。当然这都是个人使用习惯问题。

blink-search 的grep结果如下,目前显示还清晰,不过如果文件名比较长,或者文件夹层数较深,匹配的文本就显示不全。

这是consult grep的结果,是分组显示的,还是挺清晰的。

color-rg 的默认按键你可以改。

blink-search 是用于替换 helm 的设计, 它的主要作用是快速融合不同的搜索后端, 它的设计不适合再进行文件分组, 那样会把这个搜索框架搞的过于复杂。

你要的效果应该是 color-rg 加上对 rga 后端支持, 同时 rga 启用时, 重构的功能要禁用。

原来是这个原因,了解了解。

color-rg是否可以设置如ripgrep中-A, --after-context 显示匹配内容后的 行和-B, --before-context 显示匹配内容前的 行?

还有一个比较偷懒的办法是把所有 pdf 拼成一个文件: gs -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=combine.pdf -dBATCH *.pdf

后面就像平常一样开 zathura 或者 pdf-occur 了。

(来自开卷考试的经验)