dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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