Skip to content

Instantly share code, notes, and snippets.

@DarrenN
Forked from avescodes/Editing Clojure with Emacs
Last active December 22, 2022 14:41
Show Gist options
  • Save DarrenN/6285185 to your computer and use it in GitHub Desktop.
Save DarrenN/6285185 to your computer and use it in GitHub Desktop.
Emacs setup for Clojure
shibuya.el
dazza.el
elpa/
places
eshell/
.smex-items
ac-comphist.dat
projectile-bookmarks.eld

Editing Clojure with Emacs

This gist will guide you to setting up a basic emacs-starter-kit-based Emacs configuration for editing Clojure.

Contents:

Getting Emacs

The first step is to get Emacs. Whatever you do, just make sure you get Emacs 24.

To paraphrase Install the right Emacs:

Once you've installed Emacs verify you have the correct version with emacs --version

▸ emacs --version
GNU Emacs 24.2.1
Copyright (C) 2012 Free Software Foundation, Inc.
GNU Emacs comes with ABSOLUTELY NO WARRANTY.
You may redistribute copies of Emacs
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING.

Now, for the config...

Installing the config

Prerequisite: Install Leiningen. Installation instructions.

To follow along with the contents of the presentation I would suggest you download and install the Emacs configuration bundled in this gist like so:

  1. Clean out any existing Emacs configurations you have.

  2. Clone this gist to your ~/.emacs.d folder with the command

     $ git clone https://gist.github.com/5126926.git ~/.emacs.d
    
  3. Launch emacs without any arguments like $ emacs. You'll see a number of package fetch and compilation messages fly by as Emacs installs all of the packages specified in init.el.

If you didn't encounter any errors you've likely arrived at a simple, stable Emacs configuration. The configuration is annotated, so feel free to read and edit as you see fit.

Enjoy

A special note for Mac users

If you try to follow along into the Paredit sections you'll be sorely disappointed when you start trying to do things like C-→. The problem is this: every Mac terminal sends the incorrect key codes to your terminal, and by-proxy, Emacs.

Fix this by following the excellent instructions of Cosmin Stejerean's Emacs + paredit under terminal. His instructions address how to fix Terminal.app, iTerm and iTerm2 to send the proper key codes for using Paredit.

Keyboard Shortcuts

Protip: To find out what any keybinding does use C-h k followed by the keybinding in question to discover what it does.

REPL-driven Development with nREPL:

Keybinding Result
M-x nrepl-jack-in, C-c M-j (when in Clojure file) Open an nREPL session. Intelligently loads project context if launched from any file within a Leiningen project.
C-↑, C-↓ Browse up or down through nREPL's history of commands.
C-x 3 Split window vertically
C-x f Open file
C-M-x Evaluate expression at cursor. This command evaluates whatever top-level expression is under your cursor.
C-c M-n Change the namespace of your nREPL REPL to match the command was issued from.
C-c C-k Evaluate an entire file.
C-c C-e Evaluate the expression preceding your cursor.
C-c C-r Evaluate a selected region. Start selecting text with the keybinding C-Space.
M-. Jump to the definition of the symbol under your cursor.
M-, Jump back one level (after jumping with M-.).

Paredit

Keybinding Result
C-→ Absorb one expression from the right of the current expression
Also try C-←, C-M-→ and C-M-←

