Clojure Programming
Table of Contents
A literate programming file for programming in Clojure.
I like Clojure as a modern Lisp, but I don’t get to play with it much anymore. 😢 The following instructions create a fully blinged-out Emacs-Clojure setup.
Introduction
To get Clojure working on a new MacOS system using Homebrew, try the following:
brew install clojure/tools/clojure brew install leiningen
And make sure it works:
clojure --help
Or by starting a REPL:
clj
Then for each project, create the project directory with this command:
lein new app fresh-app
Emacs Support
Install and configure clojure-mode:
(use-package clojure-mode :init (add-to-list 'org-babel-load-languages '(clojure . t)) :config (defun ha-prettify-clojure () "Make the Clojure syntax prettier." (push '("fn" . ?𝝀) prettify-symbols-alist) (push '("__" . ?⁈) prettify-symbols-alist) (prettify-symbols-mode)) :hook (clojure-mode . ha-prettify-clojure))
Need the IDE features associated with Cider:
(use-package cider :after clojure-mode :commands (cider-connect cider-jack-in cider) :init (setq cider-show-error-buffer t ;; The use of paredit when editing Clojure (or any other Lisp) code is ;; highly recommended. You're probably using it already in your ;; clojure-mode buffers (if you're not you probably should). You might ;; also want to enable paredit in the REPL buffer as well. ;; (add-hook 'cider-repl-mode-hook #'paredit-mode) ;; Don't select the error buffer when it's displayed: cider-auto-select-error-buffer nil ;; Controls whether to pop to the REPL buffer on connect. cider-repl-pop-to-buffer-on-connect nil cider-repl-use-clojure-font-lock t ;; T to wrap history around when the end is reached. cider-repl-wrap-history t cider-repl-history-size 1000 ;; Hide `*nrepl-connection*' and `*nrepl-server*' buffers from appearing ;; in some buffer switching commands like switch-to-buffer nrepl-hide-special-buffers t ;; Stop error buffer from popping up while working in buffers other than the REPL: nrepl-popup-stacktraces nil) ;; Enabling CamelCase support for editing commands (like forward-word, ;; backward-word, etc) in the REPL is quite useful since we often have ;; to deal with Java class and method names. The built-in Emacs minor ;; mode subword-mode provides such functionality :hook (cider-repl-mode . #'subword-mode) :config (ha-local-leader :keymaps clojure-mode-map "w" '(:ignore t :which-key "cider") "w s" '("start" . cider-jack-in) "w r" '("restart" . cider-restart) "w c" '("connect" . cider-connect) "w b" '("switch" . cider-switch-repl-buffer) "w n" '("namespace" . cider-repl-set-ns) "w e" '("describe" . cider-describe-connection) "w x" '("interrupt" . cider-interrupt) "w q" '("quit" . cider-quit) "b" '(:ignore t :which-key "load") "b b" '("load buffer" . cider-load-buffer) "b f" '("load file" . cider-load-file) "b f" '("load all" . cider-load-all-files) "b r" '("refresh all" . cider-ns-refresh) "e" '(:ignore t :which-key "eval") "e e" '("last s-expr" . cider-eval-last-sexp) "e E" '("replace s-expr" . cider-eval-last-sexp-and-replace) "e ." '("s-expr point" . cider-eval-sexp-at-point) "e f" '("defun" . cider-eval-defun-at-point) "e F" '("file" . cider-eval-file) "e b" '("buffer" . cider-eval-buffer) "e r" '("region" . cider-eval-region) "e R" '("to repl" . cider-eval-last-sexp-to-repl) "e n" '("namespace" . cider-eval-ns-form) "e ;" '("expression" . cider-read-and-eval) "e i" '("inspect" . cider-inspect) "e p" '(:ignore t :which-key "pprint") "e p p" '("last s-expr" . cider-pprint-eval-last-sexp) "e p f" '("defun" . cider-pprint-eval-defun-at-point) "e p r" '("to repl" . cider-pprint-eval-last-sexp-to-repl) ;; Overshadowing xref menu in `ha-programming': "s" '(:ignore t :which-key "search") "s d" '("definition" . cider-find-resource) "s s" '("var" . cider-find-var) "s f" '("file" . cider-find-ns) "s o" '("other window" . xref-find-definitions-other-window) "s D" '("deps" . cider-xref-fn-deps) "s b" '("back" . cider-pop-back) "t" '(:ignore t :which-key "test") "t t" '("run test" . cider-test-run-test) "t r" '("rerun test" . cider-test-rerun-test) "t a" '("run all" . cider-test-run-ns-tests) "t p" '("run project" . cider-test-run-project-tests) "t f" '("run failed" . cider-test-rerun-failed-tests) "t R" '("show report" . cider-test-show-report) "d" '(:ignore t :which-key "docs") "d d" '("documentation" . cider-doc) "d s" '("search docs" . cider-apropos-documentation) "d j" '("javadocs" . cider-javadoc) "d c" '("clojuredocs" . cider-clojuredocs) "d a" '("apropos" . cider-apropos)))
Read the entire CIDER manual, specifically the Usage document.
Linting
Note: The Spacemacs community recommends using clj-kondo in combination with joker. Add lint warnings to :
(use-package flycheck-clojure :after flycheck :config (flycheck-clojure-setup))
To install the joker
binary:
brew install candid82/brew/joker
And the flycheck-joker package should do the trick:
(use-package flycheck-joker)
To install the clj-kondo
binary is a bit more involved:
curl -sLO https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/script/install-clj-kondo chmod +x install-clj-kondo ./install-clj-kondo
And the flycheck-clj-kondo project should do the integration:
(use-package flycheck-clj-kondo)
Search on Clojars more easily
This clojars extension allows you to search for projects on and copies your selection to the kill ring in a format suitable for your project.clj
.
(use-package clojars :after clojure-mode :config (ha-local-leader :keymaps clojure-mode-map "h j" '("clojars" . clojars)))
Clojure Cheatsheet
(use-package clojure-cheatsheet :after clojure-mode :config (ha-local-leader :keymaps clojure-mode-map "h c" '("cheatsheet" . clojure-cheatsheet)))
Snippets
For clojure-specific templates for yasnippets, we use David Nolen’s clojure-snippets repository:
(use-package clojure-snippets)
Refactoring
The clj-refactor project:
(use-package clj-refactor :after clojure-mode :hook (clojure-mode . clj-refactor-mode) :config ;; Configure the Clojure Refactoring prefix. (cljr-add-keybindings-with-prefix "C-c .") (ha-local-leader :keymaps clojure-mode-map ;; Would really like to have this on the , prefix: "r" '("refactoring" . hydra-cljr-help-menu/body) "h d" '("describe refactoring" . cljr-describe-refactoring) "h r" '("refactoring" . hydra-cljr-toplevel-form-menu/body)) :diminish clj-refactor-mode)
The advanced refactorings require the refactor-nrepl middleware, which should explain why we added the refactor-nrepl
to the :plugins
section in the ~/.lein/profiles.clj
file (see below).
The real problem is trying to remember all the refactoring options. Remember: C-c . h h
Org Babel
And of course, we want to put this with org blocks:
(use-package ob-clojure :straight (:type built-in) :custom (org-babel-clojure-backend 'cider) :config (add-to-list 'org-babel-load-languages '(clojure . t)))
Does this now work?
(format "The answer is %d" (+ (* 4 10) 2))
“The answer is 42”
LSP, a WIP
Check out the goodies in this essay for hooking Clojure to LSP. Haven’t done this yet, and to be honest, I’m not sure what it buys us beyond what Cider offers.
(add-hook 'clojure-mode-hook 'lsp) (add-hook 'clojurescript-mode-hook 'lsp) (add-hook 'clojurec-mode-hook 'lsp)