Skip to content

Latest commit

 

History

History
630 lines (469 loc) · 25.9 KB

corgi_manual.org

File metadata and controls

630 lines (469 loc) · 25.9 KB

Corgi User Manual

Principles

Minimal

Minimal is relative, one person’s essential is an other person’s bloat. What do we mean for Corgi to be minimal?

First off we limit the amount of packages that Corgi pulls in, even though some of the packages we do pull in are fairly large. Beyond that we limit the amount of customization we do beyond what these packages provide. When in doubt, do less. When there are multiple competing options for how to handle something, do the one that is closer to the default.

Combined this brings several benefits. It keeps Emacs running smoothly. It limits the number of moving parts, so there is less that can go wrong. And it makes it possible for people to keep an overview of what is it they are installing.

Reproducible installs

The modern Emacs ecosystem has gone far beyond what it is was originally intended for. People are doing increasingly impressive and complex things on top of this humble LISP runtime, with packages recursively depending on dozens if not hundreds of other packages.

Sadly our practices have not kept up with this reality. Two people can install the same Emacs config minutes apart, but still get different versions of packages, so no one is actually running the same code as anyone else. You are almost certainly getting a combination of package versions that was never tested before. Predictably this leads to breakage.

This is partly technical and partly cultural, it seems the diligence to not introduce breaking API changes is not pervasive yet, and frankly the language and package system provide little affordances to limit the blast radius.

straight.el seeks to rectify this by providing reproducible installs. All packages are installed via Git, using specific commit hashes, which are “frozen” in a version file. When first using Corgi it will install its own version file, so you get the exact same code that Corgi was developed and tested with.

Packages all the way down

When installing Corgi we don’t give you a huge .emacs.d directory to copy, instead you create your own, and simply pull in the corgi package. Beyond that it’s a standard Emacs config, and it’s your config. Add customizations to .emacs.d/init.el to your heart’s content.

Corgi itself is a collection of packages like corgi-editor, corgi-defaults, or corgi-emacs, and most of what these do is pulling in and configuring other packages. If you don’t like what any of them is doing, then copy the relevant package over to your personal config and customize it from there.

Vim-style modal editing

There are two schools of thought for how to do editor key bindings, modal and key-chord. Emacs traditionally is a key-chord style editor, you combine keys with one or more modifiers in elaborate “chords”. The vi/vim family of editors instead uses different editor “modes”, like “normal mode”, “insert mode”, or “visual mode”. What each key does depends on the current mode, in insert mode pressing `D` will insert the letter “D”, but in normal mode it will delete up to the end of the line.

Not all commands can be bound to a single key like that, others are bound to key sequences. `gg` goes to the beginning of the buffer. `,ep` evaluates and expression and pretty prints the result.

(Note that Emacs has a separate, unrelated concept of modes. To differentiate them these vim-style modes are also referred to as states.)

There are two reasons why we have made a strong decision to promote the modal style: RSI, and command composition.

RSI or repetitive strain injury, also known as a “mouse arm” or “emacs pinky” is an inflammation of the neural pathways in the arm and wrist. It’s a painful affliction that many programmers suffer from, and recovering can be a long and frustrating process, sometimes forcing people to put their programming career on hold for extended periods of time.

The Emacs key chords can be a contributing factor to RSI. It is telling that a common form of RSI among Emacs users shows up in the left pinky, used to press Ctrl over and over. It seems in contrast that pressing single keys in sequence, so the hands do less moving around and less stretching out, is easier on the body.

This alone should be a fairly compelling argument, but modal editing isn’t only good for your health, it will also make you a more productive programmer. See, these single keypress commands don’t just exist in isolation, they can be combined into “sentences” to very concisely express complex operations.

<number><command><text object or motion>

In normal mode `w` moves forward by one word. `3w` moves forward by 3 words. `dw` deletes the next word. `3dw` deletes the next 3 words. Combining these commands and text objects allows you to do in two or three key strokes what might be a lot of bouncing the cursor around and pressing several key chords otherwise.

Some more examples:

  • `2yL` copy (“yank”) the next 2 s-expressions
  • `c$` “change” from cursor position to the end of the line, this deletes the relevant section and changes to insert mode
  • `cs(]` change surrounding parenthesis to brackets
  • `di”` delete inner string, i.e. delete everything inside the surrounding double quotes

Focused on Clojure and LISP

