This commit is contained in:
ry
2022-01-11 18:05:13 -08:00
parent 2046befee2
commit 8e7b654716
846 changed files with 71287 additions and 4 deletions

View File

@@ -0,0 +1,115 @@
#+TITLE: lang/clojure
#+DATE: May 30, 2017
#+SINCE: v2.0
#+STARTUP: inlineimages nofold
* Table of Contents :TOC_3:noexport:
- [[#description][Description]]
- [[#maintainers][Maintainers]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#hacks][Hacks]]
- [[#prerequisites][Prerequisites]]
- [[#features][Features]]
- [[#keybindings][Keybindings]]
- [[#configuration][Configuration]]
- [[#troubleshooting][Troubleshooting]]
* Description
This module adds support for the Clojure(Script) language.
+ Interactive development environment (~cider~): REPL, compilation, debugging,
running tests, definitions & documentation lookup, code completion, and much
more
+ Refactoring (~clj-refactor~)
+ Linting (~clj-kondo~), requires ~:checkers syntax~
+ LSP support (~clojure-lsp~)
** Maintainers
This module has no dedicated maintainers.
** Module Flags
+ =+lsp= Enables LSP support, alongside Cider.
** Plugins
+ [[https://github.com/clojure-emacs/cider][cider]]
+ [[https://github.com/clojure-emacs/clj-refactor.el][clj-refactor]]
+ [[https://github.com/borkdude/flycheck-clj-kondo][flycheck-clj-kondo]]
** Hacks
+ Error messages emitted from CIDER are piped into the REPL buffer when it is
first opened, to make them easier to notice.
+ Disabled the help banner when opening the cider REPL.
* Prerequisites
This module requires:
+ [[https://clojure.org/][clojure]]
+ [[https://leiningen.org/][leiningen]] (REPL)
+ [[https://github.com/borkdude/clj-kondo][clj-kondo]] (linter)
+ With =+lsp=
+ [[https://clojure-lsp.github.io/clojure-lsp/][clojure-lsp]]
* TODO Features
# An in-depth list of features, how to use them, and their dependencies.
** Keybindings
| Binding | Description |
|---------------------+------------------------------------------------------|
| =<localleader> '= | =cider-jack-in-clj= |
| =<localleader> C= | =cider-connect-cljs= |
| =<localleader> M= | =cider-macroexpand-all= |
| =<localleader> R= | =hydra-cljr-help-menu/body= |
| =<localleader> c= | =cider-connect-clj= |
| =<localleader> e D= | =cider-insert-defun-in-repl= |
| =<localleader> e E= | =cider-insert-last-sexp-in-repl= |
| =<localleader> e R= | =cider-insert-region-in-repl= |
| =<localleader> e b= | =cider-eval-buffer= |
| =<localleader> e d= | =cider-eval-defun-at-point= |
| =<localleader> e e= | =cider-eval-last-sexp= |
| =<localleader> e r= | =cider-eval-region= |
| =<localleader> e u= | =cider-undef= |
| =<localleader> g b= | =cider-pop-back= |
| =<localleader> g g= | =cider-find-var= |
| =<localleader> g n= | =cider-find-ns= |
| =<localleader> h a= | =cider-apropos= |
| =<localleader> h c= | =cider-clojuredocs= |
| =<localleader> h d= | =cider-doc= |
| =<localleader> h j= | =cider-javadoc= |
| =<localleader> h n= | =cider-find-ns= |
| =<localleader> h w= | =cider-clojuredocs-web= |
| =<localleader> i e= | =cider-enlighten-mode= |
| =<localleader> i i= | =cider-inspect= |
| =<localleader> i r= | =cider-inspect-last-result= |
| =<localleader> m "= | =cider-jack-in-cljs= |
| =<localleader> m= | =cider-macroexpand-1= |
| =<localleader> n N= | =cider-browse-ns-all= |
| =<localleader> n n= | =cider-browse-ns= |
| =<localleader> n r= | =cider-ns-refresh= |
| =<localleader> p d= | =cider-pprint-eval-defun-at-point= |
| =<localleader> p D= | =cider-pprint-eval-defun-to-comment= |
| =<localleader> p p= | =cider-pprint-eval-last-sexp= |
| =<localleader> p P= | =cider-pprint-eval-last-sexp-to-comment= |
| =<localleader> p r= | =cider-pprint-eval-last-sexp-to-repl= |
| =<localleader> r B= | =+clojure/cider-switch-to-repl-buffer-and-switch-ns= |
| =<localleader> r L= | =cider-load-buffer-and-switch-to-repl-buffer= |
| =<localleader> r R= | =cider-restart= |
| =<localleader> r b= | =cider-switch-to-repl-buffer= |
| =<localleader> r c= | =cider-find-and-clear-repl-output= |
| =<localleader> r l= | =cider-load-buffer= |
| =<localleader> r n= | =cider-repl-set-ns= |
| =<localleader> r q= | =cider-quit= |
| =<localleader> r r= | =cider-ns-refresh= |
| =<localleader> t a= | =cider-test-rerun-test= |
| =<localleader> t l= | =cider-test-run-loaded-tests= |
| =<localleader> t n= | =cider-test-run-ns-tests= |
| =<localleader> t p= | =cider-test-run-project-tests= |
| =<localleader> t r= | =cider-test-rerun-failed-tests= |
| =<localleader> t s= | =cider-test-run-ns-tests-with-filters= |
| =<localleader> t t= | =cider-test-run-test= |
* TODO Configuration
# How to configure this module, including common problems and how to address them.
* TODO Troubleshooting
# Common issues and their solution, or places to look for help.

View File

@@ -0,0 +1,44 @@
;;; lang/clojure/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +clojure-cider-lookup-definition (identifier)
"A lookup handler for `cider-mode'.
This is necessary to fix `cider-find-dwim's inability to capture the full symbol
at point."
(cider-find-dwim identifier))
;;
;;; Commands
;;;###autoload
(defun +clojure/open-repl (&optional arg type)
"Open a Cider REPL for clojure and return the buffer."
(interactive "P")
;; TODO Better error handling
;; type is `clj' for clojure and `cljs' for clojurescript
;; ... with no type specified, assume `clj'.
(let ((type (or type 'clj)))
(if-let (buffer (cider-current-repl type))
(pop-to-buffer buffer)
(let ((process (cond ((eq type 'clj) (cider-jack-in-clj arg))
((eq type 'cljs) (cider-jack-in-cljs arg)))))
(message "Starting CIDER server for the first time...")
(while (and (process-live-p process)
(not (cider-current-repl type)))
(sit-for 1))
(message "Starting CIDER server for the first time...done")
(pop-to-buffer (cider-current-repl type))))))
;;;###autoload
(defun +clojure/open-cljs-repl (&optional arg)
"Open a Cider REPL for clojurescript and return the buffer."
(interactive "P")
(+clojure/open-repl arg 'cljs))
;;;###autoload
(defun +clojure/cider-switch-to-repl-buffer-and-switch-ns ()
"TODO"
(interactive)
(cider-switch-to-repl-buffer t))

View File

@@ -0,0 +1,246 @@
;;; lang/clojure/config.el -*- lexical-binding: t; -*-
(after! projectile
(pushnew! projectile-project-root-files "project.clj" "build.boot" "deps.edn"))
;; Large clojure buffers tend to be slower than large buffers of other modes, so
;; it should have a lower threshold too.
(add-to-list 'doom-large-file-size-alist '("\\.\\(?:clj[sc]?\\|dtm\\|edn\\)\\'" . 0.5))
;;
;;; Packages
(use-package! clojure-mode
:hook (clojure-mode . rainbow-delimiters-mode)
:config
(when (featurep! +lsp)
(add-hook! '(clojure-mode-local-vars-hook
clojurec-mode-local-vars-hook
clojurescript-mode-local-vars-hook)
(defun +clojure-disable-lsp-indentation-h ()
(setq-local lsp-enable-indentation nil))
#'lsp!)
(after! lsp-clojure
(dolist (m '(clojure-mode
clojurec-mode
clojurescript-mode
clojurex-mode))
(add-to-list 'lsp-language-id-configuration (cons m "clojure"))))))
(use-package! cider
;; NOTE if `org-directory' doesn't exist, `cider-jack' in won't work
:hook (clojure-mode-local-vars . cider-mode)
:init
(after! clojure-mode
(set-repl-handler! '(clojure-mode clojurec-mode) #'+clojure/open-repl :persist t)
(set-repl-handler! 'clojurescript-mode #'+clojure/open-cljs-repl :persist t)
(set-eval-handler! '(clojure-mode clojurescript-mode clojurec-mode) #'cider-eval-region))
;; HACK Fix raxod502/radian#446: CIDER tries to calculate the frame's
;; background too early; sometimes before the initial frame has been
;; initialized, causing errors.
(defvar cider-docview-code-background-color nil)
(defvar cider-stacktrace-frames-background-color nil)
(add-transient-hook! #'cider-docview-fontify-code-blocks (cider--docview-adapt-to-theme))
(add-transient-hook! #'cider-stacktrace-render-cause (cider--stacktrace-adapt-to-theme))
:config
(add-hook 'cider-mode-hook #'eldoc-mode)
(set-lookup-handlers! '(cider-mode cider-repl-mode)
:definition #'+clojure-cider-lookup-definition
:documentation #'cider-doc)
(set-popup-rules!
'(("^\\*cider-error*" :ignore t)
("^\\*cider-repl" :quit nil :ttl nil)
("^\\*cider-repl-history" :vslot 2 :ttl nil)))
(setq nrepl-hide-special-buffers t
nrepl-log-messages nil
cider-font-lock-dynamically '(macro core function var deprecated)
cider-overlays-use-font-lock t
cider-prompt-for-symbol nil
cider-repl-history-display-duplicates nil
cider-repl-history-display-style 'one-line
cider-repl-history-file (concat doom-cache-dir "cider-repl-history")
cider-repl-history-highlight-current-entry t
cider-repl-history-quit-action 'delete-and-restore
cider-repl-history-highlight-inserted-item t
cider-repl-history-size 1000
cider-repl-result-prefix ";; => "
cider-repl-print-length 100
cider-repl-use-clojure-font-lock t
cider-repl-use-pretty-printing t
cider-repl-wrap-history nil
cider-stacktrace-default-filters '(tooling dup)
;; Don't focus the CIDER REPL when it starts. Since it can take so long
;; to start up, you either wait for a minute doing nothing or be
;; prepared for your cursor to suddenly change buffers without warning.
;; See https://github.com/clojure-emacs/cider/issues/1872
cider-repl-pop-to-buffer-on-connect 'display-only)
;; Error messages emitted from CIDER is silently funneled into *nrepl-server*
;; rather than the *cider-repl* buffer. How silly. We might want to see that
;; stuff and who's going to check *nrepl-server* on every startup? I've got a
;; better idea: we copy these errors into the *cider-repl* buffer.
(add-hook! 'cider-connected-hook
(defun +clojure--cider-dump-nrepl-server-log-h ()
"Copy contents of *nrepl-server* to beginning of *cider-repl*."
(when (buffer-live-p nrepl-server-buffer)
(save-excursion
(goto-char (point-min))
(insert
(with-current-buffer nrepl-server-buffer
(buffer-string)))))))
;; When in cider-debug-mode, override evil keys to not interfere with debug keys
(after! evil
(add-hook! cider--debug-mode
(defun +clojure--cider-setup-debug ()
"Setup cider debug to override evil keys cleanly"
(evil-make-overriding-map cider--debug-mode-map 'normal)
(evil-normalize-keymaps))))
(when (featurep! :ui modeline +light)
(defvar-local cider-modeline-icon nil)
(defun +clojure--cider-set-modeline (face label)
"Update repl icon on modeline with cider information."
(setq cider-modeline-icon (concat
" "
(+modeline-format-icon 'faicon "terminal" "" face label -0.0575)
" "))
(add-to-list 'global-mode-string
'(t (:eval cider-modeline-icon))
'append))
(add-hook! '(cider-connected-hook
cider-disconnected-hook
cider-mode-hook)
(defun +clojure--cider-connected-update-modeline ()
"Update modeline with cider connection state."
(let* ((connected (cider-connected-p))
(face (if connected 'warning 'breakpoint-disabled))
(label (if connected "Cider connected" "Cider disconnected")))
(+clojure--cider-set-modeline face label))))
(add-hook! '(cider-before-eval-hook)
(defun +clojure--cider-before-eval-hook-update-modeline ()
"Update modeline with cider state before eval."
(+clojure--cider-set-modeline 'warning "Cider evaluating")))
(add-hook! '(cider-after-eval-done-hook)
(defun +clojure--cider-after-eval-done-hook-update-modeline ()
"Update modeline with cider state after eval."
(+clojure--cider-set-modeline 'success "Cider syncronized")))
(add-hook! '(cider-file-loaded-hook)
(defun +clojure--cider-file-loaded-update-modeline ()
"Update modeline with cider file loaded state."
(+clojure--cider-set-modeline 'success "Cider syncronized"))))
;; Ensure that CIDER is used for sessions in org buffers.
(when (featurep! :lang org)
(after! ob-clojure
(setq! org-babel-clojure-backend 'cider)))
;; The CIDER welcome message obscures error messages that the above code is
;; supposed to be make visible.
(setq cider-repl-display-help-banner nil)
(map! (:localleader
(:map (clojure-mode-map clojurescript-mode-map clojurec-mode-map)
"'" #'cider-jack-in-clj
"\"" #'cider-jack-in-cljs
"c" #'cider-connect-clj
"C" #'cider-connect-cljs
"m" #'cider-macroexpand-1
"M" #'cider-macroexpand-all
(:prefix ("d" . "debug")
"d" #'cider-debug-defun-at-point)
(:prefix ("e" . "eval")
"b" #'cider-eval-buffer
"d" #'cider-eval-defun-at-point
"D" #'cider-insert-defun-in-repl
"e" #'cider-eval-last-sexp
"E" #'cider-insert-last-sexp-in-repl
"r" #'cider-eval-region
"R" #'cider-insert-region-in-repl
"u" #'cider-undef)
(:prefix ("g" . "goto")
"b" #'cider-pop-back
"g" #'cider-find-var
"n" #'cider-find-ns)
(:prefix ("h" . "help")
"n" #'cider-find-ns
"a" #'cider-apropos
"c" #'cider-clojuredocs
"d" #'cider-doc
"j" #'cider-javadoc
"w" #'cider-clojuredocs-web)
(:prefix ("i" . "inspect")
"e" #'cider-enlighten-mode
"i" #'cider-inspect
"r" #'cider-inspect-last-result)
(:prefix ("n" . "namespace")
"n" #'cider-browse-ns
"N" #'cider-browse-ns-all
"r" #'cider-ns-refresh)
(:prefix ("p" . "print")
"p" #'cider-pprint-eval-last-sexp
"P" #'cider-pprint-eval-last-sexp-to-comment
"d" #'cider-pprint-eval-defun-at-point
"D" #'cider-pprint-eval-defun-to-comment
"r" #'cider-pprint-eval-last-sexp-to-repl)
(:prefix ("r" . "repl")
"n" #'cider-repl-set-ns
"q" #'cider-quit
"r" #'cider-ns-refresh
"R" #'cider-restart
"b" #'cider-switch-to-repl-buffer
"B" #'+clojure/cider-switch-to-repl-buffer-and-switch-ns
"c" #'cider-find-and-clear-repl-output
"l" #'cider-load-buffer
"L" #'cider-load-buffer-and-switch-to-repl-buffer)
(:prefix ("t" . "test")
"a" #'cider-test-rerun-test
"l" #'cider-test-run-loaded-tests
"n" #'cider-test-run-ns-tests
"p" #'cider-test-run-project-tests
"r" #'cider-test-rerun-failed-tests
"s" #'cider-test-run-ns-tests-with-filters
"t" #'cider-test-run-test)))
(:when (featurep! :editor evil +everywhere)
:map cider-repl-mode-map
:i [S-return] #'cider-repl-newline-and-indent
:i [M-return] #'cider-repl-return
(:localleader
"n" #'cider-repl-set-ns
"q" #'cider-quit
"r" #'cider-ns-refresh
"R" #'cider-restart
"c" #'cider-repl-clear-buffer)
:map cider-repl-history-mode-map
:i [return] #'cider-repl-history-insert-and-quit
:i "q" #'cider-repl-history-quit
:i "l" #'cider-repl-history-occur
:i "s" #'cider-repl-history-search-forward
:i "r" #'cider-repl-history-search-backward
:i "U" #'cider-repl-history-undo-other-window)))
(use-package! clj-refactor
:hook (clojure-mode . clj-refactor-mode)
:config
(set-lookup-handlers! 'clj-refactor-mode
:references #'cljr-find-usages)
(map! :map clojure-mode-map
:localleader
:desc "refactor" "R" #'hydra-cljr-help-menu/body))
(use-package! flycheck-clj-kondo
:when (featurep! :checkers syntax)
:after flycheck)

View File

@@ -0,0 +1,21 @@
;; -*- no-byte-compile: t; -*-
;;; lang/clojure/packages.el
;; HACK Fix #5577. Paredit is a cider dependency. We install paredit ourselves
;; to get it from emacsmirror, because the original upstream is a custom
;; git server with shallow clones disabled.
(package! paredit
:recipe (:host github :repo "emacsmirror/paredit")
:pin "8330a41e8188fe18d3fa805bb9aa529f015318e8")
;; HACK Forward declare these clj-refactor/cider deps so that their deps are
;; byte-compiled first.
(package! parseclj :pin "a8c4cf30fb68b66ae51541462a8b21753229a6e5")
(package! parseedn :pin "e5ba280d1fb7b408d54062d4eac545326e850172")
;;; Core packages
(package! clojure-mode :pin "e31186843d06ea86f3771244d1cde0112f9e2079")
(package! clj-refactor :pin "12af23ad8b76519cb8b95eec4e8a5706d3186cd0")
(package! cider :pin "af2e1649981729930efbbf58af232b3e413da0af")
(when (featurep! :checkers syntax)
(package! flycheck-clj-kondo :pin "d8a6ee9a16aa24b5be01f1edf9843d41bdc75555"))