cider-mode.el (48689B)
1 ;;; cider-mode.el --- Minor mode for REPL interactions -*- lexical-binding: t -*- 2 3 ;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov 4 ;; Copyright © 2013-2023 Bozhidar Batsov, Artur Malabarba and CIDER contributors 5 ;; 6 ;; Author: Tim King <kingtim@gmail.com> 7 ;; Phil Hagelberg <technomancy@gmail.com> 8 ;; Bozhidar Batsov <bozhidar@batsov.dev> 9 ;; Artur Malabarba <bruce.connor.am@gmail.com> 10 ;; Hugo Duncan <hugo@hugoduncan.org> 11 ;; Steve Purcell <steve@sanityinc.com> 12 13 ;; This program is free software: you can redistribute it and/or modify 14 ;; it under the terms of the GNU General Public License as published by 15 ;; the Free Software Foundation, either version 3 of the License, or 16 ;; (at your option) any later version. 17 18 ;; This program is distributed in the hope that it will be useful, 19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 ;; GNU General Public License for more details. 22 23 ;; You should have received a copy of the GNU General Public License 24 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 25 26 ;; This file is not part of GNU Emacs. 27 28 ;;; Commentary: 29 30 ;; Minor mode for REPL interactions. 31 32 ;;; Code: 33 34 (require 'clojure-mode) 35 (require 'cider-eval) 36 (require 'cider-test) ; required only for the menu 37 (require 'cider-eldoc) 38 (require 'cider-resolve) 39 (require 'cider-doc) ; required only for the menu 40 (require 'cider-profile) ; required only for the menu 41 (require 'cider-completion) 42 (require 'cider-inspector) 43 (require 'cider-find) 44 (require 'subr-x) 45 46 (defcustom cider-mode-line-show-connection t 47 "If the mode-line lighter should detail the connection." 48 :group 'cider 49 :type 'boolean 50 :package-version '(cider "0.10.0")) 51 52 (defun cider--modeline-info () 53 "Return info for the cider mode modeline. 54 Info contains the connection type, project name and host:port endpoint." 55 (if-let* ((current-connection (ignore-errors (cider-current-repl)))) 56 (with-current-buffer current-connection 57 (concat 58 (symbol-name cider-repl-type) 59 (when cider-mode-line-show-connection 60 (format ":%s@%s:%s" 61 (or (cider--project-name nrepl-project-dir) "<no project>") 62 (pcase (plist-get nrepl-endpoint :host) 63 ("localhost" "") 64 (x x)) 65 (plist-get nrepl-endpoint :port))))) 66 "not connected")) 67 68 ;;;###autoload 69 (defcustom cider-mode-line 70 '(:eval (format " cider[%s]" (cider--modeline-info))) 71 "Mode line lighter for cider mode. 72 73 The value of this variable is a mode line template as in 74 `mode-line-format'. See Info Node `(elisp)Mode Line Format' for details 75 about mode line templates. 76 77 Customize this variable to change how cider mode displays its status in the 78 mode line. The default value displays the current connection. Set this 79 variable to nil to disable the mode line entirely." 80 :group 'cider 81 :type 'sexp 82 :risky t 83 :package-version '(cider "0.7.0")) 84 85 86 ;;; Switching between REPL & source buffers 87 88 (defun cider--switch-to-repl-buffer (repl-buffer &optional set-namespace) 89 "Select the REPL-BUFFER, when possible in an existing window. 90 When SET-NAMESPACE is t, sets the namespace in the REPL buffer to 91 that of the namespace in the Clojure source buffer." 92 (let ((buffer (current-buffer))) 93 ;; first we switch to the REPL buffer 94 (if cider-repl-display-in-current-window 95 (pop-to-buffer-same-window repl-buffer) 96 (pop-to-buffer repl-buffer)) 97 ;; then if necessary we update its namespace 98 (when set-namespace 99 (cider-repl-set-ns (with-current-buffer buffer (cider-current-ns)))) 100 (goto-char (point-max)))) 101 102 (defun cider-switch-to-repl-buffer (&optional set-namespace) 103 "Switch to current REPL buffer, when possible in an existing window. 104 The type of the REPL is inferred from the mode of current buffer. With a 105 prefix arg SET-NAMESPACE sets the namespace in the REPL buffer to that of 106 the namespace in the Clojure source buffer" 107 (interactive "P") 108 (cider--switch-to-repl-buffer 109 (cider-current-repl nil 'ensure) 110 set-namespace)) 111 112 (declare-function cider-load-buffer "cider-eval") 113 114 (defun cider-load-buffer-and-switch-to-repl-buffer (&optional set-namespace) 115 "Load the current buffer into the matching REPL buffer and switch to it. 116 When SET-NAMESPACE is true, we'll also set the REPL's ns to match that of the 117 Clojure buffer." 118 (interactive "P") 119 (cider-load-buffer) 120 (cider-switch-to-repl-buffer set-namespace)) 121 122 (defun cider-switch-to-last-clojure-buffer () 123 "Switch to the last Clojure buffer. 124 The default keybinding for this command is 125 the same as variable `cider-switch-to-repl-buffer', 126 so that it is very convenient to jump between a 127 Clojure buffer and the REPL buffer." 128 (interactive) 129 (if (derived-mode-p 'cider-repl-mode) 130 (let* ((a-buf) 131 (the-buf (let ((repl-type (cider-repl-type-for-buffer))) 132 (seq-find (lambda (b) 133 (unless (with-current-buffer b (derived-mode-p 'cider-repl-mode)) 134 (when-let* ((type (cider-repl-type-for-buffer b))) 135 (unless a-buf 136 (setq a-buf b)) 137 (or (eq type 'multi) 138 (eq type repl-type))))) 139 (buffer-list))))) 140 (if-let* ((buf (or the-buf a-buf))) 141 (if cider-repl-display-in-current-window 142 (pop-to-buffer-same-window buf) 143 (pop-to-buffer buf)) 144 (user-error "No Clojure buffer found"))) 145 (user-error "Not in a CIDER REPL buffer"))) 146 147 (defun cider-find-and-clear-repl-output (&optional clear-repl) 148 "Find the current REPL buffer and clear it. 149 With a prefix argument CLEAR-REPL the command clears the entire REPL 150 buffer. Returns to the buffer in which the command was invoked. See also 151 the related commands `cider-repl-clear-buffer' and 152 `cider-repl-clear-output'." 153 (interactive "P") 154 (let ((origin-buffer (current-buffer))) 155 (switch-to-buffer (cider-current-repl nil 'ensure)) 156 (if clear-repl 157 (cider-repl-clear-buffer) 158 (cider-repl-clear-output)) 159 (switch-to-buffer origin-buffer))) 160 161 ;;; cider-run 162 (defvar cider--namespace-history nil 163 "History of user input for namespace prompts.") 164 165 (defun cider--var-namespace (var) 166 "Return the namespace of VAR. 167 VAR is a fully qualified Clojure variable name as a string." 168 (replace-regexp-in-string "\\(?:#'\\)?\\(.*\\)/.*" "\\1" var)) 169 170 (defun cider-run (&optional function) 171 "Run -main or FUNCTION, prompting for its namespace if necessary. 172 With a prefix argument, prompt for function to run instead of -main." 173 (interactive (list (when current-prefix-arg (read-string "Function name: ")))) 174 (cider-ensure-connected) 175 (let ((name (or function "-main"))) 176 (when-let* ((response (cider-nrepl-send-sync-request 177 `("op" "ns-list-vars-by-name" 178 "name" ,name)))) 179 (if-let* ((vars (split-string (substring (nrepl-dict-get response "var-list") 1 -1)))) 180 (cider-interactive-eval 181 (if (= (length vars) 1) 182 (concat "(" (car vars) ")") 183 (let* ((completions (mapcar #'cider--var-namespace vars)) 184 (def (or (car cider--namespace-history) 185 (car completions)))) 186 (format "(#'%s/%s)" 187 (completing-read (format "Namespace (%s): " def) 188 completions nil t nil 189 'cider--namespace-history def) 190 name)))) 191 (user-error "No %s var defined in any namespace" (cider-propertize name 'fn)))))) 192 193 ;;; Insert (and eval) in REPL functionality 194 (defvar cider-insert-commands-map 195 (let ((map (define-prefix-command 'cider-insert-commands-map))) 196 ;; single key bindings defined last for display in menu 197 (define-key map (kbd "e") #'cider-insert-last-sexp-in-repl) 198 (define-key map (kbd "d") #'cider-insert-defun-in-repl) 199 (define-key map (kbd "r") #'cider-insert-region-in-repl) 200 (define-key map (kbd "n") #'cider-insert-ns-form-in-repl) 201 202 ;; duplicates with C- for convenience 203 (define-key map (kbd "C-e") #'cider-insert-last-sexp-in-repl) 204 (define-key map (kbd "C-d") #'cider-insert-defun-in-repl) 205 (define-key map (kbd "C-r") #'cider-insert-region-in-repl) 206 (define-key map (kbd "C-n") #'cider-insert-ns-form-in-repl))) 207 208 (defcustom cider-switch-to-repl-on-insert t 209 "Whether to switch to the REPL when inserting a form into the REPL." 210 :type 'boolean 211 :group 'cider 212 :package-version '(cider . "0.21.0")) 213 214 (defcustom cider-invert-insert-eval-p nil 215 "Whether to invert the behavior of evaling. 216 Default behavior when inserting is to NOT eval the form and only eval with 217 a prefix. This allows to invert this so that default behavior is to insert 218 and eval and the prefix is required to prevent evaluation." 219 :type 'boolean 220 :group 'cider 221 :package-version '(cider . "0.18.0")) 222 223 (defun cider-insert-in-repl (form eval) 224 "Insert FORM in the REPL buffer and switch to it. 225 If EVAL is non-nil the form will also be evaluated. Use 226 `cider-invert-insert-eval-p' to invert this behavior." 227 (while (string-match "\\`[ \t\n\r]+\\|[ \t\n\r]+\\'" form) 228 (setq form (replace-match "" t t form))) 229 (when cider-switch-to-repl-on-insert 230 (cider-switch-to-repl-buffer)) 231 (let ((repl (cider-current-repl))) 232 (with-selected-window (or (get-buffer-window repl t) 233 (selected-window)) 234 (with-current-buffer repl 235 (goto-char (point-max)) 236 (let ((beg (point))) 237 (insert form) 238 (indent-region beg (point)) 239 (font-lock-ensure beg (point))) 240 (when (if cider-invert-insert-eval-p 241 (not eval) 242 eval) 243 (cider-repl-return)) 244 (goto-char (point-max)))))) 245 246 (defun cider-insert-last-sexp-in-repl (&optional arg) 247 "Insert the expression preceding point in the REPL buffer. 248 If invoked with a prefix ARG eval the expression after inserting it." 249 (interactive "P") 250 (cider-insert-in-repl (cider-last-sexp) arg)) 251 252 (defun cider-insert-defun-in-repl (&optional arg) 253 "Insert the top level form at point in the REPL buffer. 254 If invoked with a prefix ARG eval the expression after inserting it." 255 (interactive "P") 256 (cider-insert-in-repl (cider-defun-at-point) arg)) 257 258 (defun cider-insert-region-in-repl (start end &optional arg) 259 "Insert the current region in the REPL buffer. 260 START and END represent the region's boundaries. 261 If invoked with a prefix ARG eval the expression after inserting it." 262 (interactive "rP") 263 (cider-insert-in-repl 264 (buffer-substring-no-properties start end) arg)) 265 266 (defun cider-insert-ns-form-in-repl (&optional arg) 267 "Insert the current buffer's ns form in the REPL buffer. 268 If invoked with a prefix ARG eval the expression after inserting it." 269 (interactive "P") 270 (cider-insert-in-repl (cider-ns-form) arg)) 271 272 273 274 ;;; The menu-bar 275 (defconst cider-mode-menu 276 `("CIDER" 277 ["Start or connect to any REPL" cider 278 :help "A simple wrapper around all commands for starting/connecting to a REPL."] 279 ("Clojure" 280 ["Start a Clojure REPL" cider-jack-in 281 :help "Starts an nREPL server and connects a Clojure REPL to it."] 282 ["Connect to a Clojure REPL" cider-connect 283 :help "Connects to a REPL that's already running."]) 284 ("ClojureScript" 285 ["Start a ClojureScript REPL" cider-jack-in-cljs 286 :help "Starts an nREPL server and connects a ClojureScript REPL to it."] 287 ["Connect to a ClojureScript REPL" cider-connect-clojurescript 288 :help "Connects to a ClojureScript REPL that's already running."] 289 ["Create a ClojureScript REPL from a Clojure REPL" cider-jack-in-sibling-clojurescript]) 290 "--" 291 ["Quit" cider-quit :active (cider-connected-p)] 292 ["Restart" cider-restart :active (cider-connected-p)] 293 "--" 294 ["Connection info" cider-describe-connection 295 :active (cider-connected-p)] 296 ["Select any CIDER buffer" cider-selector] 297 "--" 298 ["Configure CIDER" (customize-group 'cider)] 299 "--" 300 ["A sip of CIDER" cider-drink-a-sip] 301 ["View user manual" cider-view-manual] 302 ["View quick reference card" cider-view-refcard] 303 ["Report a bug" cider-report-bug] 304 ["Version info" cider-version] 305 "--" 306 ["Close ancillary buffers" cider-close-ancillary-buffers 307 :active (seq-remove #'null cider-ancillary-buffers)] 308 ("nREPL" :active (cider-connected-p) 309 ["List nREPL middleware" cider-list-nrepl-middleware] 310 ["Describe nREPL session" cider-describe-nrepl-session] 311 ["Toggle message logging" nrepl-toggle-message-logging])) 312 "Menu for CIDER mode.") 313 314 (defconst cider-mode-eval-menu 315 '("CIDER Eval" :visible (cider-connected-p) 316 ["Eval top-level sexp" cider-eval-defun-at-point] 317 ["Eval top-level sexp to point" cider-eval-defun-up-to-point] 318 ["Eval top-level sexp to comment" cider-eval-defun-to-comment] 319 ["Eval top-level sexp and pretty-print to comment" cider-pprint-eval-defun-to-comment] 320 "--" 321 ["Eval current list" cider-eval-list-at-point] 322 ["Eval current sexp" cider-eval-sexp-at-point] 323 ["Eval and tap current sexp" cider-tap-sexp-at-point] 324 ["Eval current sexp to point" cider-eval-sexp-up-to-point] 325 ["Eval current sexp in context" cider-eval-sexp-at-point-in-context] 326 "--" 327 ["Eval last sexp" cider-eval-last-sexp] 328 ["Eval and tap last sexp" cider-tap-last-sexp] 329 ["Eval last sexp in context" cider-eval-last-sexp-in-context] 330 ["Eval last sexp and insert" cider-eval-print-last-sexp 331 :keys "\\[universal-argument] \\[cider-eval-last-sexp]"] 332 ["Eval last sexp in popup buffer" cider-pprint-eval-last-sexp] 333 ["Eval last sexp and replace" cider-eval-last-sexp-and-replace] 334 ["Eval last sexp to REPL" cider-eval-last-sexp-to-repl] 335 ["Eval last sexp and pretty-print to REPL" cider-pprint-eval-last-sexp-to-repl] 336 ["Eval last sexp and pretty-print to comment" cider-pprint-eval-last-sexp-to-comment] 337 "--" 338 ["Eval selected region" cider-eval-region] 339 ["Eval ns form" cider-eval-ns-form] 340 "--" 341 ["Interrupt evaluation" cider-interrupt] 342 "--" 343 ["Insert last sexp in REPL" cider-insert-last-sexp-in-repl] 344 ["Insert last sexp in REPL and eval" (cider-insert-last-sexp-in-repl t) 345 :keys "\\[universal-argument] \\[cider-insert-last-sexp-in-repl]"] 346 ["Insert top-level sexp in REPL" cider-insert-defun-in-repl] 347 ["Insert region in REPL" cider-insert-region-in-repl] 348 ["Insert ns form in REPL" cider-insert-ns-form-in-repl] 349 "--" 350 ["Load this buffer" cider-load-buffer] 351 ["Load this buffer and switch to REPL" cider-load-buffer-and-switch-to-repl-buffer] 352 ["Load another file" cider-load-file] 353 ["Recursively load all files in directory" cider-load-all-files] 354 ["Load all project files" cider-load-all-project-ns] 355 ["Refresh loaded code" cider-ns-refresh] 356 ["Require and reload" cider-ns-reload] 357 ["Require and reload all" cider-ns-reload-all] 358 ["Run project (-main function)" cider-run]) 359 "Menu for CIDER mode eval commands.") 360 361 (defconst cider-mode-interactions-menu 362 `("CIDER Interactions" :visible (cider-connected-p) 363 ["Complete symbol" complete-symbol] 364 "--" 365 ("REPL" 366 ["Set REPL to this ns" cider-repl-set-ns] 367 ["Switch to REPL" cider-switch-to-repl-buffer] 368 ["REPL Pretty Print" cider-repl-toggle-pretty-printing 369 :style toggle :selected cider-repl-use-pretty-printing] 370 ["Clear latest output" cider-find-and-clear-repl-output] 371 ["Clear all output" (cider-find-and-clear-repl-output t) 372 :keys "\\[universal-argument] \\[cider-find-and-clear-repl-output]"] 373 "--" 374 ["Configure the REPL" (customize-group 'cider-repl)]) 375 ,cider-doc-menu 376 ("Find (jump to)" 377 ["Find definition" cider-find-var] 378 ["Find namespace" cider-find-ns] 379 ["Find resource" cider-find-resource] 380 ["Find keyword" cider-find-keyword] 381 ["Go back" cider-pop-back]) 382 ("Xref" 383 ["Find fn references" cider-xref-fn-refs] 384 ["Find fn references and select" cider-xref-fn-refs-select] 385 ["Find fn dependencies" cider-xref-fn-defs] 386 ["Find fn dependencies and select" cider-xref-fn-defs-select]) 387 ("Browse" 388 ["Browse namespace" cider-browse-ns] 389 ["Browse all namespaces" cider-browse-ns-all] 390 ["Browse spec" cider-browse-spec] 391 ["Browse all specs" cider-browse-spec-all] 392 ["Browse REPL input history" cider-repl-history] 393 ["Browse classpath" cider-classpath] 394 ["Browse classpath entry" cider-open-classpath-entry]) 395 ("Format" 396 ["Format EDN last sexp" cider-format-edn-last-sexp] 397 ["Format EDN region" cider-format-edn-region] 398 ["Format EDN buffer" cider-format-edn-buffer]) 399 ("Macroexpand" 400 ["Macroexpand-1" cider-macroexpand-1] 401 ["Macroexpand-all" cider-macroexpand-all]) 402 ,cider-test-menu 403 ("Debug" 404 ["Inspect" cider-inspect] 405 ["Toggle var tracing" cider-toggle-trace-var] 406 ["Toggle ns tracing" cider-toggle-trace-ns] 407 "--" 408 ["Debug top-level form" cider-debug-defun-at-point 409 :keys "\\[universal-argument] \\[cider-eval-defun-at-point]"] 410 ["List instrumented defs" cider-browse-instrumented-defs] 411 "--" 412 ["Configure the Debugger" (customize-group 'cider-debug)]) 413 ,cider-profile-menu 414 ("Misc" 415 ["Clojure Cheatsheet" cider-cheatsheet] 416 ["Flush completion cache" cider-completion-flush-caches])) 417 "Menu for CIDER interactions.") 418 419 420 (declare-function cider-ns-refresh "cider-ns") 421 (declare-function cider-ns-reload "cider-ns") 422 (declare-function cider-ns-reload-all "cider-ns") 423 (declare-function cider-browse-ns "cider-browse-ns") 424 (declare-function cider-eval-ns-form "cider-eval") 425 (declare-function cider-repl-set-ns "cider-repl") 426 (declare-function cider-find-ns "cider-find") 427 428 (defvar cider-ns-map 429 (let ((map (define-prefix-command 'cider-ns-map))) 430 (define-key map (kbd "b") #'cider-browse-ns) 431 (define-key map (kbd "M-b") #'cider-browse-ns) 432 (define-key map (kbd "e") #'cider-eval-ns-form) 433 (define-key map (kbd "M-e") #'cider-eval-ns-form) 434 (define-key map (kbd "f") #'cider-find-ns) 435 (define-key map (kbd "M-f") #'cider-find-ns) 436 (define-key map (kbd "n") #'cider-repl-set-ns) 437 (define-key map (kbd "M-n") #'cider-repl-set-ns) 438 (define-key map (kbd "r") #'cider-ns-refresh) 439 (define-key map (kbd "M-r") #'cider-ns-refresh) 440 (define-key map (kbd "l") #'cider-ns-reload) 441 (define-key map (kbd "M-l") #'cider-ns-reload-all) 442 map) 443 "CIDER NS keymap.") 444 445 ;; Those declares are needed, because we autoload all those commands when first 446 ;; used. That optimizes CIDER's initial load time. 447 (declare-function cider-macroexpand-1 "cider-macroexpansion") 448 (declare-function cider-macroexpand-all "cider-macroexpansion") 449 (declare-function cider-selector "cider-selector") 450 (declare-function cider-toggle-trace-ns "cider-tracing") 451 (declare-function cider-toggle-trace-var "cider-tracing") 452 (declare-function cider-find-resource "cider-find") 453 (declare-function cider-find-keyword "cider-find") 454 (declare-function cider-find-var "cider-find") 455 (declare-function cider-find-dwim-at-mouse "cider-find") 456 (declare-function cider-xref-fn-refs "cider-xref") 457 (declare-function cider-xref-fn-refs-select "cider-xref") 458 (declare-function cider-xref-fn-deps "cider-xref") 459 (declare-function cider-xref-fn-deps-select "cider-xref") 460 461 (defconst cider--has-many-mouse-buttons (not (memq window-system '(mac ns))) 462 "Non-nil if system binds forward and back buttons to <mouse-8> and <mouse-9>. 463 464 As it stands Emacs fires these events on <mouse-8> and <mouse-9> on 'x' and 465 'w32'systems while on macOS it presents them on <mouse-4> and <mouse-5>.") 466 467 (defcustom cider-use-xref t 468 "Enable xref integration." 469 :type 'boolean 470 :safe #'booleanp 471 :group 'cider 472 :version '(cider . "1.2.0")) 473 474 (defcustom cider-xref-fn-depth -90 475 "The depth to use when adding the CIDER xref function to the relevant hook. 476 By convention this is a number between -100 and 100, lower numbers indicating a 477 higher precedence." 478 :type 'integer 479 :group 'cider 480 :version '(cider . "1.2.0")) 481 482 (defconst cider-mode-map 483 (let ((map (make-sparse-keymap))) 484 (define-key map (kbd "C-c C-d") 'cider-doc-map) 485 (unless cider-use-xref 486 (define-key map (kbd "M-.") #'cider-find-var) 487 (define-key map (kbd "M-,") #'cider-pop-back)) 488 (define-key map (kbd (if cider--has-many-mouse-buttons "<mouse-8>" "<mouse-4>")) #'xref-pop-marker-stack) 489 (define-key map (kbd (if cider--has-many-mouse-buttons "<mouse-9>" "<mouse-5>")) #'cider-find-dwim-at-mouse) 490 (define-key map (kbd "C-c C-.") #'cider-find-ns) 491 (define-key map (kbd "C-c C-:") #'cider-find-keyword) 492 (define-key map (kbd "C-c M-.") #'cider-find-resource) 493 (define-key map (kbd "M-TAB") #'complete-symbol) 494 (define-key map (kbd "C-M-x") #'cider-eval-defun-at-point) 495 (define-key map (kbd "C-c C-c") #'cider-eval-defun-at-point) 496 (define-key map (kbd "C-x C-e") #'cider-eval-last-sexp) 497 (define-key map (kbd "C-c C-e") #'cider-eval-last-sexp) 498 (define-key map (kbd "C-c C-p") #'cider-pprint-eval-last-sexp) 499 (define-key map (kbd "C-c C-f") #'cider-pprint-eval-defun-at-point) 500 (define-key map (kbd "C-c C-v") 'cider-eval-commands-map) 501 (define-key map (kbd "C-c C-j") 'cider-insert-commands-map) 502 (define-key map (kbd "C-c M-;") #'cider-eval-defun-to-comment) 503 (define-key map (kbd "C-c M-e") #'cider-eval-last-sexp-to-repl) 504 (define-key map (kbd "C-c M-p") #'cider-insert-last-sexp-in-repl) 505 (define-key map (kbd "C-c M-:") #'cider-read-and-eval) 506 (define-key map (kbd "C-c C-u") #'cider-undef) 507 (define-key map (kbd "C-c C-M-u") #'cider-undef-all) 508 (define-key map (kbd "C-c C-m") #'cider-macroexpand-1) 509 (define-key map (kbd "C-c M-m") #'cider-macroexpand-all) 510 (define-key map (kbd "C-c M-n") 'cider-ns-map) 511 (define-key map (kbd "C-c M-i") #'cider-inspect) 512 (define-key map (kbd "C-c M-t v") #'cider-toggle-trace-var) 513 (define-key map (kbd "C-c M-t n") #'cider-toggle-trace-ns) 514 (define-key map (kbd "C-c C-z") #'cider-switch-to-repl-buffer) 515 (define-key map (kbd "C-c M-z") #'cider-load-buffer-and-switch-to-repl-buffer) 516 (define-key map (kbd "C-c C-o") #'cider-find-and-clear-repl-output) 517 (define-key map (kbd "C-c C-k") #'cider-load-buffer) 518 (define-key map (kbd "C-c C-l") #'cider-load-file) 519 (define-key map (kbd "C-c C-M-l") #'cider-load-all-files) 520 (define-key map (kbd "C-c C-b") #'cider-interrupt) 521 (define-key map (kbd "C-c ,") 'cider-test-commands-map) 522 (define-key map (kbd "C-c C-t") 'cider-test-commands-map) 523 (define-key map (kbd "C-c M-s") #'cider-selector) 524 (define-key map (kbd "C-c M-d") #'cider-describe-connection) 525 (define-key map (kbd "C-c C-=") 'cider-profile-map) 526 (define-key map (kbd "C-c C-? r") #'cider-xref-fn-refs) 527 (define-key map (kbd "C-c C-? C-r") #'cider-xref-fn-refs-select) 528 (define-key map (kbd "C-c C-? d") #'cider-xref-fn-deps) 529 (define-key map (kbd "C-c C-? C-d") #'cider-xref-fn-deps-select) 530 (define-key map (kbd "C-c C-q") #'cider-quit) 531 (define-key map (kbd "C-c M-r") #'cider-restart) 532 (dolist (variable '(cider-mode-interactions-menu 533 cider-mode-eval-menu 534 cider-mode-menu)) 535 (easy-menu-do-define (intern (format "%s-open" variable)) 536 map 537 (get variable 'variable-documentation) 538 (cider--menu-add-help-strings (symbol-value variable)))) 539 map)) 540 541 ;; This menu works as an easy entry-point into CIDER. Even if cider.el isn't 542 ;; loaded yet, this will be shown in Clojure buffers next to the "Clojure" 543 ;; menu. 544 ;;;###autoload 545 (with-eval-after-load 'clojure-mode 546 (easy-menu-define cider-clojure-mode-menu-open clojure-mode-map 547 "Menu for Clojure mode. 548 This is displayed in `clojure-mode' buffers, if `cider-mode' is not active." 549 `("CIDER" :visible (not cider-mode) 550 ["Start a Clojure REPL" cider-jack-in-clj 551 :help "Starts an nREPL server and connects a Clojure REPL to it."] 552 ["Connect to a Clojure REPL" cider-connect-clj 553 :help "Connects to a REPL that's already running."] 554 ["Start a ClojureScript REPL" cider-jack-in-cljs 555 :help "Starts an nREPL server and connects a ClojureScript REPL to it."] 556 ["Connect to a ClojureScript REPL" cider-connect-cljs 557 :help "Connects to a ClojureScript REPL that's already running."] 558 ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clj&cljs 559 :help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL."] 560 "--" 561 ["View user manual" cider-view-manual]))) 562 563 ;;; Dynamic indentation 564 (defcustom cider-dynamic-indentation t 565 "Whether CIDER should aid Clojure(Script) indentation. 566 If non-nil, CIDER uses runtime information (such as the \":style/indent\" 567 metadata) to improve standard `clojure-mode' indentation. 568 If nil, CIDER won't interfere with `clojure-mode's indentation. 569 570 Toggling this variable only takes effect after a file is closed and 571 re-visited." 572 :type 'boolean 573 :package-version '(cider . "0.11.0") 574 :group 'cider) 575 576 (defun cider--get-symbol-indent (symbol-name) 577 "Return the indent metadata for SYMBOL-NAME in the current namespace." 578 (let* ((ns (let ((clojure-cache-ns t)) ; we force ns caching here for performance reasons 579 ;; silence bytecode warning of unused lexical var 580 (ignore clojure-cache-ns) 581 (cider-current-ns)))) 582 (if-let* ((meta (cider-resolve-var ns symbol-name)) 583 (indent (or (nrepl-dict-get meta "style/indent") 584 (nrepl-dict-get meta "indent")))) 585 (let ((format (format ":indent metadata on ‘%s’ is unreadable! \nERROR: %%s" 586 symbol-name))) 587 (with-demoted-errors format 588 (cider--deep-vector-to-list (read indent)))) 589 ;; There's no indent metadata, but there might be a clojure-mode 590 ;; indent-spec with fully-qualified namespace. 591 (when (string-match cider-resolve--prefix-regexp symbol-name) 592 (when-let* ((sym (intern-soft (replace-match (save-match-data 593 (cider-resolve-alias ns (match-string 1 symbol-name))) 594 t t symbol-name 1)))) 595 (get sym 'clojure-indent-function)))))) 596 597 598 ;;; Dynamic font locking 599 (defcustom cider-font-lock-dynamically '(macro core deprecated) 600 "Specifies how much dynamic font-locking CIDER should use. 601 Dynamic font-locking this refers to applying syntax highlighting to vars 602 defined in the currently active nREPL connection. This is done in addition 603 to `clojure-mode's usual (static) font-lock, so even if you set this 604 variable to nil you'll still see basic syntax highlighting. 605 606 The value is a list of symbols, each one indicates a different type of var 607 that should be font-locked: 608 `macro' (default): Any defined macro gets the `font-lock-keyword-face'. 609 `function': Any defined function gets the `font-lock-function-face'. 610 `var': Any non-local var gets the `font-lock-variable-name-face'. 611 `deprecated' (default): Any deprecated var gets the `cider-deprecated-face' 612 face. 613 `core' (default): Any symbol from clojure.core (face depends on type). 614 615 The value can also be t, which means to font-lock as much as possible." 616 :type '(choice (set :tag "Fine-tune font-locking" 617 (const :tag "Any defined macro" macro) 618 (const :tag "Any defined function" function) 619 (const :tag "Any defined var" var) 620 (const :tag "Any defined deprecated" deprecated) 621 (const :tag "Any symbol from clojure.core" core)) 622 (const :tag "Font-lock as much as possible" t)) 623 :group 'cider 624 :package-version '(cider . "0.10.0")) 625 626 (defcustom cider-font-lock-reader-conditionals t 627 "Apply font-locking to unused reader conditional expressions. 628 The result depends on the buffer CIDER connection type." 629 :type 'boolean 630 :group 'cider 631 :package-version '(cider . "0.15.0")) 632 633 (defface cider-deprecated-face 634 '((((background light)) :background "light goldenrod") 635 (((background dark)) :background "#432")) 636 "Face used on deprecated vars." 637 :group 'cider) 638 639 (defface cider-instrumented-face 640 '((((type graphic)) :box (:color "#c00" :line-width -1)) 641 (t :underline t :background "#800")) 642 "Face used to mark code being debugged." 643 :group 'cider-debug 644 :group 'cider 645 :package-version '(cider . "0.10.0")) 646 647 (defface cider-traced-face 648 '((((type graphic)) :box (:color "cyan" :line-width -1)) 649 (t :underline t :background "#066")) 650 "Face used to mark code being traced." 651 :group 'cider 652 :package-version '(cider . "0.11.0")) 653 654 (defface cider-reader-conditional-face 655 '((t (:inherit font-lock-comment-face))) 656 "Face used to mark unused reader conditional expressions." 657 :group 'cider 658 :package-version '(cider . "0.15.0")) 659 660 (defconst cider-reader-conditionals-regexp "\\(?:#\\?@?[[:space:]\n]*(\\)" 661 "Regexp for matching reader conditionals with a non-capturing group. 662 Starts from the reader macro characters to the opening parentheses.") 663 664 (defvar cider--reader-conditionals-match-data (list nil nil) 665 "Reusable list for `match-data` in reader conditionals font lock matchers.") 666 667 (defun cider--search-reader-conditionals (limit) 668 "Matcher for finding reader conditionals. 669 Search is done with the given LIMIT." 670 (when (and cider-font-lock-reader-conditionals 671 (cider-connected-p)) 672 (when (search-forward-regexp cider-reader-conditionals-regexp limit t) 673 (let ((start (match-beginning 0)) 674 (state (syntax-ppss))) 675 (if (or (nth 3 state) (nth 4 state)) ; inside string or comment? 676 (cider--search-reader-conditionals limit) 677 (when (<= (point) limit) 678 (ignore-errors 679 (let ((md (match-data nil cider--reader-conditionals-match-data))) 680 (setf (nth 0 md) start) 681 (setf (nth 1 md) (point)) 682 (set-match-data md) 683 t)))))))) 684 685 (defun cider--anchored-search-suppressed-forms-internal (repl-types limit) 686 "Helper function for `cider--anchored-search-suppressed-forms`. 687 REPL-TYPES is a list of strings repl-type strings. LIMIT is the same as 688 the LIMIT in `cider--anchored-search-suppressed-forms`" 689 (when (= (length repl-types) 1) 690 (let ((type (car repl-types)) 691 (expr (read (current-buffer))) 692 (start (save-excursion (backward-sexp) (point)))) 693 (when (<= (point) limit) 694 (forward-sexp) 695 (if (not (string-equal (symbol-name expr) (concat ":" type))) 696 (ignore-errors 697 (cl-assert (<= (point) limit)) 698 (let ((md (match-data nil cider--reader-conditionals-match-data))) 699 (setf (nth 0 md) start) 700 (setf (nth 1 md) (point)) 701 (set-match-data md) 702 t)) 703 (cider--anchored-search-suppressed-forms-internal repl-types limit)))))) 704 705 (defun cider--anchored-search-suppressed-forms (limit) 706 "Matcher for finding unused reader conditional expressions. 707 An unused reader conditional expression is an expression for a platform 708 that does not match the CIDER connection for the buffer. Search is done 709 with the given LIMIT." 710 (let ((repl-types (seq-uniq (seq-map 711 (lambda (repl) 712 (symbol-name (cider-repl-type repl))) 713 (cider-repls)))) 714 (result 'retry)) 715 (while (and (eq result 'retry) (<= (point) limit)) 716 (condition-case condition 717 (setq result 718 (cider--anchored-search-suppressed-forms-internal 719 repl-types limit)) 720 (invalid-read-syntax 721 (setq result 'retry)) 722 (wrong-type-argument 723 (setq result 'retry)) 724 (scan-error 725 (setq result 'retry)) 726 (end-of-file 727 (setq result nil)) 728 (error 729 (setq result nil) 730 (message 731 "Error during fontification while searching for forms: %S" 732 condition)))) 733 (if (eq result 'retry) (setq result nil)) 734 result)) 735 736 (defconst cider--reader-conditionals-font-lock-keywords 737 '((cider--search-reader-conditionals 738 (cider--anchored-search-suppressed-forms 739 (save-excursion 740 (let* ((state (syntax-ppss)) 741 (list-pt (nth 1 state))) 742 (when list-pt 743 (goto-char list-pt) 744 (forward-list) 745 (backward-char) 746 (point)))) 747 nil 748 (0 'cider-reader-conditional-face t)))) 749 "Font Lock keywords for unused reader conditionals in CIDER mode.") 750 751 (defun cider--unless-local-match (value) 752 "Return VALUE, unless `match-string' is a local var." 753 (unless (or (get-text-property (point) 'cider-block-dynamic-font-lock) 754 (member (match-string 0) 755 (get-text-property (point) 'cider-locals))) 756 value)) 757 758 (defun cider--compile-font-lock-keywords (symbols-plist core-plist) 759 "Return a list of font-lock rules for symbols in SYMBOLS-PLIST, CORE-PLIST." 760 (let ((cider-font-lock-dynamically (if (eq cider-font-lock-dynamically t) 761 '(function var macro core deprecated) 762 cider-font-lock-dynamically)) 763 deprecated enlightened 764 macros functions vars instrumented traced) 765 (cl-labels ((handle-plist 766 (plist) 767 (let ((do-function (memq 'function cider-font-lock-dynamically)) 768 (do-var (memq 'var cider-font-lock-dynamically)) 769 (do-macro (memq 'macro cider-font-lock-dynamically)) 770 (do-deprecated (memq 'deprecated cider-font-lock-dynamically))) 771 (while plist 772 (let ((sym (pop plist)) 773 (meta (pop plist))) 774 (pcase (nrepl-dict-get meta "cider/instrumented") 775 (`nil nil) 776 (`"\"breakpoint-if-interesting\"" 777 (push sym instrumented)) 778 (`"\"light-form\"" 779 (push sym enlightened))) 780 ;; The ::traced keywords can be inlined by MrAnderson, so 781 ;; we catch that case too. 782 ;; FIXME: This matches values too, not just keys. 783 (when (seq-find (lambda (k) (and (stringp k) 784 (string-match (rx "clojure.tools.trace/traced" eos) k))) 785 meta) 786 (push sym traced)) 787 (when (and do-deprecated (nrepl-dict-get meta "deprecated")) 788 (push sym deprecated)) 789 (let ((is-macro (nrepl-dict-get meta "macro")) 790 (is-function (or (nrepl-dict-get meta "fn") 791 (nrepl-dict-get meta "arglists")))) 792 (cond ((and do-macro is-macro) 793 (push sym macros)) 794 ((and do-function is-function) 795 (push sym functions)) 796 ((and do-var (not is-function) (not is-macro)) 797 (push sym vars))))))))) 798 (when (memq 'core cider-font-lock-dynamically) 799 (let ((cider-font-lock-dynamically '(function var macro core deprecated))) 800 (handle-plist core-plist))) 801 (handle-plist symbols-plist)) 802 `( 803 ,@(when macros 804 `((,(concat (rx (or "(" "#'")) ; Can't take the value of macros. 805 "\\(" (regexp-opt macros 'symbols) "\\)") 806 1 (cider--unless-local-match font-lock-keyword-face)))) 807 ,@(when functions 808 `((,(regexp-opt functions 'symbols) 0 809 (cider--unless-local-match font-lock-function-name-face)))) 810 ,@(when vars 811 `((,(regexp-opt vars 'symbols) 0 812 (cider--unless-local-match font-lock-variable-name-face)))) 813 ,@(when deprecated 814 `((,(regexp-opt deprecated 'symbols) 0 815 (cider--unless-local-match 'cider-deprecated-face) append))) 816 ,@(when enlightened 817 `((,(regexp-opt enlightened 'symbols) 0 818 (cider--unless-local-match 'cider-enlightened-face) append))) 819 ,@(when instrumented 820 `((,(regexp-opt instrumented 'symbols) 0 821 (cider--unless-local-match 'cider-instrumented-face) append))) 822 ,@(when traced 823 `((,(regexp-opt traced 'symbols) 0 824 (cider--unless-local-match 'cider-traced-face) append)))))) 825 826 (defconst cider--static-font-lock-keywords 827 (eval-when-compile 828 `((,(regexp-opt '("#break" "#dbg" "#light") 'symbols) 0 font-lock-warning-face))) 829 "Default expressions to highlight in CIDER mode.") 830 831 (defvar-local cider--dynamic-font-lock-keywords nil) 832 833 (defun cider-refresh-dynamic-font-lock (&optional ns) 834 "Ensure that the current buffer has up-to-date font-lock rules. 835 NS defaults to `cider-current-ns', and it can also be a dict describing the 836 namespace itself." 837 (interactive) 838 (when (and cider-font-lock-dynamically 839 font-lock-mode) 840 (font-lock-remove-keywords nil cider--dynamic-font-lock-keywords) 841 (when-let* ((ns (or ns (cider-current-ns))) 842 (symbols (cider-resolve-ns-symbols ns))) 843 (setq-local cider--dynamic-font-lock-keywords 844 (cider--compile-font-lock-keywords 845 symbols (cider-resolve-ns-symbols (cider-resolve-core-ns)))) 846 (font-lock-add-keywords nil cider--dynamic-font-lock-keywords 'end)) 847 (font-lock-flush))) 848 849 850 ;;; Detecting local variables 851 (defun cider--read-locals-from-next-sexp () 852 "Return a list of all locals inside the next logical sexp." 853 (save-excursion 854 (ignore-errors 855 (clojure-forward-logical-sexp 1) 856 (let ((out nil) 857 (end (point))) 858 (forward-sexp -1) 859 ;; FIXME: This returns locals found inside the :or clause of a 860 ;; destructuring map. 861 (while (search-forward-regexp "\\_<[^:&]\\(\\sw\\|\\s_\\)*\\_>" end 'noerror) 862 (push (match-string-no-properties 0) out)) 863 out)))) 864 865 (defun cider--read-locals-from-bindings-vector () 866 "Return a list of all locals inside the next bindings vector." 867 (save-excursion 868 (ignore-errors 869 (cider-start-of-next-sexp) 870 (when (eq (char-after) ?\[) 871 (forward-char 1) 872 (let ((out nil)) 873 (setq out (append (cider--read-locals-from-next-sexp) out)) 874 (while (ignore-errors (clojure-forward-logical-sexp 3) 875 (unless (eobp) 876 (forward-sexp -1) 877 t)) 878 (setq out (append (cider--read-locals-from-next-sexp) out))) 879 out))))) 880 881 (defun cider--read-locals-from-arglist () 882 "Return a list of all locals in current form's arglist(s)." 883 (let ((out nil)) 884 (save-excursion 885 (ignore-errors 886 (cider-start-of-next-sexp) 887 ;; Named fn 888 (when (looking-at-p "\\s_\\|\\sw") 889 (cider-start-of-next-sexp 1)) 890 ;; Docstring 891 (when (eq (char-after) ?\") 892 (cider-start-of-next-sexp 1)) 893 ;; Attribute map 894 (when (eq (char-after) ?{) 895 (cider-start-of-next-sexp 1)) 896 ;; The arglist 897 (pcase (char-after) 898 (?\[ (setq out (cider--read-locals-from-next-sexp))) 899 ;; FIXME: This returns false positives. It takes all arglists of a 900 ;; function and returns all args it finds. The logic should be changed 901 ;; so that each arglist applies to its own scope. 902 (?\( (ignore-errors 903 (while (eq (char-after) ?\() 904 (save-excursion 905 (forward-char 1) 906 (setq out (append (cider--read-locals-from-next-sexp) out))) 907 (cider-start-of-next-sexp 1))))))) 908 out)) 909 910 (defun cider--parse-and-apply-locals (end &optional outer-locals) 911 "Figure out local variables between point and END. 912 A list of these variables is set as the `cider-locals' text property over 913 the code where they are in scope. 914 Optional argument OUTER-LOCALS is used to specify local variables defined 915 before point." 916 (while (search-forward-regexp "(\\(ns\\_>\\|def\\|fn\\|for\\b\\|loop\\b\\|with-\\|do[a-z]+\\|\\([a-z]+-\\)?let\\b\\)" 917 end 'noerror) 918 (goto-char (match-beginning 0)) 919 (let ((sym (match-string 1)) 920 (sexp-end (save-excursion 921 (or (ignore-errors (forward-sexp 1) 922 (point)) 923 end)))) 924 ;; #1324: Don't do dynamic font-lock in `ns' forms, they are special 925 ;; macros where nothing is evaluated, so we'd get a lot of false 926 ;; positives. 927 (if (equal sym "ns") 928 (add-text-properties (point) sexp-end '(cider-block-dynamic-font-lock t)) 929 (forward-char 1) 930 (forward-sexp 1) 931 (let ((locals (append outer-locals 932 (pcase sym 933 ((or "fn" "def" "") (cider--read-locals-from-arglist)) 934 (_ (cider--read-locals-from-bindings-vector)))))) 935 (add-text-properties (point) sexp-end (list 'cider-locals locals)) 936 (clojure-forward-logical-sexp 1) 937 (cider--parse-and-apply-locals sexp-end locals))) 938 (goto-char sexp-end)))) 939 940 (defun cider--update-locals-for-region (beg end) 941 "Update the `cider-locals' text property for region from BEG to END." 942 (save-excursion 943 (goto-char beg) 944 ;; If the inside of a `ns' form changed, reparse it from the start. 945 (when (and (not (bobp)) 946 (get-text-property (1- (point)) 'cider-block-dynamic-font-lock)) 947 (ignore-errors (beginning-of-defun))) 948 (save-excursion 949 ;; Move up until we reach a sexp that encloses the entire region (or 950 ;; a top-level sexp), and set that as the new BEG. 951 (goto-char end) 952 (while (and (or (> (point) beg) 953 (not (eq (char-after) ?\())) 954 (condition-case nil 955 (progn (backward-up-list) t) 956 (scan-error nil)))) 957 (setq beg (min beg (point))) 958 ;; If there are locals above the current sexp, reapply them to the 959 ;; current sexp. 960 (let ((locals-above (when (> beg (point-min)) 961 (get-text-property (1- beg) 'cider-locals)))) 962 (condition-case nil 963 (clojure-forward-logical-sexp 1) 964 (error (goto-char end))) 965 (add-text-properties beg (point) `(cider-locals ,locals-above)) 966 ;; Extend the region being font-locked to include whole sexps. 967 (setq end (max end (point))) 968 (goto-char beg) 969 (ignore-errors 970 (cider--parse-and-apply-locals end locals-above)))))) 971 972 (defun cider--docview-as-string (sym info) 973 "Return a string of what would be displayed by `cider-docview-render'. 974 SYM and INFO is passed to `cider-docview-render'" 975 (with-temp-buffer 976 (cider-docview-render (current-buffer) sym info) 977 (goto-char (point-max)) 978 (forward-line -1) 979 (replace-regexp-in-string 980 "[`']" "\\\\=\\&" 981 (buffer-substring-no-properties (point-min) (1- (point)))))) 982 983 (defcustom cider-use-tooltips t 984 "If non-nil, CIDER displays mouse-over tooltips. 985 It does this as well as the `help-echo' mechanism." 986 :group 'cider 987 :type 'boolean 988 :package-version '(cider "0.12.0")) 989 990 (defvar cider--debug-mode-response) 991 (defvar cider--debug-mode) 992 993 (defun cider--help-echo (_ obj pos) 994 "Return the help-echo string for OBJ at POS. 995 See \(info \"(elisp) Special Properties\")" 996 (while-no-input 997 (when (and (bufferp obj) 998 (cider-connected-p) 999 cider-use-tooltips 1000 (not (eq help-at-pt-display-when-idle t))) 1001 (with-current-buffer obj 1002 (ignore-errors 1003 (save-excursion 1004 (goto-char pos) 1005 (when-let* ((sym (cider-symbol-at-point))) 1006 (if (member sym (get-text-property (point) 'cider-locals)) 1007 (concat (format "`%s' is a local" sym) 1008 (when cider--debug-mode 1009 (let* ((locals (nrepl-dict-get cider--debug-mode-response "locals")) 1010 (local-val (cadr (assoc sym locals)))) 1011 (format " with value:\n%s" local-val)))) 1012 (let* ((info (cider-sync-request:info sym)) 1013 (candidates (nrepl-dict-get info "candidates"))) 1014 (if candidates 1015 (concat "There were ambiguities resolving this symbol:\n\n" 1016 (mapconcat (lambda (x) (cider--docview-as-string sym x)) 1017 candidates 1018 (concat "\n\n" (make-string 60 ?-) "\n\n"))) 1019 (cider--docview-as-string sym info))))))))))) 1020 1021 (defun cider--wrap-fontify-locals (func) 1022 "Return a function that will call FUNC after parsing local variables. 1023 The local variables are stored in a list under the `cider-locals' text 1024 property." 1025 (lambda (beg end &rest rest) 1026 (with-silent-modifications 1027 (remove-text-properties beg end '(cider-locals nil cider-block-dynamic-font-lock nil)) 1028 (when cider-use-tooltips 1029 (add-text-properties beg end '(help-echo cider--help-echo))) 1030 (when cider-font-lock-dynamically 1031 (cider--update-locals-for-region beg end))) 1032 (apply func beg end rest))) 1033 1034 1035 ;;; Minor-mode definition 1036 (defvar x-gtk-use-system-tooltips) 1037 1038 ;;;###autoload 1039 (define-minor-mode cider-mode 1040 "Minor mode for REPL interaction from a Clojure buffer. 1041 1042 \\{cider-mode-map}" 1043 :init-value nil 1044 :lighter cider-mode-line 1045 :keymap cider-mode-map 1046 (if cider-mode 1047 (progn 1048 (setq-local sesman-system 'CIDER) 1049 (cider-eldoc-setup) 1050 (add-hook 'completion-at-point-functions #'cider-complete-at-point nil t) 1051 (font-lock-add-keywords nil cider--static-font-lock-keywords) 1052 (cider-refresh-dynamic-font-lock) 1053 (font-lock-add-keywords nil cider--reader-conditionals-font-lock-keywords) 1054 ;; `font-lock-mode' might get enabled after `cider-mode'. 1055 (add-hook 'font-lock-mode-hook #'cider-refresh-dynamic-font-lock nil 'local) 1056 (setq-local font-lock-fontify-region-function 1057 (cider--wrap-fontify-locals font-lock-fontify-region-function)) 1058 ;; GTK tooltips look bad, and we have no control over the face. 1059 (setq-local x-gtk-use-system-tooltips nil) 1060 ;; `tooltip' has variable-width by default, which looks terrible. 1061 (set-face-attribute 'tooltip nil :inherit 'unspecified) 1062 (when cider-dynamic-indentation 1063 (setq-local clojure-get-indent-function #'cider--get-symbol-indent)) 1064 (setq-local clojure-expected-ns-function #'cider-expected-ns) 1065 (when cider-use-xref 1066 (add-hook 'xref-backend-functions #'cider--xref-backend cider-xref-fn-depth 'local)) 1067 (setq next-error-function #'cider-jump-to-compilation-error)) 1068 ;; Mode cleanup 1069 (mapc #'kill-local-variable '(next-error-function 1070 x-gtk-use-system-tooltips 1071 font-lock-fontify-region-function 1072 clojure-get-indent-function)) 1073 (remove-hook 'completion-at-point-functions #'cider-complete-at-point t) 1074 (when cider-use-xref 1075 (remove-hook 'xref-backend-functions #'cider--xref-backend 'local)) 1076 (remove-hook 'font-lock-mode-hook #'cider-refresh-dynamic-font-lock 'local) 1077 (font-lock-add-keywords nil cider--reader-conditionals-font-lock-keywords) 1078 (font-lock-remove-keywords nil cider--dynamic-font-lock-keywords) 1079 (font-lock-remove-keywords nil cider--static-font-lock-keywords) 1080 (font-lock-flush) 1081 (remove-hook 'completion-at-point-functions #'cider-complete-at-point t))) 1082 1083 (defun cider-set-buffer-ns (ns) 1084 "Set this buffer's namespace to NS and refresh font-locking." 1085 (setq-local cider-buffer-ns ns) 1086 (when (or cider-mode (derived-mode-p 'cider-repl-mode)) 1087 (cider-refresh-dynamic-font-lock ns))) 1088 1089 (provide 'cider-mode) 1090 1091 ;;; cider-mode.el ends here