About
This is my literate configuration for Emacs. This document includes a collection of configuration snippets to express how I personally like to use Emacs, each accompanied by some reasoning. I think it’s important to include reasoning for each part so I can understand why I use it. Plus the added benefit of others being able to peruse and borrow parts, just as I have from others.
The combination of literacy and functionality is achieved using the amazing org-mode, with org-babel.
Throughout this document, you’ll notice heavy use of the brilliant use-package.
For anyone who hasn’t tried out use-package
; I emplore you to do so - it truly makes managing your configuration an absolute joy.
General
This section covers many different types of configuration for native Emacs capabilities
Start the server
Start the Emacs server so other clients can connect and use the same session.
This is useful for when you may be oprating Emacs from the GUI usually, but want to use the same session from a TTY/terminal.
Also handy for when you have your EDITOR
set to emacsclient
.
(server-start)
Personal stuff
Pretty self explanatory: just setting some personal details about who’s using Emacs.
(setq user-full-name "Calum MacRae"
user-mail-address "hi@cmacr.ae")
Deactivation
Deactivation of functionality I don’t tend to use:
- Backup files
- Autosaving
- Start-up message
- Audible bell
(setq
make-backup-files nil
auto-save-default nil
inhibit-startup-message t
ring-bell-function 'ignore)
UTF-8
Configure Emacs for full UTF-8 compatability
(set-charset-priority 'unicode)
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(setq default-process-coding-system '(utf-8-unix . utf-8-unix))
:ensure
for use-package
statements
Global use-package
has an :ensure
keyword which dictates whether packages are installed or not.
As most of my use-package
configurations are for external packages, I set this to always ensure.
Then, in cases where I don’t want this to be true, I simply set :ensure nil
(setq use-package-always-ensure t)
Discard customizations
Emacs has a comprehensive customization system that allows configuration changes interactively.
Personally, I opt to ensure all the configuration I use for my environment is fully declarative.
As such, the following configuration sets the custom-file
to be a random temporary file created each time Emacs starts.
This means any customizations made interactively are discarded entirely.
(setq custom-file (make-temp-file ""))
Just use ‘y’ or ‘n’ instead of ‘yes’ or ‘no’
You’ll find yes-or-no
prompts coming up in Emacs a lot.
I’d much rather just type y
or n
than yes
or no
every time…
(fset 'yes-or-no-p 'y-or-n-p)
Set meta for Darwin systems
(cond
((string-equal system-type "darwin")
(setq mac-option-modifier 'meta)))
Set the scratch buffer string
Set the scratch buffer’s initial contents to include a comment with a timestamp of creation. Not really all that useful, but cleaner than the default comment, and I like having something there.
(setq initial-scratch-message (format ";; Scratch buffer - started on %s\n\n" (current-time-string)))
Confirm quit
This adds a confirmation prompt when quitting Emacs - because I’m only human.
(setq confirm-kill-emacs 'yes-or-no-p)
A few Darwin specific configurations
To make Emacs play a little nicer with window management, enable menu-bar-mode. Also, set the frame’s dimensions based on pixels - this makes Emacs play nicer with tiling window managers, where no title bar is displayed.
(cond
((string-equal system-type "darwin")
(menu-bar-mode t)
(setq frame-resize-pixelwise t)))
Follow symlinks in version control
If there are any symlinks in version controlled repositories, follow them
(setq vc-follow-symlinks t)
Use ‘root’ user by default for SSH connections using TRAMP
When connecting to a remote system over SSH via TRAMP, use the root
user by default
(set-default 'tramp-default-proxies-alist (quote ((".*" "\\`root\\'" "/ssh:%h:"))))
Set TRAMP shell prompt pattern (fix for some fancy prompts)
When connecting to some remote systems over SSH via TRAMP, you may run into some shells which use some different encoding for their prompt. This can result in a malformed prompt on the client side. This little snippet fixes that
(setq shell-prompt-pattern "\\(?:^\\|\r\\)[^]#$%>\n]*#?[]#$%>].* *\\(^[\\[[0-9;]*[a-zA-Z] *\\)*")
Set explicit shell binary
Set the filepath to the binary to run when invoking term
(or any of its siblings).
(setq explicit-shell-file-name "/run/current-system/sw/bin/zsh")
Use M-3 to insert an octothorp
I’m usually on a British keyboard, so when doing M-3
: insert an octothorp, not a GBP sign
(global-set-key (kbd "M-3") '(lambda () (interactive) (insert "#")))
Configure FlySpell to use aspell
I use aspell
, so this simply sets Flyspell to use it and passes a couple extra arguments
(setq ispell-program-name "aspell")
(setq ispell-extra-args '("--sug-mode=ultra" "--lang=en_GB"))
Kill term buffers upon exit
If I’m using an interactive terminal, it’s nice to just ^D
out of it and have the buffer disappear
(defadvice term-handle-exit
(after term-kill-buffer-on-exit activate)
(kill-buffer))
Calendar/Diary
Set the start of the week for the calendar to be Monday. Sort entries when viewing diary items.
(setq calendar-week-start-day 1)
(setq diary-file "~/org/diary")
(add-hook 'diary-list-entries-hook 'diary-sort-entries t)
IRC
Emacs comes with a great builtin IRC client: ERC.
These are some general settings that’re all pretty self explanatory: hide particular activity, autojoin channels for particular servers.
For convenience, I’ve also defined a erc-conn
function for my usual connection parameters.
(use-package erc
:ensure nil
:config
(setq erc-hide-list '("PART" "QUIT" "JOIN"))
(setq erc-autojoin-channels-alist '(("freenode.net"
"#lobsters"
"#nixos"
"#nix-darwin"))
erc-server "irc.freenode.net"
erc-nick "cmacrae"))
(defun cm/erc-conn ()
(interactive)
(erc-tls :server "irc.freenode.net" :port 6697 :nick "cmacrae"))
Packages
This section covers external packages I use and their configuration, in no particular order
Ivy|Counsel|Swiper
Absolutely brilliant interactive interface and completion frameworks.
These packages improve the Emacs experience so much.
As you can see from the :bind
sections, I use these to replace some of the most used actions.
Ivy
- Suppress count visibility for
ivy-read
- Set initial input chars to
nil
- Provide
insert
andyank
options for candidates - Display the candidate menu at the current point position with
ivy-posframe
- Add some graphical niceties with
ivy-rich
(use-package ivy
:hook (after-init . ivy-mode)
:preface
(defun ivy-yank-action (x)
(kill-new x))
(defun ivy-copy-to-buffer-action (x)
(with-ivy-window
(insert x)))
:bind
("C-s" . swiper)
("M-x" . counsel-M-x)
("C-x C-f" . counsel-find-file)
:config
(setq ivy-count-format ""
ivy-initial-inputs-alist nil)
(ivy-set-actions t
'(("i" ivy-copy-to-buffer-action "insert")
("y" ivy-yank-action "yank"))))
(use-package ivy-posframe
:after ivy
:config
(set-face-background 'ivy-posframe-border "#51afef")
(setq ivy-posframe-border-width 1
ivy-posframe-parameters '((left-fringe . 8) (right-fringe . 8))
ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-point)
(swiper . nil)))
(ivy-posframe-mode 1))
(use-package ivy-rich
:after counsel
:config (setq ivy-rich-path-style 'abbrev)
:init (ivy-rich-mode 1))
Counsel
- Set a prettier candidate delimiter for killring
- Bind common functions
- Bind common org functions
- Ensure `smex` is installed for better candidate matching
(use-package counsel
:init
(setq counsel-yank-pop-separator
(concat "\n\n"
(concat (apply 'concat (make-list 50 "---")) "\n")))
:bind (
("M-y" . counsel-yank-pop)
("C-h f" . counsel-describe-function)
("C-h v" . counsel-describe-variable)
:map org-mode-map
("C-c C-j" . counsel-org-goto)
("C-c C-q" . counsel-org-tag))
:config
(use-package smex :ensure t))
Magit
The one true Git porcelain! Truely a joy to use - it surfaces the power of Git in such a fluent manner. Anyone using Git and Emacs needs Magit in their life!
(use-package magit
:bind ("C-c m" . magit-status)
:init
(setq magit-completing-read-function 'ivy-completing-read))
git-link
Create & yank URLs for popular git forges based on current file/buffer location. Handy for collaborating.
(use-package git-link
:bind
("C-c g l" . git-link))
Projectile
Project management based on version control repositories. Absolutely essential package for me. This makes hopping around and between various projects really easy. Not only that, but it allows project-wide actions. Like killing all buffers for a project, performing a project-wide find-and-replace, or a grep, etc.
Some configuration I use:
- Setting the completion system to
ivy
- Adding an action to invoke
neotree
upon switching projects
(use-package projectile
:init
(setq projectile-completion-system 'ivy)
(setq projectile-switch-project-action 'neotree-projectile-action)
:config
(projectile-global-mode))
counsel-projectile
Further integration of Counsel with Projectile than what’s provided natively.
As I use counsel-projectile-on
to remap a bunch of Projectile’s functions to their Counsel equivilents, but I want to use
Perspective functionality, I remap projectile-switch-project
, after counsel-projectile-on
has been called, to projectile-persp-switch-project
.
This then masks counsel-projectile-switch-project
and integrates Perspective when switching projects.
(use-package counsel-projectile
:bind
("C-c p s r" . counsel-projectile-rg)
(:map projectile-mode-map
("C-c p p" . projectile-persp-switch-project)
("C-c p f" . projectile-find-file))
:init
(counsel-projectile-mode))
Perspective
Workspaces! Indespensible if you work on a lot of projects. Perspective is like workspaces (virtual desktops) for Emacs. It’s a means of namespacing a group of tangible buffers. When combined with Projectile, this becomes a really nice combination as projects then seemlessly translate to workspaces.
Here, I’ve defined a cm/persp-neo
function for use with persp-switch-hook
. This makes NeoTree follow the perspective when switching.
I’ve also added a hydra for various Perspective actions.
(use-package perspective
:init (persp-mode)
:config
(defun cm/persp-neo ()
"Make NeoTree follow the perspective"
(interactive)
(let ((cw (selected-window))
(path (buffer-file-name))) ;; save current window and buffer
(progn
(when (and (fboundp 'projectile-project-p)
(projectile-project-p)
(fboundp 'projectile-project-root))
(neotree-dir (projectile-project-root)))
(neotree-find path))
(select-window cw)))
:hook
(persp-switch . cm/persp-neo))
(use-package persp-projectile
:after (perspective)
:bind
("C-c x" . hydra-persp/body)
:config
(defhydra hydra-persp (:columns 4
:color blue)
"Perspective"
("a" persp-add-buffer "Add Buffer")
("i" persp-import "Import")
("c" persp-kill "Close")
("n" persp-next "Next")
("p" persp-prev "Prev")
("k" persp-remove-buffer "Kill Buffer")
("r" persp-rename "Rename")
("A" persp-set-buffer "Set Buffer")
("s" persp-switch "Switch")
("C-x" persp-switch-last "Switch Last")
("b" persp-switch-to-buffer "Switch to Buffer")
("P" projectile-persp-switch-project "Switch Project")
("q" nil "Quit")))
NeoTree
Sidebar filebrowser, very handy. People seem to have accepted Treemacs as the new norm, but I like NeoTree :) Here, I’ve defined some key mappings that make it a little nicer to interact with - they should be quite self-explanatory.
(use-package neotree
:bind
("C-;" . neotree-show)
("C-c C-;" . neotree-toggle)
(:map neotree-mode-map
("C-c C-h" . neotree-hidden-file-toggle)
("C-c C-y" . neotree-copy-filepath-to-yank-ring)
("C-;" . (lambda () (interactive) (select-window (previous-window)))))
:config
(setq neo-theme (if window-system 'icons 'arrows)))
popwin
Some windows in Emacs can be quite obtrusive. popwin
aims to manage this.
By using popwin
windows that could be deemed “temporary” only take up a small amount of realestate, which is reclaimed upon said window closing.
This is handy for things like grep
results, help/compile buffers, etc.
You can also define your own “pop-up” actions. As you can see here, I’ve defined a little “pop-up” terminal. This will spawn a little terminal buffer at the top of my Emacs frame. Then, when I’m done with it and I exit the process/kill the buffer, the space is automatically reclaimed.
(use-package popwin
:defer 1
:bind
("C-x t" . cm/popwin-term)
:config
(setq display-buffer-function 'popwin:display-buffer)
(defun cm/popwin-term ()
(interactive)
(popwin:display-buffer-1
(or (get-buffer "*terminal*")
(save-window-excursion
(call-interactively 'term)))
:default-config-keywords '(:position :top))
(provide 'popwin-term))
;; Go direx
(push '("^\*go-direx:" :regexp t :position right :width 0.4 :dedicated t :stick t)
popwin:special-display-config))
Flycheck
Have Flycheck turned on for everything - checking stuff is always good!
And for convenience, add a posframe
.
(use-package flycheck
:hook
(after-init . global-flycheck-mode))
(use-package flycheck-posframe
:after flycheck
:hook (flycheck-mode . flycheck-posframe-mode))
company-mode
Slick auto-complete framework
(use-package company
:hook (prog-mode . company-mode))
ace-window
Jump around Emacs windows & frames using character prefixes. I use this constantly - it even works across multiple frames. Also added a hydra borrowed from here for some really convenient movement/manipulation!
(use-package ace-window
:bind ("M-o" . hydra-window/body)
:config
(setq aw-dispatch-always t)
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
(defhydra hydra-window (:color blue)
"window"
("h" windmove-left "left")
("j" windmove-down "down")
("k" windmove-up "up")
("l" windmove-right "right")
("a" ace-window "ace")
("s" (lambda () (interactive) (ace-window 4)) "swap")
("d" (lambda () (interactive) (ace-window 16)) "delete")
("q" nil "Quit")))
Smartparens
Brilliant automatic balancing of pairs. Makes for a really nice experience when typing in any language - programming or not. Just check out some of the gifs in the project’s README.
(use-package smartparens
:config
(progn
(smartparens-global-mode)
(show-smartparens-global-mode t)))
erc-hl-nicks
Nickname highlighting for ERC (IRC in Emacs)
(use-package erc-hl-nicks)
GitGutter
Hints and actions in the buffer/fringe for bits being followed by Git. The configuration bellow gives little diff highlights in the fringe for changes.
(use-package git-gutter
:init
(setq
git-gutter:modified-sign " "
git-gutter:added-sign " "
git-gutter:deleted-sign " ")
(global-git-gutter-mode t)
:hook
(window-setup . (lambda ()
(set-face-background 'git-gutter:modified "#da8548")
(set-face-background 'git-gutter:added "#98be65")
(set-face-background 'git-gutter:deleted "#ff6c6b"))))
YAML & Ansible
YAML’s great - so support is obviously nice to have.
I also spend quite a bit of my time working with Ansible. ansible-doc
is a handy little package to pull up Ansible module documentation within Emacs.
I’ve bound C-c h a
for the YAML mode keymap to spawn ansible-doc
(use-package ansible-doc)
(use-package yaml-mode
:bind (:map yaml-mode-map
("C-c h a" . ansible-doc)))
TOML mode
Simply to support TOML configurations
(use-package toml-mode)
Set exec/man PATH from shell
When looking for executables/man-pages, Emacs will inherit these properties from the OS environment. This package provides the ability to do so from the user’s shell, where they may have some more complex logic to determine such paths.
(use-package exec-path-from-shell
:config
(setq exec-path-from-shell-check-startup-files nil)
(exec-path-from-shell-initialize)
(exec-path-from-shell-copy-env "SSH_AGENT_PID")
(exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
Expand region
Select regions by semantic units. Really handy for selecting regions of data - just repeat keypress to expand selection further.
(use-package expand-region
:bind ("C-=" . er/expand-region))
json-mode
No reasoning needed here! Everyone needs JSON
(use-package json-mode)
Aggressive indent
Keeps code indented when making disruptive changes
(use-package aggressive-indent
:config
(global-aggressive-indent-mode 1))
MoveText
Easily move text up and down. I’ve tied this into a little hydra for more natural repeated movement.
(use-package move-text
:bind ("C-c t" . hydra-move-text/body)
:config
;; Move Text
(defhydra hydra-move-text ()
"Move text"
("k" move-text-up "Up")
("j" move-text-down "Down")
("q" nil "Quit" :color blue)))
Docker Integration
Various docker integrations:
dockerfile-mode
is pretty self explanatorydocker-tramp
allows TRAMP connections into running containersdocker
, with a hydra, allows for interaction with the Docker distribution
(use-package dockerfile-mode
:mode "\\Dockerfile\\'")
(use-package docker-tramp)
(use-package docker
:bind ("C-c d" . hydra-docker/body)
:config
(defhydra hydra-docker (:columns 5 :color blue)
"Docker"
("c" docker-containers "Containers")
("v" docker-volumes "Volumes")
("i" docker-images "Images")
("n" docker-networks "Networks")
("b" dockerfile-build-buffer "Build Buffer")
("q" nil "Quit")))
Kubernetes Integration
Integrates general purpose Kubernetes operations as a porcelain
(use-package kubernetes
:bind ("C-c k" . hydra-kube/body)
:commands (kubernetes-overview)
:config
(defhydra hydra-kube (:columns 5 :color blue)
"Kubernetes"
("o" kubernetes-overview "Overview")
("c" kubernetes-config-popup "Config")
("e" kubernetes-exec-popup "Exec")
("l" kubernetes-logs-popup "Logs")
("L" kubernetes-labels-popup "Labels")
("d" kubernetes-describe-popup "Describe")))
(use-package kubernetes-evil
:after kubernetes)
Corral
Quickly surround text with delimiters, along with a hydra
(use-package corral
:bind
("M-9" . corral-parentheses-backward)
("M-0" . corral-parentheses-forward)
("M-[" . corral-brackets-backward)
("M-]" . corral-brackets-forward)
("M-{" . corral-braces-backward)
("M-}" . corral-braces-forward)
("M-\"" . corral-double-quotes-backward)
("C-c v" . hydra-corral/body)
:config
(setq corral-preserve-point t)
(defhydra hydra-corral (:columns 5)
"Corral"
("(" corral-parentheses-backward "Back")
(")" corral-parentheses-forward "Forward")
("[" corral-brackets-backward "Back")
("]" corral-brackets-forward "Forward")
("{" corral-braces-backward "Back")
("}" corral-braces-forward "Forward")
("\"" corral-double-quotes-backward "Back")
("'" corral-single-quotes-backward "Back")
("." hydra-repeat "Repeat")))
Focus
Makes the current function at the point the only syntax-highlighted construct in the buffer. All other buffer contents are “subdued” to look like comments.
(use-package focus)
Dumb Jump
Jump to definitions
(use-package dumb-jump
:bind
("C-c j" . hydra-dumb-jump/body)
:config
(setq dumb-jump-selector 'ivy)
(defhydra hydra-dumb-jump (:color blue)
"Dumb Jump"
("g" dumb-jump-go "Jump to def")
("p" dumb-jump-back "Jump back")
("q" dumb-jump-quick-look "Quick look")
("o" dumb-jump-go-other-window "Jump in other window")
("q" nil "Quit")))
undo-tree
Powerful undo actions formulated in a tree structure
(use-package undo-tree
:config
(global-undo-tree-mode))
ivy-pass & auth-password-store
I use pass to manage my passwords. This is a handy little package for interfacing with it.
(use-package ivy-pass
:init (setq password-store-password-length 30)
:bind ("C-c M-p" . ivy-pass))
And this package allows it to act as an auth-source
(use-package auth-source-pass
:config (auth-source-pass-enable))
Nix
Various packages for working with Nix
Turn off aggressive-indent-mode
as it doesn’t play nice.
(use-package nix-mode
:init (setenv "NIX_REMOTE" "daemon")
:hook
(nix-mode . (lambda ()
(when (and (stringp buffer-file-name)
(string-match "\\.nix\\'" buffer-file-name))
(aggressive-indent-mode 0)))))
Configure company-mode
completions for NixOS options.
(use-package nixos-options)
(use-package company-nixos-options
:hook
(nix-mode . (lambda ()
(set (make-local-variable 'company-backends) '(company-nixos-options))
(company-mode))))
restclient
REST client for Emacs! Really cool package. Kinda like Postman/Insomnia.
(use-package restclient
:mode ("\\.http\\'" . restclient-mode))
Note/TODO highlighting
It’s nice to have some note/todo highlighting :)
(use-package hl-todo
:config
(global-hl-todo-mode)
:hook
(yaml-mode . hl-todo-mode))
ivy-lobsters
That’s right, I’m a crustacean 🦀
(use-package ivy-lobsters)
discover-my-major
A great little package to help discover more about the current major mode.
(use-package discover-my-major
:bind ("C-h C-m" . hydra-discover/body)
:config
(defhydra hydra-discover(:color blue)
"Discover"
("m" discover-my-major "Major")
("M" discover-my-mode "Mode")
("q" nil "Quit" :color blue)))
define-word
Display the definition of word at the point, nice!
(use-package define-word)
vterm
Fully-fledged terminal emulator based on libvterm! I manage the module and elisp as a Nix overlay in my system configuration, so no need to install it. Set it up to play nice with Evil.
(use-package vterm
:ensure nil
:after evil
:hook
(vterm-mode . (lambda ()
(setq-local evil-insert-state-cursor 'hbar)
(evil-insert-state)))
:config
(define-key vterm-mode-map [return] #'vterm-send-return)
(setq vterm-keymap-exceptions nil)
(evil-define-key 'insert vterm-mode-map (kbd "C-e") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-f") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-a") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-v") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-b") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-w") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-u") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-d") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-n") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-m") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-p") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-j") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-k") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-r") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-t") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-g") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-c") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-SPC") #'vterm--self-insert)
(evil-define-key 'insert vterm-mode-map (kbd "C-y") #'vterm-yank)
(evil-define-key 'normal vterm-mode-map (kbd "C-d") #'vterm--self-insert)
(evil-define-key 'normal vterm-mode-map (kbd "p") #'vterm-yank)
(evil-define-key 'normal vterm-mode-map (kbd "i") #'evil-insert-resume)
(evil-define-key 'normal vterm-mode-map (kbd "o") #'evil-insert-resume)
(evil-define-key 'normal vterm-mode-map (kbd "<return>") #'evil-insert-resume))
centaur-tabs
Fancy buffer tabs ✨
- Activate after init
- Disable for a few modes
- Set the header face to fit better
- Set tab grouping to work with Projectile
- Bottom bar style tab indicator
- Turn on icons
- Set the cycle scope to current project tabs
- Bind “K”/”J” in normal Evil state to move forward/backward
(use-package centaur-tabs
:hook
(after-init . centaur-tabs-mode)
(vterm-mode . centaur-tabs-local-mode)
(calendar-mode . centaur-tabs-local-mode)
(org-agenda-mode . centaur-tabs-local-mode)
(helpful . centaur-tabs-local-mode)
:config
(centaur-tabs-headline-match)
(centaur-tabs-group-by-projectile-project)
:custom
(centaur-tabs-style "bar")
(centaur-tabs-set-bar 'under)
(centaur-tabs-set-icons t)
(centaur-tabs-cycle-scope 'tabs)
:bind
(:map evil-normal-state-map
("K" . centaur-tabs-forward)
("J" . centaur-tabs-backward)))
Hydras
Great package to tie tangible actions together into convenient keybinding landscapes. Here, you’ll find some “general” hydras - other hydras that are centric around packages will be found with that package’s configuration.
General hydras:
- Zoom: increase/decrease current buffer text size
- Transpose: transpose various constructs of text
- Toggle mode: turn frequently “toggled” modes on and off
Enhancement packages:
hydra-posframe
: useposframe
to display hydra buffers at custom positions NOTE: This package is not currently available on MELPA. There’s an open issue to get it added: Ladicle/hydra-posframe#3
(use-package hydra
:bind
("C-c z" . hydra-zoom/body)
("C-c T" . hydra-transpose/body)
("C-c M" . hydra-toggle-mode/body)
:config
;; Zoom
(defhydra hydra-zoom ()
"Zoom"
("i" text-scale-increase "In")
("o" text-scale-decrease "Out")
("q" nil "Quit" :color blue))
;; Transpose
(defhydra hydra-transpose (:color red)
"Transpose"
("c" transpose-chars "Characters")
("w" transpose-words "Words")
("l" transpose-lines "Lines")
("s" transpose-sentences "Sentences")
("p" transpose-paragraphs "Paragraphs")
("q" nil "Quit" :color blue))
;; Toggle mode
(defhydra hydra-toggle-mode (:color blue)
"Toggle"
("c" centered-window-mode "Centered Buffer")
("w" whitespace-mode "Whitespace")
("f" focus-mode "Focus")
("i" aggressive-indent-mode "Aggressive indent")
("s" flyspell-mode "FlySpell")
("S" flyspell-prog-mode "FlySpell Prog")
("q" nil "Quit")))
;; TODO: [hydra/posframe] Waiting for MELPA package
;; https://github.com/Ladicle/hydra-posframe/issues/3
;; (use-package hydra-posframe
;; :hook (after-init . hydra-posframe-enable))
Evil
Vim emulation in Emacs. Because: yes, you can have the best of both worlds!
Below you’ll find various extensions to my Evil layer that generally improve the quality of life. This first configuration block is simply to turn Evil on at start and add some NeoTree bindings for compatability.
(use-package evil
:init
(setq evil-want-C-u-scroll t)
(evil-mode)
:config
(evil-define-key 'normal neotree-mode-map (kbd "TAB") 'neotree-enter)
(evil-define-key 'normal neotree-mode-map (kbd "SPC") 'neotree-quick-look)
(evil-define-key 'normal neotree-mode-map (kbd "q") 'neotree-hide)
(evil-define-key 'normal neotree-mode-map (kbd "RET") 'neotree-enter))
Compatibility
Make some things play nicer with Evil
Magit
(use-package evil-magit)
smartparens
(use-package evil-smartparens
:hook
(smartparens-enabled . evil-smartparens-mode))
Org
(use-package evil-org
:after (org)
:hook
((org-mode . evil-org-mode)
(evil-org-mode . (lambda ()
(evil-org-set-key-theme)))))
Surround
Easily surround, emulating surround.vim
(use-package evil-surround
:config
(global-evil-surround-mode 1))
Goggles
Visual hints when performing Evil operations (dd
, yy
, cw
, p
, etc.)
(use-package evil-goggles
:config
(evil-goggles-mode)
(evil-goggles-use-diff-faces))
Lion
Align operators (gl
& gL
), emulating lion.vim
(use-package evil-lion
:config
(evil-lion-mode))
Traversal
EasyMotion
Buffer traversal made easy! Emulates easymotion.vim
(use-package evil-easymotion
:config
(evilem-default-keybindings "SPC"))
Snipe
2-char searching with f
, F
, t
, T
operators. Like seek.vim/sneak.vim
(use-package evil-snipe
:after (evil-quickscope)
:config
(evil-snipe-mode 1)
(evil-snipe-override-mode 1))
Quickscope
Highlight targets for f
, F
, t
, T
operators. Emulates quick_scope.vim
(use-package evil-quickscope
:config
(global-evil-quickscope-mode 1))
Commentary
Easily comment lines/blocks. Emulates commentary.vim
(use-package evil-commentary
:config
(evil-commentary-mode))
Exchange
Exchange operator for exchanging constructs of text. Emulates exchange.vim
(use-package evil-exchange
:config
(evil-exchange-install))
Multiple Cursors
Having multiple cursors can be very powerful. This allows you to perform simultaneous actions at multiple positions within the buffer.
(use-package evil-multiedit
:config
(evil-multiedit-default-keybinds)
(evil-ex-define-cmd "ie[dit]" 'evil-multiedit-ex-match))
Custom functions
Useful functions gathered that don’t quite require an entire package.
Sort words
Taken from here; just a handy little function to sort words in a region alphabetically
(defun cm/sort-words (reverse beg end)
"Sort words in region alphabetically, in REVERSE if negative.
Prefixed with negative \\[universal-argument], sorts in reverse.
The variable `sort-fold-case' determines whether alphabetic case
affects the sort order.
See `sort-regexp-fields'."
(interactive "*P\nr")
(sort-regexp-fields reverse "\\w+" "\\&" beg end))
Sensible beginning of line
Taken from here, I use this to replace move-beginning-of-line
(C-a
).
It will take your point back to the first column of the line you’re on, as per the indentation.
A second press will then take your point back to the very beginning of the line.
Pressing again will take you back to the indented column.
(defun cm/sensible-move-beginning-of-line (arg)
"Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first. If
point reaches the beginning or end of the buffer, stop there."
(interactive "^p")
(setq arg (or arg 1))
;; Move lines first
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
(global-set-key [remap move-beginning-of-line]
'cm/sensible-move-beginning-of-line)
Yank filename
Simple little function to copy the current filename to the clipboard.
(defun cm/yank-filename ()
"Copy the current buffer file name to the clipboard."
(interactive)
(let ((filename (if (equal major-mode 'dired-mode)
default-directory
(buffer-file-name))))
(when filename
(kill-new filename)
(message "Copied buffer file name '%s' to the clipboard." filename))))
Load theme
Fully unloads the current theme and loads the new one of choice. This is handy to have as loading a theme over another can leave behind bad faces.
(defun cm/switch-theme (theme)
"Disable active themes and load THEME."
(interactive (list (intern (completing-read "Theme: "
(->> (custom-available-themes)
(-map #'symbol-name))))))
(mapc #'disable-theme custom-enabled-themes)
(load-theme theme 'no-confirm))
New blog post
A convenience function to create a new Org file suited to blogging with Hugo.
(defvar cm/blog-location)
(defvar cm/blog-project)
(setq cm/blog-location (concat (getenv "HOME") "/dev/cmacr.ae/content/post"))
(setq cm/blog-project "~/dev/cmacr.ae")
(defun cm/new-blog-post ()
"Set up a new blog post file & buffer."
(interactive)
(setq today (format-time-string "%Y-%m-%d")
title (read-string "Title: ")
filename (replace-regexp-in-string " " "-" (downcase title)))
(write-region
(format "#+date: %s\n#+title: %s\n#+tags[]: %s\n\n"
today
title
(read-string "Tags: "))
nil
(format "%s/%s"
cm/blog-location
(concat
(format "%s-" today)
(format "%s.org" filename)))
;; Don't automatically overwrite existing file
nil nil nil t)
(projectile-persp-switch-project cm/blog-project)
(find-file (format "%s/%s-%s.org" cm/blog-location today filename))
(cm/persp-neo)
(company-mode)
(company-emoji-init)
(emojify-mode)
(end-of-buffer)
(centered-window-mode)
(delete-other-windows))
Appearance
Configuration related to the appearance of Emacs
Hide stuff
Hide various elements of the Emacs GUI:
- toolbar
- tooltips
- scrollbar
- menubar
- blinking cursor
- macOS titlebar (transparent)
- frame title
(dolist (mode
'(tool-bar-mode
tooltip-mode
scroll-bar-mode
blink-cursor-mode))
(funcall mode 0))
(cond
((string-equal system-type "darwin")
(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))))
(setq frame-title-format '(""))
Fringes
Fringes always looked too fat to me by default, and take up too much space. This just makes them a bit thinner and turns the fringe off completely where I don’t feel it’s necessary.
(fringe-mode '(4 . 0))
(defun cm/hide-fringes ()
(set-window-fringes (selected-window) 0 0))
(add-hook 'eshell-mode 'cm/hide-fringes)
Centered buffers
A really simple package that will centre your buffer contents in the frame.
Purely cosmetic, but I do find it helps with focus from time to time.
If I’m working on something that only needs one buffer, I’ll usually centre it.
I have this bound to a key in my toggle-mode
hydra so I can switch it on/off easily.
(use-package centered-window)
Current line highlighting
Highlights the current line of the point. Just helps to visualise where you are in the buffer. I turn it on globally, but explicitly turn it off where I don’t deem it necessary.
(global-hl-line-mode t)
(make-variable-buffer-local 'global-hl-line-mode)
(defvar my-ghd-modes '(
shell-mode-hook
git-commit-mode-hook
term-mode-hook
)
"Modes to ensure global-hl-line-mode is disabled for.")
(dolist (m my-ghd-modes)
(add-hook m (lambda () (setq global-hl-line-mode nil))))
Indent guides
Cool little package to provide indentation guides.
This will display a line of |
characters with a comment face to indicate the indentation of the current block.
(use-package highlight-indent-guides
:hook
(prog-mode . highlight-indent-guides-mode)
:config
(setq highlight-indent-guides-auto-odd-face-perc 15
highlight-indent-guides-auto-even-face-perc 15
highlight-indent-guides-auto-character-face-perc 20
highlight-indent-guides-responsive 'stack
highlight-indent-guides-method 'character))
Rainbow Delimiters
So handy! This will colourize delimiters differently based on their depth. Really helps you not get burried when you’re in deep.
(use-package rainbow-delimiters
:hook
(prog-mode . rainbow-delimiters-mode)
(yaml-mode . rainbow-delimiters-mode))
All the icons
Fancy! Just a bit of extra prettiness. This places little glyphs around to better convey some things where text may be a bit cluttered. That, and it makes things look nice! We’re visual creatures, after-all.
In this first block, I’ve added a conditional call to the downloading of the all-the-icons
font, based on the OS environment.
(use-package all-the-icons
:init
(cond
((string-equal system-type "darwin")
(if (not
(file-exists-p (concat (getenv "HOME") "/Library/Fonts/all-the-icons.ttf")))
(all-the-icons-install-fonts "t")))))
Dired
Makes dired
buffers a little more easy on the eyes.
Actually very helpful when trying to pick some files out manually.
(use-package all-the-icons-dired
:hook
(dired-mode . all-the-icons-dired-mode))
Ivy
Icons in some ivy
operations (file icons in counsel-find-file
, etc.)
(use-package all-the-icons-ivy
:hook (after-init . all-the-icons-ivy-setup)
:init
(setq all-the-icons-ivy-buffer-commands '())
(setq all-the-icons-ivy-file-commands
'(counsel-find-file
counsel-file-jump
counsel-recentf
counsel-projectile-find-file
counsel-projectile-find-dir)))
(use-package all-the-icons-ivy-rich
:init (all-the-icons-ivy-rich-mode 1))
Theme
Fashion First!
Right now, I’m using the beautiful doom-one
& doom-solarized-light
themes from
hlissner’s doom-themes.
They’re high contrast, and easy on the eyes, and right enough to easily distinguish
between different constructs, but not sickening.
(use-package doom-themes
:init
(setq doom-themes-enable-bold t
doom-themes-enable-italic t
doom-themes-neotree-file-icons t
doom-one-brighter-comments t)
(load-theme 'doom-solarized-light t)
(doom-themes-neotree-config))
Modeline
The ever important modeline! Making your modeline look good and express useful information is vital, in my opinion. There’s a lot of info you can cram in there - but to do so tastefully and efficiently is key.
(use-package doom-modeline
:hook (after-init . doom-modeline-mode)
:config
(setq doom-modeline-persp-name nil
doom-modeline-buffer-encoding nil
doom-modeline-icon t
doom-modeline-buffer-file-name-style 'truncate-with-project))
Make focussed & file visiting buffers stand out
The following expression adds a little flair to focussed buffers and those visiting files. I have it activate upon visiting files and after switching perspectives.
(use-package solaire-mode
:init
(advice-add #'persp-load-state-from-file :after #'solaire-mode-restore-persp-mode-buffers)
:hook
(after-change-major-mode . turn-on-solaire-mode)
:config
(solaire-mode-swap-bg))
(use-package dimmer
:hook (after-init . dimmer-mode)
:config
(dimmer-configure-hydra)
(dimmer-configure-magit)
(dimmer-configure-org)
(dimmer-configure-posframe))
Font
Some configuration for fonts
Emoji
Because this is the world we live in: don’t hate, appreciate! Emojis can be fun in READMEs (and maybe Git commits where machine readability doesn’t matter all that much)
(use-package company-emoji
:hook
((markdown-mode . company-mode)
(git-commit-mode . company-mode))
:config
(add-to-list 'company-backends 'company-emoji))
(use-package emojify
:hook
((markdown-mode . emojify-mode)
(git-commit-mode . emojify-mode)
(magit-status-mode . emojify-mode)
(magit-log-mode . emojify-mode)))
Language Config
Configuration specific to languages I tend to use
Language Server Protocol
Serious “IDEness”…
(use-package lsp-mode
:ensure t
:commands (lsp lsp-deferred)
:hook (go-mode . lsp-deferred)
:config
;; Performance tweaks
(setq gc-cons-threshold 100000000)
(setq read-process-output-max (* 1024 1024))
;; Set up before-save hooks to format buffer and add/delete imports.
(defun lsp-go-install-save-hooks ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t))
(add-hook 'go-mode-hook #'lsp-go-install-save-hooks))
(use-package lsp-ui :commands lsp-ui-mode)
(use-package company
:config
(setq company-idle-delay 0)
(setq company-minimum-prefix-length 1))
(use-package company-lsp :commands company-lsp)
(use-package lsp-ivy :commands lsp-ivy-workspace-symbol)
Rego
(use-package rego-mode)
Markdown
Markdown compatability. Activate markdown-mode
for .md
files and turn on flyspell
(use-package markdown-mode
:mode "\\.md\\'"
:hook
(markdown-mode . flyspell-mode))
Jinja2
Jinja2 compatability. Activate jinja2-mode
for .j2
files
(use-package jinja2-mode
:mode "\\.j2\\'")
JavaScript
JavaScript compatability. Activate js2-mode
for .js
files
(use-package js2-mode
:mode "\\.js\\'")
HashiCorp
Compatability with HCL
and Terraform syntax.
Activate hcl-mode
for .nomad
files.
(use-package hcl-mode
:mode "\\.nomad\\'")
(use-package terraform-mode
:hook
(terraform-mode . company-mode)
(terraform-mode . (lambda ()
(when (and (stringp buffer-file-name)
(string-match "\\.tf\\(vars\\)?\\'" buffer-file-name))
(aggressive-indent-mode 0))))
(before-save . terraform-format-buffer))
Org Config
Configuration for the brilliant Org mode!
General
- A few global keybindings for captures, agenda, etc.
- Turn on flyspell mode
- Follow filesystem links for Org files
- Agenda files directory
- Custom capture templates
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)
(use-package org-mode
:ensure nil
:hook (org-mode . flyspell-mode)
:config
(setq org-return-follows-link t
org-src-fontify-natively t
org-agenda-files '("~/org")
org-capture-templates
'(("t" "Todo" entry (file+headline "~/org/inbox.org" "Tasks")
"* TODO %^{Brief Description} %^g\n%?\tAdded: %U")
("r" "ToRead" entry (file+headline "~/org/inbox.org" "Tasks")
"* TOREAD %^{Title} %^g\n%?\tLink: %c")
("p" "Project" entry (file+headline "~/org/inbox.org" "Projects")
"* %^{Brief Description} %^g\n%?\tAdded: %U")
("m" "Maybe" entry (file+headline "~/org/inbox.org" "Maybe/Some Day")
"* %^{Brief Description} %^g\n%?\tAdded: %U"))))
org-bullets
Make Org headings look a bit fancier
(use-package org-bullets
:hook
(org-mode . (lambda () (org-bullets-mode 1))))