cider-eval.el (71428B)
1 ;;; cider-eval.el --- Interactive evaluation (compilation) functionality -*- 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 ;; Arne Brasseur <arne@arnebraasseur.net> 13 14 ;; This program is free software: you can redistribute it and/or modify 15 ;; it under the terms of the GNU General Public License as published by 16 ;; the Free Software Foundation, either version 3 of the License, or 17 ;; (at your option) any later version. 18 19 ;; This program is distributed in the hope that it will be useful, 20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 ;; GNU General Public License for more details. 23 24 ;; You should have received a copy of the GNU General Public License 25 ;; along with this program. If not, see <http://www.gnu.org/licenses/>. 26 27 ;; This file is not part of GNU Emacs. 28 29 ;;; Commentary: 30 31 ;; This file contains CIDER's interactive evaluation (compilation) functionality. 32 ;; Although Clojure doesn't really have the concept of evaluation (only 33 ;; compilation), we're using everywhere in the code the term evaluation for 34 ;; brevity (and to be in line with the naming employed by other similar modes). 35 ;; 36 ;; This files also contains all the logic related to displaying errors and 37 ;; evaluation warnings. 38 ;; 39 ;; Pretty much all of the commands here are meant to be used mostly from 40 ;; `cider-mode', but some of them might make sense in other contexts as well. 41 42 ;;; Code: 43 44 (require 'ansi-color) 45 (require 'cl-lib) 46 (require 'compile) 47 (require 'map) 48 (require 'seq) 49 (require 'subr-x) 50 51 (require 'clojure-mode) 52 53 (require 'cider-client) 54 (require 'cider-common) 55 (require 'cider-jar) 56 (require 'cider-overlays) 57 (require 'cider-popup) 58 (require 'cider-repl) 59 (require 'cider-stacktrace) 60 (require 'cider-util) 61 62 (defconst cider-read-eval-buffer "*cider-read-eval*") 63 (defconst cider-result-buffer "*cider-result*") 64 65 (defcustom cider-show-error-buffer t 66 "Control the popup behavior of cider stacktraces. 67 The following values are possible t or 'always, 'except-in-repl, 68 'only-in-repl. Any other value, including nil, will cause the stacktrace 69 not to be automatically shown. 70 71 Irrespective of the value of this variable, the `cider-error-buffer' is 72 always generated in the background. Use `cider-selector' to 73 navigate to this buffer." 74 :type '(choice (const :tag "always" t) 75 (const except-in-repl) 76 (const only-in-repl) 77 (const :tag "never" nil)) 78 :group 'cider) 79 80 (defcustom cider-auto-jump-to-error t 81 "Control the cursor jump behavior in compilation error buffer. 82 When non-nil automatically jump to error location during interactive 83 compilation. When set to 'errors-only, don't jump to warnings. 84 When set to nil, don't jump at all." 85 :type '(choice (const :tag "always" t) 86 (const errors-only) 87 (const :tag "never" nil)) 88 :group 'cider 89 :package-version '(cider . "0.7.0")) 90 91 (defcustom cider-auto-select-error-buffer t 92 "Controls whether to auto-select the error popup buffer." 93 :type 'boolean 94 :group 'cider) 95 96 (defcustom cider-auto-track-ns-form-changes t 97 "Controls whether to auto-evaluate a source buffer's ns form when changed. 98 When non-nil CIDER will check for ns form changes before each eval command. 99 When nil the users are expected to take care of the re-evaluating updated 100 ns forms manually themselves." 101 :type 'boolean 102 :group 'cider 103 :package-version '(cider . "0.15.0")) 104 105 (defcustom cider-auto-inspect-after-eval t 106 "Controls whether to auto-update the inspector buffer after eval. 107 Only applies when the *cider-inspect* buffer is currently visible." 108 :type 'boolean 109 :group 'cider 110 :package-version '(cider . "0.25.0")) 111 112 (defcustom cider-save-file-on-load 'prompt 113 "Controls whether to prompt to save the file when loading a buffer. 114 If nil, files are not saved. 115 If 'prompt, the user is prompted to save the file if it's been modified. 116 If t, save the file without confirmation." 117 :type '(choice (const prompt :tag "Prompt to save the file if it's been modified") 118 (const nil :tag "Don't save the file") 119 (const t :tag "Save the file without confirmation")) 120 :group 'cider 121 :package-version '(cider . "0.6.0")) 122 123 (defcustom cider-file-loaded-hook nil 124 "List of functions to call when a load file has completed." 125 :type 'hook 126 :group 'cider 127 :package-version '(cider . "0.1.7")) 128 129 (defconst cider-output-buffer "*cider-out*") 130 131 (defcustom cider-interactive-eval-output-destination 'repl-buffer 132 "The destination for stdout and stderr produced from interactive evaluation." 133 :type '(choice (const output-buffer) 134 (const repl-buffer)) 135 :group 'cider 136 :package-version '(cider . "0.7.0")) 137 138 (defface cider-error-highlight-face 139 '((((supports :underline (:style wave))) 140 (:underline (:style wave :color "red") :inherit unspecified)) 141 (t (:inherit font-lock-warning-face :underline t))) 142 "Face used to highlight compilation errors in Clojure buffers." 143 :group 'cider) 144 145 (defface cider-warning-highlight-face 146 '((((supports :underline (:style wave))) 147 (:underline (:style wave :color "yellow") :inherit unspecified)) 148 (t (:inherit font-lock-warning-face :underline (:color "yellow")))) 149 "Face used to highlight compilation warnings in Clojure buffers." 150 :group 'cider) 151 152 (defcustom cider-comment-prefix ";; => " 153 "The prefix to insert before the first line of commented output." 154 :type 'string 155 :group 'cider 156 :package-version '(cider . "0.16.0")) 157 158 (defcustom cider-comment-continued-prefix ";; " 159 "The prefix to use on the second and subsequent lines of commented output." 160 :type 'string 161 :group 'cider 162 :package-version '(cider . "0.16.0")) 163 164 (defcustom cider-comment-postfix "" 165 "The postfix to be appended after the final line of commented output." 166 :type 'string 167 :group 'cider 168 :package-version '(cider . "0.16.0")) 169 170 (defcustom cider-eval-register ?e 171 "The text register assigned to the most recent evaluation result. 172 When non-nil, the return value of all CIDER eval commands are 173 automatically written into this register." 174 :type '(choice character 175 (const nil)) 176 :group 'cider 177 :package-version '(cider . "1.4.0")) 178 179 180 ;;; Utilities 181 182 (defun cider--clear-compilation-highlights () 183 "Remove compilation highlights." 184 (remove-overlays (point-min) (point-max) 'cider-note-p t)) 185 186 (defun cider-clear-compilation-highlights (&optional arg) 187 "Remove compilation highlights. 188 When invoked with a prefix ARG the command doesn't prompt for confirmation." 189 (interactive "P") 190 (when (or arg (y-or-n-p "Are you sure you want to clear the compilation highlights? ")) 191 (cider--clear-compilation-highlights))) 192 193 (defun cider--quit-error-window () 194 "Buries the `cider-error-buffer' and quits its containing window." 195 (when-let* ((error-win (get-buffer-window cider-error-buffer))) 196 (save-excursion 197 (quit-window nil error-win)))) 198 199 200 ;;; Sideloader 201 ;; 202 ;; nREPL includes sideloader middleware which provides a Java classloader that 203 ;; is able to dynamically load classes and resources at runtime by interacting 204 ;; with the nREPL client (as opposed to using the classpath of the JVM hosting 205 ;; nREPL server). 206 ;; 207 ;; This performs a similar functionality as the load-file 208 ;; operation, where we can load Clojure namespaces (as source files) or Java 209 ;; classes (as bytecode) by simply requiring or importing them. 210 ;; 211 ;; See https://nrepl.org/nrepl/design/middleware.html#sideloading 212 213 (defcustom cider-sideloader-path nil 214 "List of directories and jar files to scan for sideloader resources. 215 When not set the cider-nrepl jar will be added automatically when upgrading 216 an nREPL connection." 217 :type 'list 218 :group 'cider 219 :package-version '(cider . "1.2.0")) 220 221 (defcustom cider-dynload-cider-nrepl-version nil 222 "Version of the cider-nrepl jar used for dynamically upgrading a connection. 223 Defaults to `cider-required-middleware-version'." 224 :type 'string 225 :group 'cider 226 :package-version '(cider . "1.2.0")) 227 228 (defun cider-read-bytes (path) 229 "Read binary data from PATH. 230 Return the binary data as unibyte string." 231 ;; based on f-read-bytes 232 (with-temp-buffer 233 (set-buffer-multibyte nil) 234 (setq buffer-file-coding-system 'binary) 235 (insert-file-contents-literally path nil) 236 (buffer-substring-no-properties (point-min) (point-max)))) 237 238 (defun cider-retrieve-resource (dirs name) 239 "Find a resource NAME in a list DIRS of directories or jar files. 240 Similar to a classpath lookup. Returns the file contents as a string." 241 (seq-some 242 (lambda (path) 243 (cond 244 ((file-directory-p path) 245 (let ((expanded (expand-file-name name path))) 246 (when (file-exists-p expanded) 247 (cider-read-bytes expanded)))) 248 ((and (file-exists-p path) (string-suffix-p ".jar" path)) 249 (cider-jar-retrieve-resource path name)))) 250 dirs)) 251 252 (defun cider-provide-file (file) 253 "Provide FILE in a format suitable for sideloading." 254 (let ((contents (cider-retrieve-resource cider-sideloader-path file))) 255 (if contents 256 (base64-encode-string contents 'no-line-breaks) 257 ;; if we can't find the file we should return an empty string 258 (base64-encode-string "")))) 259 260 (defun cider-sideloader-lookup-handler () 261 "Make a sideloader-lookup handler." 262 (lambda (response) 263 (nrepl-dbind-response response (id status type name) 264 (if status 265 (when (member "sideloader-lookup" status) 266 (cider-request:sideloader-provide id type name)))))) 267 268 (defun cider-add-middleware-handler (continue) 269 "Make a add-middleware handler. 270 CONTINUE is an optional continuation function." 271 (lambda (response) 272 (nrepl-dbind-response response (status unresolved-middleware) ;; id middleware 273 (when unresolved-middleware 274 (seq-do 275 (lambda (mw) 276 (cider-repl-emit-interactive-stderr 277 (concat "WARNING: middleware " mw " was not found or failed to load.\n"))) 278 unresolved-middleware)) 279 (when (and status (member "done" status) continue) 280 (funcall continue))))) 281 282 (defun cider-request:sideloader-start (&optional connection tooling) 283 "Perform the nREPL \"sideloader-start\" op. 284 If CONNECTION is nil, use `cider-current-repl'. 285 If TOOLING is truthy then the operation is performed over the tooling 286 session, rather than the regular session." 287 (cider-ensure-op-supported "sideloader-start") 288 (cider-nrepl-send-request `("op" "sideloader-start") 289 (cider-sideloader-lookup-handler) 290 connection 291 tooling)) 292 293 (defun cider-request:sideloader-provide (id type file &optional connection) 294 "Perform the nREPL \"sideloader-provide\" op for ID, TYPE and FILE. 295 If CONNECTION is nil, use `cider-current-repl'." 296 (cider-nrepl-send-request `("id" ,id 297 "op" "sideloader-provide" 298 "type" ,type 299 "name" ,file 300 "content" ,(cider-provide-file file)) 301 (cider-sideloader-lookup-handler) 302 connection)) 303 304 (defun cider-sideloader-start (&optional connection) 305 "Start nREPL's sideloader. 306 If CONNECTION is nil, use `cider-current-repl'." 307 (interactive) 308 (message "Starting nREPL's sideloader") 309 (cider-request:sideloader-start connection) 310 (cider-request:sideloader-start connection 'tooling)) 311 312 (defvar cider-nrepl-middlewares 313 '("cider.nrepl/wrap-apropos" 314 "cider.nrepl/wrap-classpath" 315 "cider.nrepl/wrap-clojuredocs" 316 "cider.nrepl/wrap-complete" 317 "cider.nrepl/wrap-content-type" 318 "cider.nrepl/wrap-debug" 319 "cider.nrepl/wrap-enlighten" 320 "cider.nrepl/wrap-format" 321 "cider.nrepl/wrap-info" 322 "cider.nrepl/wrap-inspect" 323 "cider.nrepl/wrap-macroexpand" 324 "cider.nrepl/wrap-ns" 325 "cider.nrepl/wrap-out" 326 "cider.nrepl/wrap-slurp" 327 "cider.nrepl/wrap-profile" 328 "cider.nrepl/wrap-refresh" 329 "cider.nrepl/wrap-resource" 330 "cider.nrepl/wrap-spec" 331 "cider.nrepl/wrap-stacktrace" 332 "cider.nrepl/wrap-test" 333 "cider.nrepl/wrap-trace" 334 "cider.nrepl/wrap-tracker" 335 "cider.nrepl/wrap-undef" 336 "cider.nrepl/wrap-version" 337 "cider.nrepl/wrap-xref")) 338 339 (defun cider-request:add-middleware (middlewares 340 &optional connection tooling continue) 341 "Use the nREPL dynamic loader to add MIDDLEWARES to the nREPL session. 342 343 - If CONNECTION is nil, use `cider-current-repl'. 344 - If TOOLING it truthy, use the tooling session instead of the main session. 345 - CONTINUE is an optional continuation function, which will be called when the 346 add-middleware op has finished successfully." 347 (cider-nrepl-send-request `("op" "add-middleware" 348 "middleware" ,middlewares) 349 (cider-add-middleware-handler continue) 350 connection 351 tooling)) 352 353 (defun cider-add-cider-nrepl-middlewares (&optional connection) 354 "Use dynamic loading to add the cider-nrepl middlewares to nREPL. 355 If CONNECTION is nil, use `cider-current-repl'." 356 (cider-request:add-middleware 357 cider-nrepl-middlewares connection nil 358 (lambda () 359 ;; When the main session is done adding middleware, then do the tooling 360 ;; session. At this point all the namespaces have been sideloaded so this 361 ;; is faster, we don't want these to race to sideload resources. 362 (cider-request:add-middleware 363 cider-nrepl-middlewares connection 'tooling 364 (lambda () 365 ;; Ask nREPL again what its capabilities are, so we know which new 366 ;; operations are supported. 367 (nrepl--init-capabilities (or connection (cider-current-repl)))))))) 368 369 (defvar cider-required-middleware-version) 370 (defun cider-upgrade-nrepl-connection (&optional connection) 371 "Sideload cider-nrepl middleware. 372 If CONNECTION is nil, use `cider-current-repl'." 373 (interactive) 374 (when (not cider-sideloader-path) 375 (setq cider-sideloader-path (list (cider-jar-find-or-fetch 376 "cider" "cider-nrepl" 377 (or cider-dynload-cider-nrepl-version 378 cider-required-middleware-version))))) 379 (cider-sideloader-start connection) 380 (cider-add-cider-nrepl-middlewares connection)) 381 382 383 ;;; Dealing with compilation (evaluation) errors and warnings 384 (defun cider-find-property (property &optional backward) 385 "Find the next text region which has the specified PROPERTY. 386 If BACKWARD is t, then search backward. 387 Returns the position at which PROPERTY was found, or nil if not found." 388 (let ((p (if backward 389 (previous-single-char-property-change (point) property) 390 (next-single-char-property-change (point) property)))) 391 (when (and (not (= p (point-min))) (not (= p (point-max)))) 392 p))) 393 394 (defun cider-jump-to-compilation-error (&optional _arg _reset) 395 "Jump to the line causing the current compilation error. 396 _ARG and _RESET are ignored, as there is only ever one compilation error. 397 They exist for compatibility with `next-error'." 398 (interactive) 399 (cl-labels ((goto-next-note-boundary 400 () 401 (let ((p (or (cider-find-property 'cider-note-p) 402 (cider-find-property 'cider-note-p t)))) 403 (when p 404 (goto-char p) 405 (message "%s" (get-char-property p 'cider-note)))))) 406 ;; if we're already on a compilation error, first jump to the end of 407 ;; it, so that we find the next error. 408 (when (get-char-property (point) 'cider-note-p) 409 (goto-next-note-boundary)) 410 (goto-next-note-boundary))) 411 412 (defun cider--show-error-buffer-p () 413 "Return non-nil if the error buffer must be shown on error. 414 Takes into account both the value of `cider-show-error-buffer' and the 415 currently selected buffer." 416 (let* ((selected-buffer (window-buffer (selected-window))) 417 (replp (with-current-buffer selected-buffer (derived-mode-p 'cider-repl-mode)))) 418 (memq cider-show-error-buffer 419 (if replp 420 '(t always only-in-repl) 421 '(t always except-in-repl))))) 422 423 (defun cider-new-error-buffer (&optional mode error-types) 424 "Return an empty error buffer using MODE. 425 426 When deciding whether to display the buffer, takes into account not only 427 the value of `cider-show-error-buffer' and the currently selected buffer 428 but also the ERROR-TYPES of the error, which is checked against the 429 `cider-stacktrace-suppressed-errors' set. 430 431 When deciding whether to select the buffer, takes into account the value of 432 `cider-auto-select-error-buffer'." 433 (if (and (cider--show-error-buffer-p) 434 (not (cider-stacktrace-some-suppressed-errors-p error-types))) 435 (cider-popup-buffer cider-error-buffer cider-auto-select-error-buffer mode 'ancillary) 436 (cider-make-popup-buffer cider-error-buffer mode 'ancillary))) 437 438 (defun cider-emit-into-color-buffer (buffer value) 439 "Emit into color BUFFER the provided VALUE." 440 (with-current-buffer buffer 441 (let ((inhibit-read-only t) 442 (buffer-undo-list t)) 443 (goto-char (point-max)) 444 (insert (format "%s" value)) 445 (ansi-color-apply-on-region (point-min) (point-max))) 446 (goto-char (point-min)))) 447 448 (defun cider--handle-err-eval-response (response) 449 "Render eval RESPONSE into a new error buffer. 450 451 Uses the value of the `out' slot in RESPONSE." 452 (nrepl-dbind-response response (out) 453 (when out 454 (let ((error-buffer (cider-new-error-buffer))) 455 (cider-emit-into-color-buffer error-buffer out) 456 (with-current-buffer error-buffer 457 (compilation-minor-mode +1)))))) 458 459 (defun cider-default-err-eval-handler () 460 "Display the last exception without middleware support." 461 (cider--handle-err-eval-response 462 (cider-nrepl-sync-request:eval 463 "(clojure.stacktrace/print-cause-trace *e)"))) 464 465 (defun cider-default-err-eval-print-handler () 466 "Display the last exception without middleware support. 467 When clojure.stracktrace is not present." 468 (cider--handle-err-eval-response 469 (cider-nrepl-sync-request:eval 470 "(println (ex-data *e))"))) 471 472 (defun cider--render-stacktrace-causes (causes &optional error-types) 473 "If CAUSES is non-nil, render its contents into a new error buffer. 474 Optional argument ERROR-TYPES contains a list which should determine the 475 op/situation that originated this error." 476 (when causes 477 (let ((error-buffer (cider-new-error-buffer #'cider-stacktrace-mode error-types))) 478 (cider-stacktrace-render error-buffer (reverse causes) error-types)))) 479 480 (defun cider--handle-stacktrace-response (response causes) 481 "Handle stacktrace op RESPONSE, aggregating the result into CAUSES. 482 If RESPONSE contains a cause, cons it onto CAUSES and return that. If 483 RESPONSE is the final message (i.e. it contains a status), render CAUSES 484 into a new error buffer." 485 (nrepl-dbind-response response (class msg status type) 486 (cond ((and (member "notification" status) causes) 487 (nrepl-notify msg type)) 488 (class (cons response causes)) 489 (status (cider--render-stacktrace-causes causes))))) 490 491 (defun cider-default-err-op-handler () 492 "Display the last exception, with middleware support." 493 ;; Causes are returned as a series of messages, which we aggregate in `causes' 494 (let (causes) 495 (cider-nrepl-send-request 496 (thread-last 497 (map-merge 'list 498 '(("op" "analyze-last-stacktrace")) 499 (cider--nrepl-print-request-map fill-column)) 500 (seq-mapcat #'identity)) 501 (lambda (response) 502 ;; While the return value of `cider--handle-stacktrace-response' is not 503 ;; meaningful for the last message, we do not need the value of `causes' 504 ;; after it has been handled, so it's fine to set it unconditionally here 505 (setq causes (cider--handle-stacktrace-response response causes)))))) 506 507 (defun cider-default-err-handler () 508 "This function determines how the error buffer is shown. 509 It delegates the actual error content to the eval or op handler." 510 (cond ((cider-nrepl-op-supported-p "analyze-last-stacktrace") 511 (cider-default-err-op-handler)) 512 ((cider-library-present-p "clojure.stacktrace") 513 (cider-default-err-eval-handler)) 514 (t (cider-default-err-eval-print-handler)))) 515 516 517 ;; The format of the error messages emitted by Clojure's compiler changed in 518 ;; Clojure 1.10. That's why we're trying to match error messages to both the 519 ;; old and the new format, by utilizing a combination of two different regular 520 ;; expressions. 521 522 (defconst cider-clojure-1.10-error `(sequence 523 "Syntax error " 524 (minimal-match (zero-or-more anything)) 525 "compiling " 526 (minimal-match (zero-or-more anything)) 527 "at (" 528 (group-n 2 (minimal-match (zero-or-more anything))) 529 ":" 530 (group-n 3 (one-or-more digit)) 531 (optional ":" (group-n 4 (one-or-more digit))) 532 ").")) 533 534 (defconst cider-clojure-1.9-error `(sequence 535 (zero-or-more anything) 536 ", compiling:(" 537 (group-n 2 (minimal-match (zero-or-more anything))) 538 ":" 539 (group-n 3 (one-or-more digit)) 540 (optional ":" (group-n 4 (one-or-more digit))) 541 ")")) 542 543 (defconst cider-clojure-warning `(sequence 544 (minimal-match (zero-or-more anything)) 545 (group-n 1 "warning") 546 ", " 547 (group-n 2 (minimal-match (zero-or-more anything))) 548 ":" 549 (group-n 3 (one-or-more digit)) 550 (optional ":" (group-n 4 (one-or-more digit))) 551 " - ")) 552 553 554 (defconst cider-clojure-compilation-regexp 555 (eval 556 `(rx bol (or ,cider-clojure-1.9-error 557 ,cider-clojure-warning 558 ,cider-clojure-1.10-error)) 559 t)) 560 561 562 (defvar cider-compilation-regexp 563 (list cider-clojure-compilation-regexp 2 3 4 '(1)) 564 "Specifications for matching errors and warnings in Clojure stacktraces. 565 See `compilation-error-regexp-alist' for help on their format.") 566 567 (add-to-list 'compilation-error-regexp-alist-alist 568 (cons 'cider cider-compilation-regexp)) 569 (add-to-list 'compilation-error-regexp-alist 'cider) 570 571 (defun cider-extract-error-info (regexp message) 572 "Extract error information with REGEXP against MESSAGE." 573 (let ((file (nth 1 regexp)) 574 (line (nth 2 regexp)) 575 (col (nth 3 regexp)) 576 (type (nth 4 regexp)) 577 (pat (car regexp))) 578 (when (string-match pat message) 579 ;; special processing for type (1.2) style 580 (setq type (if (consp type) 581 (or (and (car type) (match-end (car type)) 1) 582 (and (cdr type) (match-end (cdr type)) 0) 583 2))) 584 (list 585 (when file 586 (let ((val (match-string-no-properties file message))) 587 (unless (string= val "NO_SOURCE_PATH") val))) 588 (when line (string-to-number (match-string-no-properties line message))) 589 (when col 590 (let ((val (match-string-no-properties col message))) 591 (when (and val (not (string-blank-p val))) (string-to-number val)))) 592 (aref [cider-warning-highlight-face 593 cider-warning-highlight-face 594 cider-error-highlight-face] 595 (or type 2)) 596 message)))) 597 598 (defun cider--goto-expression-start () 599 "Go to the beginning a list, vector, map or set outside of a string. 600 We do so by starting and the current position and proceeding backwards 601 until we find a delimiters that's not inside a string." 602 (if (and (looking-back "[])}]" (line-beginning-position)) 603 (null (nth 3 (syntax-ppss)))) 604 (backward-sexp) 605 (while (or (not (looking-at-p "[({[]")) 606 (nth 3 (syntax-ppss))) 607 (backward-char)))) 608 609 (defun cider--find-last-error-location (message) 610 "Return the location (begin end buffer) from the Clojure error MESSAGE. 611 If location could not be found, return nil." 612 (save-excursion 613 (let ((info (cider-extract-error-info cider-compilation-regexp message))) 614 (when info 615 (let ((file (nth 0 info)) 616 (line (nth 1 info)) 617 (col (nth 2 info))) 618 (unless (or (not (stringp file)) 619 (cider--tooling-file-p file)) 620 (when-let* ((buffer (cider-find-file file))) 621 (with-current-buffer buffer 622 (save-excursion 623 (save-restriction 624 (widen) 625 (goto-char (point-min)) 626 (forward-line (1- line)) 627 (move-to-column (or col 0)) 628 (let ((begin (progn (if col (cider--goto-expression-start) (back-to-indentation)) 629 (point))) 630 (end (progn (if col (forward-list) (move-end-of-line nil)) 631 (point)))) 632 (list begin end buffer)))))))))))) 633 634 (defun cider-handle-compilation-errors (message eval-buffer) 635 "Highlight and jump to compilation error extracted from MESSAGE. 636 EVAL-BUFFER is the buffer that was current during user's interactive 637 evaluation command. Honor `cider-auto-jump-to-error'." 638 (when-let* ((loc (cider--find-last-error-location message)) 639 (overlay (make-overlay (nth 0 loc) (nth 1 loc) (nth 2 loc))) 640 (info (cider-extract-error-info cider-compilation-regexp message))) 641 (let* ((face (nth 3 info)) 642 (note (nth 4 info)) 643 (auto-jump (if (eq cider-auto-jump-to-error 'errors-only) 644 (not (or (eq face 'cider-warning-highlight-face) 645 (string-match-p "warning" note))) 646 cider-auto-jump-to-error))) 647 (overlay-put overlay 'cider-note-p t) 648 (overlay-put overlay 'font-lock-face face) 649 (overlay-put overlay 'cider-note note) 650 (overlay-put overlay 'help-echo note) 651 (overlay-put overlay 'modification-hooks 652 (list (lambda (o &rest _args) (delete-overlay o)))) 653 (when auto-jump 654 (with-current-buffer eval-buffer 655 (push-mark) 656 ;; At this stage selected window commonly is *cider-error* and we need to 657 ;; re-select the original user window. If eval-buffer is not 658 ;; visible it was probably covered as a result of a small screen or user 659 ;; configuration (https://github.com/clojure-emacs/cider/issues/847). In 660 ;; that case we don't jump at all in order to avoid covering *cider-error* 661 ;; buffer. 662 (when-let* ((win (get-buffer-window eval-buffer))) 663 (with-selected-window win 664 (cider-jump-to (nth 2 loc) (car loc))))))))) 665 666 667 ;;; Interactive evaluation handlers 668 (defun cider-insert-eval-handler (&optional buffer) 669 "Make an nREPL evaluation handler for the BUFFER. 670 The handler simply inserts the result value in BUFFER." 671 (let ((eval-buffer (current-buffer)) 672 (res "")) 673 (nrepl-make-response-handler (or buffer eval-buffer) 674 (lambda (_buffer value) 675 (with-current-buffer buffer 676 (insert value)) 677 (when cider-eval-register 678 (setq res (concat res value)))) 679 (lambda (_buffer out) 680 (cider-repl-emit-interactive-stdout out)) 681 (lambda (_buffer err) 682 (cider-handle-compilation-errors err eval-buffer)) 683 (lambda (_buffer) 684 (when cider-eval-register 685 (set-register cider-eval-register res)))))) 686 687 (defun cider--emit-interactive-eval-output (output repl-emit-function) 688 "Emit output resulting from interactive code evaluation. 689 The OUTPUT can be sent to either a dedicated output buffer or the current 690 REPL buffer. This is controlled by `cider-interactive-eval-output-destination'. 691 REPL-EMIT-FUNCTION emits the OUTPUT." 692 (pcase cider-interactive-eval-output-destination 693 (`output-buffer (let ((output-buffer (or (get-buffer cider-output-buffer) 694 (cider-popup-buffer cider-output-buffer t)))) 695 (cider-emit-into-popup-buffer output-buffer output) 696 (pop-to-buffer output-buffer))) 697 (`repl-buffer (funcall repl-emit-function output)) 698 (_ (error "Unsupported value %s for `cider-interactive-eval-output-destination'" 699 cider-interactive-eval-output-destination)))) 700 701 (defun cider-emit-interactive-eval-output (output) 702 "Emit OUTPUT resulting from interactive code evaluation. 703 The output can be send to either a dedicated output buffer or the current 704 REPL buffer. This is controlled via 705 `cider-interactive-eval-output-destination'." 706 (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stdout)) 707 708 (defun cider-emit-interactive-eval-err-output (output) 709 "Emit err OUTPUT resulting from interactive code evaluation. 710 The output can be send to either a dedicated output buffer or the current 711 REPL buffer. This is controlled via 712 `cider-interactive-eval-output-destination'." 713 (cider--emit-interactive-eval-output output 'cider-repl-emit-interactive-stderr)) 714 715 (defun cider--make-fringe-overlays-for-region (beg end) 716 "Place eval indicators on all sexps between BEG and END." 717 (with-current-buffer (if (markerp end) 718 (marker-buffer end) 719 (current-buffer)) 720 (save-excursion 721 (goto-char beg) 722 (remove-overlays beg end 'category 'cider-fringe-indicator) 723 (condition-case nil 724 (while (progn (clojure-forward-logical-sexp) 725 (and (<= (point) end) 726 (not (eobp)))) 727 (cider--make-fringe-overlay (point))) 728 (scan-error nil))))) 729 730 (declare-function cider-inspect-last-result "cider-inspector") 731 (defun cider-interactive-eval-handler (&optional buffer place) 732 "Make an interactive eval handler for BUFFER. 733 PLACE is used to display the evaluation result. 734 If non-nil, it can be the position where the evaluated sexp ends, 735 or it can be a list with (START END) of the evaluated region. 736 Update the cider-inspector buffer with the evaluation result 737 when `cider-auto-inspect-after-eval' is non-nil." 738 739 (let* ((eval-buffer (current-buffer)) 740 (beg (car-safe place)) 741 (end (or (car-safe (cdr-safe place)) place)) 742 (beg (when beg (copy-marker beg))) 743 (end (when end (copy-marker end))) 744 (fringed nil) 745 (res "")) 746 (nrepl-make-response-handler (or buffer eval-buffer) 747 (lambda (_buffer value) 748 (setq res (concat res value)) 749 (cider--display-interactive-eval-result res end)) 750 (lambda (_buffer out) 751 (cider-emit-interactive-eval-output out)) 752 (lambda (_buffer err) 753 (cider-emit-interactive-eval-err-output err) 754 755 (when (or (not cider-show-error-buffer) 756 (not (cider-connection-has-capability-p 'jvm-compilation-errors))) 757 758 ;; Display errors as temporary overlays 759 (let ((cider-result-use-clojure-font-lock nil)) 760 (cider--display-interactive-eval-result 761 err end 'cider-error-overlay-face))) 762 (cider-handle-compilation-errors err eval-buffer)) 763 (lambda (buffer) 764 (if beg 765 (unless fringed 766 (cider--make-fringe-overlays-for-region beg end) 767 (setq fringed t)) 768 (cider--make-fringe-overlay end)) 769 (when (and cider-auto-inspect-after-eval 770 (boundp 'cider-inspector-buffer) 771 (windowp (get-buffer-window cider-inspector-buffer 'visible))) 772 (cider-inspect-last-result) 773 (select-window (get-buffer-window buffer))) 774 (when cider-eval-register 775 (set-register cider-eval-register res)))))) 776 777 778 (defun cider-load-file-handler (&optional buffer done-handler) 779 "Make a load file handler for BUFFER. 780 Optional argument DONE-HANDLER lambda will be run once load is complete." 781 (let ((eval-buffer (current-buffer)) 782 (res "")) 783 (nrepl-make-response-handler (or buffer eval-buffer) 784 (lambda (buffer value) 785 (cider--display-interactive-eval-result value) 786 (when cider-eval-register 787 (setq res (concat res value))) 788 (when (buffer-live-p buffer) 789 (with-current-buffer buffer 790 (cider--make-fringe-overlays-for-region (point-min) (point-max)) 791 (run-hooks 'cider-file-loaded-hook)))) 792 (lambda (_buffer value) 793 (cider-emit-interactive-eval-output value)) 794 (lambda (_buffer err) 795 (cider-emit-interactive-eval-err-output err) 796 (cider-handle-compilation-errors err eval-buffer)) 797 (lambda (buffer) 798 (when cider-eval-register 799 (set-register cider-eval-register res)) 800 (when done-handler 801 (funcall done-handler buffer))) 802 (lambda () 803 (funcall nrepl-err-handler))))) 804 805 (defun cider-eval-print-handler (&optional buffer) 806 "Make a handler for evaluating and printing result in BUFFER." 807 ;; NOTE: cider-eval-register behavior is not implemented here for performance reasons. 808 ;; See https://github.com/clojure-emacs/cider/pull/3162 809 (nrepl-make-response-handler (or buffer (current-buffer)) 810 (lambda (buffer value) 811 (with-current-buffer buffer 812 (insert 813 (if (derived-mode-p 'cider-clojure-interaction-mode) 814 (format "\n%s\n" value) 815 value)))) 816 (lambda (_buffer out) 817 (cider-emit-interactive-eval-output out)) 818 (lambda (_buffer err) 819 (cider-emit-interactive-eval-err-output err)) 820 ())) 821 822 (defun cider-eval-print-with-comment-handler (buffer location comment-prefix) 823 "Make a handler for evaluating and printing commented results in BUFFER. 824 LOCATION is the location marker at which to insert. COMMENT-PREFIX is the 825 comment prefix to use." 826 (let ((res "")) 827 (nrepl-make-response-handler buffer 828 (lambda (_buffer value) 829 (setq res (concat res value))) 830 (lambda (_buffer out) 831 (cider-emit-interactive-eval-output out)) 832 (lambda (_buffer err) 833 (cider-emit-interactive-eval-err-output err)) 834 (lambda (buffer) 835 (with-current-buffer buffer 836 (save-excursion 837 (goto-char (marker-position location)) 838 (insert (concat comment-prefix 839 res "\n")))) 840 (when cider-eval-register 841 (set-register cider-eval-register res)))))) 842 843 (defun cider-maybe-insert-multiline-comment (result comment-prefix continued-prefix comment-postfix) 844 "Insert eval RESULT at current location if RESULT is not empty. 845 RESULT will be preceded by COMMENT-PREFIX. 846 CONTINUED-PREFIX is inserted for each additional line of output. 847 COMMENT-POSTFIX is inserted after final text output." 848 (unless (string= result "") 849 (clojure-indent-line) 850 (let ((lines (split-string result "[\n]+" t)) 851 (beg (point)) 852 (col (current-indentation))) 853 ;; only the first line gets the normal comment-prefix 854 (insert (concat comment-prefix (pop lines))) 855 (dolist (elem lines) 856 (insert (concat "\n" continued-prefix elem))) 857 (indent-rigidly beg (point) col) 858 (unless (string= comment-postfix "") 859 (insert comment-postfix))))) 860 861 (defun cider-eval-pprint-with-multiline-comment-handler (buffer location comment-prefix continued-prefix comment-postfix) 862 "Make a handler for evaluating and inserting results in BUFFER. 863 The inserted text is pretty-printed and region will be commented. 864 LOCATION is the location marker at which to insert. 865 COMMENT-PREFIX is the comment prefix for the first line of output. 866 CONTINUED-PREFIX is the comment prefix to use for the remaining lines. 867 COMMENT-POSTFIX is the text to output after the last line." 868 (let ((res "")) 869 (nrepl-make-response-handler 870 buffer 871 (lambda (_buffer value) 872 (setq res (concat res value))) 873 nil 874 (lambda (_buffer err) 875 (setq res (concat res err))) 876 (lambda (buffer) 877 (with-current-buffer buffer 878 (save-excursion 879 (goto-char (marker-position location)) 880 ;; edge case: defun at eob 881 (unless (bolp) (insert "\n")) 882 (cider-maybe-insert-multiline-comment res comment-prefix continued-prefix comment-postfix))) 883 (when cider-eval-register 884 (set-register cider-eval-register res))) 885 nil 886 nil 887 (lambda (_buffer warning) 888 (setq res (concat res warning)))))) 889 890 (defun cider-popup-eval-handler (&optional buffer) 891 "Make a handler for printing evaluation results in popup BUFFER. 892 This is used by pretty-printing commands." 893 ;; NOTE: cider-eval-register behavior is not implemented here for performance reasons. 894 ;; See https://github.com/clojure-emacs/cider/pull/3162 895 (nrepl-make-response-handler 896 (or buffer (current-buffer)) 897 (lambda (buffer value) 898 (cider-emit-into-popup-buffer buffer (ansi-color-apply value) nil t)) 899 (lambda (_buffer out) 900 (cider-emit-interactive-eval-output out)) 901 (lambda (_buffer err) 902 (cider-emit-interactive-eval-err-output err)) 903 nil 904 nil 905 nil 906 (lambda (buffer warning) 907 (cider-emit-into-popup-buffer buffer warning 'font-lock-warning-face t)))) 908 909 910 ;;; Interactive valuation commands 911 912 (defvar cider-to-nrepl-filename-function 913 (with-no-warnings 914 (lambda (path) 915 (let ((path* (if (eq system-type 'cygwin) 916 (cygwin-convert-file-name-to-windows path) 917 path))) 918 (or (cider--translate-path-to-nrepl path*) path*)))) 919 "Function to translate Emacs filenames to nREPL namestrings.") 920 921 (defun cider--prep-interactive-eval (form connection) 922 "Prepare the environment for an interactive eval of FORM in CONNECTION. 923 Ensure the current ns declaration has been evaluated (so that the ns 924 containing FORM exists). Cache ns-form in the current buffer unless FORM is 925 ns declaration itself. Clear any compilation highlights and kill the error 926 window." 927 (cider--clear-compilation-highlights) 928 (cider--quit-error-window) 929 (let ((cur-ns-form (cider-ns-form))) 930 (when (and cur-ns-form 931 (not (cider-ns-form-p form)) 932 (cider-repl--ns-form-changed-p cur-ns-form connection)) 933 (when cider-auto-track-ns-form-changes 934 ;; The first interactive eval on a file can load a lot of libs. This can 935 ;; easily lead to more than 10 sec. 936 (let ((nrepl-sync-request-timeout 30)) 937 ;; TODO: check for evaluation errors 938 (cider-nrepl-sync-request:eval cur-ns-form connection))) 939 ;; cache at the end, in case of errors 940 (cider-repl--cache-ns-form cur-ns-form connection)))) 941 942 (defvar-local cider-interactive-eval-override nil 943 "Function to call instead of `cider-interactive-eval'.") 944 945 (defun cider-interactive-eval (form &optional callback bounds additional-params) 946 "Evaluate FORM and dispatch the response to CALLBACK. 947 If the code to be evaluated comes from a buffer, it is preferred to use a 948 nil FORM, and specify the code via the BOUNDS argument instead. 949 950 This function is the main entry point in CIDER's interactive evaluation 951 API. Most other interactive eval functions should rely on this function. 952 If CALLBACK is nil use `cider-interactive-eval-handler'. 953 BOUNDS, if non-nil, is a list of two numbers marking the start and end 954 positions of FORM in its buffer. 955 ADDITIONAL-PARAMS is a map to be merged into the request message. 956 957 If `cider-interactive-eval-override' is a function, call it with the same 958 arguments and only proceed with evaluation if it returns nil." 959 (let ((form (or form (apply #'buffer-substring-no-properties bounds))) 960 (start (car-safe bounds)) 961 (end (car-safe (cdr-safe bounds)))) 962 (when (and start end) 963 ;; NOTE: don't use `remove-overlays' as it splits and leaves behind 964 ;; partial overlays, leading to duplicate eval results in some situations. 965 (dolist (ov (overlays-in start end)) 966 (when (eq (overlay-get ov 'cider-temporary) t) 967 (delete-overlay ov)))) 968 (unless (and cider-interactive-eval-override 969 (functionp cider-interactive-eval-override) 970 (funcall cider-interactive-eval-override form callback bounds)) 971 (cider-map-repls :auto 972 (lambda (connection) 973 (cider--prep-interactive-eval form connection) 974 (cider-nrepl-request:eval 975 form 976 (or callback (cider-interactive-eval-handler nil bounds)) 977 ;; always eval ns forms in the user namespace 978 ;; otherwise trying to eval ns form for the first time will produce an error 979 (if (cider-ns-form-p form) "user" (cider-current-ns)) 980 (when start (line-number-at-pos start)) 981 (when start (cider-column-number-at-pos start)) 982 (seq-mapcat #'identity additional-params) 983 connection)))))) 984 985 (defun cider-eval-region (start end) 986 "Evaluate the region between START and END." 987 (interactive "r") 988 (cider-interactive-eval nil 989 nil 990 (list start end) 991 (cider--nrepl-pr-request-map))) 992 993 (defun cider-eval-last-sexp (&optional output-to-current-buffer) 994 "Evaluate the expression preceding point. 995 If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current 996 buffer." 997 (interactive "P") 998 (cider-interactive-eval nil 999 (when output-to-current-buffer (cider-eval-print-handler)) 1000 (cider-last-sexp 'bounds) 1001 (cider--nrepl-pr-request-map))) 1002 1003 (defun cider-eval-last-sexp-and-replace () 1004 "Evaluate the expression preceding point and replace it with its result." 1005 (interactive) 1006 (let ((last-sexp (cider-last-sexp))) 1007 ;; we have to be sure the evaluation won't result in an error 1008 (cider-nrepl-sync-request:eval last-sexp) 1009 ;; seems like the sexp is valid, so we can safely kill it 1010 (let ((opoint (point))) 1011 (clojure-backward-logical-sexp) 1012 (kill-region (point) opoint)) 1013 (cider-interactive-eval last-sexp 1014 (cider-eval-print-handler) 1015 nil 1016 (cider--nrepl-pr-request-map)))) 1017 1018 (defun cider-eval-list-at-point (&optional output-to-current-buffer) 1019 "Evaluate the list (eg. a function call, surrounded by parens) around point. 1020 If invoked with OUTPUT-TO-CURRENT-BUFFER, output the result to current buffer." 1021 (interactive "P") 1022 (save-excursion 1023 (goto-char (cadr (cider-list-at-point 'bounds))) 1024 (cider-eval-last-sexp output-to-current-buffer))) 1025 1026 (defun cider-eval-sexp-at-point (&optional output-to-current-buffer) 1027 "Evaluate the expression around point. 1028 If invoked with OUTPUT-TO-CURRENT-BUFFER, output the result to current buffer." 1029 (interactive "P") 1030 (save-excursion 1031 (goto-char (cadr (cider-sexp-at-point 'bounds))) 1032 (cider-eval-last-sexp output-to-current-buffer))) 1033 1034 (defun cider-tap-last-sexp (&optional output-to-current-buffer) 1035 "Evaluate and tap the expression preceding point. 1036 If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current 1037 buffer." 1038 (interactive "P") 1039 (let ((tapped-form (concat "(clojure.core/doto " (cider-last-sexp) " (clojure.core/tap>))"))) 1040 (cider-interactive-eval tapped-form 1041 (when output-to-current-buffer (cider-eval-print-handler)) 1042 nil 1043 (cider--nrepl-pr-request-map)))) 1044 1045 (defun cider-tap-sexp-at-point (&optional output-to-current-buffer) 1046 "Evaluate and tap the expression around point. 1047 If invoked with OUTPUT-TO-CURRENT-BUFFER, output the result to current buffer." 1048 (interactive "P") 1049 (save-excursion 1050 (goto-char (cadr (cider-sexp-at-point 'bounds))) 1051 (cider-tap-last-sexp output-to-current-buffer))) 1052 1053 (defvar-local cider-previous-eval-context nil 1054 "The previous evaluation context if any. 1055 That's set by commands like `cider-eval-last-sexp-in-context'.") 1056 1057 1058 (defun cider--guess-eval-context () 1059 "Return context for `cider--eval-in-context'. 1060 This is done by extracting all parent let bindings." 1061 (save-excursion 1062 (let ((res "")) 1063 (condition-case nil 1064 (while t 1065 (backward-up-list) 1066 (when (looking-at (rx "(" (or "when-let" "if-let" "let") (opt "*") 1067 symbol-end (* space) 1068 (group "["))) ;; binding vector 1069 (let ((beg (match-end 1)) 1070 (end (save-excursion 1071 (goto-char (match-beginning 1)) 1072 (forward-sexp 1) 1073 (1- (point))))) 1074 (setq res (concat (buffer-substring-no-properties beg end) ", " res))))) 1075 (scan-error res))))) 1076 1077 (defun cider--eval-in-context (bounds &optional guess) 1078 "Evaluate code at BOUNDS in user-provided evaluation context. 1079 When GUESS is non-nil, attempt to extract the context from parent let-bindings." 1080 (let* ((code (string-trim-right 1081 (buffer-substring-no-properties (car bounds) (cadr bounds)))) 1082 (eval-context 1083 (minibuffer-with-setup-hook (if guess #'beginning-of-buffer #'ignore) 1084 (read-string "Evaluation context (let-style): " 1085 (if guess (cider--guess-eval-context) 1086 cider-previous-eval-context)))) 1087 (code (concat "(let [" eval-context "]\n " code ")"))) 1088 (setq-local cider-previous-eval-context eval-context) 1089 (cider-interactive-eval code 1090 nil 1091 bounds 1092 (cider--nrepl-pr-request-map)))) 1093 1094 (defun cider-eval-last-sexp-in-context (guess) 1095 "Evaluate the preceding sexp in user-supplied context. 1096 The context is just a let binding vector (without the brackets). 1097 The context is remembered between command invocations. 1098 1099 When GUESS is non-nil, or called interactively with \\[universal-argument], 1100 attempt to extract the context from parent let-bindings." 1101 (interactive "P") 1102 (cider--eval-in-context (cider-last-sexp 'bounds) guess)) 1103 1104 (defun cider-eval-sexp-at-point-in-context (guess) 1105 "Evaluate the sexp around point in user-supplied context. 1106 1107 The context is just a let binding vector (without the brackets). 1108 The context is remembered between command invocations. 1109 1110 When GUESS is non-nil, or called interactively with \\[universal-argument], 1111 attempt to extract the context from parent let-bindings." 1112 (interactive "P") 1113 (cider--eval-in-context (cider-sexp-at-point 'bounds) guess)) 1114 1115 (defun cider-eval-defun-to-comment (&optional insert-before) 1116 "Evaluate the \"top-level\" form and insert result as comment. 1117 1118 The formatting of the comment is defined in `cider-comment-prefix' 1119 which, by default, is \";; => \" and can be customized. 1120 1121 With the prefix arg INSERT-BEFORE, insert before the form, otherwise afterwards." 1122 (interactive "P") 1123 (let* ((bounds (cider-defun-at-point 'bounds)) 1124 (insertion-point (nth (if insert-before 0 1) bounds))) 1125 (cider-interactive-eval nil 1126 (cider-eval-print-with-comment-handler 1127 (current-buffer) 1128 (set-marker (make-marker) insertion-point) 1129 cider-comment-prefix) 1130 bounds 1131 (cider--nrepl-pr-request-map)))) 1132 1133 (defun cider-pprint-form-to-comment (form-fn insert-before) 1134 "Evaluate the form selected by FORM-FN and insert result as comment. 1135 FORM-FN can be either `cider-last-sexp' or `cider-defun-at-point'. 1136 1137 The formatting of the comment is controlled via three options: 1138 `cider-comment-prefix' \";; => \" 1139 `cider-comment-continued-prefix' \";; \" 1140 `cider-comment-postfix' \"\" 1141 1142 so that with customization you can optionally wrap the output 1143 in the reader macro \"#_( .. )\", or \"(comment ... )\", or any 1144 other desired formatting. 1145 1146 If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards." 1147 (let* ((bounds (funcall form-fn 'bounds)) 1148 (insertion-point (nth (if insert-before 0 1) bounds)) 1149 ;; when insert-before, we need a newline after the output to 1150 ;; avoid commenting the first line of the form 1151 (comment-postfix (concat cider-comment-postfix 1152 (if insert-before "\n" "")))) 1153 (cider-interactive-eval nil 1154 (cider-eval-pprint-with-multiline-comment-handler 1155 (current-buffer) 1156 (set-marker (make-marker) insertion-point) 1157 cider-comment-prefix 1158 cider-comment-continued-prefix 1159 comment-postfix) 1160 bounds 1161 (cider--nrepl-print-request-map fill-column)))) 1162 1163 (defun cider-pprint-eval-last-sexp-to-comment (&optional insert-before) 1164 "Evaluate the last sexp and insert result as comment. 1165 1166 The formatting of the comment is controlled via three options: 1167 `cider-comment-prefix' \";; => \" 1168 `cider-comment-continued-prefix' \";; \" 1169 `cider-comment-postfix' \"\" 1170 1171 so that with customization you can optionally wrap the output 1172 in the reader macro \"#_( .. )\", or \"(comment ... )\", or any 1173 other desired formatting. 1174 1175 If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards." 1176 (interactive "P") 1177 (cider-pprint-form-to-comment 'cider-last-sexp insert-before)) 1178 1179 (defun cider-pprint-eval-defun-to-comment (&optional insert-before) 1180 "Evaluate the \"top-level\" form and insert result as comment. 1181 1182 The formatting of the comment is controlled via three options: 1183 `cider-comment-prefix' \";; => \" 1184 `cider-comment-continued-prefix' \";; \" 1185 `cider-comment-postfix' \"\" 1186 1187 so that with customization you can optionally wrap the output 1188 in the reader macro \"#_( .. )\", or \"(comment ... )\", or any 1189 other desired formatting. 1190 1191 If INSERT-BEFORE is non-nil, insert before the form, otherwise afterwards." 1192 (interactive "P") 1193 (cider-pprint-form-to-comment 'cider-defun-at-point insert-before)) 1194 1195 (declare-function cider-switch-to-repl-buffer "cider-mode") 1196 1197 (defun cider-eval-last-sexp-to-repl (&optional prefix) 1198 "Evaluate the expression preceding point and insert its result in the REPL. 1199 If invoked with a PREFIX argument, switch to the REPL buffer." 1200 (interactive "P") 1201 (cider-interactive-eval nil 1202 (cider-insert-eval-handler (cider-current-repl)) 1203 (cider-last-sexp 'bounds) 1204 (cider--nrepl-pr-request-map)) 1205 (when prefix 1206 (cider-switch-to-repl-buffer))) 1207 1208 (defun cider-pprint-eval-last-sexp-to-repl (&optional prefix) 1209 "Evaluate expr before point and insert its pretty-printed result in the REPL. 1210 If invoked with a PREFIX argument, switch to the REPL buffer." 1211 (interactive "P") 1212 (cider-interactive-eval nil 1213 (cider-insert-eval-handler (cider-current-repl)) 1214 (cider-last-sexp 'bounds) 1215 (cider--nrepl-print-request-map fill-column)) 1216 (when prefix 1217 (cider-switch-to-repl-buffer))) 1218 1219 (defun cider-eval-print-last-sexp (&optional pretty-print) 1220 "Evaluate the expression preceding point. 1221 Print its value into the current buffer. 1222 With an optional PRETTY-PRINT prefix it pretty-prints the result." 1223 (interactive "P") 1224 (cider-interactive-eval nil 1225 (cider-eval-print-handler) 1226 (cider-last-sexp 'bounds) 1227 (if pretty-print 1228 (cider--nrepl-print-request-map fill-column) 1229 (cider--nrepl-pr-request-map)))) 1230 1231 (defun cider--pprint-eval-form (form) 1232 "Pretty print FORM in popup buffer." 1233 (let* ((buffer (current-buffer)) 1234 (result-buffer (cider-popup-buffer cider-result-buffer nil 'clojure-mode 'ancillary)) 1235 (handler (cider-popup-eval-handler result-buffer))) 1236 (with-current-buffer buffer 1237 (cider-interactive-eval (when (stringp form) form) 1238 handler 1239 (when (consp form) form) 1240 (cider--nrepl-print-request-map fill-column))))) 1241 1242 (defun cider-pprint-eval-last-sexp (&optional output-to-current-buffer) 1243 "Evaluate the sexp preceding point and pprint its value. 1244 If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current 1245 buffer, else display in a popup buffer." 1246 (interactive "P") 1247 (if output-to-current-buffer 1248 (cider-pprint-eval-last-sexp-to-comment) 1249 (cider--pprint-eval-form (cider-last-sexp 'bounds)))) 1250 1251 (defun cider--prompt-and-insert-inline-dbg () 1252 "Insert a #dbg button at the current sexp." 1253 (save-excursion 1254 (let ((beg)) 1255 (skip-chars-forward "\r\n[:blank:]") 1256 (unless (looking-at-p "(") 1257 (ignore-errors (backward-up-list))) 1258 (setq beg (point)) 1259 (let* ((cond (cider-read-from-minibuffer "Condition for debugging (leave empty for \"always\"): ")) 1260 (button (propertize (concat "#dbg" 1261 (unless (equal cond "") 1262 (format " ^{:break/when %s}" cond))) 1263 'font-lock-face 'cider-fragile-button-face))) 1264 (when (> (current-column) 30) 1265 (insert "\n") 1266 (indent-according-to-mode)) 1267 (insert button) 1268 (when (> (current-column) 40) 1269 (insert "\n") 1270 (indent-according-to-mode))) 1271 (make-button beg (point) 1272 'help-echo "Breakpoint. Reevaluate this form to remove it." 1273 :type 'cider-fragile)))) 1274 1275 (defun cider-eval-defun-at-point (&optional debug-it) 1276 "Evaluate the current toplevel form, and print result in the minibuffer. 1277 With DEBUG-IT prefix argument, also debug the entire form as with the 1278 command `cider-debug-defun-at-point'." 1279 (interactive "P") 1280 (let ((inline-debug (eq 16 (car-safe debug-it)))) 1281 (when debug-it 1282 (when (derived-mode-p 'clojurescript-mode) 1283 (when (y-or-n-p (concat "The debugger doesn't support ClojureScript yet, and we need help with that." 1284 " \nWould you like to read the Feature Request?")) 1285 (browse-url "https://github.com/clojure-emacs/cider/issues/1416")) 1286 (user-error "The debugger does not support ClojureScript")) 1287 (when inline-debug 1288 (cider--prompt-and-insert-inline-dbg))) 1289 (cider-interactive-eval (when (and debug-it (not inline-debug)) 1290 (concat "#dbg\n" (cider-defun-at-point))) 1291 nil 1292 (cider-defun-at-point 'bounds) 1293 (cider--nrepl-pr-request-map)))) 1294 1295 (defun cider--insert-closing-delimiters (code) 1296 "Closes all open parenthesized or bracketed expressions of CODE." 1297 (with-temp-buffer 1298 (insert code) 1299 (goto-char (point-max)) 1300 (let ((matching-delimiter nil)) 1301 (while (ignore-errors 1302 (save-excursion 1303 (backward-up-list 1) 1304 (setq matching-delimiter (cdr (syntax-after (point))))) 1305 t) 1306 (insert-char matching-delimiter))) 1307 (buffer-string))) 1308 1309 (defun cider-eval-defun-up-to-point (&optional output-to-current-buffer) 1310 "Evaluate the current toplevel form up to point. 1311 If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current 1312 buffer. It constructs an expression to eval in the following manner: 1313 1314 - It find the code between the point and the start of the toplevel expression; 1315 - It balances this bit of code by closing all open expressions; 1316 - It evaluates the resulting code using `cider-interactive-eval'." 1317 (interactive "P") 1318 (let* ((beg-of-defun (save-excursion (beginning-of-defun) (point))) 1319 (code (buffer-substring-no-properties beg-of-defun (point))) 1320 (code (cider--insert-closing-delimiters code))) 1321 (cider-interactive-eval code 1322 (when output-to-current-buffer 1323 (cider-eval-print-handler)) 1324 (list beg-of-defun (point)) 1325 (cider--nrepl-pr-request-map)))) 1326 1327 (defun cider--matching-delimiter (delimiter) 1328 "Get the matching (opening/closing) delimiter for DELIMITER." 1329 (pcase delimiter 1330 (?\( ?\)) 1331 (?\[ ?\]) 1332 (?\{ ?\}) 1333 (?\) ?\() 1334 (?\] ?\[) 1335 (?\} ?\{))) 1336 1337 (defun cider-eval-sexp-up-to-point (&optional output-to-current-buffer) 1338 "Evaluate the current sexp form up to point. 1339 If invoked with OUTPUT-TO-CURRENT-BUFFER, print the result in the current 1340 buffer. It constructs an expression to eval in the following manner: 1341 1342 - It finds the code between the point and the start of the sexp expression; 1343 - It balances this bit of code by closing the expression; 1344 - It evaluates the resulting code using `cider-interactive-eval'." 1345 (interactive "P") 1346 (let* ((beg-of-sexp (save-excursion (up-list) (backward-list) (point))) 1347 (beg-delimiter (save-excursion (up-list) (backward-list) (char-after))) 1348 (beg-set? (save-excursion (up-list) (backward-list) (char-before))) 1349 (code (buffer-substring-no-properties beg-of-sexp (point))) 1350 (code (if (= beg-set? ?#) (concat (list beg-set?) code) code)) 1351 (code (concat code (list (cider--matching-delimiter beg-delimiter))))) 1352 (cider-interactive-eval code 1353 (when output-to-current-buffer 1354 (cider-eval-print-handler)) 1355 (list beg-of-sexp (point)) 1356 (cider--nrepl-pr-request-map)))) 1357 1358 (defun cider-pprint-eval-defun-at-point (&optional output-to-current-buffer) 1359 "Evaluate the \"top-level\" form at point and pprint its value. 1360 If invoked with OUTPUT-TO-CURRENT-BUFFER, insert as comment in the current 1361 buffer, else display in a popup buffer." 1362 (interactive "P") 1363 (if output-to-current-buffer 1364 (cider-pprint-eval-defun-to-comment) 1365 (cider--pprint-eval-form (cider-defun-at-point 'bounds)))) 1366 1367 (defun cider-eval-ns-form (&optional undef-all) 1368 "Evaluate the current buffer's namespace form. 1369 When UNDEF-ALL is non-nil, unmap all symbols and aliases first." 1370 (interactive "P") 1371 (when-let ((ns (clojure-find-ns))) 1372 (save-excursion 1373 (goto-char (match-beginning 0)) 1374 (when undef-all 1375 (cider-undef-all ns)) 1376 (cider-eval-defun-at-point)))) 1377 1378 (defun cider-read-and-eval (&optional value) 1379 "Read a sexp from the minibuffer and output its result to the echo area. 1380 If VALUE is non-nil, it is inserted into the minibuffer as initial input." 1381 (interactive) 1382 (let* ((form (cider-read-from-minibuffer "Clojure Eval: " value)) 1383 (override cider-interactive-eval-override) 1384 (ns-form (if (cider-ns-form-p form) "" (format "(ns %s)" (cider-current-ns))))) 1385 (with-current-buffer (get-buffer-create cider-read-eval-buffer) 1386 (erase-buffer) 1387 (clojure-mode) 1388 (unless (string= "" ns-form) 1389 (insert ns-form "\n\n")) 1390 (insert form) 1391 (let ((cider-interactive-eval-override override)) 1392 (cider-interactive-eval form 1393 nil 1394 nil 1395 (cider--nrepl-pr-request-map)))))) 1396 1397 (defun cider-read-and-eval-defun-at-point () 1398 "Insert the toplevel form at point in the minibuffer and output its result. 1399 The point is placed next to the function name in the minibuffer to allow 1400 passing arguments." 1401 (interactive) 1402 (let* ((fn-name (cadr (split-string (cider-defun-at-point)))) 1403 (form (format "(%s)" fn-name))) 1404 (cider-read-and-eval (cons form (length form))))) 1405 1406 (defun cider-kill-last-result () 1407 "Save the last evaluated result into the kill ring." 1408 (interactive) 1409 (kill-new 1410 (nrepl-dict-get (cider-nrepl-sync-request:eval "*1") "value"))) 1411 1412 (defun cider-undef () 1413 "Undefine a symbol from the current ns." 1414 (interactive) 1415 (cider-ensure-op-supported "undef") 1416 (cider-read-symbol-name 1417 "Undefine symbol: " 1418 (lambda (sym) 1419 (cider-nrepl-send-request 1420 `("op" "undef" 1421 "ns" ,(cider-current-ns) 1422 "sym" ,sym) 1423 (cider-interactive-eval-handler (current-buffer)))))) 1424 1425 (defun cider-undef-all (&optional ns) 1426 "Undefine all symbols and aliases from the namespace NS." 1427 (interactive) 1428 (cider-ensure-op-supported "undef-all") 1429 (cider-nrepl-send-sync-request 1430 `("op" "undef-all" 1431 "ns" ,(or ns (cider-current-ns))))) 1432 1433 ;; Eval keymaps 1434 (defvar cider-eval-pprint-commands-map 1435 (let ((map (define-prefix-command 'cider-eval-pprint-commands-map))) 1436 ;; single key bindings defined last for display in menu 1437 (define-key map (kbd "e") #'cider-pprint-eval-last-sexp) 1438 (define-key map (kbd "d") #'cider-pprint-eval-defun-at-point) 1439 (define-key map (kbd "c e") #'cider-pprint-eval-last-sexp-to-comment) 1440 (define-key map (kbd "c d") #'cider-pprint-eval-defun-to-comment) 1441 1442 ;; duplicates with C- for convenience 1443 (define-key map (kbd "C-e") #'cider-pprint-eval-last-sexp) 1444 (define-key map (kbd "C-d") #'cider-pprint-eval-defun-at-point) 1445 (define-key map (kbd "C-c e") #'cider-pprint-eval-last-sexp-to-comment) 1446 (define-key map (kbd "C-c C-e") #'cider-pprint-eval-last-sexp-to-comment) 1447 (define-key map (kbd "C-c d") #'cider-pprint-eval-defun-to-comment) 1448 (define-key map (kbd "C-c C-d") #'cider-pprint-eval-defun-to-comment) 1449 map)) 1450 1451 (defvar cider-eval-commands-map 1452 (let ((map (define-prefix-command 'cider-eval-commands-map))) 1453 ;; single key bindings defined last for display in menu 1454 (define-key map (kbd "w") #'cider-eval-last-sexp-and-replace) 1455 (define-key map (kbd "r") #'cider-eval-region) 1456 (define-key map (kbd "n") #'cider-eval-ns-form) 1457 (define-key map (kbd "d") #'cider-eval-defun-at-point) 1458 (define-key map (kbd "e") #'cider-eval-last-sexp) 1459 (define-key map (kbd "q") #'cider-tap-last-sexp) 1460 (define-key map (kbd "l") #'cider-eval-list-at-point) 1461 (define-key map (kbd "v") #'cider-eval-sexp-at-point) 1462 (define-key map (kbd "t") #'cider-tap-sexp-at-point) 1463 (define-key map (kbd "o") #'cider-eval-sexp-up-to-point) 1464 (define-key map (kbd ".") #'cider-read-and-eval-defun-at-point) 1465 (define-key map (kbd "z") #'cider-eval-defun-up-to-point) 1466 (define-key map (kbd "c") #'cider-eval-last-sexp-in-context) 1467 (define-key map (kbd "b") #'cider-eval-sexp-at-point-in-context) 1468 (define-key map (kbd "k") #'cider-kill-last-result) 1469 (define-key map (kbd "f") 'cider-eval-pprint-commands-map) 1470 1471 ;; duplicates with C- for convenience 1472 (define-key map (kbd "C-w") #'cider-eval-last-sexp-and-replace) 1473 (define-key map (kbd "C-r") #'cider-eval-region) 1474 (define-key map (kbd "C-n") #'cider-eval-ns-form) 1475 (define-key map (kbd "C-d") #'cider-eval-defun-at-point) 1476 (define-key map (kbd "C-e") #'cider-eval-last-sexp) 1477 (define-key map (kbd "C-q") #'cider-tap-last-sexp) 1478 (define-key map (kbd "C-l") #'cider-eval-list-at-point) 1479 (define-key map (kbd "C-v") #'cider-eval-sexp-at-point) 1480 (define-key map (kbd "C-t") #'cider-tap-sexp-at-point) 1481 (define-key map (kbd "C-o") #'cider-eval-sexp-up-to-point) 1482 (define-key map (kbd "C-.") #'cider-read-and-eval-defun-at-point) 1483 (define-key map (kbd "C-z") #'cider-eval-defun-up-to-point) 1484 (define-key map (kbd "C-c") #'cider-eval-last-sexp-in-context) 1485 (define-key map (kbd "C-b") #'cider-eval-sexp-at-point-in-context) 1486 (define-key map (kbd "C-k") #'cider-kill-last-result) 1487 (define-key map (kbd "C-f") 'cider-eval-pprint-commands-map) 1488 map)) 1489 1490 (defun cider--file-string (file) 1491 "Read the contents of a FILE and return as a string." 1492 (with-current-buffer (find-file-noselect file) 1493 (save-restriction 1494 (widen) 1495 (substring-no-properties (buffer-string))))) 1496 1497 (defun cider-load-buffer (&optional buffer callback undef-all) 1498 "Load (eval) BUFFER's file in nREPL. 1499 If no buffer is provided the command acts on the current buffer. If the 1500 buffer is for a cljc file, and both a Clojure and ClojureScript REPL exists 1501 for the project, it is evaluated in both REPLs. 1502 Optional argument CALLBACK will override the default ‘cider-load-file-handler’. 1503 When UNDEF-ALL is non-nil or called with \\[universal-argument], removes 1504 all ns aliases and var mappings from the namespace before reloading it." 1505 (interactive (list (current-buffer) nil (equal current-prefix-arg '(4)))) 1506 (setq buffer (or buffer (current-buffer))) 1507 ;; When cider-load-buffer or cider-load-file are called in programs the 1508 ;; current context might not match the buffer's context. We use the caller 1509 ;; context instead of the buffer's context because that's the common use 1510 ;; case. For the other use case just let-bind the default-directory. 1511 (let ((orig-default-directory default-directory)) 1512 (with-current-buffer buffer 1513 (check-parens) 1514 (let ((default-directory orig-default-directory)) 1515 (unless buffer-file-name 1516 (user-error "Buffer `%s' is not associated with a file" (current-buffer))) 1517 (when (and cider-save-file-on-load 1518 (buffer-modified-p) 1519 (or (eq cider-save-file-on-load t) 1520 (y-or-n-p (format "Save file %s? " buffer-file-name)))) 1521 (save-buffer)) 1522 (remove-overlays nil nil 'cider-temporary t) 1523 (when undef-all 1524 (cider-undef-all (cider-current-ns))) 1525 (cider--clear-compilation-highlights) 1526 (cider--quit-error-window) 1527 (let ((filename (buffer-file-name buffer)) 1528 (ns-form (cider-ns-form))) 1529 (cider-map-repls :auto 1530 (lambda (repl) 1531 (when ns-form 1532 (cider-repl--cache-ns-form ns-form repl)) 1533 (cider-request:load-file (cider--file-string filename) 1534 (funcall cider-to-nrepl-filename-function 1535 (cider--server-filename filename)) 1536 (file-name-nondirectory filename) 1537 repl 1538 callback))) 1539 (message "Loading %s..." filename)))))) 1540 1541 (defun cider-load-file (filename &optional undef-all) 1542 "Load (eval) the Clojure file FILENAME in nREPL. 1543 If the file is a cljc file, and both a Clojure and ClojureScript REPL 1544 exists for the project, it is evaluated in both REPLs. The heavy lifting 1545 is done by `cider-load-buffer'. 1546 When UNDEF-ALL is non-nil or called with \\[universal-argument], removes 1547 all ns aliases and var mappings from the namespace before reloading it." 1548 (interactive (list 1549 (read-file-name "Load file: " nil nil nil 1550 (when (buffer-file-name) 1551 (file-name-nondirectory 1552 (buffer-file-name)))) 1553 (equal current-prefix-arg '(4)))) 1554 (if-let* ((buffer (find-buffer-visiting filename))) 1555 (cider-load-buffer buffer nil undef-all) 1556 (cider-load-buffer (find-file-noselect filename) nil undef-all))) 1557 1558 (defun cider-load-all-files (directory undef-all) 1559 "Load all files in DIRECTORY (recursively). 1560 Useful when the running nREPL on remote host. 1561 When UNDEF-ALL is non-nil or called with \\[universal-argument], removes 1562 all ns aliases and var mappings from the namespaces being reloaded" 1563 (interactive "DLoad files beneath directory: \nP") 1564 (mapcar (lambda (file) (cider-load-file file undef-all)) 1565 (directory-files-recursively directory "\\.clj[cs]?$"))) 1566 1567 (defalias 'cider-eval-file #'cider-load-file 1568 "A convenience alias as some people are confused by the load-* names.") 1569 1570 (defalias 'cider-eval-all-files #'cider-load-all-files 1571 "A convenience alias as some people are confused by the load-* names.") 1572 1573 (defalias 'cider-eval-buffer #'cider-load-buffer 1574 "A convenience alias as some people are confused by the load-* names.") 1575 1576 (defun cider-load-all-project-ns () 1577 "Load all namespaces in the current project." 1578 (interactive) 1579 (cider-ensure-connected) 1580 (cider-ensure-op-supported "ns-load-all") 1581 (when (y-or-n-p "Are you sure you want to load all namespaces in the project? ") 1582 (message "Loading all project namespaces...") 1583 (let ((loaded-ns-count (length (cider-sync-request:ns-load-all)))) 1584 (message "Loaded %d namespaces" loaded-ns-count)))) 1585 1586 (provide 'cider-eval) 1587 1588 ;;; cider-eval.el ends here