FG42/docs/videos.org

22 KiB

Emacs From Scratch - An Emacs tutorial for beginners

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

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 family

    • C-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:

    1. Everything in Lisp is an expression and evaluation of an expression means replacing it with its value
    2. Almost everything in Lisp evaluates to themselves except:

      • Lists
      • Symbols
      • Quotes
    3. 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:

  1. Lisp is made of Expressions and they evaluate to a value.
  2. All the expression types evaluates to themselves with some exceptions.
  3. 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

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"]
    }
}

/tmp/cons.svg

  (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
    }
}

/tmp/list.svg

  (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 same
  • equal 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 or lax-plist-get
  (plist-get x :a)
  (plist-get x "b")
  (lax-plist-get x "b")
  • plist-put or lax-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 the CDR 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 the CONDITION 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 with element set to the CAR of list on each iteration
  • At the ends evaluates result and return the evaluation result (if result 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 iterates number 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 forms
    • macroexpand-all: Expands the macros all the way down to the subforms
    • macroexpand-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

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 /FG42/FG42/src/branch/master/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 them
  • major-mode-restore: This function restores the major mode recorded by major-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.