This setup is mainly intended for Clojure programmers. That isn’t to say that you can’t or shouldn’t use Corgi with other languages, but when making choices of what to include a big factor is how much it matters to Clojure and LISP developers.

So we care a lot about interactive development, and structural editing, and make sure we have short convenient bindings for these things.

Consistent keybindings

When a command is specific to a major mode, its binding should do the closest equivalent in other major modes, or do nothing. So no bindings that do radically different things depending on the mode. (this is mostly for `,` leader keys)

Keybindings all live in one place

The common practice in the Emacs world is for packages to come with a bunch of keybindings out of the box, and for the Emacs config to then add a bunch more. These are all set up in their own library (.el) files imperatively with calls to define-key and friends.

In Corgi we load key bindings from key and signal files. These are pure-data specifications. Corgi comes with corgi-keys.el and corgi-signals.el, these contain all the bindings that Corgi provides, you can see them all in a single place.

For custom bindings you can put a user-keys.el and user-signals.el in your Emacs user directory, and they will get merged in (and get precedence, so you can override built-ins). For more extensive changes you can place a custom corgi-keys.el and/or corgi-signals.el in your Emacs user directory, and it will be used instead of the built-in ones.

Signals provide a level of indirection, they are keywords, like :eval/buffer, or :file/open. In the key binding file you configure how the signal is triggered (e.g. with , e b or SPC f f), in the signals file you bind it to a specific command, based on the mode, like eval-buffer in Emacs LISP, or cider-eval-buffer in Clojure.

This way we get great consistency, you can rebind how you want to “evaluate the current buffer”, and this key binding will work everywhere, even if the concrete commands are language-dependent.

It also means you can change the concrete command, like using counsel-find-file instead of the vanilla find-file, and this change will work regardless of how you are invoking that.

First class terminal and GUI support

While using GUI Emacs generally provides the better experience, there is value in continuing to support usage from a simple dumb terminal emulator. It’s especially valuable for use on servers, and combined with Tmux it provides a great low-latency mechanism for pairing in the cloud. (See our blog post on the matter).

So we mainly try to not do anything funky that terminals can’t handle, we pick bindings that can be communicated over a TTY, and include a few tweaks to make the emacs-in-a-terminal experience a little more smooth.

It’s your config

We don’t take over init.el, at the end of the day you decide how your config works. You decide which Corgi packages to pull in, and if you don’t like some of our choices, you can simply copy over the relevant code and tweak it from there.

We call this an unbundled Emacs config.

Installation

Installing Emacs

TODO: These instructions were written a few years ago, check if they are still accurate.

How to install Emacs depends on your operating system. Make sure you install “GNU Emacs”, and not an alternative version like “XEmacs” or “Aquamacs”.

Windows

Windows versions of GNU Emacs are distributed as zip-files, which can be found on gnu.org ftp mirrors. Find the zip file with the highest version number, at time of writing this is emacs-24.5-bin-i686-mingw32.zip, and download it.

Create a directory where you want to put Emacs, for example C:\Program Files\Emacs and unpack the zip file there.

The runemacs.exe program in the bin directory will start Emacs. The addpm.exe program will add Emacs to the start menu.

OS X

OS X already comes with a version of Emacs installed, but it’s very old (Emacs 22), and it doesn’t have GUI support, so it only works inside a terminal. You want to avoid using this version.

If you have Homebrew available then that is the most straightforward option.

$ brew update
$ brew install emacs --with-cocoa
$ brew linkapps emacs

Alternatively you can download a .dmg at emacsformacosx.com.

That should be enough to get you going. Emacs Redux has some more tips on setting up Emacs on OS X.

Linux

Your package manager should have ready-made packages available. If you have a graphical package manager look for an “emacs” package, or install the package from the command line with apt-get or yum.

Configuring Emacs: straight.el, use-package, corgi-packages

Corgi differs from other Emacs configurations like Spacemacs, Doom, or Prelude in that it’s not an Emacs configuration at all, instead it is a collection of packages that are meant as a foundation for building your own config.

These packages are distributed via Git, and can be installed with the straight.el functional package manager, so you will not find them on MELPA or similar repositories. These Corgi packages take care of various bits of Emacs boilerplate, as well as installing and configuring a set of base packages for you, so you get a system that is pleasant to use out of the box. To configure these third-party packages Corgi uses use-package.

This means that a corgi-based Emacs config consists of four parts

  • Install straight.el
  • Install use-package
  • Install corgi packages
  • Do your own setup

