[解决]为懒猫的 blink-search 添加搜索 PDF 等其他文档的后端

背景

使用 Org roam 做笔记,平时使用 counsel 对笔记文件夹中的 Org 文本文件进行文本搜索,但有时需要参考一下其他文档比如 PDF,Office 文件,这时候就需要到专门集中存放PDF,Office 的文件夹中使用能够对它们内容进行搜索的工具搜索,然后查看自己所需的页面。

需求

昨晚在Github发现一个项目,

它可以对一个文件夹中的压缩包,PDF,Office 文件等进行搜索。而我想如果能将该工具整合到懒猫的 blink-search中就好了。将平时存放 PDF 的文件夹设置为 Common Directory,使用 ripgrep-all 搜索关键字,返回文档名和 Page Number,需要预览页面时使用 EAF 打开该页面。

不知道有没有大神对此想法有兴趣,尝试实现一下,顺便惠泽一下大众。

个人不是程序员,刚才看了懒猫 blink-search 的后端的实现,发现看不懂。

虽然我会一点点 Python 和 Elisp,写过一点点插件,不想当伸手党,无奈才疏学浅,力有不逮。

2 个赞

我最开始想写的,但是现在都看纸质书,自己太懒了,不想写,rga确实有用。

我自己要看的书单账太多了,最近没时间大规模投入elisp编程上,抱歉。

ug默认就支持来着

发现 helm 有个 helm-ff-run-pdfgrep 可以一定程度满足我的需求。 安装好依赖命令 pdfgrep。 在 Emacs 中加入以下配置:

