Emacs Lisp: How to Write Commands

By Xah Lee. Date: . Last updated: .

Here's the basics of how to write a emacs command that user can call.

Command Template

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

(defun my-command ()
  "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."
  (let (var1 var2 …)
    (setq var1 …)
    (setq var2 …)
    ;; do something …

In your doc string, if you want clickable URL, or clickable reference to other commands, etc., See: Emacs Lisp: Doc String Markup.

Basic Functions

Get Buffer Text as String

Grab Text of Given Begin / End Positions

Here's how to get a buffer region as string.

;; return string between position 3 to 99
(buffer-substring-no-properties 3 99)

(info "(elisp) Buffer Contents")

Get Current Word

;; return the word under cursor
;; may include underscore or hyphen. word here means lang identifier/symbol
;; exactly what characters is “symbol” is defined by syntax table
;; return the word cursor is on, usually not including hyphen or underscore
(current-word t t)
;; exactly what characters is word is defined by syntax table

Here's a example of command that lookup word on the web of current word.

(require 'browse-url) ; part of gnu emacs

(defun my-lookup-wikipedia ()
  "Look up the word under cursor in Wikipedia.
If there is a text selection (a phrase), use that.

This command switches to browser."
  (let (word)
    (setq word
          (if (use-region-p)
              (buffer-substring-no-properties (region-beginning) (region-end))
    (setq word (replace-regexp-in-string " " "_" word))
    (browse-url (concat "http://en.wikipedia.org/wiki/" word))
    ;; (eww myUrl) ; emacs's own browser

〔►see Emacs: Lookup Google, Dictionary, Documentation

Get Current Line

;; return current line as string
(buffer-substring-no-properties (line-beginning-position) (line-end-position) )

Get Thing at Point

thing-at-point is a way to get the “thing” under cursor.

The thing can be {word, symbol, line, sentence, URL, file name, …}.

;; grab a “thing” at point. The “thing” is text unit. It can be 'word 'symbol 'list 'sexp 'defun 'filename 'url 'email 'sentence 'whitespace 'line 'number 'page

;; grab the current filename
(setq str (thing-at-point 'filename))

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 my-select-inside-quotes ()
  "Select text between double straight quotes
on each side of cursor."
  (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)))

More examples: Emacs: Select Line, between Quotes, Extend Selection

(info "(elisp) Buffer Contents")

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?

{Cut, Copy, Paste}, kill-ring

Emacs Lisp: 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 begin/end positions of the region. Example:

(defun my-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."
(let (pos1 pos2 bds)
  (if (use-region-p)
     (setq pos1 (region-beginning) pos2 (region-end))
      (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. 〔►see Emacs 23.1 New Features (released 2009-07)〕 So, it's good that your own text processing commands also behave that way.

Beginning of Line, End of Line, Move to Previous Line, Next Line

Emacs Lisp: Functions to Process Lines.

Prompting User for Input

See: Emacs Lisp: Get 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 str "some big string here you need to process")
(setq strNew
        (insert str)

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

Find Replace Text of a Buffer

Emacs Lisp: Find Replace String in Buffer

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."
  (require 'dired)
  (mapc 'myProcessFile (dired-get-marked-files))
Like it? Buy Xah Emacs Tutorial. Thanks.