The Corgi repo contains a sample-config that you can copy to $HOME/.emacs.d. We recommend using Chemacs2, especially if you already have an existing config that you want to keep.

If you don’t have an existing Emacs config yet:

git clone https://github.com/plexus/chemacs2.git ~/.emacs.d

git clone https://github.com/lambdaisland/corgi /tmp/corgi
cp -r /tmp/corgi/sample-config ~/.emacs.corgi

[ -f ~/.emacs-profiles.el ] || cat <EOF > ~/.emacs-profiles.el
(("default" . ((user-emacs-directory . "~/.emacs.corgi"))))
EOF

If you have an .emacs.d you want to keep:

[ -f ~/.emacs ] && mv ~/.emacs ~/.emacs.bak
[ -d ~/.emacs.d ] && mv ~/.emacs.d ~/.emacs.default
git clone https://github.com/plexus/chemacs2.git ~/.emacs.d

git clone https://github.com/lambdaisland/corgi /tmp/corgi
cp -r /tmp/corgi/sample-config ~/.emacs.corgi

[ -f ~/.emacs-profiles.el ] || cat <<EOF > ~/.emacs-profiles.el
(("default" . ((user-emacs-directory . "~/.emacs.default")))
 ("corgi" . ((user-emacs-directory . "~/.emacs.corgi"))))
EOF

And run with emacs --with-profile corgi. It’s recommended you go through the files in the sample config to get a bit more familiar with what is there, this will after all become your own config going forward. They are elaborately documented to help you make sense of it all.

Key bindings

Corgi’s key system is provided by one of a Corgi package called Corkey. It contains the key binding functionality based on simple configuration files, as well as Corgi’s default bindings.

You can find all binding definitions in corgi-keys.el and corgi-signals.el. The keys file contains the actual bindings as a nested datastructure, bound to symbolic signals (keywords). E.g.

(("SPC" "Global leader key"
  ("f" "File commands"
   ("f" "Find file" :file/open))))

The signals file provides the mapping from this keyword to an actual Emacs command, based on the current major mode or minor modes.

((corkey-local-mode (:file/open counsel-find-file)))

This indirection serves two purposes. It allows you to change the command you want to use for a certain action like opening a file, indepently of its keybinding. So you can configure the command you want to use, and have it work consistently even when switching between different sets of bindings.

The other purpose is to provide mnemonic bindings that work in a mode-specific way. E.g. SPC s s invokes the signal :repl/toggle. In Clojure mode this will be bound to cider-switch-to-repl-buffer, whereas in Emacs LISP mode it will call ielm, and in SQL mode it does a sql-show-sqli-buffer. Each brings you to the REPL associated with the file you are working on.

If you decide you don’t like SPC s s for this functionality then you can rebind that, and it will work accordingly in all these modes.

Keys to get you started

Corgi relies on evil-mode, which Emulates Vim. For basic editing (insert, delete, copy, paste, etc.) we recommend going through a Vim tutorial.

For other commands like opening files or jumping around windows (panes/splits) we follow the conventions set out by Spacemacs, where all commands start with hitting the space bar (SPC), typically followed by a prefix key denoting a category (e.g. f for “file”) and a final key for the specific command. So e.g. SPC f f to open a file.

Press SPC or SPC f and wait a moment to get a list of options.

Besides this SPC “leader key” we also use , for mode-specific commands, e.g. in a Clojure buffer , j j will “Jack-in” a REPL.

General

SPC SPC : execute command (Emacs M-x)

Window SPC w

  • SPC [0...9] - go to window number [0..9]
  • SPC w /- split window vertically
  • SPC w -- split window horizontally
  • SPC w d- delete window
  • SPC w 1- extend current window to whole screen

buffers SPC b

  • SPC b b- list of all buffers
  • SPC b d- kill current buffer (not delete)

file SPC f

  • SPC f s - save a file
  • SPC p p- open a project
  • SPC f f- find a file
  • SPC p f- find a file in current project

Getting help SPC h

  • SPC h - help system
  • SPC h d f - description of selected function
  • SPC h d k - description of selected key binding

Working with REPLs

  • , s c connect to a REPL / process
  • , s s toggle between REPL and code buffer
  • , s q quit current REPL
  • , s Q quit all REPLs for the current project/session
  • , j j connect to a regular REPL (Clojure)
  • , j o connect to “other” REPL (ClojureScript)
  • , j a connect to “all” (Clojure + ClojureScript)
  • , l l link current project/buffer to an open REPL session

