Emacs Lisp Examples: Symbol Properties, Set Frame, Font, Fold, Shell Call

By Xah Lee. Date: . Last updated: .

This page shows some short but advanced examples of elisp. For more basic elisp examples, goto: Basic Elisp Examples.

This examples shows a simple use of defadvice.

(defadvice kill-ring-save (before slick-copy activate compile)
  "When called interactively with no active region, copy the current line."
   (if mark-active
       (list (region-beginning) (region-end))
       (message "Current line is copied.")
       (list (line-beginning-position) (line-beginning-position 2)) ) ) ))
(defadvice kill-region (before slick-copy activate compile)
  "When called interactively with no active region, cut the current line."
   (if mark-active
       (list (region-beginning) (region-end))
       (list (line-beginning-position) (line-beginning-position 2)) ) ) ))

Source www.emacswiki.org

Symbol Properties, Emacs Frame, Font

The following example lets you press a key to change the font in the current window. When pressed again, it cycles among 2 fonts, one monospaced and one variable width.

(defun cycle-font ()
  "Change font in current frame.
When called repeatedly, cycle thru a predefined set of fonts.
Warning: expected to work for Windows only. May not work in other OS."
  (if (not (eq last-command this-command))
        (set-frame-parameter nil 'font "Courier New-10")
        (put this-command 'state "2"))
     ((string= (get this-command 'state) "1")
      (set-frame-parameter nil 'font "Courier New-10") (put this-command 'state "2"))
     ((string= (get this-command 'state) "2" )
      (set-frame-parameter nil 'font "Lucida Sans Unicode-10") (put this-command 'state "1"))
     ) ) )

Although the code is only 15 lines, but this example shows several advanced elisp use and emacs system. It shows you: ① how to get and set properties to lisp symbols. ② how to set font in a “frame”. ③ How to test if a command is called repeatedly.

First, the code checks if it is being called repeatedly. If being called for the first time, then just set font to monospace. But if it is being called repeatedly, it will cycle between 2 fonts: one monospace (same width) and the other variable width.

;; check if the command is being called repeatedly
(if (not (eq last-command this-command))

In the above, the “this-command” and “last-command” are built-in variables. They store the command currently called or last called.

(set-frame-parameter nil 'font "Courier New-10") ;; set a font in current frame

In the above, set-frame-parameter is used to set a font in a frame. What we call a “window” is called a “frame” by emacs (and what we call a “pane” in a split window is called a “window” by emacs.).

(get this-command 'state) ; get the value of a lisp symbol's property
(put this-command 'state "2") ; set the value of a lisp symbol's property

In the above, it gets and sets lisp symbol's property. A lisp “symbol” is similar to a variable/function name in common languages. It stores a value. However, a lisp symbol can also hold other special values, called “properties”. You can almost think of it as a “object” of object oriented programing.

In the line (put this-command 'state "2"), the “this-command” evaluates to the symbol cycle-font, so it becomes this: (put 'cycle-font 'state "2"), and that means set the value “2” to the “state” property of the symbol cycle-font.

We are using a property to store the current state of the cycle point. The code checks the value of the “state” property to know what state it is in.

Symbols can have any number of properties, and each property can have any name, and each property's value can be almost any lisp datatype.

Lisp's symbol property is heavily used in elisp. For example, syntax coloring info are stored as property of strings, font faces use symbol property to store their definition such as color, size, font family.

PS: for a much better cycle-font command, see: How to Quickly Switch Fonts in Emacs.

(info "(elisp) Symbols")

Fold, Reduce

The following example shows a basic way to define the Fold (aka “reduce”) function that's commonly found in functional programing languages. For a example documentation of Fold in Mathematica, see: Mathematica: Fold

(defun fold (f x list)
  "Recursively applies (F i j) to LIST starting with X.
For example, (fold F X '(1 2 3)) computes (F (F (F X 1) 2) 3)."
 (let ((li list) (x2 x))
   (while li
     (setq x2 (funcall f x2 (pop li)))

Here's a example of usage:

(defun sum2 (x y) (+ x y)) ; add 2 numbers

(fold 'sum2 1 '(2 3)) ; returns 6

In the following, we use fold to define replace-string-pairs, which does string replacement by a given list of find-replace pairs.

(defun replace-string-pairs (str pairs)
  "replace the string STR repeatedy by the list PAIRS.
 (replace-string-pairs \"abcd\" '( (\"a\" \"1\") (\"b\" \"2\") (\"c\" \"3\")) )
 ⇒  123d"
     (x y) ""
     (replace-regexp-in-string (nth 0 y) (nth 1 y) x)
    str pairs

Note: this is just a toy example, not with practical considerations. Emacs lisp compiler does not optimize linear recursion (i.e. Tail Recursion). So the above is comparatively slow and resource hogging, also no error handling. In the Common Lisp package (require 'cl), there's the reduce function that is should be more practical.

For a practical elisp function that does multiple replace pairs, see: Elisp: Multi-Pair String Replacement Function.

Get Image Width Height

moved to Emacs: HTML Image Path to Img Tag

Open File in OS

Emacs: Open File in External App

Parsing Nested Text

The following example is the solution to this challenge:

Define a function f, such that (f "simple sexp") returns the argument as a lisp's list. Example: (f "(a (b) c)") returns (a (b) c).

This problem is a simple example of a parser. The program reads a string that represents a tree, and turn this string into a actual tree structure.

The following code is given by Jorgen Schäfer, 2006. If i recall correctly, it's written in less than 10 minutes.

(defun parse-string (s)
    (insert s)
    (goto-char (point-min))

(defun parse ()
   ((looking-at "\\s-*(\\s-*")
    (goto-char (match-end 0))
    (let ((exprs nil)
          (this nil))
      (while (and (not (looking-at "\\s-*)\\s-*"))
                  (setq this (parse)))
        (setq exprs (cons this exprs)))
      (goto-char (match-end 0))
      (apply 'list (reverse exprs))))
   ((looking-at "\\s-*\\([a-z]+\\)\\s-*")
    (goto-char (match-end 0))
    (intern (match-string 1)))
    (error "Syntax error"))))

; sample usage
(parse-string "(a (b (h h)) (c d))")

Thanks to Hauke Rehfeld for a improvment of “fold”.

Liket it? Put $5 at patreon. Or Buy Xah Emacs Tutorial. Thanks.