Question: Why does face-attribute
interfer with isearch filter predicates?
The following code is part of my answer to the question about searching for bold text.
If I set the variable isearch-face-bold-p-fun
to the predicate function isearch-face-p
searching the Emacs man page for daemon
with isearch-bold-forward
works like expected. Isearch stops at bold occurences of the word daemon
.
If I set isearch-face-bold-p-fun
to isearch-face-bold-p
instead and search the Emacs man page for daemon
no matches are found.
(There are even stranger effects in other constructed examples.)
The main difference between isearch-face-p
and isearch-face-bold-p
is the call of face-bold-p
which works with face-attribute
. That motivates my question.
(defun isearch-face-p (start end)
"Return non-nil if face at start-position is bold."
(save-excursion
(goto-char start)
(face-at-point t t)))
(defun isearch-face-bold-p (start end)
"Return non-nil if face at start-position is bold."
(save-excursion
(goto-char start)
(cl-some (lambda (face) (and (facep face) (face-bold-p face))) (face-at-point t t))))
(defvar isearch-face-bold-p-fun #'isearch-face-bold-p
"Isearch filter for bold text.
The value should be a function suitable for `isearch-filter-predicate'.")
(defun isearch-bold-forward-end-fun ()
"Uninstall isearch filter `isearch-face-bold-p'."
(remove-function (local 'isearch-filter-predicate) isearch-face-bold-p-fun)
(remove-hook 'isearch-mode-end-hook #'isearch-bold-forward-end-fun))
(defun isearch-bold-forward ()
"Like `isearch-forward' only accepting bold matches."
(interactive)
(add-function :after-while (local 'isearch-filter-predicate) isearch-face-bold-p-fun)
(add-hook 'isearch-mode-end-hook #'isearch-bold-forward-end-fun)
(call-interactively #'isearch-forward))
Tested with emacs -Q Emacs version GNU Emacs 26.2 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.22.30) of 2019-04-12.
EDIT:
Drew solved why the search in the man buffer for Emacs failed.
Pityngly that is not the only case that fails.
In the following code I adapt Drew's proposal to add the non-nil INHERIT
argument to face-bold-p
.
(defun isearch-face-p (start end)
"Return non-nil if face at start-position is bold."
(save-excursion
(goto-char start)
(face-at-point nil t)))
(defun isearch-face-bold-p (start end)
"Return non-nil if face at start-position is bold."
(save-excursion
(goto-char start)
(cl-some (lambda (face) (and (facep face) (face-bold-p face nil t))))))
(defcustom isearch-face-bold-p-fun #'isearch-face-bold-p
"Isearch filter for bold text.
The value should be a function suitable for `isearch-filter-predicate'."
:group 'isearch
:type 'function)
(defun isearch-bold-forward-end-fun ()
"Uninstall isearch filter `isearch-face-bold-p'."
(remove-function (local 'isearch-filter-predicate) isearch-face-bold-p-fun)
(remove-hook 'isearch-mode-end-hook #'isearch-bold-forward-end-fun))
(defun isearch-bold-forward ()
"Like `isearch-forward' only accepting bold matches."
(interactive)
(add-function :after-while (local 'isearch-filter-predicate) isearch-face-bold-p-fun)
(add-hook 'isearch-mode-end-hook #'isearch-bold-forward-end-fun)
(call-interactively #'isearch-forward))
New Test:
Isearch for bold
with M-x
isearch-bold-forward
starting at the beginning of the following org snippet.
Point jumps from the b
of the first occurrence of bold
to the o
of the second (non-bold) occurrence.
Some of the text here is **bold**.
Some intermediate text.
We do not need to bold everything.
Here is some more **bold** text.
Local Variables:
mode: org
End: