Learning Emacs and Elisp

Raw link: https://www.youtube.com/watch?v=46w9e4GAjsU

In this video I explain how to use Emacs’ Help system to get information about the environment. This is part of the basic skillset one needs to make good use of Emacs. I also talk about relevant information regarding functions and variables as well as how to get started with Emacs Lisp (Elisp).

Below is the text of the presentation. Consider it a continuation of what I have already been sharing in my dotemacs about the frequently asked question How do you learn Emacs?.

#+TITLE: Learning Emacs and Elisp
#+AUTHOR: Protesilaos Stavrou (https://protesilaos.com)
#+DATE: 2022-01-31

* Use a decent interface for completions

The best thing you can do to upgrade your Emacs setup is to use an
interface that visualises the available options whenever you use the
minibuffer, such as with the =M-x= key.

There are several options, though I recommend you get started with a
nimble tool called "Vertico" by Daniel Mendler: it is excellent for
newcomers as well as highly configurable for more experienced users.
One of the best Emacs packages on offer.

Before installing a package or updating existing ones, you need to
refresh the archive's index: =M-x package-refresh-contents=.

Then install the package =M-x package-install= followed by =vertico=.

Either do =M-x vertico-mode= or add this to your configuration and
evaluate it:

#+begin_src emacs-lisp
(vertico-mode 1)
#+end_src

* The Help system is your best friend

You can always learn by watching videos such as mine or those of David
Wilson (System Crafters), Andrew Tropin, Mike Zamansky, Raoul Comninos
(Emacs Elements), and others, though the best option will always be to
rely on your own means and leverage the self-documenting nature of
Emacs.  In other words, to become a competent user you need to master
the Help system.

All help commands start with the =C-h= key.  Get started with those:

+ C-h f :: Help for /functions/.
+ C-h v :: Help for /variables/.
+ C-h o :: Help for functions /or/ variables.
+ C-h k :: Help for the given /key/ binding.
+ C-h w :: Help for /where/ the given command is bound to.
+ C-h m :: Help for the current /modes/ (major and minor).

When the point is over a symbol (proper name) of a function or variable,
the =C-h f=, =C-h v=, or =C-h o= will use that as the default choice.

Note that when the symbol of a function or variable contains two dashes
=--= then it means that it is intended for use inside the file/package
that defines it.  Exercise caution if you plan to incorporate those in
your setup, as they might change without prior notice.

* The C-h as a prefix and as a suffix

In the previous section we demonstrated key sequences that start with
=C-h=.  This means that =C-h= is used as a prefix.  There is, however, the
option to use this key as a suffix to any sequence that is not yet
concluded.

For example, Emacs has lots of commands that start with =C-x r=.  These
relate to registers, bookmarks, and rectangle operations.  To produce a
Help buffer that shows us all the extensions to that sequence, we append
=C-h= to it, so =C-x r C-h=.

You can even use the =C-h= /suffix/ for the =C-h= /prefix/.  This is a fancy way
of saying that =C-h C-h= will present a special view about the available
help functions.

If you are using the =embark= package by Omar Antolín Camarena and do not
want to produce a Help buffer for the =C-h= suffix, add this to your
configuration:

