#+title:  Clojure Programming
#+author: Howard X. Abrams
#+date:   2022-04-05
#+tags: emacs clojure programming lisp

A literate programming file for programming in Clojure.

#+begin_src emacs-lisp :exports none
  ;;; ha-programming-clojure --- programming in Clojure. -*- lexical-binding: t; -*-
  ;;
  ;; © 2022-2023 Howard X. Abrams
  ;;   Licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard X. Abrams
  ;; Created: April  5, 2022
  ;;
  ;; This file is not part of GNU Emacs.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;            ~/src/hamacs/ha-programming-clojure.org
  ;;       And tangle the file to recreate this one.
  ;;
  ;;; Code:
  #+end_src
I like [[http://clojure.org][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 [[http://brew.sh][Homebrew]], try the following:
#+begin_src sh
  brew install clojure/tools/clojure
  brew install leiningen
#+end_src

And make sure it works:
#+begin_src sh
  clojure --help
#+end_src

Or by starting a REPL:
#+begin_src sh
  clj
#+end_src

Then for each project, create the project directory with this command:
#+begin_src sh
  lein new app fresh-app
#+end_src
* Emacs Support
Install and configure [[https://github.com/clojure-emacs/clojure-mode/][clojure-mode]]:
#+begin_src emacs-lisp
  (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))
#+end_src

Need the IDE features associated with [[https://github.com/clojure-emacs/cider][Cider]]:
#+begin_src emacs-lisp
  (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)))
#+end_src
Read the entire [[https://docs.cider.mx/][CIDER manual]], specifically the [[https://docs.cider.mx/cider/usage/cider_mode.html][Usage document]].
** Linting
*Note:* The [[https://develop.spacemacs.org/layers/+lang/clojure/README.html][Spacemacs community]] recommends using [[https://github.com/borkdude/clj-kondo][clj-kondo]] in combination with [[https://github.com/candid82/joker][joker]].
Add lint warnings to [[file:emacs.org::*Flycheck][Flycheck]]:
#+begin_src elisp
  (use-package flycheck-clojure
    :after flycheck
    :config
    (flycheck-clojure-setup))
#+end_src

To install the =joker= binary:
#+begin_src sh
  brew install candid82/brew/joker
#+end_src

And the [[https://github.com/candid82/flycheck-joker][flycheck-joker]] package should do the trick:
#+begin_src emacs-lisp
  (use-package flycheck-joker)
#+end_src

To install the =clj-kondo= binary is a bit more involved:
#+begin_src sh
  curl -sLO https://raw.githubusercontent.com/clj-kondo/clj-kondo/master/script/install-clj-kondo
  chmod +x install-clj-kondo
  ./install-clj-kondo
#+end_src

And the [[https://github.com/borkdude/flycheck-clj-kondo][flycheck-clj-kondo]] project should do the integration:
#+begin_src emacs-lisp
  (use-package flycheck-clj-kondo)
#+end_src
 Search on Clojars more easily
This [[https://github.com/joshuamiller/clojars.el][clojars]] extension allows you to search for projects on [[www.clojars.org][clojars.org]] and copies your selection to the kill ring in a format suitable for your =project.clj=.
#+begin_src emacs-lisp
  (use-package clojars
    :after clojure-mode
    :config
    (ha-local-leader :keymaps clojure-mode-map
     "h j" '("clojars" . clojars)))
#+end_src
** Clojure Cheatsheet
The [[https://github.com/clojure-emacs/clojure-cheatsheet][clojure-cheatsheet]]
#+begin_src emacs-lisp
  (use-package clojure-cheatsheet
    :after clojure-mode
    :config
    (ha-local-leader :keymaps clojure-mode-map
     "h c" '("cheatsheet" . clojure-cheatsheet)))
#+end_src
** Snippets
For clojure-specific templates for [[https://github.com/capitaomorte/yasnippet][yasnippets]], we use David Nolen's [[http://github.com/swannodette/clojure-snippets][clojure-snippets]] repository:
  #+begin_src elisp
  (use-package clojure-snippets)
  #+end_src
** Refactoring
The [[https://github.com/clojure-emacs/clj-refactor.el][clj-refactor]] project:
#+begin_src elisp
  (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)
#+end_src

The advanced refactorings require the [[https://github.com/clojure-emacs/refactor-nrepl][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 [[https://github.com/clojure-emacs/clj-refactor.el/wiki][refactoring options]]. Remember: =C-c . h h=
** Org Babel
And of course, we want to put this with org blocks:
#+begin_src emacs-lisp
  (use-package ob-clojure
    :straight (:type built-in)
    :custom
    (org-babel-clojure-backend 'cider)
    :config
    (add-to-list 'org-babel-load-languages '(clojure . t)))
#+end_src

Does this now work?
#+begin_src clojure :results raw replace
  (format "The answer is %d" (+ (* 4 10) 2))
#+end_src

#+results:
"The answer is 42"
* LSP, a WIP
Check out the goodies in [[https://emacs-lsp.github.io/lsp-mode/tutorials/clojure-guide/][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.
#+begin_src emacs-lisp :tangle no
  (add-hook 'clojure-mode-hook 'lsp)
  (add-hook 'clojurescript-mode-hook 'lsp)
  (add-hook 'clojurec-mode-hook 'lsp)
#+end_src
* Technical Artifacts                                :noexport:
Let's =provide= a name so we can =require= this file:
#+begin_src emacs-lisp :exports none
  (provide 'ha-programming-clojure)
  ;;; ha-programming-clojure.el ends here
  #+end_src

#+description: programming in Clojure.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp  :tangle yes
#+property:    header-args    :results none :eval no-export :comments no mkdirp yes

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
