(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")

