(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 meat2. 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") That file is not loaded by default. To make Emacs always load it, add these lines to your ~/.emacs: ;; From: (find-templates-intro "4. Adding meat") (require 'eev-qrl) ; (find-eev "eev-qrl.el")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")