(Re)generate: (find-here-links-intro)
Source code: (find-efunction 'find-here-links-intro)
More intros: (find-eev-quick-intro)
(find-eev-intro)
(find-refining-intro)
This buffer is _temporary_ and _editable_.
It is meant as both a tutorial and a sandbox.
This intro will be merged with
(find-refining-intro)
at some point...
Eev's central idea is that you can keep "executable logs" of
what you do, in a format that is reasonably readable and that is
easy to "play back" later, step by step and in any order. We
call these executable logs, or executable notes, "e-scripts".
[Video links:]
(find-eev2019hsubs "05:24" "e-scripts")
(find-eev2019video "05:24" "e-scripts")
(find-eev2019hsubs "12:54" "A demo")
(find-eev2019video "12:54" "A demo")
1. Alternating between "task" and "notes"
In the old days log books were always made of paper, and there
was nothing automatic in taking notes with them. We would have to
decide what to write and how to write it, and we would have to
alternate between the "task" and "taking notes". After many
years of practice _some_ people would learn how to take notes
without distracting themselves much from the task at hand, and
they would learn how to make their notes at the same time concise
and readable enough.
Nowadays, with computers, there are _some_ ways to write logs
automatically - for example, most shells record the commands
given to them - but the output is of low quality.
Eev takes an intermediate stance between "notes by hand"
and "automatic notes". It is possible to do
"task"+"notes" with just a few more keystrokes than for
doing just "task", but that requires learning some tricks,
and having some practice.
1.1. Reading and writing
Learning eev is like learning to read and write. We first learn
_how to read_, and we learn _how to write_ in a second stage,
when we have read a lot and we are able to read what we write.
Learning eev is also like learning to use paper notebooks. It is
much easier to understand the notes and ideas what we wrote
ourselves in our notebooks than to understand what other people
wrote in their notebooks... when we go back to what _we_ wrote we
are able to reconnect with what we were thinking, even when our
notes are quite terse because we did not write down all details -
and we can't do that with other people's notes.
So: we have to first learn how to _read_ executable notes in
order to be able to _write_ our own executable notes, but after
learning the basics we will usually find it much easier to read
our own executable notes than to read other people's notes...
This is very similar to what happens in programming. Programmers
usually takes years, and a lot of effort, to learn to write code
and comments that other people find readable, and what they do
after that is not that they write super-readable code all the
time - instead they _adjust_ the level of readability of their
code depending on the situation: they write the code that will be
used and read by other people in a cleaner, more readable style
with lots of comments, and they write "throwaway code" that
they will only run once in a very terse, and often messy, style.
Most of my executable notes are written - or rather, "appear" -
when I am learning something and I am switching rather mindlessly
between "task" and "notes" as I explained in the previous
section. What I write looks like throwaway code, only worse -
because I usually leave lots of unfinished parts intermixed with
the parts that run, without marking clearly which are which...
but when I go through my notes about a task again I usually clean
my notes a bit. For me going through my executable notes about a
task again always involves a bit of _rewriting_ - which is
something that programmers often do with their own code, but that
we don't do much with paper notebooks.
Anyway: be prepared to create executable notes that are almost
unreadable, even by you when you go back to them a few hours
later - that's normal, that's how things are, and you can, and
WILL, rewrite the most useful parts...
Note that this "intro" is about writing elisp hyperlinks. The
tricks for writing eepitch blocks and index-anchor pairs are
discussed elsewhere.
2. "Here"
In this tutorial we will learn the basic technique for creating
an elisp hyperlink to "here" and copying it to our notes.
"Here" means the current Emacs buffer; we saw in the main
tutorial that elisp hyperlinks like
(find-eev-quick-intro "4. Creating Elisp Hyperlinks")
(find-emacs-keys-intro "3. Cutting & pasting")
(find-fline "~/")
(find-eevfile "")
(find-eevfile "eev-blinks.el")
(find-efunctiondescr 'find-file)
(find-enode "Modes")
(find-elnode "Defining Functions" "(defun foo () 5)")
(find-man "date")
open eev tutorials (`find-xxx-intro's), directories, files,
descriptions of emacs functions, sections of manuals in "info"
format, and manpages. All the elisp hyperlinks above are of the
kind described in the first paragraphs of this section of the
main tutorial:
(find-eev-quick-intro "3. Elisp hyperlinks")
They (usually) create a new buffer, and it is possible to "go
back" from that buffer with `M-k' of `M-K':
(find-eval-intro "5. Going back")
[Video links:]
(find-eev2020video "2:25" "variants that were better behaved")
(find-eev2020video "2:36" "in the same window as before")
3. `find-here-links'
Eev has a function, called `find-here-links' and bound to `M-h
M-h', that is able to distinguish several kinds of "here"s.
When we run it it creates a temporary buffer with lots of elisp
hyperlinks, and when we have enough practice we can spot in a
second which of its hyperlinks is the "hyperlink to here" that
we want to copy to our notes.
This tutorial is about a _variant_ of `find-here-links' that is
more suitable for beginners.
4. `find-here-links-3'
Suppose that you are in a buffer with something interesting -
"here" -, and you want to generate a hyperlink to it and copy
that hyperlink to your notes. Some terminology:
1. The target of that hyperlink will be the "here" buffer, so
let's call the "here" buffer the "target buffer" from
now on.
2. `find-here-links' creates a temporary buffer with several
elisp hyperlinks - let's call that buffer the "elinks
buffer".
3. Beginners start by putting all their (executable) notes in a
single file, "~/TODO"; remember that `M-1 M-j' jumps to
that file. The "notes buffer" is a buffer visiting the
file "~/TODO".
The key sequence `M-h M-3' saves the current window configuration
in a variable called `ee-window-configuration-before-M-h-M-3',
creates a 3-window setting like this,
_____________________
| | |
| | elinks |
| | buffer |
| target |__________|
| buffer | |
| | notes |
| | buffer |
|__________|__________|
and puts the cursor at the elinks buffer.
5. `find-here-links-1'
After creating the three windows described above we will usually
want to select a line from the elinks buffer - the right one,
i.e., the one with a hyperlink to the target buffer - and copy it
to the notes buffer; the next section explains how to do this.
After copying the hyperlink - or after deciding that we don't
want to copy it - we want to restore the original window
configuration that we had before typing `M-h M-3'. We can do that
by typing `M-h M-1' (`find-here-links-1'); I chose to use the
suffix "1" because in most cases the original window
configuration has a single window with the target buffer in it,
and the "1" is a reference to this:
(find-emacs-keys-intro "6. Windows" "C-x 1")
Note that `M-h M-1' undoes what `M-h M-3' did. In a figure:
_______________ _____________________ ________________
| | | | | | |
| | | | elinks | | |
| | | | buffer | | |
| target | M-h M-3 | target |__________| M-h M-1 | target |
| buffer | ------> | buffer | | ------> | buffer |
| | | | notes | | |
| | | | buffer | | |
|_______________| |__________|__________| |________________|
[Video links:]
(find-eevfherelhsubs "04:40" "2. The beginner's way")
(find-eevfherelvideo "04:40" "2. The beginner's way")
(find-eevfherelhsubs "04:40" "2.1. The 3-window setting - and going back from it")
(find-eevfherelvideo "04:40" "2.1. The 3-window setting - and going back from it")
(find-eevfherelhsubs "06:47" "`M-h M-3': three windows; `M-h M-1' goes back")
(find-eevfherelvideo "06:47" "`M-h M-3': three windows; `M-h M-1' goes back")
(find-eevfherelhsubs "07:26" "the original configuration can be anything")
(find-eevfherelvideo "07:26" "the original configuration can be anything")
6. Copying the hyperlink
When you are a beginner, the easiest way to copy an elisp
hyperlink from the elinks buffer to the notes buffer is to put
the cursor on the line with the hyperlink, then type `M-h M-w'
(`ee-copy-this-line-to-kill-ring'), and then go to the notes
buffer and copy it to there with `C-y' or with the entry "Edit
-> Paste" in the menu bar. Note that in the three-window setting
copying a hyperlink from the elinks buffer to the notes buffer
means copying it from the upper right window to the lower right
window:
_____________________
| | |
| | elinks |
| | buffer |
| target |____||____|
| buffer | \/ |
| | notes |
| | buffer |
|__________|__________|
When you become a slightly more advanced user the easiest way is
the one with the key sequences described here:
(find-eev-quick-intro "5.2. Cutting and pasting")
[Video links:]
(find-eevfherelvideo "08:56" "2.2. Copying one link")
(find-eevfherelvideo "08:56" "2.2. Copying one link")
7. Refining your hyperlinks
After learning the technique above, that was based on the keys:
M-h M-3 -- find-here-links-3
M-h M-w -- ee-copy-this-line-to-kill-ring
C-y -- yank, i.e., paste; see: (find-enode "Kill Ring")
M-h M-1 -- find-here-links-1
The next steps are to learn how:
a) Refine hyperlinks. See:
(find-refining-intro "1. Pos-spec-lists")
(find-refining-intro "2. Refining hyperlinks")
b) Work with a single window. See:
(find-refining-intro "3. Three buffers")
c) Use other keys that create buffers with hyperlinks. See:
(find-emacs-keys-intro "Some other keys that create")
8. Debugging
The best way to understand the innards of `find-here-links' is to
call it in "debug mode" - or, more precisely, to call it with a
prefix argument. When we run `find-here-links' _without_ a prefix
argument it displays a header and then the links to "here";
_with_ a prefix argument it displays the header and then a lot of
internal information about "here". For example, if you run
(eek "M-h M-h ;; find-here-links")
then "here" is this intro, and `find-here-links' displays a
header and then a "body" that is the result of running
(ee-find-intro-links). You can inspect the header and the body
separately with:
(find-elinks (ee-find-here-links-header))
(find-elinks (ee-find-here-links))
(find-elinks (ee-find-intro-links))
If you run `find-here-links' with a prefix argument, as in this
`eek' sexp,
(eek "M-0 M-h M-h ;; M-0 find-here-links")
then `find-here-links' will display the same header as before and
then a lot of information on how `(ee-find-here-links)' decided
that "here" was an intro - this one - and then
selected `(ee-find-intro-links)' as the right lower-level
function to use to generate the body. You can inspect the header
and the body of that buffer separately with:
(find-elinks (ee-find-here-links-header))
(ee-detect-here)
(find-elinks (ee-find-here-debug-links))
You can also try these tests:
(find-eev "eev-htests.el" "tests")
Each test tests a kind of "here", and generates a 3-window
setting of one of these two forms:
______________________ ______________________
| | | | | |
| | One kind | | | One kind |
| | of here | | | of here |
| Tests |____________| | Tests |____________|
| | | | | |
| | links to | | | one link |
| | here | | | to here |
|_________|____________| |_________|____________|
They are explained in:
(find-eev "eev-htests.el" "find-tlh")
9. The hlang
The original implementation of `find-here-links' was simple but
was hard to debug - its core used a big `cond' and it didn't keep
a lot of information on how it detected the kind of "here".
See:
(find-eev "eev-hlinks.el" "ee-find-here-links-old")
Then in 2021-2023 I rewrote it several times, and now
`ee-detect-here' runs a program that looks like this:
(find-eev "eev-hlinks.el" "hprog")
(find-eev "eev-hlinks.el" "ee-hprog-find-here-links")
The variable `ee-hprog-find-here-links' contains an "hprogram",
that is a program written in the "hlang", that is a little
language that is used mostly for deciding what is "here" and
keeping track of some information on how that decision was made.
The hlang is defined in the link below. To understand how it
works read its docstrings,
(find-eev "eev-hlinks.el" "hlang")
and try these examples:
(ee-hlang-:lisp '(+ 20 3) '(+ 40 5))
(ee-hlang-:or '(:lisp nil) '(:lisp nil) '(:lisp 42) '(:lisp 99))
(ee-hlang-:if '(< 1 2) '(list 'lt))
(ee-hlang-:if '(> 1 2) '(list 'gt))
(ee-hlang-eval '(:lisp (+ 20 3) (+ 40 5)))
(ee-hlang-eval '(:or (:lisp nil) (:lisp nil) (:lisp 42) (:lisp 99)))
(ee-hlang-eval '(:if (< 1 2) (list 'lt)))
(ee-hlang-eval '(:if (> 1 2) (list 'gt)))
(ee-hlang-eval '(:or (:if (< 1 2) (list 'lt)) (:if (> 1 2) (list 'gt))))
(ee-hlang-eval '(:or (:if (> 1 2) (list 'gt)) (:if (< 1 2) (list 'lt))))
Note this:
(find-efunction 'ee-hlang-:if)
(find-efunction 'ee-hlang-:if "d) we DO NOT evaluate SEXP2")
(find-efunction 'ee-find-here-links)
(find-efunction 'ee-find-here-links "(eval ee-hlang-sexp2)")
When we are in debug mode we don't eval the "then" part of
an (:if ...)! Check this low-level example to understand the
details:
(ee-hlang-eval '(:or (:if nil (error 1))
(:if t (error 2))
(:if t (error 3))
(:if nil (error 4))))
(eval ee-hlang-sexp2)
9.1. A historical note
My main motivation for the hlang was my frustration with Org and
Hyperbole. They are infinitely more popular then eev, probably
because they look very user-friendly, but when I tried to learn
them I stumbled on their hacker-unfriendliness...
Both Org and Hyperbole have cases in which they have to inspect
what we have "here", "around point", or "in a link", and
then they have to classify what they found into several different
cases, and act in a different way for each different case. Let me
call the function that classifies and acts accordingly a
"dispatcher".
I tried to add new hyperlink types to Org ages ago, when the way
to do that was not as well-documented as it is now. The current
way is explained here:
(find-orgnode "External Links")
(find-orgnode "Adding Hyperlink Types")
and when I tried to understand how Org's code blocks "really
work" my experience was so painful that I made a video about it:
Page: http://anggtwu.net/2021-org-for-non-users.html
Info: (find-1stclassvideo-links "2021orgfornonusers")
Play: (find-2021orgfornonusersvideo "00:00")
Subs: (find-2021orgfornonuserslsubs "00:00")
...but that was nothing in comparison with Hyperbole! I spent a
lot of time trying to build a bridge between eev and Hyperbole
that would make them easy to use together, but each one of my
questions about the innards of Hyperbole - for example, my
questions about the dispatchers for button types - was treated as
The Wrong Question... and in the end Hyperbole got my Eternal
Hate. See:
(find-es "hyperbole")
Then I decided that ok, I will never be able to make eev look as
user-friendly as Org or as Hyperbole, but at least I can make eev
more hacker-friendly than them... and if tinkering with the
innards of Org and Hyperbole is so unfun then I can make the
innards of eev more fun to play with - and then I rewrote
`find-here-links', that at that point was the part of eev whose
code was worst.