What is Emacs Lisp?

  • Dialect of the Lisp programming language
  • Used by the Emacs text editor to implement most of the editing functionality
  • Used to customize and extends emacs

Pretty much what makes emacs the programmer's editor

This talk

Three parts

  • Emacs Lisp as a programming language
  • Editor related features of the language
  • An example function to implement simple functionality

+ Some live examples in between

One-slide intro to Lisp

Lots of Irritating Silly Parentheses!

Or,

LISt Processing

The syntax is entirely made up of lists, almost every expression being a function call.

ELISP> (+ 1 2)
3
ELISP> (concat "hello" ", " "world")
"hello, world"

The first element of the list evaluated to a function and applied to the rest of the elements as arguments

Ways to evaluate expressions in Emacs

  • C-x C-e
  • M-x {eval-buffer,eval-region,eval-defun}
  • ielm (inferior emacs lisp mode)

Variable binding

set and setq can be used for top-level bindings

ELISP> (set 'a 20)
20
ELISP> (setq a 20)
20
ELISP> (setq b 30 c 40)
40

setq is a more convenient version of set which helps in two ways:

  • takes care of quoting the symbol
  • supports multiple bindings

Comments

anything after semicolon is considered a comment (ie. ignored)

; i am inside a comment
;; i am also inside a comment

(setq name "Vineet") ; me too a comment

Primitive Datatypes

[1/2..]

  • Numbers: 42, -100.05, 5e10, #b1010, #o37, #xabcd
  • Boolean: t, nil
  • Strings: "hello", "world"
  • Symbols: foo

Symbols are interned

  ELISP> foo
  *** Eval error ***  Symbol's value as variable is void: foo
  ELISP> 'foo
  foo
  

Primitive Datatypes

[..2/2]

  • Cons Cells
ELISP> (setq p (cons 1 2))
(1 . 2)
ELISP> (car p)
1
ELISP> (cdr p)
2

There are the most basic ones but there are many other primitive types related to programming such as vector, hash-table etc. as well as a few that are specific to emacs such as buffer, frame, window etc.

Lists (Non primitive)

ELISP> (1 2 3 4)
;; above will result in error
ELISP> (list 1 2 3 4)
(1 2 3 4)
ELISP> '(1 2 3 4)
(1 2 3 4)
ELISP> '() ; empty list 
ELISP> (equal '() nil)
t
ELISP> (cons 1 '())
ELISP> (setq nums (cons 1 (cons 2 (cons 3 (cons 4 '())))))
(1 2 3 4)
ELISP> (car nums) ; first
1
ELISP> (cdr nums) ; rest
(2 3 4)

Equality Predicates

;; comparing numbers
ELISP> (= 2 (+ 1 1))
t
;; checking for `same` objects
ELISP> (eq 1 1)
t
ELISP> (eq 'a 'a)
t
ELISP> (eq "hi" "hi")
nil
;; comparing objects with their content
ELISP> (equal (list 1 2 3) (list 1 2 3))
t
;; checking for string equality
ELISP> (string= "hi" (concat "h" "i"))
t

Control Structures

ELISP> (if t "yes" "no")
"yes"
ELISP> (if (not t) "yes" "no")
"no"

ELISP> (when t "yes")
"yes"

ELISP> (cond ((> a b) 1)
             ((< a b) -1)
             ((= a b) 0))

Local bindings

ELISP> (setq a 10)
10
ELISP> (let ((a 5)
             (b (+ a 1))
         (+ a b))
16
ELISP> a
10

let* can be used if subsequent bindings need to use previous ones.


Note: set, setq, if, cond, let etc. are special forms and not functions

Doing stuff sequentially

ELISP> (progn
         (do-something)
         (do-something-else)
         (+ 1 2)
         (+ 3 4))
7
ELISP>

usually code that uses progn involves side effects to be carried out in order.

Function definitions

ELISP> (defun greet (greeting name)
         "Optional docstring"
         (concat greeting ", " name))
greet
ELISP> (greet "hello" "vineet")
"hello, vineet"
ELISP>

;; anonymous functions
ELISP> ((lambda (x) (+ 1 x)) 3)
4

The result of the last expression of the function body is implicitly returned

Dynamic Binding

With support for Lexical Binding since v24.1

ELISP> (defun g (f)
         (lambda (x)
           (funcall f x)))
g
ELISP> (funcall (g (lambda (x) (+ 1 x))) 10)
*** Eval error ***  Symbol's value as variable is void: f

ELISP> (setq lexical-binding t)
t
ELISP> (defun g (f)
         (lambda (x)
           (funcall f x)))
g
ELISP> (funcall (g (lambda (x) (+ 1 x))) 10)
11

Regex

(defconst sphinx-doc-fun-regex "^\s*def \\([a-zA-Z0-9_]+\\)(\\(.*\\)):$")

(defun sphinx-doc-fun-def (string)
  "Returns a pair of name of the function and list of the name of
  the arguments"
  (when (string-match sphinx-doc-fun-regex string)
    (list (match-string 1 string)
          (sphinx-doc-fun-args (match-string 2 string)))))

Loading code

load-path: list of paths where files to load are looked up

eager loading: load

lazy loading: autoload, "require"-ing provided features

Programming editor functionality

Editor related types

buffer, frame, window etc are editor related types

ELISP> (current-buffer)
#<buffer *ielm*>
ELISP> (bufferp (current-buffer))
t

Interactive functions

Interactive functions can be called using M-x <function-name>.

They can also be associated to keys bindings.

When we type out a key sequence, emacs calls the bounded function.

(defun some-function ()
  (interactive)
  ...)

Evaluate the defun and then,

M-x some-function

Example

Programmatically simulating user interaction

Write the name of the current buffer with the message "Hi! You are viewing the '<buffer-name>' buffer" on the next line with an indent of 8 spaces and then move the cursor back to the earlier position

Example

What would the user do?

  • goto next line
  • type out 8 spaces from the beginning of the line
  • type out the message along with the buffer name
  • move to the older position

Example

What will our function need to do?

  • goto next line
  • type out 8 spaces from the beginning of the line
  • get the name of the current buffer
  • type out the message with the name of the buffer
  • move to the older position

Example

Working Code

(defun buffer-greet ()
  (interactive)
  (let ((name (buffer-name (current-buffer))))
    (save-excursion
      (progn
        (next-line)
        (move-beginning-of-line 1)
        (insert "        ")
        (insert (format "Hi! You are viewing the '%s' buffer" name))))))

Thank You!