We should get packages from the melpa-stable, melpa, and gnu repos.
(require 'package)
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/"))
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
(package-initialize)
Now that we know where to get packages from, we can get and use use-package to manage the getting and using of all the other packages.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-when-compile
(require 'use-package))
Don’t put tabs everywhere. Use spaces instead. Tabs are evil.
(setq-default indent-tabs-mode nil)
If we need we can use M-x untabify
or M-x tabify
to change things up.
If we’re in a terminal, we should allow terminal-mouse stuff.
(unless window-system
(xterm-mouse-mode))
By default, emacs likes to think of things at the granularity of files (or ”buffers”, which is roughly an open file). However, projectile mode helps us think about “a project” as a thing that can be opened, and a context in which we can do things. For example “find a file in this project” or “find this text in a project” or similar.
(use-package projectile
:ensure t
:delight)
(projectile-mode)
Now that we have projectile mode, we can bind a key for
“control-p”- or “command-t”-like behaviour – a fuzzy filename
matcher that opens files in the current project. It’s bound to C-c
p g
by default. Let’s also bind it to command-p.
(global-set-key (kbd "s-p") 'projectile--find-file-dwim)
You can ask emacs for all the other project-related shortcuts by
typing C-c p ?
.
Feebleline gets rid of the emacs modeline, and replaces it with a minimal thing in the echo area.
(use-package feebleline
:ensure t
:config (setq feebleline-msg-functions
'((feebleline-line-number :post "" :fmt "%5s")
(feebleline-column-number :pre ":" :fmt "%-2s")
(feebleline-file-directory :face feebleline-dir-face :post "")
(feebleline-file-or-buffer-name :face font-lock-keyword-face :post "")
(feebleline-file-modified-star :face font-lock-warning-face :post "")
(feebleline-git-branch :face feebleline-git-face :pre " : ")
(feebleline-project-name :align right)
(current-time-string :align right))))
I’m not going to turn it on by default, because I want all the GUI
elements to be available if this is someone’s first time pairing in
emacs. However, feebleline-mode
is used by gds-quiet-ui
below.
Display line numbers – this is especially useful when pair-programming.
(if (< emacs-major-version 26)
(progn
(unless window-system
(setq linum-format "%3d \u2502 "))
(global-linum-mode))
(global-display-line-numbers-mode))
We can use diminish delight to clean up the mode-line, by
reducing the amount of space taken up by the names of all the
various modes we might be running.
(use-package delight
:ensure t)
Make autocompletion friendlier.
(ido-mode)
(setq ido-auto-merge-work-directories-length -1)
If we’re in a window system, use the nyx theme to make emacs’ colours a little prettier.
(when (display-graphic-p)
(use-package nyx-theme
:ensure t)
(load-theme 'nyx t))
Enable transparency if your window manager supports it
(set-frame-parameter (selected-frame) 'alpha '(85 . 70))
(add-to-list 'default-frame-alist '(alpha . (85 . 70)))
By default, we leave all the toolbars and menubars visible. But sometimes it’s nice to get rid of all that and make things quiet.
(defun gds-quiet-ui ()
"Make the GUI less noisy
Turn off the tool-bar, menu-bar, scrollbars, and mode-line"
(interactive)
(tool-bar-mode 0)
(menu-bar-mode 0)
(scroll-bar-mode 0)
(feebleline-mode 1))
We can use dimmer.el to make it more obvious which window/pane is selected at a given time.
(use-package dimmer
:load-path "~/.emacs.d/from-the-net/dimmer.el/"
:delight
:config
(dimmer-configure-company-box)
(dimmer-configure-gnus)
(dimmer-configure-magit)
(dimmer-configure-org)
(setq dimmer-fraction 0.4)
(dimmer-mode t))
We can use the emacs back button for browser-like navigation. So long as the toolbar is left on, we’ll get back and forward buttons in the toolbar. If you want to navigate backwards and forwards with the keyboard, use the following:
- For backwards
C-x C-<left>
orC-x <left>
. - For forwards
C-x C-<right>
orC-x <right>
.
(use-package back-button
:ensure t
:delight
:commands back-button-mode)
(back-button-mode 1)
(define-key back-button-mode-map (kbd "C-x <left>") 'back-button-global-backward)
(define-key back-button-mode-map (kbd "C-x <right>") 'back-button-global-forward)
Note that by default, the back button
package uses C-x <left>
and C-x <right>
for moving backward and forward in the current
buffer only. To go backwards and forwards across buffers, you need
to use C-x C-<left>
and C-x C-<right>
. Unfortunately
C-<left>
and C-<right>
don’t work at the termina. Since we
think that inter-buffer (global) movement is more valuable than
the local movement, we map both sets of shortcuts to that.
I like to be able to navigate quickly to wherever on the screen I happen to be looking. I do this with avy. Since navigation is pretty much the most common thing I do in an editor, I want shortcuts that are extremely short, and that don’t clash with any of the other millions of shortcuts in the emacs ecosystem. To get this, I use key-chords:
- If you mash
jl
, every line on screen will get a two-character label. Type the label to jump to that line. - If you mash
jw
, every window on screen will get a one-character label. Type the label to jump to that line1. - If you mash
jj
, you get prompted for a character. Type the first character of the word you want to jump to. Now all words that begin with that character will be labelled. Type the label to jump to that word.(use-package key-chord :ensure t :delight :config (use-package avy :ensure t :delight) (use-package ace-window :ensure t :delight) (key-chord-mode t) (key-chord-define-global "jj" 'avy-goto-word-1) (key-chord-define-global "jl" 'avy-goto-line) (key-chord-define-global "jw" 'ace-window) (avy-setup-default))
Allow editing with multple cursors. There’s a good introduction video here.
(use-package multiple-cursors
:ensure t
:delight
:config
(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C->") 'mc/mark-next-like-this)
(global-set-key (kbd "C-<") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c C-<") 'mc/mark-all-like-this))
Quoting from the undo-tree web page:
Emacs has a powerful undo system. Unlike the standard undo/redo system in most software, it allows you to recover any past state of a buffer (whereas the standard undo/redo system can lose past states as soon as you redo). However, this power comes at a price: many people find Emacs’ undo system confusing and difficult to use
I agree. To get an idea of how non-intuitive the vanilla-emacs undo system
is, note that there is a keybinding for undo
, but no keybinding
for redo
.
The undo-tree package solves the problem by:
- Having a redo function
- Still allowing us to recover all past states of the buffer, by visualising those states as a tree.
To undo a change, hit C-z
2, C-/
or C-_
as normal. To
redo a change, hit C-?
or M-_
. If you undo a few changes,
accidentally type something (so “redo” will no longer do anything),
and suddenly realise that you shouldn’t have hit “undo” in the
first place, then hit C-x u
. This will show you a tree
representation of the changes you just made, undid, and the new
timeline you created when you accidentally typed whatever it was
you typed. You can move around in this branching timeline by
clicking, or using standard emacs navigation keys.
(use-package undo-tree
:ensure t
:delight)
(global-undo-tree-mode)
;; https://www.emacswiki.org/emacs/UndoTree
Out of the box, undo-tree doesn’t play nicely with the line numbers we might be relying on for pair-programming. To fix this (as suggested here), we can add a bunch of advice around the undo-tree functions which force the line numbers to update themselves.
(when (< emacs-major-version 26)
(defun undo-tree-visualizer-update-linum (&rest args)
(linum-update undo-tree-visualizer-parent-buffer))
(advice-add 'undo-tree-visualize-undo :after #'undo-tree-visualizer-update-linum)
(advice-add 'undo-tree-visualize-redo :after #'undo-tree-visualizer-update-linum)
(advice-add 'undo-tree-visualize-undo-to-x :after #'undo-tree-visualizer-update-linum)
(advice-add 'undo-tree-visualize-redo-to-x :after #'undo-tree-visualizer-update-linum)
(advice-add 'undo-tree-visualizer-mouse-set :after #'undo-tree-visualizer-update-linum)
(advice-add 'undo-tree-visualizer-set :after #'undo-tree-visualizer-update-linum))
Having sensible undo/redo shortcuts is great, but it’d be even
better to have toolbar buttons for more than just the undo
part
of the picture.
Start a shell with a single keypress.
(global-set-key (kbd "C-x M-m") #'eshell)
Make dired (directory editing) behaviour more sensible:
- If we open two directory windows next to each other and ask to move a file from one of them, the other will be the default target.
- Hit
r
on a directory window in order to edit filenames and permissions like regular text.
(require 'wdired)
(setq
dired-dwim-target t
wdired-allow-to-change-permissions t)
(define-key dired-mode-map "r" 'wdired-change-to-wdired-mode)
Let’s have a NERDTree-like file browser. There’s one called neotree. We’ll bind it to `<f8>`, to toggle on and off.
(use-package neotree
:ensure t
:delight
:commands neotree-toggle
:bind (("<f8>" . neotree-toggle)))
- Don’t clobber symlinks with backup files
- Put all backups in
~/.saves
instead of right next to the files I’m editing - Version the backups
(setq
backup-by-copying t
backup-directory-alist
'(("." . "~/.saves"))
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t)
The cmd-k shortcut is used for navigation in slack on mac. In emacs on mac, it seems to be set to kill the current buffer. Let’s stop that.
(global-unset-key (kbd "s-k"))
I’d like this config to be pretty friendly to folks who’re used to
“normal” IDEs like IntelliJ. But I also want to be able to use
things like the cool emacs rectangle editing
functions. Unfortunately, all the rectancle shortcuts begin with
C-x
, which windows users will expect to mean cut to clipboard
.
We can toggle between windows-style and emacs-style with M-x
cua-mode
. If you’re pairing with someone who expects windows-style
shortcuts, do that.
It’s a pain to remember that command, so I windows logo in the toolbar would probably be easier.
Narrowing is a handy trick that focuses your editor on a small part of a potentially very large file. This can be useful when presenting work to other people, or for scoping a semi-automated edit. For example, suppose I wanted to use a keyboard macro to edit many instances of a common pattern, but only within one section of my file. If I narrow to that section before editing, then my macro cannot accidentally affect the rest of the file.
Narrowing is turned off by default, so let’s turn it on:
(put 'narrow-to-region 'disabled nil)
The emacs help system is AWESOME, right out of the box. If you’ve
never played with it before, start with either C-h C-h
(to get
help on help) or C-h t
to start the emacs tutorial. You can look
up any package, any keybinding, any function, and any variable in
your emacs. You can find out what it does, where it was defined,
and so on.
The helpful package adds the icing to the cake. It syntax highlights the help text, and adds a bunch of useful contextual information and hyperlinks.
(use-package helpful
:ensure t
:bind
("C-h f" . helpful-callable)
("C-h v" . helpful-variable)
("C-h k" . helpful-key)
("C-c C-d" . helpful-at-point))
Org-mode structure templates are handy for entering common org-mode boilerplate. For example, for creating source code blocks, and so on. You can read about them on the web, or in your local org info page.
I like to enable inline snippet expansion:
(require 'org-tempo)
By default, org-mode will only export to ascii, html, icalendar, and latex. We can enable markdown, beamer and odt support.
(setq org-export-backends (list 'ascii 'html 'icalendar 'latex 'md 'beamer 'odt))
For more formats, we need to install stuff.
Here’s one for exporting to the format used by Atlassian Confluence – which is a wiki we use at work.
(use-package ox-confluence
:load-path "~/.emacs.d/from-the-net/")
(add-to-list 'org-export-backends 'confluence)
With ox-clip, we can export from org-mode as html-formatted text
in the clipboard. This one doesn’t work from the usual export
dispatcher, so we bind it in org-mode to C-c C-h
(use-package ox-clip
:ensure t)
(define-key org-mode-map (kbd "C-c C-h") 'ox-clip-formatted-copy)
One of the helpful things in org-mode is its ability to hyperlink
between lots of different types of things. I find it useful to have
a global binding for org-store-link
, as suggested in the manual.
(global-set-key (kbd "C-c l") 'org-store-link)
Org-mode also has a handy time-tracking feature, which you can use to keep track of how long you spend working on different tasks.
(setq org-clock-persist 'history)
(org-clock-persistence-insinuate)
Org-mode works great as a task-management system. I like mine to sync with trello, so I can use the same task lists in android as I do on my laptop – that plumbing is here.
I like to have six levels of task:
- things I want to do
- things I’m doing right now
- things I’ve done
- things I can’t do, and it’s not my fault
- things I might do later
- things I’m totally not doing
(setq org-todo-keywords
'((sequence "TODO(t!/!)" "DOING(g!/!)"
"|"
"DONE(d!/!)" "BLOCKED(b@/!)" "LATER(l!/!)" "NOTDOING(n@/!)")))
(setq org-log-into-drawer t)
Note that the BLOCKED
and NOTDOING
states have a funny @
code
in them. This means that when I decide that I’m blocked on
something, I’ll usually want to record what I’m blocked on. And
when I’m not doing something, I’ll generally want to record
why. Those recordings should go into a drawer where I don’t have to
look at them most of the time.
When I finish a thing, I like to record when I finished it.
(setq org-log-done 'time)
Often a task will pop into existence while in the middle of
another task. That’s what org-capture
is for. If I’m reading an
email and realise that I need to do a thing about this email, I
hit C-c c
and get an entry in my main org file.
If I want to see what jobs need doing, I can use my agenda. I hit
C-c a
to get a generated list of things that need doing.
(global-set-key (kbd "C-c c") 'org-capture)
(global-set-key (kbd "C-c a") 'org-agenda)
For this to work, we need to actually have some todo lists. Since I like mine to be in sync between org and trello, that bit of the plumbing is here.
Because we’re using org-trello, we need to capture all tasks as
top-level items (trello doesn’t support trees). I like to capture
tasks as regular TODO
items, which may or may not have deadlines.
If you don’t plan to use the org-trello stuff, you’ll want to set
org-default-notes-file
to something sensible for you.
(setq org-capture-templates
'(
("t" "Todo" entry (file org-default-notes-file)
"* TODO %? \n %a%(gds-org-pop-gmail-link)\n %K"
)
("1" "Todo by tomorrow" entry (file org-default-notes-file)
"* TODO %? \n :DEADLINE: <%(gds-tomorrow)>\n :PROPERTIES:\n :END:\n %a%(gds-org-pop-gmail-link)\n %K"
)
("2" "Todo within a week" entry (file org-default-notes-file)
"* TODO %? \n :DEADLINE: <%(gds-next-week)>\n :PROPERTIES:\n :END:\n %a%(gds-org-pop-gmail-link)\n %K"
)
("3" "Todo within a fortnight" entry (file org-default-notes-file)
"* TODO %? \n :DEADLINE: <%(gds-in-a-fortnight)>\n :PROPERTIES:\n :END:\n %a%(gds-org-pop-gmail-link)\n %K"
)
("4" "Todo within a month" entry (file org-default-notes-file)
"* TODO %? \n :DEADLINE: <%(gds-next-month)>\n :PROPERTIES:\n :END:\n %a%(gds-org-pop-gmail-link)\n %K"
)
("5" "Todo within two months" entry (file org-default-notes-file)
"* TODO %? \n :DEADLINE: <%(gds-in-two-months)>\n :PROPERTIES:\n :END:\n %a%(gds-org-pop-gmail-link)\n %K"
)))
One helpful feature of these capture templates is the %a
, which
means whenever we capture a task, it will include a hyperlink to
whatever we were doing before we hit C-c c
. For example, if I’m
reading an email from a customer asking a difficult question, I
might hit C-c c
and create a task to do the technical experiment
I need to do in order to answer that question. When I complete
that task sometime later, I can follow the link in the task to
find the email I want to reply to with my results.
This works great so long as I live entirely within emacs, but I’ll
also often want to refer to my tasks from within trello. In those
situations, I need links to gmail rather than gnus. To make that
work, we’ll need to join this org-capture config with our email
config. We’ll create a variable gds-org-gmail-link-buffer
to
communicate through. We’ll configure gnus to fill that buffer with
a gmail link every time we call org-capture
. At this end, we’ll
pop any link out of the buffer and use it in our capture.
(defvar gds-org-gmail-link-buffer nil
"A gmail link to a recently viewed email.
This is a one-place buffer, which might be nil, or might contain
a link to an email using Gmail. It should be set before calling
`org-capture', whereupon `gds-org-pop-gmail-link' will use it,
and set it back to nil.")
(defun gds-org-pop-gmail-link ()
"Return either a link to a recent email, or \"\".
If there's a gmail link waiting in `gds-org-gmail-link-buffer',
then use it to construct a string for an org capture, set the
buffer to nil. If not, return the empty string."
(let ((link gds-org-gmail-link-buffer))
(if link
(progn
(setq gds-org-gmail-link-buffer nil)
(format "\n %s" link))
"")))
Finally, we need a few little date utility functions, for when we want to capture a task that only makes sense for the next day, week, month, etc.
(defun gds-today ()
"Get today's date as a string."
(format-time-string "%F" (current-time)))
(defun gds-tomorrow ()
"Get tomorrow's date as a string."
(format-time-string "%F" (time-add (current-time) (days-to-time 1))))
(defun gds-next-week ()
"Get next week's date as a string."
(format-time-string "%F" (time-add (current-time) (days-to-time 7))))
(defun gds-in-a-fortnight ()
"Get next fortnight's date as a string."
(format-time-string "%F" (time-add (current-time) (days-to-time 14))))
(defun gds-next-month ()
"Get next month's date as a string."
(cl-destructuring-bind (sec min hour day month year dow dst zone)
(decode-time (current-time))
(format-time-string "%F" (encode-time 0 0 0 day (+ 1 month) year))))
(defun gds-in-two-months ()
"Get two month's date as a string."
(cl-destructuring-bind (sec min hour day month year dow dst zone)
(decode-time (current-time))
(format-time-string "%F" (encode-time 0 0 0 day (+ 2 month) year))))
By default, the only language that org-mode will run from org files
is emacs-lisp
. Let’s make it possible to run bash code from org too.
(org-babel-do-load-languages
'org-babel-load-languages
'((shell . t)))
When I’m on a mac, sometimes my webcam or sound stop working. The way to fix it is to kill some process, and allow the system to restart it.
(defun gds-fix-camera ()
"On a mac, restart the camera driver."
(interactive)
(shell "*fix-camera*")
(with-current-buffer "*fix-camera*"
(insert "sudo pkill VDCAssistant")))
(defun gds-fix-sound ()
"On a mac, restart the sound driver."
(interactive)
(shell "*fix-sound*")
(with-current-buffer "*fix-sound*"
(insert "sudo kill -9 `ps ax|grep 'coreaudio[a-z]' | awk '{print $1}'`")))
Make this a proper interactive thing that asks for the sudo password, then gets out of the way when it’s done.
- State “TODO” from [2019-06-19 Wed 10:31]
The emacs server and accompanying emacsclient
allows us to edit
files at the commandline (for example, as a result of running git
commit
) in an existing instance of emacs. This has the advantages
of:
- Faster startup times for the client
- Access to common state in all instances of the editor, for example, for use in autocomplete functions
The client only works if there is an instance of emacs running, in
which the command M-x server-start
has been run. We could include
that command in these configs. Howvever, if we did that, then every
invocation of emacs
after the first would suffer an error. This is
because it would attempt to start a second server listening on the
same port as the first.
Rather than invite errors of that kind, my preference is to use the
following wrapper scripts around emacsclient
.
In $HOME/bin/e
:
#!/usr/bin/env bash
emacsclient -a "" -t "${@}"
In $HOME/bin/ec
:
#!/usr/bin/env bash
emacsclient -a "" -c "${@}"
The e
script starts a terminal-based client. The ec
script
starts a client in a GUI window. We can therefore add $EDITOR="e"
to our .bashrc
.
1 If there are only two windows open, avy will skip the label-and-choose step, and just jump you straight into the other window.
2 So long as you’ve enabled cua-mode
. See Optional windows-friendly shortcuts above.