Structural editing

  • > slurp forward
  • < barf forward
  • SPC x s splice backward
  • SPC x t transpose sexp
  • L forward sexp
  • H backward sexp

The latter two are “text objects” that can be combined with other vim-style operations

  • yL copy next sexp (paste with p
  • dL delete next sexp
  • cL “change” sexp (delete and switch to insert mode)

Packages

corgi-packages

The corgi-packages repo acts itself as a straight package, but a special kind of package called a “recipe repository”, it contains the descriptions of all Corgi packages so Straight knows where to find them.

It also contains the corgi/upgrade-self command, which you can use to upgrade Corgi itself. This will upgrade corgi-packages to the latest git commit, and overwrite the corgi versions file, before relying on Straight to get all other packages to the correct version.

(defun corgi/upgrade-self ()
  "Upgrade Corgi

   Fetch the latest corgi-packages, and make straight use the
versions specified therein. Will overwrite any local changes to
straight/versions/corgi.el"
  (interactive)
  (straight-pull-package "corgi-packages")
  (corgi-copy-versions-file)
  (straight-thaw-versions))

corgi-defaults

This simply sets a slew of of Emacs variables to more sensible values, from disabling the menubar and toolbar, to fixing modifier keys on Mac and disabling the system bell.

There are many versions of this kind of thing around, this one is ours. We’ve tried to include mostly non-controversial things, but if there is anything you don’t like then just copy this file over to your own config, load your own version instead of ours, and take it from there.

corgi-editor

This is the meat-and-potatoes of the Corgi experience, how the editor feels and behaves. This sets up and configures a bunch of packages like Evil, Smartparens, Ivy (minibuffer completion), Swiper (fuzzy search), Projectile (project-aware commands), Aggressive indent, Company (completion).

Full list at time of writing:

  • aggressive-indent: auto-indent code as you type
  • avy: jump to specific character
  • company: completion framework
  • counsel: Improves some of the built-in UI using the Ivy completion features
  • diminish: Clean up the modeline by hiding certain minor modes
  • dumb-jump: Simple jump to identifier, mainly a fallback
  • evil: Vim-style editing
  • evil-cleverparens: Evil-based structural editing
  • evil-collection: Make many more areas of Emacs play nice with Evil
  • evil-surround: Port of Vim-surround, especially handy in LISP
  • expand-region: Edit by semantically shrinking/expanding the selection
  • goto-last-change: Jump to the last change in the file
  • ivy: Minibuffer completion framework
  • projectile: Project-specific functionality
  • rainbow-delimiters: Color matching parenthesis, brackets, etc.
  • smartparens: Structural editing
  • smex: Interactive fuzzy-searching alternative to M-x
  • string-edit: Edit string contents in a separate buffer (great when you have a lot of escaping)
  • swiper: Fuzzy search inside the buffer
  • undo-fu: Better undo
  • which-key: Make keys discoverable
  • winum: Number buffers and jump to them easily
  • xclip: Only on terminal, integrate with the system clipboard

corgi-emacs-lisp

Emacs Lisp config, mainly to have a development experience that feels similar to using CIDER and Clojure. (show results in overlay, threading refactorings)

corgi-commands

The few custom commands that we ship with. This includes a few things we emulate from Spacemacs, and commands for jumping to the user’s init.el (this file, with `SPC f e i’), or opening the user’s key binding or signals file.

corgi-clojure

Extensive setup for a good Clojure experience, including clojure-mode, CIDER, and a modeline indicator that shows which REPLs your evaluations go to. Also contains `corgi/cider-pprint-eval-register’, bound to `,,’.

We also include clj-ns-name, which changes Clojure buffer names to their namespace name.

Babashka utility REPL

corgi/cider-jack-in-babashka starts a new bb process and connects to it, creating a REPL buffer called *babashka-repl*. This is meant as a project-independent long running utility REPL, so that you can always eval basic Clojure expressions. Whenever you are in a Clojure file and there is no project-specific connected REPL then evaluations will go to this Babashka REPL instead.

Modeline indicator

corgi/enable-cider-connection-indicator will add an indicator in the modeline showing you which Clojure REPL(s) if any evaluations will go to. It’s either a clj on a blue background, a cljs on a yellow background, or a bb on a green background. If you are linked to a REPL from another project then the project directory will be included.

Eval from register

Emacs has registers, named slots where you can put snippets of text. corgi/cider-pprint-eval-register leverages this, it lets you send code from a register to your REPL, and get the result pretty printed in a separate buffer.

This is bound to ,,, so e.g. if you have (kaocha.repl/run) in the k register, then ,,k in a Clojure buffer will run Kaocha on the current file.

There are two ways to leverage this, you can pre-set some registers in your init.el, like the Kaocha example, so you basically get your own shortcut commands. It’s also really useful to use Emacs’s copy-to-register in a more ad-hoc way. Say you are working on a function, and then you have a snippet that calls that function that you use to test it out. Copy the snippet to a register and you no longer need to jump back and forth.

corgi-stateline

Change the color of the modeline based on the Evil state (e.g. green when in insert state)

Corkey

Corkey is Corgi’s key binding system. It’s powerful and flexible, but does things quite differently from other Emacs configs, and we encourage you to familiarize yourself with its concepts.

Some of the goals of Corkey:

  • Have all bindings centralized in simple data files
  • Make it easy to add or override bindings in your config
  • Provide consistent bindings across modes (e.g. have the same key combination to “eval” something, regardless of the language)
  • Make it easy to customize these consistently, e.g. change the “eval” keybinding in one place and have it apply to all modes
  • Make it easy to share complete sets of bindings with others
  • Provide both vim-style state-specific bindings and “global” (any state) bindings
  • Have great which-key hints/descriptions for everything
  • Allow toggling all Corkey bindings on or off globally

Initializing Corkey

In the example config we’ve shown how to initialize Corkey:

(use-package corkey
  :config
  (corkey-mode 1)
  (corkey/load-and-watch))

This makes sure the corkey package is installed and loaded, it then enables the global corkey-mode, and sets up the built-in bindings by loading the default binding files.

Installing bindings

Before you use Corkey you need to load the set of key bindings it will use, it will then apply the right set of bindings depending on the major and minor modes active in a given buffer. This is done with corkey/load-and-watch, which sets up file watchers for the key binding and signal mapping files, so any changes in them are reflected immediately.

corkey/load-and-watch takes two optional arguments, a list of binding files, and a list of signal files, so (corgi/load-and-watch) is really just a shorthand for

(corgi/load-and-watch '(corgi-keys user-keys) '(corgi-signals user-signals))

These are references to EDN-like files. Corkey will try to look this up in your emacs-user-directory, and if not found there falls back to scanning the Emacs library path.

In other words: Corkey will look for corgi-keys.el in your Emacs config directory, and if it doesn’t find it there it will use the one provided by corgi-packages/corkey. The same goes for corgi-signals. This means that you can copy these files to your Emacs config directory and customize them there.

user-keys.el and user-signals.el are what goes into your own config, here you can add whatever extra bindings you like to have. The sample config has an example.

You can jump to all of these files with built-in commands

  • SPC f e k - Open user-keys, create it if it doesn’t exist
  • SPC f e s - Open user-signals, create it if it doesn’t exist
  • SPC f e K - Open corgi-keys (all built-in bindings)
  • SPC f e S - Open corgi-signals (all built-in signal mappings)

See the comments in those files for more info on how to set things up.

Differences from Vim

Generally we don’t override Evil’s keybindings, and Evil in turns emulates vim closely. Some differences

  • L and H move forward/backward by one s-expression, instead of moving to the beginning/end of the buffer
  • SPC and , are both used as leader/prefix keys. Press either and wait a bit to see what they can do.

Walkthrough of a Clojure session

When working on a Clojure project I will typically start by opening a .clj, .cljs, or .edn file, and “jacking-in” CIDER. I’ll either use SPC j j if it’s a plain Clojure project (just start a Clojure REPL), or SPC j a for both CLJ and CLJS (most CLJS projects I still like to have a CLJ REPL around).

Once that’s started you can jump back and forth to the REPL buffer with , s s. When you’re done you can close a single REPL with , s q, or all connected REPLs for this project with , s Q.

If you are running your nREPL sever outside of Emacs, then use , s c to connect to it.

To evaluate forms I mainly use , RET (evaluate outer form), and , e p (evaluate the form before the cursor, and pretty print the result to a separate buffer), but there are a slew of “eval” commands available.

Inside LISP buffers the L and H text objects come in handy, these jump back and forth across s-expressions, and can be combined with Vim commands, like dL (delete sexp) or 3yL (copy next 3 sexps).

, g g is a general binding to jump to an identifier’s definition, get used to using this a lot. This will also help you explore libraries you’re using. , g b will pop you back to where you came from.