xah lee ([info]xah_lee) wrote in [info]emacs,
@ 2008-06-14 06:36:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Here's a short tutorial i wrote up today that is a basic collection of Emacs Lisp Idioms.

The HTML version with links, syntax coloring, is at: http://xahlee.org/emacs/elisp_idioms.html

The text version follows. Comments, corrections, suggestions welcome.

(line endings in the following are eaten up by lj)


----------------------------- This page collects some basic emacs lisp programing patterns related to text processing.

This page is grouped into 2 sections: Interactive and Batch. The Interactive section focuses on idioms of writing the type of commands you call when actively editing. For example, looking up the word under cursor in google search, replace certain words in current region, insert XML template, rename a given function in the current programing project, etc. The Batch section focuses on batch style text processing, typically the type of tasks one would do in unix shell tools or Perl. For example, find and replace on a list of given files or dir, run XML validation on a bunch of files.

The idioms on this page contains only very basic ones, suitable for beginning elisp programer.

You should first be familiar with basic emacs functions that get cursor position, move cursor, search text, inserting and deleting text. See Elisp Common Functions Reference.

------------------------------ Interactive Command Idioms

Grabbing Text

Grab the text of given beginning position and ending position.

; 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 strings that has properties.

Reference: Elisp Manual: Buffer-Contents.

------------------------------

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. (setq myStr (thing-at-point 'word)) ; grab the current word (setq myStr (thing-at-point 'symbol)) ; grab the current word with hyphens or underscore (setq line (thing-at-point 'line)) ; grab the current line

; grab the start and end positions of a line (or any other thing) (setq myBoundaries (bounds-of-thing-at-point 'line)) 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.

Reference: Elisp Manual: Syntax-Tables.

------------------------------ Here's a example of php reference lookup command that grabs by “symbol”.

(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))) Reference: Elisp Manual: Buffer-Contents.

------------------------------ Grab Between Matching Pairs

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

(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) ) )

------------------------------ Acting on Region

Idiom for a command that works on the current region.

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

(defun remove-hard-wrap-region (start end) "Replace newline chars in region by single spaces." (interactive "r") (let ((fill-column 90002000)) (fill-region start end)))

------------------------------ Idiom for acting on the region, if there's one, else, on the current word or thing.

(defun down-case-word-or-region () "Make current word or region into lower case." (interactive) (let (pos1 pos2) (if (and transient-mark-mode mark-active) (setq pos1 (region-beginning) pos2 (region-end)) (setq pos1 (car (bounds-of-thing-at-point 'word)) pos2 (cdr (bounds-of-thing-at-point 'word)))) (downcase-region pos1 pos2) ) )

------------------------------ Prompting and Getting Input

Idiom for promping user for input as the argument to your command.

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

“(interactive)” makes the function available thru interactive use, where user can call it with execute-extended-command (M-x).

“(interactive "s")” will prompt the user to enter a argument, taken as string, as argument to the function.

“(interactive "n")” will prompt the user to enter a argument, taken as number, as argument to the function.

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) )

Reference: Elisp Manual: Defining-Commands.

------------------------------ Batch Style Text Processing Idioms

Open a file, process it, save, close it

; open a file, process it, save, close it (defun my-process-file (fpath) "process the file at fullpath fpath ..." (let (mybuffer) (setq mybuffer (find-file fpath)) (goto-char (point-min)) ;; in case buffer already open ;; do something (save-buffer) (kill-buffer mybuffer)))

For processing hundreds of files, you don't need emacs to keep undo info or fontification. This is more efficient:

(defun my-process-file (fpath) "process the file at fullpath fpath ..." (let () ;; create temp buffer without undo record. first space is necessary (set-buffer (get-buffer-create " myTemp")) (insert-file-contents filePath nil nil nil t) ;; process it ... (kill-buffer " myTemp")))

------------------------------ Find Replace strings:

; idiom for string replacement in current buffer; ; use search-forward-regexp if you need regexp (goto-char (point-min)) (while (search-forward "myStr1" nil t) (replace-match "myReplaceStr2")) (goto-char (point-min)) (while (search-forward "myStr2" nil t) (replace-match "myReplaceStr2")) ;; repeat for other strings ...

Calling a shell command.

; idiom for calling a shell command (shell-command "cp /somepath/myfile.txt /somepath")

; idiom for calling a shell command and get its output (shell-command-to-string "ls") Both shell-command and shell-command-to-string will wait for the shell process to finish before continuing. To not wait, use start-process or start-process-shell-command.

Reference: Elisp Manual: Asynchronous-Processes.

------------------------------ Traverse a directory recursively.

In the following, my-process-file is a function that takes a file full path as input. The find-lisp-find-files will generate a list of full paths, using a regex on file name. The “mapc” will apply the function to elements in a list.

; idiom for traversing a directory (require 'find-lisp) (mapc 'my-process-file (find-lisp-find-files "~/web/emacs/" "\\.html$"))

------------------------------

Xah ∑ http://xahlee.org/




Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…