Homepage: https://www.emacswiki.org/emacs/download/find-where.el
Author: Drew Adams
Updated:
Find where something is true
Find where something is true.
Get or go to the next place where some predicate is satisfied.
But first, you don't really need this library! ;-)
In Emacs and Emacs Lisp there are multiple ways to find things.
And in many cases it will be simpler or more efficient to use
another way than to take advantage of this library.
What this library offers is some convenience sometimes, and a
certain kind of generality: Specify what you want to find by a
predicate. The predicate is tested at successive places, forward
or backward, until it is satisfied.
By default, the forward and backward movement is among buffer
positions, in the usual sense. And by default, each movement
before testing is just one character. This is the minimum
movement needed to get past the current position (which is often
the last place found).
Moving only one character and testing is obviously not very
efficient, but it is all that can be done in the general case.
When you know a way to move farther before testing and to be sure
there is no need to test closer, you can take advantage of that by
providing for the appropriate movement in the predicate you
provide or in optional forward and backward movement functions.
Clearly, this move-one-char-and-test approach is not the way to go
for ordinary string searching. Emacs uses an efficient,
Boyer-Moore string-search algorithm (see
https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm),
which essentially moves forward in chunks that are as long as what
your search string matches, rather than moving just a character at
a time before each match attempt.
So if you want to search for a sequence of characters, just use
`(re-)search-forward' or similar. And if you need an additional
test at a match position (e.g., check a text or overlay property)
you can easily add that. So forget about this library for
ordinary buffer search.
Still, you might find this library convenient for some things,
even in cases where there is an easy alternative. The abstraction
of defining a destination by a predicate that holds there can be
helpful.
So just what does it mean to "find where something is true"? Find
what? Well, the what is defined by your predicate. What is
found, that is, returned, is both the position (where) and
whatever the predicate returns as a non-nil value. For example,
you can find a text THING, such as the next vector with 13
elements, and have it returned along with its bounds (start and
end positions).
The library defines some general functions to find and return the
next or previous where-plus-what, which you provide with a
defining predicate. And it defines corresponding commands to go
to the next or previous such location.
The commands have names that start (after prefix `fw-') with
`to-'. The corresponding non-interactive functions generally have
the same names without the `to-'. For example, function
`fw-next-where' returns the next position and data that satisfy a
predicate, and command `fw-to-next-where' goes there.
By default, movement is to the start position of something, but
really the where, in relation to its what, is up to you. The
default behavior is thus different from the standard Emacs
`forward-THING' and `backward-THING' behavior, which moves just
past the THING rather than just to it.
This default behavior applies to all functions provided here, not
just to those (`fw-next-thing' etc.) that find buffer THINGs. For
THINGs, `find-where.el' requires library `thingatpt+.el', which
uses and enhances standard library `thingatpt.el'.
When repeated, the commands reuse the same predicate as the last
time (it is the value of variable `fw-last-pred'), but a plain
prefix argument (`C-u') makes them prompt you for the predicate to
use. The predicate you enter must accept a buffer position (the
position to test) as its first argument.
A typical use might check something about the character at (i.e.,
after) that position.
The THING-related function `fw-next-thing' just uses
`fw-next-where' with a predicate that tests whether the position
is at the start of a THING, where being at the start also means
that the previous buffer position is not on the same thing (not
just the same type of thing).
For instance, if the THING type passed to `fw-next-thing' is
`list' then it returns the start position of the next list (as
well as the list text as a string and its end position).
When repeated, the THING commands reuse the same THING type as the
last time (it is the value of variable `fw-last-thing'), but a
plain prefix argument (`C-u') makes them prompt you for the THING
type to use.
You can bind any of the commands defined here to keys, of course.
But you can also easily define other commands that make use of
them, and bind those commands to keys.
For example, here's a simple command that moves to the start of
the next use of face `font-lock-doc-face':
(defun to-next-doc-face (n)
"Move to next occurrence of `font-lock-doc-face'.
With numeric prefix arg N, move to the Nth next occurrence."
(interactive "p")
(fw-to-next-where #'doc-face-start-p nil nil n))
where the predicate is defined like so:
(defun doc-face-start-p (position)
"Return non-nil if char at POSITION starts `font-lock-doc-face'.
That is, it has that face, and any char just before it does not."
(and (eq (get-text-property position 'face) 'font-lock-doc-face)
(or (= position (point-min))
(not (eq (get-text-property (1- position) 'face)
'font-lock-doc-face)))))
Note the use here of two complementary tests within the predicate.
The character at the tested position must pass the test (having
property `font-lock-doc-face'), and the preceding char, if there
is one, must NOT pass the test. This means that
`to-next-doc-face' finds the _first_ character that passes the
test. This is typical of a predicate used with `find-where.el'
functions.
For this reason, you can use helper function `fw-test-start-p' to
take care of that true-here-but-not-just-before-here logic. It
takes the position to test and a predicate as arguments. The
predicate must be true at the position and false just before the
position, for `fw-test-start-p' to be true (return non-nil).
Using `fw-test-start-p', `doc-face-start-p' becomes just this:
(defun doc-face-start-p (position)
"Return non-nil if char at POSITION starts `font-lock-doc-face'.
That is, it has that face, and any char just before it does not."
(fw-test-start-p position
(lambda ()
(eq (get-text-property (point) 'face)
'font-lock-doc-face))))
The predicate arg to `fw-to-next-where' can accept additional
arguments, besides the position. So you can use a predicate that
accepts, as argument, the face to look for, as well as the
position to test. For example:
(defun face-start-p (position face)
"Return non-nil if the character at POSITION starts FACE.
That is, it has FACE, and any character just before it does not."
(and (eq (get-text-property position 'face) face)
(or (= position (point-min))
(not (eq (get-text-property (1- position) 'face)
face)))))
Or simplified using `fw-test-start-p':
(defun face-start-p (position face)
"Return non-nil if the character at POSITION starts FACE.
That is, it has FACE, and any character just before it does not."
(fw-test-start-p position
`(lambda ()
(eq (get-text-property (point) 'face)
',face))))
(defvar last-face nil "Last face used by `to-next-face'.")
(defun to-next-face (arg)
"Move to next text-property occurrence of face `last-face'.
With plain `C-u', prompt for the face to assign to `last-face'.
With numeric prefix arg N, move to the Nth next occurrence."
(interactive "P")
(if (or (consp arg) (not last-face))
(setq last-face (read-face-name "Face: ")
arg 1)
(setq arg (prefix-numeric-value arg)))
(fw-to-next-where #'face-start-p nil (list last-face) arg))
[Note: Text property `face' can actually have a list of faces as
its value, so instead of using an `eq' text in those `*-start-p'
functions a more realistic example would test for the particular
face using both `eq' and `memq' (return true if either is true).]
Now recall that the way `fw-to-next-where' works by default is to
move forward only one char and then test with the predicate. This
is not very efficient, but it is all that can be done, unless
there is some way to move farther forward before testing and be
sure there is no need to test closer.
In the case of testing for a given text property (or overlay
property) at a buffer position there is such a better way: use
function `next-single-property-change'. Using that, we can define
the forward-movement function `to-next-face-prop', which we can
pass to `to-next-face' to override the default one-char movement
(`forward-char').
(defun to-next-face-prop ()
"Go to next change in `face' text property."
(let ((pos (next-single-property-change (point) 'face)))
(when pos (goto-char pos))))
(defun to-next-face (arg)
(interactive "P")
(if (or (consp arg) (not last-face))
(setq last-face (read-face-name "Face: ")
arg 1)
(setq arg (prefix-numeric-value arg)))
(fw-to-next-where #'face-start-p nil (list last-face) arg
nil nil nil #'to-next-face-prop))
As an example of defining a next-THING command, this defines a
command to move forward among sexps:
(defun to-next-sexp (n)
"Go to next start of a sexp."
(interactive "p")
(fw-to-next-thing 'sexp nil n))
And this moves among strings:
(defun to-next-string (n)
"Go to next start of a string."
(interactive "p")
(fw-to-next-thing 'string nil n))
Note that the various `fw-to-next-*' and `fw-to-previous-*'
commands move to the _beginning_ of the next or previous place
where something is true. For example, if you use `fw-next-thing'
with THING `word' then the cursor moves to the beginning of each
word. This is different from typical Emacs `forward-*' and
`backward-*' commands, which move _past_ the end or the beginning
of something.
Typical Emacs `forward-*' commands essentially perform the
following sequence of actions, expressed in terms of `fw-next-*':
1. While some predicate PRED-X is NOT true, do `fw-to-next-X'.
2. Do `fw-to-next-NOT-X'.
Step 1 moves to the next place X is true (e.g., a word beginning).
Step 2 moves just past where X continues to be true (e.g., just
after the end of the word).
Partly as a way of illustrating this, commands `fw-downward-word'
and `fw-upward-word' act like Emacs `forward-word' and
`backward-word', but they move through text vertically, not
horizontally. They are defined using steps 1 and 2.
Commands defined here:
`fw-downward-word', `fw-to-column-down', `fw-to-column-up',
`fw-to-next-thing', `fw-to-next-where',
`fw-to-next-where-vertical', `fw-to-previous-thing',
`fw-to-previous-where', `fw-to-previous-where-vertical',
`fw-upward-word'.
Non-interactive functions defined here:
`fw--next/prev-thing', `fw--next/prev-where',
`fw--read-predicate', `fw--to-next/prev-thing',
`fw--to-next/prev-where', `fw-next-thing', `fw-next-where',
`fw-next-where-vertical', `fw-not-word-char-after-p',
`fw-not-word-char-before-p', `fw-previous-thing',
`fw-previous-where', `fw-previous-where-vertical',
`fw-test-start-p', `fw-thing-start-p', `fw-word-char-after-p',
`fw-word-char-before-p'.
Internal variables defined here:
`fw-last-pred', `fw-last-thing'.