Emacs Lisp Idioms for Writing Interactive Commands

Buy Xah Emacs Tutorial. Master emacs benefits for life.
, , …,

This page is a collection of basic emacs lisp programing patterns for writing interactive commands.

Command Template

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

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

More details here. Be sure to mention the return value if relevant.
Lines here should not be longer than 70 chars,
and don't indent them."
  (interactive)
  (let (localVar1 localVar2 …)
    (setq localVar1 …)
    (setq localVar2 …)
    …
    ;; do something …
  )
)

In your inline documentation, if you want clickable URL, or clickable reference to other commands, etc., See: Emacs Function's Inline Doc String Markups.

Get Buffer Text as String

Grab Text of Given Begin & End Positions

How to get a buffer region as string?

;; get the string from buffer
;; get the string of the first 10 chars in buffer
(setq myStr (buffer-substring 1 10))

Emacs's string can have text properties for the purposes of syntax coloring, button, clickable link, etc. (info "(elisp) Text Properties")

if you are doing text processing, usually you don't need text properties.

The buffer-substring-no-properties function just return a plain string without these properties.

;; get the string from buffer
(setq myStr (buffer-substring-no-properties startPos endPos))

Note: most function that take string as argument can also accept a string that has properties. The function simply ignore the properties.

(info "(elisp) Buffer Contents")

Get Current Word or Line

Here's how to get the word under cursor, or the current {line, sentence, URL, file name, …}.

;; 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 “symbol”. Symbol is current major mode's concept of a language's word unit. Usually includes underscore, may include hyphens or other
(setq myStr (thing-at-point 'symbol))

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

Note: emacs's concept of “symbol” is like a “identifier”. It usually includes a to z, 0 to 9, underscore “_”, and sometimes hyphen “-”. The exact charset of “symbol” depends on current major mode's Syntax Table. (info "(elisp) Syntax Class Table")

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”.

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.

If a region is active (a phrase), lookup that phrase."
  (interactive)
  (let (myWord myUrl)
    (setq myWord
          (if (use-region-p)
              (buffer-substring-no-properties (region-beginning) (region-end))
            (thing-at-point 'symbol)))
    (setq myUrl
          (concat "http://us.php.net/" myWord))
    (browse-url myUrl)))

〔➤ Emacs: Command to Lookup Reference: {Dictionary, Wikipedia, Google, PHP, Perl, …}

(info "(elisp) Buffer Contents")

For more about thing-at-point, see: Emacs Lisp: Using thing-at-point.

Get Boundary Positions of a Text Unit

Sometimes, you need to not just grab current word, but do other things such as delete the word. You need to know the beginning and ending positions of the region you are interested.

Use bounds-of-thing-at-point. For example, see: Emacs Lisp: Using thing-at-point.

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 skip-chars-forward. In the following example, the p1 is set to the position of the double quote to the left of cursor (the first char to the right of the quote). Similarly, for p2 to the right of cursor.

(defun xah-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 ^). However, note that this code does not consider nested matching pairs.

Text Selection, Region, Mark, Copy, Paste, Kill Ring

Concept of Mark, Region, Active Region, transient-mark-mode

Emacs: What's Region, Active Region, transient-mark-mode?

How to {Cut,Copy,Paste} {to,from} kill-ring?

See: Emacs Lisp Idioms: How to Cut/Copy/Paste to/from kill-ring?

Working on Region

How to make a command work on region?

Let your function have 2 parameters, then use (interactive "r"), then the parameters will be filled with beginning & ending positions of the region. Example:

(defun dosomething-region (p1 p2)
  "Prints region starting and ending positions."
  (interactive "r")
  (message "Region starts: %d, end at: %d" p1 p2)
)

Working on Active Region or Current Word/Paragraph/Buffer

Often you want a command that works on the current word (or line, paragraph), 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 (use-region-p)
     (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)
  ))

Starting with emacs 23, many commands will automatically act on text selection if there's one. 〔➤ New Features in Emacs 23〕 So, it's good that your own text processing commands also behave that way.

{get position, move to} {beginning, end} of Line, move to {previous, next} Line

See: Emacs Lisp: Functions for Processing Lines.

Prompting User for Input

See: Emacs Lisp Idioms: Prompting for User Input.

Processing String in Temp Buffer

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, and you need to do more than just getting substring or number of chars, put it in a temp buffer. Here's a example:

;; process string in a temp buffer

(setq myStr "some big string here you need to process")
(setq myStrNew
      (with-temp-buffer
        (insert myStr)

        ;; code to manipulate your string as buffer text
        ;; 
        (buffer-string) ; get result
        ))

Find/Replace Text

Find/Replace string is one of the most important method in text processing. See: Emacs Lisp: How to Find/Replace Text

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))
)
Like it?
Buy Xah Emacs Tutorial
or share
blog comments powered by Disqus