(Re)generate: (find-eepitch-intro)
Source code: (find-eev "eev-intro.el" "find-eepitch-intro")
More intros: (find-eev-quick-intro)
(find-eval-intro)
(find-wrap-intro)
This buffer is _temporary_ and _editable_.
It is meant as both a tutorial (for eepitch) and a sandbox.
This intro _complements_ the material in:
(find-eev-quick-intro "6. Controlling shell-like programs")
For a good visual introduction to eepitch, see this page:
http://anggtwu.net/eepitch.html
For some demos, see:
(find-eev-quick-intro "6. Controlling shell-like programs" "[Video links:]")
In this intro we suppose that the reader knows what is a terminal
and what is a shell. In Unix-like systems the terminal and the
shell are clearly different programs, and it's easy to understand
how a terminal can be used to run other programs that are not
shells (e.g., a Python interpreter; see "REPL" below); in
Windows most people don't know that the "DOS window" is in fact
a Windows console running cmd.exe. Some links:
https://en.wikipedia.org/wiki/Pipeline_(Unix)
https://en.wikipedia.org/wiki/Unix_philosophy
https://en.wikipedia.org/wiki/Unix-like
https://en.wikipedia.org/wiki/Shell_(computing)
https://en.wikipedia.org/wiki/Shell_(computing)#Text_(CLI)_shells
https://en.wikipedia.org/wiki/Shell_script
https://en.wikipedia.org/wiki/Command-line_interface
https://en.wikipedia.org/wiki/Command-line_interface#Command-line_interpreter
https://en.wikipedia.org/wiki/Read-eval-print_loop ("REPL")
https://en.wikipedia.org/wiki/Terminal_emulator
https://en.wikipedia.org/wiki/Text_terminal
https://en.wikipedia.org/wiki/MS-DOS#Windows_command-line_interface
https://en.wikipedia.org/wiki/Windows_Console
https://en.wikipedia.org/wiki/Cmd.exe
1. Some demos
Let's start with the simplest case. If you put the cursor on the
first line that starts with a red star below and type the key
<f8> six times,
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
echo "We are at: $PWD"
cd /tmp/
echo "We changed to: $(pwd)"
you will notice that each <f8> does something with the current
line and move the cursor to the next line; first three <f8>s - on
the lines that start with red stars - create a window setting
like this,
________________________________
| | |
| notes | target |
| buffer | buffer |
| (this intro) | ("*shell*") |
| | |
|________________|_______________|
and the last three <f8>s - on "non-red star lines" - send the
lines
echo "We are at: $PWD"
cd /tmp/
echo "We changed to: $(pwd)"
to the "target buffer", that in this case is the buffer with a
terminal running a shell; the shell behaves exactly is if the the
user had typed those three lines at its prompt.
1.1. Another target
If you put the cursor at the first red star line below and type
<f8> six times you will get something very similar to the example
above,
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
1 + 2
print("Hello " +
"world")
but now the window setting will be like this:
________________________________
| | |
| notes | target |
| buffer | buffer |
| (this intro) | ("*python*") |
| | |
|________________|_______________|
and the target buffer will be called "*python*", and it
contains a terminal running a Python interpreter.
1.2. Two targets
The demo below uses an advanced feature - the function
`find-3EE', explained at:
(find-multiwindow-intro "find-3EE")
to create a 3-window setup like this:
_______________________
| | |
| | *shell* |
| notes |____________|
| buffer | |
| | *python* |
|__________|____________|
Some non-red star lines in it send the current line to the
"*shell*" buffer, and some send the current line to the
"*python*" buffer. The red star lines with "(eepitch-shell)"
set the target to "*shell*", and the red star lines with with
"(eepitch-python)" set the target to "*python*". Try it! Put
the cursor on the first red star line below, then type <f8>
twelve times:
* (find-3EE '(eepitch-shell) '(eepitch-python))
* (eepitch-shell)
echo Hello... > /tmp/o
* (eepitch-python)
print(open("/tmp/o").read())
* (eepitch-shell)
echo ...and bye >> /tmp/o
* (eepitch-python)
print(open("/tmp/o").read())
1.3. Two targets, two windows
The demo below is similar to the one with three windows in the
previous section, but it uses just two windows - and the window
at the right alternates between "*shell*" and "*python*":
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
echo Hello... > /tmp/o
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
print(open("/tmp/o").read())
* (eepitch-shell)
echo ...and bye >> /tmp/o
* (eepitch-python)
print(open("/tmp/o").read())
2. How <f8> works
The key <f8> works in one way when the cursor is on a line that
starts with a red star - it executes everything at the right of
the "*" as Lisp code, and then moves down - and in a totally
different way on non-red star lines: on non-red star lines it
makes sure that the target buffer is being displayed, then sends
the current line to the target buffer "as if the user had typed
it", then moves down.
2.1. Eepitch blocks
A block of three red star lines like
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
or
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
is called an "eepitch block". The _final effect_ of typing <f8>
thrice on an eepitch block like this
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
is easy to describe: after the third <f8> we get a window setting
like this,
________________________
| | |
| notes | target |
| buffer | buffer |
| | ("*shell*") |
| | |
|__________|_____________|
where the target buffer is running a _new_ shell...
2.2. `(eepitch-kill)'
The effect of running <f8> on a line like
* (eepitch-kill)
is to kill the current target. More precisely, `(eepitch-kill)'
kills a buffer with the name stored in the variable
`eepitch-buffer-name', if a buffer with that name exists; in the
examples above the target buffer names are always either
"*shell*" or "*python*". If we are in a window setting like
this and the target is "*shell*"
________________________
| | |
| notes | target |
| buffer | buffer |
| | ("*shell*") |
| | |
|__________|_____________|
and we run `(eepitch-kill)' the window setting becomes this:
_____________________
| | |
| notes | some |
| buffer | other |
| | buffer |
| | |
|__________|__________|
which may be confusing...
2.2. `(eepitch-shell)'
The effect of running <f8> on a line like
* (eepitch-shell)
can be *roughly* described as:
a) Set the name of the target buffer to "*shell*".
b) If the target buffer does not exist, create it - by
running `(shell)'.
c) If the target buffer is not being displayed then display it
- by creating a two-window setting with the target buffer at
the right.
This is a simplification, though... the sexp
(eepitch-shell)
runs this,
(eepitch '(shell))
and the name of the target buffer is obtained from the
sexp `(shell)' by running it in a certain way.
2.3. `eepitch'
The documentation for `eepitch' says:
(eepitch CODE)
Set up a target for eepitch and make sure it is displayed in
another window.
The argument CODE must be a "shell-like sexp", i.e., one that
when evaluated always switches to a buffer with a fixed name,
and when that buffer does not exists it creates it.
For example, running `(shell)' switches to a buffer whose name is
"*shell*"; the name of the target buffer can obtained
from the sexp `(shell)' by running this:
(save-window-excursion
(shell)
(setq eepitch-buffer-name
(buffer-name (current-buffer))))
2.4. `(eepitch-python)'
The effect of running <f8> on a line like
* (eepitch-python)
is very similar to `(eepitch-shell)', but it uses "*python*" as
the name of the target buffer. `(eepitch-python)' is defined as:
(eepitch '(find-comintprocess "python" "python"))
2.5. `find-comintprocess'
The sexp
(find-comintprocess "buffer name" "program and args")
switches to a buffer called "*buffer name*" and if that buffer
does not have an associated process then it runs "program and
args" there in comint mode. Comint is explained here:
(find-enode "Shell Mode")
The sexp
(eepitch-comint "buffer name" "program and args")
works as an abbreviation for:
(eepitch '(find-comintprocess "buffer name" "program and args"))
Most `eepitch-<lang>' functions are defined using
`eepitch-comint'. See:
(find-eev "eepitch.el" "eepitch-langs")
(find-eev "eepitch.el" "find-comintprocess")
(find-eev "eepitch.el" "find-comintprocess" "defun eepitch-comint ")
2.6. `find-vtermprocess'
Some programs don't run well inside comint buffers, but run well
inside other terminal emulators that are harder to set up but
that handle more escape sequences, like vterm:
https://github.com/akermu/emacs-libvterm
Most `eepitch-<lang>' functions are defined using
`eepitch-comint' and `find-comintprocess'; the `eepitch-<lang>'
functions that need vterm are defined using `eepitch-vterm' and
`find-vtermprocess'. See:
(find-eev "eepitch.el" "other-terms")
(find-eev "eepitch.el" "eepitch-langs-vterm")
3. Test blocks
Suppose that we have a file "foo.py" containing this (without
the indentation):
def square (x):
return x*x
"""
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
execfile("foo.py", globals())
print(square(5))
"""
Python treats everything between the first and the second
`"""'s as a multiline comment, and ignores it - but for us
this multiline comment contains an eepitch block that starts a
Python interpreter, then a line that loads "foo.py" in it, then
a line that tests the function "square" defined in foo.py. We
call the block between the `"""'s a "test block".
A "test block" is a multiline comment in a Python script, a Lua
script, or in a script in one of the other supported languages -
we call them the "ambient script" and the "ambient language"
- that contains at least:
1) an eepitch block that runs an interpreter for the ambient
language,
2) a line that loads the ambient script in that interpreter,
3) code that tests functions defined in the ambient script.
We can insert a test block in the current buffer by running `M-x
ee-insert-test', or `M-x eeit'. The current implementation of M-x
ee-insert-test' uses the name of the major mode to decide which
other function to call. If you are in a buffer in which the value
of the variable
major-mode
is `FooBar-mode' then `M-x eeit' tries to run the function
`ee-insert-test-FooBar-mode', and yields an error if that
function does not exist. To add support for `FooBar-mode' to `M-x
eeit', just define a function with the right name. See the source
for examples:
(find-eev "eev-testblocks.el" "examples")
My presentation at the EmacsConf2021 was about test blocks.
Links:
Pages: http://anggtwu.net/eepitch.html
http://anggtwu.net/emacsconf2021.html
https://emacsconf.org/2021/talks/test/
Slides: http://anggtwu.net/LATEX/2021emacsconf.pdf
Video: (find-eev2021hsubs "00:14" "if we type f8 several times here")
(find-eev2021video "00:14" "if we type f8 several times here")
(find-eev2021hsubs "00:42" "as a multi-line comment")
(find-eev2021video "00:42" "as a multi-line comment")
3.1. `find-eeit-links'
If you run this,
(find-eeit-links 'lua-mode)
you will get a buffer with:
a) links to inspect the current definition of
`ee-insert-test-lua-mode',
b) links that let you compare that with the `ee-insert-test-'s
for other major modes,
c) a barebones `(defun ...)' that lets you redefine
`ee-insert-test-lua-mode'.
If you run `find-eeit-links' interactively with `M-x' then it
will run as:
(find-eeit-links <current-major-mode>)
and you can use that to inspect the `ee-insert-test-' support for
the current major mode, or to implement it yourself.
[Video links:]
(find-eev2021video "04:22" "find-eeit-links")
(find-eev2021hsubs "04:22" "find-eeit-links")
3.2. Test blocks as documentation
I found that test blocks are a really good way to document my
programs. Most people think that they look very alien at first,
but they understand them immediately when they see a demo - so
here are some demos. You need to have lua5.1 in your path to run
them; they use eepitch-lua51, that calls lua5.1. So try this
first:
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
print("Hello!")
for k,v in pairs(os) do print(k, v) end
os.exit()
If it works then try the demo below. Note that eepitch treats the
lines with two red stars as comments; the sexps in "**"-lines
are hyperlinks, and the ones in "*"-lines are not.
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
rm -Rv /tmp/dednat6/
mkdir /tmp/dednat6/
cd /tmp/dednat6/
wget http://anggtwu.net/dednat6-minimal.zip
unzip dednat6-minimal.zip
* (code-c-d "dn6lua" "/tmp/dednat6/dednat6/" :anchor)
* (setenv "LUA_INIT" "@/tmp/dednat6/dednat6/edrxlib.lua")
** (find-dn6lua "edrxlib.lua")
** (find-dn6lua "treetex.lua" "TreeNode-tests" 3)
** (find-dn6lua "rect.lua" "dedtorect-tests" 3)
3.3. `eepitch-preprocess-line'
The key <f8> is bound to `eepitch-this-line'. You can see the
source code of that function by following these hyperlinks:
(find-eev "eepitch.el" "eepitch-this-line")
(find-efunction 'eepitch-this-line)
The source of `eepitch-this-line' contains this mysterious setq:
(let ((line (buffer-substring (ee-bol) (ee-eol))))
(setq line (eepitch-preprocess-line line))
...
)
By default `eepitch-preprocess-line' is a no-op that simply
returns this argument unchanged. Its definition is just this:
;; See:
;; (find-eepitch-intro "3.3. `eepitch-preprocess-line'")
(defun eepitch-preprocess-line (line) line)
Remember that the behavior of <f8> is usually described in
human-friendly terms as:
"lines starting with two red stars are treated as comments,
lines starting with a red star are executed as lisp,
and other lines are sent to the target buffer."
The function `eepitch-preprocess-line' is a stub that lets us
change in arbitrary ways what is the "line" that is processed
in the sense above. Let's see a simple example. Try:
(replace-regexp-in-string "^abc" "" "foo")
(replace-regexp-in-string "^abc" "" "abcfoo")
(replace-regexp-in-string "^abc" "" "abcfooabc")
(replace-regexp-in-string "^abc" "" "fooabc")
A `(replace-regexp-in-string "^abc" "" ...)' deletes an
initial "abc" from a string if that string starts with "abc",
but returns other strings unchanged. So, if we redefine
`eepitch-preprocess-line' in this way,
(setq eepitch-preprocess-regexp "^#: ")
(defun eepitch-preprocess-line (line)
(replace-regexp-in-string eepitch-preprocess-regexp "" line))
then the
(let ((line (buffer-substring (ee-bol) (ee-eol))))
(setq line (eepitch-preprocess-line line))
...
)
in the source of `eepitch-this-line' will first set `line' to the
string in the current line between the beginning-of-line and the
end-of-line, and then if `line' starts with "#: " that prefix
is deleted from it; and it is this "line after removing the
prefix" that is processed according the the rules of two red
stars/one red star/no red stars.
Now let's see a practical example. Gnuplot does not support
multiline comments, and using exactly the hack above I can make
<f8> ignore the prefix "#: ". Then a block like this in a
Gnuplot file
#: * (eepitch-shell)
#: * (eepitch-kill)
#: * (eepitch-shell)
#: gnuplot
#: load "foo.plt"
#: plot sin(x)
works as a test block. Running
(setq eepitch-preprocess-regexp "^")
or
(defun eepitch-preprocess-line (line) line)
disables the hack. A similar technique for using test blocks in
makefiles is explained here:
http://anggtwu.net/eev-make.html
(find-2022eevmake0video)
Running `M-x eeit' in a makefile runs
`ee-insert-test-makefile-mode', that inserts a test block like
this:
# See: (find-eepitch-intro "3.3. `eepitch-preprocess-line'")
# (setq eepitch-preprocess-regexp "^")
# (setq eepitch-preprocess-regexp "^#T ")
#
#T * (eepitch-shell)
#T * (eepitch-kill)
#T * (eepitch-shell)
#T make -f nameofthismakefile TARGET
The lines that start with just "# " serve as a reminder that
you need a special setup to make that test block work.
Some languages have syntaxes for comments that are much more
eepitch-unfriendly and test-blocks-unfriendly than this. An
extreme example is SmallTalk, in which comments are delimited by
double quotes and can't contain double quotes. It should be
possible to use `eepitch-preprocess-line' to add support for test
blocks in SmallTalk source files - but I haven't tried that yet.
4. Badly-behaved targets
In an eepitch block like this one
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
the first two red star lines are typically only used when we want to kill
a current shell target - if it exists - and then create a new one.
For "badly-behaved targets" - I will explain the term precisely in the
next section - it is hard to define a function `eepitch-BBT' that would
work well enough in an eepitch block like this one,
* (eepitch-BBT)
* (eepitch-kill)
* (eepitch-BBT)
and it is more practical to have a eepitch block with functions specific
for the target BBT, like this one:
* (eepitch-BBT-kill)
* (eepitch-BBT-start)
* (eepitch-BBT-select)
but sometimes it is better to replace the middle red star line by
several lines, and make them remind us how to go back to our source
buffer. For example, here,
* (eepitch-gdb-kill)
** To restart gdb:
* (eepitch-set-source-and-M-x-b 2)
* (gdb "gdb -i=mi")
* (eepitch-gdb-select)
the sexp `(gdb "gdb -i=mi")' asks some questions, messes up our window
configuration, and only leaves us at the target buffer after too many
keystrokes.
Note that running the sexp `(eepitch-set-source-and-M-x-b 2)' prints
instructions in the echo area - it says:
"`M-x b' will set the eepitch target and return to `*(find-eepitch-intro)*'"
So after the `(gdb "gdb -i=mi")' finishes we need to run `M-x b'. The
sexp `(eepitch-set-source-and-M-x-b 2)' has saved the source buffer -
"*(find-eepitch-intro)*" - and the line that we need to return to,
that is 2 lines below the `(eepitch-set-source-and-M-x-b 2)' itself;
when we type `M-x b' Emacs interprets that as: this is the target
<b>uffer - go <b>ack to the source <b>uffer and use this window setup:
_____________________
| | |
| source | target |
| buffer | buffer |
| | |
|__________|__________|
4.1. What are badly-behaved targets?
(Examples: gdb, slime)
(Compare with Sly)
UNFINISHED!!!
See: (find-eev "eepitch.el" "badly-behaved")
-=-=-=-=-
Old stuff:
1. Motivation
Suppose that we have to do some reasonably complex task using a
shell, and that we want to take notes of what we do because we
might have to do something similar later.
The two usual ways to interact with a shell are:
1) through a _script_, that is, by preparing in advance all
commands to be executed, putting them in a script file, and
then running that file,
2) _interactively_, by typing the commands one by one on a
shell prompt.
Suppose that we have to discover which commands to run as we go;
that rules out preparing a script beforehand, so we need to use
the shell interactively. After issuing the right commands, the
two usual ways to retrieve what we did are:
a) through the _shell history_, which records the last commands
that the shell received,
b) by looking at the full _transcript_ of our interaction with
the shell.
The way (a) gets a list of commands, without comments, that can
be then saved into a text editor; the way (b) may require some
tricky editing to isolate the commands from their outputs.
Eepitch.el implements a simple alternative way of interacting
with shells (and other shell-like programs) while keeping notes.
It has only one essential key binding, <F8>, which is better
explained through the executable example in the next section, and
two unessential features, `M-T' and "*", which will be
explained later.
2. The main key: <F8>
Emacs can run a shell in a buffer, and it can split its frame
into windows, like this:
___________________
| | |
| our | a |
| notes | shell |
| | buffer |
|_________|_________|
The usual way to use a shell buffer is to move the cursor there
and type commands into its prompt; the eepitch-y way is to leave
the cursor at the "notes" buffer, write the commands for the
shell there, and send these commands to the shell with <F8>.
Here's what <F8> does:
When we type <F8> on a line that starts with a red
star ("*"), it executes the rest of the line as Lisp, and
moves down; when we type <F8> on a line that does not start
with a "*", it makes sure that the "target buffer" is being
displayed (the "target" is usually the buffer called
"*shell*"), it "send"s the current line to the target
buffer, and moves down.
"Sending the current line to the target buffer" means copying
the contents of the current line to the target - as if the user
had typed that line there by hand -, then "typing" a <RET> at
the target buffet.
Please try that in the example after this paragraph, by typing
<F8> six times starting at the first line that says
"* (eepitch-shell)". The three red star lines at the top will
create a target buffer, destroy it, and create it again; the
other three lines will send commands to the target shell.
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
echo "We are at: $PWD"
cd /tmp/
echo "We changed to: $(pwd)"
3. Other targets
Just like `(eepitch-shell)' creates a shell buffer and sets the
eepitch target to it, `(eepitch-python)' creates a buffer with a
Python interpreter and uses it as the eepitch target. Try:
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
def square (x):
return x*x
print(square(5))
We can use several targets at the time, alternating between them.
For example:
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
echo Hello... > /tmp/o
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
print(open("/tmp/o").read())
* (eepitch-shell)
echo ...and bye >> /tmp/o
* (eepitch-python)
print(open("/tmp/o").read())
There is a (much) more advanced example of working with several
targets here:
(find-prepared-intro "An `ee' for Python")
4. More on eepitch-kill
Note that `(eepitch-kill)' kills the _current_ target, that may
or may not be a shell buffer, a Python interaction buffer, etc...
That explains the first line in blocks like:
* (eepitch-python)
* (eepitch-kill)
* (eepitch-python)
and:
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
by running the first `(eepitch-python)' we can be sure that the
following `(eepitch-kill)' will kill the Python buffer, not the
shell buffer! And the last `(eepitch-python)' in the block of
three lines will then create a new Python interaction buffer,
erasing all definitions done in previous sessions.
5. Creating eepitch blocks: `M-T'
Write just "shell" or "python" in a line, then type
`M-T' (i.e., meta-shift-t) there. The line will be turned into
three - an "* (eepitch-xxx)", an "* (eepitch-kill)", and an
"* (eepitch-xxx)". We call these blocks of three lines
"eepitch blocks". Try this below, converting the "shell" into
an eepitch block for starting a shell.
shell
pwd
cd /tmp/
pwd
6. Red stars
Eepitch.el sets the glyph for the char 15 to a red star in the
standard display table. In layman's terms: eepitch.el tells Emacs
that the character 15 should be displayed as a red star. The
character 15 corresponds to control-O, whose default
representation on screen would be "^O". You can enter a
literal ^O in a buffer by typing `C-q C-o'.
7. For more information
On hyperlinks: (find-eval-intro)
On keys similar to `M-T': (find-wrap-intro)
An older text about eepitch:
(find-eev "eepitch.readme")
(find-eev "eepitch.readme" "the-trivial-case")
(find-eev "eepitch.readme" "red-stars")
(find-eev "eepitch.readme" "eepitch-blocks")
(find-eev "eepitch.readme" "eepitch-blocks")
Many functions like `eepitch-shell':
(find-efunction 'eepitch-bash)
What functions can generate target buffers:
(find-eevfile "eepitch.el" "shell-like sexp")
(find-efunction 'eepitch)