pdf-tools在使用 isearch-forward 根isearch-backward是是可以显示高亮的,然而一旦使用isearch-repeat-forward/backward高亮就没了。evil用户习惯于用n/N来向前/向后搜索,没了这个还是挺不方便的。。
以下是实现方法,有点hack,不过体验良好。
由于实现方法有点hack,难以保证未来(及以前)版本的pdf-tools可以使用,
如果以后不能用的话,就无视此贴吧,大不了用 C-s
或 C-s M-s o
等,
不过pdf-tools看起来不会大改的样子。
功能:
- 按n/N搜索时保持高亮
- 按/键搜索,如果搜索成功,则按回车后不要清除高亮,如果搜索失败,则照常清除高亮。
- 按ESC清除搜索高亮
分有使用evil-collection包和没有使用的情况, 使用evil-collection包还得避免按键绑定被evil-collection覆盖。
先是没有使用evil-collection包的版本:
(with-eval-after-load 'pdf-tools
(defun my/isearch-failed? ()
(or (not isearch-success) isearch-error))
(defvar-local my/pdf-isearch-highlight-matches nil)
(defun my/pdf-isearch-cleanup-highlight ()
(setq my/pdf-isearch-highlight-matches nil)
(pdf-view-redisplay))
(defun my/pdf-isearch-hl-matches-controllable-highlight (orig-fun current matches &optional occur-hack-p)
(funcall orig-fun current matches (or my/pdf-isearch-highlight-matches occur-hack-p)))
(advice-add #'pdf-isearch-hl-matches
:around #'my/pdf-isearch-hl-matches-controllable-highlight)
(defun my/pdf-isearch-repeat-forward (&optional arg)
(interactive "P")
(setq my/pdf-isearch-highlight-matches t)
(isearch-repeat-forward arg))
(defun my/pdf-isearch-repeat-backward (&optional arg)
(interactive "P")
(setq my/pdf-isearch-highlight-matches t)
(isearch-repeat-backward arg))
;; 搜索超过文档结尾时,直接wrap到文档头开始搜索,不要暂停,
;; 如果不这样设置,则搜索超过文档结尾时,暂停的时候,结尾的高亮会不见
;; 估计不难修复,但是这样搞更简单。
(add-hook 'pdf-view-mode-hook
(lambda () (setq-local isearch-wrap-pause 'no)))
(defun my/pdf-view-force-normal-state ()
(interactive)
(evil-force-normal-state)
(my/pdf-isearch-cleanup-highlight))
(advice-add #'pdf-isearch-mode-initialize
:before
(lambda (&rest args)
"在正常使用C-s等进行搜索的时候,重置 `my/pdf-isearch-highlight-matches'。"
(setq my/pdf-isearch-highlight-matches nil)))
(defun my/pdf-isearch-mode-cleanup ()
"按/键搜索,如果搜索成功,则按回车后不要清除高亮,如果搜索失败,则清除高亮。"
(pdf-isearch-active-mode -1)
(when (my/isearch-failed?) (pdf-view-redisplay)))
(advice-add #'pdf-isearch-mode-cleanup
:override #'my/pdf-isearch-mode-cleanup)
(evil-define-key 'normal pdf-view-mode-map
;; 按n/N 向前/向后搜索,按ESC则返回normal state的同时清除搜索高亮
(kbd "n") #'my/pdf-isearch-repeat-forward
(kbd "N") #'my/pdf-isearch-repeat-backward
(kbd "<escape>") #'my/pdf-view-force-normal-state)
;; 或者,如果你有用general.el包的话,可以改用`general-def'定义按键绑定。
;; (general-def 'normal 'pdf-view-mode-map
;; "n" #'my/pdf-isearch-repeat-forward
;; "N" #'my/pdf-isearch-repeat-backward
;; "<escape>" #'my/pdf-view-force-normal-state)
)
接下来是使用evil-collection包的版本,要避免定义的按键绑定被evil-collection覆盖,改一个地方:
(with-eval-after-load 'pdf-tools
;; ...此处省略,和前面一样
;; 把下面这段
;; (evil-define-key 'normal pdf-view-mode-map
;; ;; 按n/N 向前/向后搜索,按ESC则返回normal state的同时清除搜索高亮
;; (kbd "n") #'my/pdf-isearch-repeat-forward
;; (kbd "N") #'my/pdf-isearch-repeat-backward
;; (kbd "<escape>") #'my/pdf-view-force-normal-state)
;; 改成下面这段
(defmacro my/modify-evil-collection-key-bindings (mode &rest body)
(declare (indent defun))
(let ((feature-name-var (gensym "feature-name"))
(mode-var (gensym "mode"))
(mode-str-var (gensym "mode-str"))
(hook-fun-name-var (gensym "hook-fun-name-var")))
`(let* ((,mode-var ,mode)
(,mode-str-var (symbol-name ,mode-var))
(,feature-name-var (intern (concat "evil-collection-" ,mode-str-var))))
(if (featurep ,feature-name-var)
;; 在当前evil-collection的实现中,如果feature名对应的文件
;; 已经加载,则对应的按键setup函数也已经调用,故在这种情况下,
;; 我们可以直接执行body。
(progn ,@body)
;; 否则,添加hook到`evil-collection-setup-hook',
;; 判断mode名匹配后执行body。
(let ((,hook-fun-name-var
;; 这里使用gensym生成hook函数名,由于生成的符号是未intern的,
;; 这会导致evil-collection无法调用我们的hook函数,于是我们需要
;; 提前intern下,同时由于gensym会在符号名后面附加一个全局计数器,
;; 加上我们的函数有前缀 my/ ,因此一般不会产生名称冲突。
(intern
(symbol-name
(gensym (concat "my/modify-evil-collection-key-bindings-"
,mode-str-var))))))
;; 使用defun底层使用的defalias,不然defun不会eval函数名参数。
(defalias ,hook-fun-name-var
(lambda (mode keymaps)
(when (eq mode ,mode-var)
,@body))
(concat "修改evil-collection设置的" ,mode-str-var "的按键绑定。"))
(add-hook 'evil-collection-setup-hook ,hook-fun-name-var))))))
(my/modify-evil-collection-key-bindings 'pdf
(evil-define-key 'normal pdf-view-mode-map
;; 按n/N 向前/向后搜索,按ESC则返回normal state的同时清除搜索高亮
(kbd "n") #'my/pdf-isearch-repeat-forward
(kbd "N") #'my/pdf-isearch-repeat-backward
(kbd "<escape>") #'my/pdf-view-force-normal-state))
)
2 个赞
完美解决 ,pdf-tools现在基本不更新了,应该能稳定用很长时间。
之前忘记实现记住搜索方向了,每次搜索不管是按 /
还是 ?
开始搜索,之后按n
都是向后搜索,按N
都是向前搜索,正常如果按 ?
开始搜索则应该反过来,补上。
不用evil-collection包的版本:
(with-eval-after-load 'pdf-tools
(defun my/isearch-failed? ()
(or (not isearch-success) isearch-error))
(defvar-local my/pdf-isearch-highlight-matches nil)
(defun my/pdf-isearch-cleanup-highlight ()
(setq my/pdf-isearch-highlight-matches nil)
(pdf-view-redisplay))
;; 模仿occur-hack-p,注入变量以控制高亮
(defun my/pdf-isearch-hl-matches-controllable-highlight (orig-fun current matches &optional occur-hack-p)
(funcall orig-fun current matches (or my/pdf-isearch-highlight-matches occur-hack-p)))
(advice-add #'pdf-isearch-hl-matches
:around #'my/pdf-isearch-hl-matches-controllable-highlight)
(defvar-local my/pdf-isearch-forward? t)
(defun my/pdf-isearch-forward (&optional regexp-p no-recursive-edit)
(interactive "P\np")
(setq my/pdf-isearch-forward? t)
(isearch-forward regexp-p no-recursive-edit))
(defun my/pdf-isearch-backward (&optional regexp-p no-recursive-edit)
(interactive "P\np")
(setq my/pdf-isearch-forward? nil)
(isearch-backward regexp-p no-recursive-edit))
;; 下面参数arg,原接口是raw prefix,不是数字prefix,
;; 这里保持一致,我不知道怎么反转raw prefix(不用hack的方式,
;; 比如操作raw prefix列表),不然my/pdf-isearch-repeat-backward
;; 可以简单反转下prefix调用my/pdf-isearch-repeat-backward,而不用写两遍。
(defun my/pdf-isearch-repeat-forward (&optional arg)
(interactive "P")
(setq my/pdf-isearch-highlight-matches t)
(if my/pdf-isearch-forward?
(isearch-repeat-forward arg)
(isearch-repeat-backward arg)))
(defun my/pdf-isearch-repeat-backward (&optional arg)
(interactive "P")
(setq my/pdf-isearch-highlight-matches t)
(if my/pdf-isearch-forward?
(isearch-repeat-backward arg)
(isearch-repeat-forward arg)))
;; 搜索超过文档结尾时,直接wrap到文档头开始搜索,不要暂停,
;; 如果不这样设置,则搜索超过文档结尾时,暂停的时候,结尾的高亮会不见
;; 估计不难修复,但是这样搞更简单。
(add-hook 'pdf-view-mode-hook
(lambda () (setq-local isearch-wrap-pause 'no)))
(defun my/pdf-view-force-normal-state ()
(interactive)
(evil-force-normal-state)
(my/pdf-isearch-cleanup-highlight))
(advice-add #'pdf-isearch-mode-initialize
:before
(lambda (&rest args)
"在正常使用C-s等进行搜索的时候,重置 `my/pdf-isearch-highlight-matches'。"
(setq my/pdf-isearch-highlight-matches nil)))
(defun my/pdf-isearch-mode-cleanup ()
"按/键搜索,如果搜索成功,则按回车后不要清除高亮,如果搜索失败,则清除高亮。"
(pdf-isearch-active-mode -1)
(when (my/isearch-failed?) (pdf-view-redisplay)))
(advice-add #'pdf-isearch-mode-cleanup
:override #'my/pdf-isearch-mode-cleanup)
(evil-define-key 'normal pdf-view-mode-map
;; 按/或者?开始向前或者向后搜索并记住搜索方向,
;; 按n/N继续向前/向后搜索,
;; 按ESC则返回normal state的同时清除搜索高亮
(kbd "/") #'my/pdf-isearch-forward
(kbd "?") #'my/pdf-isearch-backward
(kbd "n") #'my/pdf-isearch-repeat-forward
(kbd "N") #'my/pdf-isearch-repeat-backward
(kbd "<escape>") #'my/pdf-view-force-normal-state)
;; 或者,如果你有用general.el包的话,可以改用`general-def'定义按键绑定。
;; (general-def 'normal 'pdf-view-mode-map
;; "/" #'my/pdf-isearch-forward
;; "?" #'my/pdf-isearch-backward
;; "n" #'my/pdf-isearch-repeat-forward
;; "N" #'my/pdf-isearch-repeat-backward
;; "<escape>" #'my/pdf-view-force-normal-state)
)
接下来是使用evil-collection包的版本,要避免定义的按键绑定被evil-collection覆盖,改一个地方:
(with-eval-after-load 'pdf-tools
;; ...此处省略,和前面一样
;; 把下面这段
;; (evil-define-key 'normal pdf-view-mode-map
;; ;; 按/或者?开始向前或者向后搜索并记住搜索方向,
;; ;; 按n/N继续向前/向后搜索,
;; ;; 按ESC则返回normal state的同时清除搜索高亮
;; (kbd "/") #'my/pdf-isearch-forward
;; (kbd "?") #'my/pdf-isearch-backward
;; (kbd "n") #'my/pdf-isearch-repeat-forward
;; (kbd "N") #'my/pdf-isearch-repeat-backward
;; (kbd "<escape>") #'my/pdf-view-force-normal-state)
;; 改成下面这段
(defmacro my/modify-evil-collection-key-bindings (mode &rest body)
(declare (indent defun))
(let ((feature-name-var (gensym "feature-name"))
(mode-var (gensym "mode"))
(mode-str-var (gensym "mode-str"))
(hook-fun-name-var (gensym "hook-fun-name-var")))
`(let* ((,mode-var ,mode)
(,mode-str-var (symbol-name ,mode-var))
(,feature-name-var (intern (concat "evil-collection-" ,mode-str-var))))
(if (featurep ,feature-name-var)
;; 在当前evil-collection的实现中,如果feature名对应的文件
;; 已经加载,则对应的按键setup函数也已经调用,故在这种情况下,
;; 我们可以直接执行body。
(progn ,@body)
;; 否则,添加hook到`evil-collection-setup-hook',
;; 判断mode名匹配后执行body。
(let ((,hook-fun-name-var
;; 这里使用gensym生成hook函数名,由于生成的符号是未intern的,
;; 这会导致evil-collection无法调用我们的hook函数,于是我们需要
;; 提前intern下,同时由于gensym会在符号名后面附加一个全局计数器,
;; 加上我们的函数有前缀 my/ ,因此一般不会产生名称冲突。
(intern
(symbol-name
(gensym (concat "my/modify-evil-collection-key-bindings-"
,mode-str-var))))))
;; 使用defun底层使用的defalias,不然defun不会eval函数名参数。
(defalias ,hook-fun-name-var
(lambda (mode keymaps)
(when (eq mode ,mode-var)
,@body))
(concat "修改evil-collection设置的" ,mode-str-var "的按键绑定。"))
(add-hook 'evil-collection-setup-hook ,hook-fun-name-var))))))
(my/modify-evil-collection-key-bindings 'pdf
(evil-define-key 'normal pdf-view-mode-map
;; 按/或者?开始向前或者向后搜索并记住搜索方向,
;; 按n/N继续向前/向后搜索,
;; 按ESC则返回normal state的同时清除搜索高亮
(kbd "/") #'my/pdf-isearch-forward
(kbd "?") #'my/pdf-isearch-backward
(kbd "n") #'my/pdf-isearch-repeat-forward
(kbd "N") #'my/pdf-isearch-repeat-backward
(kbd "<escape>") #'my/pdf-view-force-normal-state))
)
1 个赞