18 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
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/branch/ep12/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?
Episode 12 - Features & Load Paths
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