Example: (+ 1 2) 3(+ 1 2 3)
M-↑ "Splice sexp killing backwards". A utility-knife of a command that unwraps an expression, deleting anything before the cursor.
Example: (filter :completed? (map (fn [[_ v]] v)▋todos))(filter :completed? todos)
;; General
(setq initial-scratch-message nil) ; *scratch* starts empty
(when (locate-library "clojure-mode") ; Set *scratch* to Clojure mode
(setq initial-major-mode 'clojure-mode))
(projectile-global-mode) ; Quickly navigate projects using Projectile (C-c p C-h for available commands)
(setq projectile-show-paths-function 'projectile-hashify-with-relative-paths) ; Projectile shows full relative paths
;; Visual
(load-theme 'twilight t) ; Load my preferred theme, twilight
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode) ; Enable rainbow delimiters when programming
(remove-hook 'prog-mode-hook 'esk-turn-on-hl-line-mode) ; Disable emacs-starter-kits line highlighting
(global-linum-mode t) ; Always show line numbers on left
(setq linum-format "%4d ") ; Line numbers gutter should be four characters wide
(line-number-mode 1) ; Mode line shows line numbers
(column-number-mode 1) ; Mode line shows column numbers
(setq-default tab-width 2) ; Tab width of 2
(fset 'yes-or-no-p 'y-or-n-p) ; Emacs prompts should accept "y" or "n" instead of the full word
(setq visible-bell nil) ; No more Mr. Visual Bell Guy.
;; Clojure
(setq auto-mode-alist (cons '("\\.edn$" . clojure-mode) auto-mode-alist)) ; *.edn are Clojure files
(setq auto-mode-alist (cons '("\\.cljs$" . clojure-mode) auto-mode-alist)) ; *.cljs are Clojure files
;; nREPL customizations
(setq nrepl-hide-special-buffers t) ; Don't show buffers like connection or server
(setq nrepl-popup-on-error nil) ; Don't popup new buffer for errors (show in nrepl buffer)
(setq nrepl-popup-stacktraces-in-repl t) ; Display stacktrace inline
(add-hook 'nrepl-interaction-mode-hook 'nrepl-turn-on-eldoc-mode) ; Enable eldoc - shows fn argument list in echo area
(add-hook 'nrepl-mode-hook 'paredit-mode) ; Use paredit in *nrepl* buffer
(add-to-list 'same-window-buffer-names "*nrepl*") ; Make C-c C-z switch to *nrepl*
;; Ido-mode customizations
(setq ido-decorations ; Make ido-mode display vertically
(quote
("\n-> " ; Opening bracket around prospect list
"" ; Closing bracket around prospect list
"\n " ; separator between prospects
"\n ..." ; appears at end of truncated list of prospects
"[" ; opening bracket around common match string
"]" ; closing bracket around common match string
" [No match]" ; displayed when there is no match
" [Matched]" ; displayed if there is a single match
" [Not readable]" ; current diretory is not readable
" [Too big]" ; directory too big
" [Confirm]"))) ; confirm creation of new file or buffer
(add-hook 'ido-setup-hook ; Navigate ido-mode vertically
(lambda ()
(define-key ido-completion-map [down] 'ido-next-match)
(define-key ido-completion-map [up] 'ido-prev-match)
(define-key ido-completion-map (kbd "C-n") 'ido-next-match)
(define-key ido-completion-map (kbd "C-p") 'ido-prev-match)))
Check out README.md to get started editing Clojure with Emacs.
(require 'package)
(add-to-list 'package-archives
'("melpa" . "http://melpa.milkbox.net/packages/") t)
(package-initialize)
(when (not package-archive-contents)
(package-refresh-contents))
;; Add in your own as you wish:
(defvar my-packages '(starter-kit
starter-kit-bindings
starter-kit-js
starter-kit-lisp
starter-kit-ruby
;; CoffeeScript
coffee-mode
;; Webmode
multi-web-mode
;; SCSS
scss-mode
;; Clojure & friends
clojure-mode
nrepl
rainbow-delimiters
;; Project navigation
projectile
ack-and-a-half
;; Misc.
json-mode
js2-mode
js2-refactor
markdown-mode
twilight-theme
hlinum
exec-path-from-shell
)
"A list of packages to ensure are installed at launch.")
;; Automaticaly install any missing packages
(dolist (p my-packages)
(when (not (package-installed-p p))
(package-install p)))
;; Scoop up $PATH
(when (memq window-system '(mac ns))
(exec-path-from-shell-initialize))
;; JavaScript
(require 'js2-mode)
(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
(add-to-list 'interpreter-mode-alist '("node" . js2-mode))
(eval-after-load 'js2-mode
'(progn
(setq-default js2-basic-offset 2)
(font-lock-add-keywords 'js2-mode `(("\\(function *\\)(" (0 (progn (compose-region (match-beginning 1) (match-end 1) "ƒ") nil)))))))
(require 'js2-refactor)
(js2r-add-keybindings-with-prefix "C-c C-m")
;; HTML
(require 'multi-web-mode)
(setq mweb-default-major-mode 'html-mode)
(setq mweb-tags '((php-mode "<\\?php\\|<\\? \\|<\\?=" "\\?>")
(js-mode "<script[^>]*>" "</script>")
(css-mode "<style[^>]*>" "</style>")))
(setq mweb-filename-extensions '("php" "htm" "html" "ctp" "phtml" "php4" "php5"))
(multi-web-global-mode 1)
(add-hook 'html-mode-hook 'turn-off-auto-fill)
;; SCSS
(autoload 'scss-mode "scss-mode")
(add-to-list 'auto-mode-alist '("\\.scss\\'" . scss-mode))
(setq scss-compile-at-save nil)
(add-hook 'scss-mode-hook (lambda () (interactive) (auto-complete-mode)))
;; Setting rbenv path
(setenv "PATH" (concat (getenv "HOME") "/.rbenv/shims:" (getenv "HOME") "/.rbenv/bin:" (getenv "PATH")))
(setq exec-path (cons (concat (getenv "HOME") "/.rbenv/shims") (cons (concat (getenv "HOME") "/.rbenv/bin") exec-path)))
;; yasnippet
(require 'yasnippet)
(yas-global-mode 1)
;; Auto-complete
(require 'auto-complete-config)
(ac-config-default)
(ac-set-trigger-key "TAB")
(ac-set-trigger-key "<tab>")
;; Column markers
(require 'column-marker)
(add-hook 'js2-mode-hook (lambda () (interactive) (column-marker-2 80) (auto-complete-mode)))
(add-hook 'javascript-mode-hook (lambda () (interactive) (column-marker-2 80)))
(add-hook 'ruby-mode-hook (lambda () (interactive) (column-marker-3 80) (auto-complete-mode)))
(add-hook 'coffee-mode-hook (lambda () (interactive) (column-marker-2 80) (auto-complete-mode)))
;; Load the provided Clojure start kit configurations
(load (concat user-emacs-directory "clojure-starter-kit.el"))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(font-lock-warning-face ((t (:inherit nil :foreground "red" :background nil))))
'(linum-highlight-face ((t (:inherit default :background "color-238" :foreground "white"))))
'(show-paren-match ((((class color) (background dark)) (:inherit nil :foreground "red")))))
;; My old self needs bigger type
(set-face-attribute 'default nil :height 140)
;; Bad whitespace go away
(add-hook 'before-save-hook 'delete-trailing-whitespace)
(setq whitespace-action '(auto-cleanup)) ;; automatically clean up bad whitespace
(setq whitespace-style '(trailing space-before-tab indentation empty space-after-tab)) ;; only show bad whitespace
;; Map M to Ctrl
(global-set-key "\C-x\C-m" 'execute-extended-command)
(global-set-key "\C-c\C-m" 'execute-extended-command)
;; KIBIT
;; Teach compile the syntax of the kibit output
(require 'compile)
(add-to-list 'compilation-error-regexp-alist-alist
'(kibit "At \\([^:]+\\):\\([[:digit:]]+\\):" 1 2 nil 0))
(add-to-list 'compilation-error-regexp-alist 'kibit)
;; A convenient command to run "lein kibit" in the project to which
;; the current emacs buffer belongs to.
(defun kibit ()
"Run kibit on the current project.
Display the results in a hyperlinked *compilation* buffer."
(interactive)
(compile "lein kibit"))
(defun kibit-current-file ()
"Run kibit on the current file.
Display the results in a hyperlinked *compilation* buffer."
(interactive)
(compile (concat "lein kibit " buffer-file-name)))
;; Buffer Management
(defun kill-other-buffers ()
"Kill other buffers."
(interactive)
(mapc 'kill-buffer
(delq (current-buffer)
(remove-if-not 'buffer-file-name (buffer-list)))))
(defun kill-buffers ()
"Kill all buffers."
(interactive)
(mapc 'kill-buffer
(delq (current-buffer)
(buffer-list))))
;; Sibuya has hi-res monitor, so we need to bump up again!
(set-face-attribute 'default nil :height 150)
(set-default-font "Consolas-16")
(global-auto-revert-mode t)
(require 'php-auto-yasnippets)
(define-key php-mode-map (kbd "C-c C-y") 'yas/create-php-snippet)
@dcassiero
Copy link

WOW! Nice!

@saolsen
Copy link

saolsen commented Dec 20, 2013

GOOD THING YOU HAVE PHP MODE SET UP!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment