init.el for Noah Hoffman (deprecated)
This configuration served me well for many years, but I have transitioned to a much simpler setup (https://github.com/nhoffman/emacs-config). I'm leaving this here for (my own) historical interest
In a fit of literate programming yak-shaving, I implemented my Emacs configuration as an org-mode file. I have also tried to provide a complete-ish description of my environment for anyone interested in starting more or less from scratch.
- The source for this page is available at https://github.com/nhoffman/.emacs.d
- The html-exported version of this page is hosted at http://nhoffman.github.io/.emacs.d
Table of Contents
- 1. Initial setup
- 2. Using and maintaining this configuration
- 3. Startup
- 4. ELPA
- 5. hydra
- 6. sidestepping customize
- 7. Navigation
- 8. Marking, cursor movement and appearance
- 9. Define a "launcher" keymap with hydra
- 10. which-key
- 11. Function keys.
- 12. General appearance
- 13. Environment
- 14. Exiting and saving
- 15. Platform and display-specific settings
- 16. Scrolling
- 17. Keyboard macros
- 18. ediff
- 19. emacs desktop
- 20. Move lines up and down with arrow keys
- 21. Buffers and windows
- 22. spelling
- 23. pine/alpine
- 24. yasnippet
- 25. LaTeX
- 26. ESS
- 27. markdown-mode
- 28. rmarkdown and polymode
- 29. org-mode
- 30. chrome "edit with emacs"
- 31. Python
- 32. Javascript/json
- 33. Groovy mode
- 34. shell
- 35. scons
- 36. text-mode
- 37. rst-mode
- 38. moinmoin-mode
- 39. web-mode
- 40. tramp
- 41. git/magit
- 42. sql support
- 43. gpg
- 44. Outline minor mode
- 45. Search and replace
- 46. Misc utilities
- 47. elisp-format
- 48. emacsclient
- 49. discover.el
- 50. dired-x
- 51. enable "advanced" commands
- 52. custom-set-variables
- 53. License
1 Initial setup
You can install this configuration by either cloning it directly from my repository, or by forking on GitHub and installing yours (obviously you will want to do the latter if you want to retain your own modifications:
cd ~
git clone https://github.com/nhoffman/.emacs.d.git
After cloning the repository into your home directory, just a bit of setup is required before first use.
1.1 set up a shell environment
This configuration provides some commands (tested with bash and zsh)
that are useful for using Emacs from the shell. In particular, if you
are on a mac and have installed Emacs for OS X or compiled the Cocoa
version, Emacs will be installed to
/Applications/Emacs.app/Contents/MacOS/Emacs
and emacsclient
is
found in
/Applications/Emacs.app/Contents/MacOS/bin/emacsclient
. These are
aliased to emacs
and emacsclient
, respectively.
In addition, there are some shell commands that simplify running and using Emacs in server mode:
- edaemon
- launch the Emacs server daemon, removing any locked desktop files.
- ec
- attach to the Emacs server in GUI mode (
emacsclient -c
) in the background. - enw
- attach to the Emacs server in terminal mode in place (
emacsclient -nw
). - e
- open a file in an already open window (
emacsclient -n
).
Rather than copying the functions defined in init.bash
elsewhere,
I'd recommend sourcing it instead. For example, just place the
following in your ~/.bash_login
or ~/.zshrc
or whatever (depending
on your shell):
if [[ -f ~/.emacs.d/init.bash ]]; then source ~/.emacs.d/init.bash fi
Here's what this file looks like
if [[ $(uname) == 'Darwin' ]]; then EMACS=/Applications/Emacs.app/Contents/MacOS/Emacs EMACSCLIENT=/Applications/Emacs.app/Contents/MacOS/bin/emacsclient alias emacs="$EMACS" alias emacsclient="$EMACSCLIENT" else EMACS=emacs EMACSCLIENT=emacsclient fi emacs-venv-activate(){ local venv=~/.emacs.d/emacs-env if [[ -f "$venv/bin/activate" && -z "$VIRTUAL_ENV" ]]; then echo "using virtualenv in $venv" source "$venv/bin/activate" else echo "no virtualenv" fi } edaemon(){ rm -f ~/.emacs.desktop.lock ~/.emacs.d/.emacs.desktop.lock (cd ~ && "$EMACS" --daemon) } ec(){ "$EMACSCLIENT" -c "$@" & } enw(){ "$EMACSCLIENT" -nw "$@" } e(){ # open file in an existing server process re='^[0-9]+$' if [[ $2 =~ $re ]]; then "$EMACSCLIENT" -n +$2:0 $1 else "$EMACSCLIENT" -n "$@" fi } # if [[ $(basename $SHELL) == "zsh" ]]; then # compdef ec=ls # compdef enw=ls # compdef e=ls # fi
You will have to open a new terminal window for the shell commands above to become available. Once they are, you can launch the graphical version of emacs using:
emacs -c &
or the terminal version using
emacs -nw
A bit of explanation about Emacs server: the above two commands launch Emacs in an entirely new process. Using the Emacs server, you can run an Emacs server instance in the background and then "attach" either a graphical or terminal window as necessary. Working locally, you would do this by first starting the server:
edaemon
And then opening either a graphical or terminal window, for example:
ec
If you are subsequently working at the command line and you want to
open somefile
in an already-open Emacs window, you can use:
e somefile
(You can of course always open a file from within Emacs using many
mechanisms, eg using C-c f
).
Emacs server is particularly useful when you are running Emacs
remotely on a server and you want to be able to log out and return to
your work later: if you quit the terminal process using C-x C-c
(M-x save-buffers-kill-terminal
), the server continues running in
the background. You can kill the server from within Emacs using M-x
save-buffers-kill-emacs
.
1.2 option as Meta (M-
) on a Mac
I use Emacs from a variety of terminal types on my machines running OS X:
- the Cocoa version when working locally
- X11 when working remotely over a fast connection
- a terminal application when working remotely over a slow connection
I have done my best to configure all three to provide an experience
that's as comparable as possible. Here are some configuration
suggestions to use the option key as Meta (M-
), as opposed to Esc.
1.2.1 Cocoa
I just download it from http://emacsformacosx.com/ - as far as I can tell, option is used as Meta by default.
1.2.2 X11
I use XQuartz
Create the file ~/.Xmodmap as follows to use option as Meta in X11 (you'll need to quit X11 for the changes to take effect):
cat > ~/.Xmodmap <<EOF clear Mod1 clear Mod2 keycode 63 = Mode_switch keycode 66 = Meta_L add Mod1 = Meta_L add Mod2 = Mode_switch EOF
This post has more information on configuring X11.
1.2.3 Terminal
I prefer iTerm2 over Terminal.app
Head over to Preferences –> Profiles –> Keys and do these things:
- select "Left/right option key acts as": +Esc (to use option as Meta)
- + –> Keyboard shortcut "OPT+<left arrow>": Send Escape sequence "b"
- + –> Keyboard shortcut "OPT+<right arrow>": Send Escape sequence "f"
The last two items cause option plus the right and left arrows to
perform the same actions as M-f
(forward-word
) and M-b
(backward-word
) in both Emacs and in contexts that support default
readline key bindings (which is just about everywhere).
1.2.4 What next?
If you are completely new to Emacs, the very first thing to do is to
become acquainted with the built-in help system. You can get to the
help menu by typing <f1>
or C-h ?
.
Next, I'd recommend starting with the built in tutorial by typing C-h
t
.
1.3 install packages from ELPA
The only required step to use this configuration is to install
packages from EPLA, the Emacs Lisp Package Archive. See the "ELPA"
section below for a list of packages installed by this configuration
(defined in my-package-list
). First, launch Emacs; I'd recommend
launching without emacs-desktop, for example emacs -nw
--no-desktop
. Install specified packages with M-x
install-packages
(see the ELPA section below). At this point
it's usually a good idea to quit and relaunch Emacs.
1.4 create virtualenv
The packages used here (particularly elpy
) require some python
bits. The easiest way to provide them is to install them in a
virtualenv. There's a script to do this - just run:
bin/venv.sh
This will create ~/.emacs.d/python2-env
and
~/.emacs.d/python3-env
. See the Python section below for more about
virtualenvs.
1.5 initialize org-export submodule (optional)
If you want to compile init.org
to html using the provided build
script, you'll need to initialize and update the git submodule
containing the org-export
project
(https://github.com/nhoffman/org-export). This only needs to be done
once after checking out this repository:
git submodule update --init
To update the org-export
repository, first try
git submodule update
This will update to whatever commit is associated with the project, eg
git submodule status
197f69ae1f421f4b183d66b5a47dc7d3654ed7e2 org-export (heads/master)
If this doesn't do anything, try
(cd org-export && git checkout org-export && git pull origin master)
If there were any changes, you'll need to make a commit in
.emacs.d
. Ugh, submodules.
2 Using and maintaining this configuration
All changes to the configuration should be made within code blocks in
this file. After any changes, this file must be "tangled" to produce
init.el
. The elisp version of the configuration is committed to the
git repository (even though it is a derived file) to make it easier to
get started when first cloning the repository onto a new system. An
html-exported version of this file is also published to GitHub
pages. All of this is automated using scons
. The default target is
init.el
, so after changing this file, you can compile init.el
by
simply typing
scons
If you'd rather tangle the file interactively, use C-c C-v t
(org-babel-tangle
).
Additional targets include scons html
to compile html/index.html
and scons publish
to update the gh-pages
branch of the repo on
GitHub.
To help keep track of functions I've defined, I like to make aliases that prepend the value of `my-alias-prefix'. Here's a function to help with making aliases.
(defvar my-alias-prefix "my/") (defun make-alias (fun &optional prefix) "Create an alias for function `fun' by prepending the value of `my-alias-prefix' to the symbol name. Use `prefix' to provide an alternative prefix string. Example: (defun bar () (message \"I am bar\")) (make-alias 'bar \"foo-\") (foo-bar) => \"I am bar\"" (interactive) (defalias (intern (concat (or prefix my-alias-prefix) (symbol-name fun))) fun))
I edit this file so frequently, let's make some functions to find, tangle, and load it.
(defvar my/init-org "~/.emacs.d/init.org" "org-mode version of init file") (defvar my/init-el "~/.emacs.d/init.el" "tangled version of `my/init-org'") (defun init-edit () "Edit org-mode version of init file specified by `my/init-org'" (interactive) (find-file my/init-org)) (make-alias 'init-edit) (defun init-load () (interactive) (load my/init-el)) (make-alias 'init-load) (defun init-tangle-and-load () "Tangle `my/init-org' and load the result" (interactive) (init-edit) (org-babel-tangle) (init-load) (switch-to-buffer "*Messages*")) (make-alias 'init-tangle-and-load)
3 Startup
This will only work with emacs >= 24.x
(unless (>= emacs-major-version 24) (error "Emacs version 24 or higher is required"))
(message "loading ~/.emacs.d/init.el")
3.1 Auto-refresh
automatically refresh buffers from disk (default is every 5 sec) see http://www.cs.cmu.edu/cgi-bin/info2www?(emacs)Reverting
(global-auto-revert-mode 1)
3.2 Enable debugging
;; (setq debug-on-error t) ;; (setq debug-on-signal t)
3.3 dir-local variables
I can't explain why, but I started getting errors that a
.dir-locals.el
file high up in the file system could not be found
when opening a new file in emacsclient. This seems to have stopped the
error (conveniently, I don't use this feature):
(setq enable-dir-local-variables nil)
4 ELPA
Set up and initialize ELPA package manager.
Some useful ELPA variables and functions:
M-x package-list-packages |
open list of packages |
package-activated-list |
variable containing list of the names of currently activated packages |
package-install |
install a package |
package-installed-p |
return true if package is installed |
4.1 define repositories
Add some extra package repositories. The default value of package-archives is
(("gnu" . "http://elpa.gnu.org/packages/"))
(when (>= emacs-major-version 24) (require 'package) (setq package-archives '(("ELPA" . "http://tromey.com/elpa/") ("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/") ("melpa-stable" . "http://stable.melpa.org/packages/") ("marmalade" . "http://marmalade-repo.org/packages/") ("org" . "http://orgmode.org/elpa/") ("elpy" . "http://jorgenschaefer.github.io/packages/") )) ;; Check if we're on Emacs 24.4 or newer, if so, use the pinned package feature (when (boundp 'package-pinned-packages) (setq package-pinned-packages '((elpy . "elpy") (flycheck . "melpa-stable") (helm-descbinds . "melpa-stable") (helm-swoop . "melpa-stable") (highlight-indentation . "elpy") ;; fixes error in elpy 1.6 (hydra . "gnu") (magit . "melpa-stable") (markdown-mode . "melpa-stable") (org . "org") (smart-mode-line . "melpa-stable") (swiper . "melpa-stable") (web-mode . "melpa") (which-key . "melpa-stable") ))) (package-initialize))
Starting in emacs 25.1, repositories can be assigned a priority, which can be used to hide packages in low priority repositories also represented in higher-priority repositories.
(setq package-archive-priorities '(("org" . 30) ("elpy" . 30) ("melpa-stable" . 20) ("marmalade" . 10) ("gnu" . 10) ("melpa" . 5))) (setq package-menu-hide-low-priority t)
4.2 use-package
This init file is designed to fail gracefully when packages are not
yet installed. use-package
can automate the installation of missing
packages (when :ensure t
is specified), or provides a warning that a
package is not installed. However, we have a bit of a problem when it
comes to use-package
itself, which must be installed to avoid
failures on startup. Here's the solution that I've arrived at
- when
use-package
is not available, give the user the option of installing it - if yes, do so
- otherwise, define a macro that generates warning messages wherever
use-package
is invoked.
(unless (package-installed-p 'use-package) (if (yes-or-no-p "use-package is not installed yet - install it? ") (progn ;; bootstrap use-package (message "** installing use-package") (package-refresh-contents) (package-install 'use-package)) (message "** defining fake use-package macro") (defmacro use-package (pkg &rest args) (warn "use-package is not installed - could not activate %s" (symbol-name pkg)) )))
4.3 define a list of packages
I could not find an obvious way to define a list of packages to
automatically install, so here are some functions to do so. Execute
M-x install-packages
to install any missing packages. Note that
when installing org-mode from ELPA for the first time, you must be
sure that the builtin version of org-mode has not been loaded since
emacs was first started.
(defun package-installed-not-builtin-p (package &optional min-version) "Return true if PACKAGE, of MIN-VERSION or newer, is installed (ignoring built-in versions). MIN-VERSION should be a version list" (unless package--initialized (error "package.el is not yet initialized!")) (if (< emacs-major-version 4) ;; < emacs 24.4 (let ((pkg-desc (assq package package-alist))) (if pkg-desc (version-list-<= min-version (package-desc-vers (cdr pkg-desc))))) ;; >= emacs 24.4 (let ((pkg-descs (cdr (assq package package-alist)))) (and pkg-descs (version-list-<= min-version (package-desc-version (car pkg-descs))))) )) (defun package-install-list (pkg-list) ;; Install each package in pkg-list if necessary. (mapcar (lambda (pkg) (unless (package-installed-not-builtin-p pkg) (package-install pkg))) pkg-list) (message "done installing packages")) (defvar my-package-list '(auctex ;; csv-mode discover dash-at-point edit-server elpy ess expand-region flycheck gist git-timemachine groovy-mode helm helm-descbinds helm-swoop helm-projectile htmlize hydra jinja2-mode magit markdown-mode ;; moinmoin-mode org org-re-reveal ox-minutes polymode poly-R projectile rainbow-delimiters smart-mode-line swiper visual-regexp visual-regexp-steroids web-mode which-key yaml-mode yasnippet yas-jit)) (defun install-packages () ;; Install packages listed in global 'my-package-list' (interactive) (package-list-packages) (package-install-list my-package-list)) (make-alias 'install-packages)
5 hydra
Hydra is "a package for GNU Emacs that can be used to tie related
commands into a family of short bindings with a common prefix." I
define various hyrdas as entry points to various commands below. For
now, I'll just be sure to test if hydra
is installed each time I
call defhydra
. For example:
(if (require 'hydra nil 'noerror) (progn (message "** hydra is installed")) (message "** hydra is not installed"))
5.1 hydra-toggle-mode
A hydra for toggling modes. Activate via hydra-launcher
using C-\ g
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-toggle-mode (:color blue :columns 4 :post (redraw-display)) "hydra-toggle-mode" ("RET" redraw-display "<quit>") ;; ("c" csv-mode "csv-mode") ("h" html-mode "html-mode") ("j" jinja2-mode "jinja2-mode") ("k" markdown-mode "markdown-mode") ("l" lineum-mode "lineum-mode") ("m" moinmoin-mode "moinmoin-mode") ("o" org-mode "org-mode") ("p" python-mode "python-mode") ("r" R-mode "R-mode") ("s" sql-mode "sql-mode") ("t" text-mode "text-mode") ("v" visual-line-mode "visual-line-mode") ("w" web-mode "web-mode") ("y" yaml-mode "yaml-mode") )) (message "** hydra is not installed"))
6 sidestepping customize
Cribbed from a post on oremacs, this macro provides "a setq that is aware of the custom-set property of a variable."
(defmacro csetq (variable value) `(funcall (or (get ',variable 'custom-set) 'set-default) ',variable ,value))
7 Navigation
7.1 electric-buffer-list
Replace default list-buffers
with electric-buffer-list
for buffer
selection.
(global-set-key (kbd "C-x C-b") 'electric-buffer-list)
7.2 Switch windows with arrow keys
Note that other-window is bound by default to C-x o
(defun back-window () (interactive) (other-window -1)) (global-set-key (kbd "C-<right>") 'other-window) (global-set-key (kbd "C-<left>") 'back-window)
7.3 helm
Helm is a pretty intense change to the default behavior for executing commands, switching buffers, finding files, etc. It takes some getting used to, but woah.
See http://tuhdo.github.io/helm-intro.html for setup suggestions.
Using the configuration below, some hints:
- When in the
helm-M-x
buffer,TAB
shows documentation for the selected command. - As suggested, I've replaced the default behavior of
M-y
to use helm's equivalent, which shows a menu of recently copied regions (rather than cycling through entries of the kill ring after a yank).
(condition-case nil (progn (require 'helm-config) (helm-mode 1) (global-set-key (kbd "M-x") 'helm-M-x) (global-set-key (kbd "C-x C-f") 'helm-find-files) (global-set-key (kbd "M-y") 'helm-show-kill-ring) (global-set-key (kbd "C-c h o") 'helm-occur) (global-set-key (kbd "C-h SPC") 'helm-all-mark-rings) (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) (define-key helm-map (kbd "C-i") 'helm-execute-persistent-action) (define-key helm-map (kbd "C-z") 'helm-select-action) ) (error (message "** could not activate helm")))
7.4 helm-descbinds
Describe key bindings for the current modes: see https://github.com/emacs-helm/helm-descbinds
(condition-case nil (progn (require 'helm-descbinds) (global-set-key (kbd "C-h b") 'helm-descbinds)) (error (message "** could not activate helm-descbinds")))
7.5 hydra for helm
A hydra for activating helm commands that I can't otherwise
remember. Activate via hydra-launcher
using C-\ h
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-helm (:color blue :columns 4 :post (redraw-display)) "hydra-toggle-mode" ("RET" redraw-display "<quit>") ("b" helm-browse-project "helm-browse-project") ("d" helm-descbinds "helm-descbinds") ("f" helm-projectile-find-file "helm-projectile-find-file") ("g" helm-projectile-grep "helm-projectile-grep") ("j" helm-projectile-switch-project "helm-projectile-switch-project") ("o" helm-occur "helm-occur") ("O" helm-org-in-buffer-headings "helm-org-in-buffer-headings") ("p" helm-projectile "helm-projectile") ("s" helm-swoop "helm-swoop") )) (message "** hydra is not installed"))
7.6 projectile and helm-projectile
Project-centric file and directory navigation - see https://github.com/bbatsov/projectile
Installed using ELPA.
Basic key bindings (see the url above for a complete list).
keybinding | description |
C-c p C-h | Help with projectile key bindings |
C-c p f | Display a list of all files in the project. |
C-c p d | Display a list of all directories in the project. |
projectile is integrated with helm by the package helm-projectile. Usage information is here: http://tuhdo.github.io/helm-projectile.html
We'll configure both together
(if (and (package-installed-p 'projectile) (package-installed-p 'helm-projectile)) (progn (projectile-global-mode) (setq projectile-completion-system 'helm) (helm-projectile-on) (if (executable-find "fd") (setq projectile-generic-command "fd . -0")) (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)) (message "** not using projectile or helm-projectile - one or both not installed"))
7.6.1 ignoring directories using grep functions
Native emacs grep functions (like M-x rgrep
) as well as
projectile-grep
and helm-projectile-grep
all ignore directories
specified by the variable grep-find-ignored-directories
. Let's
add some to the defaults.
(when (boundp 'grep-find-ignored-directories) (add-to-list 'grep-find-ignored-directories ".eggs") (add-to-list 'grep-find-ignored-directories "src"))
Make a function to ignore contents of a project's virtualenv, and advise functions using grep to apply it before execution.
(defun grep-ignore-venv-current-project (&rest args) (interactive) (let ((venv (find-venv-current-project))) (if venv (progn (setq venv (file-name-nondirectory (replace-regexp-in-string "/$" "" venv))) (message "adding '%s' to grep-find-ignored-directories" venv) (add-to-list 'grep-find-ignored-directories venv)) (message "no virtualenv at this location") ))) (advice-add 'rgrep :before #'grep-ignore-venv-current-project) (advice-add 'projectile-grep :before #'grep-ignore-venv-current-project) (advice-add 'helm-projectile-grep :before #'grep-ignore-venv-current-project)
7.7 uniquify
Distinguish buffer names for identically-named files.
- http://www.emacswiki.org/emacs/uniquify
- Note that as of 24.4, uniquify is enabled by default, but with
uniquify-buffer-name-style
set topost-forward-angle-brackets
.
(require 'uniquify) (setq uniquify-buffer-name-style 'post-forward)
7.8 swiper
Swiper is an isearch replacement.
(use-package swiper :ensure t :bind (("C-s" . swiper)))
8 Marking, cursor movement and appearance
8.1 avy
https://github.com/abo-abo/avy
Jump to the start of a word in the visible text of any window by choosing the character at the start of the word. Provides a replacement for ace-jump-mode.
(use-package avy :ensure t :bind (("M-'" . avy-goto-word-1)))
8.2 expand-region
Expand region increases the selected region by semantic units. Just keep pressing the key until it selects what you want.
Installed using ELPA.
Expand-region doesn't do what I want inside a quoted string (which is to mark the region within the quotes), so here's a function to do so. It's pretty naive, and doesn't enforce that the quote characters are matching.
(defun my/mark-inside-quotes () "Mark string between two quote charactes (double, single, or grave accent)" (interactive) (unless (re-search-forward "[\"'`]" nil t) (error "No quote character after the cursor")) (backward-char 1) (set-mark (point)) (unless (re-search-backward "[\"'`]" nil t) (error "No matching quote character before the cursor")) (forward-char 1) (exchange-point-and-mark)) (defun my/mark-first-quoted-string () (interactive) (re-search-forward "[\"'`]" nil t) (my/mark-inside-quotes))
(if (require 'hydra nil 'noerror) (defhydra hydra-expand-region (global-map "M-=") "hydra-expand-region" ("=" er/expand-region "er/expand-region") ("-" er/contract-region "er/contract-region") ("q" my/mark-inside-quotes "my/mark-inside-quotes" :color blue) ("Q" my/mark-first-quoted-string "my/mark-first-quoted-string" :color blue)) (message "** hydra is not installed"))
9 Define a "launcher" keymap with hydra
Use a hydra
to define a key map containing a grab-bag of commonly
used functions. Much easier than trying to find unused key
combinations.
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-launcher (:color teal :columns 4 :post (redraw-display)) "hydra-launcher" ("C-g" redraw-display "<quit>") ("RET" redraw-display "<quit>") ("b" copy-buffer-file-name "copy-buffer-file-name") ("d" insert-date "insert-date") ("D" dash-at-point "dash-at-point") ("e" save-buffers-kill-emacs "save-buffers-kill-emacs") ("f" fix-frame "fix-frame") ("g" hydra-toggle-mode/body "toggle mode") ("h" hydra-helm/body "helm commands") ("i" init-edit "init-edit") ("n" my/find-org-index "my/find-org-index") ("N" my/org-index-add-entry "my/org-index-add-entry") ("m" magit-status "magit-status") ("o" hydra-org-navigation/body "hydra-org-navigation") ("O" copy-region-or-line-other-window "copy-region-or-line-other-window") ("p" hydra-python/body "python menu") ("P" list-processes "list-processes") ("s" ssh-refresh "ssh-refresh") ("t" org-todo-list "org-todo-list") ("T" transpose-buffers "transpose-buffers") ("u" untabify "untabify") ("w" hydra-web-mode/body "web-mode commands")) (global-set-key (kbd "C-\\") 'hydra-launcher/body)) (message "** hydra is not installed"))
10 which-key
"Emacs package that displays available keybindings in popup"
A popup appears showing completions for an uncompleted prefix after a
short delay. Eg, type C-x
and pause… a list of available
completions appears, each identified by the bound function!
see https://github.com/justbur/emacs-which-key
(use-package which-key :ensure t :pin melpa-stable ;; this has no effect but I'll leave it here ;; until the apparent bug in use-package is fixed ;; (in the meantime, the repo is pinned using ;; package-pinned-packages) :config (which-key-mode))
11 Function keys.
It's kind of surprising that the function keys aren't either defined or bound to more commonly used functions by default.
key | default binding | also bound to | my binding |
---|---|---|---|
f1 | view-order-manuals | C-h | |
f2 | 2C-command | C-x 6 | fix-frame |
f3 | kmacro-start-macro-or-insert-counter | ||
f4 | kmacro-end-or-call-macro | ||
f5 | call-last-kbd-macro | ||
f6 | lineum-mode | ||
f7 | visual-line-mode | ||
f8 | ns-toggle-fullscreen | ||
f9 | |||
f10 | menu-bar-open | ||
f11 | (OS X: Show Desktop) | ||
f12 | (OS X: Show Dashboard) |
(global-set-key (kbd "<f6>") 'linum-mode) (global-set-key (kbd "<f7>") 'visual-line-mode) (global-set-key (kbd "<f8>") 'flymake-popup-current-error-menu)
(defalias 'dtw 'delete-trailing-whitespace)
12 General appearance
(setq column-number-mode t) (setq inhibit-splash-screen t) (setq require-final-newline t) (setq make-backup-files nil) (setq initial-scratch-message nil) (setq suggest-key-bindings 4) (show-paren-mode 1)
12.1 smart-mode-line
https://github.com/Malabarba/smart-mode-line
(if (require 'smart-mode-line nil 'noerror) (progn (setq sml/no-confirm-load-theme t) (setq sml/theme 'light) (setq sml/name-width 30) ;; (setq sml/mode-width 'full) (setq sml/time-format "%H:%M") (sml/setup)) (message "** smart-mode-line is not installed"))
12.2 title bar
File path in title bar. See http://stackoverflow.com/questions/3669511/the-function-to-show-current-files-full-path-in-mini-buffer
(setq frame-title-format (list (format "%s %%S: %%j " (system-name)) '(buffer-file-name "%f" (dired-directory dired-directory "%b"))))
12.3 Prettier cursor
(set-cursor-color "red")
(blink-cursor-mode 1)
13 Environment
13.1 update load path
Store packages not available via elpa in ~./.emacs.d/elisp
(add-to-list 'load-path "~/.emacs.d/elisp/")
13.2 update SSH_AUTH_SOCK
If you 1) forward ssh authentication (ie, ssh -A), 2) have a long-running emacs –daemon and 3) set an expiration on your ssh authentication, then you will lose the ability to perform ssh public key authentication once the authentication expires. So actions like pushing/pulling using magit will fail. This can be addressed by updating the value of the SSH_AUTH_SOCK environment variable. Here's a function to fix this.
After installing El Capitan, I've had to follow the instructions here
to make ssh-agent work with the version of openssh installed via
Homebrew. Using this scheme, ~/.ssh-auth-sock
stores the value of
SSH_AUTH_SOCK
.
(defun ssh-refresh () "Reset the environment variable SSH_AUTH_SOCK" (interactive) (let (ssh-auth-sock-old (getenv "SSH_AUTH_SOCK")) (setenv "SSH_AUTH_SOCK" (car (split-string (shell-command-to-string (if (eq system-type 'darwin) "cat ~/.ssh-auth-sock" ;; "ls -t $(find /tmp/* -user $USER -name Listeners 2> /dev/null)" "ls -t $(find /tmp/ssh-* -user $USER -name 'agent.*' 2> /dev/null)" ))))) (message (format "SSH_AUTH_SOCK %s --> %s" ssh-auth-sock-old (getenv "SSH_AUTH_SOCK"))))) (make-alias 'ssh-refresh)
13.3 PATH setup
Add paths to 'exec-path' so that Emacs can find executables not otherwise defined in PATH.
(add-to-list 'exec-path "~/.emacs.d/bin")
Also update the $PATH
environment variable inherited by shell
commands run from within Emacs.
(defun prepend-path (path) "Add `path' to the beginning of $PATH unless already present." (interactive) (unless (string-match path (getenv "PATH")) (setenv "PATH" (concat path ":" (getenv "PATH"))))) (prepend-path "~/.emacs.d/bin")
13.4 exec-path-from-shell
Initialize the PATH environment variable when starting up the Emacs app from the finder. Found this tip here: https://plus.google.com/104330705025733851532/posts/K6YPSVEB9Nx
Commenting out for now, but seems promising….
;; (when (memq window-system '(mac ns)) ;; (exec-path-from-shell-initialize))
14 Exiting and saving
Require prompt before exit on C-x C-c
(global-set-key [(control x) (control c)] (function (lambda () (interactive) (cond ((y-or-n-p "Quit? (save-buffers-kill-terminal) ") (save-buffers-kill-terminal))))))
Delete trailing whitespace before save.
(setq delete-trailing-lines nil)
(add-hook 'before-save-hook 'delete-trailing-whitespace)
15 Platform and display-specific settings
Detect platform and window system and set up fonts and other
system-specific settings accordingly. It may be necessary to run M-x
fix-frame
after opening a new frame attached to a running emacs
server process.
(defun set-default-font-verbosely (font-name) (interactive) (message (format "** setting default font to %s" font-name)) (condition-case nil (set-default-font font-name) (error (message (format "** Error: could not set to font %s" font-name))))) (defun fix-frame (&optional frame) "Apply platform-specific settings." (interactive) (menu-bar-mode -1) ;; hide menu bar (tool-bar-mode -1) ;; hide tool bar (scroll-bar-mode -1) ;; hide scroll bar (cond ((string= "ns" window-system) ;; cocoa (progn (message (format "** running %s windowing system" window-system)) ;; key bindings for mac - see ;; http://stuff-things.net/2009/01/06/emacs-on-the-mac/ ;; http://osx.iusethis.com/app/carbonemacspackage (set-keyboard-coding-system 'mac-roman) (setq mac-option-modifier 'meta) (setq mac-command-key-is-meta nil) (set-default-font-verbosely "Menlo-14"))) ((string= "x" window-system) (progn (message (format "** running %s windowing system" window-system)) (set-default-font-verbosely "Liberation Mono-10") ;; M-w or C-w copies to system clipboard ;; see http://www.gnu.org/software/emacs/elisp/html_node/Window-System-Selections.html (setq x-select-enable-clipboard t))) (t (message "** running in terminal mode")))) (global-set-key (kbd "<f2>") 'fix-frame) (make-alias 'fix-frame) (fix-frame)
16 Scrolling
See http://www.emacswiki.org/emacs/SmoothScrolling
(setq mouse-wheel-scroll-amount '(3 ((shift) . 3))) ;; number of lines at a time (setq mouse-wheel-progressive-speed nil) ;; don't accelerate scrolling (setq mouse-wheel-follow-mosue 't) ;; scroll window under mouse (setq scroll-step 1) ;; keyboard scroll one line at a time (setq scroll-conservatively 1) ;; scroll by one line to follow cursor off screen (setq scroll-margin 2) ;; Start scrolling when 2 lines from top/bottom
17 Keyboard macros
See http://www.emacswiki.org/emacs/KeyboardMacros note that default bindings for macros are:
C-x ( | start defining a keyboard macro |
C-x ) | stop defining the keyboard macro |
C-x e | execute the keyboard macro |
Some additional keyboard macro bindings.
(global-set-key (kbd "<f5>") 'call-last-kbd-macro)
18 ediff
Always split windows horizontally.
(csetq ediff-split-window-function 'split-window-horizontally)
19 emacs desktop
References:
- http://www.gnu.org/software/emacs/manual/html_node/emacs/Saving-Emacs-Sessions.html
- http://www.emacswiki.org/emacs/DeskTop
Save desktop periodically instead of just on exit, but not if emacs is
started with --no-desktop
. Note that "–no-desktop" is deleted from
`command-line-args' when desktop is activated, so we have to check
before that.
(defun desktop-save-no-p () "Save desktop without prompting (replaces `desktop-save-in-desktop-dir')" (interactive) ;; (message (format "Saving desktop in %s" desktop-dirname)) (desktop-save desktop-dirname)) (if (member "--no-desktop" command-line-args) (message "** desktop auto-save is disabled") (progn (require 'desktop) (desktop-save-mode 1) (message "** desktop auto-save is enabled") (add-hook 'auto-save-hook 'desktop-save-no-p)))
When the server is running, start with the first buffer with a name not starting with a star or space.
(not working yet, alas!)
;; (defun buffer-list-nostar () ;; (delq nil (mapcar ;; (lambda (buf) ;; (unless (string-match "^[* ]" (buffer-name buf)) buf)) ;; (buffer-list)))) ;; (add-hook 'before-make-frame-hook ;; (lambda () ;; (message "** running 'before-make-frame-hook") ;; ;; (let ((buf (buffer-file-name (car (buffer-list-nostar))))) ;; ;; (print (buffer-list-nostar)) ;; ;; (when buf ;; ;; (setq initial-buffer-choice buf) ;; ;; (message "** setting initial buffer to %s" buf))) ;; (print (buffer-list)) ;; (setq initial-buffer-choice (buffer-file-name (car (delq nil (mapcar ;; (lambda (buf) ;; (unless (string-match "^[* ]" (buffer-name buf)) buf)) ;; (buffer-list)))))) ;; ))
20 Move lines up and down with arrow keys
See http://stackoverflow.com/questions/2423834/move-line-region-up-and-down-in-emacs
Move line up
(defun move-line-up () (interactive) (transpose-lines 1) (previous-line 2)) (global-set-key (kbd "M-<up>") 'move-line-up)
Move line down.
(defun move-line-down () (interactive) (next-line 1) (transpose-lines 1) (previous-line 1)) (global-set-key (kbd "M-<down>") 'move-line-down)
21 Buffers and windows
21.1 Transpose buffers
- see http://www.emacswiki.org/emacs/SwitchingBuffers
- note that original code used function 'plusp', which seems not to be defined in recent versions of emacs
(defun transpose-buffers (arg) "Transpose the buffers shown in two windows." (interactive "p") (let ((selector (if (>= arg 0) 'next-window 'previous-window))) (while (/= arg 0) (let ((this-win (window-buffer)) (next-win (window-buffer (funcall selector)))) (set-window-buffer (selected-window) next-win) (set-window-buffer (funcall selector) this-win) (select-window (funcall selector))) ;; (setq arg (if (plusp arg) (1- arg) (1+ arg))) (setq arg (if (>= arg 0) (1- arg) (1+ arg))) ))) (global-set-key (kbd "C-x 4") 'transpose-buffers)
21.2 Switch buffers between frames
Also from http://www.emacswiki.org/emacs/SwitchingBuffers
(defun switch-buffers-between-frames () "switch-buffers-between-frames switches the buffers between the two last frames" (interactive) (let ((this-frame-buffer nil) (other-frame-buffer nil)) (setq this-frame-buffer (car (frame-parameter nil 'buffer-list))) (other-frame 1) (setq other-frame-buffer (car (frame-parameter nil 'buffer-list))) (switch-to-buffer this-frame-buffer) (other-frame 1) (switch-to-buffer other-frame-buffer))) (global-set-key (kbd "C-x 5") 'switch-buffers-between-frames)
21.3 Toggle frame split
Toggles between a horizontal and vertical split (two frames only).
Copied from http://www.emacswiki.org/emacs/ToggleWindowSplit (submitted by Wilfred).
(defun toggle-frame-split () "If the frame is split vertically, split it horizontally or vice versa. Assumes that the frame is only split into two." (interactive) (unless (= (length (window-list)) 2) (error "Can only toggle a frame split in two")) (let ((split-vertically-p (window-combined-p))) (delete-window) ; closes current window (if split-vertically-p (split-window-horizontally) (split-window-vertically)) ; gives us a split with the other window twice (switch-to-buffer nil))) ; restore the original window in this part of the frame (global-set-key (kbd "C-x 6") 'toggle-frame-split)
21.4 Force horizontal splits
(setq split-height-threshold nil)
22 spelling
Use hunspell or aspell instead of ispell if one is available. For
hunspell, use dictionaries included in ~/.emacs.d/dictionaries
. If a
spell check program is available, enable flyspell-mode
.
(defvar enable-flyspell-p) ;; (setq debug-on-error t) ;; (setq debug-on-signal t) (if (cond ;; ((executable-find "hunspell") ;; (setq ispell-local-dictionary "en_US") ;; (setq ispell-local-dictionary-alist ;; '(("en_US" ;; DICTIONARY-name ;; "[[:alpha:]]" ;; CASECHARS ;; "[^[:alpha:]]" ;; NOT-CASECHARS ;; "[']" ;; OTHERCHARS ;; nil ;; MANY-OTHERCHARS-P ;; ("-d" "~/.emacs.d/dictionaries/en_US") ;; ISPELL-ARGS ;; ;; ("-d" "en_US") ;; ISPELL-ARGS ;; nil ;; EXTENDED-CHARACTER-MODE ;; utf-8 ;; CHARACTER-SET ;; ))) ;; (setenv "DICPATH" "~/.emacs.d/dictionaries") ;; (setq-default ispell-program-name "hunspell") ;; (setq ispell-really-hunspell t)) ((executable-find "aspell") (setq ispell-dictionary "en") (setq ispell-program-name "aspell"))) (progn (message "** using %s for flyspell" ispell-program-name) (autoload 'flyspell-mode "flyspell" "On-the-fly spelling checker." t) (setq flyspell-issue-welcome-flag nil) (setq enable-flyspell-p t)) (setq enable-flyspell-p nil) (message "** could not find hunspell or aspell"))
23 pine/alpine
http://snarfed.org/space/emacs font-lock faces for composing email
(add-hook 'find-file-hooks '(lambda () (if (equal "pico." (substring (buffer-name (current-buffer)) 0 5)) ;; (message "** running hook for pine/alpine") (mail-mode))))
24 yasnippet
Use yasnippet. The after-save-hook
causes all snippets to be
reloaded after saving a snippet file.
(use-package yasnippet :init (progn (add-hook 'after-save-hook (lambda () (when (eql major-mode 'snippet-mode) (yas-reload-all))))) :commands (yas-global-mode) :mode ("\\.yasnippet" . snippet-mode))
25 LaTeX
Install AuxTeX from ELPA.
26 ESS
Installed using ELPA, but seems to need require
to be called
explicitly.
(condition-case nil (require 'ess-site) (error (message "** could not load ESS")))
On systems using "modules", use this function to specify the R interpreter for inferior R processes.
(defun set-inferior-ess-r-program-name () "Set `inferior-ess-r-program-name' as the absolute path to the R interpreter. On systems using 'modules' (http://modules.sourceforge.net/), load the R module before defining the path." (interactive) (setq inferior-ess-r-program-name (replace-regexp-in-string "\n" "" (shell-command-to-string "which ml > /dev/null && (ml R; which R) || which R")))) (make-alias 'set-inferior-ess-r-program-name)
Hooks
(add-hook 'ess-mode-hook '(lambda() (message "Loading ess-mode hooks") ;; leave my underscore key alone! (setq ess-S-assign "_") ;; (ess-toggle-underscore nil) ;; set ESS indentation style ;; choose from GNU, BSD, K&R, CLB, and C++ (ess-set-style 'GNU 'quiet) (if enable-flyspell-p (flyspell-mode)) (set-inferior-ess-r-program-name)))
27 markdown-mode
Installed using ELPA.
Some useful key bindings to keep in mind:
S-TAB | visibility cycling for headings and content |
use-package
fails to catch errors when a package is not installed,
so the use-package
declaration is only evaluated when
markdown-mode
is actually installed.
(if (require 'markdown-mode nil 'noerror) (use-package markdown-mode :commands (markdown-mode gfm-mode) :mode (("README\\.md" . gfm-mode) ("\\.md" . markdown-mode) ("\\.markdown" . markdown-mode)) :bind (:map markdown-mode-map ;; don't redefine =M-<left>= and =M-<right>= in this mode ("M-<right>" . nil) ("M-<left>" . nil)) :init (setq markdown-command "multimarkdown") :config (custom-set-faces '(markdown-code-face ((t (:inherit fixed-pitch :background "lavender")))))) (message "** markdown-mode is not installed"))
28 rmarkdown and polymode
Polymode is what you might expect given the name: it supports multiple major modes in the same buffer depending on context. Install polymode using ELPA, then enable it:
Rmarkdown is a format consisting of markdown containing R code
chunks. Using polymode, code chunks are in R-mode, and text is
markdown-mode. C-c C-c
evaluates a code chunk.
(condition-case nil (progn (require 'poly-R) (require 'poly-markdown) (add-to-list 'auto-mode-alist '("\\.Rmd" . poly-markdown+r-mode)) ;; (add-to-list 'auto-mode-alist '("\\.md" . poly-markdown-mode)) (define-key polymode-mode-map (kbd "M-n r") 'my/ess-render-rmarkdown)) (error (message "** could not activate polymode")))
Activating polymode defines M-n
as a prefix for polymode key bindings.
Here's a function for rendering the current buffer, copied (and modified a bit) from a comment left by @kwstat on an issue in the polymode github repository.
(defun my/ess-render-rmarkdown () "Compile R markdown (.Rmd). Should work for any output type." (interactive) ;; Check if attached R-session (condition-case nil (ess-get-process) (error (ess-switch-process))) (let* ((rmd-buf (current-buffer))) (save-excursion (let* ((sprocess (ess-get-process ess-current-process-name)) (sbuffer (process-buffer sprocess)) (buf-coding (symbol-name buffer-file-coding-system)) (buffer-file-name-html (concat (file-name-sans-extension buffer-file-name) ".html")) (R-cmd (format "library(rmarkdown); rmarkdown::render(\"%s\")" buffer-file-name))) (save-buffer) (message "Running rmarkdown on %s" buffer-file-name) (ess-execute R-cmd 'buffer nil nil) (switch-to-buffer rmd-buf) (ess-show-buffer (buffer-name sbuffer) nil)))))
29 org-mode
org-mode hooks
(add-hook 'org-mode-hook '(lambda () (message "Loading org-mode hooks") ;; (font-lock-mode) (setq org-confirm-babel-evaluate nil) (setq org-src-fontify-natively t) (setq org-edit-src-content-indentation 0) (define-key org-mode-map (kbd "M-<right>") 'forward-word) (define-key org-mode-map (kbd "M-<left>") 'backward-word) ;; provides key mapping for the above; replaces default ;; key bindings for org-promote/demote-subtree (define-key org-mode-map (kbd "M-S-<right>") 'org-do-demote) (define-key org-mode-map (kbd "M-S-<left>") 'org-do-promote) (define-key org-mode-map (kbd "C-c n") 'hydra-org-navigation/body) (visual-line-mode) ;; org-babel ;; enable a subset of languages for evaluation in code blocks (setq my/org-babel-load-languages '((R . t) (latex . t) (python . t) (sql . t) (sqlite . t) (emacs-lisp . t) (dot . t))) ;; use "shell" for org-mode versions 9 and above (add-to-list 'my/org-babel-load-languages (if (>= (string-to-number (substring (org-version) 0 1)) 9) '(shell . t) '(sh . t))) (org-babel-do-load-languages 'org-babel-load-languages my/org-babel-load-languages) (require 'ox-minutes nil t) ;; (defun org-with-silent-modifications(&rest args) ;; "Replaces function causing error on org-export" ;; (message "Using fake 'org-with-silent-modifications'")) (defadvice org-todo-list (after org-todo-list-bottom ()) "Move to bottom of page after entering org-todo-list" (progn (end-of-buffer) (recenter-top-bottom))) (ad-activate 'org-todo-list) )) (setq org-agenda-files (list "~/Dropbox/notes/index.org")) (push '("\\.org\\'" . org-mode) auto-mode-alist) (push '("\\.org\\.txt\\'" . org-mode) auto-mode-alist)
Custom key bindings
29.1 navigation
I can't seem to remember the default bindings for navigation in org-mode, so I made this hydra for movement between headings and code blocks.
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-org-navigation (:exit nil :foreign-keys warn :columns 4 :post (redraw-display)) "hydra-org-navigation" ("RET" nil "<quit>") ("i" org-previous-item "org-previous-item") ("k" org-next-item "org-next-item") ("<right>" org-next-block "org-next-block") ("<left>" org-previous-block "org-previous-block") ("<down>" outline-next-visible-heading "outline-next-visible-heading") ("<up>" outline-previous-visible-heading "outline-previous-visible-heading") ("S-<down>" org-forward-paragraph "org-forward-paragraph") ("S-<up>" org-backward-paragraph "org-backward-paragraph") ("s" (org-insert-structure-template "src") "add src block" :color blue) ("w" my/org-element-as-docx "my/org-element-as-docx" :color blue) ("q" nil "<quit>"))) (message "** hydra is not installed")) ;; org-mode-map binds "C-c n" in org-mode-map
29.2 org-re-reveal
Fork of org-reveal
for generating presentations with reveal.js
Example document header:
#+OPTIONS: num:nil toc:nil #+REVEAL_TRANS: None #+REVEAL_THEME: Moon #+REVEAL_ROOT: ./reveal.js #+Title: LM510: Informatics Managenent #+Author: Noah Hoffman #+Email: ngh2@uw.edu
This assumes that you have cloned https://github.com/hakimel/reveal.js in the same directory as the .org file.
(if (require 'org-re-reveal nil 'noerror) (use-package org-re-reveal) (message "** org-re-reveal is not installed"))
29.3 org-mode utilities
(defun insert-date () ;; Insert today's timestamp in format "<%Y-%m-%d %a>" (interactive) (insert (format-time-string "<%Y-%m-%d %a>"))) (make-alias 'insert-date)
(defun org-add-entry (filename time-format) ;; Add an entry to an org-file with today's timestamp. (interactive "FFile: ") (find-file filename) (end-of-buffer) (delete-blank-lines) (insert (format-time-string time-format)))
Add a new entry to main notes file.
(defvar my/org-index "~/Dropbox/notes/index.org") (defun my/org-index-add-entry () (interactive) (org-add-entry my/org-index "\n* <%Y-%m-%d %a> ")) (defun my/find-org-index () (interactive) (find-file my/org-index))
Add a new entry to my journal.
(global-set-key (kbd "C-x C-j") (lambda () (interactive) (org-add-entry "~/Dropbox/journal/journal.org" "\n* %A, %B %d, %Y")))
Export the contents of the element at point and convert to .docx using pandoc.
(defun safename (str) "Remove non-alphanum characters and downcase" (let ((exprs '(("^\\W+" "") ("\\W+$" "") ("\\W+" "-")))) (dolist (e exprs) (setq str (replace-regexp-in-string (nth 0 e) (nth 1 e) str))) (downcase str))) (defun my/org-element-as-docx () "Export the contents of the element at point to a file and convert to .docx with pandoc" (interactive) (let* ((sec (car (cdr (org-element-at-point)))) (header (plist-get sec ':title)) (fname (safename header)) (basedir (shell-quote-argument (read-directory-name "Output directory: " (expand-file-name "~/Downloads")))) (orgfile (make-temp-file fname nil ".org")) (docx (concat (file-name-as-directory basedir) fname ".docx"))) (write-region (plist-get sec ':begin) (plist-get sec ':end) orgfile) (call-process-shell-command (format "pandoc %s -o %s" orgfile docx)) (if (y-or-n-p "open file?") (shell-command (format "open %s" docx))) (message "wrote %s" docx) ))
30 chrome "edit with emacs"
'edit-server' is initialized by ELPA, but we need to start the server.
(condition-case nil (edit-server-start) (error (message "** could not start edit-server (chrome edit with emacs)")))
31 Python
elpy
(installed from ELPA above) provides the primary basis for my
python programming environment. After a bit of trial and error, the
combination of elpy
and flycheck
(as opposed to flymake
, see
below) supports fairly seamless switching between python2 and python3
programming environments.
An important note on using virtualenvs: when emacs is first launched,
no venv is activated, and elpy
uses system defaults for the python
interpreter and any supporting python packages that may be
installed. This is probably not what you want. Use the functions via
the hydra defined below to activate appropriate virtualenv for your
project. The table below shows my key bindings:
C-\ p 2 |
activate-venv-default-py2 |
C-\ p 3 |
activate-venv-default-py3 |
C-\ p v |
activate-venv-current-project |
See below for the definition of these functions.
The default virtualenvs may be created using bin/venv.sh
, which looks like this:
#!/bin/bash # set up virtualenvs for python2 and python3 set -e VENV2=~/.emacs.d/python2-env virtualenv $VENV2 $VENV2/bin/pip install -U pip $VENV2/bin/pip install -U -r requirements.txt VENV3=~/.emacs.d/python3-env python3 -m venv $VENV3 $VENV3/bin/pip install -U pip $VENV3/bin/pip install -U -r requirements.txt
31.1 python-mode hooks
(add-hook 'python-mode-hook '(lambda () (setq indent-tabs-mode nil) (setq tab-width 4) (setq py-indent-offset tab-width) (setq py-smart-indentation t) (define-key python-mode-map "\C-m" 'newline-and-indent) (elpy-mode)))
File name mappings
(push '("SConstruct" . python-mode) auto-mode-alist) (push '("SConscript" . python-mode) auto-mode-alist) (push '("*.cgi" . python-mode) auto-mode-alist)
Default 'untabify converts a tab to equivalent number of spaces before deleting a single character.
(setq backward-delete-char-untabify-method "all")
31.2 flycheck
Flycheck implements syntax checking for many languages, but for now,
I'm just using it in the context of elpy
. It's a tossup whether to
configure flycheck separately from elpy, but I did so below.
(if (require 'flycheck nil 'noerror) (use-package flycheck :init (setq flycheck-flake8rc "~/.emacs.d/flake8.conf") (setq flycheck-check-syntax-automatically '(mode-enabled save))) (message "** flycheck is not installed"))
31.3 elpy
See https://github.com/jorgenschaefer/elpy/wiki/Installation
Configure elpy
. Note that I use C-
+ arrows to move between
windows, and M
+ arrows to move by word. These are in muscle memory
at this point, and elpy can't have them (these bindings are removed below).
(if (require 'elpy nil 'noerror) (use-package elpy :bind (:map elpy-mode-map ("C-<right>" . nil) ("C-<left>" . nil) ("M-<right>" . nil) ("M-<left>" . nil) ("M-<right>" . nil) ("M-C-]" . elpy-nav-move-iblock-right) ("M-C-[" . elpy-nav-move-iblock-left)) :init (when (require 'flycheck nil t) (add-hook 'elpy-mode-hook 'flycheck-mode)) :config (setq elpy-modules (delq 'elpy-module-django elpy-modules)) (setq elpy-modules (delq 'elpy-module-flymake elpy-modules)) (setq elpy-rpc-backend "jedi") (add-to-list 'elpy-project-ignored-directories "src") (add-to-list 'elpy-project-ignored-directories "*-env")) (message "** elpy is not installed"))
Features such as syntax checking, linting, and code completion rely on external python packages. Here's a script that creates a default virtualenv for each of python2 and python3, and installs all of the external packages that I use there:
cd ~/.emacs.d
bin/venv.sh
On (re)activation of a virtualenv, it seems to be necessary to restart
the elpy RPC process and reload elpy-mode
to reset syntax
checking. Otherwise, elpy
does a nice job of switching virtualenvs.
(defvar venv-default-py2 "~/.emacs.d/python2-env") (defvar venv-default-py3 "~/.emacs.d/python3-env") (defvar venv-default venv-default-py2) (defun activate-venv-and-reload (venv) (pyvenv-activate venv) (elpy-rpc-restart) (elpy-mode)) (defun activate-venv-default-py2 () (interactive) (setq elpy-rpc-python-command "python2") (activate-venv-and-reload venv-default-py2)) (defun activate-venv-default-py3 () (interactive) (setq elpy-rpc-python-command "python3") (activate-venv-and-reload venv-default-py3))
This function installs python dependencies to the current virtualenv from within Emacs.
(defun elpy-install-requirements () "Install python requirements to the current virtualenv." (interactive) (unless pyvenv-virtual-env (error "Error: no virtualenv is active")) (let ((dest "*elpy-install-requirements-output*") (install-cmd (format "%s/bin/pip install -U --force '%%s'" pyvenv-virtual-env)) (deps '("elpy" "jedi" "pyflakes" "pep8" "flake8" "importmagic" "yapf"))) (generate-new-buffer dest) (mapcar #'(lambda (pkg) (message (format install-cmd pkg)) (call-process-shell-command (format install-cmd pkg) nil dest)) deps) (call-process-shell-command (format "%s/bin/pip freeze" pyvenv-virtual-env) nil dest) (switch-to-buffer dest)) (elpy-rpc-restart)) (make-alias 'elpy-install-requirements)
Define some utilities for activating a virtualenv in the current
project. Especially during the transition to python3, there may be
more than one around, and it's useful to be able to switch between
virtualenvs for the same project. Turns out it's really easy to use
helm
to present a menu of options.
(defun list-venvs (basedir) "Return a list of paths to virtualenvs in 'basedir' or nil if none can be found" (interactive) (let ((fstr "find %s -path '*bin/activate' -maxdepth 4") (pth (replace-regexp-in-string "/$" "" basedir))) (mapcar (lambda (string) (replace-regexp-in-string "/bin/activate$" "" string)) (cl-remove-if (lambda (string) (= (length string) 0)) (split-string (shell-command-to-string (format fstr pth)) "\n"))) )) (defun list-venvs-current-project () (if (elpy-project-root) (list-venvs elpy-project-root) (error "error: there is no project here"))) (defun helm-choose-venv-current-project () (interactive) (helm :sources (helm-build-sync-source "choose a virtualenv" :candidates 'list-venvs-current-project) :buffer "*helm choose virtualenv*")) (defun activate-venv-current-project () "Activate a virtualenv if one can be found in the current project; otherwise activate the virtualenv defined in `venv-default'. Also restarts the elpy rpc process." (interactive) (let ((venv (helm-choose-venv-current-project))) (if venv (if (y-or-n-p (format "Activate %s?" venv)) (progn (activate-venv-and-reload venv) (message "Using %s" pyvenv-virtual-env))) (message "could not find a virtualenv here")))) (make-alias 'activate-venv-current-project)
31.4 autopep8
Apply autopep8
(https://github.com/hhatto/autopep8) to the current
buffer. Reference: Mastering Emacs:
http://www.masteringemacs.org/articles/2011/10/19/executing-shell-commands-emacs/
(defun p8 () "Apply autopep8 to the current region or buffer" (interactive) (unless (region-active-p) (mark-whole-buffer)) (shell-command-on-region (region-beginning) (region-end) ;; beginning and end of region or buffer "autopep8 -" ;; command and parameters (current-buffer) ;; output buffer t ;; replace? "*autopep8 errors*" ;; name of the error buffer t) ;; show error buffer? (goto-char (region-end)) ;; ... and delete trailing newlines (re-search-backward "\n+" nil t) (replace-match "" nil t))
Instead of simply replacing the current buffer, use ediff to compare it to the output of autopep8.
(defun p8-and-ediff () "Compare the current buffer to the output of autopep8 using ediff" (interactive) (let ((p8-output (get-buffer-create (format "* %s autopep8 *" (buffer-name))))) (shell-command-on-region (point-min) (point-max) ;; beginning and end of buffer "autopep8 -" ;; command and parameters p8-output ;; output buffer nil ;; replace? "*autopep8 errors*" ;; name of the error buffer t) ;; show error buffer? (ediff-buffers (current-buffer) p8-output)))
31.5 a hydra for python-related actions
Let's expose some of the frequently-used actions using a hyrda
presented as a submenu of my main hydra (ie, C-\ p
).
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-python (:color blue :columns 4 :post (redraw-display)) "hydra-python" ("RET" redraw-display "<quit>") ("2" activate-venv-default-py2 "activate-venv-default-py2") ("3" activate-venv-default-py3 "activate-venv-default-py3") ("c" flycheck-list-errors "flycheck-list-errors") ("e" elpy-config "elpy-config") ("f" flycheck-verify-setup "flycheck-verify-setup") ("g" elpy-goto-definition-other-window "elpy-goto-definition-other-window") ("i" elpy-install-requirements "elpy-install-requirements") ("v" activate-venv-current-project "activate-venv-current-project") ("y" elpy-yapf-fix-code "elpy-yapf-fix-code"))) (message "** hydra is not installed"))
32 Javascript/json
Indent json and javascript files using 2 spaces (default is 4).
(add-hook 'js-mode-hook (lambda () (make-local-variable 'js-indent-level) (setq js-indent-level 2))) (add-hook 'json-mode-hook (lambda () (make-local-variable 'js-indent-level) (setq js-indent-level 2)))
33 Groovy mode
Useful for nextflow
(use-package groovy-mode :ensure t :mode ("\\.nf" . groovy-mode))
34 shell
Recognize zsh and bash scripts by file suffix.
(add-to-list 'auto-mode-alist '("\\.zsh\\'" . sh-mode)) (add-to-list 'auto-mode-alist '("\\.bash\\'" . sh-mode))
35 scons
I should really start using a snippet package, but for now:
(defun scons-insert-command () (interactive) (insert "output, = env.Command( target=, source=, action=('') )")) (make-alias 'scons-insert-command)
36 text-mode
(add-hook 'text-mode-hook '(lambda () ;; (longlines-mode) (if enable-flyspell-p (flyspell-mode))))
37 rst-mode
(add-hook 'rst-mode-hook '(lambda () (message "Loading rst-mode hooks") (if enable-flyspell-p (flyspell-mode)) (define-key rst-mode-map (kbd "C-c C-a") 'rst-adjust)))
38 moinmoin-mode
A mode for editing moinmoin wiki text. This mode is installed via elpa, but must be initialized.
;; (condition-case nil ;; (require 'moinmoin-mode) ;; (error (message "** could not load moinmoin-mode")))
39 web-mode
A major-mode for editing web templates http://web-mode.org
Also: A Hydra for web-mode commands; activate via hydra-launcher
using C-\ w
(if (require 'web-mode nil 'noerror) (use-package web-mode :mode (("\\.html" . web-mode)) :bind ("C-c w" . hydra-web-mode/body) :init (setq web-mode-enable-current-element-highlight t) (setq web-mode-engines-alist '(("django" . "\\.html"))) (setq indent-tabs-mode nil) (defhydra hydra-web-mode (:color blue :columns 4 :post (redraw-display)) "hydra-web-mode" ("RET" redraw-display "<quit>") ("b" web-mode-element-beginning "element-beginning") ("e" web-mode-element-beginning "element-end") ("/" web-mode-element-close "element-close"))) (message "** web-mode is not installed"))
40 tramp
(condition-case nil (require 'tramp) (setq tramp-default-method "scp") (error (message "** could not load tramp")))
41 git/magit
(require 'vc-git)
Magit is installed via ELPA.
(global-set-key (kbd "C-c m") 'magit-status)
42 sql support
Use sqlite3
(setq sql-sqlite-program "sqlite3")
Preset connections
(setq sql-connection-alist '((some-server (sql-product 'mysql) (sql-server "1.2.3.4") (sql-user "me") (sql-password "mypassword") (sql-database "thedb") (sql-port 3307)))) (defun sql-connect-preset (name) "Connect to a predefined SQL connection listed in `sql-connection-alist'" (eval `(let ,(cdr (assoc name sql-connection-alist)) (flet ((sql-get-login (&rest what))) (sql-product-interactive sql-product))))) (defun sql-mastermu () (interactive) (sql-connect-preset 'mastermu)) ;; buffer naming (defun sql-make-smart-buffer-name () "Return a string that can be used to rename a SQLi buffer. This is used to set `sql-alternate-buffer-name' within `sql-interactive-mode'." (or (and (boundp 'sql-name) sql-name) (concat (if (not(string= "" sql-server)) (concat (or (and (string-match "[0-9.]+" sql-server) sql-server) (car (split-string sql-server "\\."))) "/")) sql-database))) (add-hook 'sql-interactive-mode-hook (lambda () (setq sql-alternate-buffer-name (sql-make-smart-buffer-name)) (sql-rename-buffer)))
43 gpg
(require 'epa-file) (setenv "GPG_AGENT_INFO" nil) ;; suppress graphical passphrase prompt
44 Outline minor mode
The default key bindings for outline-minor-mode start with 'C-c @ C-', which is… awkward. Use alternative bindings courtesy of Sue D. Nymme via emacswiki (http://emacswiki.org/emacs/OutlineMinorMode).
;; Outline-minor-mode key map (define-prefix-command 'cm-map nil "Outline-") ;; HIDE (define-key cm-map "q" 'hide-sublevels) ; Hide everything but the top-level headings (define-key cm-map "t" 'hide-body) ; Hide everything but headings (all body lines) (define-key cm-map "o" 'hide-other) ; Hide other branches (define-key cm-map "c" 'hide-entry) ; Hide this entry's body (define-key cm-map "l" 'hide-leaves) ; Hide body lines in this entry and sub-entries (define-key cm-map "d" 'hide-subtree) ; Hide everything in this entry and sub-entries ;; SHOW (define-key cm-map "a" 'show-all) ; Show (expand) everything (define-key cm-map "e" 'show-entry) ; Show this heading's body (define-key cm-map "i" 'show-children) ; Show this heading's immediate child sub-headings (define-key cm-map "k" 'show-branches) ; Show all sub-headings under this heading (define-key cm-map "s" 'show-subtree) ; Show (expand) everything in this heading & below ;; MOVE (define-key cm-map "u" 'outline-up-heading) ; Up (define-key cm-map "n" 'outline-next-visible-heading) ; Next (define-key cm-map "p" 'outline-previous-visible-heading) ; Previous (define-key cm-map "f" 'outline-forward-same-level) ; Forward - same level (define-key cm-map "b" 'outline-backward-same-level) ; Backward - same level ;; commands are prefixed with C-c o (global-set-key (kbd "C-c o") cm-map)
45 Search and replace
Here are a few packages that make search and replace more fun. I'll define a keymap using a hydra in the final section below.
45.1 occur-dwim
Copied from a post on "(or emacs"
Note that plain-old occur
can be executed using "M-s o" by default
(which I can never remember), of via hyrda-search
defined below.
(defun occur-dwim () "Call `occur' with the current region (if active) or word." (interactive) (push (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) (let ((sym (thing-at-point 'symbol))) (when (stringp sym) (regexp-quote sym)))) regexp-history) (call-interactively 'occur))
45.2 visual-regexp
Two packages by the same author add visual cues to searching and/or replacing with regular expressions:
- visual-regexp
- https://github.com/benma/visual-regexp.el
- visual-regexp-steroids
- https://github.com/benma/visual-regexp-steroids.el/
Installed using ELPA.
(if (package-installed-p 'visual-regexp-steroids) (require 'visual-regexp-steroids))
45.3 helm-swoop
Show matches to a search string in a another window.
A couple of nice touches:
- Use
M-i
to activate from withinisearch
- While searching,
C-c C-e
activates edit-mode, in which you can edit the buffer from within thehelm-swoop
buffer
(condition-case nil (require 'helm-swoop) (error (message "** could not activate helm-swoop")))
45.4 Hydra for searching
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-search (:color blue :columns 4) "hydra-search" ("RET" helm-swoop "helm-swoop") ("b" helm-swoop-back-to-last-point "helm-swoop-back-to-last-point") ("f" file-file-in-project "find-file-in-project") ("m" helm-multi-swoop "helm-multi-swoop") ("M" helm-multi-swoop-all "helm-multi-swoop-all") ("o" occur "occur-dwim") ("O" occur-dwim "occur") ("r" vr/isearch-backward "vr/isearch-backward") ("s" vr/isearch-forward "vr/isearch-forward")) (global-set-key (kbd "C-c s") 'hydra-search/body)) (message "** hydra is not installed"))
45.5 Hydra for string replacement
(if (require 'hydra nil 'noerror) (progn (defhydra hydra-replace (:color blue) "hydra-replace" ("RET" replace-string "replace-string") ("r" vr/replace "vr/replace") ("q" query-replace "query-replace") ("Q" vr/query-replace "vr/query-replace")) (global-set-key (kbd "C-c r") 'hydra-replace/body)) (message "** hydra is not installed"))
46 Misc utilities
46.1 copy-buffer-file-name
(defun copy-buffer-file-name () "Add `buffer-file-name' to `kill-ring' and echo the value to the minibuffer" (interactive) (if buffer-file-name (progn (kill-new buffer-file-name t) (message buffer-file-name)) (message "no file associated with this buffer"))) (make-alias 'copy-buffer-file-name)
46.2 copy-and-comment
(defun copy-and-comment () "Comment active region and paste uncommented text on the following line." (interactive) (kill-new (buffer-substring (region-beginning) (region-end))) (comment-region (region-beginning) (region-end)) (goto-char (region-end)) (delete-blank-lines) (newline 2) (yank)) (global-set-key (kbd "M-C-;") 'copy-and-comment)
46.3 unfill-paragraph
from http://defindit.com/readme_files/emacs_hints_tricks.html
(defun unfill-paragraph () (interactive) (let ((fill-column (point-max))) (fill-paragraph nil))) (global-set-key (kbd "M-C-q") 'unfill-paragraph) (make-alias 'unfill-paragraph)
46.4 Copy region to other window
Adapted from http://emacs.stackexchange.com/questions/3743/how-to-move-region-to-other-window
(defun copy-region-or-line-other-window () "Copy selected text or current line to other window" (interactive) (progn (save-excursion (if (region-active-p) (copy-region-as-kill (region-beginning) (region-end)) (copy-region-as-kill (line-beginning-position) (+ (line-end-position) 1))) (other-window 1) (yank)) (other-window -1))) (make-alias 'copy-region-or-line-other-window)
47 elisp-format
Written by Andy Stewart and available on emacswiki: http://www.emacswiki.org/emacs/elisp-format.el
(condition-case nil (require 'elisp-format) (error (message "** could not load elisp-format")))
48 emacsclient
Buffers opened from command line don't create new frame
(setq ns-pop-up-frames nil)
49 discover.el
Introduced in this blog post by Mickey Petersen, discover.el
provides Magit-style contextual menus for dired
(activate using
?
), register keys in C-x r
, and the Isearch keys in M-s
. I do in
fact discover something every time I use it!
(condition-case nil (progn (require 'discover) (global-discover-mode 1)) (error (message "** could not activate discover")))
50 dired-x
Toggle dired-omit-mode
with C-x M-o
. Hide uninteresting files by
default. By default, .
and ..
are hidden in dired-omit-mode
;
redefine dired-omit-files
to show these.
(use-package dired-x :config (progn (setq dired-omit-verbose nil) (add-hook 'dired-mode-hook #'dired-omit-mode) (setq dired-omit-files "^\\.?#")))
51 enable "advanced" commands
Not sure why these are disabled by default.
(put 'downcase-region 'disabled nil) (put 'upcase-region 'disabled nil) (put 'narrow-to-region 'disabled nil)
52 custom-set-variables
Emacs modifies this statement if you use the interactive "customize" function, so don't do that.
(custom-set-variables
'(safe-local-variable-values (quote ((toggle-read-only . t)))))
53 License
;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see <http://www.gnu.org/licenses/>.