Skip to content

Latest commit

 

History

History
714 lines (594 loc) · 27.1 KB

basics.org

File metadata and controls

714 lines (594 loc) · 27.1 KB

Package Management

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

UI Tweaks And Keybindings

Basic Sanity

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.

Behave more like an IDE, even in the terminal

If we’re in a terminal, we should allow terminal-mouse stuff.

(unless window-system
  (xterm-mouse-mode))

Project Management

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

Modern mode-line

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.

Visual Stuff

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

Navigation

Browser-like back/forward buttons

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> or C-x <left>.
  • For forwards C-x C-<right> or C-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.

Jumping the cursor around

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

Multiple-cursor editing

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

Sort out emacs’ crazy undo/redo system

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

Add toolbar button for redo and undo-tree-visualize

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.

Handy Tools

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

Make Backups More Sensible

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

Don’t close files when I type cmd-k

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

Optional windows-friendly shortcuts

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.

Add a toolbar button for toggling cua-mode on and off.

It’s a pain to remember that command, so I windows logo in the toolbar would probably be easier.

Enable narrowing

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)

Make the emacs help system prettier

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 config

Enable Structure Templates

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)

Export to more formats

Built in formats

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

Additional Formats

For more formats, we need to install stuff.

Confluence

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)

Clipboard

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)

Hyperlink to anything

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)

Enable time tracking

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)

Task management

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)

Viewing and capturing tasks

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

Hyperlinks and Gmail Integration

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

Helper functions

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

Execute more kinds of source blocks

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

OS Helpers

Mac webcam or sound management

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]

Emacs Server

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.

Footnotes

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.