#+TITLE: Emacs From Scratch - An Emacs tutorial for beginners #+SEQ_TODO: TODO(t/!) NEXT(n/!) BLOCKED(b@/!) | DONE(d%) CANCELLED(c@/!) FAILED(f@/!) #+TAGS: READER(r) MISC(m) #+STARTUP: logdrawer logdone logreschedule indent content align constSI entitiespretty overview * 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= 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 - 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) #+NAME: ep-5-cons #+BEGIN_SRC graphviz-dot :file /tmp/cons.svg :cmdline -Kdot -Tsvg 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"] } } #+END_SRC #+RESULTS: ep-5-cons [[file:/tmp/cons.svg]] #+BEGIN_SRC emacs-lisp (setq x1 (cons 'head 'tail)) (car x1) (cdr x1) '(head . tail) (cons 2 nil) (cons 1 x1) #+END_SRC #+NAME: ep-5-list #+BEGIN_SRC graphviz-dot :file /tmp/list.svg :cmdline -Kdot -Tsvg 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 } } #+END_SRC #+RESULTS: ep-5-list [[file:/tmp/list.svg]] #+BEGIN_SRC emacs-lisp (setq x2 (cons 1 (cons 2 (cons 3 nil)))) (setq x3 (list 1 2 3)) #+END_SRC ** Some useful functions - add-to-list #+BEGIN_SRC emacs-lisp (setq x4 (list 1 2 3 4)) (add-to-list 'x4 3) (add-to-list 'x4 6) #+END_SRC - push & pop #+BEGIN_SRC emacs-lisp (setq x5 (list 1 2 3 4)) (push 1 x5) (pop x5) #+END_SRC - member #+BEGIN_SRC emacs-lisp (setq x6 (list 'a 'b 1 2)) (member 'z x6) (member 1 x6) #+END_SRC - delete #+BEGIN_SRC emacs-lisp (setq x7 (list 1 2 3 4 5)) (delete 3 x7) x7 #+END_SRC - remove #+BEGIN_SRC emacs-lisp (setq x8 (list 1 2 3 4 5)) (remove 3 x8) x8 #+END_SRC - car - cdr #+BEGIN_SRC emacs-lisp (setq x9 (list 1 2 3 4)) (car x9) (cdr x9) #+END_SRC * 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 #+BEGIN_SRC emacs-lisp (setq x '(:a 1 "b" 2 c 3 :d ("foo" "bar") :j nil)) x #+END_SRC *** Functions - ~plist-get~ or ~lax-plist-get~ #+BEGIN_SRC emacs-lisp (plist-get x :a) (plist-get x "b") (lax-plist-get x "b") #+END_SRC - ~plist-put~ or ~lax-plist-put~ #+BEGIN_SRC emacs-lisp (plist-put x :z 325) (plist-put x :z 10) (lax-plist-put x "b" 20) #+END_SRC - ~plist-member~ #+BEGIN_SRC emacs-lisp (plist-get x :j) (plist-get x :g) (plist-member x :j) (plist-member x :g) #+END_SRC ** An example use case of plist #+BEGIN_SRC emacs-lisp (defun foo (&rest args) (message "ARGS: %s" args) (plist-get args :name)) (foo :age 20 :name 'bob "blah" '(12 34 4)) #+END_SRC ** 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 #+BEGIN_SRC emacs-lisp (setq y '((:a . 1) ("b" . 2) (c . 3) (:d "foo" "bar"))) y #+END_SRC *** Functions - ~assoc~ #+BEGIN_SRC emacs-lisp (assoc :a y) (assoc "b" y) #+END_SRC - ~rassoc~ #+BEGIN_SRC emacs-lisp (rassoc 1 y) (rassoc 3 y) #+END_SRC - ~assq~ #+BEGIN_SRC emacs-lisp (assq :a y) (assq "b" y) #+END_SRC - ~alist-get~ #+BEGIN_SRC emacs-lisp (alist-get :a y) (alist-get :z y "blah") #+END_SRC - ~cons~ #+BEGIN_SRC emacs-lisp (setq y1 (cons '(:z . 31) y)) (assoc :z y1) #+END_SRC ** References - https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html - https://www.gnu.org/software/emacs/manual/html_node/elisp/Plist-Access.html - https://www.gnu.org/software/emacs/manual/html_node/elisp/Plists-and-Alists.html * 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: #+BEGIN_SRC emacs-lisp (while CONDITION ...body...) #+END_SRC - 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: #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC ** dolist - It's a macro - Form: #+BEGIN_SRC emacs-lisp (dolist (element list [result]) ...body...) #+END_SRC - 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: #+BEGIN_SRC emacs-lisp (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)))) #+END_SRC ** dotimes - It's a macro too - Form: #+BEGIN_SRC emacs-lisp (dotimes (var number) ...body...) #+END_SRC - It's similar to ~dolist~ but iterates ~number~ of times - ~var~ in will contains the number of current iteration - Example: #+BEGIN_SRC emacs-lisp (let ((foo 10)) (dotimes (x foo) (message ">> %s" x))) (let ((foo 10)) (macroexpand-1 '(dotimes (x foo) (message ">> %s" x)))) #+END_SRC * 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 #+BEGIN_SRC emacs-lisp (defun foo (int) (print int) (when (> int 0) (foo (- int 1)))) (foo 10) #+END_SRC ** Functional style loops *** Mapping functions Family - ~mapcar~ #+BEGIN_SRC emacs-lisp (mapcar #'1+ '(1 2 3 4 5)) (mapcar (lambda (x) (* x x)) '(1 2 3 4)) #+END_SRC - ~mapc~ #+BEGIN_SRC emacs-lisp (mapc #'1+ '(1 2 3 4 5)) (mapc #'print '(1 2 3 4 5)) #+END_SRC - ~map-concat~ #+BEGIN_SRC emacs-lisp (mapconcat (lambda (x) (format "[%s]" x)) '(1 2 3 4 5) " <|> ") #+END_SRC *** Seq module A collection of handy functions that operate on ~sequences~. - ~seq-reduce~ #+BEGIN_SRC emacs-lisp (require 'seq) (seq-reduce (lambda (acc x) (+ acc x)) '(1 2 3 4) 0) #+END_SRC - ~seq-filter~ #+BEGIN_SRC emacs-lisp (seq-filter (lambda (x) (< x 10)) '(8 12 22 9)) #+END_SRC - ~seq-partition~ #+BEGIN_SRC emacs-lisp (seq-partition '(a b c d e f) 2) #+END_SRC - ~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 #+BEGIN_SRC emacs-lisp (defmacro inc (val) (list #'1+ val)) (defmacro mydef (name val) (list 'setq (intern (concat "my-" (symbol-name name))) val)) (defmacro foo (val) (1+ val)) #+END_SRC - 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 #+BEGIN_SRC emacs-lisp (macroexpand-1 '(inc 10)) (macroexpand-1 '(mydef blah 20)) (print (macroexpand-1 '(when (> 10 5) (print "something")))) #+END_SRC ** Feedback or question - https://lxsameer.com - [[https://twitter.com/lxsameer][@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 #+BEGIN_SRC emacs-lisp `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) #+END_SRC ** ~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 [[file:../core/fg42/flags.el]] * DONE Episode 11 - Common pitfalls of Macros CLOSED: [2022-12-04 Sun 12:15] ** Compiletime vs runtime #+BEGIN_SRC emacs-lisp ;; `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))) #+END_SRC ** Variable Capture #+BEGIN_SRC emacs-lisp ;; `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)) #+END_SRC More on *uninterned* symbols: https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html ** Many evaluation #+BEGIN_SRC emacs-lisp ;; 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))))) #+END_SRC ** Evaluating arguments of a macro Never evaluate the arguments of a macro manually. E.g. with =eval= #+BEGIN_SRC emacs-lisp ;; `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)) #+END_SRC 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: #+BEGIN_SRC bash emacs --batch -l /path/to/the/file #+END_SRC *** =load= function Loads an elisp file into the running Emacs process. For more info ~C-h f load~. #+BEGIN_SRC emacs-lisp (load "/path/to/the/file") (load "PATH1") #+END_SRC - 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. #+BEGIN_SRC emacs-lisp (add-to-list 'load-path "/path/to/a/directory") #+END_SRC *** =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. #+BEGIN_SRC emacs-lisp (featurep 'some-feature) #+END_SRC *** =provide= #+BEGIN_SRC emacs-lisp (provide 'some-feature) #+END_SRC *** =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~. #+BEGIN_SRC emacs-lisp (require 'some-feature) ;; Or (require 'some-feature "/path/to/file") #+END_SRC ** 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 =-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 =-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. #+BEGIN_SRC emacs-lisp (("\\`/tmp/fol/" . text-mode) ("\\.texinfo\\'" . texinfo-mode) ("\\.texi\\'" . texinfo-mode) ("\\.el\\'" . emacs-lisp-mode) ("\\.c\\'" . c-mode) ("\\.h\\'" . c-mode) …) #+END_SRC 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. #+BEGIN_SRC emacs-lisp ;; Enable (blah-mode 1) ;; Disable (blah-mode -1) #+END_SRC ** 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. * Episode 14 - Editing Modes, Part 2 ** Quick overview: *** A simple minor mode *** Interactive functions *** Hooks *** Keymaps ** Let's do it ** Resources: - https://www.gnu.org/software/emacs/manual/html_node/elisp/Library-Headers - https://www.gnu.org/software/emacs/manual/html_node/elisp/Hooks.html - https://www.gnu.org/software/emacs/manual/html_node/elisp/Keymaps.html