Tool Time Session 2: Emacs Customization with Lisp
Table of Contents
- 1. Metadata
- 2. What's About to Happen?
- 3. Keystrokes, Commands, Lisp Code
- 4. Nature of Customizations
- 5. Basic and Recommended Customizations
- 6. Dynamically Executing Code
- 7. Customizing Key Bindings
- 8. Aside: What Command did I run?
- 9. Mode Keybindings and Mode Hooks
- 10. Package System
- 11. Changing Colors and Fonts
- 12. The Customization System
- 13. Emacs Lisp
- 14. Demo of Defining some Handy Functions
1 Metadata
Session Synopsis: Most of the power in Emacs derives from a built-in Lisp interpreter. Nearly every aspect of Emacs can be customized through writing bits of Lisp code and this session will cover how. As a by-product, participants will gain some basics of Lisp, a language still relevant 50+ years into its life.
- Tooltime Website: http://z.umn.edu/tooltime
- Video Recording of Session 2: https://youtu.be/7ReBnH0MalQ
- Code pack associated with the talk: 02-emacs-customization-code.zip
- Org Files used to generate this page: 02-emacs-customization.org web-header.org
2 What's About to Happen?
- We'll talk about the Emacs text editor
- Focus on some of the customization aspects that are useful to get in your pocket early and build on over time
- Try to surmount the difficult of getting acquainted with a very old but still relevant piece of software
2.1 Thank Yous
- Joe Finnegan: for making the recordings possible and enshrining all my mistakes permanently the in the clogged tubes of the Internet
- Computer Science Dept: for supporting and advertising the series
- Institute of Mathematics and its Applications: for lending us Keller 3-180 to do this session
- Students Past and Present: for showing interest in these tools, pestering me to show them how they work, and showing up today
3 Keystrokes, Commands, Lisp Code
- In previous session, determined that things happen in Emacs by running commands of various kinds
- Some commands have a Key Binding associated with them
- Others are interactive allowing
M-x command
to be used - Ultimately all of them are some sort of Lisp function that can be executed by asking Emacs to call the func
- Keystroke
M-:
calls up a prompt for raw Lisp code - Below table shows 3 ways to run some common commands
Keys | Interactive Command | Emacs Lisp Function Evaluation |
---|---|---|
C-f | M-x forward-char | M-: (forward-char) |
C-g | M-x keyboard-quit | M-: (keyboard-quit) |
C-x C-f | M-x find-file | M-: (find-file "~/.emacs") |
C-x C-s | M-x save-buffer | M-: (save-buffer) |
C-x C-v | M-x find-alternate-file | M-: (find-alternate-file "~/.bashrc") |
C-x b | M-x switch-to-buffer | M-: (switch-to-buffer ".bashrc") |
C-x C-b | M-x buffer-menu | M-: (buffer-menu) |
4 Nature of Customizations
- Interacting with Emacs means running commands
- Changing the appearance of Emacs will also mean running commands
- Unlike interactive editing, often want Customizations run once at startup
- Makes sense to Save customizations in a file
- Basic customizations (key bindings, etc.) are usually put in the
file
~/.emacs
Many old-school Unix programs follow this pattern: custom settings stored in a file in the users home directory, usually starting with a dot to make it a Unix "hidden" file
Program Settings File In Home Dir Emacs .emacs Bash Shell .bashrc Vim .vimrc Top .toprc - The "rc" associated with many files is short for "run commands" as in "run these commands on startup"
- For Emacs, the 'run commands' are Lisp code to run at startup
5 Basic and Recommended Customizations
- Default functions to allow buffer/file selection are limited, don't provide robust auto-completion
ido-mode
enables better auto-completionEvery time one starts Emacs, could type
M-x ido-mode
OR one could add the Lisp code to
.emacs
(ido-mode 1) ; turns on ido-mode
- Try this and restart Emacs: ido-mode will be active at startup
- A number of other global mode customizations are worth exploring
;; simple .emacs file with some basic mode customizations (ido-mode 1) ;; better auto completion for files/buffers (blink-cursor-mode 0) ;; disable blinking cursor to prevent mental trauma (show-paren-mode 1) ;; shows matching parens or failure to match (global-auto-revert-mode 1) ;; reload files that change on disk (column-number-mode 1) ;; show column numbers in mode line (global-hl-line-mode 1) ;; change the color of the line the cursor is on (setq-default truncate-lines t) ;; default to NOT wrapping long lines in display (tool-bar-mode 0) ;; hide the tool bar (menu-bar-mode 0) ;; hide the "File Edit..." menu (scroll-bar-mode -1) ;; hide the scroll bars (setq inhibit-startup-screen t) ;; don't show the "splash" screen (setq initial-scratch-message ;; put this text in the scratch buffer ";; Scratch that!\n")
6 Dynamically Executing Code
- Emacs has a built-in Lisp interpreter
- After making changes to
.emacs
, Can restart… - OR can just execute the code/file
In an Emacs Lisp buffer (
.emacs
or*scratch*
), several keys/commands for executing LispKeys Effect C-M-x Execute Lisp code under cursor C-x C-e Execute Lisp expression left of the cursor M-x eval-buffer Evaluate all Lisp code in the buffer M-x load-file Load and execute a Lisp file from disk - Note that Emacs provides code execution for other interpreters as well: send a python function/buffer to the Python interpreter, send a Clojure function definition to a running session, etc.
- Worth exploring for those interactive environments
7 Customizing Key Bindings
- Emacs allows keys to be bound to run arbitrary commands, highly configurable
Two common interactive functions for this
Keys Effect M-x global-set-key Bind a key binding for all of Emacs M-x local-set-key Bind a key for only the current buffer - Using these will prompt for a key sequence then an interactive Lisp function (command) to run
- Will look later at Standard vs Interactive Lisp functions
- lorem ipus abaasldf jasdkf jasdf jkasdf jasdf jkasdf jj asdf jkkasdf jjkkasdf jjasdf jjkkasdf jasdf asdf jasdkf asdf jaksdjf kasdf asdf asdf kk asdfj asdf jasdf
Some of my favorite global key bindings
(global-set-key "\M-o" 'other-window) ; change quickly between windows, default C-x o (global-set-key "\C-cc" (quote compile)) ; compile in buffers directory, usually via 'make' (global-set-key "\C-cl" 'auto-fill-mode) ; toggle whether emacs wraps/newlines text or not (global-set-key "\C-cp" 'replace-string) ; query for find/replace and replace all (global-set-key "\M-j" 'join-line) ; join current line with line above (global-set-key "\C-xg" 'magit-status) ; emacs interface to git called 'Magit' (global-set-key "\C-ck" 'browse-kill-ring) ; examine the "cut" text newest to oldest (global-set-key "\C-cv" 'find-file-at-point) ; treat text at point as a file name and open it (global-set-key "\C-cd" 'set-comment-column) ; set how for righ-hand comments appear, use C-; to introduce comments
- Note use of quotes above: this is an important part of Lisp, allows symbols to be used, will discuss some later, but here the symbols are for function names to run when keys are pressed
Emacs has several ways to express keystrokes that
global-set-key
understands(global-set-key "\C-cc" 'compile) ; standard, old-school (global-set-key "c" 'compile) ; includes non-printing ctrl-C char which may not show up in web browsers (global-set-key (kbd "C-c c") 'compile) ; kbd converts to internal syntax
8 Aside: What Command did I run?
Sometimes know an interactive version of a command
M-x ido-mode
Don't know the right way to call the Lisp function
(ido-mode ???)
Commonly the case for setting key bindings
M-x global-set-key ... (global-set-key ???)
Some key bindings are difficult to describe
Trick: run the interactive command then use
Keys Effect C-x Esc Esc Rerun last command, shows lisp code for it - Will show last Lisp code evaluated in the minibuffer
- Very useful for figuring out key bindings
9 Mode Keybindings and Mode Hooks
- Major Modes like
c-mode
,python-mode
,shell-mode
, etc. will bind keys specially for the tasks they address General Conventions for Key Bindings:
Keys Notes key
Self-insertion, movement, modes customize, users usually don't C-key / M-key
Used for movement, editing, modes customize, users do less often C-x key
are global-ish, buffer/window/file control, modes/users don't often customize C-c key
are specific to major modes, most likely to customize by users and major modes - Documentation for major modes (via
C-h m
) lists their most significant keybindings - Users often want tweaks for key bindings, mode
variables, etc. just for that mode
- Kauffman likes to hide some files in directories
but show them when pressing 'h' (
dired-mode
) - Kauffman prefers 2 spaces of indentation in C code,
use
//
for comments in C code (c-mode
)
- Kauffman likes to hide some files in directories
but show them when pressing 'h' (
- Emacs provides Mode Hooks, a place to hang code that will be run whenever a major mode starts
- Some sample short mode hooks
(require 'dired-x) ; require additional functionality for dired (add-hook 'dired-mode-hook ; when running dired-mode to edit directories... (lambda () ; run the following 0-arg function which... (setq dired-omit-files ; sets a regexp for which files to hide "^\\.?#\\|^\\.$\\|^\\.\\.$\\|^\\.[a-zA-Z]") (dired-omit-mode 1) ; initially hide uninteresting files (local-set-key "h" 'dired-omit-mode) ; toggle hiding 'uninteresting' files ... ; and some other stuff )) (add-hook 'c-mode-common-hook ; when starting a c-mode or derived... (lambda () ; run this 0-arg function which... (setq comment-start "// ") ; use // for comment syntax (setq comment-end "") ; no ending required (setq c-basic-offset 2))) ; 2 spaces for indentation
- Keep in mind mode hooks run when the mode initially starts; changes to mode hooks mean that you must reload a file which will restart the mode and re-run the hooks
Keep in mind
add-hook
adds hooks:(add-hook 'dired-mode-hook (lambda () (local-set-key "h" 'help))) ; second function to run (add-hook 'dired-mode-hook (lambda () (local-set-key "h" 'dired-omit-mode))) ; first function to run
CS folks can probably guess why the order of the two functions above is as indicated (What is the "Lis" in "Lisp"?)
Hook variables store a singly linked list which adds things (
cons
) onto the front. The most recently added function in the hook is run first followed by earlier functions.Hooks are stored in variables and an be reset via
(setq dired-mode-hook '()) ; sets list of hooks to empty
10 Package System
- Emacs has an extensive set of packages that can be installed to augment its built-in features
- This like typical package managers in Linux like APT,
RPM, and Pacman
- "Emacs is a great operating system, lacking only a decent text editor."
Note: Default GNU server that lists packages is somewhat limited, strongly suggest following code which will query another widely used MELPA package server:
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t) (package-initialize)
Package manager basics are as follows
Keys Effect M-x list-packages Opens a buffer listing packages available/installed n,p Next / previous lines C-s Standard interactive search forward Return Show more package info i Mark server package for installation d Mark installed package for deletion x Execute install/deletions - Demo installation of the
nyan-mode
which is pure fun - Of course, after installing packages, one has to know
how to use them, e.g. add
(nyan-mode)
to.emacs
- Installation of packages is in the
.emacs.d/
directory where Emacs stores lots of other stuff
11 Changing Colors and Fonts
- Emacs comes with a few scant built-in color themes
- Can explore these via
M-x load-theme
, use TAB to get completion lists For builtin themes, following code in
.emacs
will change colors at startup(load-theme (quote wombat) t)
- A number of other color themes like
solarized
andabyss
exist in the package manager - BUT getting these to work in your init file could prove troublesome
- Color themes are relatively new to Emacs and are still experiencing some growing pains (e.g. "Face" system preceded it, ad hoc color-theme package preceded it, only recently centering)
- For the daring, can define your own themes by copying
and extending existing ones: see
lila-theme.el
(deftheme lila "Created 2018-04-27.") (custom-theme-set-variables 'lila '(ansi-color-names-vector ["black" "red3" "green3" "yellow3" "blue2" "magenta3" "cyan3" "white"])) (custom-theme-set-faces 'lila '(default ((t (:inherit nil :stipple nil :background "#000000" :foreground "#FFFFFF" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight normal :height 90 :width normal :foundry "PfEd" :family "DejaVu Sans Mono")))) '(cursor ((((background light)) (:background "black")) (((background dark)) (:background "white")))) '(fixed-pitch ((t (:family "Monospace")))) '(variable-pitch ((((type w32)) (:foundry "outline" :family "Arial")) (t (:family "Sans Serif")))) '(escape-glyph ((((background dark)) (:foreground "cyan")) (((type pc)) (:foreground "magenta")) (t (:foreground "brown")))) ...
- Might want to say a few words about Faces at this time if time allows
12 The Customization System
- Emacs has a built-in customization system
Try
M-x customize
and browse arroundKeys Effect M-x customize Opens customization subsystem buffer M-x customize-group Select a specific group to customize (e.g. lisp, c, java) M-x customize-variable Customize a variable defined in a package to be something in particular Enter Select something C-c C-c Save for Current session to try out the settings C-x C-s Save the setting into .emacs permanently - Saved customizations are in a special region of
.emacs
13 Emacs Lisp
- Lisp is old, has a long association with AI, BUT is just a good programming language if you can stomach it: "These were your father's parentheses…"
General "syntax" is
(operator arg1 arg2 arg3)
- Operator may be a normal function name like
insert
or a special form likeif
ordefun
- Takes some getting used to but once you do, it feels REALLY good
- MIT and UMN used to teach Programming 1 in Scheme, a dialect of Lisp
- Every proficient programmer should have some Lisp in their tool belt
14 Demo of Defining some Handy Functions
A simple interactive function (command) to get us started
(defun hello-world () ; define a func, 0 args "Say hello world in the messages/mini-buffer" ; documentation string (interactive) ; make func interactive (via M-x hello-world) (message "hello world")) ; put a messag ein the mini buffer
- Tend to bounce between writing code, checking what functions key bindings run, consulting function documentation in Emacs, reading manual, Googling for useful functions
- Problem: Start a shell in the directory associated with a buffer
- Otherwise run
*shell*
is not running, start it running - Change to the current directory in it
- Split windows (vertically) so that the shell appears in an adjacent window
- Otherwise run
(defun shell-jump-to-current-directory () "Open *shell* and change to directory of the given buffer. Start's shell if needed. Handles dired-mode specially to look for the 'closest' subdirectory when multiple directories are open." (interactive) (let* ((dir (if (equal major-mode 'dired-mode) ; using dired? (dired-find-directory-at-point) ; dired mode: find closest directory default-directory)) ; other buffer: use default dir for buffer (dir (replace-regexp-in-string " " "\\\\ " dir)) ; escape spaces (cmd (concat "cd " dir))) ; add on the 'cd' (when (one-window-p) ; in there is only one window (split-window-right)) ; split to 2 windows, left/right (if (get-buffer "*shell*") ; check if shell is already active (progn ; yes (true case) (other-window 1) ; switch to other window (switch-to-buffer "*shell*")) ; change to the *shell* buffer (progn ; no (false case) (shell) ; start the shell (sleep-for 0 10))) ; 10 millisec delay to let the shell get started before sending input (hack) (end-of-buffer) ; move point to the end of the shell buffer (insert cmd) ; insert the 'cd' command (comint-send-input))) ; signal the shell that a command has been sent (global-set-key "\C-cj" 'shell-jump-to-current-directory)