If you enjoyed this site, please consider donating $3. Any amount is appreciated. Thanks!

Emacs Lisp Idioms

Xah Lee, 2008-06, 2009-08

This page collects some basic emacs lisp programing patterns related to writing a command when actively editing. For example, writing a command that does google search of the word under cursor, a command that switches to web browser to visit the url under cursor, a command that does find/replace pairs in a text selection, a command that inserts XML template at cursor point, a command that rename a given function in the current file, etc.

You should first be familiar with basic emacs functions that get cursor position, move cursor, search text, inserting and deleting text. See Emacs Lisp Basic Text-editing Functions. For emacs lisp idioms on text processing in batch like Perl, see: Text Processing with Emacs Lisp.

Typical Command Template

This is the typical template for all user-defined emacs commands.

(defun myCommand ()
  "one sentence summary of what this command do

more details here. Be sure to mention what it returns."
  (interactive)
  (let (localVar1 localVar2 ...)
    (setq localVar1 ...)
    (setq localVar2 ...)
    ...
    ;; do something ...
  )
)

Grabbing Text

Grab text of given starting and ending positions

; get the string from buffer
(setq myStr (buffer-substring myStartPos myEndPos))
(setq myStr (buffer-substring-no-properties myStartPos myEndPos))

Emacs's string can have properties for the purposes of syntax coloring, active button, hypertext, etc. The “buffer-substring-no-properties” function just return a plain string without these properties. However, most functions that takes string as argument can also accept a string that has properties.

(info "(elisp) Buffer Contents")

Grabbing the current word or line

Grabbing the current word, line, sentence, url, file name etc.

; grab a thing at point. The “thing” is a semantic unit. It can be a
; word, symbol, line, sentence, filename, url and others.