(setq helm-pdfgrep-default-read-command 'eaf-open)

(defun helm-pdfgrep-action-1 (_split pageno fname)
  (let* ((buffer-id)
         (pdf-file-name (substring-no-properties fname)))

    (run-hook-with-args-until-success 'helm-pdfgrep-default-read-command pdf-file-name)

    (setq buffer-id
          (car
           (->> (eaf--get-eaf-buffers)
                (-map
                 (lambda (buffer) (with-current-buffer buffer
                                    (when (string= (file-name-nondirectory pdf-file-name) (buffer-name))
                                      eaf--buffer-id))))
                (-filter (lambda (x) (not (equal x #'nil)))))))
    (eaf-call-async "execute_function_with_args" buffer-id "jump_to_page_with_num" (format "%s" pageno))
    ))

在指定的文件夹下,运行 C-x c C-x C-f 运行 helm-find-files,输入 PDF 过滤出所有的 PDF 格式文件,M-a 选中所有的待选项目,M-i p 运行 helm-ff-run-pdfgrep 命令,输入需要在选中的 PDF 文件中搜索的关键字,如示意图中的 :“HMU”。 2022-11-21 11.09.55

C-n,或者 C-p 选择需要预览的结果,然后使用 TAB 调用 helm-pdfgrep-action-1 进行预览。

现在的问题是 eaf 在第一次打开 PDF 时,无法按照我写的代码跳转到指定的页面,如图中所示我希望预览的是文件"HMU 更换.pdf"的第 5 页,然而第一打开 PDF 时,跳转到的是上一次打开该 PDF 所在的第 6 页,需要再按一次 TAB 键,让 helm-pdfgrep-action-1 中的 (eaf-call-async "execute_function_with_args" buffer-id "jump_to_page_with_num" (format "%s" pageno)) 再运行一次,才会调到第 6 页。

也就是说第一次预览时 (eaf-call-async "execute_function_with_args" buffer-id "jump_to_page_with_num" (format "%s" pageno)) 没有按照设想的运行进行跳转。

我猜是 EAF 还没完全打开 PDF,就运行该代码,所以无法跳转,所以如何实现在 EAF 打开 PDF 后再运行 (eaf-call-async "execute_function_with_args" buffer-id "jump_to_page_with_num" (format "%s" pageno))呢?

我查看了 eaf-pdf-viewer 的源代码,发现了 (add-hook 'eaf-pdf-viewer-hook 'eaf-pdf-imenu-setup) 中 eaf-pdf-viewer-hook 这个 hook ,但是不知道如何修改我自己的配置。

eaf-pdf-viewer本身是可以记录上一次地阅读进度的,不会存在第一次打开无法跳转进度的问题。

看看eaf-pdf-viewer怎么打开上次进度的代码就可以解决你的问题。

你先尝试研究,不行我才告诉你怎么做,这样你可以有所成长。

我把 eaf-pdf-viewer.el 所有函数都看了,实在看不出来是哪里实现了恢复阅读进度的功能。 :sweat_smile:

我找到了存放 PDF 阅读进度的 session.json 文件,里面的内容是: "/Users/c/Library/Mobile Documents/iCloud~QReader~MarginStudy/Documents/Python/Design_Patterns_Explained_Simply.pdf": "199.87666666666664:2.2077452044878756:fit_to_width:False:0"

按照我的理解 “199.87666666666664:2.2077452044878756:fit_to_width:False:0” 分别对应的是 scroll_offset, scale, read_mode, inverted_mode。

通过 self.buffer_widget.scroll_offset = float(scroll_offset) 来恢复进度。

eaf 恢复阅读进度并不是通过 (eaf-call-async "execute_function_with_args" buffer-id "jump_to_page_with_num" (format "%s" pageno)) 这样跳到特定页面的方法。

所以和我预期的行为不一致。因为 helm-ff-run-pdfgrep 的结果返回中包含一个 page number,所以还是希望通过跳转 page number,而不是通过 scroll_offset。

所以说, 需要改造 eaf-pdf-viewer 的接口代码, 当指定跳转到某一页的时候, 就不要执行 restore session 的代码, 这样就符合你的要求了。

eaf-pdf-viewer/eaf_pdf_buffer.py at 0007d52730feae665123bd46274f74c102f7a012 · emacs-eaf/eaf-pdf-viewer · GitHub 这一行有一个 arguments 是 Elisp 调用 Python 专门传递额外参数的, 可以给制定页面提交一个额外的参数。

如果符合指定页面参数时, eaf-pdf-viewer/eaf_pdf_buffer.py at 0007d52730feae665123bd46274f74c102f7a012 · emacs-eaf/eaf-pdf-viewer · GitHub 加一个判断, 不要从历史记录中恢复页面就好了。

原理就是这两个地方改一下, 欢迎 PR

我也有类似的需求,所以简单基于blink-search 和 pdf-tools写了一个,待会提交一下PR试试。

不过因为我用的是pdf-tools,不是eaf-pdf-preview,所以可能还得补充一下eaf-pdf-preview这块的高亮和定位

2 个赞

@czqhurricane

首先更新 EAF Pdf Viewer 到 Add eaf-pdf-jump-to-page API. · emacs-eaf/[email protected] · GitHub , 提供了一个 eaf-pdf-jump-to-page 的API, 自动打开文档并定位到某一页。

再更新 blink-search 到 Support EAF pdf viewer. · manateelazycat/[email protected] · GitHub

接着设置 (setq blink-search-grep-pdf-backend 'eaf-pdf-viewer)

就可以直接 blink-search 搜索后用 EAF Pdf Viewer 跳到对应的页数。

大佬出手就是不一样,我憋了半天,模仿来模仿去,写不好。新加的函数 eaf-pdf-jump-to-page 可以和 helm-ff-run-pdfgrep 完美配合,这里留下记录,供他人需要。

(setq helm-pdfgrep-default-read-command 'eaf-pdf-jump-to-page)

(defun helm-pdfgrep-action-1 (_split pageno fname)
  (let* ((pdf-file-name (substring-no-properties fname)))

    (run-hook-with-args-until-success 'helm-pdfgrep-default-read-command pdf-file-name pageno)))

(defun blink-search-grep-pdf-do (file page submatches)
  ;;highlight the matches
  (cond
   ((and (eq blink-search-grep-pdf-backend 'pdf-tools) (featurep 'pdf-tools))
    (blink-search-grep-pdf-pdftool-goto file page submatches))
   ((and (eq blink-search-grep-pdf-backend 'eaf-pdf-viewer) (featurep 'eaf-pdf-viewer))
    (eaf-pdf-jump-to-page file page))
   ;; TODO support other pdf backends
   (t (message "Unknown backend %s" blink-search-grep-pdf-backend))))

blink-search-grep-pdf-do 函数接受的第一个参数 file 路径过长时会变成:"~/Library/Mobile Documents/iCloud~QReader~MarginStudy/Documents/WebDownloads/../../../../../../WDM 80-11-11.pdf" 这种中间的部分路径变成省略号,导致 eaf-pdf-viewer 无法打开相应的文件。真实的路径是 /Users/c/Library/Mobile Documents/iCloud~QReader~MarginStudy/Documents/WebDownloads/WDM 80-11-11.pdf 。不知道为何如此?

截图中红框所示过长的路径变成省略号了?

需要设置 blink-search-grep-pdf-search-paths 解决。

没设置的话应该会用当前所在的路径去搜索,路径显示的话应该会用parse_rg_line去获得相对路径,不过不知道为啥会出现这种情况

还有个疑问要为 blink-search-grep-pdf-search-paths 设置多个路径怎么设置?

    def init_dir(self, search_dir):
        self.search_paths = get_emacs_var("blink-search-grep-pdf-search-paths")
        self.search_paths = self.search_paths if self.search_paths else search_dir
        if type(self.search_paths) is str:
            self.search_paths = [self.search_paths]

self.search_paths 怎么从 blink-search-grep-pdf-search-paths 字符串变成列表? 能不能给个实例。

(setq blink-search-grep-pdf-search-paths "/Users/c/Library/Mobile Documents/iCloud~QReader~MarginStudy/Documents/WebDownloads")

(setq blink-search-grep-pdf-search-paths '("/Users/royokong/arxiv_papers" "/Users/royokong/papers"))

直接用elisp的列表就行了

1 个赞

大佬是搞航空的吗? :grinning:

是的,好像用 Emacs 的各种行业都有。我都记不清当初为啥用 Emacs 了😂。虽然我不是用 Emacs 写代码吃饭,但是论坛逛的很上头啊,经常能看到好玩的新东西。

论坛里面个个都是人才,说话又好听,超喜欢在里面的。:dog:

4 个赞

最后一句话,我好像在哪听过,哈哈哈。