#+begin_src emacs-lisp
(setq prefix-help-command #'embark-prefix-help-command)
#+end_src

Though make a habit to always learn the defaults before making sweeping
changes.  Otherwise you may not appreciate what is on offer and might
not fully understand how your system is pieced together.

* How to read manuals (they are not novels!)

Emacs is a powerful piece of software.  Its potential is unlocked only
after you become familiar with it.  That requires commitment, patience,
and persistence.

In other words: you learn Emacs one step at a time.  Part of that
process involves reading the wealth of documentation that is available
through the built-in Info reader.  Access it with =C-h i= and then use
=C-h m= to learn all about that mode.

Because the Emacs manual is a special file that you will be visiting
often, it has its own key binding: =C-h r= (think of "r" as a mnemonic for
"read").

As with all technical documentation, you do not read them in one go.
They are not stories.  Instead, you study them one snippet at a time.
Once you learn about it, you move to the next one, gradually broadening
your knowledge (and/or refining your setup).

* Tinker with Emacs Lisp (Elisp)

When you are comfortable with the aforementioned, you might want to
start tweaking Emacs to your liking by writing your own code.  As with
all things Emacs: keep it slow and steady.

I learnt Elisp without being a programmer and with no background
knowledge in Lisp or related.  Now, after 2.5 years of using Emacs, I am
at a point where I am "good enough" with my skills and have been
maintaining lots of code/packages.

Let's start with a very simple scenario, which is drawn from my early
days with Emacs.

+ Suppose you want to write a function that will be bound to a key.

+ Those are called "commands" because they are interactive.

+ This command will move the point (the cursor) 15 lines at a time in
  the given direction.

+ Type =C-h k= and follow it up with =C-n= or =C-p= (the commands that move to
  the next/previous line).  You will learn that those are called
  ~next-line~ and ~previous-line~, respectively.  The Help buffer tells you
  that these commands accept a numeric argument to move N lines at a
  time.  And it also includes a direct link to the source code.  By
  looking at source code you will eventually infer that functions are
  declared with the ~defun~ keyword (technically a Lisp macro).

With those in mind, you switch to the =*scratch*= buffer and start
experimenting:

#+begin_src emacs-lisp
(defun my-next-15-lines ()
  "Move to the next 15 lines."
  (interactive)                   ; this turns a function into a command
  (next-line 15))

(defun my-previous-15-lines ()
  "Move to the previous 15 lines."
  (interactive)
  (previous-line 15))
#+end_src

If you read the aforementioned Help buffer closely, you will realise
that your code can be improved, because the right command for custom
code is to use ~forward-line~.  So you update things accordingly:

#+begin_src emacs-lisp
(defun my-next-15-lines ()
  "Move to the next 15 lines."
  (interactive)
  (forward-line 15))

(defun my-previous-15-lines ()
  "Move to the previous 15 lines."
  (interactive)
  (forward-line -15))
#+end_src

Now you want to bind those to Ctrl+Shift and then n/p.  To learn how
Emacs represents any key, use =C-h k= and then type it.  You will get
=C-S-n= and =C-S-p=.

The manual explains how to write key bindings.  Here is how I do it:

#+begin_src emacs-lisp
(define-key global-map (kbd "C-S-n") #'my-next-15-lines)
(define-key global-map (kbd "C-S-p") #'my-previous-15-lines)
#+end_src

Notice that the =global-map= applies to all major modes.  Many commands
apply only to specific modes and thus have their own keymaps.  The =C-h k=
buffer informs you accordingly.

* Experiment with Elisp conditionality

Building on the previous example, let's add some variables and
conditions to the mix.  Here is a basic variable:

#+begin_src emacs-lisp
(defvar my-first-toggle nil
  "If non-nil, do stuff.")
#+end_src

[ Evaluate code by moving to its end and typing =C-x C-e= or in Elisp
  buffers with =C-M-x= with point inside of it.  There is also the =M-:=
  way, but let's keep it simple. ]

Let's go to the =*scratch*= and start experimenting:

#+begin_src emacs-lisp
(if my-first-toggle
    (message "It is true")
  (message "It is not true"))
#+end_src

While this may not be practical, let's add a third command which re-uses
the previous two:

#+begin_src emacs-lisp
(defun my-next-15-lines ()
  "Move to the next 15 lines."
  (interactive)
  (forward-line 15))

(defun my-previous-15-lines ()
  "Move to the previous 15 lines."
  (interactive)
  (forward-line -15))

(defun my-next-or-prev-15-lines ()
  "Move to the next or previous 15 lines.
The motion depends on the value of `my-first-toggle'."
  (interactive)
  (if my-first-toggle
      (my-next-15-lines)
    (my-previous-15-lines)))
#+end_src

A more realistic scenario in this case is to let your command accept
arguments (i.e. parameters that govern its behaviour).  The simplest one
is the =C-u= prefix argument, which you type before the key of the
command.

To read such an argument, you need this:

#+begin_src emacs-lisp
(defun my-next-or-prev-15-lines (&optional arg)
  "Move to the next or previous 15 lines.
With optional prefix ARG, move to the next lines.  Else to the previous ones."
  (interactive "P")
  (if my-first-toggle
      (my-next-15-lines)
    (my-previous-15-lines)))
#+end_src

Type =C-h f= and then search for ~interactive~.  It tells you other presets
it accepts (you can always write your own, but that is advanced stuff).

Notice the use of the =&optional= type.  Without it, the command would
return an error if it where not called with the prefix argument.  That
would, in turn, make the ~if~ meaningless, because it would always be
true.

* Practice and be patient

Learn the fundamentals.  The skill that will always be useful is to use
the Help system (and Emacs' introspectable nature in general, but let's
not get technical).

Be patient.  There is no shortcut to expertise.  All experts started out
as rookies.  This also means that you learn from your mistakes through a
process of trial and error: it is okay.

Tinker with Elisp by trying to understand small patterns.  Then
experiment with ways to combine them into larger designs.

Complement the official resources with other learning material, such as
blog posts, videos, and the code of other people.

Always---always---read the documentation!