; grab the current word
(setq myStr (thing-at-point 'word))

; grab the current word with hyphens or underscore
(setq myStr (thing-at-point 'symbol))

; grab the current line
(setq myStr (thing-at-point 'line))

; grab the start and end positions of a word (or any other thing)
(setq myBoundaries (bounds-of-thing-at-point 'word))

Note that, when the thing is a “symbol”, it usually means any alphanumeric sequence with dash “-” or underscore “_” characters. For example, if you are writing php reference lookup command, and the cursor is on p in “ print_r($y);”, you want to grab the whole “print_r” not just “print”. The exact meaning of symbol depends on the mode's Syntax Table.

(info "(elisp) Syntax Tables")

Here's a example of php reference lookup command that grabs by “symbol” if there's no active region.

(defun php-lookup ()
  "Look up current word in PHP ref site in a browser.\n
  If a region is active (a phrase), lookup that phrase."
  (interactive)
  (let (myword myurl)
    (setq myword
          (if (and transient-mark-mode mark-active)
              (buffer-substring-no-properties (region-beginning) (region-end))
            (thing-at-point 'symbol)))
    (setq myurl
          (concat "http://us.php.net/" myword))
    (browse-url myurl)))

(info "(elisp) Buffer Contents")

Grab Between Matching Pairs

Grab the current text between delimiters such as between angle brackets “<>”, parens “()”, double quotes “""”, etc.

The trick is to use skip-chars-backward and forward. In the following example, the p1 is set to the position of the double quote to the left of cursor, and p2 is set to the position on the double quote to the right of the cursor.

(defun select-inside-quotes ()
  "Select text between double straight quotes
on each side of cursor."
  (interactive)
  (let (p1 p2)
    (skip-chars-backward "^\"")
    (setq p1 (point))
    (skip-chars-forward "^\"")
    (setq p2 (point))

    (goto-char p1)
    (push-mark p2)
    (setq mark-active t)
  )
)

If you want to grab text inside parens, you can change the “"^\""” to “"^("” and “"^)"”. Similar for matching brakets “<>”. However, note that this code does not consider nested matching pairs.

Acting on Region

Region

The last Mark position to the current cursor position is called a Region. As soon as you did set a mark in a buffer (“Ctrl+Enter” or “Alt+x set-mark-command”), there is a region. So, a region is almost always there.

Active Region

Emacs has a transient-mark-mode. When on, it will highlight the region. Because emacs almost always has a region, and highlight a region always in not a desired behavior, so there's a new concept of Active Region. When set-mark-command is called, and no other text changing commands are called, the Region is active. The current active/inactive status of mark is stored in the variable mark-active, and this variable can be set/unset by commands.

Emacs's concept of “active region” is very much equivalent to the modern term “Text Selection”. Paraphrase: when a region is active, there is a text selection.

In summary: A region is the last mark position to current cursor position in the buffer. A region exist as soon as the user set a mark in a buffer. If transient-mark-mode is on, then there's a concept of Active Region. The value of the variable mark-active indicates whether the region is active. When a region is active, it is highlighted.

By convention, commands ending in the word “-region” acts on the region, regardless whether the region is active. For example: kill-region, comment-region, fill-region, indent-region. Some command's behavior changes depending whether there is a text selection (that is: whether the region is active). If there's text selection, it acts on it, else it may acts on the current word or do other things. For example: comment-dwim.

Starting with emacs 23, transient-mark-mode is on by default, and many commands, including fill-paragraph, ispell-word, indent-for-tab-command, behavior has changed. Now they will now act on region if it is active (paraphrase: they act on text selection if there is one). This is convenient to user, because this way, user doesn't have to choose the region or non-region version of the same command. The command will automatically consider the current text selection as target when there is one. (This means, technically: when transient-mark-mode and mark-active both have values t.)

(info "(elisp) The Mark")

Working On The Region

Here's a idiom for a command that process the current region's text.

Let your function have 2 parameters, suppose they are named “start” and “end”, then use “(interactive "r")”, then the parameters will be filled with beginning and ending positions of the region. Example:

(defun ff (start end)
  "Prints region starting and ending positions." 
  (interactive "r")
  (let () 
    (message "Region starts: %d, end at: %d" start end)
  )
)

Working On Active Region or Current Word

Often you want a command that works on the current word (line, or block of text), but if there is a text selection, take the text selection as input. Here's a template for this.

(defun downcase-word-or-region ()
  "Downcase current word or region."
(interactive)
(let (pos1 pos2 bds)
  (if (and transient-mark-mode
           mark-active)
      (setq pos1 (region-beginning) pos2 (region-end))
    (progn
      (setq bds (bounds-of-thing-at-point 'symbol))
      (setq pos1 (car bds) pos2 (cdr bds))))

  ;; now, pos1 and pos2 are the starting and ending positions of the
  ;; current word, or current text selection if exist.
  (downcase-region pos1 pos2)
  ))

Prompting User for Input

Get User Input as Arguments

Often you want a command that prompts user to enter some argument.

Use this code “(interactive "‹code›‹promp string›")”. Example:

(defun query-friends-phone (name)
  "..."
  (interactive "sEnter friend's name: ")
  (message "Name: %s" name)
)

What the “(interactive "sEnter friend's name:")” does is that it will ask user to input something, taken as string, and becomes the value of your command's parameter.

The “interactive” can be used to get other types of input. Here are some basic examples of using “interactive”.

The prompt text can follow the single-letter code string.

If your function takes multiple inputs, you can promp user multiple times, using a single call “interactive”, by joining the promp code string with “\n” in between, like this:

(defun query-friends-phone (name age)
  "..."
  (interactive "sEnter friend's name: \nnEnter friend's age: ")
  (message "Name: %s, Age: %d" name age)
)

(info "(elisp) Defining Commands")

Query User For Input

The “(interactive ...)” clause is useful for filling parameters of your command. But sometimes you need to promp user in the middle of a program. For example: “Make change to this file?”. You can use “y-or-n-p” function. Like this:

(if (y-or-n-p "Do it?")
    (progn
      ;; code to do something here
    )
  (progn
    ;; code if user answered no.
  )
)

The y-or-n-p will ask the user to type a “y” or “n” character. You can also use “yes-and-no-p”, which forces user to type full “yes” and “no” to answer. This can be used for example when you want to confirm deleting files.

(info "(elisp) Yes-or-No Queries")

If you need more general mechanism for getting user input, you'll need to use “read-from-minibuffer”. This can be useful, for example, when you want your prompt to have keyword completion or input history.

(info "(elisp) Text from Minibuffer")

Processing String

In perl, there are maybe 20 functions that act on string. In elisp, there are only about 5, because elisp has a buffer data type that's more powerful and flexible, and you have over 3 thousand functions that acts on text in a buffer. When you have a string or text, and you need to do more than just getting substring or number of chars, put it in a temp buffer. Here's a example:

;; suppose mystr is a var whose value is a string
(setq mystr "some string here you need to process")
(setq mystrNew
      (with-temp-buffer
        (insert mystr)

        ;; code to manipulate the string here
        ;; ...

        (buffer-string) ; get result
        ))

Find Replace text

Find and Replace string is one of the most important method in text processing. Here's how you do it.

; idiom for string replacement in current buffer;

  (goto-char (point-min))
  (while (search-forward "myStr1" nil t) (replace-match "myReplaceStr1"))

  (goto-char (point-min))
  (while (search-forward "myStr2" nil t) (replace-match "myReplaceStr2"))

  ;; repeat for other string pairs

; if you need regexp, use search-forward-regexp 

If you need to do find replace on a region only, wrap the code with “(save-restriction (narrow-to-region start end) ‹find replace code here› )”.

; idiom for string replacement in region
(save-restriction
  (narrow-to-region start end)

  (goto-char (point-min))
  (while (search-forward "myStr1" nil t) (replace-match "myReplaceStr1"))

  ;; repeat for other string pairs
)

Apply to dired's Marked Files

To apply a function to marked files in dired, use “dired-get-marked-files”, like this:

;; idiom for processing a list of files in dired's marked files
 
;; suppose myProcessFile is your function that takes a file path
;; and do some processing on the file

(defun dired-myProcessFile ()
  "apply myProcessFile function to marked files in dired."
  (interactive)
  (require 'dired)
  (mapc 'myProcessFile (dired-get-marked-files))
)

For emacs lisp idioms on text processing in batch like Perl, see: Text Processing with Emacs Lisp.

The idioms on this page are packaged as a yasnippet template set. Download it at: Emacs Lisp Idiom Templates. For a explanation of what YASnippet is and how to use it, see How To Define Templates In YASnippet.


Related essays:

2008-06
© 2008 by Xah Lee.