(Re)generate: (find-strange-functions-intro)
Source code: (find-efunction 'find-strange-functions-intro)
More intros: (find-eev-quick-intro)
(find-eev-intro)
(find-eepitch-intro)
This buffer is _temporary_ and _editable_.
It is meant as both a tutorial and a sandbox.
Prerequisites:
(find-elisp-intro)
TODO: update this:
http://anggtwu.net/2024-find-lgreps.html
Note: this is a work in progress!
Both this intro and the HTML page above are unfinished!
1. Introduction: videos
We saw in
(find-eev-quick-intro "9.1. `code-c-d'")
(find-eev-quick-intro "9.1. `code-c-d'" "find-code-c-d")
that each call to `code-c-d' defines several functions, and we
can use a `find-code-c-d' to display the code that the
corresponding `code-c-d' would run.
I will refer to the functions defined by a call to `code-c-d' as
"functions defined in strange ways", or, to abbreviate, as
"strange functions".
Here is an another example. This a sexp whose "head" is a
strange function, and that plays a certain video:
(find-2024gitvideo "00:34")
It was defined by a call to `code-1stclassvideo'. Try the
`find-code-1stclassvideo' below:
;; (find-code-1stclassvideo "2024git")
(code-1stclassvideo "2024git")
It defines three associated functions - `find-2024gitvideo', that
plays the video, and `find-2024githsubs' and `find-2024gitlsubs',
that display the subtitles of the video in different formats.
Sometimes we want a quick way to start by one of the sexps below
and produce one of the other two,
(find-2024gitvideo "00:34")
(find-2024githsubs "00:34")
(find-2024gitlsubs "00:34")
and sometimes we want a way to jump to the place in which these
`find-2024git*' functions were defined... note that the three
`find-efunctionpp's below do work,
(find-efunctionpp 'find-2024gitvideo)
(find-efunctionpp 'find-2024githsubs)
(find-efunctionpp 'find-2024gitlsubs)
(find-efunction 'find-2024gitvideo)
(find-efunction 'find-2024githsubs)
(find-efunction 'find-2024gitlsubs)
but the `find-efunction's don't - they open the file in which the
`find-2024git*'s were defined, but they can't find the exact
point in which the `code-1stclassvideo' was run.
2. Here
This is a sexp that starts with a strange function - or, to
abbreviate, a "strange sexp":
(find-2024gitvideo "00:34")
If you type `M-h M-s' on the line above the `M-h M-s' will
display a lot of information about the "strange sexp here".
It will run this,
(find-sf-links '(find-2024gitvideo "00:34"))
and that will display a temporary buffer with the variants of
that sexp, some information about the video "2024git", the
definitions of the (strange) functions `find-2024gitvideo',
`find-2024gitlsubs', and `find-2024githsubs', and a few other
things.
At this moment only a few kinds of strange sexps are supported.
Try:
(find-sf-links '(find-2024gitvideo "00:34"))
(find-sf-links '(find-eev "eev-strange-functions.el"))
and:
(code-pdf-page "livesofanimals" "~/Coetzee99.pdf")
(code-pdf-text "livesofanimals" "~/Coetzee99.pdf" -110)
(find-sf-links '(find-livesofanimalspage (+ -110 113) "LECTURE I."))
(find-sf-links '(find-livesofanimalstext (+ -110 113) "LECTURE I."))
and try using `M-h M-s' on the two sexps below:
(find-eev "eev-strange-functions.el")
(find-livesofanimalspage (+ -110 113) "LECTURE I.")
3. Debugging
Remember that both `M-h M-h' and f8 treat a prefix argument as a
request to enter a kind of debugging mode:
(find-here-links-intro "8. Debugging")
(find-eev "eepitch.el" "debug")
`M-h M-s' follows the same convention. Try `M-1 M-h M-s' on the
sexp below:
(find-2024gitvideo "00:34")
You will get the same temporary buffer that you get with:
(find-sf-debug-links '(find-2024gitvideo "00:34"))
It shows the values of several variables and has several links to the
source code. For example, this one,
(find-evariable 'ee-hprog-for-sf)
that shows the "hprogram" that that `M-h M-s' uses to detect which
kind of strange sexp we have "here".
4. The load-history
Try `M-h M-s' on these sexps again:
(find-2024gitvideo "00:34")
(find-eev "eev-strange-functions.el")
You will see that the temporary buffers have blocks like these ones:
;; Source and location in the load-history:
;; (find-efunctionlgrep 'find-2024gitvideo "2024git")
;; (find-eloadhistory-for 'find-2024gitvideo)
;; (find-eloadhistory-for 'find-2024gitvideo 2 " find-2024gitvideo)")
;; Source and location in the load-history:
;; (find-efunctionlgrep 'find-eevfile 'eev)
;; (find-eloadhistory-for 'find-eevfile)
;; (find-eloadhistory-for 'find-eevfile 2 " ee-eevfile)")
Each of the hyperlinks with `find-eloadhistory-for' displays one entry
in the load-history in a pretty-printed format, and a header with help
at the top. For example,
(find-eloadhistory-for 'find-eevfile 2 " ee-eevfile)")
shows the entry in the load-history for the file in which `find-eevfile'
was defined, and it jumps to the first defun in this block:
ee-eevfile
(defun . ee-eevfile)
(defun . find-eevfile)
(defun . find-eevsh)
(defun . find-eevsh0)
(defun . find-eevsh00)
(defun . find-eevgrep)
(defun . find-eev)
These are exactly the functions that were defined by this call to
`code-c-d',
;; (find-code-c-d "eev" ee-eev-source-directory :anchor)
(code-c-d "eev" ee-eev-source-directory :anchor)
that appears here:
(find-eev "eev-code.el" "code-c-d-s" "\"eev\"")
We can use that to see which functions and variables were defined just
before and just after the function that we are looking for.
[TODO: explain this:]
(find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p)
4.1. `find-efunctionlgrep'
This hyperlink
(find-efunctionlgrep 'find-eevfile 'eev)
tries to find the exact point in which the function `find-eevfile' was
defined using another method: it first converts the stem in the second
argument, "eev", into a regexp that means:
search for either "eev" between double quotes, or for "eev"
surrounded by certain other characters, like whitespace, a single
quote, or parentheses
and then it searches for that regexp, using lgrep, in the file in which
the function `find-eevfile' was defined. Try it again:
(find-efunctionlgrep 'find-eevfile 'eev)
it returns four lines that can be the place in which `find-eevfile' was
defined - and three of them are false positives.
The best way to understand the technical details of
`find-efunctionlgrep' is to try the tests in the source code:
(find-eev "eev-plinks.el" "find-efunctionlgrep")
5. Defuns, recreated
[Explain why we can't always trust the defuns block]
[An example with code-c-d:]
(find-eev "eev-code.el" "code-c-d-s")
(find-eev "eev-code.el" "code-c-d-s" "eli")
[compare:]
(find-2a
' (find-code-c-d "eli" ee-emacs-lisp-directory "eintr" :gz)
' (find-sf-links '(find-elinode "Top"))
)
6. Macros
[TODO: explain that in recent versions of Emacs `find-efunction' can
find most functions defined by cl macros, but not all...]
Emacs comes with lots of strange functions; most (or all?) of them are
created by macros. Here is an example: `cl-struct-p' is a function
defined in a normal way, but `cl-struct-p--cmacro' is a function defined
in a strange way:
ok: (find-efunction 'cl-struct-p)
fails: (find-efunction 'cl-struct-p--cmacro)
bytecomp: (find-efunctionpp 'cl-struct-p)
bytecomp: (find-efunctionpp 'cl-struct-p--cmacro)
The `find-efunctionpp's are not very helpful because these two functions
are byte-compiled, and the
(find-efunction 'cl-struct-p--cmacro)
fails. But if we try this,
(find-efunction-links 'cl-struct-p--cmacro)
(eek "M-h M-f cl-struct-p--cmacro")
one of the blocks that appears in the temporary buffer is this one:
# (find-efunctionlgrep 'cl-struct-p--cmacro)
# (find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p--cmacro)
# (find-eloadhistory-for 'cl-struct-p--cmacro)
# (find-eloadhistory-for 'cl-struct-p--cmacro 2 " cl-struct-p--cmacro)")
The second argument to `find-efunctionlgrep' is a stem. In this case the
`find-efunction-links' has guessed the stem incorrectly - it guessed the
stem as being the name of the original function, for simplicity -, but
if we duplicate that line and experiment a bit we can find a stem that
works:
# (find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p--cmacro)
# (find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p)
The second `find-efunctionlgrep' above shows three good guesses for
places that can be the sexps that define `cl-struct-p--cmacro', but I
find the code very hard to understand... the first guess points to a
sexp that is several lines long, the second guess points to this sexp,
that is just one line:
(cl-assert (cl-struct-p (cl--find-class 'cl-structure-class)))
We can try to run macroexpand and macroexpand-all on that sexp and
pretty-print the results, with `find-eppm' and `find-eppma',
(find-eppm '
(cl-assert (cl-struct-p (cl--find-class 'cl-structure-class)))
)
(find-eppma '
(cl-assert (cl-struct-p (cl--find-class 'cl-structure-class)))
)
and do the same with the other guesses. I tried this with the three
sexps - the three "guesses" - and examined the two expansions for each
one, and I couldn't find any mentions to `cl-struct-p--cmacro'... so in
this case this `find-efunctionlgrep'
(find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p)
didn't help me to understand where, and how, `cl-struct-p--cmacro', was
defined... bleh =(.
[TODO: explain this:]
(find-egrep "grep --color=auto -nH --null -e --cmacro *.el */*.el")
(find-efunction 'cl-define-compiler-macro)
(find-efunction 'cl-define-compiler-macro "\"--cmacro\"")
[TODO: delete most of the old stuff below, reuse a few parts...]
The easiest, and most high-level, way to inspect strange
functions uses a kind of "here". If you type `M-h M-s' with the
point on the line below,
(find-2024githsubs "00:34")
then `M-x sf' will first move the point to the end of the line
and then it will do some interesting things with the "sexp
before point", just like `M-e'. The "here" in that case will
be that sexp, whose "head" is a strange function. So the `sf'
in `M-x sf' is an abbreviation for:
do things with the strange function here, or rather,
with the "sexp here", that starts with a strange function,
where the "sexp here" is the sexp before the end of line,
and a "strange function" is a "function defined in a
strange way".
And "do things" means: display in a temporary buffer variants
of that sexp and lots of other information.
4. The load-history
The function `find-eev2021video' is known - try:
(find-eev2021video "00:40")
(find-efunctionpp 'find-eev2021video)
but `find-efunction' can't find its definition... try:
(find-efunction 'find-eev2021video)
it finds the file in which that function was defined, but it
can't find its "defun". That's because `find-eev2021video' is a
"function defined in a strange way", or, to abbreviate, a
"strange function".
_Most_ strange functions defined by eev are defined by calls to
`code-c-d' and friends - see:
(find-eev-quick-intro "9.1. `code-c-d'")
(find-eev-quick-intro "9.1. `code-c-d'" "find-code-c-d")
`find-eev2021' was defined by a call to `code-1stclassvideo',
like this one,
;; (find-code-1stclassvideo "eev2021")
(code-1stclassvideo "eev2021")
that defines `find-eev2021video', that plays a video, and two
"variants" of `find-eev2021video', that access the subtitles of
that video. Try:
(find-eev2021video "00:40")
(find-eev2021hsubs "00:40")
(find-eev2021lsubs "00:40")
Sometimes we want to start with a sexp like the ones above, and
generate its variants; sometimes we want to start with one of the
sexps above and display a lot of information about the "strange
function" at the car of its sexp; and sometimes we want to start
with one of the sexps above and display a lot of information
about the video that it points to. We can do all that with `M-x
sf', that:
does lots of things with the <S>trange <F>unction in the
current line.
More precisely, it acts at the sexp at the end of the line, like
`M-e', but if that sexp starts with a strange function it does
lots of things with that sexp. Try it on the sexps above!
[Only a few families of strange functions are supported at this
moment]
[Describe M-x sf - how to load it, how to test it]
1. Introduction
Sometimes `find-efunction' can't find the place in which a
function was defined. Try the sexps below:
(find-efunction 'cl-struct-p)
(find-efunction 'cl-struct-p--cmacro)
(find-efunctionpp 'cl-struct-p)
(find-efunctionpp 'cl-struct-p--cmacro)
(find-function-noselect 'cl-struct-p)
;; --> (#<buffer cl-preloaded.el> . 14565)
(find-function-noselect 'cl-struct-p--cmacro)
;; --> (#<buffer cl-preloaded.el>)
In `cl-struct-p' everything works, but in `cl-struct-p--cmacro'
`find-efuction' only does half of its expected job: it finds the
file in which `cl-struct-p--cmacro' was defined, but not its
"point of definition" - and it fails with an obscure error.
Take a look at the docstrings and the source of `find-efunction'
and `find-function-noselect':
(find-efunctiondescr 'find-efunction)
(find-efunction 'find-efunction)
(find-efunctiondescr 'find-function-noselect)
(find-efunction 'find-function-noselect)
The docstring of `find-function-noselect' explains that it:
Return(s) a pair (BUFFER . POINT) pointing to the definition of FUNCTION.
Finds the source file containing the definition of FUNCTION
in a buffer and the point of the definition. The buffer is
not selected. If the function definition can't be found in
the buffer, returns (BUFFER).
The hard work is done by `find-function-search-for-symbol':
(find-efunction 'find-function-search-for-symbol)
One of the comments in its source is this one:
;; `regexp' matches definitions using known forms like
;; `defun', or `defvar'. But some functions/variables
;; are defined using special macros (or functions), so
;; if `regexp' can't find the definition, we look for
;; something of the form "(SOMETHING <symbol> ...)".
;; This fails to distinguish function definitions from
;; variable declarations (or even uses thereof), but is
;; a good pragmatic fallback.
Sometimes these fallbacks in `find-function-search-for-symbol'
are not enough - they don't work for some macros, like
`cl-struct-p--cmacro', and they don't work for functions defined
by `code-c-d' and its friends. Here's one example:
(find-efunction 'find-eetcfile)
(find-efunctionpp 'find-eetcfile)
But these sexp hyperlinks work,
(find-efunctionlgrep 'cl-struct-p--cmacro 'cl-struct-p)
(find-efunctionlgrep 'find-eetcfile 'eetc)
because they first go to the files in which `cl-struct-p--cmacro'
and `find-eetcfile', and then they search for their stems -
cl-struct-p--cmacro
\---------/\------/
stem suffix
find-eetcfile
\---/\--/\--/
prefix stem suffix
surrounded by certain delimiter characters.