22 KiB
Emacs From Scratch - An Emacs tutorial for beginners
- Episode 1 - Introduction
- Episode 2 - Survive on the first day
- Episode 3 - Lisp Basics
- Episode 4 - Conditionals
- Episode 5 - Lists
- Episode 6 - Property & Association Lists
- Episode 7 - Basic Loops
- Episode 8 - More Loops
- Episode 9 - Introduction to Macros
- Episode 10 - More on Macros
- Episode 11 - Common pitfalls of Macros
- Episode 12 - Features & Load Paths
- Episode 13 - Editing Modes, Part 1
- Episode 14 - Editing Modes, Part 2
DONE Episode 1 - Introduction
CLOSED: [2022-01-09 Sun 19:03]
What is it all about?
- Create an editor, development environment and Window Manager (yeah window manager) and …
- GNU/Emacs is amazing
- Spread the joy
- Guide for contributors
- A guide for people who wants to learn some Lisp
The Plan
- Parallel video series to How to build a compiler with LLVM and MLIR
- Git branches
- No live coding
- Start from Emacs and Jump to FG42
- Feel free to contribute
FG42 and a bit of history
-
It's my:
- Editor/IDE
- Email client
- Window Manager
- File Manager
- Terminal Emulator
- IRC client (at some point)
- TODO manager
- "Office suit" (air qoute)
- ….
- Started on 2010 in a train
- I'm working on the V3
-
Requirements
- Emacs >= 27.1
- Website: https://fg42.org/
- Repository: https://devheroes.codes/FG42
- Website: https://lxsameer.com
- Email: lxsameer@gnu.org or lxsameer@lxsameer.com
DONE Episode 2 - Survive on the first day
CLOSED: [2022-02-11 Fri 10:28]
Why bother?
Why GNU/Emacs?
People might say because of
- Org-mode
- LSP support
- Support for many many languages
- It's lightweight
- Tons of plugins
- …
I'd say
- Coz it's a lisp environment with an editor attached to it
-
Lisp is the difference
- It's super easy to extend/hack emacs
- In compare to other editors
- You can do pretty much whatever your like in ELisp
- It's like a framework for building an editor
FAQ
What is the difference between Doom, Spacemacs, FG42 and …?
Different editors based on Emacs
Which one should I use?
How to install Emacs packages?
UI Basics & Basic Concepts
- Window
- Frame
- Mode line
- Buffers
- Mini buffer/Echo area
- Point
- Major mode
- Minor mode
Basic Keybindings
How to read key bindings? Here is an example C-x M-e 4
, it means press x
key while
holding Control
and then press e
key while pressing down Alt
and finally press 4
.
- Emacs tutorial
C-h t
(or https://www.gnu.org/software/emacs/tour/) - Quit emacs
C-x C-c
- Abort
C-g
-
Get Help
C-h
familyC-h f
C-h v
C-h m
C-h k
C-h a
- Run a command (function)
M-x
- Visit a file
C-x C-f
- Search in Buffer
C-s
- Save buffer
C-x C-s
- Select region
C-SPC
- Copy
M-w
- Cut(killing)
C-w
- Paste(yanking)
C-y
- Kill buffer
C-x k
- Switch buffer
C-x b
- Undo
C-/
- Eval expression
C-x C-e
describe-mode
Lisp basics
- Expressions everywhere
- Code a data structure
-
Almost everything in Lisp evaluates to themselves except:
- lists
- Symbols
- …
-
Lisp laws:
- Everything in Lisp is an expression and evaluation of an expression means replacing it with its value
-
Almost everything in Lisp evaluates to themselves except:
- Lists
- Symbols
- Quotes
-
…
- List evaluation
DONE Episode 3 - Lisp Basics
CLOSED: [2022-03-12 Sat 18:38]
FAQ:
-
How to install Emacs?
- Just make sure to have Emacs >= 27.1
-
What's the routine to use Emacs ?
- Don't use Emacs like how you use other Editors.
- There is no limits(Almost).
-
How evaluate forms?
- C-x C-e at the end of any expression and anywhere
- ielm
- Batch mode
So Fare:
Lisp rules:
- Lisp is made of Expressions and they evaluate to a value.
- All the expression types evaluates to themselves with some exceptions.
- List evaluation
We will continue with Lisp basics for few episodes
Variables
- How to define variables and constants
- Set the value of a variable
- Global and local scope
-
Lexical bindings vs Dynamic bindings
-*- lexical-binding: t; -*-
Functions
- How to define functions
- vs macros
- vs special forms
DONE Episode 4 - Conditionals
CLOSED: [2022-04-09 Sat 11:24]
What we learned so far
what is true and what's not
Let & prog family
Conditional special forms
- if
- when
- unless
- cond
Useful forms and functions
- eq
- equal
- and
- not
- or
- …
Type Predicts
- numberp
- stringp
- listp
- boundp
- More at https://www.gnu.org/software/emacs/manual/html_node/elisp/Type-Predicates.html
type-of
DONE Episode 5 - Lists
CLOSED: [2022-05-18 Wed 12:40]
What is a list?
- Not a primitive type
- A linked list made out of cons cells
Cons Cells
- A container for pairs
- CAR
- CDR (could-er)
digraph {
graph [bgcolor=transparent]
fontcolor="gray80"
node [color=gray80 shape="box", fontcolor="gray80"]
edge [color=gray80]
rankdir = "LR"
subgraph cluster_0 {
label="Cell"
color="gray80"
graph [bgcolor=transparent, fontcolor="gray80"]
node [color=gray80 shape="box", fontcolor="gray80"]
a0[label="car: 'head"]
b0[label="cdr: 'tail"]
}
}
(setq x1 (cons 'head 'tail))
(car x1)
(cdr x1)
'(head . tail)
(cons 2 nil)
(cons 1 x1)
digraph {
graph [bgcolor=transparent]
fontcolor="gray80"
node [color=gray80 shape="box", fontcolor="gray80"]
edge [color=gray80]
rankdir = "LR"
subgraph cluster_0 {
label="Cell"
color="gray80"
graph [bgcolor=transparent, fontcolor="gray80"]
node [color=gray80 shape="box", fontcolor="gray80"]
a0[label="car: 1"]
b0[label="cdr: "]
}
subgraph cluster_1 {
label="Cell"
color="gray80"
node [color=gray80 shape="box"]
a1[label="car: 2"]
b1[label="cdr: "]
b0 -> a1
}
subgraph cluster_2 {
label="Cell"
color="gray80"
node [color=gray80 shape="box"]
a2[label="car: 3"]
b2[label="cdr: nil"]
b1 -> a2
}
}
(setq x2 (cons 1 (cons 2 (cons 3 nil))))
(setq x3 (list 1 2 3))
Some useful functions
- add-to-list
(setq x4 (list 1 2 3 4))
(add-to-list 'x4 3)
(add-to-list 'x4 6)
- push & pop
(setq x5 (list 1 2 3 4))
(push 1 x5)
(pop x5)
- member
(setq x6 (list 'a 'b 1 2))
(member 'z x6)
(member 1 x6)
- delete
(setq x7 (list 1 2 3 4 5))
(delete 3 x7)
x7
- remove
(setq x8 (list 1 2 3 4 5))
(remove 3 x8)
x8
- car
- cdr
(setq x9 (list 1 2 3 4))
(car x9)
(cdr x9)
DONE Episode 6 - Property & Association Lists
CLOSED: [2022-06-13 Mon 10:38]
I have an idea
- Develop FG42 on stream
-
I'll announce the date and time on:
- Twitter: @lxsameer
- Youtube Community (maybe)
eq vs equal
eq
checks whether two objects are the sameequal
checks whether two objects have similar structure and content.
Property List (plist)
- Is a list of paired elements
- Each of the pairs associates a property name with a property or value
- The order of the property names (keys) is not significant
- Keys must be unique
- Emacs uses plists to store text properties and symbol properties
Examples
(setq x '(:a 1 "b" 2 c 3 :d ("foo" "bar") :j nil))
x
Functions
plist-get
orlax-plist-get
(plist-get x :a)
(plist-get x "b")
(lax-plist-get x "b")
plist-put
orlax-plist-put
(plist-put x :z 325)
(plist-put x :z 10)
(lax-plist-put x "b" 20)
plist-member
(plist-get x :j)
(plist-get x :g)
(plist-member x :j)
(plist-member x :g)
An example use case of plist
(defun foo (&rest args)
(message "ARGS: %s" args)
(plist-get args :name))
(foo :age 20 :name 'bob "blah" '(12 34 4))
Association List (alist)
- It is a list of cons cells called associations
- Mapping some keys to some values
- The
CAR
of each cons cell is the key, and theCDR
is the associated value - The order of associations matters
Examples
(setq y '((:a . 1) ("b" . 2) (c . 3) (:d "foo" "bar")))
y
Functions
assoc
(assoc :a y)
(assoc "b" y)
rassoc
(rassoc 1 y)
(rassoc 3 y)
assq
(assq :a y)
(assq "b" y)
alist-get
(alist-get :a y)
(alist-get :z y "blah")
cons
(setq y1 (cons '(:z . 31) y))
(assoc :z y1)
DONE Episode 7 - Basic Loops
CLOSED: [2022-07-09 Sat 10:31]
About previous episode
- Duplicate keys in an association list
eval-defun
While
- Form:
(while CONDITION
...body...)
- Loops and evaluates
body
as long as theCONDITION
evaluates to true. - It always evaluats to the value of the
CONDITION
which will be false aaaaall the time. - We need to explicitly manage the condition.
- Example:
(let ((foo 1))
(while (< foo 10)
(message ">> %s" foo)
(setq foo (+ 1 foo))))
(let ((foo '(bar baz)))
(while foo
(message ">> %s" (car foo))
(setq foo (cdr foo))))
dolist
- It's a macro
- Form:
(dolist (element list [result])
...body...)
- Evaluates the
body
withelement
set to the CAR oflist
on each iteration - At the ends evaluates
result
and return the evaluation result (ifresult
is provided) - Example:
(let ((foo '(9 8 7 6 5 4)))
(dolist (x foo (+ 10 1))
(message ">> %s" x)))
(let ((foo '(9 8 7 6 5 4)))
(macroexpand-1
'(dolist (x foo 10)
(message ">> %s" x))))
dotimes
- It's a macro too
- Form:
(dotimes (var number)
...body...)
- It's similar to
dolist
but iteratesnumber
of times var
in will contains the number of current iteration- Example:
(let ((foo 10))
(dotimes (x foo)
(message ">> %s" x)))
(let ((foo 10))
(macroexpand-1
'(dotimes (x foo)
(message ">> %s" x))))
DONE Episode 8 - More Loops
CLOSED: [2022-07-29 Fri 15:11]
Recursion
- A Recursive function, is a function that defines in terms of itself
- Easy to implement
- But should be careful with it to aviod infinit loops
Example
(defun foo (int)
(print int)
(when (> int 0)
(foo (- int 1))))
(foo 10)
Functional style loops
Mapping functions Family
mapcar
(mapcar #'1+ '(1 2 3 4 5))
(mapcar (lambda (x) (* x x))
'(1 2 3 4))
mapc
(mapc #'1+ '(1 2 3 4 5))
(mapc #'print '(1 2 3 4 5))
map-concat
(mapconcat (lambda (x)
(format "[%s]" x))
'(1 2 3 4 5)
" <|> ")
Seq module
A collection of handy functions that operate on sequences
.
seq-reduce
(require 'seq)
(seq-reduce
(lambda (acc x)
(+ acc x))
'(1 2 3 4)
0)
seq-filter
(seq-filter (lambda (x) (< x 10))
'(8 12 22 9))
seq-partition
(seq-partition '(a b c d e f) 2)
seq-min
,seq-max
DONE Episode 9 - Introduction to Macros
CLOSED: [2022-09-11 Sun 11:08]
What is a macro?
- A macro is defined much like a function
- It works on compile time
- It returns a new expression as the return value
- The compiler "expands" a macro by replacing the macro call with the return value of the macro
-
Different evaluation rule than functions
- Unlike function calls, the arguments to a macro will be passed to the macro as they are, without evaluation.
- Macros vs inline functions
How to define and expand a macro?
- We can define a macro using
defmacro
form
(defmacro inc (val)
(list #'1+ val))
(defmacro mydef (name val)
(list 'setq (intern (concat "my-" (symbol-name name))) val))
(defmacro foo (val)
(1+ val))
-
We can inspect the expansion process using the
macroexpand
function family.macroexpand
: Expands the macros until no macro exists in the top level formsmacroexpand-all
: Expands the macros all the way down to the subformsmacroexpand-1
: Expands the macro only for one level or cycle
(macroexpand-1 '(inc 10))
(macroexpand-1 '(mydef blah 20))
(print (macroexpand-1 '(when (> 10 5) (print "something"))))
Feedback or question
- https://lxsameer.com
- @lxsameer on Twitter
DONE Episode 10 - More on Macros
CLOSED: [2022-11-04 Fri 15:25]
Quasiquote aka backquote (`)
- Backquote constructs allow us to quote an expression, but selectively evaluate sub-expressions
- Similar to a template engine for lisp expressions
- It can be used on its own but it will shine in macro definitions
- On the simplest form it is identical to
quote
- Special markers
,
and,@
can be used to mark expressions for evaluation ,
will replace the return value of the evaluation with the expression,@
will replace and splice the return value of the evaluation
Examples
`3
`(1 2 3 4)
`(1 2 3 ,(+ 5 6))
`(1 2 3 ,(list 4 5 6))
`(1 2 3 ,@(list 4 5 6))
`(setq ,(intern (upcase "some_var")) 3)
declare
form
-
It's a special macro which can be used to add meta properties to a function or macro
doc-string
indent
Real Examples
Let's discuss defflag
and when-flag
macros in /pouya-abbassi/FG42/src/commit/0d198274896caa9fc00cd41c16a334d9c307d6d5/core/fg42/flags.el
DONE Episode 11 - Common pitfalls of Macros
CLOSED: [2022-12-04 Sun 12:15]
Compiletime vs runtime
;; `do-something-with-side-effect' evaluates on compile time
;; in the macro's context
(defmacro wrong (x)
(when x
(do-something-with-side-effect x)))
;; `do-something-with-side-effect' evaluates on run time
;; in the caller's context
(defmacro correct (x)
(when x
`(do-something-with-side-effect ,x)))
Variable Capture
;; `result' will hide any binding with the same name in the
;; parent scope
(defmacro wrong (f &rest body)
`(let ((result ,f))
(if result
,@body
(message "Failed: %s" result))))
(defvar result 1)
(wrong 200 (message "> %s" result))
;; Instead we need to generate a local binding
(defmacro correct (f &rest body)
(let ((var (gensym)))
`(let ((,var ,f))
(if ,var
,@body
(message "Failed: %s" ,var)))))
(defvar result 1)
(correct 200 (message "%s" result))
More on uninterned symbols: https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html
Many evaluation
;; The argument `x' will be evaluated 2 times in this example.
;; what if it contains side effects?
(defmacro wrong (x)
`(if ,x
(do-something-with ,x)
(do-somthing-else ,x)))
;; It's better to use a local binding for it and evaluate it
;; just once
(defmacro correct (x)
(let ((var (gensym)))
`(let ((,var ,x))
(if ,var
(do-something-with ,var)
(do-somthing-else ,var)))))
Evaluating arguments of a macro
Never evaluate the arguments of a macro manually. E.g. with eval
;; `eval' here will be evaluated before the caller context
;; is even populated
(defmacro wrong (x)
(list 'setq (eval x) 'blah))
;; The correct way is to mark `x' for evaluation instead
(defmacro correct (x)
`(setq ,x 'blah))
More info: https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval-During-Expansion.html
What's next?
DONE Episode 12 - Features & Load Paths
CLOSED: [2023-01-14 Sat 11:44]
Emacs Lisp files
Write elisp code in files with .el
suffix.
Batch mode
Execute an elisp file via Emacs in a non-interactive (script like) fashion:
emacs --batch -l /path/to/the/file
load
function
Loads an elisp file into the running Emacs process. For more info C-h f load
.
(load "/path/to/the/file")
(load "PATH1")
- It first looks for the
PATH + .elc
combination - If not successful, looks for the
PATH + .el
combination - If not successful, looks for platform dependent suffixes
- If not successful, tries to load the
PATH
as it is
Load Path
load-path
List of directories to search for files to load.
(add-to-list 'load-path "/path/to/a/directory")
EMACSLOADPATH
environment variable
Emacs feature
Emacs tracks loaded packages and file via features
. Each elisp file can provide
, zero or
more features
.
Features are just symbols.
features
list
A list of all loaded features. For more info, try C-h v features
.
featurep
A predicate function to check whether a feature is loaded or not.
(featurep 'some-feature)
provide
(provide 'some-feature)
require
If the given feature as the parameter is not loaded yet, loads it via the load
function. For more info, C-h f require
.
(require 'some-feature)
;; Or
(require 'some-feature "/path/to/file")
Installing Emacs packages the hard way
We can clone a library somewhere on the disk and add the path to it to the load-path
list
and load the library files.
But that would be tedious to do so for all the libraries. That's why we use a package manager
DONE Episode 13 - Editing Modes, Part 1
CLOSED: [2023-06-10 Sat 15:44]
Emacs provides a concept called editing mode
that allows
us to control different aspect of the editor.
Major Modes
Major modes are mutually exclusive, so each Buffer has exactly on major mode and just one major mode can be active at any given time. So, it is possible to switch between different major modes.
Major modes control the main behaviour of your editor for each buffer. For example, The active major mode might:
- Provide syntax highlighter
- Control the indentation
- Provide a local
keymap
- …
To put it simply, major modes are specialized to handle certain files and buffers.
Naming Convenstion
Usually, the name of a major mode is like <major-mode-name>-mode
which is
an interactive function that we can call either directly or via M-x
interface.
For example:
- python-mode
- fundamental-mode
- emacs-lisp-mode
- …
Keymap
We will take about Keymaps in the future
Major modes usually have a keymap to hold their local keybindings that has the
the -map
suffix.
Hooks
We will take about Hooks in the future
Hooks are lists of functions that can be called on certain occasions. For example, after a major mode activates.
Usually major modes come with at least one hook called <major-mode-name>-hook
that
runs after the major mode activates. E.g. emacs-lisp-mode-hook
or python-mode-hook
.
How Emacs choose a major mode for a buffer?
When we open a file, Emacs goes through some hoops to figure out what major mode should it choose for that buffer.
To put it simply and avoid a lot of details, Emacs will try to match the buffer name
against the keys in auto-mode-alist
and uses the mode provided by that key as a value.
(("\\`/tmp/fol/" . text-mode)
("\\.texinfo\\'" . texinfo-mode)
("\\.texi\\'" . texinfo-mode)
("\\.el\\'" . emacs-lisp-mode)
("\\.c\\'" . c-mode)
("\\.h\\'" . c-mode)
…)
For more info check out: https://www.gnu.org/software/emacs/manual/html_node/elisp/Auto-Major-Mode.html
How to switch the major mode
Just call the other mode or
major-mode-suspend
: Kills all the buffer local variables and record themmajor-mode-restore
: This function restores the major mode recorded bymajor-mode-suspend
For more info: https://www.gnu.org/software/emacs/manual/html_node/elisp/Major-Modes.html
Minor Modes
A minor mode provides optional features that users may enable or disable independently of the choice of major mode. Minor modes can be enabled individually or in combination.
The main difference with major modes is that minor modes are not mutually exclusive and are not tied to buffers. They can operate globally or local to buffers.
How to enable/disable minor modes?
In order to toggle a minor mode we just have to call its function with no argument interactively. In order to disable a minor mode we can pass a negative integer, and to enable it we can pass a positive integer.
;; Enable
(blah-mode 1)
;; Disable
(blah-mode -1)
Useful functions and variables
describe-mode
: Display documentation of current major mode and minor modes and a brief summary of the state of the current buffer.local-minor-modes
: This buffer-local variable lists the currently enabled minor modes in the current buffer, and is a list of symbols.global-minor-modes
: This variable lists the currently enabled global minor modes, and is a list of symbols.minor-mode-list
: The value of this variable is a list of all minor mode commands.