diff --git a/CHANGELOG.develop b/CHANGELOG.develop index e9c1a4d2c..98ca9413c 100644 --- a/CHANGELOG.develop +++ b/CHANGELOG.develop @@ -1225,6 +1225,7 @@ Other: - Added =sayid= debugger (thanks to Daniel Manila and Arne Brasseur) - Added =flycheck-clojure= linters (thanks to Eugene Yaremenko) - Added =clj-kondo= to Clojure linters (thanks to Luo Tian and John Stevenson) + - Added =flycheck-joker= to Clojure linters (thanks to didibus) - Improvements: - Stored cider REPL history in spacemacs cache (thanks to Ryan Fowler) - Removed backtick from smartparens pairs for Clojure @@ -1240,6 +1241,8 @@ Other: (thanks to Dieter Komendera) - Improved spacemacs-jump-handlers (thanks to Ag Ibragimov) - Autoscroll to end of REPL when sending buffer content (thanks to Vitaly Banchenko) + - Added ability to use multiple linters together (thanks to didibus) +>>>>>>> fa4786d5a... [clojure] Added support for joker linter and having mutliple active linters - Key bindings: - ~SPC m e ;~ to eval sexp and show result as comment (thanks to John Stevenson) diff --git a/layers/+lang/clojure/README.org b/layers/+lang/clojure/README.org index 0642123f8..bd628e00b 100644 --- a/layers/+lang/clojure/README.org +++ b/layers/+lang/clojure/README.org @@ -13,7 +13,9 @@ - [[#enabling-sayid-or-clj-refactor][Enabling sayid or clj-refactor]] - [[#enabling-automatic-linting][Enabling Automatic Linting]] - [[#enable-clj-kondo-linter][Enable clj-kondo linter]] + - [[#enable-joker-linter][Enable joker linter]] - [[#enable-squiggly-linter][Enable Squiggly linter]] + - [[#enable-multiple-linters][Enable multiple linters]] - [[#starting-clojure-manually-outside-of-emacs][Starting Clojure manually (outside of Emacs)]] - [[#quick-start-with-boot][Quick Start with boot]] - [[#quick-start-with-lein][Quick Start with lein]] @@ -53,7 +55,7 @@ This layer adds support for [[https://clojure.org/][Clojure]] language using [[h - REPL via [[https://github.com/clojure-emacs/cider][CIDER]] - Code formatting via [[https://github.com/clojure-emacs/cider][CIDER]] using [[https://github.com/weavejester/cljfmt][Cljfmt]] - Refactoring via [[https://github.com/clojure-emacs/clj-refactor.el][clj-refactor]] -- Linting via [[https://github.com/clojure-emacs/squiggly-clojure][squiggly-clojure]] +- Linting via [[https://github.com/clojure-emacs/squiggly-clojure][squiggly-clojure]], [[https://github.com/borkdude/clj-kondo][clj-kondo]] or [[https://github.com/candid82/joker][joker]] - Aligning of code forms via [[https://github.com/clojure-emacs/clojure-mode][clojure-mode]] - Debugging with [[https://github.com/clojure-emacs/sayid][sayid]] - Clojure cheatsheet @@ -111,20 +113,17 @@ Enabling either of these packages will cause extra nREPL middleware to be injected when jacking in CIDER. ** Enabling Automatic Linting -[[https://github.com/borkdude/clj-kondo][clj-kondo]] and [[https://github.com/clojure-emacs/squiggly-clojure][squiggly-clojure]] provide automated linting via =flycheck=. -These packages disabled by default as they require the relevant linter binaries +[[https://github.com/borkdude/clj-kondo][clj-kondo]], [[https://github.com/candid82/joker][joker]] and [[https://github.com/clojure-emacs/squiggly-clojure][squiggly-clojure]] provide automated linting via =flycheck=. +These packages are disabled by default as they require the relevant linter binaries to be installed locally. -The recommended linter is [[https://github.com/borkdude/clj-kondo][clj-kondo]] +The recommended linter is to use [[https://github.com/borkdude/clj-kondo][clj-kondo]] in combination with [[https://github.com/candid82/joker][joker]]. Together they catch the most issues while both running extremely quickly. squiggly reloads your code on every change which gives unexpected results if your code is not re-loadable. squiggly also requires =org.clojure/core.typed= be added to the development dependencies of your projects or build tool when using =cider-connect=. -Joker is a Clojure linter written in Go and is available separately via the -[[https://github.com/n2o/clojure-lint-spacemacs-layer][unofficial clojure-lint layer]] - *** Enable clj-kondo linter This linter based on static syntax checking and requires the [[https://github.com/borkdude/clj-kondo][clj-kondo]] binary installed on the system PATH that =spacemacs.env= includes. Please read the @@ -149,6 +148,30 @@ to your Spacemacs configuration: ) #+END_SRC +*** Enable joker linter +This linter is based on static syntax checking and requires the [[https://github.com/candid82/joker][joker]] binary +installed on the system PATH that =spacemacs.env= includes. Please read the +[[https://github.com/candid82/joker#installation][joker binary installation instructions]] + +Enable the joker automatic linter in Spacemacs by adding a =:variables= option +to your Spacemacs configuration: + +#+BEGIN_SRC emacs-lisp + ;; Witout any variables your configuration would just include clojure + dotspacemacs-configuration-layers + '(... + clojure + ) + + ;; to use joker as a linter, add this variable to the clojure layer + ;; wrapping the clojure layer in a list + dotspacemacs-configuration-layers + '(... + (clojure :variables + clojure-enable-linters 'joker) + ) +#+END_SRC + *** Enable Squiggly linter [[https://github.com/clojure-emacs/squiggly-clojure][squiggly-clojure]] uses [[https://github.com/jonase/eastwood][Eastwood]] and [[https://github.com/jonase/kibit][Kibit]] for linting. Please install these projects before configuring Spacemacs with =squiggly=. @@ -157,8 +180,7 @@ Make sure to read the [[https://github.com/clojure-emacs/squiggly-clojure#warnin Please read the section on [[https://github.com/clojure-emacs/squiggly-clojure#dependencies-in-clojure][squiggly dependencies]] if you are using =cider-connect= -Enable the clj-kondo automatic linter in Spacemacs by adding a =:variables= option -to your Spacemacs configuration: +Enable the squiggly (eastwood, kibit and core.typed) automatic linter in Spacemacs by adding a =:variables= option to your Spacemacs configuration: #+BEGIN_SRC emacs-lisp ;; Without any variables your configuration would just include Clojure @@ -179,6 +201,29 @@ to your Spacemacs configuration: Troubleshooting: please read [[https://github.com/clojure-emacs/squiggly-clojure#debugging-and-bug-reporting][debugging and bug reporting]] and try to reproduce using the [[https://github.com/clojure-emacs/squiggly-clojure/tree/master/sample-project][sample project]]. +*** Enable multiple linters +You can choose to enable multiple linters and have them all run together. This gives you better linting coverage, as they don't catch all the same issues. + +You will need to follow their individual install instructions first, to get all their necessary binaries and configs on the system PATH that =spacemacs.env= includes. Please refer to their individual Enable ... linter section respectively. + +Once all the linters you want to enable are installed, you simply need to specify a list of them in the =:variables= option to your Spacemacs configuration: + +#+BEGIN_SRC emacs-lisp + ;; Witout any variables your configuration would just include clojure + dotspacemacs-configuration-layers + '(... + clojure + ) + + ;; to enable multiple linters, say clj-kondo and joker, add a list variable to the clojure layer + ;; listing each one in the order you want them running, wrapping the clojure layer in a list + dotspacemacs-configuration-layers + '(... + (clojure :variables + clojure-enable-linters '(clj-kondo joker)) + ) +#+END_SRC + ** Starting Clojure manually (outside of Emacs) CIDER communicates with your Clojure process through nREPL, and for CIDER to function correctly extra nREPL middleware needs to be present diff --git a/layers/+lang/clojure/packages.el b/layers/+lang/clojure/packages.el index 647aa34d1..dc0439251 100644 --- a/layers/+lang/clojure/packages.el +++ b/layers/+lang/clojure/packages.el @@ -20,8 +20,15 @@ eldoc evil-cleverparens flycheck - (flycheck-clojure :toggle (eq clojure-enable-linters 'squiggly)) - (flycheck-clj-kondo :toggle (eq clojure-enable-linters 'clj-kondo)) + (flycheck-clojure :toggle (memq 'squiggly (if (listp clojure-enable-linters) + clojure-enable-linters + (list clojure-enable-linters)))) + (flycheck-clj-kondo :toggle (memq 'clj-kondo (if (listp clojure-enable-linters) + clojure-enable-linters + (list clojure-enable-linters)))) + (flycheck-joker :toggle (memq 'joker (if (listp clojure-enable-linters) + clojure-enable-linters + (list clojure-enable-linters)))) ggtags counsel-gtags helm-gtags @@ -173,9 +180,6 @@ ;; add support for golden-ratio (with-eval-after-load 'golden-ratio (add-to-list 'golden-ratio-extra-commands 'cider-popup-buffer-quit-function)) - ;; setup linters. NOTE: It must be done after both CIDER and Flycheck are loaded. - (when (eq clojure-enable-linters 'squiggly) - (with-eval-after-load 'flycheck (flycheck-clojure-setup))) ;; add support for evil (evil-set-initial-state 'cider-stacktrace-mode 'motion) (evil-set-initial-state 'cider-popup-buffer-mode 'motion) @@ -435,13 +439,70 @@ (add-hook 'clojure-mode-hook 'parinfer-mode)) (defun clojure/post-init-flycheck () + ;; When user has chosen to use multiple linters. + (when (> (safe-length clojure-enable-linters) 1) + ;; If adding a linter, you must add to checkers-per-mode for each mode + ;; it can support the mapping from the linter name in clojure-enable-linters + ;; to the flycheck checker to use to add to flycheck. + (let* ((checkers-per-mode '((clj . ((clj-kondo . clj-kondo-clj) + (joker . clojure-joker) + (squiggly . clojure-cider-eastwood))) + (cljc . ((clj-kondo . clj-kondo-cljc) + (joker . clojure-joker))) + (cljs . ((clj-kondo . clj-kondo-cljs) + (joker . clojurescript-joker))) + (edn . ((clj-kondo . clj-kondo-edn) + (joker . edn-joker)))))) + ;; For each checker mode + (dolist (mode-checkers checkers-per-mode) + ;; We find the first checker in order from the user configured linters which + ;; the mode supports and make it the primary-linter. All other linters after that + ;; the mode support is made a next-linter. Finally, we extract the checkers of the + ;; primary linter and the next linters. + (let* ((checkers (cdr mode-checkers)) + (primary-linter (seq-find (lambda (l) + (assq l checkers)) + clojure-enable-linters)) + (primary-checker (cdr (assq primary-linter checkers))) + (next-linters (seq-filter (lambda (l) + (and (not (eq l primary-linter)) + (assq l checkers))) + clojure-enable-linters)) + (next-checkers (mapcar (lambda (l) + (cdr (assq l checkers))) + next-linters))) + ;; Move primary checker to the front of flycheck lists of checkers so that + ;; it is used as the primary checker, because flycheck picks the first one + ;; it finds. + (delq primary-checker flycheck-checkers) + (push primary-checker flycheck-checkers) + ;; For every checker, set their next checkers, starting with the primary + ;; checker which has all others has a next-checker, and then the next + ;; one has all the ones after it, and so on, until the last one which + ;; has no next-checker to be added. This is because flycheck next-checkers + ;; must be nested if we want more than two to run. It will pick the first + ;; available next-checker from next-checkers and run that after. If we want + ;; a checker after that one, it must also have next-checkers configured. + (let ((checkers-to-add next-checkers)) + (dolist (checker (cons primary-checker next-checkers)) + (dolist (next-checker checkers-to-add) + (flycheck-add-next-checker checker next-checker t)) + (setq checkers-to-add (cdr checkers-to-add)))))))) (spacemacs|forall-clojure-modes m (spacemacs/enable-flycheck m))) (defun clojure/init-flycheck-clojure () (use-package flycheck-clojure - :if (configuration-layer/package-usedp 'flycheck))) + :if (configuration-layer/package-usedp 'flycheck) + :config (progn + (flycheck-clojure-setup) + (with-eval-after-load 'cider + (flycheck-clojure-inject-jack-in-dependencies))))) (defun clojure/init-flycheck-clj-kondo () (use-package flycheck-clj-kondo :if (configuration-layer/package-usedp 'flycheck))) + +(defun clojure/init-flycheck-joker () + (use-package flycheck-joker + :if (configuration-layer/package-usedp 'flycheck)))