Complete Computing Environment: Programming in Emacs

Table of Contents

Code is my creative outlet. I use it as a way to express myself, as a way to unwind my thoughts in to reality and as a way to build myself a future worth living. My code comes in many forms and serves many purpose. The act of programming, of writing code, is liberating, is freeing, despite the limitations and struggles of using a computer.

I spend a lot of time exploring and dabbling in my free time. I have a wealth of exploratory projects, things built on new ideas and new languages, play toys and future business ideas. These are singular acts of creation, my own children and creations, shared with the people around me as explanations and instructions to understanding my way of grokking the world. My tools should enable the creation of pet projects, the curation of their code, and the insurance of their completion, regardless of the language.

More than on personal projects, though, I spend time transforming the code others have written. This is the bulk of the code I write as my day job, transformations of existing code that is not my own and that I may not yet have the full understanding of. My editor should enable and embrace refactoring and extending existing code. It should make introspecting and exploring code bases easy and intuitive.

My projects are all version controlled using Git. Exploring the history of a project, gaining points of reference within codebases, these are core parts of my learning model in a new code base; who owns what, and how do they think? My tools should make it easy to answer these questions, and should allow me to manipulate the repository myself, be that rewriting history, or simply committing new code.

(provide 'cce-code-core)

Core Programming Environment Setup

There is some base-level tooling required for me to get my job done as a programmer, let's install those tools here:

- name: git installed
    state: installed
    name: git
  when: ansible_pkg_mgr == "apt"

- name: git installed
    state: installed
    name: git
  when: ansible_pkg_mgr == "dnf"

I only use line numbering when I'm programming, let's turn it on! I turn off eager loading and delay the rendering of this because for big files, because it can get really damn slow.

(setq linum-delay t
      linum-eager nil)
(add-hook 'prog-mode-hook 'linum-mode)

Install some one-off modes that auto-enable when we need them and need no configuration.

(install-pkgs '(jinja2-mode

Aggressive Indent makes sure your code is always properly indented. Paired with DTRT, we end up with the ability to never actually have to care about our code indentation, as it should be.

(install-pkgs '(aggressive-indent))
(eval-after-load 'aggressive-indent-mode (lambda ()
                                           (diminish 'aggressive-indent-mode "⮕")))

(defun rrix/re-indent ()
  (unless (or (eq major-mode
              (eq major-mode
    (local-set-key (kbd "RET") 'reindent-then-newline-and-indent)))
(add-hook 'prog-mode-hook 'rrix/re-indent)

Fix ediff-mode to not mess up windows and confuse EXWM all the time.

(setq ediff-window-setup-function 'ediff-setup-windows-plain
      ediff-split-window-function 'split-window-horizontally)
(install-pkgs '(company))

I use company for more than just programming completions (see 💕=company-emoji=💕, f.e.), so let's just enable it everywhere.

(global-set-key (kbd "C-TAB") 'company-complete)

Language Support

Base LSP

I am going to try to use the Language Server Protocol support for Emacs as that seems like it's becoming well-supported enough to try integrating. Simple to install, for the most part, but some of the language servers are going to Require Some Bullshit to set up (like installing Node!)

(install-pkgs '(lsp-mode company-lsp lsp-ui))
(add-hook 'lsp-mode-hook 'lsp-ui-mode)

hydra-lsp is a hydra which has LSP functions in it; it's on hydra-projectile's l binding.

(defhydra hydra-lsp (:exit t :hint nil)
 Buffer^^               Server^^                   Symbol
 [_f_] format           [_M-r_] restart            [_d_] declaration  [_i_] implementation  [_o_] documentation
 [_m_] imenu            [_S_]   shutdown           [_D_] definition   [_t_] type            [_r_] rename
 [_x_] execute action   [_M-s_] describe session   [_R_] references   [_s_] signature"
  ("d" lsp-find-declaration)
  ("D" lsp-ui-peek-find-definitions)
  ("R" lsp-ui-peek-find-references)
  ("i" lsp-ui-peek-find-implementation)
  ("t" lsp-find-type-definition)
  ("s" lsp-signature-help)
  ("o" lsp-describe-thing-at-point)
  ("r" lsp-rename)

  ("f" lsp-format-buffer)
  ("m" lsp-ui-imenu)
  ("x" lsp-execute-code-action)

  ("M-s" lsp-describe-session)
  ("M-r" lsp-restart-workspace)
  ("S" lsp-shutdown-workspace))


- name: java 8 installed
    name: java-1.8.0-openjdk
    state: installed
  when: ansible_pkg_mgr == "dnf"

- name: java 8 installed
    name: openjdk-8-jdk
    state: installed
  when: ansible_pkg_mgr == "apt"
(install-pkgs '(lsp-java))
(require 'lsp-java)
(add-hook 'java-mode-hook 'lsp-mode)
(add-hook 'java-mode-hook 'company-mode)
(define-key java-mode-map (kbd "C-<tab>") 'company-lsp)


I am … decidedly not a fan of Google's Go language, but I need to use it to make far more money than I deserve, so I use it.

export GOPATH=/home/rrix/go
export PATH=/home/rrix/bin:$GOPATH/bin:$PATH

Install some important tools, and the toolchain itself.

- name: golang and tools installed
    state: installed
    - golang-go
    - golang-1.8-doc
  when: ansible_distribution_release == "stretch"

- name: golang and tools installed
    state: installed
    - golang-go
    - golang-1.10-doc
  when: ansible_distribution_release == "bionic"

- name: golang and tools installed
    state: installed
    - golang-bin
    - golang-docs
  when: ansible_pkg_mgr == "dnf"

- name: gocode etc installed
    cmd: "GOPATH=~/go go get {{item}}"
    creates: /home/{{local_account}}/go/bin/goimports
  become: yes
  become_user: "{{local_account}}"

I use goimports instead of gofmt, configure and enable it, as well as lsp-mode

(install-pkgs '(go-mode))

(setq gofmt-command "goimports")
(defun cce/go-mode-hook ()
  (add-hook 'before-save-hook 'gofmt-before-save nil t)
  (aggressive-indent-mode -1)

(add-hook 'go-mode-hook #'cce/go-mode-hook)
(eval-after-load "go-mode"
  (lambda ()
    (define-key go-mode-map (kbd "C-<tab>") 'company-lsp)))
- name: go-langserver installed
    cmd: "GOPATH=~/go go get {{item}}"
    creates: /home/{{local_account}}/go/bin/go-langserver
  become: yes
  become_user: "{{local_account}}"


Python is simply using python-language-server from everyone's favorite jack-booted thugs, Palantir. Adding pyflakes gets me some added integrations.

- name: install python-language-server
    cmd: pip install --user python-language-server[all]
    creates: ~/.local/bin/pyls
- name: pyflakes installed
    name: pyflakes
    state: installed
  when: ansible_pkg_mgr == "apt"

- name: pyflakes installed
    name: pyflakes
    state: installed
  when: ansible_pkg_mgr == "dnf"
(install-pkgs '(lsp-python))
(eval-after-load "python-mode"
  (lambda ()
    (add-hook 'python-mode-hook 'lsp)
    (add-hook 'python-mode-hook 'company-mode)
    (define-key python-mode-map (kbd "C-<tab>") 'company-lsp)))

NEXT Shell

NEXT Elixir

Code exploration

Code Surfing

Code Surf is something that I've wanted to experiment with for a while; basically, we want to be able to follow code similar to how I use in my highlight-symbol hydra. It puts a focus on the data flow of the function that you're looking at.

(defun enable-code-surf ()

  (set-face-attribute 'font-lock-builtin-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-comment-face nil :inherit 'default :foreground "grey50" :background nil)
  (set-face-attribute 'font-lock-comment-delimiter-face nil :inherit 'font-lock-comment-face)
  (set-face-attribute 'font-lock-constant-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-doc-face nil :inherit 'default :foreground "grey50" :background nil)
  (set-face-attribute 'font-lock-function-name-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-keyword-face     nil :inherit 'default :foreground nil :background nil :weight 'normal)
  (set-face-attribute 'font-lock-negation-char-face nil :inherit 'default) :foreground nil :background nil
  (set-face-attribute 'font-lock-preprocessor-face nil :inherit 'default) :foreground nil :background nil
  (set-face-attribute 'font-lock-string-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-type-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-variable-name-face nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-warning-face      nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-regexp-grouping-backslash nil :inherit 'default :foreground nil :background nil)
  (set-face-attribute 'font-lock-regexp-grouping-construct nil :inherit 'default :foreground nil :background nil)

  (setq rainbow-identifiers-faces-to-override '(font-lock-function-name-face

(setq code-surf-last-themes custom-enabled-themes)
(define-minor-mode code-surf-mode "Disable most font locking and enable rainbow-delimeters"
  nil "[CS]" nil
  (if code-surf-mode
      (message "enable")
      (message "disable")
      (global-rainbow-identifiers-mode -1)
      (global-auto-highlight-symbol-mode -1)
      (mapc (lambda (name)
              (enable-theme name))

(define-global-minor-mode global-rainbow-identifiers-mode rainbow-identifiers-mode (lambda ()
                                                                                     (when (derived-mode-p 'prog-mode)

(install-pkgs '(rainbow-identifiers))

(setq rainbow-identifiers-cie-l*a*b*-lightness 40
      rainbow-identifiers-cie-l*a*b*-saturation 70
      rainbow-identifiers-choose-face-function 'rainbow-identifiers-cie-l*a*b*-choose-face)


Projectile1 is one of those tools like Helm that some people will say is too heavy or big or bloated, but is incredibly well designed and full featured.

(require 'projectile)
(add-hook 'prog-mode-hook 'projectile-mode)
(diminish 'projectile-mode)

Projectile has some pretty nice functionality, which we expose via this Hydra, prefixed on C-x p, as a override to projectile's default C-x p prefix.

From here we can dive in to a live grep with a, find files with h, switch project using P, and other random crap.

(defhydra hydra-projectile-other-window (:color teal)
  ("f"  projectile-find-file-other-window        "file")
  ("g"  projectile-find-file-dwim-other-window   "file dwim")
  ("d"  projectile-find-dir-other-window         "dir")
  ("b"  projectile-switch-to-buffer-other-window "buffer")
  ("q"  nil                                      "cancel" :color blue))

(defhydra hydra-projectile (global-map "C-x p" :color teal)
     PROJECTILE: %(projectile-project-root)

Search: [_a_] deadgrep  [_A_] counsel-rg
Find:   [_b_] buffer    [_d_] dir        [_f_] file
Look:   [_i_] ibuffer   [_j_] imenu

Fun:    [_o_] other win [_l_] lsp        [_K_] cleanup
  ("a"   deadgrep "rg")
  ("A"   counsel-rg "qrg")
  ("b"   projectile-switch-to-buffer "buf")
  ("d"   projectile-find-dir "dir")
  ("f"   projectile-find-file "file")
  ("i"   projectile-ibuffer "ibuf")
  ("j"   counsel-imenu "imenu")
  ("K"   projectile-kill-buffers)
  ("l"   hydra-lsp/body)
  ("P"   projectile-switch-project)
  ("p"   projectile-switch-project)
  ("r"   projectile-recentf "recentf")
  ("x"   projectile-remove-known-project)
  ("X"   projectile-cleanup-known-projects)
  ("z"   projectile-cache-current-file)
  ("o"   hydra-projectile-other-window/body "other")
  ("q"   nil "cancel" :color blue))


- name: ripgrep installed
    state: installed
    name: ripgrep
  when: ansible_pkg_mgr == "apt"

- name: ripgrep installed
    state: installed
    name: ripgrep
  when: ansible_pkg_mgr == "dnf"
(install-pkgs '(deadgrep))

Ubiquitous Version Control

Magit and VC are both a part of my toolchain, through this hydra that I use. They're both incredibly powerful tools, serving slightly different use cases.

I have a hydra that acts as an entry point for my Version Control workflow.

(install-pkgs '(magit))
(require 'magit)
(defhydra hydra-magit (global-map "C-c g" :color teal :hint nil)
     PROJECTILE: %(projectile-project-root)

     Immuting            Mutating
  _w_: blame line      _b_: checkout
  _a_: annotate file   _B_: branch mgr
  _d_: diff            _c_: commit
  _s_: status          _e_: rebase
  _l_: log
  _t_: time machine

  ("w" git-messenger:popup-message)
  ("a" vc-annotate)
  ("b" magit-checkout)
  ("B" magit-branch-manager)
  ("c" vc-next-action)
  ("d" magit-diff-working-tree)
  ("e" magit-interactive-rebase)
  ("s" magit-status)
  ("l" magit-log)
  ("t" git-timemachine))

Git Blame

I have Git blame on C-x g ! using git-messenger 2, a neat little piece of kit which gives you a popup notification with the commit details of the current line of code.

(install-pkgs '(git-messenger))
(require 'git-messenger)

NEXT Branching

Phabricator/Arcanist basically requires you to always be working in a branch. If you want to open a diff, you open a diff between two branches, your work branch and master, so you have to be working on a branch. I need an easy way to keep me from commit on master in certain repositories, maybe as a pre-commit hook as a starter, but it should probably end up being a part of my editor workflow.

In the mean time, I can switch branches using a combination of Magit and Hydra, on C-x g b and C-x g B to open the branch manager.


I have two ways of doing this, in general; larger commits and single-file changes.

The latter I can use vc-next-action to do in a single blast, but for larger blobs of code, or to stage single hunks in a file, I'll need to use magit-status, add the files and then commit them all.

I have vc-next-action bound to C-x g c and I have magit-status bound to C-x g s.

History rewriting

The workflow that I use at work encourages clean git history, by doing rebases during landing; this lets me rewrite history as I please while I am working and bundle each piece of work in to a single commit or set of commits. This is bound to C-x g e.

History viewing

Magit has good repository logging, and git-timemachine3 can walk an individual file's history really well. I have magit-log bound to C-x g l and I have git-timemachine bound to C-x g t.

(install-pkgs '(git-timemachine))
(require 'git-timemachine)


I don't do this enough to put it on a dedicated global key, it is available inside of magit-status under z, let's leave it there.

NEXT Refactoring Tools

NEXT Phabricator/Arcanist integration

NEXT Create some nice Phabricator reports to make Triage easier

NEXT Phabricator org-mode integration

  • pull tdown in to an org-mode document
  • updates as necessary


Author: Ryan Rix

Created: 2019-05-07 Tue 11:12

Validate XHTML 1.0