(Re)generate: (find-templates-intro)
Source code: (find-eev "eev-intro.el" "find-templates-intro")
More intros: (find-eev-quick-intro)
(find-escripts-intro)
(find-links-conv-intro)
(find-eev-intro)
This buffer is _temporary_ and _editable_.
It is meant as both a tutorial and a sandbox.
This intro is being rewritten!
You will need a lot of knowledge of elisp to understand this.
See:
(find-elisp-intro)
1. Introduction
The functions of eev that are meant to be used as hyperlinks can
be classified in this way - look at the class (c):
(find-links-conv-intro "3. Classification")
(find-links-conv-intro "3. Classification" "c)")
I will refer to these functions are "`find-*-links' functions".
You can see a list of `find-*-links' functions by running:
(find-eaproposf "^find-.*-links$")
(find-eev "eev-tlinks.el" ".find-debpkg-links")
Most of them were written as "5-minute hacks". I explained the
main ideas in this part of my presentation at the EmacsConf2020:
(find-eev2020video "43:49" "(find-emacs-tangents-links)")
(find-eev2020lsubs "43:49" "(find-emacs-tangents-links)")
The final definition of `find-emacs-tangents-links' is here:
(find-eev "eev-tlinks.el" "find-emacs-tangents-links")
Creating a new `find-*-links' function involves four steps:
1. generate a bare skeleton
2. generate an adjusted skeleton
3. add meat
4. debug the meat
2. Skeletons
To generate a bare skeleton we run `find-find-links-links-new'.
Try:
(eek "M-x find-find-links-links-new")
To adjust the skeleton we edit the first line in that buffer and
run it again "to regenerate the buffer". Remember this slogan:
(find-links-intro "5. The first line regenerates the buffer")
This sexp generates (an example of) an "adjusted skeleton",
(find-find-links-links-new "yttranscript" "c hash" "")
in which we have adjusted the name of the function -
`find-yttranscript-links' - and its arguments, "c" and
"hash".
Look at the comments before the definition of
`find-yttranscript-links':
(find-eev "eev-tlinks.el" "find-yttranscript-links")
it has this line,
;; Skel: (find-find-links-links-new "yttranscript" "c hash" "")
that recreates the adjusted skeleton in a temporary buffer. Check
these other "Skel:" lines:
(find-eevgrep "grep --color=auto -nH --null -e Skel: eev-tlinks.el")
3. Meat
Most of the "meat" in a `find-*-links' function is in the
argument to `ee-template0'. See:
(find-eev "eev-tlinks.el" "find-yttranscript-links")
(find-eev "eev-tlinks.el" "find-yttranscript-links" "ee-template0")
The docstrings of `ee-template0' and `ee-template00' have some
examples of how they expand substrings of the form "{expr}":
(find-efunctiondescr 'ee-template0)
(find-efunctiondescr 'ee-template00)
Here are some other examples with comments indicating how each
substitution works:
(ee-template0 "_{(+ 2 3)}_")
\-------/
5
(let ((hello "Hi! ")
(a 2)
(b 3))
(ee-template0 "{hello}{a}+{b}={(+ a b)}"))
\-----/\-/ \-/ \-------/
"Hi! " 2 3 5
The first example returns "_5_" - note that the (+ 2 3) returns
a number, but its result gets converted to a string - and the
second example returns "Hi! 2+3=5".
In the second example the `ee-template0' only has access to the
values variables `hello', `a' and `b' if it is run in dynamic
binding. Try to execute it again, now both with `M-e', that uses
dynamic binding, and with `M-1 M-1 M-e', that uses lexical
biding; with `M-11e' you will get an error. The gory details are
explained here,
(find-lexical-intro)
...and this one of the reasons why I use dynamic binding in all
the files of eev.
Note that `ee-template0' expands "{<}"s to "{"s and "{>}"s
to "}"s. Try:
(ee-template0 "{<}bla{>}")
\-/ \-/
"{" "}"
This trick is explained here:
(find-efunctiondescr 'ee-template0)
(find-efunction 'ee-template0)
Some functions in eev need use `{(ee-S expr)}' instead of
`{expr}'. Here are some links to examples and to the explanation
of what `ee-S' does:
(find-eev "eepitch.el" "find-eepitch-debug-links")
(find-eev "eepitch.el" "find-eepitch-debug-links" "ee-S")
(find-eev "eev-tlinks.el" "find-extra-file-links")
(find-eev "eev-tlinks.el" "find-extra-file-links" "ee-S")
(find-eev "eev-wrap.el" "ee-S")
4. Adding meat
The tricky part of adding stuff to the string in the
`(ee-template0 "...")' is that some characters need to quoted.
See this file for an interactive function - `M-x qrl', where the
"qrl" means "query-replace-list" - that will quote them in
the right way:
(find-eev "eev-qrl.el")
Note that `qrl' is an alias:
(find-eev "eev-aliases.el" "query-replace-list")
5. Debugging the meat
This video, from 2021,
(find-1stclassvideo-links "2021ffll")
explains how to use `find-find-links-links-new', but from about
36:00 onwards it explains how to debug `find-*-links' functions
using a method that was horribly complex. In 2021 I didn't know
how to use `C-M-x' (`eval-defun') -
(find-enode "Lisp Eval" "C-M-x" "containing or following point")
to evaluate the defun around point; `C-M-x' makes everything much
simpler.
Let's see an example. Run this
(find-find-links-links-new "mytaskC" "foo bar" "")
to create an adjusted skeleton in a temporary buffer; change its
meat to:
_{foo}_{bar}_
and type `C-M-x'. The meat is inside the defun for
`find-mytaskC-links', so running `C-M-x' will redefine
`find-mytaskC-links'.
Suppose that we want to edit its meat and check the result of two
tests after every few keystrokes. Suppose that our two tests are
these ones,
(find-mytaskC-links)
(find-mytaskC-links "FOO" "BAR")
or rather these ones - try them:
(find-2a nil '(find-mytaskC-links))
(find-2a nil '(find-mytaskC-links "FOO" "BAR"))
the `find-2a's will make their temporary buffers be shown at the
window at the right, and in the first test the "{foo}" and the
"{bar}" will be kept unchanged, and in the second test they
will become "FOO" and "BAR".
The temporary buffer generated by
(find-find-links-links-new "mytaskC" "foo bar" "")
has three defuns like these ones at its bottom:
(defun ee-template-test (&rest args)
(let ((ee-buffer-name "*ee-template-test*"))
(find-2a nil `(find-mytaskC-links ,@args))))
(defun tt0 () (interactive) (eek "C-M-x") (ee-template-test))
(defun tt () (interactive) (eek "C-M-x") (ee-template-test "A" "B"))
Eval each of these three defuns with `M-e'. The last two are
meant to be run from inside the
(defun find-mytaskC-links ...)
with `M-x tt0' and `M-x tt'; let's see how. Generate this
temporary buffer again,
(find-find-links-links-new "mytaskC" "foo bar" "")
and edit the meat of `find-mytaskC-links' to make it this again:
_{foo}_{bar}_
Then type `M-x tt0' and `M-x tt' while still inside the defun -
`M-x tt0' will reevalute `find-mytaskC-links' and show the result
of the first test in the window at the right, and `M-x tt0' will
reevalute `find-mytaskC-links' and show the result of the second
test in the window at the right. Then edit the meat a bit, and
try `M-x tt0' and `M-x tt' again. TA-DAA! =)
Note that we did everything in this example using only temporary
buffers. Now try to do something similar copying your working
version of `find-mytaskC-links' to a file in emacs-lisp-mode - I
usually use the file "~/elisp/test.el" for this kind of draft
code, and I only move the defuns of my new `find-*-links'
functions to other files after they become minimally useful.
6. The `let*' block
The third argument to `find-find-links-links-new' is a list of
local variables in a `let*'. Compare the temporary buffers
generated by the three sexps below (hint: use `M-2 M-e'):
(find-find-links-links-new "mytaskC" "foo bar" "")
(find-find-links-links-new "mytaskC" "foo bar" "plic")
(find-find-links-links-new "mytaskC" "foo bar" "plic bletch")
In the second and the third cases the `(apply ...)' of the first
case get wrapped in `(let* ...)'s, like this:
(apply ...
pos-spec-list)
(let* ((plic "{plic}"))
(apply ...
pos-spec-list))
(let* ((plic "{plic}")
(bletch "{bletch}"))
(apply ...
pos-spec-list))
The "{plic}" and the "{bletch}" are placeholders, and in real
`find-*-links' functions they are replaced by non-trivial
expressions - that are a second kind of meat that needs to be
added to the `defun's by hand. For examples, see:
(find-eevgrep "grep --color=auto -nH --null -e 'let\\*' eev-tlinks.el")
7. let* macros
Let's discuss a concrete example.
Consider the skeleton generated by:
(find-find-links-links-new "mytaskC" "a b" "c d")
Its `(let* ...)' block looks like this:
(let* ((c "{<}c{>}")
(d "{<}d{>}"))
...)
Let's replace that `(let* ...)' block by this other block
(let* ((c (format "<%s,%s>" a b)
(d (format "[%s,%s]" a b)))
...)
to make `c' and `d' depend on `a' and `b', and let's replace the
`...' by something much shorter. The result - an adjusted
skeleton, with the two kinds of meat - will be:
;; Skel: (find-find-links-links-new "mytaskC" "a b" "c d")
;;
(defun find-mytaskC-links (&optional a b &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for mytaskC."
(interactive)
(setq a (or a "{a}"))
(setq b (or b "{b}"))
(let* ((c (format "<%s,%s>" a b))
(d (format "[%s,%s]" a b)))
(apply
'find-elinks
`((find-mytaskC-links ,a ,b ,@pos-spec-list)
;; Convention: the first sexp always regenerates the buffer.
(find-efunction 'find-mytaskC-links)
,(ee-template0 "\na: {a}\nb: {b}\nc: {c}\nd: {d}"))
pos-spec-list)))
Now run the defun above, and type <f8>s on the three lines below
to understand how it works:
* (find-2a nil '(find-mytaskC-links))
* (find-2a nil '(find-mytaskC-links "A" "B"))
* (find-2a nil '(find-mytaskC-links "AA" "BB"))
When the `(let* ...)' block has many lines sometimes it's
convenient to create a macro to substitute the `(let* ...)'. If
we do that in the example above it becomes a `defun' and a
`defmacro':
;; Skel: (find-find-links-links-new "mytaskC" "a b" "c d")
;;
(defun find-mytaskC-links (&optional a b &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for mytaskC."
(interactive)
(setq a (or a "{a}"))
(setq b (or b "{b}"))
(ee-let*-macro-mytaskC
a b
(apply
'find-elinks
`((find-mytaskC-links ,a ,b ,@pos-spec-list)
;; Convention: the first sexp always regenerates the buffer.
(find-efunction 'find-mytaskC-links)
,(ee-template0 "\na: {a}\nb: {b}\nc: {c}\nd: {d}"))
pos-spec-list)))
;; Skel: (find-let*-macro-links "mytaskC" "a b" "c d")
(defmacro ee-let*-macro-mytaskC (a b &rest code)
"An internal function used by `find-mytaskC-links'."
`(let* ((a ,a)
(b ,b)
(c (format "<%s,%s>" a b))
(d (format "[%s,%s]" a b)))
,@code))
Try it - eval the `defun' and the `defmacro' above and then run
<f8>s on these three lines:
* (find-2a nil '(find-mytaskC-links))
* (find-2a nil '(find-mytaskC-links "A" "B"))
* (find-2a nil '(find-mytaskC-links "AA" "BB"))
The details of how the macro `ee-let*-macro-mytaskC' works are
quite tricky, but what matters here is that we can generate
macros like that by starting with skeletons. Try:
;; Skel: (find-let*-macro-links "mytaskC" "a b" "c d")
After generating the skeleton all the other adjustments need to
be made by hand.
G. Garbage (to be recycled)
(find-eev "eev-tlinks.el" ".find-debpkg-links")
(find-eev-quick-intro "8.3. Creating index/section anchor pairs")
In the beginning I wrote the code of all those functions by hand.
Then some patterns start to emerge, and I wrote some functions to
help me to write those functions. The basic idea is explained in
this part of my talk at the EmacsConf2020, in which I presented
an example of a "5-minute hack":
Its comments have these two lines:
;; Skel: (find-find-links-links-new "emacs-tangents" "yyyy mm dd msg txtstem" "")
;; Test: (find-emacs-tangents-links "2022" "06" "06")
The "Skel:" line indicates that the code of
`find-emacs-tangents-links' was written using
`find-find-links-links-new', in several steps:
In 2021 I recorded a video, called
How I write 5-minute hacks in eev using `M-x find-find-links-links-new'
(find-1stclassvideo-links "2021ffll")
G.1. Introduction
.................
In dec/2019 I sent this e-mail to the eev mailing list:
https://lists.gnu.org/archive/html/eev/2019-12/msg00001.html
It was a kind of a call for help. It contained a very brief
explanation of how the "templated" functions of eev, like
`find-ekey-links' and `find-latex-links', are implemented, and
showed how people can write their own templated functions as
quick hacks.
If you want to learn how to _use_ templated functions, start by:
(find-eev-quick-intro "4.2. `find-ekey-links' and friends")
(find-eev-quick-intro "7.5. `find-latex-links'")
If you want to look at the source code of the existing templated
functions, take a look at:
(find-eev "eev-elinks.el")
(find-eev "eev-tlinks.el")
(find-links-intro "3. Elisp hyperlinks buffers conventions")
This tutorial is for people who want to learn how to _write_
their own templated functions.
To learn how to write your own templated functions you need to:
1) learn how to use `ee-template0' by reading its source code
and playing with examples in the source and here,
2) learn how to use `find-elinks' - same thing,
3) learn how to use `find-find-links-links-new'.
G.3. `find-elinks'
..................
See:
(find-efunction 'find-elinks)
(find-eev "eev-elinks.el" "find-elinks")
Now try these examples. They are multi-line versions with
comments of the examples in the source file.
(find-elinks
'((a sexp)
"a string")
)
Now try these examples. They are longer, multi-line versions of
the examples in the source file.
(find-elinks
'((a sexp)
"a string")
)
(find-elinks
'((a sexp)
"a string")
"st")
(find-elinks
'((a sexp)
"a string")
"st" "i")
(find-elinks
'((a sexp)
(another sexp)
(sexps get comment signs)
(strings in sexps: "foo bar")
(newlines in strings in sexps get backslashed: "\n")
(ticks in sexps: 'a '(b c))
(nils in sexps: nil () (nil nil))
"a string"
"another string"
"strings don't get comment signs"
"empty strings become empty lines"
""
"newlines in strings\nbecome real newlines"
"nils are dropped:"
nil
"see?"
""
(another sexp)
)
)
Normally the first argument to `find-elinks' is backquoted. See:
(find-elnode "Backquote")
Try:
`(foo ,(+ 2 3) bar)
`(foo ,'(+ 2 3) bar)
`(foo ,(list 2 3) bar)
`(foo ,@(list 2 3) bar)
See:
(find-eev "eev-elinks.el" "find-efunction-links")
The first argument to `find-elinks' is called LIST. Elements of
LIST that are sexps are converted to strings using `ee-HS'. See:
(find-eev "eev-wrap.el" "ee-S")
G.4. Skels
..........
Many functions in eev have comments that start with ";; Skel:",
like this:
;; Skel: (find-find-links-links-new "fossil" "url subdir c" "")
A comment like that before a function means that I wrote that
function by first running that sexp and then modifying the code
that that sexp generated, that was a "skeleton".
Try:
(find-find-links-links-new "fossil" "url subdir c" "")
(find-eev "eev-tlinks.el" "find-fossil-links")
(find-eevgrep "grep --color -nH --null -e Skel: *.el")
G.5. `find-find-links-links'
............................
(Note: `find-find-links-links' is obsolete, and was superseded by
`find-find-links-links-new')
ALL my `find-*-links' started as quick hacks.
SOME of them were useful enough to deserve being cleaned up.
A FEW of them ended up in:
http://anggtwu.net/eev-current/eev-elinks.el.html
http://anggtwu.net/eev-current/eev-tlinks.el.html
(find-eev "eev-elinks.el")
(find-eev "eev-tlinks.el")
...but there are lots of other `find-*-links' functions in:
http://anggtwu.net/.emacs.templates.html
They are trivial to write. I start with a skeleton that I obtain by
running `M-x find-find-links-links', and then I modify the first line
in that buffer, regenerate, modify, regenerate, and so on until happy.
Run each of the sexps below with `M-2 M-e' to compare the buffers that
they generate:
(find-find-links-links "{k}" "{stem}" "{args}")
(find-find-links-links "\\M-u" "{stem}" "{args}")
(find-find-links-links "\\M-u" "macports" "{args}")
(find-find-links-links "\\M-u" "macports" "pkgname")
(find-find-links-links "\\M-u" "macports" "pkgname anotherarg")
So: start by running something like
(find-find-links-links "\\M-u" "macports" "pkgname")
(find-find-links-links "\\M-u" "homebrew" "pkgname")
then copy the
(define-key eev-mode-map "\M-h\M-u" 'find-macports-links)
(defun find-macports-links (&optional pkgname &rest pos-spec-list)
"Visit a temporary buffer containing hyperlinks for foo."
(interactive)
(setq pkgname (or pkgname "{pkgname}"))
(apply 'find-elinks
`((find-macports-links ,pkgname ,@pos-spec-list)
;; Convention: the first sexp always regenerates the buffer.
(find-efunction 'find-macports-links)
""
,(ee-template0 "\
")
)
pos-spec-list))
;; Test: (find-macports-links ___)
to your notes, replace the `(interactive)' by
(interactive (list (ee-debpkgname-ask)))
and start adding things to the string in (ee-template0 "...").
I will try to update this intro in the next days:
(find-templates-intro)
http://anggtwu.net/eev-intros/find-templates-intro.html
Etc:
(find-eev "eev-tlinks.el" "find-find-links-links")
(find-eev "eev-tlinks.el" "find-intro-links")
(find-eev "eev-wrap.el" "find-eewrap-links")