Being Productive With Emacs
http://stuff.mit.edu/iap/2009/emacs/
Phil Sung
MIT, CSAIL; psung@alum.mit.edu
Last time
- A boatload of editing commands
- Macros
- Major and minor modes
- The help key: C-h C-h
Today
- Elisp basics
- Customizing Emacs
- Modifying Emacs
- Writing new commands
Why Elisp?
- Macros only repeat canned key sequences
- Sometimes you need
- Control flow
- Calculations
- User interactions
- Additional features
- Maintainability
What is elisp used for?
- Customizations
- Almost any aspect of the editor can be customized
- Extending Emacs
- Tweak existing functionality or replace it with your own
- Implementing Emacs itself
- Learn by example — you can see how any part of Emacs is
implemented
Example: setting a variable
- (setq show-trailing-whitespace t)
Running elisp
- Evaluate an expression with M-x eval-expression
or M-:
- Or put it in the *scratch* buffer
- C-M-x to evaluate an expression
- Use the *scratch* buffer to experiment with elisp code
- Alternatively, do M-x emacs-lisp-mode in any buffer you
like
The init file
- Elisp code here gets run on each startup
- Open it with C-x C-f ~/.emacs
Example: variables and functions
- (setq undo-limit 100000)
- (setq fill-column 80)
- (column-number-mode t)
- (menu-bar-mode nil)
- Learn about a variable or function with C-h v or C-h
f
- How to discover variables/functions? Try the manual first
Key bindings
- Every command has a long name
- Some commands have keybindings
- Emacs looks in a keymap to find the binding for any key
that you press
Example: rebinding keys
- (global-set-key "\M-o" 'find-file)
- (global-set-key [f2]
'split-window-horizontally)
- (global-set-key "\C-ct"
'toggle-truncate-lines)
- (global-set-key "\C-c\C-t"
'toggle-truncate-lines)
Example: rebinding keys
- Keybindings can also be specific to a mode or a feature
- (define-key text-mode-map "\C-cp"
'backward-paragraph)
Key binding conventions
- Reserved for users: C-c [letter]
- Reserved for major and minor modes:
- C-c C-[anything]
- C-c [punctuation]
- C-c [digit]
Hooks
- Well-defined places for you to insert your own functionality
- "When this happens, do the following..."
Example: hooks
- General formula:
(add-hook 'name-of-hook
'(lambda () (thing-1) (thing-2)...))
- (add-hook 'html-mode-hook
'(lambda () (flyspell-mode 1)))
- (add-hook 'java-mode-hook
'(lambda () (setq tab-width 4)))
- (add-hook 'vc-checkin-hook
'email-my-urop-student)
Hooks
- Every major mode has a hook that is run upon entry
- For more, do M-x apropos-variable and search for "hook"
- Examples of things you can do with hooks:
- "Before checking in a file, make sure it compiles"
- "When I save a file, if it's a shell script, make it executable"
What's with all the parentheses?
- Two kinds of expressions in lisp
- Atoms:
- Compounds:
- (setq some-variable 5)
- (foo-mode t)
- (+ 3 8)
- (+ 3 (* 8 20))
- General form: (operator operand operand ...)
Evaluating expressions
- Get or set variables
- (setq inhibit-startup-message t)
- inhibit-startup-message
- Perform some computation
- (+ 1 2)
- (menu-bar-mode nil)
- Define a function for later use
Control flow in elisp
- (if
TEST
ONE-THING
ANOTHER-THING)
- (while
TEST
DO-THIS
THEN-THAT
...)
- e.g. (if (< 4 10) 51 52)
Local variables
- (let ((a new-value) (b another-value))
(do-something)
(do-something-else))
Functions
- (defun function-name (param1 param2)
"Description of function"
(do-this)
(do-that))
- Invoke like this:
(function-name
arg1 arg2)
- Value of last expression is the "return value"
- You can redefine almost any existing function
Example: square
- (defun square (x)
(message (* x x)))
- (square 5) prints "25"
Commands vs. functions
- Every command is a function
- M-x forward-char, C-f are the same
as (forward-char)
- See how to invoke a function by reading its documentation
- Any command you invoke with M-x or a keybinding can be invoked
programmatically in elisp, too
Commands vs. functions
- Not every function is a command
- e.g. (square 5) => 25
- M-x square => ???
- Functions need arguments! We use interactive specifications to
tell Emacs where those arguments should come from
Interactive specifications
- (defun forward-three-chars ()
(forward-char)
(forward-char)
(forward-char))
- Invoke with M-: (forward-three-chars)
Interactive specifications
- (defun forward-three-chars ()
(interactive)
(forward-char)
(forward-char)
(forward-char))
- Invoke with M-: (forward-three-chars) or M-x
forward-three-chars
Interactive specifications
- (defun delete-file (filename)
(...))
- Invoke with M-: (delete-file "/foo/bar")
Interactive specifications
- (defun delete-file (filename)
(interactive "fDelete file: ")
(...))
- Invoke with M-: (delete-file "/foo/bar") or M-x
delete-file RET /foo/bar RET
- Lots more interactive specifications...
- Reduces duplication of code
Manipulating Emacs from elisp
- Remember: every command is a function
- Useful functions:
- (point)
- (goto-char N)
- (point-min), (point-max)
- (message STR)
- General strategy: find commands that would have had desired result;
replace them with elisp function calls.
Example: count-words
- Strategy
- Go to beginning of buffer.
- Find words one by one until we reach end of buffer.
- Print the total number of words we found.
Example: count-words
(defun count-words ()
"Print the number of words in the buffer."
(interactive)
(let ((count 0))
(goto-char (point-min))
(while (???)
(setq count (1+ count)))
(message "Buffer contains %d words" count)))
Example: count-words
(defun count-words ()
"Print the number of words in the buffer."
(interactive)
(let ((count 0))
(goto-char (point-min))
(while (and (< (point) (point-max))
(re-search-forward "\\w+\\W*" (point-max) t))
(setq count (1+ count)))
(message "Buffer contains %d words" count)))
Example: count-words
- What's wrong with this function?
Example: count-words
- save-excursion restores point to where it was before we started
(defun count-words ()
"Print the number of words in the buffer."
(interactive)
(save-excursion
(let ((count 0))
(goto-char (point-min))
(while (and (< (point) (point-max))
(re-search-forward "\\w+\\W*" (point-max) t))
(setq count (1+ count)))
(message "Buffer contains %d words" count))))
Homework
- Write a function count-words-region
- Hint: read up on interactive specifications
Manipulating text with elisp
- Locating the cursor: point, point-min, point-max, bobp, eobp, bolp, eolp,
current-column
- Moving around in text: goto-char, all your favorite keyboard commands,
save-excursion
- Reading text: char-after, char-before, buffer-substring,
thing-at-point
- Searching: search-forward, re-search-forward
- Modifying text: insert, insert-buffer, newline, delete-region
Finding the right functions
- Many functions are only intended to be called interactively
- M-< or beginning-of-buffer sets the mark and prints a message
- Use (goto-char (point-min)) instead
- Function docs frequently contain warnings about lisp usage
Recap
- Setting variables
- Elisp basics and syntax
- Rebinding keys
- Hooks
- Writing a new function
Next steps
- Info documentation: C-h i
- Emacs lisp intro
- Emacs lisp reference
- Emacs wiki: http://www.emacswiki.org/