Remove Clojure-related packages

This commit is contained in:
Gergely Polonkai 2016-10-06 10:53:40 +02:00
parent 1b7f39b4ab
commit dc69a7ed04
38 changed files with 1 additions and 16516 deletions

View File

@ -1,202 +0,0 @@
;;; cider-apropos.el --- Apropos functionality for Clojure -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Jeff Valk, Bozhidar Batsov and CIDER contributors
;;
;; Author: Jeff Valk <jv@jeffvalk.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Apropos functionality for Clojure.
;;; Code:
(require 'cider-doc)
(require 'cider-util)
(require 'cider-compat)
(require 'cider-client)
(require 'cider-popup)
(require 'nrepl-dict)
(require 'clojure-mode)
(require 'apropos)
(require 'button)
(defconst cider-apropos-buffer "*cider-apropos*")
(push cider-apropos-buffer cider-ancillary-buffers)
(defcustom cider-apropos-actions '(("display-doc" . cider-doc-lookup)
("find-def" . cider--find-var)
("lookup-on-grimoire" . cider-grimoire-lookup))
"Controls the actions to be applied on the symbol found by an apropos search.
The first action key in the list will be selected as default. If the list
contains only one action key, the associated action function will be
applied automatically. An action function can be any function that receives
the symbol found by the apropos search as argument."
:type '(alist :key-type string :value-type function)
:group 'cider
:package-version '(cider . "0.13.0"))
(defun cider-apropos-doc (button)
"Display documentation for the symbol represented at BUTTON."
(cider-doc-lookup (button-get button 'apropos-symbol)))
(defun cider-apropos-summary (query ns docs-p include-private-p case-sensitive-p)
"Return a short description for the performed apropos search.
QUERY can be a regular expression list of space-separated words
\(e.g take while) which will be converted to a regular expression
\(like take.+while) automatically behind the scenes. The search may be
limited to the namespace NS, and may optionally search doc strings
\(based on DOCS-P), include private vars (based on INCLUDE-PRIVATE-P),
and be case-sensitive (based on CASE-SENSITIVE-P)."
(concat (if case-sensitive-p "Case-sensitive " "")
(if docs-p "Documentation " "")
(format "Apropos for %S" query)
(if ns (format " in namespace %S" ns) "")
(if include-private-p
" (public and private symbols)"
" (public symbols only)")))
(defun cider-apropos-highlight (doc query)
"Return the DOC string propertized to highlight QUERY matches."
(let ((pos 0))
(while (string-match query doc pos)
(setq pos (match-end 0))
(put-text-property (match-beginning 0)
(match-end 0)
'font-lock-face apropos-match-face doc)))
doc)
(defun cider-apropos-result (result query docs-p)
"Emit a RESULT matching QUERY into current buffer, formatted for DOCS-P."
(nrepl-dbind-response result (name type doc)
(let* ((label (capitalize (if (string= type "variable") "var" type)))
(help (concat "Display doc for this " (downcase label))))
(cider-propertize-region (list 'apropos-symbol name
'action 'cider-apropos-doc
'help-echo help)
(insert-text-button name 'type 'apropos-symbol)
(insert "\n ")
(insert-text-button label 'type (intern (concat "apropos-" type)))
(insert ": ")
(let ((beg (point)))
(if docs-p
(insert (cider-apropos-highlight doc query) "\n")
(insert doc)
(fill-region beg (point))))
(insert "\n")))))
(declare-function cider-mode "cider-mode")
(defun cider-show-apropos (summary results query docs-p)
"Show SUMMARY and RESULTS for QUERY in a pop-up buffer, formatted for DOCS-P."
(with-current-buffer (cider-popup-buffer cider-apropos-buffer t)
(let ((inhibit-read-only t))
(apropos-mode)
(if (boundp 'header-line-format)
(setq-local header-line-format summary)
(insert summary "\n\n"))
(dolist (result results)
(cider-apropos-result result query docs-p))
(goto-char (point-min)))))
;;;###autoload
(defun cider-apropos (query &optional ns docs-p privates-p case-sensitive-p)
"Show all symbols whose names match QUERY, a regular expression.
QUERY can also be a list of space-separated words (e.g. take while) which
will be converted to a regular expression (like take.+while) automatically
behind the scenes. The search may be limited to the namespace NS, and may
optionally search doc strings (based on DOCS-P), include private vars
\(based on PRIVATES-P), and be case-sensitive (based on CASE-SENSITIVE-P)."
(interactive
(cons (read-string "Search for Clojure symbol (a regular expression): ")
(when current-prefix-arg
(list (let ((ns (completing-read "Namespace (default is all): " (cider-sync-request:ns-list))))
(if (string= ns "") nil ns))
(y-or-n-p "Search doc strings? ")
(y-or-n-p "Include private symbols? ")
(y-or-n-p "Case-sensitive? ")))))
(cider-ensure-connected)
(cider-ensure-op-supported "apropos")
(if-let ((summary (cider-apropos-summary
query ns docs-p privates-p case-sensitive-p))
(results (cider-sync-request:apropos query ns docs-p privates-p case-sensitive-p)))
(cider-show-apropos summary results query docs-p)
(message "No apropos matches for %S" query)))
;;;###autoload
(defun cider-apropos-documentation ()
"Shortcut for (cider-apropos <query> nil t)."
(interactive)
(cider-ensure-connected)
(cider-ensure-op-supported "apropos")
(cider-apropos (read-string "Search for Clojure documentation (a regular expression): ") nil t))
(defun cider-apropos-act-on-symbol (symbol)
"Apply selected action on SYMBOL."
(let* ((first-action-key (car (car cider-apropos-actions)))
(action-key (if (= 1 (length cider-apropos-actions))
first-action-key
(completing-read (format "Choose action to apply to `%s` (default %s): "
symbol first-action-key)
cider-apropos-actions nil nil nil nil first-action-key)))
(action-fn (cdr (assoc action-key cider-apropos-actions))))
(if action-fn
(funcall action-fn symbol)
(user-error "Unknown action `%s`" action-key))))
;;;###autoload
(defun cider-apropos-select (query &optional ns docs-p privates-p case-sensitive-p)
"Similar to `cider-apropos', but presents the results in a completing read.
Show all symbols whose names match QUERY, a regular expression.
QUERY can also be a list of space-separated words (e.g. take while) which
will be converted to a regular expression (like take.+while) automatically
behind the scenes. The search may be limited to the namespace NS, and may
optionally search doc strings (based on DOCS-P), include private vars
\(based on PRIVATES-P), and be case-sensitive (based on CASE-SENSITIVE-P)."
(interactive
(cons (read-string "Search for Clojure symbol (a regular expression): ")
(when current-prefix-arg
(list (let ((ns (completing-read "Namespace (default is all): " (cider-sync-request:ns-list))))
(if (string= ns "") nil ns))
(y-or-n-p "Search doc strings? ")
(y-or-n-p "Include private symbols? ")
(y-or-n-p "Case-sensitive? ")))))
(cider-ensure-connected)
(cider-ensure-op-supported "apropos")
(if-let ((summary (cider-apropos-summary
query ns docs-p privates-p case-sensitive-p))
(results (mapcar (lambda (r) (nrepl-dict-get r "name"))
(cider-sync-request:apropos query ns docs-p privates-p case-sensitive-p))))
(cider-apropos-act-on-symbol (completing-read (concat summary ": ") results))
(message "No apropos matches for %S" query)))
;;;###autoload
(defun cider-apropos-documentation-select ()
"Shortcut for (cider-apropos-select <query> nil t)."
(interactive)
(cider-ensure-connected)
(cider-ensure-op-supported "apropos")
(cider-apropos-select (read-string "Search for Clojure documentation (a regular expression): ") nil t))
(provide 'cider-apropos)
;;; cider-apropos.el ends here

View File

@ -1,315 +0,0 @@
;;; cider-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "cider" "cider.el" (22508 49905 421980 342000))
;;; Generated autoloads from cider.el
(autoload 'cider-version "cider" "\
Display CIDER's version.
\(fn)" t nil)
(autoload 'cider-jack-in "cider" "\
Start an nREPL server for the current project and connect to it.
If PROMPT-PROJECT is t, then prompt for the project for which to
start the server.
If CLJS-TOO is non-nil, also start a ClojureScript REPL session with its
own buffer.
\(fn &optional PROMPT-PROJECT CLJS-TOO)" t nil)
(autoload 'cider-jack-in-clojurescript "cider" "\
Start an nREPL server and connect to it both Clojure and ClojureScript REPLs.
If PROMPT-PROJECT is t, then prompt for the project for which to
start the server.
\(fn &optional PROMPT-PROJECT)" t nil)
(autoload 'cider-connect "cider" "\
Connect to an nREPL server identified by HOST and PORT.
Create REPL buffer and start an nREPL client connection.
When the optional param PROJECT-DIR is present, the connection
gets associated with it.
\(fn HOST PORT &optional PROJECT-DIR)" t nil)
(eval-after-load 'clojure-mode '(progn (define-key clojure-mode-map (kbd "C-c M-j") #'cider-jack-in) (define-key clojure-mode-map (kbd "C-c M-J") #'cider-jack-in-clojurescript) (define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect)))
;;;***
;;;### (autoloads nil "cider-apropos" "cider-apropos.el" (22508 49905
;;;;;; 341980 175000))
;;; Generated autoloads from cider-apropos.el
(autoload 'cider-apropos "cider-apropos" "\
Show all symbols whose names match QUERY, a regular expression.
QUERY can also be a list of space-separated words (e.g. take while) which
will be converted to a regular expression (like take.+while) automatically
behind the scenes. The search may be limited to the namespace NS, and may
optionally search doc strings (based on DOCS-P), include private vars
\(based on PRIVATES-P), and be case-sensitive (based on CASE-SENSITIVE-P).
\(fn QUERY &optional NS DOCS-P PRIVATES-P CASE-SENSITIVE-P)" t nil)
(autoload 'cider-apropos-documentation "cider-apropos" "\
Shortcut for (cider-apropos <query> nil t).
\(fn)" t nil)
(autoload 'cider-apropos-select "cider-apropos" "\
Similar to `cider-apropos', but presents the results in a completing read.
Show all symbols whose names match QUERY, a regular expression.
QUERY can also be a list of space-separated words (e.g. take while) which
will be converted to a regular expression (like take.+while) automatically
behind the scenes. The search may be limited to the namespace NS, and may
optionally search doc strings (based on DOCS-P), include private vars
\(based on PRIVATES-P), and be case-sensitive (based on CASE-SENSITIVE-P).
\(fn QUERY &optional NS DOCS-P PRIVATES-P CASE-SENSITIVE-P)" t nil)
(autoload 'cider-apropos-documentation-select "cider-apropos" "\
Shortcut for (cider-apropos-select <query> nil t).
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-browse-ns" "cider-browse-ns.el" (22508
;;;;;; 49905 313980 117000))
;;; Generated autoloads from cider-browse-ns.el
(autoload 'cider-browse-ns "cider-browse-ns" "\
List all NAMESPACE's vars in BUFFER.
\(fn NAMESPACE)" t nil)
(autoload 'cider-browse-ns-all "cider-browse-ns" "\
List all loaded namespaces in BUFFER.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-classpath" "cider-classpath.el" (22508
;;;;;; 49905 433980 367000))
;;; Generated autoloads from cider-classpath.el
(autoload 'cider-classpath "cider-classpath" "\
List all classpath entries.
\(fn)" t nil)
(autoload 'cider-open-classpath-entry "cider-classpath" "\
Open a classpath entry.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-debug" "cider-debug.el" (22508 49905
;;;;;; 325980 141000))
;;; Generated autoloads from cider-debug.el
(autoload 'cider-debug-defun-at-point "cider-debug" "\
Instrument the \"top-level\" expression at point.
If it is a defn, dispatch the instrumented definition. Otherwise,
immediately evaluate the instrumented expression.
While debugged code is being evaluated, the user is taken through the
source code and displayed the value of various expressions. At each step,
a number of keys will be prompted to the user.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-grimoire" "cider-grimoire.el" (22508
;;;;;; 49905 377980 250000))
;;; Generated autoloads from cider-grimoire.el
(autoload 'cider-grimoire-web "cider-grimoire" "\
Open grimoire documentation in the default web browser.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates.
\(fn &optional ARG)" t nil)
(autoload 'cider-grimoire "cider-grimoire" "\
Open grimoire documentation in a popup buffer.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates.
\(fn &optional ARG)" t nil)
;;;***
;;;### (autoloads nil "cider-inspector" "cider-inspector.el" (22508
;;;;;; 49905 369980 234000))
;;; Generated autoloads from cider-inspector.el
(autoload 'cider-inspect-last-sexp "cider-inspector" "\
Inspect the result of the the expression preceding point.
\(fn)" t nil)
(autoload 'cider-inspect-defun-at-point "cider-inspector" "\
Inspect the result of the \"top-level\" expression at point.
\(fn)" t nil)
(autoload 'cider-inspect-last-result "cider-inspector" "\
Inspect the most recent eval result.
\(fn)" t nil)
(autoload 'cider-inspect "cider-inspector" "\
Inspect the result of the preceding sexp.
With a prefix argument ARG it inspects the result of the \"top-level\" form.
With a second prefix argument it prompts for an expression to eval and inspect.
\(fn &optional ARG)" t nil)
(autoload 'cider-inspect-expr "cider-inspector" "\
Evaluate EXPR in NS and inspect its value.
Interactively, EXPR is read from the minibuffer, and NS the
current buffer's namespace.
\(fn EXPR NS)" t nil)
(define-obsolete-function-alias 'cider-inspect-read-and-inspect 'cider-inspect-expr "0.13.0")
;;;***
;;;### (autoloads nil "cider-macroexpansion" "cider-macroexpansion.el"
;;;;;; (22508 49905 413980 326000))
;;; Generated autoloads from cider-macroexpansion.el
(autoload 'cider-macroexpand-1 "cider-macroexpansion" "\
Invoke \\=`macroexpand-1\\=` on the expression preceding point.
If invoked with a PREFIX argument, use \\=`macroexpand\\=` instead of
\\=`macroexpand-1\\=`.
\(fn &optional PREFIX)" t nil)
(autoload 'cider-macroexpand-all "cider-macroexpansion" "\
Invoke \\=`clojure.walk/macroexpand-all\\=` on the expression preceding point.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-mode" "cider-mode.el" (22508 49905 349980
;;;;;; 192000))
;;; Generated autoloads from cider-mode.el
(defvar cider-mode-line '(:eval (format " cider[%s]" (cider--modeline-info))) "\
Mode line lighter for `cider-mode'.
The value of this variable is a mode line template as in
`mode-line-format'. See Info Node `(elisp)Mode Line Format' for
details about mode line templates.
Customize this variable to change how `cider-mode' displays its
status in the mode line. The default value displays the current connection.
Set this variable to nil to disable the mode line
entirely.")
(custom-autoload 'cider-mode-line "cider-mode" t)
(eval-after-load 'clojure-mode '(easy-menu-define cider-clojure-mode-menu-open clojure-mode-map "Menu for Clojure mode.\n This is displayed in `clojure-mode' buffers, if `cider-mode' is not active." `("CIDER" :visible (not cider-mode) ["Start a REPL" cider-jack-in :help "Starts an nREPL server (with lein, boot, or maven) and connects a REPL to it."] ["Connect to a REPL" cider-connect :help "Connects to a REPL that's already running."] ["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript :help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL.\n Configure `cider-cljs-lein-repl' to change the ClojureScript REPL to use."] "--" ["View manual online" cider-view-manual])))
(autoload 'cider-mode "cider-mode" "\
Minor mode for REPL interaction from a Clojure buffer.
\\{cider-mode-map}
\(fn &optional ARG)" t nil)
;;;***
;;;### (autoloads nil "cider-scratch" "cider-scratch.el" (22508 49905
;;;;;; 357980 209000))
;;; Generated autoloads from cider-scratch.el
(autoload 'cider-scratch "cider-scratch" "\
Go to the scratch buffer named `cider-scratch-buffer-name'.
\(fn)" t nil)
;;;***
;;;### (autoloads nil "cider-selector" "cider-selector.el" (22508
;;;;;; 49905 425980 351000))
;;; Generated autoloads from cider-selector.el
(autoload 'cider-selector "cider-selector" "\
Select a new buffer by type, indicated by a single character.
The user is prompted for a single character indicating the method by
which to choose a new buffer. The `?' character describes then
available methods. OTHER-WINDOW provides an optional target.
See `def-cider-selector-method' for defining new methods.
\(fn &optional OTHER-WINDOW)" t nil)
;;;***
;;;### (autoloads nil "cider-test" "cider-test.el" (22508 49905 405980
;;;;;; 309000))
;;; Generated autoloads from cider-test.el
(defvar cider-auto-test-mode nil "\
Non-nil if Cider-Auto-Test mode is enabled.
See the `cider-auto-test-mode' command
for a description of this minor mode.
Setting this variable directly does not take effect;
either customize it (see the info node `Easy Customization')
or call the function `cider-auto-test-mode'.")
(custom-autoload 'cider-auto-test-mode "cider-test" nil)
(autoload 'cider-auto-test-mode "cider-test" "\
Toggle automatic testing of Clojure files.
When enabled this reruns tests every time a Clojure file is loaded.
Only runs tests corresponding to the loaded file's namespace and does
nothing if no tests are defined or if the file failed to load.
\(fn &optional ARG)" t nil)
;;;***
;;;### (autoloads nil "cider-util" "cider-util.el" (22508 49905 417980
;;;;;; 334000))
;;; Generated autoloads from cider-util.el
(autoload 'cider-view-manual "cider-util" "\
View the manual in your default browser.
\(fn)" t nil)
;;;***
;;;### (autoloads nil nil ("cider-client.el" "cider-common.el" "cider-compat.el"
;;;;;; "cider-doc.el" "cider-eldoc.el" "cider-interaction.el" "cider-overlays.el"
;;;;;; "cider-pkg.el" "cider-popup.el" "cider-repl.el" "cider-resolve.el"
;;;;;; "cider-stacktrace.el" "nrepl-client.el" "nrepl-dict.el")
;;;;;; (22508 49905 437980 375000))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:
;;; cider-autoloads.el ends here

View File

@ -1,219 +0,0 @@
;;; cider-browse-ns.el --- CIDER namespace browser
;; Copyright © 2014-2016 John Andrews, Bozhidar Batsov and CIDER contributors
;; Author: John Andrews <john.m.andrews@gmail.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; M-x cider-browse-ns
;;
;; Display a list of all vars in a namespace.
;; Pressing <enter> will take you to the cider-doc buffer for that var.
;; Pressing ^ will take you to a list of all namespaces (akin to `dired-mode').
;; M-x cider-browse-ns-all
;;
;; Explore Clojure namespaces by browsing a list of all namespaces.
;; Pressing <enter> expands into a list of that namespace's vars as if by
;; executing the command (cider-browse-ns "my.ns").
;;; Code:
(require 'cider-interaction)
(require 'cider-client)
(require 'cider-compat)
(require 'cider-util)
(require 'nrepl-dict)
(defconst cider-browse-ns-buffer "*cider-ns-browser*")
(push cider-browse-ns-buffer cider-ancillary-buffers)
(defvar-local cider-browse-ns-current-ns nil)
;; Mode Definition
(defvar cider-browse-ns-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map cider-popup-buffer-mode-map)
(define-key map "d" #'cider-browse-ns-doc-at-point)
(define-key map "s" #'cider-browse-ns-find-at-point)
(define-key map (kbd "RET") #'cider-browse-ns-operate-at-point)
(define-key map "^" #'cider-browse-ns-all)
(define-key map "n" #'next-line)
(define-key map "p" #'previous-line)
map))
(defvar cider-browse-ns-mouse-map
(let ((map (make-sparse-keymap)))
(define-key map [mouse-1] #'cider-browse-ns-handle-mouse)
map))
(define-derived-mode cider-browse-ns-mode special-mode "browse-ns"
"Major mode for browsing Clojure namespaces.
\\{cider-browse-ns-mode-map}"
(setq buffer-read-only t)
(setq-local electric-indent-chars nil)
(setq-local truncate-lines t)
(setq-local cider-browse-ns-current-ns nil))
(defun cider-browse-ns--text-face (var-meta)
"Return font-lock-face for a var.
VAR-META contains the metadata information used to decide a face.
Presence of \"arglists-str\" and \"macro\" indicates a macro form.
Only \"arglists-str\" indicates a function. Otherwise, its a variable.
If the NAMESPACE is not loaded in the REPL, assume TEXT is a fn."
(cond
((not var-meta) 'font-lock-function-name-face)
((and (nrepl-dict-contains var-meta "arglists")
(string= (nrepl-dict-get var-meta "macro") "true"))
'font-lock-keyword-face)
((nrepl-dict-contains var-meta "arglists") 'font-lock-function-name-face)
(t 'font-lock-variable-name-face)))
(defun cider-browse-ns--properties (var var-meta)
"Decorate VAR with a clickable keymap and a face.
VAR-META is used to decide a font-lock face."
(let ((face (cider-browse-ns--text-face var-meta)))
(propertize var
'font-lock-face face
'mouse-face 'highlight
'keymap cider-browse-ns-mouse-map)))
(defun cider-browse-ns--list (buffer title items &optional ns noerase)
"Reset contents of BUFFER.
Display TITLE at the top and ITEMS are indented underneath.
If NS is non-nil, it is added to each item as the
`cider-browse-ns-current-ns' text property. If NOERASE is non-nil, the
contents of the buffer are not reset before inserting TITLE and ITEMS."
(with-current-buffer buffer
(cider-browse-ns-mode)
(let ((inhibit-read-only t))
(unless noerase (erase-buffer))
(goto-char (point-max))
(insert (cider-propertize title 'ns) "\n")
(dolist (item items)
(insert (propertize (concat " " item "\n")
'cider-browse-ns-current-ns ns)))
(goto-char (point-min)))))
(defun cider-browse-ns--first-doc-line (doc)
"Return the first line of the given DOC string.
If the first line of the DOC string contains multiple sentences, only
the first sentence is returned. If the DOC string is nil, a Not documented
string is returned."
(if doc
(let* ((split-newline (split-string doc "\n"))
(first-line (car split-newline)))
(cond
((string-match "\\. " first-line) (substring first-line 0 (match-end 0)))
((= 1 (length split-newline)) first-line)
(t (concat first-line "..."))))
"Not documented."))
(defun cider-browse-ns--items (namespace)
"Return the items to show in the namespace browser of the given NAMESPACE.
Each item consists of a ns-var and the first line of its docstring."
(let* ((ns-vars-with-meta (cider-sync-request:ns-vars-with-meta namespace))
(propertized-ns-vars (nrepl-dict-map #'cider-browse-ns--properties ns-vars-with-meta)))
(mapcar (lambda (ns-var)
(let* ((doc (nrepl-dict-get-in ns-vars-with-meta (list ns-var "doc")))
;; to avoid (read nil)
;; it prompts the user for a Lisp expression
(doc (when doc (read doc)))
(first-doc-line (cider-browse-ns--first-doc-line doc)))
(concat ns-var " " (propertize first-doc-line 'font-lock-face 'font-lock-doc-face))))
propertized-ns-vars)))
;; Interactive Functions
;;;###autoload
(defun cider-browse-ns (namespace)
"List all NAMESPACE's vars in BUFFER."
(interactive (list (completing-read "Browse namespace: " (cider-sync-request:ns-list))))
(with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t)
(cider-browse-ns--list (current-buffer)
namespace
(cider-browse-ns--items namespace))
(setq-local cider-browse-ns-current-ns namespace)))
;;;###autoload
(defun cider-browse-ns-all ()
"List all loaded namespaces in BUFFER."
(interactive)
(with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t)
(let ((names (cider-sync-request:ns-list)))
(cider-browse-ns--list (current-buffer)
"All loaded namespaces"
(mapcar (lambda (name)
(cider-browse-ns--properties name nil))
names))
(setq-local cider-browse-ns-current-ns nil))))
(defun cider-browse-ns--thing-at-point ()
"Get the thing at point.
Return a list of the type ('ns or 'var) and the value."
(let ((line (car (split-string (cider-string-trim (thing-at-point 'line)) " "))))
(if (string-match "\\." line)
(list 'ns line)
(list 'var (format "%s/%s"
(or (get-text-property (point) 'cider-browse-ns-current-ns)
cider-browse-ns-current-ns)
line)))))
(defun cider-browse-ns-doc-at-point ()
"Show the documentation for the thing at current point."
(interactive)
(let* ((thing (cider-browse-ns--thing-at-point))
(value (cadr thing)))
;; value is either some ns or a var
(cider-doc-lookup value)))
(defun cider-browse-ns-operate-at-point ()
"Expand browser according to thing at current point.
If the thing at point is a ns it will be browsed,
and if the thing at point is some var - its documentation will
be displayed."
(interactive)
(let* ((thing (cider-browse-ns--thing-at-point))
(type (car thing))
(value (cadr thing)))
(if (eq type 'ns)
(cider-browse-ns value)
(cider-doc-lookup value))))
(defun cider-browse-ns-find-at-point ()
"Find the definition of the thing at point."
(interactive)
(let* ((thing (cider-browse-ns--thing-at-point))
(type (car thing))
(value (cadr thing)))
(if (eq type 'ns)
(cider-find-ns nil value)
(cider-find-var current-prefix-arg value))))
(defun cider-browse-ns-handle-mouse (event)
"Handle mouse click EVENT."
(interactive "e")
(cider-browse-ns-operate-at-point))
(provide 'cider-browse-ns)
;;; cider-browse-ns.el ends here

View File

@ -1,112 +0,0 @@
;;; cider-classpath.el --- Basic Java classpath browser
;; Copyright © 2014-2016 Bozhidar Batsov and CIDER contributors
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Basic Java classpath browser for CIDER.
;;; Code:
(require 'cider-client)
(require 'cider-popup)
(require 'cider-compat)
(defvar cider-classpath-buffer "*cider-classpath*")
(push cider-classpath-buffer cider-ancillary-buffers)
(defvar cider-classpath-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map cider-popup-buffer-mode-map)
(define-key map (kbd "RET") #'cider-classpath-operate-on-point)
(define-key map "n" #'next-line)
(define-key map "p" #'previous-line)
map))
(defvar cider-classpath-mouse-map
(let ((map (make-sparse-keymap)))
(define-key map [mouse-1] #'cider-classpath-handle-mouse)
map))
(define-derived-mode cider-classpath-mode special-mode "classpath"
"Major mode for browsing the entries in Java's classpath.
\\{cider-classpath-mode-map}"
(setq buffer-read-only t)
(setq-local electric-indent-chars nil)
(setq-local truncate-lines t))
(defun cider-classpath-list (buffer items)
"Populate BUFFER with ITEMS."
(with-current-buffer buffer
(cider-classpath-mode)
(let ((inhibit-read-only t))
(erase-buffer)
(dolist (item items)
(insert item "\n"))
(goto-char (point-min)))))
(defun cider-classpath-properties (text)
"Decorate TEXT with a clickable keymap and function face."
(let ((face (cond
((not (file-exists-p text)) 'font-lock-warning-face)
((file-directory-p text) 'dired-directory)
(t 'default))))
(propertize text
'font-lock-face face
'mouse-face 'highlight
'keymap cider-classpath-mouse-map)))
(defun cider-classpath-operate-on-point ()
"Expand browser according to thing at current point."
(interactive)
(let* ((bol (line-beginning-position))
(eol (line-end-position))
(line (buffer-substring-no-properties bol eol)))
(find-file-other-window line)))
(defun cider-classpath-handle-mouse (event)
"Handle mouse click EVENT."
(interactive "e")
(cider-classpath-operate-on-point))
;;;###autoload
(defun cider-classpath ()
"List all classpath entries."
(interactive)
(cider-ensure-connected)
(cider-ensure-op-supported "classpath")
(with-current-buffer (cider-popup-buffer cider-classpath-buffer t)
(cider-classpath-list (current-buffer)
(mapcar (lambda (name)
(cider-classpath-properties name))
(cider-sync-request:classpath)))))
;;;###autoload
(defun cider-open-classpath-entry ()
"Open a classpath entry."
(interactive)
(cider-ensure-connected)
(cider-ensure-op-supported "classpath")
(when-let ((entry (completing-read "Classpath entries: " (cider-sync-request:classpath))))
(find-file-other-window entry)))
(provide 'cider-classpath)
;;; cider-classpath.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +0,0 @@
;;; cider-common.el --- Common use functions -*- lexical-binding: t; -*-
;; Copyright © 2015-2016 Artur Malabarba
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Common functions that are useful in both Clojure buffers and REPL
;; buffers.
;;; Code:
(require 'cider-compat)
(require 'nrepl-dict)
(require 'cider-util)
(require 'tramp)
(defcustom cider-prompt-for-symbol t
"Controls when to prompt for symbol when a command requires one.
When non-nil, always prompt, and use the symbol at point as the default
value at the prompt.
When nil, attempt to use the symbol at point for the command, and only
prompt if that throws an error."
:type '(choice (const :tag "always" t)
(const :tag "dwim" nil))
:group 'cider
:package-version '(cider . "0.9.0"))
(defun cider--should-prompt-for-symbol (&optional invert)
"Return the value of the variable `cider-prompt-for-symbol'.
Optionally invert the value, if INVERT is truthy."
(if invert (not cider-prompt-for-symbol) cider-prompt-for-symbol))
(defun cider-prompt-for-symbol-function (&optional invert)
"Prompt for symbol if funcall `cider--should-prompt-for-symbol' is truthy.
Otherwise attempt to use the symbol at point for the command, and only
prompt if that throws an error.
INVERT is used to invert the semantics of the function `cider--should-prompt-for-symbol'."
(if (cider--should-prompt-for-symbol invert)
#'cider-read-symbol-name
#'cider-try-symbol-at-point))
(defun cider--kw-to-symbol (kw)
"Convert the keyword KW to a symbol."
(when kw
(replace-regexp-in-string "\\`:+" "" kw)))
(declare-function cider-read-from-minibuffer "cider-interaction")
(defun cider-read-symbol-name (prompt callback)
"Read a symbol name using PROMPT with a default of the one at point.
Use CALLBACK as the completing read var callback."
(funcall callback (cider-read-from-minibuffer
prompt
;; if the thing at point is a keyword we treat it as symbol
(cider--kw-to-symbol (cider-symbol-at-point 'look-back)))))
(defun cider-try-symbol-at-point (prompt callback)
"Call CALLBACK with symbol at point.
On failure, read a symbol name using PROMPT and call CALLBACK with that."
(condition-case nil (funcall callback (cider--kw-to-symbol (cider-symbol-at-point 'look-back)))
('error (funcall callback (cider-read-from-minibuffer prompt)))))
(declare-function cider-jump-to "cider-interaction")
(defun cider--find-buffer-for-file (file)
"Return a buffer visiting FILE.
If FILE is a temp buffer name, return that buffer."
(if (string-prefix-p "*" file)
file
(and file
(not (cider--tooling-file-p file))
(cider-find-file file))))
(defun cider--jump-to-loc-from-info (info &optional other-window)
"Jump to location give by INFO.
INFO object is returned by `cider-var-info' or `cider-member-info'.
OTHER-WINDOW is passed to `cider-jump-to'."
(let* ((line (nrepl-dict-get info "line"))
(file (nrepl-dict-get info "file"))
(name (nrepl-dict-get info "name"))
;; the filename might actually be a REPL buffer name
(buffer (cider--find-buffer-for-file file)))
(if buffer
(cider-jump-to buffer (if line (cons line nil) name) other-window)
(error "No source location"))))
(declare-function url-filename "url-parse" (cl-x) t)
(defun cider--url-to-file (url)
"Return the filename from the resource URL.
Uses `url-generic-parse-url' to parse the url. The filename is extracted and
then url decoded. If the decoded filename has a Windows device letter followed
by a colon immediately after the leading '/' then the leading '/' is dropped to
create a valid path."
(let ((filename (url-unhex-string (url-filename (url-generic-parse-url url)))))
(if (string-match "^/\\([a-zA-Z]:/.*\\)" filename)
(match-string 1 filename)
filename)))
(defun cider-tramp-prefix (&optional buffer)
"Use the filename for BUFFER to determine a tramp prefix.
Defaults to the current buffer.
Return the tramp prefix, or nil if BUFFER is local."
(let* ((buffer (or buffer (current-buffer)))
(name (or (buffer-file-name buffer)
(with-current-buffer buffer
default-directory))))
(when (tramp-tramp-file-p name)
(let ((vec (tramp-dissect-file-name name)))
(tramp-make-tramp-file-name (tramp-file-name-method vec)
(tramp-file-name-user vec)
(tramp-file-name-host vec)
nil)))))
(defun cider--client-tramp-filename (name &optional buffer)
"Return the tramp filename for path NAME relative to BUFFER.
If BUFFER has a tramp prefix, it will be added as a prefix to NAME.
If the resulting path is an existing tramp file, it returns the path,
otherwise, nil."
(let* ((buffer (or buffer (current-buffer)))
(name (concat (cider-tramp-prefix buffer) name)))
(if (tramp-handle-file-exists-p name)
name)))
(defun cider--server-filename (name)
"Return the nREPL server-relative filename for NAME."
(if (tramp-tramp-file-p name)
(with-parsed-tramp-file-name name nil
localname)
name))
(defvar cider-from-nrepl-filename-function
(with-no-warnings
(if (eq system-type 'cygwin)
#'cygwin-convert-file-name-from-windows
#'identity))
"Function to translate nREPL namestrings to Emacs filenames.")
(defcustom cider-prefer-local-resources nil
"Prefer local resources to remote (tramp) ones when both are available."
:type 'boolean
:group 'cider)
(defun cider--file-path (path)
"Return PATH's local or tramp path using `cider-prefer-local-resources'.
If no local or remote file exists, return nil."
(let* ((local-path (funcall cider-from-nrepl-filename-function path))
(tramp-path (and local-path (cider--client-tramp-filename local-path))))
(cond ((equal local-path "") "")
((and cider-prefer-local-resources (file-exists-p local-path))
local-path)
((and tramp-path (file-exists-p tramp-path))
tramp-path)
((and local-path (file-exists-p local-path))
local-path))))
(declare-function archive-extract "arc-mode")
(declare-function archive-zip-extract "arc-mode")
(defun cider-find-file (url)
"Return a buffer visiting the file URL if it exists, or nil otherwise.
If URL has a scheme prefix, it must represent a fully-qualified file path
or an entry within a zip/jar archive. If URL doesn't contain a scheme
prefix and is an absolute path, it is treated as such. Finally, if URL is
relative, it is expanded within each of the open Clojure buffers till an
existing file ending with URL has been found."
(require 'arc-mode)
(cond ((string-match "^file:\\(.+\\)" url)
(when-let ((file (cider--url-to-file (match-string 1 url)))
(path (cider--file-path file)))
(find-file-noselect path)))
((string-match "^\\(jar\\|zip\\):\\(file:.+\\)!/\\(.+\\)" url)
(when-let ((entry (match-string 3 url))
(file (cider--url-to-file (match-string 2 url)))
(path (cider--file-path file))
(name (format "%s:%s" path entry)))
(or (find-buffer-visiting name)
(if (tramp-tramp-file-p path)
(progn
;; Use emacs built in archiving
(find-file path)
(goto-char (point-min))
;; Make sure the file path is followed by a newline to
;; prevent eg. clj matching cljs.
(search-forward (concat entry "\n"))
;; moves up to matching line
(forward-line -1)
(archive-extract)
(current-buffer))
;; Use external zip program to just extract the single file
(with-current-buffer (generate-new-buffer
(file-name-nondirectory entry))
(archive-zip-extract path entry)
(set-visited-file-name name)
(setq-local default-directory (file-name-directory path))
(setq-local buffer-read-only t)
(set-buffer-modified-p nil)
(set-auto-mode)
(current-buffer))))))
(t (if-let ((path (cider--file-path url)))
(find-file-noselect path)
(unless (file-name-absolute-p url)
(let ((cider-buffers (cider-util--clojure-buffers))
(url (file-name-nondirectory url)))
(or (cl-loop for bf in cider-buffers
for path = (with-current-buffer bf
(expand-file-name url))
if (and path (file-exists-p path))
return (find-file-noselect path))
(cl-loop for bf in cider-buffers
if (string= (buffer-name bf) url)
return bf))))))))
(defun cider--open-other-window-p (arg)
"Test prefix value ARG to see if it indicates displaying results in other window."
(let ((narg (prefix-numeric-value arg)))
(pcase narg
(-1 t) ; -
(16 t) ; empty empty
(_ nil))))
(defun cider-abbreviate-ns (namespace)
"Return a string that abbreviates NAMESPACE."
(when namespace
(let* ((names (reverse (split-string namespace "\\.")))
(lastname (car names)))
(concat (mapconcat (lambda (s) (concat (substring s 0 1) "."))
(reverse (cdr names))
"")
lastname))))
(defun cider-last-ns-segment (namespace)
"Return the last segment of NAMESPACE."
(when namespace
(car (reverse (split-string namespace "\\.")))))
(provide 'cider-common)
;;; cider-common.el ends here

View File

@ -1,157 +0,0 @@
;;; cider-compat.el --- Functions from newer Emacs versions for compatibility -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Pretty much everything here's copied from subr-x for compatibility with
;; Emacs 24.3 and 24.4.
;;; Code:
(eval-and-compile
(unless (fboundp 'internal--thread-argument)
(defmacro internal--thread-argument (first? &rest forms)
"Internal implementation for `thread-first' and `thread-last'.
When Argument FIRST? is non-nil argument is threaded first, else
last. FORMS are the expressions to be threaded."
(pcase forms
(`(,x (,f . ,args) . ,rest)
`(internal--thread-argument
,first? ,(if first? `(,f ,x ,@args) `(,f ,@args ,x)) ,@rest))
(`(,x ,f . ,rest) `(internal--thread-argument ,first? (,f ,x) ,@rest))
(_ (car forms)))))
(unless (fboundp 'thread-first)
(defmacro thread-first (&rest forms)
"Thread FORMS elements as the first argument of their successor.
Example:
(thread-first
5
(+ 20)
(/ 25)
-
(+ 40))
Is equivalent to:
(+ (- (/ (+ 5 20) 25)) 40)
Note how the single `-' got converted into a list before
threading."
(declare (indent 1)
(debug (form &rest [&or symbolp (sexp &rest form)])))
`(internal--thread-argument t ,@forms)))
(unless (fboundp 'thread-last)
(defmacro thread-last (&rest forms)
"Thread FORMS elements as the last argument of their successor.
Example:
(thread-last
5
(+ 20)
(/ 25)
-
(+ 40))
Is equivalent to:
(+ 40 (- (/ 25 (+ 20 5))))
Note how the single `-' got converted into a list before
threading."
(declare (indent 1) (debug thread-first))
`(internal--thread-argument nil ,@forms))))
(eval-and-compile
(unless (fboundp 'internal--listify)
(defsubst internal--listify (elt)
"Wrap ELT in a list if it is not one."
(if (not (listp elt))
(list elt)
elt)))
(unless (fboundp 'internal--check-binding)
(defsubst internal--check-binding (binding)
"Check BINDING is properly formed."
(when (> (length binding) 2)
(signal
'error
(cons "`let' bindings can have only one value-form" binding)))
binding))
(unless (fboundp 'internal--build-binding-value-form)
(defsubst internal--build-binding-value-form (binding prev-var)
"Build the conditional value form for BINDING using PREV-VAR."
`(,(car binding) (and ,prev-var ,(cadr binding)))))
(unless (fboundp 'internal--build-binding)
(defun internal--build-binding (binding prev-var)
"Check and build a single BINDING with PREV-VAR."
(thread-first
binding
internal--listify
internal--check-binding
(internal--build-binding-value-form prev-var))))
(unless (fboundp 'internal--build-bindings)
(defun internal--build-bindings (bindings)
"Check and build conditional value forms for BINDINGS."
(let ((prev-var t))
(mapcar (lambda (binding)
(let ((binding (internal--build-binding binding prev-var)))
(setq prev-var (car binding))
binding))
bindings)))))
(eval-and-compile
(unless (fboundp 'if-let)
(defmacro if-let (bindings then &rest else)
"Process BINDINGS and if all values are non-nil eval THEN, else ELSE.
Argument BINDINGS is a list of tuples whose car is a symbol to be
bound and (optionally) used in THEN, and its cadr is a sexp to be
evalled to set symbol's value. In the special case you only want
to bind a single value, BINDINGS can just be a plain tuple."
(declare (indent 2)
(debug ([&or (&rest (symbolp form)) (symbolp form)] form body)))
(when (and (<= (length bindings) 2)
(not (listp (car bindings))))
;; Adjust the single binding case
(setq bindings (list bindings)))
`(let* ,(internal--build-bindings bindings)
(if ,(car (internal--listify (car (last bindings))))
,then
,@else))))
(unless (fboundp 'when-let)
(defmacro when-let (bindings &rest body)
"Process BINDINGS and if all values are non-nil eval BODY.
Argument BINDINGS is a list of tuples whose car is a symbol to be
bound and (optionally) used in BODY, and its cadr is a sexp to be
evalled to set symbol's value. In the special case you only want
to bind a single value, BINDINGS can just be a plain tuple."
(declare (indent 1) (debug if-let))
(list 'if-let bindings (macroexp-progn body)))))
(provide 'cider-compat)
;;; cider-compat.el ends here

View File

@ -1,752 +0,0 @@
;;; cider-debug.el --- CIDER interaction with the cider.debug nREPL middleware -*- lexical-binding: t; -*-
;; Copyright © 2015-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Instrument code with `cider-debug-defun-at-point', and when the code is
;; executed cider-debug will kick in. See this function's doc for more
;; information.
;;; Code:
(require 'nrepl-dict)
(require 'nrepl-client) ; `nrepl--mark-id-completed'
(require 'cider-interaction)
(require 'cider-client)
(require 'cider-util)
(require 'cider-inspector)
(require 'cider-browse-ns)
(require 'cider-common)
(require 'cider-compat)
(require 'seq)
(require 'spinner)
;;; Customization
(defgroup cider-debug nil
"Presentation and behaviour of the cider debugger."
:prefix "cider-debug-"
:group 'cider
:package-version '(cider . "0.10.0"))
(defface cider-debug-code-overlay-face
'((((class color) (background light)) :background "grey80")
(((class color) (background dark)) :background "grey30"))
"Face used to mark code being debugged."
:group 'cider-debug
:package-version '(cider . "0.9.1"))
(defface cider-debug-prompt-face
'((t :underline t :inherit font-lock-builtin-face))
"Face used to highlight keys in the debug prompt."
:group 'cider-debug
:package-version '(cider . "0.10.0"))
(defface cider-enlightened-face
'((((class color) (background light)) :inherit cider-result-overlay-face
:box (:color "darkorange" :line-width -1))
(((class color) (background dark)) :inherit cider-result-overlay-face
;; "#dd0" is a dimmer yellow.
:box (:color "#990" :line-width -1)))
"Face used to mark enlightened sexps and their return values."
:group 'cider-debug
:package-version '(cider . "0.11.0"))
(defface cider-enlightened-local-face
'((((class color) (background light)) :weight bold :foreground "darkorange")
(((class color) (background dark)) :weight bold :foreground "yellow"))
"Face used to mark enlightened locals (not their values)."
:group 'cider-debug
:package-version '(cider . "0.11.0"))
(defcustom cider-debug-prompt 'overlay
"If and where to show the keys while debugging.
If `minibuffer', show it in the minibuffer along with the return value.
If `overlay', show it in an overlay above the current function.
If t, do both.
If nil, don't list available keys at all."
:type '(choice (const :tag "Show in minibuffer" minibuffer)
(const :tag "Show above function" overlay)
(const :tag "Show in both places" t)
(const :tag "Don't list keys" nil))
:group 'cider-debug
:package-version '(cider . "0.10.0"))
(defcustom cider-debug-use-overlays t
"Whether to higlight debugging information with overlays.
Takes the same possible values as `cider-use-overlays', but only applies to
values displayed during debugging sessions.
To control the overlay that lists possible keys above the current function,
configure `cider-debug-prompt' instead."
:type '(choice (const :tag "End of line" t)
(const :tag "Bottom of screen" nil)
(const :tag "Both" both))
:group 'cider-debug
:package-version '(cider . "0.9.1"))
(defcustom cider-debug-print-level 10
"The print-level for values displayed by the debugger.
This variable must be set before starting the repl connection."
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max depth" 10))
:group 'cider-debug
:package-version '(cider . "0.10.0"))
(defcustom cider-debug-print-length 10
"The print-length for values displayed by the debugger.
This variable must be set before starting the repl connection."
:type '(choice (const :tag "No limit" nil)
(integer :tag "Max depth" 10))
:group 'cider-debug
:package-version '(cider . "0.10.0"))
;;; Implementation
(defun cider-browse-instrumented-defs ()
"List all instrumented definitions."
(interactive)
(if-let ((all (thread-first (cider-nrepl-send-sync-request (list "op" "debug-instrumented-defs"))
(nrepl-dict-get "list"))))
(with-current-buffer (cider-popup-buffer cider-browse-ns-buffer t)
(let ((inhibit-read-only t))
(erase-buffer)
(dolist (list all)
(let* ((ns (car list))
(ns-vars-with-meta (cider-sync-request:ns-vars-with-meta ns))
;; seq of metadata maps of the instrumented vars
(instrumented-meta (mapcar (apply-partially #'nrepl-dict-get ns-vars-with-meta)
(cdr list))))
(cider-browse-ns--list (current-buffer) ns
(seq-mapn #'cider-browse-ns--properties
(cdr list)
instrumented-meta)
ns 'noerase)
(goto-char (point-max))
(insert "\n"))))
(goto-char (point-min)))
(message "No currently instrumented definitions")))
(defun cider--debug-response-handler (response)
"Handles RESPONSE from the cider.debug middleware."
(nrepl-dbind-response response (status id causes)
(when (member "enlighten" status)
(cider--handle-enlighten response))
(when (or (member "eval-error" status)
(member "stack" status))
;; TODO: Make the error buffer a bit friendlier when we're just printing
;; the stack.
(cider--render-stacktrace-causes causes))
(when (member "need-debug-input" status)
(cider--handle-debug response))
(when (member "done" status)
(nrepl--mark-id-completed id))))
(defun cider--debug-init-connection ()
"Initialize a connection with the cider.debug middleware."
(cider-nrepl-send-request
(append '("op" "init-debugger")
(when cider-debug-print-level
(list "print-level" cider-debug-print-level))
(when cider-debug-print-length
(list "print-length" cider-debug-print-length)))
#'cider--debug-response-handler))
;;; Debugging overlays
(defconst cider--fringe-arrow-string
#("." 0 1 (display (left-fringe right-triangle)))
"Used as an overlay's before-string prop to place a fringe arrow.")
(defun cider--debug-display-result-overlay (value)
"Place an overlay at point displaying VALUE."
(when cider-debug-use-overlays
;; This is cosmetic, let's ensure it doesn't break the session no matter what.
(ignore-errors
;; Result
(cider--make-result-overlay (cider-font-lock-as-clojure value)
:where (point-marker)
:type 'debug-result
'before-string cider--fringe-arrow-string)
;; Code
(cider--make-overlay (save-excursion (clojure-backward-logical-sexp 1) (point))
(point) 'debug-code
'face 'cider-debug-code-overlay-face
;; Higher priority than `show-paren'.
'priority 2000))))
;;; Minor mode
(defvar-local cider--debug-mode-commands-dict nil
"An nrepl-dict from keys to debug commands.
Autogenerated by `cider--turn-on-debug-mode'.")
(defvar-local cider--debug-mode-response nil
"Response that triggered current debug session.
Set by `cider--turn-on-debug-mode'.")
(defcustom cider-debug-display-locals nil
"If non-nil, local variables are displayed while debugging.
Can be toggled at any time with `\\[cider-debug-toggle-locals]'."
:type 'boolean
:group 'cider-debug
:package-version '(cider . "0.10.0"))
(defun cider--debug-format-locals-list (locals)
"Return a string description of list LOCALS.
Each element of LOCALS should be a list of at least two elements."
(if locals
(let ((left-col-width
;; To right-indent the variable names.
(apply #'max (mapcar (lambda (l) (string-width (car l))) locals))))
;; A format string to build a format string. :-P
(mapconcat (lambda (l) (format (format " %%%ds: %%s\n" left-col-width)
(propertize (car l) 'face 'font-lock-variable-name-face)
(cider-font-lock-as-clojure (cadr l))))
locals ""))
""))
(defun cider--debug-prompt (command-dict)
"Return prompt to display for COMMAND-DICT."
;; Force `default' face, otherwise the overlay "inherits" the face of the text
;; after it.
(format (propertize "%s\n" 'face 'default)
(cider-string-join
(nrepl-dict-map (lambda (char cmd)
(when-let ((pos (cl-search char cmd)))
(put-text-property pos (1+ pos) 'face 'cider-debug-prompt-face cmd))
cmd)
command-dict)
" ")))
(defvar-local cider--debug-prompt-overlay nil)
(defun cider--debug-mode-redisplay ()
"Display the input prompt to the user."
(nrepl-dbind-response cider--debug-mode-response (debug-value input-type locals)
(when (or (eq cider-debug-prompt t)
(eq cider-debug-prompt 'overlay))
(if (overlayp cider--debug-prompt-overlay)
(overlay-put cider--debug-prompt-overlay
'before-string (cider--debug-prompt input-type))
(setq cider--debug-prompt-overlay
(cider--make-overlay
(max (car (cider-defun-at-point 'bounds))
(window-start))
nil 'debug-prompt
'before-string (cider--debug-prompt input-type)))))
(let* ((value (concat " " cider-eval-result-prefix
(cider-font-lock-as-clojure
(or debug-value "#unknown#"))))
(to-display
(concat (when cider-debug-display-locals
(cider--debug-format-locals-list locals))
(when (or (eq cider-debug-prompt t)
(eq cider-debug-prompt 'minibuffer))
(cider--debug-prompt input-type))
(when (or (not cider-debug-use-overlays)
(eq cider-debug-use-overlays 'both))
value))))
(if (> (string-width to-display) 0)
(message "%s" to-display)
;; If there's nothing to display in the minibuffer. Just send the value
;; to the Messages buffer.
(message "%s" value)
(message nil)))))
(defun cider-debug-toggle-locals ()
"Toggle display of local variables."
(interactive)
(setq cider-debug-display-locals (not cider-debug-display-locals))
(cider--debug-mode-redisplay))
(defun cider--debug-lexical-eval (key form &optional callback _point)
"Eval FORM in the lexical context of debug session given by KEY.
Do nothing if CALLBACK is provided.
Designed to be used as `cider-interactive-eval-override' and called instead
of `cider-interactive-eval' in debug sessions."
;; The debugger uses its own callback, so if the caller is passing a callback
;; we return nil and let `cider-interactive-eval' do its thing.
(unless callback
(cider-debug-mode-send-reply (format "{:response :eval, :code %s}" form)
key)
t))
(defvar cider--debug-mode-tool-bar-map
(let ((tool-bar-map (make-sparse-keymap)))
(tool-bar-add-item "right-arrow" #'cider-debug-mode-send-reply :next :label "Next step")
(tool-bar-add-item "next-node" #'cider-debug-mode-send-reply :continue :label "Continue non-stop")
(tool-bar-add-item "jump-to" #'cider-debug-mode-send-reply :out :label "Out of sexp")
(tool-bar-add-item "exit" #'cider-debug-mode-send-reply :quit :label "Quit")
tool-bar-map))
(defvar cider--debug-mode-map)
(define-minor-mode cider--debug-mode
"Mode active during debug sessions.
In order to work properly, this mode must be activated by
`cider--turn-on-debug-mode'."
nil " DEBUG" '()
(if cider--debug-mode
(if cider--debug-mode-response
(nrepl-dbind-response cider--debug-mode-response (input-type)
;; A debug session is an ongoing eval, but it's annoying to have the
;; spinner spinning while you debug.
(when spinner-current (spinner-stop))
(setq-local tool-bar-map cider--debug-mode-tool-bar-map)
(add-hook 'kill-buffer-hook #'cider--debug-quit nil 'local)
(add-hook 'before-revert-hook #'cider--debug-quit nil 'local)
(unless (consp input-type)
(error "Activated debug-mode on a message not asking for commands: %s" cider--debug-mode-response))
;; Integrate with eval commands.
(setq cider-interactive-eval-override
(apply-partially #'cider--debug-lexical-eval
(nrepl-dict-get cider--debug-mode-response "key")))
;; Set the keymap.
(nrepl-dict-map (lambda (char cmd)
(unless (string= char "h") ; `here' needs a special command.
(define-key cider--debug-mode-map char #'cider-debug-mode-send-reply))
(when (string= char "o")
(define-key cider--debug-mode-map (upcase char) #'cider-debug-mode-send-reply)))
input-type)
(setq cider--debug-mode-commands-dict input-type)
;; Show the prompt.
(cider--debug-mode-redisplay)
;; If a sync request is ongoing, the user can't act normally to
;; provide input, so we enter `recursive-edit'.
(when nrepl-ongoing-sync-request
(recursive-edit)))
(cider--debug-mode -1)
(if (called-interactively-p 'any)
(user-error (substitute-command-keys "Don't call this mode manually, use `\\[universal-argument] \\[cider-eval-defun-at-point]' instead"))
(error "Attempt to activate `cider--debug-mode' without setting `cider--debug-mode-response' first")))
(setq cider-interactive-eval-override nil)
(setq cider--debug-mode-commands-dict nil)
(setq cider--debug-mode-response nil)
;; We wait a moment before clearing overlays and the read-onlyness, so that
;; cider-nrepl has a chance to send the next message, and so that the user
;; doesn't accidentally hit `n' between two messages (thus editing the code).
(when-let ((proc (unless nrepl-ongoing-sync-request
(get-buffer-process (cider-current-connection)))))
(accept-process-output proc 1))
(unless cider--debug-mode
(setq buffer-read-only nil)
(cider--debug-remove-overlays (current-buffer)))
(when nrepl-ongoing-sync-request
(ignore-errors (exit-recursive-edit)))))
;;; Bind the `:here` command to both h and H, because it behaves differently if
;;; invoked with an uppercase letter.
(define-key cider--debug-mode-map "h" #'cider-debug-move-here)
(define-key cider--debug-mode-map "H" #'cider-debug-move-here)
(defun cider--debug-remove-overlays (&optional buffer)
"Remove CIDER debug overlays from BUFFER if variable `cider--debug-mode' is nil."
(when (or (not buffer) (buffer-live-p buffer))
(with-current-buffer (or buffer (current-buffer))
(unless cider--debug-mode
(kill-local-variable 'tool-bar-map)
(remove-overlays nil nil 'category 'debug-result)
(remove-overlays nil nil 'category 'debug-code)
(setq cider--debug-prompt-overlay nil)
(remove-overlays nil nil 'category 'debug-prompt)))))
(defun cider--debug-set-prompt (value)
"Set `cider-debug-prompt' to VALUE, then redisplay."
(setq cider-debug-prompt value)
(cider--debug-mode-redisplay))
(easy-menu-define cider-debug-mode-menu cider--debug-mode-map
"Menu for CIDER debug mode"
`("CIDER Debugger"
["Next step" (cider-debug-mode-send-reply ":next") :keys "n"]
["Continue non-stop" (cider-debug-mode-send-reply ":continue") :keys "c"]
["Move out of sexp" (cider-debug-mode-send-reply ":out") :keys "o"]
["Quit" (cider-debug-mode-send-reply ":quit") :keys "q"]
"--"
["Evaluate in current scope" (cider-debug-mode-send-reply ":eval") :keys "e"]
["Inject value" (cider-debug-mode-send-reply ":inject") :keys "i"]
["Inspect value" (cider-debug-mode-send-reply ":inspect")]
["Inspect local variables" (cider-debug-mode-send-reply ":locals") :keys "l"]
"--"
("Configure keys prompt"
["Don't show keys" (cider--debug-set-prompt nil) :style toggle :selected (eq cider-debug-prompt nil)]
["Show in minibuffer" (cider--debug-set-prompt 'minibuffer) :style toggle :selected (eq cider-debug-prompt 'minibuffer)]
["Show above function" (cider--debug-set-prompt 'overlay) :style toggle :selected (eq cider-debug-prompt 'overlay)]
["Show in both places" (cider--debug-set-prompt t) :style toggle :selected (eq cider-debug-prompt t)]
"--"
["List locals" cider-debug-toggle-locals :style toggle :selected cider-debug-display-locals])
["Customize" (customize-group 'cider-debug)]))
(defun cider--uppercase-command-p ()
"Return true if the last command was uppercase letter."
(ignore-errors
(let ((case-fold-search nil))
(string-match "[[:upper:]]" (string last-command-event)))))
(defun cider-debug-mode-send-reply (command &optional key force)
"Reply to the message that started current bufer's debugging session.
COMMAND is sent as the input option. KEY can be provided to reply to a
specific message. If FORCE is non-nil, send a \"force?\" argument in the
message."
(interactive (list
(if (symbolp last-command-event)
(symbol-name last-command-event)
(ignore-errors
(concat ":" (nrepl-dict-get cider--debug-mode-commands-dict
(downcase (string last-command-event))))))
nil
(cider--uppercase-command-p)))
(when (and (string-prefix-p ":" command) force)
(setq command (format "{:response %s :force? true}" command)))
(cider-nrepl-send-unhandled-request
(list "op" "debug-input" "input" (or command ":quit")
"key" (or key (nrepl-dict-get cider--debug-mode-response "key"))))
(ignore-errors (cider--debug-mode -1)))
(defun cider--debug-quit ()
"Send a :quit reply to the debugger. Used in hooks."
(when cider--debug-mode
(cider-debug-mode-send-reply ":quit")
(message "Quitting debug session")))
;;; Movement logic
(defconst cider--debug-buffer-format "*cider-debug %s*")
(defun cider--debug-trim-code (code)
"Remove whitespace and reader macros from the start of the CODE.
Return trimmed CODE."
(replace-regexp-in-string "\\`#[a-z]+[\n\r[:blank:]]*" "" code))
(declare-function cider-set-buffer-ns "cider-mode")
(defun cider--initialize-debug-buffer (code ns id &optional reason)
"Create a new debugging buffer with CODE and namespace NS.
ID is the id of the message that instrumented CODE.
REASON is a keyword describing why this buffer was necessary."
(let ((buffer-name (format cider--debug-buffer-format id)))
(if-let ((buffer (get-buffer buffer-name)))
(cider-popup-buffer-display buffer 'select)
(with-current-buffer (cider-popup-buffer buffer-name 'select
#'clojure-mode 'ancillary)
(cider-set-buffer-ns ns)
(setq buffer-undo-list nil)
(let ((inhibit-read-only t)
(buffer-undo-list t))
(erase-buffer)
(insert (format "%s" (cider--debug-trim-code code)))
(when code
(insert "\n\n\n;; We had to create this temporary buffer because we couldn't find the original definition. That probably happened because "
reason
".")
(fill-paragraph))
(cider--font-lock-ensure)
(set-buffer-modified-p nil))))
(switch-to-buffer buffer-name)
(goto-char (point-min))))
(defun cider--debug-goto-keyval (key)
"Find KEY in current sexp or return nil."
(when-let ((limit (ignore-errors (save-excursion (up-list) (point)))))
(search-forward-regexp (concat "\\_<" (regexp-quote key) "\\_>")
limit 'noerror)))
(defun cider--debug-move-point (coordinates)
"Place point on after the sexp specified by COORDINATES.
COORDINATES is a list of integers that specify how to navigate into the
sexp that is after point when this function is called.
As an example, a COORDINATES list of '(1 0 2) means:
- enter next sexp then `forward-sexp' once,
- enter next sexp,
- enter next sexp then `forward-sexp' twice.
In the following snippet, this takes us to the (* x 2) sexp (point is left
at the end of the given sexp).
(letfn [(twice [x]
(* x 2))]
(twice 15))
In addition to numbers, a coordinate can be a string. This string names the
key of a map, and it means \"go to the value associated with this key\"."
(condition-case-unless-debug nil
;; Navigate through sexps inside the sexp.
(let ((in-syntax-quote nil))
(while coordinates
(while (clojure--looking-at-non-logical-sexp)
(forward-sexp))
;; An `@x` is read as (deref x), so we pop coordinates once to account
;; for the extra depth, and move past the @ char.
(if (eq ?@ (char-after))
(progn (forward-char 1)
(pop coordinates))
(down-list)
;; Are we entering a syntax-quote?
(when (looking-back "`\\(#{\\|[{[(]\\)" (line-beginning-position))
;; If we are, this affects all nested structures until the next `~',
;; so we set this variable for all following steps in the loop.
(setq in-syntax-quote t))
(when in-syntax-quote
;; A `(. .) is read as (seq (concat (list .) (list .))). This pops
;; the `seq', since the real coordinates are inside the `concat'.
(pop coordinates)
;; Non-list seqs like `[] and `{} are read with
;; an extra (apply vector ...), so pop it too.
(unless (eq ?\( (char-before))
(pop coordinates)))
;; #(...) is read as (fn* ([] ...)), so we patch that here.
(when (looking-back "#(" (line-beginning-position))
(pop coordinates))
(if coordinates
(let ((next (pop coordinates)))
(when in-syntax-quote
;; We're inside the `concat' form, but we need to discard the
;; actual `concat' symbol from the coordinate.
(setq next (1- next)))
;; String coordinates are map keys.
(if (stringp next)
(cider--debug-goto-keyval next)
(clojure-forward-logical-sexp next)
(when in-syntax-quote
(clojure-forward-logical-sexp 1)
(forward-sexp -1)
;; Here a syntax-quote is ending.
(let ((match (when (looking-at "~@?")
(match-string 0))))
(when match
(setq in-syntax-quote nil))
;; A `~@' is read as the object itself, so we don't pop
;; anything.
(unless (equal "~@" match)
;; Anything else (including a `~') is read as a `list'
;; form inside the `concat', so we need to pop the list
;; from the coordinates.
(pop coordinates))))))
;; If that extra pop was the last coordinate, this represents the
;; entire #(...), so we should move back out.
(backward-up-list))))
;; Place point at the end of instrumented sexp.
(clojure-forward-logical-sexp 1))
;; Avoid throwing actual errors, since this happens on every breakpoint.
(error (message "Can't find instrumented sexp, did you edit the source?"))))
(defun cider--debug-position-for-code (code)
"Return non-nil if point is roughly before CODE.
This might move point one line above."
(or (looking-at-p (regexp-quote code))
(let ((trimmed (regexp-quote (cider--debug-trim-code code))))
(or (looking-at-p trimmed)
;; If this is a fake #dbg injected by `C-u
;; C-M-x', then the sexp we want is actually on
;; the line above.
(progn (forward-line -1)
(looking-at-p trimmed))))))
(defun cider--debug-find-source-position (response &optional create-if-needed)
"Return a marker of the position after the sexp specified in RESPONSE.
This marker might be in a different buffer! If the sexp can't be
found (file that contains the code is no longer visited or has been
edited), return nil. However, if CREATE-IF-NEEDED is non-nil, a new buffer
is created in this situation and the return value is never nil.
Follow the \"line\" and \"column\" entries in RESPONSE, and check whether
the code at point matches the \"code\" entry in RESPONSE. If it doesn't,
assume that the code in this file has been edited, and create a temp buffer
holding the original code.
Either way, navigate inside the code by following the \"coor\" entry which
is a coordinate measure in sexps."
(nrepl-dbind-response response (code file line column ns original-id coor)
(when (or code (and file line column))
;; This is for restoring current-buffer.
(save-excursion
(let ((out))
;; We prefer in-source debugging.
(when-let ((buf (and file line column
(ignore-errors
(cider--find-buffer-for-file file)))))
;; The logic here makes it hard to use `with-current-buffer'.
(with-current-buffer buf
;; This is for restoring point inside buf.
(save-excursion
;; Get to the proper line & column in the file
(forward-line (- line (line-number-at-pos)))
(move-to-column column)
;; Check if it worked
(when (cider--debug-position-for-code code)
;; Find the desired sexp.
(cider--debug-move-point coor)
(setq out (point-marker))))))
;; But we can create a temp buffer if that fails.
(or out
(when create-if-needed
(cider--initialize-debug-buffer
code ns original-id
(if (and line column)
"you edited the code"
"your tools.nrepl version is older than 0.2.11"))
(save-excursion
(cider--debug-move-point coor)
(point-marker)))))))))
(defun cider--handle-debug (response)
"Handle debugging notification.
RESPONSE is a message received from the nrepl describing the input
needed. It is expected to contain at least \"key\", \"input-type\", and
\"prompt\", and possibly other entries depending on the input-type."
(nrepl-dbind-response response (debug-value key input-type prompt inspect)
(condition-case-unless-debug e
(progn
(pcase input-type
("expression" (cider-debug-mode-send-reply
(condition-case nil
(cider-read-from-minibuffer
(or prompt "Expression: "))
(quit "nil"))
key))
((pred sequencep)
(let* ((marker (cider--debug-find-source-position response 'create-if-needed)))
(pop-to-buffer (marker-buffer marker))
(goto-char marker))
;; The overlay code relies on window boundaries, but point could have been
;; moved outside the window by some other code. Redisplay here to ensure the
;; visible window includes point.
(redisplay)
;; Remove overlays AFTER redisplaying! Otherwise there's a visible
;; flicker even if we immediately recreate the overlays.
(cider--debug-remove-overlays)
(when cider-debug-use-overlays
(cider--debug-display-result-overlay debug-value))
(setq cider--debug-mode-response response)
(cider--debug-mode 1)))
(when inspect
(cider-inspector--render-value inspect)))
;; If something goes wrong, we send a "quit" or the session hangs.
(error (cider-debug-mode-send-reply ":quit" key)
(message "Error encountered while handling the debug message: %S" e)))))
(defun cider--handle-enlighten (response)
"Handle an enlighten notification.
RESPONSE is a message received from the nrepl describing the value and
coordinates of a sexp. Create an overlay after the specified sexp
displaying its value."
(when-let ((marker (cider--debug-find-source-position response)))
(with-current-buffer (marker-buffer marker)
(save-excursion
(goto-char marker)
(clojure-backward-logical-sexp 1)
(nrepl-dbind-response response (debug-value erase-previous)
(when erase-previous
(remove-overlays (point) marker 'category 'enlighten))
(when debug-value
(if (memq (char-before marker) '(?\) ?\] ?}))
;; Enlightening a sexp looks like a regular return value, except
;; for a different border.
(cider--make-result-overlay (cider-font-lock-as-clojure debug-value)
:where (cons marker marker)
:type 'enlighten
:prepend-face 'cider-enlightened-face)
;; Enlightening a symbol uses a more abbreviated format. The
;; result face is the same as a regular result, but we also color
;; the symbol with `cider-enlightened-local-face'.
(cider--make-result-overlay (cider-font-lock-as-clojure debug-value)
:format "%s"
:where (cons (point) marker)
:type 'enlighten
'face 'cider-enlightened-local-face))))))))
;;; Move here command
;; This is the inverse of `cider--debug-move-point'. However, that algorithm is
;; complicated, and trying to code its inverse would probably be insane.
;; Instead, we find the coordinate by trial and error.
(defun cider--debug-find-coordinates-for-point (target &optional list-so-far)
"Return the coordinates list for reaching TARGET.
Assumes that the next thing after point is a logical Clojure sexp and that
TARGET is inside it. The returned list is suitable for use in
`cider--debug-move-point'. LIST-SO-FAR is for internal use."
(when (looking-at (rx (or "(" "[" "#{" "{")))
(let ((starting-point (point)))
(unwind-protect
(let ((x 0))
;; Keep incrementing the last coordinate until we've moved
;; past TARGET.
(while (condition-case nil
(progn (goto-char starting-point)
(cider--debug-move-point (append list-so-far (list x)))
(< (point) target))
;; Not a valid coordinate. Move back a step and stop here.
(scan-error (setq x (1- x))
nil))
(setq x (1+ x)))
(setq list-so-far (append list-so-far (list x)))
;; We have moved past TARGET, now determine whether we should
;; stop, or if target is deeper inside the previous sexp.
(if (or (= target (point))
(progn (forward-sexp -1)
(<= target (point))))
list-so-far
(goto-char starting-point)
(cider--debug-find-coordinates-for-point target list-so-far)))
;; `unwind-protect' clause.
(goto-char starting-point)))))
(defun cider-debug-move-here (&optional force)
"Skip any breakpoints up to point."
(interactive (list (cider--uppercase-command-p)))
(unless cider--debug-mode
(user-error "`cider-debug-move-here' only makes sense during a debug session"))
(let ((here (point)))
(nrepl-dbind-response cider--debug-mode-response (line column)
(if (and line column (buffer-file-name))
(progn ;; Get to the proper line & column in the file
(forward-line (1- (- line (line-number-at-pos))))
(move-to-column column))
(beginning-of-defun))
;; Is HERE inside the sexp being debugged?
(when (or (< here (point))
(save-excursion
(forward-sexp 1)
(> here (point))))
(user-error "Point is outside the sexp being debugged"))
;; Move forward untill start of sexp.
(comment-normalize-vars)
(comment-forward (point-max))
;; Find the coordinate and send it.
(cider-debug-mode-send-reply
(format "{:response :here, :coord %s :force? %s}"
(cider--debug-find-coordinates-for-point here)
(if force "true" "false"))))))
;;; User commands
;;;###autoload
(defun cider-debug-defun-at-point ()
"Instrument the \"top-level\" expression at point.
If it is a defn, dispatch the instrumented definition. Otherwise,
immediately evaluate the instrumented expression.
While debugged code is being evaluated, the user is taken through the
source code and displayed the value of various expressions. At each step,
a number of keys will be prompted to the user."
(interactive)
(cider-eval-defun-at-point 'debug-it))
(provide 'cider-debug)
;;; cider-debug.el ends here

View File

@ -1,522 +0,0 @@
;;; cider-doc.el --- CIDER documentation functionality -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Bozhidar Batsov, Jeff Valk and CIDER contributors
;; Author: Jeff Valk <jv@jeffvalk.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Mode for formatting and presenting documentation
;;; Code:
(require 'cider-common)
(require 'cider-compat)
(require 'cider-util)
(require 'cider-popup)
(require 'cider-client)
(require 'cider-grimoire)
(require 'nrepl-dict)
(require 'org-table)
(require 'button)
(require 'easymenu)
;;; Variables
(defgroup cider-doc nil
"Documentation for CIDER."
:prefix "cider-doc-"
:group 'cider)
(defvar cider-doc-map
(let (cider-doc-map)
(define-prefix-command 'cider-doc-map)
(define-key cider-doc-map (kbd "a") #'cider-apropos)
(define-key cider-doc-map (kbd "C-a") #'cider-apropos)
(define-key cider-doc-map (kbd "s") #'cider-apropos-select)
(define-key cider-doc-map (kbd "C-s") #'cider-apropos-select)
(define-key cider-doc-map (kbd "f") #'cider-apropos-documentation)
(define-key cider-doc-map (kbd "C-f") #'cider-apropos-documentation)
(define-key cider-doc-map (kbd "e") #'cider-apropos-documentation-select)
(define-key cider-doc-map (kbd "C-e") #'cider-apropos-documentation-select)
(define-key cider-doc-map (kbd "d") #'cider-doc)
(define-key cider-doc-map (kbd "C-d") #'cider-doc)
(define-key cider-doc-map (kbd "r") #'cider-grimoire)
(define-key cider-doc-map (kbd "C-r") #'cider-grimoire)
(define-key cider-doc-map (kbd "w") #'cider-grimoire-web)
(define-key cider-doc-map (kbd "C-w") #'cider-grimoire-web)
(define-key cider-doc-map (kbd "j") #'cider-javadoc)
(define-key cider-doc-map (kbd "C-j") #'cider-javadoc)
cider-doc-map)
"CIDER documentation keymap.")
(defconst cider-doc-menu
'("Documentation"
["CiderDoc" cider-doc]
["JavaDoc in browser" cider-javadoc]
["Grimoire" cider-grimoire]
["Grimoire in browser" cider-grimoire-web]
["Search symbols" cider-apropos]
["Search symbols & select" cider-apropos-select]
["Search documentation" cider-apropos-documentation]
["Search documentation & select" cider-apropos-documentation-select]
"--"
["Configure Doc buffer" (customize-group 'cider-docview-mode)])
"CIDER documentation submenu.")
;;; cider-docview-mode
(defgroup cider-docview-mode nil
"Formatting/fontifying documentation viewer."
:prefix "cider-docview-"
:group 'cider)
(defcustom cider-docview-fill-column fill-column
"Fill column for docstrings in doc buffer."
:type 'list
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
;; Faces
(defface cider-docview-emphasis-face
'((t (:inherit default :underline t)))
"Face for emphasized text"
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
(defface cider-docview-strong-face
'((t (:inherit default :underline t :weight bold)))
"Face for strongly emphasized text"
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
(defface cider-docview-literal-face
'((t (:inherit font-lock-string-face)))
"Face for literal text"
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
(defface cider-docview-table-border-face
'((t (:inherit shadow)))
"Face for table borders"
:group 'cider-docview-mode
:package-version '(cider . "0.7.0"))
;; Colors & Theme Support
(defvar cider-docview-code-background-color
(cider-scale-background-color)
"Background color for code blocks.")
(defadvice enable-theme (after cider-docview-adapt-to-theme activate)
"When theme is changed, update `cider-docview-code-background-color'."
(setq cider-docview-code-background-color (cider-scale-background-color)))
;; Mode & key bindings
(defvar cider-docview-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "q" #'cider-popup-buffer-quit-function)
(define-key map "g" #'cider-docview-grimoire)
(define-key map "G" #'cider-docview-grimoire-web)
(define-key map "j" #'cider-docview-javadoc)
(define-key map "s" #'cider-docview-source)
(define-key map (kbd "<backtab>") #'backward-button)
(define-key map (kbd "TAB") #'forward-button)
(easy-menu-define cider-docview-mode-menu map
"Menu for CIDER's doc mode"
`("CiderDoc"
["Look up in Grimoire" cider-docview-grimoire]
["Look up in Grimoire (browser)" cider-docview-grimoire-web]
["JavaDoc in browser" cider-docview-javadoc]
["Jump to source" cider-docview-source]
"--"
["Quit" cider-popup-buffer-quit-function]
))
map))
(defvar cider-docview-symbol)
(defvar cider-docview-javadoc-url)
(defvar cider-docview-file)
(defvar cider-docview-line)
(define-derived-mode cider-docview-mode help-mode "Doc"
"Major mode for displaying CIDER documentation
\\{cider-docview-mode-map}"
(setq buffer-read-only t)
(setq-local truncate-lines t)
(setq-local electric-indent-chars nil)
(setq-local cider-docview-symbol nil)
(setq-local cider-docview-javadoc-url nil)
(setq-local cider-docview-file nil)
(setq-local cider-docview-line nil))
;;; Interactive functions
(defun cider-docview-javadoc ()
"Open the Javadoc for the current class, if available."
(interactive)
(if cider-docview-javadoc-url
(browse-url cider-docview-javadoc-url)
(error "No Javadoc available for %s" cider-docview-symbol)))
(defun cider-javadoc-handler (symbol-name)
"Invoke the nREPL \"info\" op on SYMBOL-NAME if available."
(when symbol-name
(let* ((info (cider-var-info symbol-name))
(url (nrepl-dict-get info "javadoc")))
(if url
(browse-url url)
(user-error "No Javadoc available for %s" symbol-name)))))
(defun cider-javadoc (arg)
"Open Javadoc documentation in a popup buffer.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates."
(interactive "P")
(cider-ensure-connected)
(cider-ensure-op-supported "info")
(funcall (cider-prompt-for-symbol-function arg)
"Javadoc for"
#'cider-javadoc-handler))
(declare-function cider-find-file "cider-common")
(declare-function cider-jump-to "cider-interaction")
(defun cider-docview-source ()
"Open the source for the current symbol, if available."
(interactive)
(if cider-docview-file
(if-let ((buffer (and (not (cider--tooling-file-p cider-docview-file))
(cider-find-file cider-docview-file))))
(cider-jump-to buffer (if cider-docview-line
(cons cider-docview-line nil)
cider-docview-symbol)
nil)
(user-error
(substitute-command-keys
"Can't find the source because it wasn't defined with `cider-eval-buffer'")))
(error "No source location for %s" cider-docview-symbol)))
(defvar cider-buffer-ns)
(declare-function cider-grimoire-lookup "cider-grimoire")
(defun cider-docview-grimoire ()
"Return the grimoire documentation for `cider-docview-symbol'."
(interactive)
(if cider-buffer-ns
(cider-grimoire-lookup cider-docview-symbol)
(error "%s cannot be looked up on Grimoire" cider-docview-symbol)))
(declare-function cider-grimoire-web-lookup "cider-grimoire")
(defun cider-docview-grimoire-web ()
"Open the grimoire documentation for `cider-docview-symbol' in a web browser."
(interactive)
(if cider-buffer-ns
(cider-grimoire-web-lookup cider-docview-symbol)
(error "%s cannot be looked up on Grimoire" cider-docview-symbol)))
(defconst cider-doc-buffer "*cider-doc*")
(add-to-list 'cider-ancillary-buffers cider-doc-buffer)
(defun cider-create-doc-buffer (symbol)
"Populates *cider-doc* with the documentation for SYMBOL."
(when-let ((info (cider-var-info symbol)))
(cider-docview-render (cider-make-popup-buffer cider-doc-buffer) symbol info)))
(defun cider-doc-lookup (symbol)
"Look up documentation for SYMBOL."
(if-let ((buffer (cider-create-doc-buffer symbol)))
(cider-popup-buffer-display buffer t)
(user-error "Symbol %s not resolved" symbol)))
(defun cider-doc (&optional arg)
"Open Clojure documentation in a popup buffer.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates."
(interactive "P")
(cider-ensure-connected)
(funcall (cider-prompt-for-symbol-function arg)
"Doc for"
#'cider-doc-lookup))
;;; Font Lock and Formatting
(defun cider-docview-fontify-code-blocks (buffer mode)
"Font lock BUFFER code blocks using MODE and remove markdown characters.
This processes the triple backtick GFM markdown extension. An overlay is used
to shade the background. Blocks are marked to be ignored by other fonification
and line wrap."
(with-current-buffer buffer
(save-excursion
(while (search-forward-regexp "```\n" nil t)
(replace-match "")
(let ((beg (point))
(bg `(:background ,cider-docview-code-background-color)))
(when (search-forward-regexp "```\n" nil t)
(replace-match "")
(cider-font-lock-region-as mode beg (point))
(overlay-put (make-overlay beg (point)) 'font-lock-face bg)
(put-text-property beg (point) 'block 'code)))))))
(defun cider-docview-fontify-literals (buffer)
"Font lock BUFFER literal text and remove backtick markdown characters.
Preformatted code text blocks are ignored."
(with-current-buffer buffer
(save-excursion
(while (search-forward "`" nil t)
(if (eq (get-text-property (point) 'block) 'code)
(forward-char)
(progn
(replace-match "")
(let ((beg (point)))
(when (search-forward "`" (line-end-position) t)
(replace-match "")
(put-text-property beg (point) 'font-lock-face 'cider-docview-literal-face)))))))))
(defun cider-docview-fontify-emphasis (buffer)
"Font lock BUFFER emphasized text and remove markdown characters.
One '*' represents emphasis, multiple '**'s represent strong emphasis.
Preformatted code text blocks are ignored."
(with-current-buffer buffer
(save-excursion
(while (search-forward-regexp "\\(*+\\)\\(\\w\\)" nil t)
(if (eq (get-text-property (point) 'block) 'code)
(forward-char)
(progn
(replace-match "\\2")
(let ((beg (1- (point)))
(face (if (> (length (match-string 1)) 1)
'cider-docview-strong-face
'cider-docview-emphasis-face)))
(when (search-forward-regexp "\\(\\w\\)\\*+" (line-end-position) t)
(replace-match "\\1")
(put-text-property beg (point) 'font-lock-face face)))))))))
(defun cider-docview-format-tables (buffer)
"Align BUFFER tables and dim borders.
This processes the GFM table markdown extension using `org-table'.
Tables are marked to be ignored by line wrap."
(with-current-buffer buffer
(save-excursion
(let ((border 'cider-docview-table-border-face))
(org-table-map-tables
(lambda ()
(org-table-align)
(goto-char (org-table-begin))
(while (search-forward-regexp "[+|-]" (org-table-end) t)
(put-text-property (match-beginning 0) (match-end 0) 'font-lock-face border))
(put-text-property (org-table-begin) (org-table-end) 'block 'table)))))))
(defun cider-docview-wrap-text (buffer)
"For text in BUFFER not propertized as 'block', apply line wrap."
(with-current-buffer buffer
(save-excursion
(while (not (eobp))
(unless (get-text-property (point) 'block)
(fill-region (point) (line-end-position)))
(forward-line)))))
;;; Rendering
(defun cider-docview-render-java-doc (buffer text)
"Emit into BUFFER formatted doc TEXT for a Java class or member."
(with-current-buffer buffer
(let ((beg (point)))
(insert text)
(save-excursion
(goto-char beg)
(cider-docview-fontify-code-blocks buffer 'java-mode) ; left alone hereafter
(cider-docview-fontify-literals buffer)
(cider-docview-fontify-emphasis buffer)
(cider-docview-format-tables buffer) ; may contain literals, emphasis
(cider-docview-wrap-text buffer))))) ; ignores code, table blocks
(defun cider--abbreviate-file-protocol (file-with-protocol)
"Abbreviate the file-path in `file:/path/to/file'."
(if (string-match "\\`file:\\(.*\\)" file-with-protocol)
(let ((file (match-string 1 file-with-protocol))
(proj-dir (clojure-project-dir)))
(if (and proj-dir
(file-in-directory-p file proj-dir))
(file-relative-name file proj-dir)
file))
file-with-protocol))
(defun cider-docview-render-info (buffer info)
"Emit into BUFFER formatted INFO for the Clojure or Java symbol."
(let* ((ns (nrepl-dict-get info "ns"))
(name (nrepl-dict-get info "name"))
(added (nrepl-dict-get info "added"))
(depr (nrepl-dict-get info "deprecated"))
(macro (nrepl-dict-get info "macro"))
(special (nrepl-dict-get info "special-form"))
(forms (nrepl-dict-get info "forms-str"))
(args (nrepl-dict-get info "arglists-str"))
(doc (or (nrepl-dict-get info "doc")
"Not documented."))
(url (nrepl-dict-get info "url"))
(class (nrepl-dict-get info "class"))
(member (nrepl-dict-get info "member"))
(javadoc (nrepl-dict-get info "javadoc"))
(super (nrepl-dict-get info "super"))
(ifaces (nrepl-dict-get info "interfaces"))
(spec (nrepl-dict-get info "spec"))
(clj-name (if ns (concat ns "/" name) name))
(java-name (if member (concat class "/" member) class))
(see-also (nrepl-dict-get info "see-also")))
(cider--help-setup-xref (list #'cider-doc-lookup (format "%s/%s" ns name)) nil buffer)
(with-current-buffer buffer
(cl-flet ((emit (text &optional face)
(insert (if face
(propertize text 'font-lock-face face)
text)
"\n")))
(emit (if class java-name clj-name) 'font-lock-function-name-face)
(when super
(emit (concat " Extends: " (cider-font-lock-as 'java-mode super))))
(when ifaces
(emit (concat "Implements: " (cider-font-lock-as 'java-mode (car ifaces))))
(dolist (iface (cdr ifaces))
(emit (concat " "(cider-font-lock-as 'java-mode iface)))))
(when (or super ifaces)
(insert "\n"))
(when (or forms args)
(insert " ")
(save-excursion
(emit (cider-font-lock-as-clojure
;; All `defn's use ([...] [...]), but some special forms use
;; (...). We only remove the parentheses on the former.
(replace-regexp-in-string "\\`(\\(\\[.*\\]\\))\\'" "\\1"
(or forms args)))))
;; It normally doesn't happen, but it's technically conceivable for
;; the args string to contain unbalanced sexps, so `ignore-errors'.
(ignore-errors
(forward-sexp 1)
(while (not (looking-at "$"))
(insert "\n")
(forward-sexp 1)))
(forward-line 1))
(when (or special macro)
(emit (if special "Special Form" "Macro") 'font-lock-variable-name-face))
(when added
(emit (concat "Added in " added) 'font-lock-comment-face))
(when depr
(emit (concat "Deprecated in " depr) 'font-lock-keyword-face))
(if class
(cider-docview-render-java-doc (current-buffer) doc)
(emit (concat " " doc)))
(when url
(insert "\n Please see ")
(insert-text-button url
'url url
'follow-link t
'action (lambda (x)
(browse-url (button-get x 'url))))
(insert "\n"))
(when javadoc
(insert "\n\nFor additional documentation, see the ")
(insert-text-button "Javadoc"
'url javadoc
'follow-link t
'action (lambda (x)
(browse-url (button-get x 'url))))
(insert ".\n"))
(insert "\n")
(when spec
(emit "Spec: " 'font-lock-function-name-face)
(mapc (lambda (s) (insert s "\n")) spec)
(insert "\n"))
(if cider-docview-file
(progn
(insert (propertize (if class java-name clj-name)
'font-lock-face 'font-lock-function-name-face)
" is defined in ")
(insert-text-button (cider--abbreviate-file-protocol cider-docview-file)
'follow-link t
'action (lambda (_x)
(cider-docview-source)))
(insert "."))
(insert "Definition location unavailable."))
(when see-also
(insert "\n\n Also see: ")
(mapc (lambda (ns-sym)
(let* ((ns-sym-split (split-string ns-sym "/"))
(see-also-ns (car ns-sym-split))
(see-also-sym (cadr ns-sym-split))
;; if the var belongs to the same namespace,
;; we omit the namespace to save some screen space
(symbol (if (equal ns see-also-ns) see-also-sym ns-sym)))
(insert-button symbol
'type 'help-xref
'help-function (apply-partially #'cider-doc-lookup symbol)))
(insert " "))
see-also))
(cider--doc-make-xrefs)
(let ((beg (point-min))
(end (point-max)))
(nrepl-dict-map (lambda (k v)
(put-text-property beg end k v))
info)))
(current-buffer))))
(declare-function cider-set-buffer-ns "cider-mode")
(defun cider-docview-render (buffer symbol info)
"Emit into BUFFER formatted documentation for SYMBOL's INFO."
(with-current-buffer buffer
(let ((javadoc (nrepl-dict-get info "javadoc"))
(file (nrepl-dict-get info "file"))
(line (nrepl-dict-get info "line"))
(ns (nrepl-dict-get info "ns"))
(inhibit-read-only t))
(cider-docview-mode)
(cider-set-buffer-ns ns)
(setq-local cider-docview-symbol symbol)
(setq-local cider-docview-javadoc-url javadoc)
(setq-local cider-docview-file file)
(setq-local cider-docview-line line)
(remove-overlays)
(cider-docview-render-info buffer info)
(goto-char (point-min))
(current-buffer))))
(provide 'cider-doc)
;;; cider-doc.el ends here

View File

@ -1,430 +0,0 @@
;;; cider-eldoc.el --- eldoc support for Clojure -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; eldoc support for Clojure.
;;; Code:
(require 'cider-client)
(require 'cider-common) ; for cider-symbol-at-point
(require 'cider-compat)
(require 'cider-util)
(require 'nrepl-dict)
(require 'seq)
(require 'eldoc)
(defvar cider-extra-eldoc-commands '("yas-expand")
"Extra commands to be added to eldoc's safe commands list.")
(defvar cider-eldoc-max-num-sexps-to-skip 30
"The maximum number of sexps to skip while searching the beginning of current sexp.")
(defvar-local cider-eldoc-last-symbol nil
"The eldoc information for the last symbol we checked.")
(defcustom cider-eldoc-ns-function #'identity
"A function that returns a ns string to be used by eldoc.
Takes one argument, a namespace name.
For convenience, some functions are already provided for this purpose:
`cider-abbreviate-ns', and `cider-last-ns-segment'."
:type '(choice (const :tag "Full namespace" identity)
(const :tag "Abbreviated namespace" cider-abbreviate-ns)
(const :tag "Last name in namespace" cider-last-ns-segment)
(function :tag "Custom function"))
:group 'cider
:package-version '(cider . "0.13.0"))
(defcustom cider-eldoc-max-class-names-to-display 3
"The maximum number of classes to display in an eldoc string.
An eldoc string for Java interop forms can have a number of classes prefixed to
it, when the form belongs to more than 1 class. When, not nil we only display
the names of first `cider-eldoc-max-class-names-to-display' classes and add
a \"& x more\" suffix. Otherwise, all the classes are displayed."
:type 'integer
:safe #'integerp
:group 'cider
:package-version '(cider . "0.13.0"))
(defcustom cider-eldoc-display-for-symbol-at-point t
"When non-nil, display eldoc for symbol at point if available.
So in (map inc ...) when the cursor is over inc its eldoc would be
displayed. When nil, always display eldoc for first symbol of the sexp."
:type 'boolean
:safe 'booleanp
:group 'cider
:package-version '(cider . "0.13.0"))
(defun cider--eldoc-format-class-names (class-names)
"Return a formatted CLASS-NAMES prefix string.
CLASS-NAMES is a list of classes to which a Java interop form belongs.
Only keep the first `cider-eldoc-max-class-names-to-display' names, and
add a \"& x more\" suffix. Return nil if the CLASS-NAMES list is empty or
mapping `cider-eldoc-ns-function' on it returns an empty list."
(when-let ((eldoc-class-names (seq-remove #'null (mapcar (apply-partially cider-eldoc-ns-function) class-names)))
(eldoc-class-names-length (length eldoc-class-names)))
(cond
;; truncate class-names list and then format it
((and cider-eldoc-max-class-names-to-display
(> eldoc-class-names-length cider-eldoc-max-class-names-to-display))
(format "(%s & %s more)"
(thread-first eldoc-class-names
(seq-take cider-eldoc-max-class-names-to-display)
(cider-string-join " ")
(cider-propertize 'ns))
(- eldoc-class-names-length cider-eldoc-max-class-names-to-display)))
;; format the whole list but add surrounding parentheses
((> eldoc-class-names-length 1)
(format "(%s)"
(thread-first eldoc-class-names
(cider-string-join " ")
(cider-propertize 'ns))))
;; don't add the parentheses
(t (format "%s" (car eldoc-class-names))))))
(defun cider-eldoc-format-thing (ns symbol thing type)
"Format the eldoc subject defined by NS, SYMBOL and THING.
THING represents the thing at point which triggered eldoc. Normally NS and
SYMBOL are used (they are derived from THING), but when empty we fallback to
THING (e.g. for Java methods). Format it as a function, if FUNCTION-P
is non-nil. Else format it as a variable."
(if-let ((method-name (if (and symbol (not (string= symbol "")))
symbol
thing))
(propertized-method-name (cider-propertize method-name type))
(ns-or-class (if (and ns (stringp ns))
(funcall cider-eldoc-ns-function ns)
(cider--eldoc-format-class-names ns))))
(format "%s/%s"
;; we set font-lock properties of classes in `cider--eldoc-format-class-names'
;; to avoid font locking the parentheses and "& x more"
;; so we only propertize ns-or-class if not already done
(if (get-text-property 1 'face ns-or-class)
;; it is already propertized
ns-or-class
(cider-propertize ns-or-class 'ns))
propertized-method-name)
;; in case ns-or-class is nil
propertized-method-name))
(defun cider-eldoc-format-sym-doc (var ns docstring)
"Return the formatted eldoc string for VAR and DOCSTRING.
Consider the value of `eldoc-echo-area-use-multiline-p' while formatting.
If the entire line cannot fit in the echo area, the var name may be
truncated or eliminated entirely from the output to make room for the
description.
Try to truncate the var with various strategies, so that the var and
the docstring can be displayed in the minibuffer without resizing the window.
We start with `cider-abbreviate-ns' and `cider-last-ns-segment'.
Next, if the var is in current namespace, we remove NS from the eldoc string.
Otherwise, only the docstring is returned."
(let* ((ea-multi eldoc-echo-area-use-multiline-p)
;; Subtract 1 from window width since emacs will not write
;; any chars to the last column, or in later versions, will
;; cause a wraparound and resize of the echo area.
(ea-width (1- (window-width (minibuffer-window))))
(strip (- (+ (length var) (length docstring)) ea-width))
(newline (string-match-p "\n" docstring))
;; Truncated var can be ea-var long
;; Subtract 2 to account for the : and / added when including
;; the namespace prefixed form in eldoc string
(ea-var (- (- ea-width (length docstring)) 2)))
(cond
((or (eq ea-multi t)
(and (<= strip 0) (null newline))
(and ea-multi (or (> (length docstring) ea-width) newline)))
(format "%s: %s" var docstring))
;; Now we have to truncate either the docstring or the var
(newline (cider-eldoc-format-sym-doc var ns (substring docstring 0 newline)))
;; Only return the truncated docstring
((> (length docstring) ea-width)
(substring docstring 0 ea-width))
;; Try to truncate the var with cider-abbreviate-ns
((<= (length (cider-abbreviate-ns var)) ea-var)
(format "%s: %s" (cider-abbreviate-ns var) docstring))
;; Try to truncate var with cider-last-ns-segment
((<= (length (cider-last-ns-segment var)) ea-var)
(format "%s: %s" (cider-last-ns-segment var) docstring))
;; If the var is in current namespace, we try to truncate the var by
;; skipping the namespace from the returned eldoc string
((and (string-equal ns (cider-current-ns))
(<= (- (length var) (length ns)) ea-var))
(format "%s: %s"
(replace-regexp-in-string (format "%s/" ns) "" var)
docstring))
;; We couldn't fit the var and docstring in the available space,
;; so we just display the docstring
(t docstring))))
(defun cider-eldoc-format-variable (thing pos eldoc-info)
"Return the formatted eldoc string for a variable.
THING is the variable name. POS will always be 0 here.
ELDOC-INFO is a p-list containing the eldoc information."
(let* ((ns (lax-plist-get eldoc-info "ns"))
(symbol (lax-plist-get eldoc-info "symbol"))
(docstring (lax-plist-get eldoc-info "docstring"))
(formatted-var (cider-eldoc-format-thing ns symbol thing 'var)))
(when docstring
(cider-eldoc-format-sym-doc formatted-var ns docstring))))
(defun cider-eldoc-format-function (thing pos eldoc-info)
"Return the formatted eldoc string for a function.
THING is the function name. POS is the argument-index of the functions
arglists. ELDOC-INFO is a p-list containing the eldoc information."
(let ((ns (lax-plist-get eldoc-info "ns"))
(symbol (lax-plist-get eldoc-info "symbol"))
(arglists (lax-plist-get eldoc-info "arglists")))
(format "%s: %s"
(cider-eldoc-format-thing ns symbol thing 'fn)
(cider-eldoc-format-arglist arglists pos))))
(defun cider-highlight-args (arglist pos)
"Format the the function ARGLIST for eldoc.
POS is the index of the currently highlighted argument."
(let* ((rest-pos (cider--find-rest-args-position arglist))
(i 0))
(mapconcat
(lambda (arg)
(let ((argstr (format "%s" arg)))
(if (string= arg "&")
argstr
(prog1
(if (or (= (1+ i) pos)
(and rest-pos
(> (1+ i) rest-pos)
(> pos rest-pos)))
(propertize argstr 'face
'eldoc-highlight-function-argument)
argstr)
(setq i (1+ i)))))) arglist " ")))
(defun cider--find-rest-args-position (arglist)
"Find the position of & in the ARGLIST vector."
(seq-position arglist "&"))
(defun cider-highlight-arglist (arglist pos)
"Format the ARGLIST for eldoc.
POS is the index of the argument to highlight."
(concat "[" (cider-highlight-args arglist pos) "]"))
(defun cider-eldoc-format-arglist (arglist pos)
"Format all the ARGLIST for eldoc.
POS is the index of current argument."
(concat "("
(mapconcat (lambda (args) (cider-highlight-arglist args pos))
arglist
" ")
")"))
(defun cider-eldoc-beginning-of-sexp ()
"Move to the beginning of current sexp.
Return the number of nested sexp the point was over or after. Return nil
if the maximum number of sexps to skip is exceeded."
(let ((parse-sexp-ignore-comments t)
(num-skipped-sexps 0))
(condition-case _
(progn
;; First account for the case the point is directly over a
;; beginning of a nested sexp.
(condition-case _
(let ((p (point)))
(forward-sexp -1)
(forward-sexp 1)
(when (< (point) p)
(setq num-skipped-sexps 1)))
(error))
(while
(let ((p (point)))
(forward-sexp -1)
(when (< (point) p)
(setq num-skipped-sexps
(unless (and cider-eldoc-max-num-sexps-to-skip
(>= num-skipped-sexps
cider-eldoc-max-num-sexps-to-skip))
;; Without the above guard,
;; `cider-eldoc-beginning-of-sexp' could traverse the
;; whole buffer when the point is not within a
;; list. This behavior is problematic especially with
;; a buffer containing a large number of
;; non-expressions like a REPL buffer.
(1+ num-skipped-sexps)))))))
(error))
num-skipped-sexps))
(defun cider-eldoc-thing-type (eldoc-info)
"Return the type of the thing being displayed by eldoc.
It can be a function or var now."
(pcase (lax-plist-get eldoc-info "type")
("function" 'fn)
("variable" 'var)))
(defun cider-eldoc-info-at-point ()
"Return eldoc info at point.
First go to the beginning of the sexp and check if the eldoc is to be
considered (i.e sexp is a method call) and not a map or vector literal.
Then go back to the point and return its eldoc."
(save-excursion
(unless (cider-in-comment-p)
(let* ((current-point (point)))
(cider-eldoc-beginning-of-sexp)
(unless (member (or (char-before (point)) 0) '(?\" ?\{ ?\[))
(goto-char current-point)
(when-let (eldoc-info (cider-eldoc-info
(cider--eldoc-remove-dot (cider-symbol-at-point))))
(list "eldoc-info" eldoc-info
"thing" (cider-symbol-at-point)
"pos" 0)))))))
(defun cider-eldoc-info-at-sexp-beginning ()
"Return eldoc info for first symbol in the sexp."
(save-excursion
(when-let ((beginning-of-sexp (cider-eldoc-beginning-of-sexp))
;; If we are at the beginning of function name, this will be -1
(argument-index (max 0 (1- beginning-of-sexp))))
(unless (or (memq (or (char-before (point)) 0)
'(?\" ?\{ ?\[))
(cider-in-comment-p))
(when-let (eldoc-info (cider-eldoc-info
(cider--eldoc-remove-dot (cider-symbol-at-point))))
(list "eldoc-info" eldoc-info
"thing" (cider-symbol-at-point)
"pos" argument-index))))))
(defun cider-eldoc-info-in-current-sexp ()
"Return eldoc information from the sexp.
If `cider-eldoc-display-for-symbol-at-poin' is non-nil and
the symbol at point has a valid eldoc available, return that.
Otherwise return the eldoc of the first symbol of the sexp."
(or (when cider-eldoc-display-for-symbol-at-point
(cider-eldoc-info-at-point))
(cider-eldoc-info-at-sexp-beginning)))
(defun cider-eldoc--convert-ns-keywords (thing)
"Convert THING values that match ns macro keywords to function names."
(pcase thing
(":import" "clojure.core/import")
(":refer-clojure" "clojure.core/refer-clojure")
(":use" "clojure.core/use")
(":refer" "clojure.core/refer")
(_ thing)))
(defun cider-eldoc-info (thing)
"Return the info for THING.
This includes the arglist and ns and symbol name (if available)."
(let ((thing (cider-eldoc--convert-ns-keywords thing)))
(when (and (cider-nrepl-op-supported-p "eldoc")
thing
;; ignore empty strings
(not (string= thing ""))
;; ignore strings
(not (string-prefix-p "\"" thing))
;; ignore regular expressions
(not (string-prefix-p "#" thing))
;; ignore chars
(not (string-prefix-p "\\" thing))
;; ignore numbers
(not (string-match-p "^[0-9]" thing)))
;; check if we can used the cached eldoc info
(cond
;; handle keywords for map access
((string-prefix-p ":" thing) (list "symbol" thing
"type" "function"
"arglists" '(("map") ("map" "not-found"))))
;; handle Classname. by displaying the eldoc for new
((string-match-p "^[A-Z].+\\.$" thing) (list "symbol" thing
"type" "function"
"arglists" '(("args*"))))
;; generic case
(t (if (equal thing (car cider-eldoc-last-symbol))
(cadr cider-eldoc-last-symbol)
(when-let ((eldoc-info (cider-sync-request:eldoc thing)))
(let* ((arglists (nrepl-dict-get eldoc-info "eldoc"))
(docstring (nrepl-dict-get eldoc-info "docstring"))
(type (nrepl-dict-get eldoc-info "type"))
(ns (nrepl-dict-get eldoc-info "ns"))
(class (nrepl-dict-get eldoc-info "class"))
(name (nrepl-dict-get eldoc-info "name"))
(member (nrepl-dict-get eldoc-info "member"))
(ns-or-class (if (and ns (not (string= ns "")))
ns
class))
(name-or-member (if (and name (not (string= name "")))
name
(format ".%s" member)))
(eldoc-plist (list "ns" ns-or-class
"symbol" name-or-member
"arglists" arglists
"docstring" docstring
"type" type)))
;; middleware eldoc lookups are expensive, so we
;; cache the last lookup. This eliminates the need
;; for extra middleware requests within the same sexp.
(setq cider-eldoc-last-symbol (list thing eldoc-plist))
eldoc-plist))))))))
(defun cider--eldoc-remove-dot (sym)
"Remove the preceding \".\" from a namespace qualified SYM and return sym.
Only useful for interop forms. Clojure forms would be returned unchanged."
(when sym (replace-regexp-in-string "/\\." "/" sym)))
(defun cider-eldoc ()
"Backend function for eldoc to show argument list in the echo area."
(when (and (cider-connected-p)
;; don't clobber an error message in the minibuffer
(not (member last-command '(next-error previous-error))))
(let* ((sexp-eldoc-info (cider-eldoc-info-in-current-sexp))
(eldoc-info (lax-plist-get sexp-eldoc-info "eldoc-info"))
(pos (lax-plist-get sexp-eldoc-info "pos"))
(thing (lax-plist-get sexp-eldoc-info "thing")))
(when eldoc-info
(if (equal (cider-eldoc-thing-type eldoc-info) 'fn)
(cider-eldoc-format-function thing pos eldoc-info)
(cider-eldoc-format-variable thing pos eldoc-info))))))
(defun cider-eldoc-setup ()
"Setup eldoc in the current buffer.
eldoc mode has to be enabled for this to have any effect."
(setq-local eldoc-documentation-function #'cider-eldoc)
(apply #'eldoc-add-command cider-extra-eldoc-commands))
(provide 'cider-eldoc)
;;; cider-eldoc.el ends here

View File

@ -1,118 +0,0 @@
;;; cider-grimoire.el --- Grimoire integration -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Bozhidar Batsov and CIDER contributors
;;
;; Author: Bozhidar Batsov <bozhidar@batsov.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; A few commands for Grimoire documentation lookup.
;;; Code:
(require 'cider-client)
(require 'cider-common)
(require 'cider-compat)
(require 'cider-popup)
(require 'nrepl-dict)
(require 'url-vars)
(defconst cider-grimoire-url "http://conj.io/")
(defconst cider-grimoire-buffer "*cider-grimoire*")
(defun cider-grimoire-replace-special (name)
"Convert the dashes in NAME to a grimoire friendly format."
(thread-last name
(replace-regexp-in-string "\\?" "_QMARK_")
(replace-regexp-in-string "\\." "_DOT_")
(replace-regexp-in-string "\\/" "_SLASH_")
(replace-regexp-in-string "\\(\\`_\\)\\|\\(_\\'\\)" "")))
(defun cider-grimoire-url (name ns)
"Generate a grimoire search v0 url from NAME, NS."
(let ((base-url cider-grimoire-url))
(when (and name ns)
(concat base-url "search/v0/" ns "/" (cider-grimoire-replace-special name) "/"))))
(defun cider-grimoire-web-lookup (symbol)
"Open the grimoire documentation for SYMBOL in a web browser."
(if-let ((var-info (cider-var-info symbol)))
(let ((name (nrepl-dict-get var-info "name"))
(ns (nrepl-dict-get var-info "ns")))
(browse-url (cider-grimoire-url name ns)))
(error "Symbol %s not resolved" symbol)))
;;;###autoload
(defun cider-grimoire-web (&optional arg)
"Open grimoire documentation in the default web browser.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates."
(interactive "P")
(funcall (cider-prompt-for-symbol-function arg)
"Grimoire doc for"
#'cider-grimoire-web-lookup))
(defun cider-create-grimoire-buffer (content)
"Create a new grimoire buffer with CONTENT."
(with-current-buffer (cider-popup-buffer cider-grimoire-buffer t)
(read-only-mode -1)
(insert content)
(read-only-mode +1)
(goto-char (point-min))
(current-buffer)))
(defun cider-grimoire-lookup (symbol)
"Look up the grimoire documentation for SYMBOL."
(if-let ((var-info (cider-var-info symbol)))
(let ((name (nrepl-dict-get var-info "name"))
(ns (nrepl-dict-get var-info "ns"))
(url-request-method "GET")
(url-request-extra-headers `(("Content-Type" . "text/plain"))))
(url-retrieve (cider-grimoire-url name ns)
(lambda (_status)
;; we need to strip the http header
(goto-char (point-min))
(re-search-forward "^$")
(delete-region (point-min) (point))
(delete-blank-lines)
;; and create a new buffer with whatever is left
(pop-to-buffer (cider-create-grimoire-buffer (buffer-string))))))
(error "Symbol %s not resolved" symbol)))
;;;###autoload
(defun cider-grimoire (&optional arg)
"Open grimoire documentation in a popup buffer.
Prompts for the symbol to use, or uses the symbol at point, depending on
the value of `cider-prompt-for-symbol'. With prefix arg ARG, does the
opposite of what that option dictates."
(interactive "P")
(when (derived-mode-p 'clojurescript-mode)
(user-error "`cider-grimoire' doesn't support ClojureScript"))
(funcall (cider-prompt-for-symbol-function arg)
"Grimoire doc for"
#'cider-grimoire-lookup))
(provide 'cider-grimoire)
;;; cider-grimoire.el ends here

View File

@ -1,389 +0,0 @@
;;; cider-inspector.el --- Object inspector -*- lexical-binding: t -*-
;; Copyright © 2013-2016 Vital Reactor, LLC
;; Copyright © 2014-2016 Bozhidar Batsov and CIDER contributors
;; Author: Ian Eslick <ian@vitalreactor.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Clojure object inspector inspired by SLIME.
;;; Code:
(require 'cl-lib)
(require 'seq)
(require 'cider-interaction)
;; ===================================
;; Inspector Key Map and Derived Mode
;; ===================================
(defconst cider-inspector-buffer "*cider-inspect*")
(push cider-inspector-buffer cider-ancillary-buffers)
;;; Customization
(defgroup cider-inspector nil
"Presentation and behaviour of the cider value inspector."
:prefix "cider-inspector-"
:group 'cider
:package-version '(cider . "0.10.0"))
(defcustom cider-inspector-page-size 32
"Default page size in paginated inspector view.
The page size can be also changed interactively within the inspector."
:type '(integer :tag "Page size" 32)
:group 'cider-inspector
:package-version '(cider . "0.10.0"))
(defvar cider-inspector-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map cider-popup-buffer-mode-map)
(define-key map (kbd "RET") #'cider-inspector-operate-on-point)
(define-key map [mouse-1] #'cider-inspector-operate-on-click)
(define-key map "l" #'cider-inspector-pop)
(define-key map "g" #'cider-inspector-refresh)
;; Page-up/down
(define-key map [next] #'cider-inspector-next-page)
(define-key map [prior] #'cider-inspector-prev-page)
(define-key map " " #'cider-inspector-next-page)
(define-key map (kbd "M-SPC") #'cider-inspector-prev-page)
(define-key map (kbd "S-SPC") #'cider-inspector-prev-page)
(define-key map "s" #'cider-inspector-set-page-size)
(define-key map [tab] #'cider-inspector-next-inspectable-object)
(define-key map "\C-i" #'cider-inspector-next-inspectable-object)
(define-key map [(shift tab)] #'cider-inspector-previous-inspectable-object)
;; Emacs translates S-TAB to BACKTAB on X.
(define-key map [backtab] #'cider-inspector-previous-inspectable-object)
map))
(define-derived-mode cider-inspector-mode special-mode "Inspector"
"Major mode for inspecting Clojure data structures.
\\{cider-inspector-mode-map}"
(set-syntax-table clojure-mode-syntax-table)
(setq buffer-read-only t)
(setq-local electric-indent-chars nil)
(setq-local truncate-lines t))
;;;###autoload
(defun cider-inspect-last-sexp ()
"Inspect the result of the the expression preceding point."
(interactive)
(cider-inspect-expr (cider-last-sexp) (cider-current-ns)))
;;;###autoload
(defun cider-inspect-defun-at-point ()
"Inspect the result of the \"top-level\" expression at point."
(interactive)
(cider-inspect-expr (cider-defun-at-point) (cider-current-ns)))
;;;###autoload
(defun cider-inspect-last-result ()
"Inspect the most recent eval result."
(interactive)
(cider-inspect-expr "*1" (cider-current-ns)))
;;;###autoload
(defun cider-inspect (&optional arg)
"Inspect the result of the preceding sexp.
With a prefix argument ARG it inspects the result of the \"top-level\" form.
With a second prefix argument it prompts for an expression to eval and inspect."
(interactive "p")
(pcase arg
(1 (cider-inspect-last-sexp))
(4 (cider-inspect-defun-at-point))
(16 (call-interactively #'cider-inspect-expr))))
(defvar cider-inspector-location-stack nil
"A stack used to save point locations in inspector buffers.
These locations are used to emulate save-excursion between
`cider-inspector-push' and `cider-inspector-pop' operations.")
(defvar cider-inspector-page-location-stack nil
"A stack used to save point locations in inspector buffers.
These locations are used to emulate save-excursion between
`cider-inspector-next-page' and `cider-inspector-prev-page' operations.")
(defvar cider-inspector-last-command nil
"Contains the value of the most recently used `cider-inspector-*' command.
This is used as an alternative to the built-in `last-command'. Whenever we
invoke any command through M-x and its variants, the value of `last-command'
is not set to the command it invokes.")
;; Operations
;;;###autoload
(defun cider-inspect-expr (expr ns)
"Evaluate EXPR in NS and inspect its value.
Interactively, EXPR is read from the minibuffer, and NS the
current buffer's namespace."
(interactive (list (cider-read-from-minibuffer "Inspect expression: " (cider-sexp-at-point))
(cider-current-ns)))
(when-let (value (cider-sync-request:inspect-expr expr ns (or cider-inspector-page-size 32)))
(cider-inspector--render-value value)))
(defun cider-inspector-pop ()
(interactive)
(setq cider-inspector-last-command 'cider-inspector-pop)
(when-let (value (cider-sync-request:inspect-pop))
(cider-inspector--render-value value)))
(defun cider-inspector-push (idx)
(push (point) cider-inspector-location-stack)
(when-let (value (cider-sync-request:inspect-push idx))
(cider-inspector--render-value value)))
(defun cider-inspector-refresh ()
(interactive)
(when-let (value (cider-sync-request:inspect-refresh))
(cider-inspector--render-value value)))
(defun cider-inspector-next-page ()
"Jump to the next page when inspecting a paginated sequence/map.
Does nothing if already on the last page."
(interactive)
(push (point) cider-inspector-page-location-stack)
(when-let (value (cider-sync-request:inspect-next-page))
(cider-inspector--render-value value)))
(defun cider-inspector-prev-page ()
"Jump to the previous page when expecting a paginated sequence/map.
Does nothing if already on the first page."
(interactive)
(setq cider-inspector-last-command 'cider-inspector-prev-page)
(when-let (value (cider-sync-request:inspect-prev-page))
(cider-inspector--render-value value)))
(defun cider-inspector-set-page-size (page-size)
"Set the page size in pagination mode to the specified PAGE-SIZE.
Current page will be reset to zero."
(interactive "nPage size: ")
(when-let (value (cider-sync-request:inspect-set-page-size page-size))
(cider-inspector--render-value value)))
;; nREPL interactions
(defun cider-sync-request:inspect-pop ()
"Move one level up in the inspector stack."
(thread-first (list "op" "inspect-pop"
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-push (idx)
"Inspect the inside value specified by IDX."
(thread-first (list "op" "inspect-push"
"idx" idx
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-refresh ()
"Re-render the currently inspected value."
(thread-first (list "op" "inspect-refresh"
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-next-page ()
"Jump to the next page in paginated collection view."
(thread-first (list "op" "inspect-next-page"
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-prev-page ()
"Jump to the previous page in paginated collection view."
(thread-first (list "op" "inspect-prev-page"
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-set-page-size (page-size)
"Set the page size in paginated view to PAGE-SIZE."
(thread-first (list "op" "inspect-set-page-size"
"page-size" page-size
"session" (cider-current-session))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
(defun cider-sync-request:inspect-expr (expr ns page-size)
"Evaluate EXPR in context of NS and inspect its result.
Set the page size in paginated view to PAGE-SIZE."
(thread-first (append (nrepl--eval-request expr (cider-current-session) ns)
(list "inspect" "true"
"page-size" page-size))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "value")))
;; Render Inspector from Structured Values
(defun cider-inspector--render-value (value)
(cider-make-popup-buffer cider-inspector-buffer 'cider-inspector-mode)
(cider-inspector-render cider-inspector-buffer value)
(cider-popup-buffer-display cider-inspector-buffer t)
(with-current-buffer cider-inspector-buffer
(when (eq cider-inspector-last-command 'cider-inspector-pop)
(setq cider-inspector-last-command nil)
;; Prevents error message being displayed when we try to pop
;; from the top-level of a data struture
(when cider-inspector-location-stack
(goto-char (pop cider-inspector-location-stack))))
(when (eq cider-inspector-last-command 'cider-inspector-prev-page)
(setq cider-inspector-last-command nil)
;; Prevents error message being displayed when we try to
;; go to a prev-page from the first page
(when cider-inspector-page-location-stack
(goto-char (pop cider-inspector-page-location-stack))))))
(defun cider-inspector-render (buffer str)
(with-current-buffer buffer
(cider-inspector-mode)
(let ((inhibit-read-only t))
(condition-case nil
(cider-inspector-render* (car (read-from-string str)))
(error (insert "\nInspector error for: " str))))
(goto-char (point-min))))
(defun cider-inspector-render* (elements)
(dolist (el elements)
(cider-inspector-render-el* el)))
(defun cider-inspector-render-el* (el)
(cond ((symbolp el) (insert (symbol-name el)))
((stringp el) (insert (propertize el 'font-lock-face 'font-lock-keyword-face)))
((and (consp el) (eq (car el) :newline))
(insert "\n"))
((and (consp el) (eq (car el) :value))
(cider-inspector-render-value (cadr el) (cl-caddr el)))
(t (message "Unrecognized inspector object: %s" el))))
(defun cider-inspector-render-value (value idx)
(cider-propertize-region
(list 'cider-value-idx idx
'mouse-face 'highlight)
(cider-inspector-render-el* (cider-font-lock-as-clojure value))))
;; ===================================================
;; Inspector Navigation (lifted from SLIME inspector)
;; ===================================================
(defun cider-find-inspectable-object (direction limit)
"Find the next/previous inspectable object.
DIRECTION can be either 'next or 'prev.
LIMIT is the maximum or minimum position in the current buffer.
Return a list of two values: If an object could be found, the
starting position of the found object and T is returned;
otherwise LIMIT and NIL is returned."
(let ((finder (cl-ecase direction
(next 'next-single-property-change)
(prev 'previous-single-property-change))))
(let ((prop nil) (curpos (point)))
(while (and (not prop) (not (= curpos limit)))
(let ((newpos (funcall finder curpos 'cider-value-idx nil limit)))
(setq prop (get-text-property newpos 'cider-value-idx))
(setq curpos newpos)))
(list curpos (and prop t)))))
(defun cider-inspector-next-inspectable-object (arg)
"Move point to the next inspectable object.
With optional ARG, move across that many objects.
If ARG is negative, move backwards."
(interactive "p")
(let ((maxpos (point-max)) (minpos (point-min))
(previously-wrapped-p nil))
;; Forward.
(while (> arg 0)
(seq-let (pos foundp) (cider-find-inspectable-object 'next maxpos)
(if foundp
(progn (goto-char pos) (setq arg (1- arg))
(setq previously-wrapped-p nil))
(if (not previously-wrapped-p) ; cycle detection
(progn (goto-char minpos) (setq previously-wrapped-p t))
(error "No inspectable objects")))))
;; Backward.
(while (< arg 0)
(seq-let (pos foundp) (cider-find-inspectable-object 'prev minpos)
;; CIDER-OPEN-INSPECTOR inserts the title of an inspector page
;; as a presentation at the beginning of the buffer; skip
;; that. (Notice how this problem can not arise in ``Forward.'')
(if (and foundp (/= pos minpos))
(progn (goto-char pos) (setq arg (1+ arg))
(setq previously-wrapped-p nil))
(if (not previously-wrapped-p) ; cycle detection
(progn (goto-char maxpos) (setq previously-wrapped-p t))
(error "No inspectable objects")))))))
(defun cider-inspector-previous-inspectable-object (arg)
"Move point to the previous inspectable object.
With optional ARG, move across that many objects.
If ARG is negative, move forwards."
(interactive "p")
(cider-inspector-next-inspectable-object (- arg)))
(defun cider-inspector-property-at-point ()
(let* ((properties '(cider-value-idx cider-range-button
cider-action-number))
(find-property
(lambda (point)
(cl-loop for property in properties
for value = (get-text-property point property)
when value
return (list property value)))))
(or (funcall find-property (point))
(funcall find-property (1- (point))))))
(defun cider-inspector-operate-on-point ()
"Invoke the command for the text at point.
1. If point is on a value then recursively call the inspector on
that value.
2. If point is on an action then call that action.
3. If point is on a range-button fetch and insert the range."
(interactive)
(seq-let (property value) (cider-inspector-property-at-point)
(cl-case property
(cider-value-idx
(cider-inspector-push value))
;; TODO: range and action handlers
(t (error "No object at point")))))
(defun cider-inspector-operate-on-click (event)
"Move to EVENT's position and operate the part."
(interactive "@e")
(let ((point (posn-point (event-end event))))
(cond ((and point
(or (get-text-property point 'cider-value-idx)))
(goto-char point)
(cider-inspector-operate-on-point))
(t
(error "No clickable part here")))))
;;;###autoload
(define-obsolete-function-alias 'cider-inspect-read-and-inspect
'cider-inspect-expr "0.13.0")
(provide 'cider-inspector)
;;; cider-inspector.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,207 +0,0 @@
;;; cider-macroexpansion.el --- Macro expansion support -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Macro expansion support.
;;; Code:
(require 'cider-mode)
(require 'cider-compat)
(defconst cider-macroexpansion-buffer "*cider-macroexpansion*")
(push cider-macroexpansion-buffer cider-ancillary-buffers)
(defcustom cider-macroexpansion-display-namespaces 'tidy
"Determines if namespaces are displayed in the macroexpansion buffer.
Possible values are:
'qualified ;=> Vars are fully-qualified in the expansion
'none ;=> Vars are displayed without namespace qualification
'tidy ;=> Vars that are :refer-ed or defined in the current namespace are
displayed with their simple name, non-refered vars from other
namespaces are refered using the alias for that namespace (if
defined), other vars are displayed fully qualified."
:type '(choice (const :tag "Suppress namespaces" none)
(const :tag "Show fully-qualified namespaces" qualified)
(const :tag "Show namespace aliases" tidy))
:group 'cider
:package-version '(cider . "0.7.0"))
(defcustom cider-macroexpansion-print-metadata nil
"Determines if metadata is included in macroexpansion results."
:type 'boolean
:group 'cider
:package-version '(cider . "0.9.0"))
(defun cider-sync-request:macroexpand (expander expr &optional display-namespaces)
"Macroexpand, using EXPANDER, the given EXPR.
The default for DISPLAY-NAMESPACES is taken from
`cider-macroexpansion-display-namespaces'."
(cider-ensure-op-supported "macroexpand")
(thread-first (list "op" "macroexpand"
"expander" expander
"code" expr
"ns" (cider-current-ns)
"display-namespaces"
(or display-namespaces
(symbol-name cider-macroexpansion-display-namespaces)))
(append (when cider-macroexpansion-print-metadata
(list "print-meta" "true")))
(cider-nrepl-send-sync-request)
(nrepl-dict-get "expansion")))
(defun cider-macroexpand-undo (&optional arg)
"Undo the last macroexpansion, using `undo-only'.
ARG is passed along to `undo-only'."
(interactive)
(let ((inhibit-read-only t))
(undo-only arg)))
(defvar cider-last-macroexpand-expression nil
"Specify the last macroexpansion preformed.
This variable specifies both what was expanded and the expander.")
(defun cider-macroexpand-expr (expander expr)
"Macroexpand, use EXPANDER, the given EXPR."
(when-let ((expansion (cider-sync-request:macroexpand expander expr)))
(setq cider-last-macroexpand-expression expr)
(cider-initialize-macroexpansion-buffer expansion (cider-current-ns))))
(defun cider-macroexpand-expr-inplace (expander)
"Substitute the form preceding point with its macroexpansion using EXPANDER."
(interactive)
(let* ((expansion (cider-sync-request:macroexpand expander (cider-last-sexp)))
(bounds (cons (save-excursion (clojure-backward-logical-sexp 1) (point)) (point))))
(cider-redraw-macroexpansion-buffer
expansion (current-buffer) (car bounds) (cdr bounds))))
(defun cider-macroexpand-again ()
"Repeat the last macroexpansion."
(interactive)
(cider-initialize-macroexpansion-buffer cider-last-macroexpand-expression (cider-current-ns)))
;;;###autoload
(defun cider-macroexpand-1 (&optional prefix)
"Invoke \\=`macroexpand-1\\=` on the expression preceding point.
If invoked with a PREFIX argument, use \\=`macroexpand\\=` instead of
\\=`macroexpand-1\\=`."
(interactive "P")
(let ((expander (if prefix "macroexpand" "macroexpand-1")))
(cider-macroexpand-expr expander (cider-last-sexp))))
(defun cider-macroexpand-1-inplace (&optional prefix)
"Perform inplace \\=`macroexpand-1\\=` on the expression preceding point.
If invoked with a PREFIX argument, use \\=`macroexpand\\=` instead of
\\=`macroexpand-1\\=`."
(interactive "P")
(let ((expander (if prefix "macroexpand" "macroexpand-1")))
(cider-macroexpand-expr-inplace expander)))
;;;###autoload
(defun cider-macroexpand-all ()
"Invoke \\=`clojure.walk/macroexpand-all\\=` on the expression preceding point."
(interactive)
(cider-macroexpand-expr "macroexpand-all" (cider-last-sexp)))
(defun cider-macroexpand-all-inplace ()
"Perform inplace \\=`clojure.walk/macroexpand-all\\=` on the expression preceding point."
(interactive)
(cider-macroexpand-expr-inplace "macroexpand-all"))
(defun cider-initialize-macroexpansion-buffer (expansion ns)
"Create a new Macroexpansion buffer with EXPANSION and namespace NS."
(pop-to-buffer (cider-create-macroexpansion-buffer))
(setq cider-buffer-ns ns)
(setq buffer-undo-list nil)
(let ((inhibit-read-only t)
(buffer-undo-list t))
(erase-buffer)
(insert (format "%s" expansion))
(goto-char (point-max))
(cider--font-lock-ensure)))
(defun cider-redraw-macroexpansion-buffer (expansion buffer start end)
"Redraw the macroexpansion with new EXPANSION.
Text in BUFFER from START to END is replaced with new expansion,
and point is placed after the expanded form."
(with-current-buffer buffer
(let ((buffer-read-only nil))
(goto-char start)
(delete-region start end)
(insert (format "%s" expansion))
(goto-char start)
(indent-sexp)
(forward-sexp))))
(declare-function cider-mode "cider-mode")
(defun cider-create-macroexpansion-buffer ()
"Create a new macroexpansion buffer."
(with-current-buffer (cider-popup-buffer cider-macroexpansion-buffer t)
(clojure-mode)
(cider-mode -1)
(cider-macroexpansion-mode 1)
(current-buffer)))
(defvar cider-macroexpansion-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "g") #'cider-macroexpand-again)
(define-key map (kbd "q") #'cider-popup-buffer-quit-function)
(define-key map (kbd "d") #'cider-doc)
(define-key map (kbd "j") #'cider-javadoc)
(define-key map (kbd ".") #'cider-find-var)
(define-key map (kbd "m") #'cider-macroexpand-1-inplace)
(define-key map (kbd "a") #'cider-macroexpand-all-inplace)
(define-key map (kbd "u") #'cider-macroexpand-undo)
(define-key map [remap undo] #'cider-macroexpand-undo)
(easy-menu-define cider-macroexpansion-mode-menu map
"Menu for CIDER's doc mode"
'("Macroexpansion"
["Restart expansion" cider-macroexpand-again]
["Macroexpand-1" cider-macroexpand-1-inplace]
["Macroexpand-all" cider-macroexpand-all-inplace]
["Macroexpand-undo" cider-macroexpand-undo]
["Go to source" cider-find-var]
["Go to doc" cider-doc]
["Go to Javadoc" cider-docview-javadoc]
["Quit" cider-popup-buffer-quit-function]))
map))
(define-minor-mode cider-macroexpansion-mode
"Minor mode for CIDER macroexpansion.
\\{cider-macroexpansion-mode-map}"
nil
" Macroexpand"
cider-macroexpansion-mode-map)
(provide 'cider-macroexpansion)
;;; cider-macroexpansion.el ends here

View File

@ -1,750 +0,0 @@
;;; cider-mode.el --- Minor mode for REPL interactions -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Minor mode for REPL interactions.
;;; Code:
(require 'clojure-mode)
(require 'cider-interaction)
(require 'cider-test)
(require 'cider-eldoc)
(require 'cider-resolve)
(require 'cider-doc)
(require 'cider-compat)
(defcustom cider-mode-line-show-connection t
"If the mode-line lighter should detail the connection."
:group 'cider
:type 'boolean
:package-version '(cider "0.10.0"))
(defun cider--modeline-info ()
"Return info for the `cider-mode' modeline.
Info contains project name and host:port endpoint."
(if-let ((current-connection (ignore-errors (cider-current-connection))))
(with-current-buffer current-connection
(concat
cider-repl-type
(when cider-mode-line-show-connection
(format ":%s@%s:%s"
(or (cider--project-name nrepl-project-dir) "<no project>")
(pcase (car nrepl-endpoint)
("localhost" "")
(x x))
(cadr nrepl-endpoint)))))
"not connected"))
;;;###autoload
(defcustom cider-mode-line
'(:eval (format " cider[%s]" (cider--modeline-info)))
"Mode line lighter for `cider-mode'.
The value of this variable is a mode line template as in
`mode-line-format'. See Info Node `(elisp)Mode Line Format' for
details about mode line templates.
Customize this variable to change how `cider-mode' displays its
status in the mode line. The default value displays the current connection.
Set this variable to nil to disable the mode line
entirely."
:group 'cider
:type 'sexp
:risky t
:package-version '(cider "0.7.0"))
;;; Switching between REPL & source buffers
(defvar-local cider-last-clojure-buffer nil
"A buffer-local variable holding the last Clojure source buffer.
`cider-switch-to-last-clojure-buffer' uses this variable to jump
back to last Clojure source buffer.")
(defun cider-remember-clojure-buffer (buffer)
"Try to remember the BUFFER from which the user jumps.
The BUFFER needs to be a Clojure buffer and current major mode needs
to be `cider-repl-mode'. The user can use `cider-switch-to-last-clojure-buffer'
to jump back to the last Clojure source buffer."
(when (and buffer
(with-current-buffer buffer
(derived-mode-p 'clojure-mode))
(derived-mode-p 'cider-repl-mode))
(setq cider-last-clojure-buffer buffer)))
(defun cider--switch-to-repl-buffer (repl-buffer &optional set-namespace)
"Select the REPL-BUFFER, when possible in an existing window.
Hint: You can use `display-buffer-reuse-frames' and
`special-display-buffer-names' to customize the frame in which
the buffer should appear.
When SET-NAMESPACE is t, sets the namespace in the REPL buffer to
that of the namespace in the Clojure source buffer."
(cider-ensure-connected)
(let ((buffer (current-buffer)))
;; first we switch to the REPL buffer
(if cider-repl-display-in-current-window
(pop-to-buffer-same-window repl-buffer)
(pop-to-buffer repl-buffer))
;; then if necessary we update its namespace
(when set-namespace
(cider-repl-set-ns (with-current-buffer buffer (cider-current-ns))))
(cider-remember-clojure-buffer buffer)
(goto-char (point-max))))
(defun cider-switch-to-repl-buffer (&optional set-namespace)
"Select the REPL buffer, when possible in an existing window.
The buffer chosen is based on the file open in the current buffer.
If the REPL buffer cannot be unambiguously determined, the REPL
buffer is chosen based on the current connection buffer and a
message raised informing the user.
Hint: You can use `display-buffer-reuse-frames' and
`special-display-buffer-names' to customize the frame in which
the buffer should appear.
With a prefix arg SET-NAMESPACE sets the namespace in the REPL buffer to that
of the namespace in the Clojure source buffer."
(interactive "P")
(cider--switch-to-repl-buffer (cider-current-repl-buffer) set-namespace))
(declare-function cider-load-buffer "cider-interaction")
(defun cider-load-buffer-and-switch-to-repl-buffer (&optional set-namespace)
"Load the current buffer into the matching REPL buffer and switch to it.
When SET-NAMESPACE is true, we'll also set the REPL's ns to match that of the
Clojure buffer."
(interactive "P")
(cider-load-buffer)
(cider-switch-to-repl-buffer set-namespace))
(defun cider-switch-to-last-clojure-buffer ()
"Switch to the last Clojure buffer.
The default keybinding for this command is
the same as `cider-switch-to-repl-buffer',
so that it is very convenient to jump between a
Clojure buffer and the REPL buffer."
(interactive)
(if (and (derived-mode-p 'cider-repl-mode)
(buffer-live-p cider-last-clojure-buffer))
(if cider-repl-display-in-current-window
(pop-to-buffer-same-window cider-last-clojure-buffer)
(pop-to-buffer cider-last-clojure-buffer))
(message "Don't know the original Clojure buffer")))
(defun cider-find-and-clear-repl-output (&optional clear-repl)
"Find the current REPL buffer and clear it.
With a prefix argument CLEAR-REPL the command clears the entire REPL buffer.
Returns to the buffer in which the command was invoked."
(interactive "P")
(let ((origin-buffer (current-buffer)))
(switch-to-buffer (cider-current-repl-buffer))
(if clear-repl
(cider-repl-clear-buffer)
(cider-repl-clear-output))
(switch-to-buffer origin-buffer)))
;;; The menu-bar
(defconst cider-mode-menu
`("CIDER"
["Start a REPL" cider-jack-in
:help "Starts an nREPL server (with lein, boot, or maven) and connects a REPL to it."]
["Connect to a REPL" cider-connect
:help "Connects to a REPL that's already running."]
["Quit" cider-quit :active cider-connections]
["Restart" cider-restart :active cider-connections]
("Clojurescript"
["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript
:help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL.
Configure `cider-cljs-lein-repl' to change the ClojureScript REPL to use."]
["Create a ClojureScript REPL from a Clojure REPL" cider-create-sibling-cljs-repl]
["Configure the ClojureScript REPL to use" (customize-variable 'cider-cljs-lein-repl)])
"--"
["Connection info" cider-display-connection-info
:active cider-connections]
["Rotate default connection" cider-rotate-default-connection
:active (cdr cider-connections)]
["Select any CIDER buffer" cider-selector]
"--"
["Configure CIDER" (customize-group 'cider)]
"--"
["A sip of CIDER" cider-drink-a-sip]
["View manual online" cider-view-manual]
["View refcard online" cider-view-refcard]
["Report a bug" cider-report-bug]
["Version info" cider-version]
"--"
["Close ancillary buffers" cider-close-ancillary-buffers
:active (seq-remove #'null cider-ancillary-buffers)]
("nREPL" :active cider-connections
["Describe session" cider-describe-nrepl-session]
["Close session" cider-close-nrepl-session]))
"Menu for CIDER mode")
(defconst cider-mode-eval-menu
'("CIDER Eval" :visible cider-connections
["Eval top-level sexp" cider-eval-defun-at-point]
["Eval current sexp" cider-eval-sexp-at-point]
["Eval last sexp" cider-eval-last-sexp]
["Eval selected region" cider-eval-region]
["Eval ns form" cider-eval-ns-form]
"--"
["Interrupt evaluation" cider-interrupt]
"--"
["Eval last sexp and insert" cider-eval-print-last-sexp
:keys "\\[universal-argument] \\[cider-eval-last-sexp]"]
["Eval last sexp in popup buffer" cider-pprint-eval-last-sexp]
["Eval last sexp and replace" cider-eval-last-sexp-and-replace]
["Eval last sexp to REPL" cider-eval-last-sexp-to-repl]
["Insert last sexp in REPL" cider-insert-last-sexp-in-repl]
["Eval top-level sexp to comment" cider-eval-defun-to-comment]
"--"
["Load this buffer" cider-load-buffer]
["Load another file" cider-load-file]
["Load all project files" cider-load-all-project-ns]
["Refresh loaded code" cider-refresh]
["Run project (-main function)" cider-run])
"Menu for CIDER mode eval commands.")
(defconst cider-mode-interactions-menu
`("CIDER Interactions" :visible cider-connections
["Complete symbol" complete-symbol]
"--"
("REPL"
["Set REPL to this ns" cider-repl-set-ns]
["Switch to REPL" cider-switch-to-repl-buffer]
["REPL Pretty Print" cider-repl-toggle-pretty-printing
:style toggle :selected cider-repl-use-pretty-printing]
["Clear latest output" cider-find-and-clear-repl-output]
["Clear all output" (cider-find-and-clear-repl-output t)
:keys "\\[universal-argument] \\[cider-find-and-clear-repl-output]"]
"--"
["Configure the REPL" (customize-group 'cider-repl)])
,cider-doc-menu
("Find (jump to)"
["Find definition" cider-find-var]
["Find resource" cider-find-resource]
["Go back" cider-pop-back])
("Macroexpand"
["Macroexpand-1" cider-macroexpand-1]
["Macroexpand-all" cider-macroexpand-all])
,cider-test-menu
("Debug"
["Inspect" cider-inspect]
["Toggle var tracing" cider-toggle-trace-var]
["Toggle ns tracing" cider-toggle-trace-ns]
"--"
["Debug top-level form" cider-debug-defun-at-point
:keys "\\[universal-argument] \\[cider-eval-defun-at-point]"]
["List instrumented defs" cider-browse-instrumented-defs]
"--"
["Configure the Debugger" (customize-group 'cider-debug)])
("Browse"
["Browse namespace" cider-browse-ns]
["Browse all namespaces" cider-browse-ns-all]
["Browse classpath" cider-classpath]
["Browse classpath entry" cider-open-classpath-entry]))
"Menu for CIDER interactions.")
(defconst cider-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-d") 'cider-doc-map)
(define-key map (kbd "M-.") #'cider-find-var)
(define-key map (kbd "C-c C-.") #'cider-find-ns)
(define-key map (kbd "M-,") #'cider-pop-back)
(define-key map (kbd "C-c M-.") #'cider-find-resource)
(define-key map (kbd "M-TAB") #'complete-symbol)
(define-key map (kbd "C-M-x") #'cider-eval-defun-at-point)
(define-key map (kbd "C-c C-c") #'cider-eval-defun-at-point)
(define-key map (kbd "C-x C-e") #'cider-eval-last-sexp)
(define-key map (kbd "C-c C-e") #'cider-eval-last-sexp)
(define-key map (kbd "C-c C-v") 'cider-eval-commands-map)
(define-key map (kbd "C-c M-;") #'cider-eval-defun-to-comment)
(define-key map (kbd "C-c M-e") #'cider-eval-last-sexp-to-repl)
(define-key map (kbd "C-c M-p") #'cider-insert-last-sexp-in-repl)
(define-key map (kbd "C-c C-p") #'cider-pprint-eval-last-sexp)
(define-key map (kbd "C-c C-f") #'cider-pprint-eval-defun-at-point)
(define-key map (kbd "C-c M-:") #'cider-read-and-eval)
(define-key map (kbd "C-c C-u") #'cider-undef)
(define-key map (kbd "C-c C-m") #'cider-macroexpand-1)
(define-key map (kbd "C-c M-m") #'cider-macroexpand-all)
(define-key map (kbd "C-c M-n") #'cider-repl-set-ns)
(define-key map (kbd "C-c M-i") #'cider-inspect)
(define-key map (kbd "C-c M-t v") #'cider-toggle-trace-var)
(define-key map (kbd "C-c M-t n") #'cider-toggle-trace-ns)
(define-key map (kbd "C-c C-z") #'cider-switch-to-repl-buffer)
(define-key map (kbd "C-c M-z") #'cider-load-buffer-and-switch-to-repl-buffer)
(define-key map (kbd "C-c C-o") #'cider-find-and-clear-repl-output)
(define-key map (kbd "C-c C-k") #'cider-load-buffer)
(define-key map (kbd "C-c C-l") #'cider-load-file)
(define-key map (kbd "C-c C-b") #'cider-interrupt)
(define-key map (kbd "C-c ,") 'cider-test-commands-map)
(define-key map (kbd "C-c C-t") 'cider-test-commands-map)
(define-key map (kbd "C-c M-s") #'cider-selector)
(define-key map (kbd "C-c M-r") #'cider-rotate-default-connection)
(define-key map (kbd "C-c M-d") #'cider-display-connection-info)
(define-key map (kbd "C-c C-x") #'cider-refresh)
(define-key map (kbd "C-c C-q") #'cider-quit)
(dolist (variable '(cider-mode-interactions-menu
cider-mode-eval-menu
cider-mode-menu))
(easy-menu-do-define (intern (format "%s-open" variable))
map
(get variable 'variable-documentation)
(cider--menu-add-help-strings (symbol-value variable))))
map))
;; This menu works as an easy entry-point into CIDER. Even if cider.el isn't
;; loaded yet, this will be shown in Clojure buffers next to the "Clojure"
;; menu.
;;;###autoload
(eval-after-load 'clojure-mode
'(easy-menu-define cider-clojure-mode-menu-open clojure-mode-map
"Menu for Clojure mode.
This is displayed in `clojure-mode' buffers, if `cider-mode' is not active."
`("CIDER" :visible (not cider-mode)
["Start a REPL" cider-jack-in
:help "Starts an nREPL server (with lein, boot, or maven) and connects a REPL to it."]
["Connect to a REPL" cider-connect
:help "Connects to a REPL that's already running."]
["Start a Clojure REPL, and a ClojureScript REPL" cider-jack-in-clojurescript
:help "Starts an nREPL server, connects a Clojure REPL to it, and then a ClojureScript REPL.
Configure `cider-cljs-lein-repl' to change the ClojureScript REPL to use."]
"--"
["View manual online" cider-view-manual])))
;;; Dynamic indentation
(defcustom cider-dynamic-indentation t
"Whether CIDER should aid Clojure(Script) indentation.
If non-nil, CIDER uses runtime information (such as the \":style/indent\"
metadata) to improve standard `clojure-mode' indentation.
If nil, CIDER won't interfere with `clojure-mode's indentation.
Toggling this variable only takes effect after a file is closed and
re-visited."
:type 'boolean
:package-version '(cider . "0.11.0")
:group 'cider)
(defun cider--get-symbol-indent (symbol-name)
"Return the indent metadata for SYMBOL-NAME in the current namespace."
(let* ((ns (cider-current-ns)))
(if-let ((meta (cider-resolve-var ns symbol-name))
(indent (or (nrepl-dict-get meta "style/indent")
(nrepl-dict-get meta "indent"))))
(let ((format (format ":indent metadata on %s is unreadable! \nERROR: %%s"
symbol-name)))
(with-demoted-errors format
(cider--deep-vector-to-list (read indent))))
;; There's no indent metadata, but there might be a clojure-mode
;; indent-spec with fully-qualified namespace.
(when (string-match cider-resolve--prefix-regexp symbol-name)
(when-let ((sym (intern-soft (replace-match (save-match-data
(cider-resolve-alias ns (match-string 1 symbol-name)))
t t symbol-name 1))))
(get sym 'clojure-indent-function))))))
;;; Dynamic font locking
(defcustom cider-font-lock-dynamically '(macro core deprecated)
"Specifies how much dynamic font-locking CIDER should use.
Dynamic font-locking this refers to applying syntax highlighting to vars
defined in the currently active nREPL connection. This is done in addition
to `clojure-mode's usual (static) font-lock, so even if you set this
variable to nil you'll still see basic syntax highlighting.
The value is a list of symbols, each one indicates a different type of var
that should be font-locked:
`macro' (default): Any defined macro gets the `font-lock-builtin-face'.
`function': Any defined function gets the `font-lock-function-face'.
`var': Any non-local var gets the `font-lock-variable-face'.
`deprecated' (default): Any deprecated var gets the `cider-deprecated-face'
face.
`core' (default): Any symbol from clojure.core (face depends on type).
The value can also be t, which means to font-lock as much as possible."
:type '(choice (set :tag "Fine-tune font-locking"
(const :tag "Any defined macro" macro)
(const :tag "Any defined function" function)
(const :tag "Any defined var" var)
(const :tag "Any defined deprecated" deprecated)
(const :tag "Any symbol from clojure.core" core))
(const :tag "Font-lock as much as possible" t))
:group 'cider
:package-version '(cider . "0.10.0"))
(defface cider-deprecated-face
'((((background light)) :background "light goldenrod")
(((background dark)) :background "#432"))
"Face used on deprecated vars."
:group 'cider)
(defface cider-instrumented-face
'((((type graphic)) :box (:color "#c00" :line-width -1))
(t :underline t :background "#800"))
"Face used to mark code being debugged."
:group 'cider-debug
:group 'cider
:package-version '(cider . "0.10.0"))
(defface cider-traced-face
'((((type graphic)) :box (:color "cyan" :line-width -1))
(t :underline t :background "#066"))
"Face used to mark code being traced."
:group 'cider
:package-version '(cider . "0.11.0"))
(defun cider--unless-local-match (value)
"Return VALUE, unless `match-string' is a local var."
(unless (or (get-text-property (point) 'cider-block-dynamic-font-lock)
(member (match-string 0)
(get-text-property (point) 'cider-locals)))
value))
(defun cider--compile-font-lock-keywords (symbols-plist core-plist)
"Return a list of font-lock rules for the symbols in SYMBOLS-PLIST and CORE-PLIST."
(let ((cider-font-lock-dynamically (if (eq cider-font-lock-dynamically t)
'(function var macro core deprecated)
cider-font-lock-dynamically))
deprecated enlightened
macros functions vars instrumented traced)
(cl-labels ((handle-plist
(plist)
(let ((do-function (memq 'function cider-font-lock-dynamically))
(do-var (memq 'var cider-font-lock-dynamically))
(do-macro (memq 'macro cider-font-lock-dynamically))
(do-deprecated (memq 'deprecated cider-font-lock-dynamically)))
(while plist
(let ((sym (pop plist))
(meta (pop plist)))
(pcase (nrepl-dict-get meta "cider.nrepl.middleware.util.instrument/breakfunction")
(`nil nil)
(`"#'cider.nrepl.middleware.debug/breakpoint-if-interesting"
(push sym instrumented))
(`"#'cider.nrepl.middleware.enlighten/light-form"
(push sym enlightened)))
;; The ::traced keywords can be inlined by MrAnderson, so
;; we catch that case too.
;; FIXME: This matches values too, not just keys.
(when (seq-find (lambda (k) (and (stringp k)
(string-match (rx "clojure.tools.trace/traced" eos) k)))
meta)
(push sym traced))
(when (and do-deprecated (nrepl-dict-get meta "deprecated"))
(push sym deprecated))
(cond ((and do-macro (nrepl-dict-get meta "macro"))
(push sym macros))
((and do-function (nrepl-dict-get meta "arglists"))
(push sym functions))
(do-var (push sym vars))))))))
(when (memq 'core cider-font-lock-dynamically)
(let ((cider-font-lock-dynamically '(function var macro core deprecated)))
(handle-plist core-plist)))
(handle-plist symbols-plist))
`(
,@(when macros
`((,(concat (rx (or "(" "#'")) ; Can't take the value of macros.
"\\(" (regexp-opt macros 'symbols) "\\)")
1 (cider--unless-local-match font-lock-keyword-face))))
,@(when functions
`((,(regexp-opt functions 'symbols) 0
(cider--unless-local-match font-lock-function-name-face))))
,@(when vars
`((,(regexp-opt vars 'symbols) 0
(cider--unless-local-match font-lock-variable-name-face))))
,@(when deprecated
`((,(regexp-opt deprecated 'symbols) 0
(cider--unless-local-match 'cider-deprecated-face) append)))
,@(when enlightened
`((,(regexp-opt enlightened 'symbols) 0
(cider--unless-local-match 'cider-enlightened-face) append)))
,@(when instrumented
`((,(regexp-opt instrumented 'symbols) 0
(cider--unless-local-match 'cider-instrumented-face) append)))
,@(when traced
`((,(regexp-opt traced 'symbols) 0
(cider--unless-local-match 'cider-traced-face) append))))))
(defconst cider--static-font-lock-keywords
(eval-when-compile
`((,(regexp-opt '("#break" "#dbg" "#light") 'symbols) 0 font-lock-warning-face)))
"Default expressions to highlight in CIDER mode.")
(defvar-local cider--dynamic-font-lock-keywords nil)
(defun cider-refresh-dynamic-font-lock (&optional ns)
"Ensure that the current buffer has up-to-date font-lock rules.
NS defaults to `cider-current-ns', and it can also be a dict describing the
namespace itself."
(interactive)
(when (and cider-font-lock-dynamically
font-lock-mode)
(font-lock-remove-keywords nil cider--dynamic-font-lock-keywords)
(when-let ((ns (or ns (cider-current-ns)))
(symbols (cider-resolve-ns-symbols ns)))
(setq-local cider--dynamic-font-lock-keywords
(cider--compile-font-lock-keywords
symbols (cider-resolve-ns-symbols (cider-resolve-core-ns))))
(font-lock-add-keywords nil cider--dynamic-font-lock-keywords 'end))
(cider--font-lock-flush)))
;;; Detecting local variables
(defun cider--read-locals-from-next-sexp ()
"Return a list of all locals inside the next logical sexp."
(save-excursion
(ignore-errors
(clojure-forward-logical-sexp 1)
(let ((out nil)
(end (point)))
(forward-sexp -1)
;; FIXME: This returns locals found inside the :or clause of a
;; destructuring map.
(while (search-forward-regexp "\\_<[^:&]\\(\\sw\\|\\s_\\)*\\_>" end 'noerror)
(push (match-string-no-properties 0) out))
out))))
(defun cider--read-locals-from-bindings-vector ()
"Return a list of all locals inside the next bindings vector."
(save-excursion
(ignore-errors
(cider-start-of-next-sexp)
(when (eq (char-after) ?\[)
(forward-char 1)
(let ((out nil))
(setq out (append (cider--read-locals-from-next-sexp) out))
(while (ignore-errors (clojure-forward-logical-sexp 3)
(unless (eobp)
(forward-sexp -1)
t))
(setq out (append (cider--read-locals-from-next-sexp) out)))
out)))))
(defun cider--read-locals-from-arglist ()
"Return a list of all locals in current form's arglist(s)."
(let ((out nil))
(save-excursion
(ignore-errors
(cider-start-of-next-sexp)
;; Named fn
(when (looking-at-p "\\s_\\|\\sw")
(cider-start-of-next-sexp 1))
;; Docstring
(when (eq (char-after) ?\")
(cider-start-of-next-sexp 1))
;; Attribute map
(when (eq (char-after) ?{)
(cider-start-of-next-sexp 1))
;; The arglist
(pcase (char-after)
(?\[ (setq out (cider--read-locals-from-next-sexp)))
;; FIXME: This returns false positives. It takes all arglists of a
;; function and returns all args it finds. The logic should be changed
;; so that each arglist applies to its own scope.
(?\( (ignore-errors
(while (eq (char-after) ?\()
(save-excursion
(forward-char 1)
(setq out (append (cider--read-locals-from-next-sexp) out)))
(cider-start-of-next-sexp 1)))))))
out))
(defun cider--parse-and-apply-locals (end &optional outer-locals)
"Figure out local variables between point and END.
A list of these variables is set as the `cider-locals' text property over
the code where they are in scope.
Optional argument OUTER-LOCALS is used to specify local variables defined
before point."
(while (search-forward-regexp "(\\(ns\\_>\\|def\\|fn\\|for\\b\\|loop\\b\\|with-\\|do[a-z]+\\|\\([a-z]+-\\)?let\\b\\)"
end 'noerror)
(goto-char (match-beginning 0))
(let ((sym (match-string 1))
(sexp-end (save-excursion
(or (ignore-errors (forward-sexp 1)
(point))
end))))
;; #1324: Don't do dynamic font-lock in `ns' forms, they are special
;; macros where nothing is evaluated, so we'd get a lot of false
;; positives.
(if (equal sym "ns")
(add-text-properties (point) sexp-end '(cider-block-dynamic-font-lock t))
(forward-char 1)
(forward-sexp 1)
(let ((locals (append outer-locals
(pcase sym
((or "fn" "def" "") (cider--read-locals-from-arglist))
(_ (cider--read-locals-from-bindings-vector))))))
(add-text-properties (point) sexp-end (list 'cider-locals locals))
(clojure-forward-logical-sexp 1)
(cider--parse-and-apply-locals sexp-end locals)))
(goto-char sexp-end))))
(defun cider--update-locals-for-region (beg end)
"Update the `cider-locals' text property for region from BEG to END."
(save-excursion
(goto-char beg)
;; If the inside of a `ns' form changed, reparse it from the start.
(when (and (not (bobp))
(get-text-property (1- (point)) 'cider-block-dynamic-font-lock))
(ignore-errors (beginning-of-defun)))
(save-excursion
;; Move up until we reach a sexp that encloses the entire region (or
;; a top-level sexp), and set that as the new BEG.
(goto-char end)
(while (and (or (> (point) beg)
(not (eq (char-after) ?\()))
(condition-case nil
(progn (backward-up-list) t)
(scan-error nil))))
(setq beg (min beg (point)))
;; If there are locals above the current sexp, reapply them to the
;; current sexp.
(let ((locals-above (when (> beg (point-min))
(get-text-property (1- beg) 'cider-locals))))
(condition-case nil
(clojure-forward-logical-sexp 1)
(error (goto-char end)))
(add-text-properties beg (point) `(cider-locals ,locals-above))
;; Extend the region being font-locked to include whole sexps.
(setq end (max end (point)))
(goto-char beg)
(ignore-errors
(cider--parse-and-apply-locals end locals-above))))))
(defun cider--docview-as-string (sym info)
"Return a string of what would be displayed by `cider-docview-render'."
(with-temp-buffer
(cider-docview-render (current-buffer) sym info)
(goto-char (point-max))
(forward-line -1)
(replace-regexp-in-string
"[`']" "\\\\=\\&"
(buffer-substring-no-properties (point-min) (1- (point))))))
(defcustom cider-use-tooltips t
"If non-nil, CIDER displays mouse-over tooltips."
:group 'cider
:type 'boolean
:package-version '(cider "0.12.0"))
(defvar cider--debug-mode-response)
(defvar cider--debug-mode)
(defun cider--help-echo (_ obj pos)
"Return the help-echo string for OBJ at POS.
See \(info \"(elisp) Special Properties\")"
(while-no-input
(when (and (bufferp obj) (cider-connected-p)
cider-use-tooltips (not help-at-pt-display-when-idle))
(with-current-buffer obj
(ignore-errors
(save-excursion
(goto-char pos)
(when-let ((sym (cider-symbol-at-point)))
(if (member sym (get-text-property (point) 'cider-locals))
(concat (format "`%s' is a local" sym)
(when cider--debug-mode
(let* ((locals (nrepl-dict-get cider--debug-mode-response "locals"))
(local-val (cadr (assoc sym locals))))
(format " with value:\n%s" local-val))))
(let* ((info (cider-sync-request:info sym))
(candidates (nrepl-dict-get info "candidates")))
(if candidates
(concat "There were ambiguities resolving this symbol:\n\n"
(mapconcat (lambda (x) (cider--docview-as-string sym x))
candidates
(concat "\n\n" (make-string 60 ?-) "\n\n")))
(cider--docview-as-string sym info)))))))))))
(defun cider--wrap-fontify-locals (func)
"Return a function that will call FUNC after parsing local variables.
The local variables are stored in a list under the `cider-locals' text
property."
(lambda (beg end &rest rest)
(with-silent-modifications
(remove-text-properties beg end '(cider-locals nil cider-block-dynamic-font-lock nil))
(add-text-properties beg end '(help-echo cider--help-echo))
(when cider-font-lock-dynamically
(cider--update-locals-for-region beg end)))
(apply func beg end rest)))
;;; Minor-mode definition
(defvar x-gtk-use-system-tooltips)
;;;###autoload
(define-minor-mode cider-mode
"Minor mode for REPL interaction from a Clojure buffer.
\\{cider-mode-map}"
nil
cider-mode-line
cider-mode-map
(if cider-mode
(progn
(cider-eldoc-setup)
(make-local-variable 'completion-at-point-functions)
(add-to-list 'completion-at-point-functions
#'cider-complete-at-point)
(font-lock-add-keywords nil cider--static-font-lock-keywords)
(cider-refresh-dynamic-font-lock)
;; `font-lock-mode' might get enabled after `cider-mode'.
(add-hook 'font-lock-mode-hook #'cider-refresh-dynamic-font-lock nil 'local)
(setq-local font-lock-fontify-region-function
(cider--wrap-fontify-locals font-lock-fontify-region-function))
;; GTK tooltips look bad, and we have no control over the face.
(setq-local x-gtk-use-system-tooltips nil)
;; `tooltip' has variable-width by default, which looks terrible.
(set-face-attribute 'tooltip nil :inherit 'unspecified)
(when cider-dynamic-indentation
(setq-local clojure-get-indent-function #'cider--get-symbol-indent))
(setq-local clojure-expected-ns-function #'cider-expected-ns)
(setq next-error-function #'cider-jump-to-compilation-error))
(mapc #'kill-local-variable '(completion-at-point-functions
next-error-function
x-gtk-use-system-tooltips
font-lock-fontify-region-function
clojure-get-indent-function))
(remove-hook 'font-lock-mode-hook #'cider-refresh-dynamic-font-lock 'local)
(font-lock-remove-keywords nil cider--dynamic-font-lock-keywords)
(font-lock-remove-keywords nil cider--static-font-lock-keywords)
(cider--font-lock-flush)))
(defun cider-set-buffer-ns (ns)
"Set this buffer's namespace to NS and refresh font-locking."
(setq-local cider-buffer-ns ns)
(when (or cider-mode (derived-mode-p 'cider-repl-mode))
(cider-refresh-dynamic-font-lock ns)))
(provide 'cider-mode)
;;; cider-mode.el ends here

View File

@ -1,311 +0,0 @@
;;; cider-overlays.el --- Managing CIDER overlays -*- lexical-binding: t; -*-
;; Copyright © 2015-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Use `cider--make-overlay' to place a generic overlay at point. Or use
;; `cider--make-result-overlay' to place an interactive eval result overlay at
;; the end of a specified line.
;;; Code:
(require 'cider-common)
(require 'cider-compat)
(require 'cl-lib)
;;; Customization
(defface cider-result-overlay-face
'((((class color) (background light))
:background "grey90" :box (:line-width -1 :color "yellow"))
(((class color) (background dark))
:background "grey10" :box (:line-width -1 :color "black")))
"Face used to display evaluation results at the end of line.
If `cider-overlays-use-font-lock' is non-nil, this face is
applied with lower priority than the syntax highlighting."
:group 'cider
:package-version '(cider "0.9.1"))
(defcustom cider-result-use-clojure-font-lock t
"If non-nil, interactive eval results are font-locked as Clojure code."
:group 'cider
:type 'boolean
:package-version '(cider . "0.10.0"))
(defcustom cider-overlays-use-font-lock t
"If non-nil, results overlays are font-locked as Clojure code.
If nil, apply `cider-result-overlay-face' to the entire overlay instead of
font-locking it."
:group 'cider
:type 'boolean
:package-version '(cider . "0.10.0"))
(defcustom cider-use-overlays 'both
"Whether to display evaluation results with overlays.
If t, use overlays. If nil, display on the echo area. If both, display on
both places.
Only applies to evaluation commands. To configure the debugger overlays,
see `cider-debug-use-overlays'."
:type '(choice (const :tag "End of line" t)
(const :tag "Bottom of screen" nil)
(const :tag "Both" both))
:group 'cider
:package-version '(cider . "0.10.0"))
(defcustom cider-eval-result-prefix "=> "
"The prefix displayed in the minibuffer before a result value."
:type 'string
:group 'cider
:package-version '(cider . "0.5.0"))
(defcustom cider-eval-result-duration 'command
"Duration, in seconds, of CIDER's eval-result overlays.
If nil, overlays last indefinitely.
If the symbol `command', they're erased after the next command.
Also see `cider-use-overlays'."
:type '(choice (integer :tag "Duration in seconds")
(const :tag "Until next command" command)
(const :tag "Last indefinitely" nil))
:group 'cider
:package-version '(cider . "0.10.0"))
;;; Overlay logic
(defun cider--delete-overlay (ov &rest _)
"Safely delete overlay OV.
Never throws errors, and can be used in an overlay's modification-hooks."
(ignore-errors (delete-overlay ov)))
(defun cider--make-overlay (l r type &rest props)
"Place an overlay between L and R and return it.
TYPE is a symbol put on the overlay's category property. It is used to
easily remove all overlays from a region with:
(remove-overlays start end 'category TYPE)
PROPS is a plist of properties and values to add to the overlay."
(let ((o (make-overlay l (or r l) (current-buffer))))
(overlay-put o 'category type)
(overlay-put o 'cider-temporary t)
(while props (overlay-put o (pop props) (pop props)))
(push #'cider--delete-overlay (overlay-get o 'modification-hooks))
o))
(defun cider--remove-result-overlay ()
"Remove result overlay from current buffer.
This function also removes itself from `post-command-hook'."
(remove-hook 'post-command-hook #'cider--remove-result-overlay 'local)
(remove-overlays nil nil 'category 'result))
(defun cider--remove-result-overlay-after-command ()
"Add `cider--remove-result-overlay' locally to `post-command-hook'.
This function also removes itself from `post-command-hook'."
(remove-hook 'post-command-hook #'cider--remove-result-overlay-after-command 'local)
(add-hook 'post-command-hook #'cider--remove-result-overlay nil 'local))
(defface cider-fringe-good-face
'((((class color) (background light)) :foreground "lightgreen")
(((class color) (background dark)) :foreground "darkgreen"))
"Face used on the fringe indicator for successful evaluation."
:group 'cider)
(defconst cider--fringe-overlay-good
(propertize " " 'display '(left-fringe empty-line cider-fringe-good-face))
"The before-string property that adds a green indicator on the fringe.")
(defcustom cider-use-fringe-indicators t
"Whether to display evaluation indicators on the left fringe."
:safe #'booleanp
:group 'cider
:type 'boolean)
(defun cider--make-fringe-overlay (&optional end)
"Place an eval indicator at the fringe before a sexp.
END is the position where the sexp ends, and defaults to point."
(when cider-use-fringe-indicators
(with-current-buffer (if (markerp end)
(marker-buffer end)
(current-buffer))
(save-excursion
(if end
(goto-char end)
(setq end (point)))
(clojure-forward-logical-sexp -1)
;; Create the green-circle overlay.
(cider--make-overlay (point) end 'cider-fringe-indicator
'before-string cider--fringe-overlay-good)))))
(cl-defun cider--make-result-overlay (value &rest props &key where duration (type 'result)
(format (concat " " cider-eval-result-prefix "%s "))
(prepend-face 'cider-result-overlay-face)
&allow-other-keys)
"Place an overlay displaying VALUE at the end of line.
VALUE is used as the overlay's after-string property, meaning it is
displayed at the end of the overlay. The overlay itself is placed from
beginning to end of current line.
Return nil if the overlay was not placed or if it might not be visible, and
return the overlay otherwise.
Return the overlay if it was placed successfully, and nil if it failed.
This function takes some optional keyword arguments:
If WHERE is a number or a marker, apply the overlay over
the entire line at that place (defaulting to `point'). If
it is a cons cell, the car and cdr determine the start and
end of the overlay.
DURATION takes the same possible values as the
`cider-eval-result-duration' variable.
TYPE is passed to `cider--make-overlay' (defaults to `result').
FORMAT is a string passed to `format'. It should have
exactly one %s construct (for VALUE).
All arguments beyond these (PROPS) are properties to be used on the
overlay."
(declare (indent 1))
(while (keywordp (car props))
(setq props (cdr (cdr props))))
;; If the marker points to a dead buffer, don't do anything.
(let ((buffer (cond
((markerp where) (marker-buffer where))
((markerp (car-safe where)) (marker-buffer (car where)))
(t (current-buffer)))))
(with-current-buffer buffer
(save-excursion
(when (number-or-marker-p where)
(goto-char where))
;; Make sure the overlay is actually at the end of the sexp.
(skip-chars-backward "\r\n[:blank:]")
(let* ((beg (if (consp where)
(car where)
(save-excursion
(clojure-backward-logical-sexp 1)
(point))))
(end (if (consp where)
(cdr where)
(line-end-position)))
(display-string (format format value))
(o nil))
(remove-overlays beg end 'category type)
(funcall (if cider-overlays-use-font-lock
#'font-lock-prepend-text-property
#'put-text-property)
0 (length display-string)
'face prepend-face
display-string)
;; If the display spans multiple lines or is very long, display it at
;; the beginning of the next line.
(when (or (string-match "\n." display-string)
(> (string-width display-string)
(- (window-width) (current-column))))
(setq display-string (concat " \n" display-string)))
;; Put the cursor property only once we're done manipulating the
;; string, since we want it to be at the first char.
(put-text-property 0 1 'cursor 0 display-string)
(when (> (string-width display-string) (* 3 (window-width)))
(setq display-string
(concat (substring display-string 0 (* 3 (window-width)))
(substitute-command-keys
"...\nResult truncated. Type `\\[cider-inspect-last-result]' to inspect it."))))
;; Create the result overlay.
(setq o (apply #'cider--make-overlay
beg end type
'after-string display-string
props))
(pcase duration
((pred numberp) (run-at-time duration nil #'cider--delete-overlay o))
(`command
;; If inside a command-loop, tell `cider--remove-result-overlay'
;; to only remove after the *next* command.
(if this-command
(add-hook 'post-command-hook
#'cider--remove-result-overlay-after-command
nil 'local)
(cider--remove-result-overlay-after-command))))
(when-let ((win (get-buffer-window buffer)))
;; Left edge is visible.
(when (and (<= (window-start win) (point))
;; In 24.3 `<=' is still a binary perdicate.
(<= (point) (window-end win))
;; Right edge is visible. This is a little conservative
;; if the overlay contains line breaks.
(or (< (+ (current-column) (string-width value))
(window-width win))
(not truncate-lines)))
o)))))))
;;; Displaying eval result
(defun cider--display-interactive-eval-result (value &optional point)
"Display the result VALUE of an interactive eval operation.
VALUE is syntax-highlighted and displayed in the echo area.
If POINT and `cider-use-overlays' are non-nil, it is also displayed in an
overlay at the end of the line containing POINT.
Note that, while POINT can be a number, it's preferable to be a marker, as
that will better handle some corner cases where the original buffer is not
focused."
(let* ((font-value (if cider-result-use-clojure-font-lock
(cider-font-lock-as-clojure value)
value))
(used-overlay (when (and point cider-use-overlays)
(cider--make-result-overlay font-value
:where point
:duration cider-eval-result-duration))))
(message
"%s"
(propertize (format "%s%s" cider-eval-result-prefix font-value)
;; The following hides the message from the echo-area, but
;; displays it in the Messages buffer. We only hide the message
;; if the user wants to AND if the overlay succeeded.
'invisible (and used-overlay
(not (eq cider-use-overlays 'both)))))))
;;; Fragile buttons
(defface cider-fragile-button-face
'((((type graphic))
:box (:line-width 3 :style released-button)
:inherit font-lock-warning-face)
(t :inverse-video t))
"Face for buttons that vanish when clicked."
:package-version '(cider . "0.12.0")
:group 'cider)
(define-button-type 'cider-fragile
'action 'cider--overlay-destroy
'follow-link t
'face nil
'modification-hooks '(cider--overlay-destroy)
'help-echo "RET: delete this.")
(defun cider--overlay-destroy (ov &rest r)
"Delete overlay OV and its underlying text.
If any other arguments are given (collected in R), only actually do anything
if the first one is non-nil. This is so it works in `modification-hooks'."
(unless (and r (not (car r)))
(let ((inhibit-modification-hooks t)
(beg (copy-marker (overlay-start ov)))
(end (copy-marker (overlay-end ov))))
(delete-overlay ov)
(delete-region beg end)
(goto-char beg)
(when (= (char-after) (char-before) ?\n)
(delete-char 1)))))
(provide 'cider-overlays)
;;; cider-overlays.el ends here

View File

@ -1,12 +0,0 @@
(define-package "cider" "20160927.2135" "Clojure Interactive Development Environment that Rocks"
'((emacs "24.3")
(clojure-mode "5.5.2")
(pkg-info "0.4")
(queue "0.1.1")
(spinner "1.7")
(seq "2.16"))
:url "http://www.github.com/clojure-emacs/cider" :keywords
'("languages" "clojure" "cider"))
;; Local Variables:
;; no-byte-compile: t
;; End:

View File

@ -1,129 +0,0 @@
;;; cider-popup.el --- Creating and quitting popup buffers -*- lexical-binding: t; -*-
;; Copyright © 2015-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Common functionality for dealing with popup buffers.
;;; Code:
(require 'cider-compat)
(define-minor-mode cider-popup-buffer-mode
"Mode for CIDER popup buffers"
nil
(" cider-tmp")
'(("q" . cider-popup-buffer-quit-function)))
(defvar-local cider-popup-buffer-quit-function #'cider-popup-buffer-quit
"The function that is used to quit a temporary popup buffer.")
(defun cider-popup-buffer-quit-function (&optional kill-buffer-p)
"Wrapper to invoke the function `cider-popup-buffer-quit-function'.
KILL-BUFFER-P is passed along."
(interactive)
(funcall cider-popup-buffer-quit-function kill-buffer-p))
(defun cider-popup-buffer (name &optional select mode ancillary)
"Create new popup buffer called NAME.
If SELECT is non-nil, select the newly created window.
If major MODE is non-nil, enable it for the popup buffer.
If ANCILLARY is non-nil, the buffer is added to `cider-ancillary-buffers'
and automatically removed when killed."
(thread-first (cider-make-popup-buffer name mode ancillary)
(cider-popup-buffer-display select)))
(defun cider-popup-buffer-display (buffer &optional select)
"Display BUFFER.
If SELECT is non-nil, select the BUFFER."
(let ((window (get-buffer-window buffer 'visible)))
(when window
(with-current-buffer buffer
(set-window-point window (point))))
;; If the buffer we are popping up is already displayed in the selected
;; window, the below `inhibit-same-window' logic will cause it to be
;; displayed twice - so we early out in this case. Note that we must check
;; `selected-window', as async request handlers are executed in the context
;; of the current connection buffer (i.e. `current-buffer' is dynamically
;; bound to that).
(unless (eq window (selected-window))
;; Non nil `inhibit-same-window' ensures that current window is not covered
;; Non nil `inhibit-switch-frame' ensures that the other frame is not selected
;; if that's where the buffer is being shown.
(funcall (if select #'pop-to-buffer #'display-buffer)
buffer `(nil . ((inhibit-same-window . ,pop-up-windows)
(reusable-frames . visible))))))
buffer)
(defun cider-popup-buffer-quit (&optional kill)
"Quit the current (temp) window.
Bury its buffer using `quit-restore-window'.
If prefix argument KILL is non-nil, kill the buffer instead of burying it."
(interactive)
(quit-restore-window (selected-window) (if kill 'kill 'append)))
(defvar-local cider-popup-output-marker nil)
(defvar cider-ancillary-buffers nil)
(defun cider-make-popup-buffer (name &optional mode ancillary)
"Create a temporary buffer called NAME using major MODE (if specified).
If ANCILLARY is non-nil, the buffer is added to `cider-ancillary-buffers'
and automatically removed when killed."
(with-current-buffer (get-buffer-create name)
(kill-all-local-variables)
(setq buffer-read-only nil)
(erase-buffer)
(when mode
(funcall mode))
(cider-popup-buffer-mode 1)
(setq cider-popup-output-marker (point-marker))
(setq buffer-read-only t)
(when ancillary
(add-to-list 'cider-ancillary-buffers name)
(add-hook 'kill-buffer-hook
(lambda () (setq cider-ancillary-buffers (remove name cider-ancillary-buffers)))
nil 'local))
(current-buffer)))
(defun cider-emit-into-popup-buffer (buffer value &optional face)
"Emit into BUFFER the provided VALUE optionally using FACE."
;; Long string output renders Emacs unresponsive and users might intentionally
;; kill the frozen popup buffer. Therefore, we don't re-create the buffer and
;; silently ignore the output.
(when (buffer-live-p buffer)
(with-current-buffer buffer
(let ((inhibit-read-only t)
(buffer-undo-list t)
(moving (= (point) cider-popup-output-marker)))
(save-excursion
(goto-char cider-popup-output-marker)
(let ((value-str (format "%s" value)))
(when face
(if (fboundp 'add-face-text-property)
(add-face-text-property 0 (length value-str) face nil value-str)
(add-text-properties 0 (length value-str) (list 'face face) value-str)))
(insert value-str))
(indent-sexp)
(set-marker cider-popup-output-marker (point)))
(when moving (goto-char cider-popup-output-marker))))))
(provide 'cider-popup)
;;; cider-popup.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,129 +0,0 @@
;;; cider-resolve.el --- Resolve clojure symbols according to current nREPL connection
;; Copyright © 2015-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; The ns cache is a dict of namespaces stored in the connection buffer. This
;; file offers functions to easily get information about variables from this
;; cache, given the variable's name and the file's namespace. This
;; functionality is similar to that offered by the `cider-var-info' function
;; (and others). The difference is that all functions in this file operate
;; without contacting the server (they still rely on an active connection
;; buffer, but no messages are actually exchanged).
;; For this reason, the functions here are well suited for very
;; performance-sentitive operations, such as font-locking or
;; indentation. Meanwhile, operations like code-jumping are better off
;; communicating with the middleware, just in the off chance that the cache is
;; outdated.
;; Below is a typical entry on this cache dict. Note that clojure.core symbols
;; are excluded from the refers to save space.
;; "cider.nrepl.middleware.track-state"
;; (dict "aliases"
;; (dict "cljs" "cider.nrepl.middleware.util.cljs"
;; "misc" "cider.nrepl.middleware.util.misc"
;; "set" "clojure.set")
;; "interns" (dict a
;; "assoc-state" (dict "arglists"
;; (("response"
;; (dict "as" "msg" "keys"
;; ("session")))))
;; "filter-core" (dict "arglists"
;; (("refers")))
;; "make-transport" (dict "arglists"
;; (((dict "as" "msg" "keys"
;; ("transport")))))
;; "ns-as-map" (dict "arglists"
;; (("ns")))
;; "ns-cache" (dict)
;; "relevant-meta" (dict "arglists"
;; (("var")))
;; "update-vals" (dict "arglists"
;; (("m" "f")))
;; "wrap-tracker" (dict "arglists"
;; (("handler"))))
;; "refers" (dict "set-descriptor!" "#'clojure.tools.nrepl.middleware/set-descriptor!"))
;;; Code:
(require 'cider-client)
(require 'nrepl-dict)
(require 'cider-util)
(defvar cider-repl-ns-cache)
(defun cider-resolve--get-in (&rest keys)
"Return (nrepl-dict-get-in cider-repl-ns-cache KEYS)."
(when cider-connections
(with-current-buffer (cider-current-connection)
(nrepl-dict-get-in cider-repl-ns-cache keys))))
(defun cider-resolve-alias (ns alias)
"Return the namespace that ALIAS refers to in namespace NS.
If it doesn't point anywhere, returns ALIAS."
(or (cider-resolve--get-in ns "aliases" alias)
alias))
(defconst cider-resolve--prefix-regexp "\\`\\(?:#'\\)?\\([^/]+\\)/")
(defun cider-resolve-var (ns var)
"Return a dict of the metadata of a clojure var VAR in namespace NS.
VAR is a string.
Return nil only if VAR cannot be resolved."
(let* ((var-ns (when (string-match cider-resolve--prefix-regexp var)
(cider-resolve-alias ns (match-string 1 var))))
(name (replace-regexp-in-string cider-resolve--prefix-regexp "" var)))
(or
(cider-resolve--get-in (or var-ns ns) "interns" name)
(unless var-ns
;; If the var had no prefix, it might be referred.
(if-let ((referal (cider-resolve--get-in ns "refers" name)))
(cider-resolve-var ns referal)
;; Or it might be from core.
(unless (equal ns "clojure.core")
(cider-resolve-var "clojure.core" name)))))))
(defun cider-resolve-core-ns ()
"Return a dict of the core namespace for current connection.
This will be clojure.core or cljs.core depending on `cider-repl-type'."
(when (cider-connected-p)
(with-current-buffer (cider-current-connection)
(cider-resolve--get-in (if (equal cider-repl-type "cljs")
"cljs.core"
"clojure.core")))))
(defun cider-resolve-ns-symbols (ns)
"Return a plist of all valid symbols in NS.
Each entry's value is the metadata of the var that the symbol refers to.
NS can be the namespace name, or a dict of the namespace itself."
(when-let ((dict (if (stringp ns)
(cider-resolve--get-in ns)
ns)))
(nrepl-dbind-response dict (interns refers aliases)
(append (cdr interns)
(nrepl-dict-flat-map (lambda (alias namespace)
(nrepl-dict-flat-map (lambda (sym meta)
(list (concat alias "/" sym) meta))
(cider-resolve--get-in namespace "interns")))
aliases)))))
(provide 'cider-resolve)
;;; cider-resolve.el ends here

View File

@ -1,75 +0,0 @@
;;; cider-scratch.el --- *scratch* buffer for Clojure -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Bozhidar Batsov and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Imitate Emacs's *scratch* buffer.
;;; Code:
(require 'cider-interaction)
(require 'clojure-mode)
(defvar cider-clojure-interaction-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map clojure-mode-map)
(define-key map (kbd "C-j") #'cider-eval-print-last-sexp)
(define-key map [remap paredit-newline] #'cider-eval-print-last-sexp)
map))
(defconst cider-scratch-buffer-name "*cider-scratch*")
(push cider-scratch-buffer-name cider-ancillary-buffers)
;;;###autoload
(defun cider-scratch ()
"Go to the scratch buffer named `cider-scratch-buffer-name'."
(interactive)
(pop-to-buffer (cider-find-or-create-scratch-buffer)))
(defun cider-find-or-create-scratch-buffer ()
"Find or create the scratch buffer."
(or (get-buffer cider-scratch-buffer-name)
(cider-create-scratch-buffer)))
(define-derived-mode cider-clojure-interaction-mode clojure-mode "Clojure Interaction"
"Major mode for typing and evaluating Clojure forms.
Like clojure-mode except that \\[cider-eval-print-last-sexp] evals the Lisp expression
before point, and prints its value into the buffer, advancing point.
\\{cider-clojure-interaction-mode-map}")
(defun cider-create-scratch-buffer ()
"Create a new scratch buffer."
(with-current-buffer (get-buffer-create cider-scratch-buffer-name)
(cider-clojure-interaction-mode)
(insert ";; This buffer is for Clojure experiments and evaluation.\n"
";; Press C-j to evaluate the last expression.\n\n")
(current-buffer)))
(provide 'cider-scratch)
;;; cider-scratch.el ends here

View File

@ -1,167 +0,0 @@
;;; cider-selector.el --- Buffer selection command inspired by SLIME's selector -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Buffer selection command inspired by SLIME's selector.
;;; Code:
(require 'cider-client)
(require 'cider-interaction)
(require 'cider-scratch)
(defconst cider-selector-help-buffer "*CIDER Selector Help*"
"The name of the selector's help buffer.")
(defvar cider-selector-methods nil
"List of buffer-selection methods for the `cider-selector' command.
Each element is a list (KEY DESCRIPTION FUNCTION).
DESCRIPTION is a one-line description of what the key selects.")
(defvar cider-selector-other-window nil
"If non-nil use `switch-to-buffer-other-window'.")
(defun cider--recently-visited-buffer (mode)
"Return the most recently visited buffer, deriving its `major-mode' from MODE.
Only considers buffers that are not already visible."
(cl-loop for buffer in (buffer-list)
when (and (with-current-buffer buffer
(derived-mode-p mode))
;; names starting with space are considered hidden by Emacs
(not (string-match-p "^ " (buffer-name buffer)))
(null (get-buffer-window buffer 'visible)))
return buffer
finally (error "Can't find unshown buffer in %S" mode)))
;;;###autoload
(defun cider-selector (&optional other-window)
"Select a new buffer by type, indicated by a single character.
The user is prompted for a single character indicating the method by
which to choose a new buffer. The `?' character describes then
available methods. OTHER-WINDOW provides an optional target.
See `def-cider-selector-method' for defining new methods."
(interactive)
(message "Select [%s]: "
(apply #'string (mapcar #'car cider-selector-methods)))
(let* ((cider-selector-other-window other-window)
(ch (save-window-excursion
(select-window (minibuffer-window))
(read-char)))
(method (cl-find ch cider-selector-methods :key #'car)))
(cond (method
(funcall (cl-caddr method)))
(t
(message "No method for character: ?\\%c" ch)
(ding)
(sleep-for 1)
(discard-input)
(cider-selector)))))
(defmacro def-cider-selector-method (key description &rest body)
"Define a new `cider-select' buffer selection method.
KEY is the key the user will enter to choose this method.
DESCRIPTION is a one-line sentence describing how the method
selects a buffer.
BODY is a series of forms which are evaluated when the selector
is chosen. The returned buffer is selected with
`switch-to-buffer'."
(let ((method `(lambda ()
(let ((buffer (progn ,@body)))
(cond ((not (get-buffer buffer))
(message "No such buffer: %S" buffer)
(ding))
((get-buffer-window buffer)
(select-window (get-buffer-window buffer)))
(cider-selector-other-window
(switch-to-buffer-other-window buffer))
(t
(switch-to-buffer buffer)))))))
`(setq cider-selector-methods
(cl-sort (cons (list ,key ,description ,method)
(cl-remove ,key cider-selector-methods :key #'car))
#'< :key #'car))))
(def-cider-selector-method ?? "Selector help buffer."
(ignore-errors (kill-buffer cider-selector-help-buffer))
(with-current-buffer (get-buffer-create cider-selector-help-buffer)
(insert "CIDER Selector Methods:\n\n")
(cl-loop for (key line nil) in cider-selector-methods
do (insert (format "%c:\t%s\n" key line)))
(goto-char (point-min))
(help-mode)
(display-buffer (current-buffer) t))
(cider-selector)
(current-buffer))
(cl-pushnew (list ?4 "Select in other window" (lambda () (cider-selector t)))
cider-selector-methods :key #'car)
(def-cider-selector-method ?c
"Most recently visited clojure-mode buffer."
(cider--recently-visited-buffer 'clojure-mode))
(def-cider-selector-method ?e
"Most recently visited emacs-lisp-mode buffer."
(cider--recently-visited-buffer 'emacs-lisp-mode))
(def-cider-selector-method ?q "Abort."
(top-level))
(def-cider-selector-method ?r
"Current REPL buffer."
(cider-current-repl-buffer))
(def-cider-selector-method ?n
"Connections browser buffer."
(cider-connection-browser)
cider--connection-browser-buffer-name)
(def-cider-selector-method ?m
"Current connection's *nrepl-messages* buffer."
(cider-current-messages-buffer))
(def-cider-selector-method ?x
"*cider-error* buffer."
cider-error-buffer)
(def-cider-selector-method ?d
"*cider-doc* buffer."
cider-doc-buffer)
(declare-function cider-find-or-create-scratch-buffer "cider-scratch")
(def-cider-selector-method ?s
"*cider-scratch* buffer."
(cider-find-or-create-scratch-buffer))
(provide 'cider-selector)
;;; cider-selector.el ends here

View File

@ -1,716 +0,0 @@
;;; cider-stacktrace.el --- Stacktrace navigator -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Jeff Valk, Bozhidar Batsov and CIDER contributors
;; Author: Jeff Valk <jv@jeffvalk.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Stacktrace filtering and stack frame source navigation
;;; Code:
(require 'cl-lib)
(require 'cider-popup)
(require 'button)
(require 'easymenu)
(require 'cider-common)
(require 'cider-compat)
(require 'cider-client)
(require 'cider-util)
(require 'seq)
;; Variables
(defgroup cider-stacktrace nil
"Stacktrace filtering and navigation."
:prefix "cider-stacktrace-"
:group 'cider)
(defcustom cider-stacktrace-fill-column t
"Fill column for error messages in stacktrace display.
If nil, messages will not be wrapped. If truthy but non-numeric,
`fill-column' will be used."
:type 'list
:group 'cider-stacktrace
:package-version '(cider . "0.7.0"))
(defcustom cider-stacktrace-default-filters '(tooling dup)
"Frame types to omit from initial stacktrace display."
:type 'list
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defcustom cider-stacktrace-print-length 50
"Set the maximum length of sequences in displayed cause data.
This sets the value of Clojure's `*print-length*` when pretty printing the
`ex-data` map for exception causes in the stacktrace that are instances of
`IExceptionInfo`.
Be advised that setting this to `nil` will cause the attempted printing of
infinite data structures."
:type '(choice integer (const nil))
:group 'cider-stacktrace
:package-version '(cider . "0.9.0"))
(defcustom cider-stacktrace-print-level 50
"Set the maximum level of nesting in displayed cause data.
This sets the value of Clojure's `*print-level*` when pretty printing the
`ex-data` map for exception causes in the stacktrace that are instances of
`IExceptionInfo`.
Be advised that setting this to `nil` will cause the attempted printing of
cyclical data structures."
:type '(choice integer (const nil))
:group 'cider-stacktrace
:package-version '(cider . "0.8.0"))
(defvar cider-stacktrace-detail-max 2
"The maximum detail level for causes.")
(defvar-local cider-stacktrace-hidden-frame-count 0)
(defvar-local cider-stacktrace-filters nil)
(defvar-local cider-stacktrace-prior-filters nil)
(defvar-local cider-stacktrace-cause-visibility nil)
(defconst cider-error-buffer "*cider-error*")
(add-to-list 'cider-ancillary-buffers cider-error-buffer)
(defcustom cider-stacktrace-suppressed-errors '()
"A set of errors that won't make the stacktrace buffer 'pop-over' your active window.
The error types are represented as strings."
:type 'list
:group 'cider-stacktrace
:package-version '(cider . "0.12.0"))
;; Faces
(defface cider-stacktrace-error-class-face
'((t (:inherit font-lock-warning-face)))
"Face for exception class names"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-error-message-face
'((t (:inherit font-lock-doc-face)))
"Face for exception messages"
:group 'cider-stacktrace
:package-version '(cider . "0.7.0"))
(defface cider-stacktrace-filter-shown-face
'((t (:inherit button :underline t :weight normal)))
"Face for filter buttons representing frames currently visible"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-filter-hidden-face
'((t (:inherit button :underline nil :weight normal)))
"Face for filter buttons representing frames currently filtered out"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-face
'((t (:inherit default)))
"Face for stack frame text"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-ns-face
'((t (:inherit font-lock-comment-face)))
"Face for stack frame namespace name"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-fn-face
'((t (:inherit default :weight bold)))
"Face for stack frame function name"
:group 'cider-stacktrace
:package-version '(cider . "0.6.0"))
(defface cider-stacktrace-promoted-button-face
'((((type graphic))
:box (:line-width 3 :style released-button)
:inherit error)
(t :inverse-video t))
"A button with this face represents a promoted (non-suppressed) error type."
:group 'cider-stacktrace
:package-version '(cider . "0.12.0"))
(defface cider-stacktrace-suppressed-button-face
'((((type graphic))
:box (:line-width 3 :style pressed-button)
:inherit widget-inactive-face)
(t :inverse-video t))
"A button with this face represents a suppressed error type."
:group 'cider-stacktrace
:package-version '(cider . "0.12.0"))
;; Colors & Theme Support
(defvar cider-stacktrace-frames-background-color
(cider-scale-background-color)
"Background color for stacktrace frames.")
(defadvice enable-theme (after cider-stacktrace-adapt-to-theme activate)
"When theme is changed, update `cider-stacktrace-frames-background-color'."
(setq cider-stacktrace-frames-background-color (cider-scale-background-color)))
;; Mode & key bindings
(defvar cider-stacktrace-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "M-p") #'cider-stacktrace-previous-cause)
(define-key map (kbd "M-n") #'cider-stacktrace-next-cause)
(define-key map (kbd "M-.") #'cider-stacktrace-jump)
(define-key map "q" #'cider-popup-buffer-quit-function)
(define-key map "j" #'cider-stacktrace-toggle-java)
(define-key map "c" #'cider-stacktrace-toggle-clj)
(define-key map "r" #'cider-stacktrace-toggle-repl)
(define-key map "t" #'cider-stacktrace-toggle-tooling)
(define-key map "d" #'cider-stacktrace-toggle-duplicates)
(define-key map "a" #'cider-stacktrace-toggle-all)
(define-key map "1" #'cider-stacktrace-cycle-cause-1)
(define-key map "2" #'cider-stacktrace-cycle-cause-2)
(define-key map "3" #'cider-stacktrace-cycle-cause-3)
(define-key map "4" #'cider-stacktrace-cycle-cause-4)
(define-key map "5" #'cider-stacktrace-cycle-cause-5)
(define-key map "0" #'cider-stacktrace-cycle-all-causes)
(define-key map [tab] #'cider-stacktrace-cycle-current-cause)
(define-key map [backtab] #'cider-stacktrace-cycle-all-causes)
(easy-menu-define cider-stacktrace-mode-menu map
"Menu for CIDER's stacktrace mode"
'("Stacktrace"
["Previous cause" cider-stacktrace-previous-cause]
["Next cause" cider-stacktrace-next-cause]
"--"
["Jump to frame source" cider-stacktrace-jump]
"--"
["Cycle current cause detail" cider-stacktrace-cycle-current-cause]
["Cycle cause #1 detail" cider-stacktrace-cycle-cause-1]
["Cycle cause #2 detail" cider-stacktrace-cycle-cause-2]
["Cycle cause #3 detail" cider-stacktrace-cycle-cause-3]
["Cycle cause #4 detail" cider-stacktrace-cycle-cause-4]
["Cycle cause #5 detail" cider-stacktrace-cycle-cause-5]
["Cycle all cause detail" cider-stacktrace-cycle-all-causes]
"--"
["Show/hide Java frames" cider-stacktrace-toggle-java]
["Show/hide Clojure frames" cider-stacktrace-toggle-clj]
["Show/hide REPL frames" cider-stacktrace-toggle-repl]
["Show/hide tooling frames" cider-stacktrace-toggle-tooling]
["Show/hide duplicate frames" cider-stacktrace-toggle-duplicates]
["Show/hide all frames" cider-stacktrace-toggle-all]))
map))
(define-derived-mode cider-stacktrace-mode special-mode "Stacktrace"
"Major mode for filtering and navigating CIDER stacktraces.
\\{cider-stacktrace-mode-map}"
(setq buffer-read-only t)
(setq-local truncate-lines t)
(setq-local electric-indent-chars nil)
(setq-local cider-stacktrace-prior-filters nil)
(setq-local cider-stacktrace-hidden-frame-count 0)
(setq-local cider-stacktrace-filters cider-stacktrace-default-filters)
(setq-local cider-stacktrace-cause-visibility (make-vector 10 0)))
;; Stacktrace filtering
(defun cider-stacktrace-indicate-filters (filters)
"Update enabled state of filter buttons.
Find buttons with a 'filter property; if filter is a member of FILTERS, or
if filter is nil ('show all') and the argument list is non-nil, fontify the
button as disabled. Upon finding text with a 'hidden-count property, stop
searching and update the hidden count text."
(with-current-buffer cider-error-buffer
(save-excursion
(goto-char (point-min))
(let ((inhibit-read-only t)
(get-face (lambda (hide)
(if hide
'cider-stacktrace-filter-hidden-face
'cider-stacktrace-filter-shown-face))))
;; Toggle buttons
(while (not (or (get-text-property (point) 'hidden-count) (eobp)))
(let ((button (button-at (point))))
(when button
(let* ((filter (button-get button 'filter))
(face (funcall get-face (if filter
(member filter filters)
filters))))
(button-put button 'face face)))
(goto-char (or (next-property-change (point))
(point-max)))))
;; Update hidden count
(when (and (get-text-property (point) 'hidden-count)
(re-search-forward "[0-9]+" (line-end-position) t))
(replace-match
(number-to-string cider-stacktrace-hidden-frame-count)))))))
(defun cider-stacktrace-apply-filters (filters)
"Set visibility on stack frames using FILTERS.
Update `cider-stacktrace-hidden-frame-count' and indicate filters applied.
Currently collapsed stacktraces are ignored, and do not contribute to the
hidden count."
(with-current-buffer cider-error-buffer
(save-excursion
(goto-char (point-min))
(let ((inhibit-read-only t)
(hidden 0))
(while (not (eobp))
(unless (get-text-property (point) 'collapsed)
(let* ((flags (get-text-property (point) 'flags))
(hide (if (seq-intersection filters flags) t nil)))
(when hide (cl-incf hidden))
(put-text-property (point) (line-beginning-position 2) 'invisible hide)))
(forward-line 1))
(setq cider-stacktrace-hidden-frame-count hidden)))
(cider-stacktrace-indicate-filters filters)))
(defun cider-stacktrace-apply-cause-visibility ()
"Apply `cider-stacktrace-cause-visibility' to causes and reapply filters."
(with-current-buffer cider-error-buffer
(save-excursion
(goto-char (point-min))
(cl-flet ((next-detail (end)
(when-let ((pos (next-single-property-change (point) 'detail)))
(when (< pos end)
(goto-char pos)))))
(let ((inhibit-read-only t))
;; For each cause...
(while (cider-stacktrace-next-cause)
(let* ((num (get-text-property (point) 'cause))
(level (elt cider-stacktrace-cause-visibility num))
(cause-end (cadr (cider-property-bounds 'cause))))
;; For each detail level within the cause, set visibility.
(while (next-detail cause-end)
(let* ((detail (get-text-property (point) 'detail))
(detail-end (cadr (cider-property-bounds 'detail)))
(hide (if (> detail level) t nil)))
(add-text-properties (point) detail-end
(list 'invisible hide
'collapsed hide))))))))
(cider-stacktrace-apply-filters
cider-stacktrace-filters))))
;;; Internal/Middleware error suppression
(defun cider-stacktrace-some-suppressed-errors-p (error-types)
"Return intersection of ERROR-TYPES and CIDER-STACKTRACE-SUPPRESSED-ERRORS.
I.e, Return non-nil if the seq ERROR-TYPES shares any elements with
`cider-stacktrace-suppressed-errors'. This means that even a 'well-behaved' (ie,
promoted) error type will be 'guilty by association' if grouped with a
suppressed error type."
(seq-intersection error-types cider-stacktrace-suppressed-errors))
(defun cider-stacktrace-suppress-error (error-type)
"Destructively add element ERROR-TYPE to the `cider-stacktrace-suppressed-errors' set."
(setq cider-stacktrace-suppressed-errors
(cl-adjoin error-type cider-stacktrace-suppressed-errors :test 'equal)))
(defun cider-stacktrace-promote-error (error-type)
"Destructively remove element ERROR-TYPE from the `cider-stacktrace-suppressed-errors' set."
(setq cider-stacktrace-suppressed-errors
(remove error-type cider-stacktrace-suppressed-errors)))
(defun cider-stacktrace-suppressed-error-p (error-type)
"Return non-nil if element ERROR-TYPE is a member of the `cider-stacktrace-suppressed-errors' set."
(member error-type cider-stacktrace-suppressed-errors))
;; Interactive functions
(defun cider-stacktrace-previous-cause ()
"Move point to the previous exception cause, if one exists."
(interactive)
(with-current-buffer cider-error-buffer
(when-let ((pos (previous-single-property-change (point) 'cause)))
(goto-char pos))))
(defun cider-stacktrace-next-cause ()
"Move point to the next exception cause, if one exists."
(interactive)
(with-current-buffer cider-error-buffer
(when-let ((pos (next-single-property-change (point) 'cause)))
(goto-char pos))))
(defun cider-stacktrace-cycle-cause (num &optional level)
"Update element NUM of `cider-stacktrace-cause-visibility', optionally to LEVEL.
If LEVEL is not specified, its current value is incremented. When it reaches 3,
it wraps to 0."
(let ((level (or level (1+ (elt cider-stacktrace-cause-visibility num)))))
(aset cider-stacktrace-cause-visibility num (mod level 3))
(cider-stacktrace-apply-cause-visibility)))
(defun cider-stacktrace-cycle-all-causes ()
"Cycle the visibility of all exception causes."
(interactive)
(with-current-buffer cider-error-buffer
(save-excursion
;; Find nearest cause.
(unless (get-text-property (point) 'cause)
(cider-stacktrace-next-cause)
(unless (get-text-property (point) 'cause)
(cider-stacktrace-previous-cause)))
;; Cycle its level, and apply that to all causes.
(let* ((num (get-text-property (point) 'cause))
(level (1+ (elt cider-stacktrace-cause-visibility num))))
(setq-local cider-stacktrace-cause-visibility
(make-vector 10 (mod level 3)))
(cider-stacktrace-apply-cause-visibility)))))
(defun cider-stacktrace-cycle-current-cause ()
"Cycle the visibility of current exception at point, if any."
(interactive)
(with-current-buffer cider-error-buffer
(when-let ((num (get-text-property (point) 'cause)))
(cider-stacktrace-cycle-cause num))))
(defun cider-stacktrace-cycle-cause-1 ()
"Cycle the visibility of exception cause #1."
(interactive)
(cider-stacktrace-cycle-cause 1))
(defun cider-stacktrace-cycle-cause-2 ()
"Cycle the visibility of exception cause #2."
(interactive)
(cider-stacktrace-cycle-cause 2))
(defun cider-stacktrace-cycle-cause-3 ()
"Cycle the visibility of exception cause #3."
(interactive)
(cider-stacktrace-cycle-cause 3))
(defun cider-stacktrace-cycle-cause-4 ()
"Cycle the visibility of exception cause #4."
(interactive)
(cider-stacktrace-cycle-cause 4))
(defun cider-stacktrace-cycle-cause-5 ()
"Cycle the visibility of exception cause #5."
(interactive)
(cider-stacktrace-cycle-cause 5))
(defun cider-stacktrace-toggle-all ()
"Reset `cider-stacktrace-filters' if present; otherwise restore prior filters."
(interactive)
(when cider-stacktrace-filters
(setq-local cider-stacktrace-prior-filters
cider-stacktrace-filters))
(cider-stacktrace-apply-filters
(setq cider-stacktrace-filters
(unless cider-stacktrace-filters ; when current filters are nil,
cider-stacktrace-prior-filters)))) ; reenable prior filter set
(defun cider-stacktrace-toggle (flag)
"Update `cider-stacktrace-filters' to add or remove FLAG, and apply filters."
(cider-stacktrace-apply-filters
(setq cider-stacktrace-filters
(if (memq flag cider-stacktrace-filters)
(remq flag cider-stacktrace-filters)
(cons flag cider-stacktrace-filters)))))
(defun cider-stacktrace-toggle-java ()
"Toggle display of Java stack frames."
(interactive)
(cider-stacktrace-toggle 'java))
(defun cider-stacktrace-toggle-clj ()
"Toggle display of Clojure stack frames."
(interactive)
(cider-stacktrace-toggle 'clj))
(defun cider-stacktrace-toggle-repl ()
"Toggle display of REPL stack frames."
(interactive)
(cider-stacktrace-toggle 'repl))
(defun cider-stacktrace-toggle-tooling ()
"Toggle display of Tooling stack frames (compiler, nREPL middleware, etc)."
(interactive)
(cider-stacktrace-toggle 'tooling))
(defun cider-stacktrace-toggle-duplicates ()
"Toggle display of stack frames that are duplicates of their descendents."
(interactive)
(cider-stacktrace-toggle 'dup))
;; Text button functions
(defun cider-stacktrace-filter (button)
"Apply filter(s) indicated by the BUTTON."
(with-temp-message "Filters may also be toggled with the keyboard."
(let ((flag (button-get button 'filter)))
(if flag
(cider-stacktrace-toggle flag)
(cider-stacktrace-toggle-all)))
(sit-for 5)))
(defun cider-stacktrace-toggle-suppression (button)
"Toggle stacktrace pop-over/pop-under behavior for the `error-type' in BUTTON.
Achieved by destructively manipulating the `cider-stacktrace-suppressed-errors' set."
(with-current-buffer cider-error-buffer
(let ((inhibit-read-only t)
(suppressed (button-get button 'suppressed))
(error-type (button-get button 'error-type)))
(if suppressed
(progn
(cider-stacktrace-promote-error error-type)
(button-put button 'face 'cider-stacktrace-promoted-button-face)
(button-put button 'help-echo "Click to suppress these stacktraces."))
(cider-stacktrace-suppress-error error-type)
(button-put button 'face 'cider-stacktrace-suppressed-button-face)
(button-put button 'help-echo "Click to promote these stacktraces."))
(button-put button 'suppressed (not suppressed)))))
(defun cider-stacktrace-navigate (button)
"Navigate to the stack frame source represented by the BUTTON."
(let* ((var (button-get button 'var))
(class (button-get button 'class))
(method (button-get button 'method))
(info (or (and var (cider-var-info var))
(and class method (cider-member-info class method))
(nrepl-dict)))
;; Stacktrace returns more accurate line numbers, but if the function's
;; line was unreliable, then so is the stacktrace by the same amount.
;; Set `line-shift' to the number of lines from the beginning of defn.
(line-shift (- (or (button-get button 'line) 0)
(or (nrepl-dict-get info "line") 1)))
;; give priority to `info` files as `info` returns full paths.
(info (nrepl-dict-put info "file" (or (nrepl-dict-get info "file")
(button-get button 'file)))))
(cider--jump-to-loc-from-info info t)
(forward-line line-shift)
(back-to-indentation)))
(defun cider-stacktrace-jump (&optional arg)
"Find definition for stack frame at point, if available.
The prefix ARG and `cider-prompt-for-symbol' decide whether to
prompt and whether to use a new window. Similar to `cider-find-var'."
(interactive "P")
(let ((button (button-at (point))))
(if (and button (button-get button 'line))
(cider-stacktrace-navigate button)
(cider-find-var arg))))
;; Rendering
(defun cider-stacktrace-emit-indented (text indent &optional fill)
"Insert TEXT, and INDENT and optionally FILL the entire block."
(let ((beg (point)))
(insert text)
(goto-char beg)
(while (not (eobp))
(insert indent)
(forward-line))
(when (and fill cider-stacktrace-fill-column)
(when (and (numberp cider-stacktrace-fill-column))
(setq-local fill-column cider-stacktrace-fill-column))
(setq-local fill-prefix indent)
(fill-region beg (point)))))
(defun cider-stacktrace-render-filters (buffer filters)
"Emit into BUFFER toggle buttons for each of the FILTERS."
(with-current-buffer buffer
(insert " Show: ")
(dolist (filter filters)
(insert-text-button (car filter)
'filter (cadr filter)
'follow-link t
'action 'cider-stacktrace-filter
'help-echo (format "Toggle %s stack frames"
(car filter)))
(insert " "))
(let ((hidden "(0 frames hidden)"))
(put-text-property 0 (length hidden) 'hidden-count t hidden)
(insert " " hidden "\n"))))
(defun cider-stacktrace-render-suppression-toggle (buffer error-types)
"Emit into BUFFER toggle buttons for each of the ERROR-TYPES leading this stacktrace buffer."
(with-current-buffer buffer
(when error-types
(insert " This is an unexpected CIDER middleware error.\n Please submit a bug report via `")
(insert-text-button "M-x cider-report-bug"
'follow-link t
'action (lambda (_button) (cider-report-bug))
'help-echo "Report bug to the CIDER team.")
(insert "`.\n\n")
(insert "\
If these stacktraces are occuring frequently, consider using the
button(s) below to suppress these types of errors for the duration of
your current CIDER session. The stacktrace buffer will still be
generated, but it will \"pop under\" your current buffer instead of
\"popping over\". The button toggles this behavior.\n\n ")
(dolist (error-type error-types)
(let ((suppressed (cider-stacktrace-suppressed-error-p error-type)))
(insert-text-button (format "%s %s" (if suppressed "Promote" "Suppress") error-type)
'follow-link t
'error-type error-type
'action 'cider-stacktrace-toggle-suppression
'suppressed suppressed
'face (if suppressed
'cider-stacktrace-suppressed-button-face
'cider-stacktrace-promoted-button-face)
'help-echo (format "Click to %s these stacktraces."
(if suppressed "promote" "suppress"))))
(insert " ")))))
(defun cider-stacktrace-render-frame (buffer frame)
"Emit into BUFFER function call site info for the stack FRAME.
This associates text properties to enable filtering and source navigation."
(with-current-buffer buffer
(nrepl-dbind-response frame (file line flags class method name var ns fn)
(let ((flags (mapcar 'intern flags))) ; strings -> symbols
(insert-text-button (format "%26s:%5d %s/%s"
(if (member 'repl flags) "REPL" file) line
(if (member 'clj flags) ns class)
(if (member 'clj flags) fn method))
'var var 'class class 'method method
'name name 'file file 'line line
'flags flags 'follow-link t
'action 'cider-stacktrace-navigate
'help-echo "View source at this location"
'font-lock-face 'cider-stacktrace-face
'type 'cider-plain-button)
(save-excursion
(let ((p4 (point))
(p1 (search-backward " "))
(p2 (search-forward "/"))
(p3 (search-forward-regexp "[^/$]+")))
(put-text-property p1 p4 'font-lock-face 'cider-stacktrace-ns-face)
(put-text-property p2 p3 'font-lock-face 'cider-stacktrace-fn-face)))
(insert "\n")))))
(declare-function cider-jump-to "cider-interaction")
(defun cider-stacktrace-render-compile-error (buffer cause)
"Emit into BUFFER the compile error CAUSE, and enable jumping to it."
(with-current-buffer buffer
(nrepl-dbind-response cause (file path line column)
(let ((indent " ")
(message-face 'cider-stacktrace-error-message-face))
(insert indent)
(insert (propertize "Error compiling " 'font-lock-face message-face))
(insert-text-button path 'compile-error t
'file file 'line line 'column column 'follow-link t
'action (lambda (_button)
(cider-jump-to (cider-find-file file)
(cons line column))))
(insert (propertize (format " at (%d:%d)" line column)
'font-lock-face message-face))))))
(defun cider-stacktrace-render-cause (buffer cause num note)
"Emit into BUFFER the CAUSE NUM, exception class, message, data, and NOTE."
(with-current-buffer buffer
(nrepl-dbind-response cause (class message data stacktrace)
(let ((indent " ")
(class-face 'cider-stacktrace-error-class-face)
(message-face 'cider-stacktrace-error-message-face))
(cider-propertize-region `(cause ,num)
;; Detail level 0: exception class
(cider-propertize-region '(detail 0)
(insert (format "%d. " num)
(propertize note 'font-lock-face 'font-lock-comment-face) " "
(propertize class 'font-lock-face class-face)
"\n"))
;; Detail level 1: message + ex-data
(cider-propertize-region '(detail 1)
(if (equal class "clojure.lang.Compiler$CompilerException")
(cider-stacktrace-render-compile-error buffer cause)
(cider-stacktrace-emit-indented
(propertize (or message "(No message)")
'font-lock-face message-face) indent t))
(insert "\n")
(when data
(cider-stacktrace-emit-indented
(cider-font-lock-as-clojure data) indent nil)))
;; Detail level 2: stacktrace
(cider-propertize-region '(detail 2)
(insert "\n")
(let ((beg (point))
(bg `(:background ,cider-stacktrace-frames-background-color)))
(dolist (frame stacktrace)
(cider-stacktrace-render-frame buffer frame))
(overlay-put (make-overlay beg (point)) 'font-lock-face bg)))
;; Add line break between causes, even when collapsed.
(cider-propertize-region '(detail 0)
(insert "\n")))))))
(defun cider-stacktrace-initialize (causes)
"Set and apply CAUSES initial visibility, filters, and cursor position."
(nrepl-dbind-response (car causes) (class)
(let ((compile-error-p (equal class "clojure.lang.Compiler$CompilerException")))
;; Partially display outermost cause if it's a compiler exception (the
;; description reports reader location of the error).
(when compile-error-p
(cider-stacktrace-cycle-cause (length causes) 1))
;; Fully display innermost cause. This also applies visibility/filters.
(cider-stacktrace-cycle-cause 1 cider-stacktrace-detail-max)
;; Move point (DWIM) to the compile error location if present, or to the
;; first stacktrace frame in displayed cause otherwise. If the error
;; buffer is visible in a window, ensure that window is selected while moving
;; point, so as to move both the buffer's and the window's point.
(with-selected-window (or (get-buffer-window cider-error-buffer)
(selected-window))
(with-current-buffer cider-error-buffer
(goto-char (point-min))
(if compile-error-p
(goto-char (next-single-property-change (point) 'compile-error))
(progn
(while (cider-stacktrace-next-cause))
(goto-char (next-single-property-change (point) 'flags)))))))))
(defun cider-stacktrace-render (buffer causes &optional error-types)
"Emit into BUFFER useful stacktrace information for the CAUSES.
Takes an optional ERROR-TYPES list which will render a 'suppression' toggle
that alters the pop-over/pop-under behavorior of the stacktrace buffers
created by these types of errors. The suppressed errors set can be customized
through the `cider-stacktrace-suppressed-errors' variable."
(with-current-buffer buffer
(let ((inhibit-read-only t))
(erase-buffer)
(insert "\n")
;; Stacktrace filters
(cider-stacktrace-render-filters
buffer
`(("Clojure" clj) ("Java" java) ("REPL" repl)
("Tooling" tooling) ("Duplicates" dup) ("All" ,nil)))
(insert "\n")
;; Option to suppress internal/middleware errors
(when error-types
(cider-stacktrace-render-suppression-toggle buffer error-types)
(insert "\n\n"))
;; Stacktrace exceptions & frames
(let ((num (length causes)))
(dolist (cause causes)
(let ((note (if (= num (length causes)) "Unhandled" "Caused by")))
(cider-stacktrace-render-cause buffer cause num note)
(setq num (1- num))))))
(cider-stacktrace-initialize causes)
(font-lock-refresh-defaults)))
(provide 'cider-stacktrace)
;;; cider-stacktrace.el ends here

View File

@ -1,690 +0,0 @@
;;; cider-test.el --- Test result viewer -*- lexical-binding: t -*-
;; Copyright © 2014-2016 Jeff Valk, Bozhidar Batsov and CIDER contributors
;; Author: Jeff Valk <jv@jeffvalk.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; This provides execution, reporting, and navigation support for Clojure tests,
;; specifically using the `clojure.test' machinery. This functionality replaces
;; the venerable `clojure-test-mode' (deprecated in June 2014), and relies on
;; nREPL middleware for report running and session support.
;;; Code:
(require 'cider-common)
(require 'cider-client)
(require 'cider-popup)
(require 'cider-stacktrace)
(require 'cider-compat)
(require 'cider-overlays)
(require 'button)
(require 'easymenu)
(require 'seq)
;;; Variables
(defgroup cider-test nil
"Presentation and navigation for test results."
:prefix "cider-test-"
:group 'cider)
(defcustom cider-test-show-report-on-success nil
"Whether to show the `*cider-test-report*` buffer on passing tests."
:type 'boolean
:group 'cider-test
:package-version '(cider . "0.8.0"))
(defcustom cider-auto-select-test-report-buffer t
"Determines if the test-report buffer should be auto-selected."
:type 'boolean
:group 'cider-test
:package-version '(cider . "0.9.0"))
(defvar cider-test-last-summary nil
"The summary of the last run test.")
(defvar cider-test-last-results nil
"The results of the last run test.")
(defconst cider-test-report-buffer "*cider-test-report*"
"Buffer name in which to display test reports.")
(add-to-list 'cider-ancillary-buffers cider-test-report-buffer)
;;; Faces
(defface cider-test-failure-face
'((((class color) (background light))
:background "orange red")
(((class color) (background dark))
:background "firebrick"))
"Face for failed tests."
:group 'cider-test
:package-version '(cider . "0.7.0"))
(defface cider-test-error-face
'((((class color) (background light))
:background "orange1")
(((class color) (background dark))
:background "orange4"))
"Face for erring tests."
:group 'cider-test
:package-version '(cider . "0.7.0"))
(defface cider-test-success-face
'((((class color) (background light))
:foreground "black"
:background "green")
(((class color) (background dark))
:foreground "black"
:background "green"))
"Face for passing tests."
:group 'cider-test
:package-version '(cider . "0.7.0"))
;; Colors & Theme Support
(defvar cider-test-items-background-color
(cider-scale-background-color)
"Background color for test assertion items.")
(defadvice enable-theme (after cider-test-adapt-to-theme activate)
"When theme is changed, update `cider-test-items-background-color'."
(setq cider-test-items-background-color (cider-scale-background-color)))
;;; Report mode & key bindings
;; The primary mode of interacting with test results is the report buffer, which
;; allows navigation among tests, jumping to test definitions, expected/actual
;; diff-ing, and cause/stacktrace inspection for test errors.
(defvar cider-test-commands-map
(let ((map (define-prefix-command 'cider-test-commands-map)))
;; Duplicates of keys below with C- for convenience
(define-key map (kbd "C-r") #'cider-test-rerun-tests)
(define-key map (kbd "C-t") #'cider-test-run-test)
(define-key map (kbd "C-n") #'cider-test-run-ns-tests)
(define-key map (kbd "C-l") #'cider-test-run-loaded-tests)
(define-key map (kbd "C-p") #'cider-test-run-project-tests)
(define-key map (kbd "C-b") #'cider-test-show-report)
;; Single-key bindings defined last for display in menu
(define-key map (kbd "r") #'cider-test-rerun-tests)
(define-key map (kbd "t") #'cider-test-run-test)
(define-key map (kbd "n") #'cider-test-run-ns-tests)
(define-key map (kbd "l") #'cider-test-run-loaded-tests)
(define-key map (kbd "p") #'cider-test-run-project-tests)
(define-key map (kbd "b") #'cider-test-show-report)
map))
(defconst cider-test-menu
'("Test"
["Run test" cider-test-run-test]
["Run namespace tests" cider-test-run-ns-tests]
["Run all loaded tests" cider-test-run-loaded-tests]
["Run all project tests" cider-test-run-project-tests]
["Run tests after load-file" cider-auto-test-mode
:style toggle :selected cider-auto-test-mode]
"--"
["Interrupt running tests" cider-interrupt]
["Rerun failed/erring tests" cider-test-rerun-tests]
["Show test report" cider-test-show-report]
"--"
["Configure testing" (customize-group 'cider-test)])
"CIDER test submenu.")
(defvar cider-test-report-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c ,") 'cider-test-commands-map)
(define-key map (kbd "C-c C-t") 'cider-test-commands-map)
(define-key map (kbd "M-p") #'cider-test-previous-result)
(define-key map (kbd "M-n") #'cider-test-next-result)
(define-key map (kbd "M-.") #'cider-test-jump)
(define-key map (kbd "<backtab>") #'cider-test-previous-result)
(define-key map (kbd "TAB") #'cider-test-next-result)
(define-key map (kbd "RET") #'cider-test-jump)
(define-key map (kbd "t") #'cider-test-jump)
(define-key map (kbd "d") #'cider-test-ediff)
(define-key map (kbd "e") #'cider-test-stacktrace)
;; `f' for "run failed".
(define-key map "f" #'cider-test-rerun-tests)
;; `g' generally reloads the buffer. The closest thing we have to that is
;; "run the test at point". But it's not as nice as rerunning all tests in
;; this buffer.
(define-key map "g" #'cider-test-run-test)
(define-key map "q" #'cider-popup-buffer-quit-function)
(easy-menu-define cider-test-report-mode-menu map
"Menu for CIDER's test result mode"
'("Test-Report"
["Previous result" cider-test-previous-result]
["Next result" cider-test-next-result]
"--"
["Rerun current test" cider-test-run-test]
["Rerun failed/erring tests" cider-test-rerun-tests]
["Run all loaded tests" cider-test-run-loaded-tests]
["Run all project tests" cider-test-run-project-tests]
"--"
["Jump to test definition" cider-test-jump]
["Display test error" cider-test-stacktrace]
["Display expected/actual diff" cider-test-ediff]))
map))
(define-derived-mode cider-test-report-mode fundamental-mode "Test Report"
"Major mode for presenting Clojure test results.
\\{cider-test-report-mode-map}"
(setq buffer-read-only t)
(setq-local truncate-lines t)
(setq-local electric-indent-chars nil))
;; Report navigation
(defun cider-test-show-report ()
"Show the test report buffer, if one exists."
(interactive)
(if-let ((report-buffer (get-buffer cider-test-report-buffer)))
(switch-to-buffer report-buffer)
(message "No test report buffer")))
(defun cider-test-previous-result ()
"Move point to the previous test result, if one exists."
(interactive)
(with-current-buffer (get-buffer cider-test-report-buffer)
(when-let ((pos (previous-single-property-change (point) 'type)))
(if (get-text-property pos 'type)
(goto-char pos)
(when-let ((pos (previous-single-property-change pos 'type)))
(goto-char pos))))))
(defun cider-test-next-result ()
"Move point to the next test result, if one exists."
(interactive)
(with-current-buffer (get-buffer cider-test-report-buffer)
(when-let ((pos (next-single-property-change (point) 'type)))
(if (get-text-property pos 'type)
(goto-char pos)
(when-let ((pos (next-single-property-change pos 'type)))
(goto-char pos))))))
(defun cider-test-jump (&optional arg)
"Find definition for test at point, if available.
The prefix ARG and `cider-prompt-for-symbol' decide whether to
prompt and whether to use a new window. Similar to `cider-find-var'."
(interactive "P")
(let ((ns (get-text-property (point) 'ns))
(var (get-text-property (point) 'var))
(line (get-text-property (point) 'line)))
(if (and ns var)
(cider-find-var arg (concat ns "/" var) line)
(cider-find-var arg))))
;;; Error stacktraces
(defvar cider-auto-select-error-buffer)
(defun cider-test-stacktrace-for (ns var index)
"Display stacktrace for the erring NS VAR test with the assertion INDEX."
(let (causes)
(cider-nrepl-send-request
(append
(list "op" "test-stacktrace" "session" (cider-current-session)
"ns" ns "var" var "index" index)
(when (cider--pprint-fn)
(list "pprint-fn" (cider--pprint-fn)))
(when cider-stacktrace-print-length
(list "print-length" cider-stacktrace-print-length))
(when cider-stacktrace-print-level
(list "print-level" cider-stacktrace-print-level)))
(lambda (response)
(nrepl-dbind-response response (class status)
(cond (class (setq causes (cons response causes)))
(status (when causes
(cider-stacktrace-render
(cider-popup-buffer cider-error-buffer
cider-auto-select-error-buffer
#'cider-stacktrace-mode)
(reverse causes))))))))))
(defun cider-test-stacktrace ()
"Display stacktrace for the erring test at point."
(interactive)
(let ((ns (get-text-property (point) 'ns))
(var (get-text-property (point) 'var))
(index (get-text-property (point) 'index))
(err (get-text-property (point) 'error)))
(if (and err ns var index)
(cider-test-stacktrace-for ns var index)
(message "No test error at point"))))
;;; Expected vs actual diffing
(defvar cider-test-ediff-buffers nil
"The expected/actual buffers used to display diff.")
(defun cider-test-ediff ()
"Show diff of the expected vs actual value for the test at point.
With the actual value, the outermost '(not ...)' s-expression is removed."
(interactive)
(let ((expected (get-text-property (point) 'expected))
(actual (get-text-property (point) 'actual)))
(if (and expected actual)
(let ((expected-buffer (generate-new-buffer " *expected*"))
(actual-buffer (generate-new-buffer " *actual*")))
(with-current-buffer expected-buffer
(insert expected)
(clojure-mode))
(with-current-buffer actual-buffer
(insert actual)
(goto-char (point-min))
(forward-char)
(forward-sexp)
(forward-whitespace 1)
(let ((beg (point)))
(forward-sexp)
(let ((actual* (buffer-substring beg (point))))
(erase-buffer)
(insert actual*)))
(clojure-mode))
(apply 'ediff-buffers
(setq cider-test-ediff-buffers
(list (buffer-name expected-buffer)
(buffer-name actual-buffer)))))
(message "No test failure at point"))))
(defun cider-test-ediff-cleanup ()
"Cleanup expected/actual buffers used for diff."
(interactive)
(mapc (lambda (b) (when (get-buffer b) (kill-buffer b)))
cider-test-ediff-buffers))
(add-hook 'ediff-cleanup-hook #'cider-test-ediff-cleanup)
;;; Report rendering
(defun cider-test-type-face (type)
"Return the font lock face for the test result TYPE."
(pcase type
("pass" 'cider-test-success-face)
("fail" 'cider-test-failure-face)
("error" 'cider-test-error-face)
(_ 'default)))
(defun cider-test-type-simple-face (type)
"Return a face for the test result TYPE using the highlight color as foreground."
(let ((face (cider-test-type-face type)))
`(:foreground ,(face-attribute face :background))))
(defun cider-test-render-summary (buffer summary)
"Emit into BUFFER the report SUMMARY statistics."
(with-current-buffer buffer
(nrepl-dbind-response summary (ns var test pass fail error)
(insert (format "Tested %d namespaces\n" ns))
(insert (format "Ran %d assertions, in %d test functions\n" test var))
(unless (zerop fail)
(cider-insert (format "%d failures" fail) 'cider-test-failure-face t))
(unless (zerop error)
(cider-insert (format "%d errors" error) 'cider-test-error-face t))
(when (zerop (+ fail error))
(cider-insert (format "%d passed" pass) 'cider-test-success-face t))
(insert "\n\n"))))
(defun cider-test-render-assertion (buffer test)
"Emit into BUFFER report detail for the TEST assertion."
(with-current-buffer buffer
(nrepl-dbind-response test (var context type message expected actual error gen-input)
(cider-propertize-region (cider-intern-keys (cdr test))
(let ((beg (point))
(type-face (cider-test-type-simple-face type))
(bg `(:background ,cider-test-items-background-color)))
(cider-insert (capitalize type) type-face nil " in ")
(cider-insert var 'font-lock-function-name-face t)
(when context (cider-insert context 'font-lock-doc-face t))
(when message (cider-insert message 'font-lock-doc-string-face t))
(when expected
(cider-insert "expected: " 'font-lock-comment-face nil
(cider-font-lock-as-clojure expected)))
(when actual
(cider-insert " actual: " 'font-lock-comment-face nil
(cider-font-lock-as-clojure actual)))
(when error
(cider-insert " error: " 'font-lock-comment-face nil)
(insert-text-button error
'follow-link t
'action '(lambda (_button) (cider-test-stacktrace))
'help-echo "View causes and stacktrace")
(insert "\n"))
(when gen-input
(cider-insert " input: " 'font-lock-comment-face nil
(cider-font-lock-as-clojure gen-input)))
(overlay-put (make-overlay beg (point)) 'font-lock-face bg))
(insert "\n")))))
(defun cider-test-non-passing (tests)
"For a list of TESTS, each an nrepl-dict, return only those that did not pass."
(seq-filter (lambda (test)
(unless (equal (nrepl-dict-get test "type") "pass")
test))
tests))
(defun cider-test-render-report (buffer summary results)
"Emit into BUFFER the report for the SUMMARY, and test RESULTS."
(with-current-buffer buffer
(let ((inhibit-read-only t))
(cider-test-report-mode)
(cider-insert "Test Summary" 'bold t)
(dolist (ns (nrepl-dict-keys results))
(insert (cider-propertize ns 'ns) "\n"))
(cider-insert "\n")
(cider-test-render-summary buffer summary)
(nrepl-dbind-response summary (fail error)
(unless (zerop (+ fail error))
(cider-insert "Results" 'bold t "\n")
;; Results are a nested dict, keyed first by ns, then var. Within each
;; var is a sequence of test assertion results.
(nrepl-dict-map
(lambda (ns vars)
(nrepl-dict-map
(lambda (_var tests)
(let* ((problems (cider-test-non-passing tests))
(count (length problems)))
(when (< 0 count)
(insert (format "%s\n%d non-passing tests:\n\n"
(cider-propertize ns 'ns) count))
(dolist (test problems)
(cider-test-render-assertion buffer test)))))
vars))
results)))
(goto-char (point-min))
(current-buffer))))
;;; Message echo
(defun cider-test-echo-running (ns &optional test)
"Echo a running message for the test NS, which may be a keyword.
The optional arg TEST denotes an individual test name."
(if test
(message "Running test %s in %s..."
(cider-propertize test 'bold)
(cider-propertize ns 'ns))
(message "Running tests in %s..."
(concat (cider-propertize
(cond ((stringp ns) ns)
((eq :non-passing ns) "failing")
((eq :loaded ns) "all loaded")
((eq :project ns) "all project"))
'ns)
(unless (stringp ns) " namespaces")))))
(defun cider-test-echo-summary (summary results)
"Echo SUMMARY statistics for a test run returning RESULTS."
(nrepl-dbind-response summary (ns test var fail error)
(if (nrepl-dict-empty-p results)
(message (concat (propertize "No assertions (or no tests) were run." 'face 'cider-test-error-face)
"Did you forget to use `is' in your tests?"))
(message (propertize
"%sRan %d assertions, in %d test functions. %d failures, %d errors."
'face (cond ((not (zerop error)) 'cider-test-error-face)
((not (zerop fail)) 'cider-test-failure-face)
(t 'cider-test-success-face)))
(concat (if (= 1 ns) ; ns count from summary
(cider-propertize (car (nrepl-dict-keys results)) 'ns)
(propertize (format "%d namespaces" ns) 'face 'default))
(propertize ": " 'face 'default))
test var fail error))))
;;; Test definition highlighting
;; On receipt of test results, failing/erring test definitions are highlighted.
;; Highlights are cleared on the next report run, and may be cleared manually
;; by the user.
;; NOTE If keybindings specific to test sources are desired, it would be
;; straightforward to turn this into a `cider-test-mode' minor mode, which we
;; enable on test sources, much like the legacy `clojure-test-mode'. At present,
;; though, there doesn't seem to be much value in this, since the report buffer
;; provides the primary means of interacting with test results.
(defun cider-test-highlight-problem (buffer test)
"Highlight the BUFFER test definition for the non-passing TEST."
(with-current-buffer buffer
(nrepl-dbind-response test (type file line message expected actual)
;; we have to watch out for vars without proper location metadata
;; right now everything evaluated interactively lacks this data
;; TODO: Figure out what to do when the metadata is missing
(when (and file line (not (cider--tooling-file-p file)))
(save-excursion
(goto-char (point-min))
(forward-line (1- line))
(search-forward "(" nil t)
(let ((beg (point)))
(forward-sexp)
(cider--make-overlay beg (point) 'cider-test
'font-lock-face (cider-test-type-face type)
'type type
'help-echo message
'message message
'expected expected
'actual actual)))))))
(defun cider-find-var-file (ns var)
"Return the buffer visiting the file in which the NS VAR is defined.
Or nil if not found."
(cider-ensure-op-supported "info")
(when-let ((info (cider-var-info (concat ns "/" var)))
(file (nrepl-dict-get info "file")))
(cider-find-file file)))
(defun cider-test-highlight-problems (results)
"Highlight all non-passing tests in the test RESULTS."
(nrepl-dict-map
(lambda (ns vars)
(nrepl-dict-map
(lambda (var tests)
(when-let ((buffer (cider-find-var-file ns var)))
(dolist (test tests)
(nrepl-dbind-response test (type)
(unless (equal "pass" type)
(cider-test-highlight-problem buffer test))))))
vars))
results))
(defun cider-test-clear-highlights ()
"Clear highlighting of non-passing tests from the last test run."
(interactive)
(when cider-test-last-results
(nrepl-dict-map
(lambda (ns vars)
(dolist (var (nrepl-dict-keys vars))
(when-let ((buffer (cider-find-var-file ns var)))
(with-current-buffer buffer
(remove-overlays nil nil 'category 'cider-test)))))
cider-test-last-results)))
;;; Test namespaces
;; Test namespace inference exists to enable DWIM test running functions: the
;; same "run-tests" function should be able to be used in a source file, and in
;; its corresponding test namespace. To provide this, we need to map the
;; relationship between those namespaces.
(defcustom cider-test-infer-test-ns 'cider-test-default-test-ns-fn
"Function to infer the test namespace for NS.
The default implementation uses the simple Leiningen convention of appending
'-test' to the namespace name."
:type 'symbol
:group 'cider-test
:package-version '(cider . "0.7.0"))
(defun cider-test-default-test-ns-fn (ns)
"For a NS, return the test namespace, which may be the argument itself.
This uses the Leiningen convention of appending '-test' to the namespace name."
(when ns
(let ((suffix "-test"))
;; string-suffix-p is only available in Emacs 24.4+
(if (string-match-p (rx-to-string `(: ,suffix eos) t) ns)
ns
(concat ns suffix)))))
;;; Test execution
(declare-function cider-emit-interactive-eval-output "cider-interaction")
(declare-function cider-emit-interactive-eval-err-output "cider-interaction")
(defun cider-test-execute (ns &optional tests silent)
"Run tests for NS, which may be a keyword, optionally specifying TESTS.
This tests a single NS, or multiple namespaces when using keywords `:project',
`:loaded' or `:non-passing'. Optional TESTS are only honored when a single
namespace is specified. Upon test completion, results are echoed and a test
report is optionally displayed. When test failures/errors occur, their sources
are highlighted.
If SILENT is non-nil, suppress all messages other then test results."
(cider-test-clear-highlights)
(cider-map-connections
(lambda (conn)
(unless silent
(if (and tests (= (length tests) 1))
;; we generate a different message when running individual tests
(cider-test-echo-running ns (car tests))
(cider-test-echo-running ns)))
(cider-nrepl-send-request
(list "op" (cond ((stringp ns) "test")
((eq :project ns) "test-all")
((eq :loaded ns) "test-all")
((eq :non-passing ns) "retest"))
"ns" (when (stringp ns) ns)
"tests" (when (stringp ns) tests)
"load?" (when (or (stringp ns)
(eq :project ns))
"true")
"session" (cider-current-session))
(lambda (response)
(nrepl-dbind-response response (summary results status out err)
(cond ((member "namespace-not-found" status)
(unless silent
(message "No test namespace: %s" (cider-propertize ns 'ns))))
(out (cider-emit-interactive-eval-output out))
(err (cider-emit-interactive-eval-err-output err))
(results
(nrepl-dbind-response summary (error fail)
(setq cider-test-last-summary summary)
(setq cider-test-last-results results)
(cider-test-highlight-problems results)
(cider-test-echo-summary summary results)
(if (or (not (zerop (+ error fail)))
cider-test-show-report-on-success)
(cider-test-render-report
(cider-popup-buffer cider-test-report-buffer
cider-auto-select-test-report-buffer)
summary results)
(when (get-buffer cider-test-report-buffer)
(with-current-buffer cider-test-report-buffer
(let ((inhibit-read-only t))
(erase-buffer)))
(cider-test-render-report
cider-test-report-buffer
summary results))))))))
conn))
:clj))
(defun cider-test-rerun-tests ()
"Rerun failed and erring tests from the last test run."
(interactive)
(if cider-test-last-summary
(nrepl-dbind-response cider-test-last-summary (fail error)
(if (not (zerop (+ error fail)))
(cider-test-execute :non-passing)
(message "No prior failures to retest")))
(message "No prior results to retest")))
(defun cider-test-run-loaded-tests ()
"Run all tests defined in currently loaded namespaces."
(interactive)
(cider-test-execute :loaded))
(defun cider-test-run-project-tests ()
"Run all tests defined in all project namespaces, loading these as needed."
(interactive)
(cider-test-execute :project))
(defun cider-test-run-ns-tests (suppress-inference &optional silent)
"Run all tests for the current Clojure namespace context.
If SILENT is non-nil, suppress all messages other then test results.
With a prefix arg SUPPRESS-INFERENCE it will try to run the tests in the
current ns."
(interactive "P")
(if-let ((ns (if suppress-inference
(cider-current-ns t)
(funcall cider-test-infer-test-ns (cider-current-ns t)))))
(cider-test-execute ns nil silent)
(if (eq major-mode 'cider-test-report-mode)
(when (y-or-n-p (concat "Test report does not define a namespace. "
"Rerun failed/erring tests?"))
(cider-test-rerun-tests))
(unless silent
(message "No namespace to test in current context")))))
(defun cider-test-run-test ()
"Run the test at point.
The test ns/var exist as text properties on report items and on highlighted
failed/erred test definitions. When not found, a test definition at point
is searched."
(interactive)
(let ((ns (get-text-property (point) 'ns))
(var (get-text-property (point) 'var)))
(if (and ns var)
(cider-test-execute ns (list var))
(let ((ns (clojure-find-ns))
(def (clojure-find-def)))
(if (and ns (member (car def) '("deftest" "defspec")))
(cider-test-execute ns (cdr def))
(message "No test at point"))))))
;;; Auto-test mode
(defun cider--test-silently ()
"Like `cider-test-run-tests', but with less feedback.
Only notify the user if there actually were any tests to run and only after
the results are received."
(when (cider-connected-p)
(let ((cider-auto-select-test-report-buffer nil)
(cider-test-show-report-on-success nil))
(cider-test-run-ns-tests nil 'soft))))
;;;###autoload
(define-minor-mode cider-auto-test-mode
"Toggle automatic testing of Clojure files.
When enabled this reruns tests every time a Clojure file is loaded.
Only runs tests corresponding to the loaded file's namespace and does
nothing if no tests are defined or if the file failed to load."
nil (cider-mode " Test") nil
:global t
(if cider-auto-test-mode
(add-hook 'cider-file-loaded-hook #'cider--test-silently)
(remove-hook 'cider-file-loaded-hook #'cider--test-silently)))
(provide 'cider-test)
;;; cider-test.el ends here

View File

@ -1,691 +0,0 @@
;;; cider-util.el --- Common utility functions that don't belong anywhere else -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Common utility functions that don't belong anywhere else.
;;; Code:
(require 'seq)
(require 'cl-lib)
(require 'clojure-mode)
(require 'cider-compat)
(require 'nrepl-dict)
(defalias 'cider-pop-back 'pop-tag-mark)
(defcustom cider-font-lock-max-length 10000
"The max length of strings to fontify in `cider-font-lock-as'.
Setting this to nil removes the fontification restriction."
:group 'cider
:type 'boolean
:package-version '(cider . "0.10.0"))
(defun cider-util--hash-keys (hashtable)
"Return a list of keys in HASHTABLE."
(let ((keys '()))
(maphash (lambda (k _v) (setq keys (cons k keys))) hashtable)
keys))
(defun cider-util--clojure-buffers ()
"Return a list of all existing `clojure-mode' buffers."
(seq-filter
(lambda (buffer) (with-current-buffer buffer (derived-mode-p 'clojure-mode)))
(buffer-list)))
(defun cider-current-dir ()
"Return the directory of the current buffer."
(if buffer-file-name
(file-name-directory buffer-file-name)
default-directory))
(defun cider-in-string-p ()
"Return true if point is in a string."
(let ((beg (save-excursion (beginning-of-defun) (point))))
(nth 3 (parse-partial-sexp beg (point)))))
(defun cider-in-comment-p ()
"Return true if point is in a comment."
(let ((beg (save-excursion (beginning-of-defun) (point))))
(nth 4 (parse-partial-sexp beg (point)))))
(defun cider--tooling-file-p (file-name)
"Return t if FILE-NAME is not a 'real' source file.
Currently, only check if the relative file name starts with 'form-init'
which nREPL uses for temporary evaluation file names."
(let ((fname (file-name-nondirectory file-name)))
(string-match-p "^form-init" fname)))
(defun cider--cljc-or-cljx-buffer-p (&optional buffer)
"Return true if the current buffer is visiting a cljc or cljx file.
If BUFFER is provided act on that buffer instead."
(with-current-buffer (or buffer (current-buffer))
(or (derived-mode-p 'clojurec-mode) (derived-mode-p 'clojurex-mode))))
;;; Thing at point
(defun cider-defun-at-point (&optional bounds)
"Return the text of the top-level sexp at point.
If BOUNDS is non-nil, return a list of its starting and ending position
instead."
(save-excursion
(save-match-data
(end-of-defun)
(let ((end (point)))
(clojure-backward-logical-sexp 1)
(funcall (if bounds #'list #'buffer-substring-no-properties)
(point) end)))))
(defun cider-ns-form ()
"Retrieve the ns form."
(when (clojure-find-ns)
(save-excursion
(goto-char (match-beginning 0))
(cider-defun-at-point))))
(defun cider-symbol-at-point (&optional look-back)
"Return the name of the symbol at point, otherwise nil.
Ignores the REPL prompt. If LOOK-BACK is non-nil, move backwards trying to
find a symbol if there isn't one at point."
(or (when-let ((str (thing-at-point 'symbol)))
(unless (text-property-any 0 (length str) 'field 'cider-repl-prompt str)
(substring-no-properties str)))
(when look-back
(save-excursion
(ignore-errors
(while (not (looking-at "\\sw\\|\\s_\\|\\`"))
(forward-sexp -1)))
(cider-symbol-at-point)))))
;;; sexp navigation
(defun cider-sexp-at-point (&optional bounds)
"Return the sexp at point as a string, otherwise nil.
If BOUNDS is non-nil, return a list of its starting and ending position
instead."
(when-let ((b (or (and (equal (char-after) ?\()
(member (char-before) '(?\' ?\, ?\@))
;; hide stuff before ( to avoid quirks with '( etc.
(save-restriction
(narrow-to-region (point) (point-max))
(bounds-of-thing-at-point 'sexp)))
(bounds-of-thing-at-point 'sexp))))
(funcall (if bounds #'list #'buffer-substring-no-properties)
(car b) (cdr b))))
(defun cider-last-sexp (&optional bounds)
"Return the sexp preceding the point.
If BOUNDS is non-nil, return a list of its starting and ending position
instead."
(apply (if bounds #'list #'buffer-substring-no-properties)
(save-excursion
(clojure-backward-logical-sexp 1)
(list (point)
(progn (clojure-forward-logical-sexp 1)
(skip-chars-forward "[:blank:]")
(when (looking-at-p "\n") (forward-char 1))
(point))))))
(defun cider-start-of-next-sexp (&optional skip)
"Move to the start of the next sexp.
Skip any non-logical sexps like ^metadata or #reader macros.
If SKIP is an integer, also skip that many logical sexps first.
Can only error if SKIP is non-nil."
(while (clojure--looking-at-non-logical-sexp)
(forward-sexp 1))
(when (and skip (> skip 0))
(dotimes (_ skip)
(forward-sexp 1)
(cider-start-of-next-sexp))))
;;; Text properties
(defun cider-maybe-intern (name)
"If NAME is a symbol, return it; otherwise, intern it."
(if (symbolp name) name (intern name)))
(defun cider-intern-keys (plist)
"Copy PLIST, with any non-symbol keys replaced with symbols."
(when plist
(cons (cider-maybe-intern (pop plist))
(cons (pop plist) (cider-intern-keys plist)))))
(defmacro cider-propertize-region (props &rest body)
"Execute BODY and add PROPS to all the inserted text.
More precisely, PROPS are added to the region between the point's
positions before and after executing BODY."
(declare (indent 1))
(let ((start (make-symbol "start")))
`(let ((,start (point)))
(prog1 (progn ,@body)
(add-text-properties ,start (point) ,props)))))
(put 'cider-propertize-region 'lisp-indent-function 1)
(defun cider-property-bounds (prop)
"Return the the positions of the previous and next change to PROP.
PROP is the name of a text property."
(let ((end (next-single-char-property-change (point) prop)))
(list (previous-single-char-property-change end prop) end)))
(defun cider-insert (text &optional face break more-text)
"Insert TEXT with FACE, optionally followed by a line BREAK and MORE-TEXT."
(insert (if face (propertize text 'font-lock-face face) text))
(when more-text (insert more-text))
(when break (insert "\n")))
;;; Font lock
(defalias 'cider--font-lock-ensure
(if (fboundp 'font-lock-ensure)
#'font-lock-ensure
(with-no-warnings
(lambda (&optional _beg _end)
(when font-lock-mode
(font-lock-fontify-buffer))))))
(defalias 'cider--font-lock-flush
(if (fboundp 'font-lock-flush)
#'font-lock-flush
(with-no-warnings
(lambda (&optional _beg _end)
(when font-lock-mode
(font-lock-fontify-buffer))))))
(defvar cider--mode-buffers nil
"A list of buffers for different major modes.")
(defun cider--make-buffer-for-mode (mode)
"Return a temp buffer using major-mode MODE.
This buffer is not designed to display anything to the user. For that, use
`cider-make-popup-buffer' instead."
(setq cider--mode-buffers (seq-filter (lambda (x) (buffer-live-p (cdr x)))
cider--mode-buffers))
(or (cdr (assq mode cider--mode-buffers))
(let ((b (generate-new-buffer (format " *cider-temp %s*" mode))))
(push (cons mode b) cider--mode-buffers)
(with-current-buffer b
;; suppress major mode hooks as we care only about their font-locking
;; otherwise modes like whitespace-mode and paredit might interfere
(setq-local delay-mode-hooks t)
(setq delayed-mode-hooks nil)
(funcall mode))
b)))
(defun cider-font-lock-as (mode string)
"Use MODE to font-lock the STRING."
(if (or (null cider-font-lock-max-length)
(< (length string) cider-font-lock-max-length))
(with-current-buffer (cider--make-buffer-for-mode mode)
(erase-buffer)
(insert string)
(font-lock-fontify-region (point-min) (point-max))
(buffer-string))
string))
(defun cider-font-lock-region-as (mode beg end &optional buffer)
"Use MODE to font-lock text between BEG and END.
Unless you specify a BUFFER it will default to the current one."
(with-current-buffer (or buffer (current-buffer))
(let ((text (buffer-substring beg end)))
(delete-region beg end)
(goto-char beg)
(insert (cider-font-lock-as mode text)))))
(defun cider-font-lock-as-clojure (string)
"Font-lock STRING as Clojure code."
(cider-font-lock-as 'clojure-mode string))
;; Button allowing use of `font-lock-face', ignoring any inherited `face'
(define-button-type 'cider-plain-button
'face nil)
;;; Colors
(defun cider-scale-color (color scale)
"For a COLOR hex string or name, adjust intensity of RGB components by SCALE."
(let* ((rgb (color-values color))
(scaled-rgb (mapcar (lambda (n)
(format "%04x" (round (+ n (* scale 65535)))))
rgb)))
(apply #'concat "#" scaled-rgb)))
(defun cider-scale-background-color ()
"Scale the current background color to get a slighted muted version."
(let ((color (frame-parameter nil 'background-color))
(dark (eq (frame-parameter nil 'background-mode) 'dark)))
(cider-scale-color color (if dark 0.05 -0.05))))
(autoload 'pkg-info-version-info "pkg-info.el")
(defvar cider-version)
(defvar cider-codename)
(defun cider--version ()
"Retrieve CIDER's version.
A codename is added to stable versions."
(let ((version (condition-case nil
(pkg-info-version-info 'cider)
(error cider-version))))
(if (string-match-p "-snapshot" cider-version)
version
(format "%s (%s)" version cider-codename))))
;;; Strings
(defun cider-string-trim-left (string)
"Remove leading whitespace from STRING."
(if (string-match "\\`[ \t\n\r]+" string)
(replace-match "" t t string)
string))
(defun cider-string-trim-right (string)
"Remove trailing whitespace from STRING."
(if (string-match "[ \t\n\r]+\\'" string)
(replace-match "" t t string)
string))
(defun cider-string-trim (string)
"Remove leading and trailing whitespace from STRING."
(cider-string-trim-left (cider-string-trim-right string)))
(defun cider-string-join (strings &optional separator)
"Join all STRINGS using SEPARATOR."
(mapconcat #'identity strings separator))
(defun cider-join-into-alist (candidates &optional separator)
"Make an alist from CANDIDATES.
The keys are the elements joined with SEPARATOR and values are the original
elements. Useful for `completing-read' when candidates are complex
objects."
(mapcar (lambda (el)
(if (listp el)
(cons (cider-string-join el (or separator ":")) el)
(cons el el)))
candidates))
(defun cider-add-to-alist (symbol car cadr)
"Add '(CAR CADR) to the alist stored in SYMBOL.
If CAR already corresponds to an entry in the alist, destructively replace
the entry's second element with CADR.
This can be used, for instance, to update the version of an injected
plugin or dependency with:
(cider-add-to-alist 'cider-jack-in-lein-plugins
\"plugin/artifact-name\" \"THE-NEW-VERSION\")"
(let ((alist (symbol-value symbol)))
(if-let ((cons (assoc car alist)))
(setcdr cons (list cadr))
(set symbol (cons (list car cadr) alist)))))
(defun cider-namespace-qualified-p (sym)
"Return t if SYM is namespace-qualified."
(string-match-p "[^/]+/" sym))
(defvar cider-version)
(defconst cider-manual-url "http://cider.readthedocs.org/en/%s/"
"The URL to CIDER's manual.")
(defun cider--manual-version ()
"Convert the version to a ReadTheDocs-friendly version."
(if (string-match-p "-snapshot" cider-version)
"latest"
"stable"))
(defun cider-manual-url ()
"The CIDER manual's url."
(format cider-manual-url (cider--manual-version)))
;;;###autoload
(defun cider-view-manual ()
"View the manual in your default browser."
(interactive)
(browse-url (cider-manual-url)))
(defun cider--manual-button (label section-id)
"Return a button string that links to the online manual.
LABEL is the displayed string, and SECTION-ID is where it points
to."
(with-temp-buffer
(insert-text-button
label
'follow-link t
'action (lambda (&rest _) (interactive)
(browse-url (concat (cider-manual-url)
section-id))))
(buffer-string)))
(defconst cider-refcard-url "https://github.com/clojure-emacs/cider/raw/%s/doc/cider-refcard.pdf"
"The URL to CIDER's refcard.")
(defun cider--github-version ()
"Convert the version to a GitHub-friendly version."
(if (string-match-p "-snapshot" cider-version)
"master"
(concat "v" cider-version)))
(defun cider-refcard-url ()
"The CIDER manual's url."
(format cider-refcard-url (cider--github-version)))
(defun cider-view-refcard ()
"View the refcard in your default browser."
(interactive)
(browse-url (cider-refcard-url)))
(defconst cider-report-bug-url "https://github.com/clojure-emacs/cider/issues/new"
"The URL to report a CIDER issue.")
(defun cider-report-bug ()
"Report a bug in your default browser."
(interactive)
(browse-url cider-report-bug-url))
(defun cider--project-name (dir)
"Extracts a project name from DIR, possibly nil.
The project name is the final component of DIR if not nil."
(when dir
(file-name-nondirectory (directory-file-name dir))))
;;; Vectors
(defun cider--deep-vector-to-list (x)
"Convert vectors in X to lists.
If X is a sequence, return a list of `cider--deep-vector-to-list' applied to
each of its elements.
Any other value is just returned."
(if (sequencep x)
(mapcar #'cider--deep-vector-to-list x)
x))
;;; Help mode
;; Same as https://github.com/emacs-mirror/emacs/blob/86d083438dba60dc00e9e96414bf7e832720c05a/lisp/help-mode.el#L355
;; the original function uses some buffer local variables, but the buffer used
;; is not configurable. It defaults to (help-buffer)
(defun cider--help-setup-xref (item interactive-p buffer)
"Invoked from commands using the \"*Help*\" buffer to install some xref info.
ITEM is a (FUNCTION . ARGS) pair appropriate for recreating the help
buffer after following a reference. INTERACTIVE-P is non-nil if the
calling command was invoked interactively. In this case the stack of
items for help buffer \"back\" buttons is cleared. Use BUFFER for the
buffer local variables.
This should be called very early, before the output buffer is cleared,
because we want to record the \"previous\" position of point so we can
restore it properly when going back."
(with-current-buffer buffer
(when help-xref-stack-item
(push (cons (point) help-xref-stack-item) help-xref-stack)
(setq help-xref-forward-stack nil))
(when interactive-p
(let ((tail (nthcdr 10 help-xref-stack)))
;; Truncate the stack.
(if tail (setcdr tail nil))))
(setq help-xref-stack-item item)))
(defcustom cider-doc-xref-regexp "`\\(.*?\\)`"
"The regexp used to search Clojure vars in doc buffers."
:type 'regexp
:safe #'stringp
:group 'cider
:package-version '(cider . "0.13.0"))
(defun cider--find-symbol-xref ()
"Parse and return the first clojure symbol in current-buffer.
Use `cider-doc-xref-regexp' for the search. Set match data and return a
string of the Clojure symbol. Return nil if there are no more matches in
the buffer."
(when (re-search-forward cider-doc-xref-regexp nil t)
(match-string 1)))
(declare-function cider-doc-lookup "cider-doc")
(declare-function cider--eldoc-remove-dot "cider-eldoc")
;; Similar to https://github.com/emacs-mirror/emacs/blob/65c8c7cb96c14f9c6accd03cc8851b5a3459049e/lisp/help-mode.el#L404
(defun cider--doc-make-xrefs ()
"Parse and hyperlink documentation cross-references in current-buffer.
Find cross-reference information in a buffer and activate such cross
references for selection with `help-xref'. Cross-references are parsed
using `cider--find-symbol-xref'.
Special references `back' and `forward' are made to go back and forth
through a stack of help buffers. Variables `help-back-label' and
`help-forward-label' specify the text for that."
(interactive "b")
;; parse the docstring and create xrefs for symbols
(save-excursion
(goto-char (point-min))
(let ((symbol))
(while (setq symbol (cider--find-symbol-xref))
(replace-match "")
(insert-text-button symbol
'type 'help-xref
'help-function (apply-partially #'cider-doc-lookup
(cider--eldoc-remove-dot symbol))))))
;; create back and forward buttons if appropiate
(insert "\n")
(when (or help-xref-stack help-xref-forward-stack)
(insert "\n"))
;; Make a back-reference in this buffer if appropriate.
(when help-xref-stack
(help-insert-xref-button help-back-label 'help-back
(current-buffer)))
;; Make a forward-reference in this buffer if appropriate.
(when help-xref-forward-stack
(when help-xref-stack
(insert "\t"))
(help-insert-xref-button help-forward-label 'help-forward
(current-buffer)))
(when (or help-xref-stack help-xref-forward-stack)
(insert "\n")))
;;; Words of inspiration
(defun cider-user-first-name ()
"Find the current user's first name."
(let ((name (if (string= (user-full-name) "")
(user-login-name)
(user-full-name))))
(string-match "^[^ ]*" name)
(capitalize (match-string 0 name))))
(defvar cider-words-of-inspiration
`("The best way to predict the future is to invent it. -Alan Kay"
"A point of view is worth 80 IQ points. -Alan Kay"
"Lisp isn't a language, it's a building material. -Alan Kay"
"Simple things should be simple, complex things should be possible. -Alan Kay"
"Everything should be as simple as possible, but not simpler. -Albert Einstein"
"Measuring programming progress by lines of code is like measuring aircraft building progress by weight. -Bill Gates"
"Controlling complexity is the essence of computer programming. -Brian Kernighan"
"The unavoidable price of reliability is simplicity. -C.A.R. Hoare"
"You're bound to be unhappy if you optimize everything. -Donald Knuth"
"Simplicity is prerequisite for reliability. -Edsger W. Dijkstra"
"Elegance is not a dispensable luxury but a quality that decides between success and failure. -Edsger W. Dijkstra"
"Deleted code is debugged code. -Jeff Sickel"
"The key to performance is elegance, not battalions of special cases. -Jon Bentley and Doug McIlroy"
"First, solve the problem. Then, write the code. -John Johnson"
"Simplicity is the ultimate sophistication. -Leonardo da Vinci"
"Programming is not about typing... it's about thinking. -Rich Hickey"
"Design is about pulling things apart. -Rich Hickey"
"Programmers know the benefits of everything and the tradeoffs of nothing. -Rich Hickey"
"Code never lies, comments sometimes do. -Ron Jeffries"
"The true delight is in the finding out rather than in the knowing. -Isaac Asimov"
"If paredit is not for you, then you need to become the sort of person that paredit is for. -Phil Hagelberg"
"Express Yourself. -Madonna"
"Put on your red shoes and dance the blues. -David Bowie"
"Do. Or do not. There is no try. -Yoda"
"The enjoyment of one's tools is an essential ingredient of successful work. -Donald E. Knuth"
"Not all those who wander are lost. -J.R.R. Tolkien"
"The best way to learn is to do. -P.R. Halmos"
"If you wish to make an apple pie from scratch, you must first invent the universe. -Carl Sagan"
"Learn the rules like a pro, so you can break them like an artist. -Pablo Picasso"
"The only way of discovering the limits of the possible is to venture a little way past them into the impossible. -Arthur C. Clarke"
"Don't wish it were easier. Wish you were better. -Jim Rohn"
"One chord is fine. Two chords is pushing it. Three chords and you're into jazz. -Lou Reed"
"We are all apprentices in a craft where no one ever becomes a master.-Ernest Hemingway"
"Clojure isn't a language, it's a building material."
"Think big!"
"Think bold!"
"Think fun!"
"Code big!"
"Code bold!"
"Code fun!"
"Take this REPL, fellow hacker, and may it serve you well."
"Let the hacking commence!"
"Hacks and glory await!"
"Hack and be merry!"
"Your hacking starts... NOW!"
"May the Source be with you!"
"May the Source shine upon thy REPL!"
"Code long and prosper!"
"Happy hacking!"
"nREPL server is up, CIDER REPL is online!"
"CIDER REPL operational!"
"Your imagination is the only limit to what you can do with this REPL!"
"This REPL is yours to command!"
"Fame is but a hack away!"
"The REPL is not enough, but it is such a perfect place to start..."
"Keep on codin' in the free world!"
"What we do in the REPL echoes in eternity!"
"Evaluating is believing."
"To infinity... and beyond."
"Showtime!"
"Unfortunately, no one can be told what CIDER is. You have to figure this out yourself."
"Procure a bottle of cider to achieve optimum programming results."
"In parentheses we trust!"
"Write you some Clojure for Great Good!"
"Oh, what a day... what a lovely day!"
"What a day! What cannot be accomplished on such a splendid day!"
"Home is where your REPL is."
,(format "%s, I've a feeling we're not in Kansas anymore."
(cider-user-first-name))
,(format "%s, this could be the start of a beautiful program."
(cider-user-first-name)))
"Scientifically-proven optimal words of hackerish encouragement.")
(defun cider-random-words-of-inspiration ()
"Select a random entry from `cider-words-of-inspiration'."
(eval (nth (random (length cider-words-of-inspiration))
cider-words-of-inspiration)))
(defvar cider-tips
'("Press <\\[cider-connect]> to connect to a running nREPL server."
"Press <\\[cider-quit]> to quit the current connection."
"Press <\\[cider-view-manual]> to view CIDER's manual."
"Press <\\[cider-view-refcard]> to view CIDER's refcard."
"Press <\\[describe-mode]> to see a list of the keybindings available (this will work in every Emacs buffer)."
"Press <\\[cider-repl-handle-shortcut]> to quickly invoke some REPL command."
"Press <\\[cider-switch-to-last-clojure-buffer]> to switch between the REPL and a Clojure source buffer."
"Press <\\[cider-find-var]> to jump to the source of something (e.g. a var, a Java method)."
"Press <\\[cider-doc]> to view the documentation for something (e.g. a var, a Java method)."
"Press <\\[cider-find-resource]> to find a resource on the classpath."
"Press <\\[cider-selector]> to quickly select a CIDER buffer."
"Press <\\[cider-test-run-ns-tests]> to run the tests for the current namespace."
"Press <\\[cider-test-run-loaded-tests]> to run all loaded tests."
"Press <\\[cider-test-run-project-tests]> to run all tests for the current project."
"Press <\\[cider-apropos]> to look for a symbol by some search string."
"Press <\\[cider-apropos-documentation]> to look for a symbol that has some string in its docstring."
"Press <\\[cider-eval-defun-at-point]> to eval the top-level form at point."
"Press <\\[cider-eval-buffer]> to eval the entire source buffer."
"Press <\\[cider-scratch]> to create a Clojure scratchpad. Pretty handy for prototyping."
"Press <\\[cider-read-and-eval]> to evaluate some Clojure expression directly in the minibuffer."
"Press <\\[cider-drink-a-sip]> to get more CIDER tips."
"Press <\\[cider-browse-ns-all]> to start CIDER's namespace browser."
"Press <\\[cider-classpath]> to start CIDER's classpath browser."
"Press <\\[cider-macroexpand-1]> to expand the preceding macro."
"Press <\\[cider-inspect]> to inspect the preceding expression's result."
"Press <C-u \\[cider-inspect]> to inspect the defun at point's result."
"Press <C-u C-u \\[cider-inspect]> to read Clojure code from the minibuffer and inspect its result."
"Press <\\[cider-refresh]> to reload modified and unloaded namespaces."
"You can define Clojure functions to be called before and after `cider-refresh' (see `cider-refresh-before-fn' and `cider-refresh-after-fn'."
"Press <\\[cider-display-connection-info]> to view information about the connection."
"Press <\\[cider-undef]> to undefine a symbol in the current namespace."
"Press <\\[cider-interrupt]> to interrupt an ongoing evaluation."
"Use <M-x customize-group RET cider RET> to see every possible setting you can customize."
"Use <M-x customize-group RET cider-repl RET> to see every possible REPL setting you can customize."
"Enable `eldoc-mode' to display function & method signatures in the minibuffer."
"Enable `cider-enlighten-mode' to display the locals of a function when it's executed."
"Use <\\[cider-close-ancillary-buffers]> to close all ancillary buffers created by CIDER (e.g. *cider-doc*)."
"Exploring CIDER's menu-bar entries is a great way to discover features."
"Keep in mind that some commands don't have a keybinding by default. Explore CIDER!"
"Tweak `cider-repl-prompt-function' to customize your REPL prompt."
"Tweak `cider-eldoc-ns-function' to customize the way namespaces are displayed by eldoc.")
"Some handy CIDER tips."
)
(defun cider-random-tip ()
"Select a random tip from `cider-tips'."
(substitute-command-keys (nth (random (length cider-tips)) cider-tips)))
(defun cider-drink-a-sip ()
"Show a random tip."
(interactive)
(message (cider-random-tip)))
(defun cider-column-number-at-pos (pos)
"Analog to `line-number-at-pos'.
Return buffer column number at position POS."
(save-excursion (goto-char pos) (current-column)))
(defun cider-propertize (text kind)
"Propertize TEXT as KIND.
KIND can be the symbols `ns', `var', `emph', `fn', or a face name."
(propertize text 'face (pcase kind
(`fn 'font-lock-function-name-face)
(`var 'font-lock-variable-name-face)
(`ns 'font-lock-type-face)
(`emph 'font-lock-keyword-face)
(face face))))
(defun cider--menu-add-help-strings (menu-list)
"Add a :help entries to items in MENU-LIST."
(mapcar (lambda (x)
(cond
((listp x) (cider--menu-add-help-strings x))
((and (vectorp x)
(not (plist-get (append x nil) :help))
(functionp (elt x 1)))
(vconcat x `[:help ,(documentation (elt x 1))]))
(t x)))
menu-list))
(provide 'cider-util)
;;; cider-util.el ends here

View File

@ -1,790 +0,0 @@
;;; cider.el --- Clojure Interactive Development Environment that Rocks -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;; Maintainer: Bozhidar Batsov <bozhidar@batsov.com>
;; URL: http://www.github.com/clojure-emacs/cider
;; Version: 0.14.0-cvs
;; Package-Requires: ((emacs "24.3") (clojure-mode "5.5.2") (pkg-info "0.4") (queue "0.1.1") (spinner "1.7") (seq "2.16"))
;; Keywords: languages, clojure, cider
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Provides a Clojure interactive development environment for Emacs, built on
;; top of nREPL.
;;; Installation:
;; Available as a package in melpa.org and stable.melpa.org
;; (add-to-list 'package-archives
;; '("melpa" . "https://melpa.org/packages/"))
;;
;; or
;;
;; (add-to-list 'package-archives
;; '("melpa-stable" . "https://stable.melpa.org/packages/") t)
;;
;; M-x package-install cider
;;; Usage:
;; M-x cider-jack-in
;;; Code:
(defgroup cider nil
"Clojure Interactive Development Environment that Rocks."
:prefix "cider-"
:group 'applications
:link '(url-link :tag "Github" "https://github.com/clojure-emacs/cider")
:link '(url-link :tag "Online Manual" "https://cider.readthedocs.org")
:link '(emacs-commentary-link :tag "Commentary" "cider"))
(defcustom cider-prompt-for-project-on-connect 'when-needed
"Controls whether to prompt for associated project on `cider-connect'.
When set to when-needed, the project will be derived from the buffer you're
visiting, when invoking `cider-connect'.
When set to t, you'll always to prompted to select the matching project.
When set to nil, you'll never be prompted to select a project and no
project inference will take place."
:type '(choice (const :tag "always" t)
(const when-needed)
(const :tag "never" nil))
:group 'cider
:package-version '(cider . "0.10.0"))
(require 'cider-client)
(require 'cider-eldoc)
(require 'cider-repl)
(require 'cider-mode)
(require 'cider-common)
(require 'cider-compat)
(require 'cider-debug)
(require 'tramp-sh)
(require 'seq)
(defconst cider-version "0.14.0-snapshot"
"Fallback version used when it cannot be extracted automatically.
Normally it won't be used, unless `pkg-info' fails to extract the
version from the CIDER package or library.")
(defconst cider-codename "Berlin"
"Codename used to denote stable releases.")
(defcustom cider-lein-command
"lein"
"The command used to execute Leiningen."
:type 'string
:group 'cider)
(defcustom cider-lein-parameters
"repl :headless"
"Params passed to Leiningen to start an nREPL server via `cider-jack-in'."
:type 'string
:group 'cider)
(defcustom cider-boot-command
"boot"
"The command used to execute Boot."
:type 'string
:group 'cider
:package-version '(cider . "0.9.0"))
(defcustom cider-boot-parameters
"repl -s wait"
"Params passed to boot to start an nREPL server via `cider-jack-in'."
:type 'string
:group 'cider
:package-version '(cider . "0.9.0"))
(defcustom cider-gradle-command
"gradle"
"The command used to execute Gradle."
:type 'string
:group 'cider
:package-version '(cider . "0.10.0"))
(defcustom cider-gradle-parameters
"--no-daemon clojureRepl"
"Params passed to gradle to start an nREPL server via `cider-jack-in'."
:type 'string
:group 'cider
:package-version '(cider . "0.10.0"))
(defcustom cider-default-repl-command
"lein"
"The default command and parameters to use when connecting to nREPL.
This value will only be consulted when no identifying file types, i.e.
project.clj for leiningen or build.boot for boot, could be found."
:type 'string
:group 'cider
:package-version '(cider . "0.9.0"))
(defcustom cider-preferred-build-tool
nil
"Allow choosing a build system when there are many.
When there are artifacts from multiple build systems (\"lein\", \"boot\",
\"gradle\") the user is prompted to select one of them. When non-nil, this
variable will suppress this behavior and will select whatever build system
is indicated by the variable if present. Note, this is only when CIDER
cannot decide which of many build systems to use and will never override a
command when there is no ambiguity."
:type '(choice (const "lein")
(const "boot")
(const "gradle")
(const :tag "Always ask" nil))
:group 'cider
:package-version '(cider . "0.13.0"))
(defcustom cider-known-endpoints nil
"A list of connection endpoints where each endpoint is a list.
For example: \\='((\"label\" \"host\" \"port\")).
The label is optional so that \\='(\"host\" \"port\") will suffice.
This variable is used by `cider-connect'."
:type '(repeat (list (string :tag "label")
(string :tag "host")
(string :tag "port")))
:group 'cider)
(defcustom cider-connected-hook nil
"List of functions to call when connected to Clojure nREPL server."
:type 'hook
:group 'cider
:package-version '(cider . "0.9.0"))
(defcustom cider-disconnected-hook nil
"List of functions to call when disconnected from the Clojure nREPL server."
:type 'hook
:group 'cider
:package-version '(cider . "0.9.0"))
(defcustom cider-auto-mode t
"When non-nil, automatically enable `cider-mode' for all Clojure buffers."
:type 'boolean
:package-version '(cider . "0.9.0"))
(defcustom cider-inject-dependencies-at-jack-in t
"When nil, do not inject repl dependencies (most likely nREPL middlewares) at `cider-jack-in' time."
:type 'boolean
:version '(cider . "0.11.0"))
(defvar cider-ps-running-nrepls-command "ps u | grep leiningen"
"Process snapshot command used in `cider-locate-running-nrepl-ports'.")
(defvar cider-ps-running-nrepl-path-regexp-list
'("\\(?:leiningen.original.pwd=\\)\\(.+?\\) -D"
"\\(?:-classpath +:?\\(.+?\\)/self-installs\\)")
"Regexp list to get project paths.
Extract project paths from output of `cider-ps-running-nrepls-command'.
Sub-match 1 must be the project path.")
(defvar cider-host-history nil
"Completion history for connection hosts.")
;;;###autoload
(defun cider-version ()
"Display CIDER's version."
(interactive)
(message "CIDER %s" (cider--version)))
(defun cider-jack-in-command (project-type)
"Determine the command `cider-jack-in' needs to invoke for the PROJECT-TYPE."
(pcase project-type
("lein" cider-lein-command)
("boot" cider-boot-command)
("gradle" cider-gradle-command)
(_ (user-error "Unsupported project type `%s'" project-type))))
(defun cider-jack-in-resolve-command (project-type)
"Determine the resolved file path to `cider-jack-in-command' if it can be
found for the PROJECT-TYPE"
(pcase project-type
("lein" (cider--lein-resolve-command))
("boot" (cider--boot-resolve-command))
("gradle" (cider--gradle-resolve-command))
(_ (user-error "Unsupported project type `%s'" project-type))))
(defun cider-jack-in-params (project-type)
"Determine the commands params for `cider-jack-in' for the PROJECT-TYPE."
(pcase project-type
("lein" cider-lein-parameters)
("boot" cider-boot-parameters)
("gradle" cider-gradle-parameters)
(_ (user-error "Unsupported project type `%s'" project-type))))
;;; Jack-in dependencies injection
(defvar cider-jack-in-dependencies nil
"List of dependencies where elements are lists of artifact name and version.")
(put 'cider-jack-in-dependencies 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-dependencies
"org.clojure/tools.nrepl" "0.2.12")
(defvar cider-jack-in-dependencies-exclusions nil
"List of exclusions for jack in dependencies.
Elements of the list are artifact name and list of exclusions to apply for the artifact.")
(put 'cider-jack-in-dependencies-exclusions 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-dependencies-exclusions
"org.clojure/tools.nrepl" '("org.clojure/clojure"))
(defcustom cider-jack-in-auto-inject-clojure nil
"Version of clojure to auto-inject into REPL.
If nil, do not inject clojure into the REPL. If `latest', inject
`cider-latest-clojure-version', which should approximate to the most recent
version of clojure. If `minimal', inject `cider-minimum-clojure-version',
which will be the lowest version cider supports. If a string, use this as
the version number. If it is a list, the first element should be a string,
specifying the artifact ID, and the second element the version number."
:type '(choice (const :tag "None" nil)
(const :tag "Latest" 'latest)
(const :tag "Minimal" 'minimal)
(string :tag "Specific Version")
(list :tag "Artifact ID and Version"
(string :tag "Artifact ID")
(string :tag "Version"))))
(defvar cider-jack-in-lein-plugins nil
"List of Leiningen plugins where elements are lists of artifact name and version.")
(put 'cider-jack-in-lein-plugins 'risky-local-variable t)
(cider-add-to-alist 'cider-jack-in-lein-plugins
"cider/cider-nrepl" (upcase cider-version))
(defvar cider-jack-in-nrepl-middlewares nil
"List of Clojure variable names.
Each of these Clojure variables should hold a vector of nREPL middlewares.")
(put 'cider-jack-in-nrepl-middlewares 'risky-local-variable t)
(add-to-list 'cider-jack-in-nrepl-middlewares "cider.nrepl/cider-middleware")
(defun cider--list-as-boot-artifact (list)
"Return a boot artifact string described by the elements of LIST.
LIST should have the form (ARTIFACT-NAME ARTIFACT-VERSION). The returned
string is quoted for passing as argument to an inferior shell."
(concat "-d " (shell-quote-argument (format "%s:%s" (car list) (cadr list)))))
(defun cider-boot-command-prefix (dependencies)
"Return a list of boot artifact strings created from DEPENDENCIES."
(concat (mapconcat #'cider--list-as-boot-artifact dependencies " ")
" "))
(defun cider-boot-repl-task-params (params middlewares)
(if (string-match "\\_<repl\\_>" params)
(replace-match (concat "repl "
(mapconcat (lambda (middleware)
(format "-m %s" (shell-quote-argument middleware)))
middlewares
" "))
'fixed 'literal params)
(message "Warning: `cider-boot-parameters' doesn't call the \"repl\" task, jacking-in might not work")
params))
(defun cider-boot-jack-in-dependencies (params dependencies plugins middlewares)
(concat (cider-boot-command-prefix (append dependencies plugins))
(cider-boot-repl-task-params params middlewares)))
(defun cider--lein-artifact-exclusions (exclusions)
"Return an exclusions vector described by the elements of EXCLUSIONS."
(if exclusions
(format " :exclusions [%s]" (mapconcat #'identity exclusions " "))
""))
(defun cider--list-as-lein-artifact (list &optional exclusions)
"Return an artifact string described by the elements of LIST.
LIST should have the form (ARTIFACT-NAME ARTIFACT-VERSION). Optionally a list
of EXCLUSIONS can be provided as well. The returned
string is quoted for passing as argument to an inferior shell."
(shell-quote-argument (format "[%s %S%s]" (car list) (cadr list) (cider--lein-artifact-exclusions exclusions))))
(defun cider-lein-jack-in-dependencies (params dependencies dependencies-exclusions lein-plugins)
(concat
(mapconcat #'identity
(append (seq-map (lambda (dep)
(let ((exclusions (cadr (assoc (car dep) dependencies-exclusions))))
(concat "update-in :dependencies conj "
(cider--list-as-lein-artifact dep exclusions))))
dependencies)
(seq-map (lambda (plugin)
(concat "update-in :plugins conj "
(cider--list-as-lein-artifact plugin)))
lein-plugins))
" -- ")
" -- "
params))
(defun cider-add-clojure-dependencies-maybe (dependencies)
"Return DEPENDENCIES with an added Clojure dependency if requested.
See also `cider-jack-in-auto-inject-clojure'."
(if cider-jack-in-auto-inject-clojure
(if (consp cider-jack-in-auto-inject-clojure)
(cons cider-jack-in-auto-inject-clojure dependencies)
(cons (list cider-clojure-artifact-id
(cond
((stringp cider-jack-in-auto-inject-clojure)
cider-jack-in-auto-inject-clojure)
((eq cider-jack-in-auto-inject-clojure 'minimal)
cider-minimum-clojure-version)
((eq cider-jack-in-auto-inject-clojure 'latest)
cider-latest-clojure-version)))
dependencies))
dependencies))
(defun cider-inject-jack-in-dependencies (params project-type)
"Return PARAMS with injected REPL dependencies.
These are set in `cider-jack-in-dependencies', `cider-jack-in-lein-plugins' and
`cider-jack-in-nrepl-middlewares' are injected from the CLI according to
the used PROJECT-TYPE. Eliminates the need for hacking profiles.clj or the
boot script for supporting cider with its nREPL middleware and
dependencies."
(pcase project-type
("lein" (cider-lein-jack-in-dependencies
params
(cider-add-clojure-dependencies-maybe
cider-jack-in-dependencies)
cider-jack-in-dependencies-exclusions
cider-jack-in-lein-plugins))
("boot" (cider-boot-jack-in-dependencies
params
(cider-add-clojure-dependencies-maybe
cider-jack-in-dependencies)
cider-jack-in-lein-plugins
cider-jack-in-nrepl-middlewares))
("gradle" params)
(_ (error "Unsupported project type `%s'" project-type))))
;;; ClojureScript REPL creation
(defconst cider--cljs-repl-types
'(("(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))"
"Rhino" "")
("(do (require 'figwheel-sidecar.repl-api) (figwheel-sidecar.repl-api/start-figwheel!) (figwheel-sidecar.repl-api/cljs-repl))"
"Figwheel-sidecar" " (add figwheel-sidecar to your plugins)")
("(do (require 'cljs.repl.node) (cemerick.piggieback/cljs-repl (cljs.repl.node/repl-env)))"
"Node" " (requires NodeJS to be installed)")
("(do (require 'weasel.repl.websocket) (cemerick.piggieback/cljs-repl (weasel.repl.websocket/repl-env :ip \"127.0.0.1\" :port 9001)))"
"Weasel" " (see Readme for additional configuration)")))
(defcustom cider-cljs-lein-repl "(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))"
"Clojure form that returns a ClojureScript REPL environment.
This is only used in lein projects. It is evaluated in a Clojure REPL and
it should start a ClojureScript REPL."
:type `(choice ,@(seq-map (lambda (x) `(const :tag ,(apply #'concat (cdr x)) ,(car x)))
cider--cljs-repl-types)
(string :tag "Custom"))
:safe (lambda (x) (assoc x cider--cljs-repl-types))
:group 'cider)
(defun cider--offer-to-open-app-in-browser (server-buffer)
"Look for a server address in SERVER-BUFFER and offer to open it."
(when (buffer-live-p server-buffer)
(with-current-buffer server-buffer
(save-excursion
(goto-char (point-min))
(when-let ((url (and (search-forward-regexp "http://localhost:[0-9]+" nil 'noerror)
(match-string 0))))
(when (y-or-n-p (format "Visit %s in a browser? " url))
(browse-url url)))))))
(defun cider-create-sibling-cljs-repl (client-buffer)
"Create a ClojureScript REPL with the same server as CLIENT-BUFFER.
The new buffer will correspond to the same project as CLIENT-BUFFER, which
should be the regular Clojure REPL started by the server process filter."
(interactive (list (cider-current-connection)))
(let* ((nrepl-repl-buffer-name-template "*cider-repl CLJS%s*")
(nrepl-create-client-buffer-function #'cider-repl-create)
(nrepl-use-this-as-repl-buffer 'new)
(client-process-args (with-current-buffer client-buffer
(unless (or nrepl-server-buffer nrepl-endpoint)
(error "This is not a REPL buffer, is there a REPL active?"))
(list (car nrepl-endpoint)
(elt nrepl-endpoint 1)
(when (buffer-live-p nrepl-server-buffer)
(get-buffer-process nrepl-server-buffer)))))
(cljs-proc (apply #'nrepl-start-client-process client-process-args))
(cljs-buffer (process-buffer cljs-proc)))
(with-current-buffer cljs-buffer
;; The new connection has now been bumped to the top, but it's still a
;; Clojure REPL! Additionally, some ClojureScript REPLs can actually take
;; a while to start (some even depend on the user opening a browser).
;; Meanwhile, this REPL will gladly receive requests in place of the
;; original Clojure REPL. Our solution is to bump the original REPL back
;; up the list, so it takes priority on Clojure requests.
(cider-make-connection-default client-buffer)
(pcase (assoc cider-cljs-lein-repl cider--cljs-repl-types)
(`(,_ ,name ,info)
(message "Starting a %s REPL%s" name (or info "")))
(_ (message "Starting a custom ClojureScript REPL")))
(cider-nrepl-send-request
(list "op" "eval"
"ns" (cider-current-ns)
"session" nrepl-session
"code" cider-cljs-lein-repl)
(cider-repl-handler (current-buffer)))
(cider--offer-to-open-app-in-browser nrepl-server-buffer))))
(defun cider--select-zombie-buffer (repl-buffers)
"Return a zombie buffer from REPL-BUFFERS, or nil if none exists."
(when-let ((zombie-buffs (seq-remove #'get-buffer-process repl-buffers)))
(when (y-or-n-p
(format "Zombie REPL buffers exist (%s). Reuse? "
(mapconcat #'buffer-name zombie-buffs ", ")))
(if (= (length zombie-buffs) 1)
(car zombie-buffs)
(completing-read "Choose REPL buffer: "
(mapcar #'buffer-name zombie-buffs)
nil t)))))
(defun cider-find-reusable-repl-buffer (endpoint project-directory)
"Check whether a reusable connection buffer already exists.
Looks for buffers where `nrepl-endpoint' matches ENDPOINT, or
`nrepl-project-dir' matches PROJECT-DIRECTORY. If such a buffer was found,
and has no process, return it. If the process is alive, ask the user for
confirmation and return 'new/nil for y/n answer respectively. If other
REPL buffers with dead process exist, ask the user if any of those should
be reused."
(if-let ((repl-buffers (cider-repl-buffers))
(exact-buff (seq-find
(lambda (buff)
(with-current-buffer buff
(or (and endpoint
(equal endpoint nrepl-endpoint))
(and project-directory
(equal project-directory nrepl-project-dir)))))
repl-buffers)))
(if (get-buffer-process exact-buff)
(when (y-or-n-p (format "REPL buffer already exists (%s). \
Do you really want to create a new one? "
exact-buff))
'new)
exact-buff)
(or (cider--select-zombie-buffer repl-buffers) 'new)))
;;;###autoload
(defun cider-jack-in (&optional prompt-project cljs-too)
"Start an nREPL server for the current project and connect to it.
If PROMPT-PROJECT is t, then prompt for the project for which to
start the server.
If CLJS-TOO is non-nil, also start a ClojureScript REPL session with its
own buffer."
(interactive "P")
(setq cider-current-clojure-buffer (current-buffer))
(let* ((project-type (cider-project-type))
(command (cider-jack-in-command project-type))
(command-resolved (cider-jack-in-resolve-command project-type))
(command-params (cider-jack-in-params project-type)))
(if command-resolved
(let* ((project (when prompt-project
(read-directory-name "Project: ")))
(project-dir (clojure-project-dir
(or project (cider-current-dir))))
(params (if prompt-project
(read-string (format "nREPL server command: %s "
command-params)
command-params)
command-params))
(params (if cider-inject-dependencies-at-jack-in
(cider-inject-jack-in-dependencies params project-type)
params))
(cmd (format "%s %s" command-resolved params)))
(when-let ((repl-buff (cider-find-reusable-repl-buffer nil project-dir)))
(let ((nrepl-create-client-buffer-function #'cider-repl-create)
(nrepl-use-this-as-repl-buffer repl-buff))
(nrepl-start-server-process
project-dir cmd
(when cljs-too #'cider-create-sibling-cljs-repl)))))
(user-error "The %s executable isn't on your `exec-path'" command))))
;;;###autoload
(defun cider-jack-in-clojurescript (&optional prompt-project)
"Start an nREPL server and connect to it both Clojure and ClojureScript REPLs.
If PROMPT-PROJECT is t, then prompt for the project for which to
start the server."
(interactive "P")
(cider-jack-in prompt-project 'cljs-too))
;;;###autoload
(defun cider-connect (host port &optional project-dir)
"Connect to an nREPL server identified by HOST and PORT.
Create REPL buffer and start an nREPL client connection.
When the optional param PROJECT-DIR is present, the connection
gets associated with it."
(interactive (cider-select-endpoint))
(setq cider-current-clojure-buffer (current-buffer))
(when-let ((repl-buff (cider-find-reusable-repl-buffer `(,host ,port) nil)))
(let* ((nrepl-create-client-buffer-function #'cider-repl-create)
(nrepl-use-this-as-repl-buffer repl-buff)
(conn (process-buffer (nrepl-start-client-process host port))))
(if project-dir
(cider-assoc-project-with-connection project-dir conn)
(let ((project-dir (clojure-project-dir)))
(cond
;; associate only if we're in a project
((and project-dir (null cider-prompt-for-project-on-connect)) (cider-assoc-project-with-connection project-dir conn))
;; associate if we're in a project, prompt otherwise
((eq cider-prompt-for-project-on-connect 'when-needed) (cider-assoc-project-with-connection project-dir conn))
;; always prompt
(t (cider-assoc-project-with-connection nil conn))))))))
(defun cider-current-host ()
"Retrieve the current host."
(if (and (stringp buffer-file-name)
(file-remote-p buffer-file-name))
tramp-current-host
"localhost"))
(defun cider-select-endpoint ()
"Interactively select the host and port to connect to."
(dolist (endpoint cider-known-endpoints)
(unless (stringp (or (nth 2 endpoint)
(nth 1 endpoint)))
(user-error "The port for %s in `cider-known-endpoints' should be a string"
(nth 0 endpoint))))
(let* ((ssh-hosts (cider--ssh-hosts))
(hosts (seq-uniq (append (when cider-host-history
;; history elements are strings of the form "host:port"
(list (split-string (car cider-host-history) ":")))
(list (list (cider-current-host)))
cider-known-endpoints
ssh-hosts
(when (file-remote-p default-directory)
;; add localhost even in remote buffers
'(("localhost"))))))
(sel-host (cider--completing-read-host hosts))
(host (car sel-host))
(port (or (cadr sel-host)
(cider--completing-read-port host (cider--infer-ports host ssh-hosts)))))
(list host port)))
(defun cider--ssh-hosts ()
"Retrieve all ssh host from local configuration files."
(seq-map (lambda (s) (list (replace-regexp-in-string ":$" "" s)))
(let ((tramp-completion-mode t))
(tramp-completion-handle-file-name-all-completions "" "/ssh:"))))
(defun cider--completing-read-host (hosts)
"Interactively select host from HOSTS.
Each element in HOSTS is one of: (host), (host port) or (label host port).
Return a list of the form (HOST PORT), where PORT can be nil."
(let* ((hosts (cider-join-into-alist hosts))
(sel-host (completing-read "Host: " hosts nil nil nil
'cider-host-history (caar hosts)))
(host (or (cdr (assoc sel-host hosts)) (list sel-host))))
;; remove the label
(if (= 3 (length host)) (cdr host) host)))
(defun cider--infer-ports (host ssh-hosts)
"Infer nREPL ports on HOST.
Return a list of elements of the form (directory port). SSH-HOSTS is a list
of remote SSH hosts."
(let ((localp (or (nrepl-local-host-p host)
(not (assoc-string host ssh-hosts)))))
(if localp
;; change dir: current file might be remote
(let* ((change-dir-p (file-remote-p default-directory))
(default-directory (if change-dir-p "~/" default-directory)))
(cider-locate-running-nrepl-ports (unless change-dir-p default-directory)))
(let ((vec (vector "sshx" nil host "" nil))
;; change dir: user might want to connect to a different remote
(dir (when (file-remote-p default-directory)
(with-parsed-tramp-file-name default-directory cur
(when (string= cur-host host) default-directory)))))
(tramp-maybe-open-connection vec)
(with-current-buffer (tramp-get-connection-buffer vec)
(cider-locate-running-nrepl-ports dir))))))
(defun cider--completing-read-port (host ports)
"Interactively select port for HOST from PORTS."
(let* ((ports (cider-join-into-alist ports))
(sel-port (completing-read (format "Port for %s: " host) ports
nil nil nil nil (caar ports)))
(port (or (cdr (assoc sel-port ports)) sel-port))
(port (if (listp port) (cadr port) port)))
(if (stringp port) (string-to-number port) port)))
(defun cider-locate-running-nrepl-ports (&optional dir)
"Locate ports of running nREPL servers.
When DIR is non-nil also look for nREPL port files in DIR. Return a list
of list of the form (project-dir port)."
(let* ((paths (cider--running-nrepl-paths))
(proj-ports (mapcar (lambda (d)
(when-let ((port (and d (nrepl-extract-port (cider--file-path d)))))
(list (file-name-nondirectory (directory-file-name d)) port)))
(cons (clojure-project-dir dir) paths))))
(seq-uniq (delq nil proj-ports))))
(defun cider--running-nrepl-paths ()
"Retrieve project paths of running nREPL servers.
Use `cider-ps-running-nrepls-command' and `cider-ps-running-nrepl-path-regexp-list'."
(let (paths)
(with-temp-buffer
(insert (shell-command-to-string cider-ps-running-nrepls-command))
(dolist (regexp cider-ps-running-nrepl-path-regexp-list)
(goto-char 1)
(while (re-search-forward regexp nil t)
(setq paths (cons (match-string 1) paths)))))
(seq-uniq paths)))
(defun cider--identify-buildtools-present ()
"Identify build systems present by their build files."
(let* ((default-directory (clojure-project-dir (cider-current-dir)))
(build-files '(("lein" . "project.clj")
("boot" . "build.boot")
("gradle" . "build.gradle"))))
(delq nil
(mapcar (lambda (candidate)
(when (file-exists-p (cdr candidate))
(car candidate)))
build-files))))
(defun cider-project-type ()
"Determine the type, either leiningen, boot or gradle, of the current project.
If more than one project file types are present, check for a preferred
build tool in `cider-preferred-build-tool`, otherwise prompt the user to
choose."
(let* ((choices (cider--identify-buildtools-present))
(multiple-project-choices (> (length choices) 1))
(default (car choices)))
(cond ((and multiple-project-choices
(member cider-preferred-build-tool choices))
cider-preferred-build-tool)
(multiple-project-choices
(completing-read (format "Which command should be used (default %s): " default)
choices nil t nil nil default))
(choices
(car choices))
(t cider-default-repl-command))))
;; TODO: Implement a check for `cider-lein-command' over tramp
(defun cider--lein-resolve-command ()
"Find `cider-lein-command' on `exec-path' if possible, or return `nil'.
In case `default-directory' is non-local we assume the command is available."
(when-let ((command (or (file-remote-p default-directory)
(executable-find cider-lein-command)
(executable-find (concat cider-lein-command ".bat")))))
(shell-quote-argument command)))
(defun cider--boot-resolve-command ()
"Find `cider-boot-command' on `exec-path' if possible, or return `nil'.
In case `default-directory' is non-local we assume the command is available."
(when-let ((command (or (file-remote-p default-directory)
(executable-find cider-boot-command)
(executable-find (concat cider-boot-command ".exe")))))
(shell-quote-argument command)))
(defun cider--gradle-resolve-command ()
"Find `cider-gradle-command' on `exec-path' if possible, or return `nil'.
In case `default-directory' is non-local we assume the command is available."
(when-let ((command (or (file-remote-p default-directory)
(executable-find cider-gradle-command)
(executable-find (concat cider-gradle-command ".exe")))))
(shell-quote-argument command)))
;;; Check that the connection is working well
;; TODO: This is nrepl specific. It should eventually go into some cider-nrepl-client
;; file.
(defun cider--check-required-nrepl-version ()
"Check whether we're using a compatible nREPL version."
(if-let ((nrepl-version (cider--nrepl-version)))
(when (version< nrepl-version cider-required-nrepl-version)
(cider-repl-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
"CIDER requires nREPL %s (or newer) to work properly"
cider-required-nrepl-version))
(cider-repl-manual-warning "troubleshooting/#warning-saying-you-have-to-use-nrepl-0212"
"Can't determine nREPL's version.\nPlease, update nREPL to %s."
cider-required-nrepl-version)))
(defun cider--check-clojure-version-supported ()
"Ensure that we are meeting the minimum supported version of Clojure."
(if-let ((clojure-version (cider--clojure-version)))
(when (version< clojure-version cider-minimum-clojure-version)
(cider-repl-manual-warning "installation/#prerequisites"
"Clojure version (%s) is not supported (minimum %s). CIDER will not work."
clojure-version cider-minimum-clojure-version))
(cider-repl-manual-warning "installation/#prerequisites"
"Clojure version information could not be determined. Requires a minimum version %s."
cider-minimum-clojure-version)))
(defun cider--check-middleware-compatibility ()
"CIDER frontend/backend compatibility check.
Retrieve the underlying connection's CIDER-nREPL version and checks if the
middleware used is compatible with CIDER. If not, will display a warning
message in the REPL area."
(let* ((version-dict (nrepl-aux-info "cider-version" (cider-current-connection)))
(middleware-version (nrepl-dict-get version-dict "version-string" "not installed")))
(unless (equal cider-version middleware-version)
(cider-repl-manual-warning "troubleshooting/#cider-complains-of-the-cider-nrepl-version"
"CIDER's version (%s) does not match cider-nrepl's version (%s). Things will break!"
cider-version middleware-version))))
(defun cider--subscribe-repl-to-server-out ()
"Subscribe to the server's *out*."
(cider-nrepl-send-request '("op" "out-subscribe")
(cider-interactive-eval-handler (current-buffer))))
(defun cider--connected-handler ()
"Handle cider initialization after nREPL connection has been established.
This function is appended to `nrepl-connected-hook' in the client process
buffer."
;; `nrepl-connected-hook' is run in connection buffer
(cider-make-connection-default (current-buffer))
(cider-repl-init (current-buffer))
(cider--check-required-nrepl-version)
(cider--check-clojure-version-supported)
(cider--check-middleware-compatibility)
(cider--debug-init-connection)
(cider--subscribe-repl-to-server-out)
(when cider-auto-mode
(cider-enable-on-existing-clojure-buffers))
(run-hooks 'cider-connected-hook))
(defun cider--disconnected-handler ()
"Cleanup after nREPL connection has been lost or closed.
This function is appended to `nrepl-disconnected-hook' in the client
process buffer."
;; `nrepl-connected-hook' is run in connection buffer
(cider-possibly-disable-on-existing-clojure-buffers)
(run-hooks 'cider-disconnected-hook))
;;;###autoload
(eval-after-load 'clojure-mode
'(progn
(define-key clojure-mode-map (kbd "C-c M-j") #'cider-jack-in)
(define-key clojure-mode-map (kbd "C-c M-J") #'cider-jack-in-clojurescript)
(define-key clojure-mode-map (kbd "C-c M-c") #'cider-connect)))
(provide 'cider)
;;; cider.el ends here

File diff suppressed because it is too large Load Diff

View File

@ -1,187 +0,0 @@
;;; nrepl-dict.el --- Dictionary functions for Clojure nREPL -*- lexical-binding: t -*-
;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov
;; Copyright © 2013-2016 Bozhidar Batsov, Artur Malabarba and CIDER contributors
;;
;; Author: Tim King <kingtim@gmail.com>
;; Phil Hagelberg <technomancy@gmail.com>
;; Bozhidar Batsov <bozhidar@batsov.com>
;; Artur Malabarba <bruce.connor.am@gmail.com>
;; Hugo Duncan <hugo@hugoduncan.org>
;; Steve Purcell <steve@sanityinc.com>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Provides functions to interact with and create `nrepl-dict's. These are
;; simply plists with an extra element at the head.
;;; Code:
(require 'cl-lib)
(defun nrepl-dict (&rest key-vals)
"Create nREPL dict from KEY-VALS."
(cons 'dict key-vals))
(defun nrepl-dict-p (object)
"Return t if OBJECT is an nREPL dict."
(and (listp object)
(eq (car object) 'dict)))
(defun nrepl-dict-empty-p (dict)
"Return t if nREPL dict DICT is empty."
(null (cdr dict)))
(defun nrepl-dict-contains (dict key)
"Return nil if nREPL dict DICT doesn't contain KEY.
If DICT does contain KEY, then a non-nil value is returned. Due to the
current implementation, this return value is the tail of DICT's key-list
whose car is KEY. Comparison is done with `equal'."
(member key (nrepl-dict-keys dict)))
(defun nrepl-dict-get (dict key &optional default)
"Get from DICT value associated with KEY, optional DEFAULT if KEY not in DICT.
If dict is nil, return nil. If DEFAULT not provided, and KEY not in DICT,
return nil. If DICT is not an nREPL dict object, an error is thrown."
(when dict
(if (nrepl-dict-p dict)
(if (nrepl-dict-contains dict key)
(lax-plist-get (cdr dict) key)
default)
(error "Not an nREPL dict object: %s" dict))))
(defun nrepl-dict-put (dict key value)
"Associate in DICT, KEY to VALUE.
Return new dict. Dict is modified by side effects."
(if (null dict)
(list 'dict key value)
(if (not (nrepl-dict-p dict))
(error "Not an nREPL dict object: %s" dict)
(setcdr dict (lax-plist-put (cdr dict) key value))
dict)))
(defun nrepl-dict-keys (dict)
"Return all the keys in the nREPL DICT."
(if (nrepl-dict-p dict)
(cl-loop for l on (cdr dict) by #'cddr
collect (car l))
(error "Not an nREPL dict")))
(defun nrepl-dict-vals (dict)
"Return all the values in the nREPL DICT."
(if (nrepl-dict-p dict)
(cl-loop for l on (cdr dict) by #'cddr
collect (cadr l))
(error "Not an nREPL dict")))
(defun nrepl-dict-map (fn dict)
"Map FN on nREPL DICT.
FN must accept two arguments key and value."
(if (nrepl-dict-p dict)
(cl-loop for l on (cdr dict) by #'cddr
collect (funcall fn (car l) (cadr l)))
(error "Not an nREPL dict")))
(defun nrepl-dict-merge (dict1 dict2)
"Destructively merge DICT2 into DICT1.
Keys in DICT2 override those in DICT1."
(let ((base (or dict1 '(dict))))
(nrepl-dict-map (lambda (k v)
(nrepl-dict-put base k v))
(or dict2 '(dict)))
base))
(defun nrepl-dict-get-in (dict keys)
"Return the value in a nested DICT.
KEYS is a list of keys. Return nil if any of the keys is not present or if
any of the values is nil."
(let ((out dict))
(while (and keys out)
(setq out (nrepl-dict-get out (pop keys))))
out))
(defun nrepl-dict-flat-map (function dict)
"Map FUNCTION over DICT and flatten the result.
FUNCTION follows the same restrictions as in `nrepl-dict-map', and it must
also alway return a sequence (since the result will be flattened)."
(when dict
(apply #'append (nrepl-dict-map function dict))))
;;; More specific functions
(defun nrepl--cons (car list-or-dict)
"Generic cons of CAR to LIST-OR-DICT."
(if (eq (car list-or-dict) 'dict)
(cons 'dict (cons car (cdr list-or-dict)))
(cons car list-or-dict)))
(defun nrepl--nreverse (list-or-dict)
"Generic `nreverse' which works on LIST-OR-DICT."
(if (eq (car list-or-dict) 'dict)
(cons 'dict (nreverse (cdr list-or-dict)))
(nreverse list-or-dict)))
(defun nrepl--push (obj stack)
"Cons OBJ to the top element of the STACK."
;; stack is assumed to be a list
(if (eq (caar stack) 'dict)
(cons (cons 'dict (cons obj (cdar stack)))
(cdr stack))
(cons (if (null stack)
obj
(cons obj (car stack)))
(cdr stack))))
(defun nrepl--merge (dict1 dict2 &optional no-join)
"Join nREPL dicts DICT1 and DICT2 in a meaningful way.
String values for non \"id\" and \"session\" keys are concatenated. Lists
are appended. nREPL dicts merged recursively. All other objects are
accumulated into a list. DICT1 is modified destructively and
then returned.
If NO-JOIN is given, return the first non nil dict."
(if no-join
(or dict1 dict2)
(cond ((null dict1) dict2)
((null dict2) dict1)
((stringp dict1) (concat dict1 dict2))
((nrepl-dict-p dict1)
(nrepl-dict-map
(lambda (k2 v2)
(nrepl-dict-put dict1 k2
(nrepl--merge (nrepl-dict-get dict1 k2) v2
(member k2 '("id" "session")))))
dict2)
dict1)
((and (listp dict2) (listp dict1)) (append dict1 dict2))
((listp dict1) (append dict1 (list dict2)))
(t (list dict1 dict2)))))
;;; Dbind
(defmacro nrepl-dbind-response (response keys &rest body)
"Destructure an nREPL RESPONSE dict.
Bind the value of the provided KEYS and execute BODY."
(declare (debug (form (&rest symbolp) body)))
`(let ,(cl-loop for key in keys
collect `(,key (nrepl-dict-get ,response ,(format "%s" key))))
,@body))
(put 'nrepl-dbind-response 'lisp-indent-function 2)
(provide 'nrepl-dict)
;;; nrepl-dict.el ends here

View File

@ -1,121 +0,0 @@
;;; clojure-mode-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path))))
;;;### (autoloads nil "clojure-mode" "clojure-mode.el" (22516 57909
;;;;;; 485546 83000))
;;; Generated autoloads from clojure-mode.el
(autoload 'clojure-mode "clojure-mode" "\
Major mode for editing Clojure code.
\\{clojure-mode-map}
\(fn)" t nil)
(autoload 'clojure-unwind "clojure-mode" "\
Unwind thread at point or above point by one level.
Return nil if there are no more levels to unwind.
\(fn)" t nil)
(autoload 'clojure-unwind-all "clojure-mode" "\
Fully unwind thread at point or above point.
\(fn)" t nil)
(autoload 'clojure-thread "clojure-mode" "\
Thread by one more level an existing threading macro.
\(fn)" t nil)
(autoload 'clojure-thread-first-all "clojure-mode" "\
Fully thread the form at point using ->.
When BUT-LAST is passed the last expression is not threaded.
\(fn BUT-LAST)" t nil)
(autoload 'clojure-thread-last-all "clojure-mode" "\
Fully thread the form at point using ->>.
When BUT-LAST is passed the last expression is not threaded.
\(fn BUT-LAST)" t nil)
(autoload 'clojure-cycle-privacy "clojure-mode" "\
Make public the current private def, or vice-versa.
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-privacy
\(fn)" t nil)
(autoload 'clojure-convert-collection-to-list "clojure-mode" "\
Convert collection at (point) to list.
\(fn)" t nil)
(autoload 'clojure-convert-collection-to-quoted-list "clojure-mode" "\
Convert collection at (point) to quoted list.
\(fn)" t nil)
(autoload 'clojure-convert-collection-to-map "clojure-mode" "\
Convert collection at (point) to map.
\(fn)" t nil)
(autoload 'clojure-convert-collection-to-vector "clojure-mode" "\
Convert collection at (point) to vector.
\(fn)" t nil)
(autoload 'clojure-convert-collection-to-set "clojure-mode" "\
Convert collection at (point) to set.
\(fn)" t nil)
(autoload 'clojure-cycle-if "clojure-mode" "\
Change a surrounding if to if-not, or vice-versa.
See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-cycle-if
\(fn)" t nil)
(autoload 'clojurescript-mode "clojure-mode" "\
Major mode for editing ClojureScript code.
\\{clojurescript-mode-map}
\(fn)" t nil)
(autoload 'clojurec-mode "clojure-mode" "\
Major mode for editing ClojureC code.
\\{clojurec-mode-map}
\(fn)" t nil)
(autoload 'clojurex-mode "clojure-mode" "\
Major mode for editing ClojureX code.
\\{clojurex-mode-map}
\(fn)" t nil)
(add-to-list 'auto-mode-alist '("\\.\\(clj\\|dtm\\|edn\\)\\'" . clojure-mode))
(add-to-list 'auto-mode-alist '("\\.cljc\\'" . clojurec-mode))
(add-to-list 'auto-mode-alist '("\\.cljx\\'" . clojurex-mode))
(add-to-list 'auto-mode-alist '("\\.cljs\\'" . clojurescript-mode))
(add-to-list 'auto-mode-alist '("\\(?:build\\|profile\\)\\.boot\\'" . clojure-mode))
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:
;;; clojure-mode-autoloads.el ends here

View File

@ -1,2 +0,0 @@
;;; -*- no-byte-compile: t -*-
(define-package "clojure-mode" "20161004.2314" "Major mode for Clojure code" '((emacs "24.3")) :url "http://github.com/clojure-emacs/clojure-mode" :keywords '("languages" "clojure" "clojurescript" "lisp"))

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
;;; clojure-quick-repls-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (or (file-name-directory #$) (car load-path)))
;;;### (autoloads nil "clojure-quick-repls" "clojure-quick-repls.el"
;;;;;; (22500 1822 828219 293000))
;;; Generated autoloads from clojure-quick-repls.el
(autoload 'clojure-quick-repls-connect "clojure-quick-repls" "\
Launch Clojure and ClojureScript repls for the current project
\(fn)" t nil)
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:
;;; clojure-quick-repls-autoloads.el ends here

View File

@ -1 +0,0 @@
(define-package "clojure-quick-repls" "20150814.36" "Quickly create Clojure and ClojureScript repls for a project." '((cider "0.8.1") (dash "2.9.0")) :url "https://github.com/symfrog/clojure-quick-repls" :keywords '("languages" "clojure" "cider" "clojurescript"))

View File

@ -1,155 +0,0 @@
;;; clojure-quick-repls.el --- Quickly create Clojure and ClojureScript repls for a project.
;; Copyright (C) 2014 symfrog
;; URL: https://github.com/symfrog/clojure-quick-repls
;; Package-Version: 20150814.36
;; Keywords: languages, clojure, cider, clojurescript
;; Version: 0.2.0-cvs
;; Package-Requires: ((cider "0.8.1") (dash "2.9.0"))
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 3
;; of the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Quickly create Clojure and ClojureScript repls for a project.
;; Once the repls are created the usual CIDER commands can be used in either a clj/cljs buffer and the forms will be routed automatically via the correct connection.
;; So no need to manually switch connections!
;;; Installation:
;; Available as a package in melpa.org.
;; M-x package-install clojure-quick-repls
;;; Usage:
;; (require 'clojure-quick-repls)
;;; Code:
(require 'cider)
(require 'dash)
(defcustom clojure-quick-repls-cljs-setup
"(require 'cljs.repl.browser)
(cemerick.piggieback/cljs-repl
:repl-env (cljs.repl.browser/repl-env :port 9000))"
"Default form to initialize ClojureScript REPL"
:type '(string)
:group 'clojure-quick-repls)
(defvar clojure-quick-repls-nrepl-connected-fn nil)
(defvar clojure-quick-repls-current-buffer nil)
(defvar clojure-quick-repls-nrepl-connect-done nil)
(defvar clojure-quick-repls-clj-con-buf nil)
(defvar clojure-quick-repls-cljs-con-buf nil)
(defun clojure-quick-repls-noop-nrepl-connected-fn ()
(fset 'clojure-quick-repls-nrepl-connected-fn (lambda (buf) nil)))
(clojure-quick-repls-noop-nrepl-connected-fn)
(defun clojure-quick-repls-clear-con-bufs ()
(setq clojure-quick-repls-clj-con-buf nil)
(setq clojure-quick-repls-cljs-con-buf nil))
(add-hook 'nrepl-connected-hook (lambda ()
(clojure-quick-repls-nrepl-connected-fn clojure-quick-repls-current-buffer)))
(add-hook 'nrepl-disconnected-hook #'clojure-quick-repls-clear-con-bufs)
;;;###autoload
(defun clojure-quick-repls-connect ()
"Launch Clojure and ClojureScript repls for the current project"
(interactive)
(setq clojure-quick-repls-current-buffer (current-buffer))
(clojure-quick-repls-noop-nrepl-connected-fn)
(cider-jack-in)
(clojure-quick-repls-clear-con-bufs)
(lexical-let* ((cljs-fn (lambda (buf)
(with-current-buffer buf
(clojure-quick-repls-noop-nrepl-connected-fn)
(if (string= "ex" (cadr (nrepl-sync-request:eval clojure-quick-repls-cljs-setup)))
(message "Failed to initialize cljs connection with form %s" clojure-quick-repls-cljs-setup)
(progn
(setq clojure-quick-repls-cljs-con-buf (nrepl-current-connection-buffer))
(message "Clj connection buffer: %s Cljs connection buffer %s" clojure-quick-repls-clj-con-buf clojure-quick-repls-cljs-con-buf)
(message "Cljs browser repl ready")
; Make the clj buf default after completion
(nrepl-make-connection-default clojure-quick-repls-clj-con-buf))))))
(clj-fn (lambda (buf)
(with-current-buffer buf
(clojure-quick-repls-noop-nrepl-connected-fn )
(fset 'clojure-quick-repls-nrepl-connected-fn cljs-fn)
(setq clojure-quick-repls-clj-con-buf (nrepl-current-connection-buffer))
(message "Creating nrepl connection for cljs")
(clojure-quick-repls-new-repl-connection)))))
(fset 'clojure-quick-repls-nrepl-connected-fn clj-fn)))
(defun clojure-quick-repls-new-repl-connection ()
(let* ((host (nrepl-current-host))
(port (nrepl-extract-port)))
(message "Creating repl connection to nrepl server on port %s, host %s" host port)
(cider-connect host port)))
(defun clojure-quick-repls-bound-truthy-p (s)
(and (boundp s) (symbol-value s)))
(defun clojure-quick-repls-buffer-extension (buffer)
(let ((name (buffer-name buffer)))
(-when-let (p-loc (string-match-p "\\." name))
(substring name (1+ p-loc) nil))) )
(defun clojure-quick-repls-set-connection (f h)
(let ((ext (clojure-quick-repls-buffer-extension (current-buffer))))
(if (and (clojure-quick-repls-bound-truthy-p 'clojure-quick-repls-clj-con-buf)
(clojure-quick-repls-bound-truthy-p 'clojure-quick-repls-cljs-con-buf)
ext
(or (string= ext "clj") (string= ext "boot") (string= ext "cljs")))
(progn
(if (string= ext "cljs")
(nrepl-make-connection-default clojure-quick-repls-cljs-con-buf)
(nrepl-make-connection-default clojure-quick-repls-clj-con-buf))
(when f
(funcall f)))
(when h
(funcall h)))))
(defun clojure-quick-repls-switch-to-relevant-repl (arg)
(interactive)
(lexical-let ((a arg))
(clojure-quick-repls-set-connection (lambda () (cider-switch-to-current-repl-buffer a))
(lambda () (cider-switch-to-relevant-repl-buffer a)))))
(if (version< emacs-version "24.4")
(progn
(defadvice cider-interactive-eval (before clojure-quick-repls-nrepl-current-session activate)
(clojure-quick-repls-set-connection nil nil))
(defadvice cider-tooling-eval (before clojure-quick-repls-nrepl-current-session activate)
(clojure-quick-repls-set-connection nil nil))
(defadvice cider-complete-at-point (before clojure-quick-repls-nrepl-current-session activate)
(clojure-quick-repls-set-connection nil nil)))
(defun clojure-quick-repls-nrepl-current-session (&optional arg1 arg2 arg3)
(clojure-quick-repls-set-connection nil nil))
(advice-add 'cider-interactive-eval :before #'clojure-quick-repls-nrepl-current-session)
(advice-add 'cider-tooling-eval :before #'clojure-quick-repls-nrepl-current-session)
(advice-add 'cider-complete-at-point :before #'clojure-quick-repls-nrepl-current-session))
(provide 'clojure-quick-repls)
;;; clojure-quick-repls.el ends here

View File

@ -1,29 +0,0 @@
;;; flycheck-clojure-autoloads.el --- automatically extracted autoloads
;;
;;; Code:
(add-to-list 'load-path (or (file-name-directory #$) (car load-path)))
;;;### (autoloads nil "flycheck-clojure" "flycheck-clojure.el" (22500
;;;;;; 1821 852214 67000))
;;; Generated autoloads from flycheck-clojure.el
(autoload 'flycheck-clojure-parse-cider-errors "flycheck-clojure" "\
Parse cider errors from JSON VALUE from CHECKER.
Return a list of parsed `flycheck-error' objects.
\(fn VALUE CHECKER)" nil nil)
(autoload 'flycheck-clojure-setup "flycheck-clojure" "\
Setup Flycheck for Clojure.
\(fn)" t nil)
;;;***
;; Local Variables:
;; version-control: never
;; no-byte-compile: t
;; no-update-autoloads: t
;; End:
;;; flycheck-clojure-autoloads.el ends here

View File

@ -1 +0,0 @@
(define-package "flycheck-clojure" "20160704.1221" "Flycheck: Clojure support" '((cider "0.8.1") (flycheck "0.22alpha1") (let-alist "1.0.1") (emacs "24")) :url "https://github.com/clojure-emacs/squiggly-clojure")

View File

@ -1,221 +0,0 @@
;;; flycheck-clojure.el --- Flycheck: Clojure support -*- lexical-binding: t; -*-
;; Copyright © 2014 Peter Fraenkel
;; Copyright (C) 2014 Sebastian Wiesner <swiesner@lunaryorn.com>
;;
;; Author: Peter Fraenkel <pnf@podsnap.com>
;; Sebastian Wiesner <swiesner@lunaryorn.com>
;; Maintainer: Peter Fraenkel <pnf@podsnap.com>
;; URL: https://github.com/clojure-emacs/squiggly-clojure
;; Package-Version: 20160704.1221
;; Version: 1.1.0
;; Package-Requires: ((cider "0.8.1") (flycheck "0.22-cvs1") (let-alist "1.0.1") (emacs "24"))
;; This file is not part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; Add Clojure support to Flycheck.
;;
;; Provide syntax checkers to check Clojure code using a running Cider repl.
;;
;; Installation:
;;
;; (eval-after-load 'flycheck '(flycheck-clojure-setup))
;;; Code:
(require 'cider-client)
(require 'flycheck)
(require 'json)
(require 'url-parse)
(eval-when-compile (require 'let-alist))
(defcustom flycheck-clojure-inject-dependencies-at-jack-in t
"When nil, do not inject repl dependencies (i.e. the linters/checkers) at `cider-jack-in' time."
:group 'flycheck-clojure
:type 'boolean)
(defvar flycheck-clojure-dep-version "0.1.6"
"Version of `acyclic/squiggly-clojure' compatible with this version of flycheck-clojure.")
;;;###autoload
(defun flycheck-clojure-parse-cider-errors (value checker)
"Parse cider errors from JSON VALUE from CHECKER.
Return a list of parsed `flycheck-error' objects."
;; Parse the nested JSON from Cider. The outer JSON contains the return value
;; from Cider, and the inner JSON the errors returned by the individual
;; checker.
(let ((error-objects (json-read-from-string (json-read-from-string value))))
(mapcar (lambda (o)
(let-alist o
;; Use the file name reported by the syntax checker, but only if
;; its absolute, because typed reports relative file names that
;; are hard to expand correctly, since they are relative to the
;; source directory (not the project directory).
(let* ((parsed-file (when .file
(url-filename
(url-generic-parse-url .file))))
(filename (if (and parsed-file
(file-name-absolute-p parsed-file))
parsed-file
(buffer-file-name))))
(flycheck-error-new-at .line .column (intern .level) .msg
:checker checker
:filename filename))))
error-objects)))
(defun cider-flycheck-eval (input callback)
"Send the request INPUT and register the CALLBACK as the response handler.
Uses the tooling session, with no specified namespace."
(cider-tooling-eval input callback))
(defun flycheck-clojure-may-use-cider-checker ()
"Determine whether a cider checker may be used.
Checks for `cider-mode', and a current nREPL connection.
Standard predicate for cider checkers."
(let ((connection-buffer (cider-default-connection :no-error)))
(and (bound-and-true-p cider-mode)
connection-buffer
(buffer-live-p (get-buffer connection-buffer))
(clojure-find-ns))))
(defun flycheck-clojure-start-cider (checker callback)
"Start a cider syntax CHECKER with CALLBACK."
(let ((ns (clojure-find-ns))
(form (get checker 'flycheck-clojure-form)))
(cider-flycheck-eval
(funcall form ns)
(nrepl-make-response-handler
(current-buffer)
(lambda (buffer value)
(funcall callback 'finished
(with-current-buffer buffer
(flycheck-clojure-parse-cider-errors value checker))))
nil ; stdout
nil ; stderr
(lambda (_)
;; If the evaluation completes without returning any value, there has
;; gone something wrong. Ideally, we'd report *what* was wrong, but
;; `nrepl-make-response-handler' is close to useless for this :(,
;; because it just `message's for many status codes that are errors for
;; us :(
(funcall callback 'errored "Done with no errors"))
(lambda (_buffer ex _rootex _sess)
(funcall callback 'errored
(format "Form %s of checker %s failed: %s"
form checker ex))))))
)
(defun flycheck-clojure-define-cider-checker (name docstring &rest properties)
"Define a Cider syntax checker with NAME, DOCSTRING and PROPERTIES.
NAME, DOCSTRING, and PROPERTIES are like for
`flycheck-define-generic-checker', except that `:start' and
`:modes' are invalid PROPERTIES. A syntax checker defined with
this function will always check in `clojure-mode', and only if
`cider-mode' is enabled.
Instead of `:start', this syntax checker requires a `:form
FUNCTION' property. FUNCTION takes the current Clojure namespace
as single argument, and shall return a string containing a
Clojure form to be sent to Cider to check the current buffer."
(declare (indent 1)
(doc-string 2))
(let* ((form (plist-get properties :form))
(orig-predicate (plist-get properties :predicate)))
(when (plist-get :start properties)
(error "Checker %s may not have :start" name))
(when (plist-get :modes properties)
(error "Checker %s may not have :modes" name))
(unless (functionp form)
(error ":form %s of %s not a valid function" form name))
(apply #'flycheck-define-generic-checker
name docstring
:start #'flycheck-clojure-start-cider
:modes '(clojure-mode)
:predicate (if orig-predicate
(lambda ()
(and (flycheck-clojure-may-use-cider-checker)
(funcall orig-predicate)))
#'flycheck-clojure-may-use-cider-checker)
properties)
(put name 'flycheck-clojure-form form)))
(flycheck-clojure-define-cider-checker 'clojure-cider-eastwood
"A syntax checker for Clojure, using Eastwood in Cider.
See URL `https://github.com/jonase/eastwood' and URL
`https://github.com/clojure-emacs/cider/' for more information."
:form (lambda (ns)
(format "(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-ew '%s))"
ns))
:next-checkers '(clojure-cider-kibit clojure-cider-typed))
(flycheck-clojure-define-cider-checker 'clojure-cider-kibit
"A syntax checker for Clojure, using Kibit in Cider.
See URL `https://github.com/jonase/kibit' and URL
`https://github.com/clojure-emacs/cider/' for more information."
:form (lambda (ns)
(format
"(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-kb '%s %s))"
ns
;; Escape file name for Clojure
(flycheck-sexp-to-string (buffer-file-name))))
:predicate (lambda () (buffer-file-name))
:next-checkers '(clojure-cider-typed))
(flycheck-clojure-define-cider-checker 'clojure-cider-typed
"A syntax checker for Clojure, using Typed Clojure in Cider.
See URL `https://github.com/clojure-emacs/cider/' and URL
`https://github.com/clojure/core.typed' for more information."
:form (lambda (ns)
(format
"(do (require 'squiggly-clojure.core) (squiggly-clojure.core/check-tc '%s))"
ns)))
(defun flycheck-clojure-inject-jack-in-dependencies ()
"Inject the REPL dependencies of flycheck-clojure at `cider-jack-in'.
If injecting the dependencies is not preferred set `flycheck-clojure-inject-dependencies-at-jack-in' to nil."
(when (and flycheck-clojure-inject-dependencies-at-jack-in
(boundp 'cider-jack-in-dependencies))
(add-to-list 'cider-jack-in-dependencies `("acyclic/squiggly-clojure" ,flycheck-clojure-dep-version))))
;;;###autoload
(defun flycheck-clojure-setup ()
"Setup Flycheck for Clojure."
(interactive)
;; Add checkers in reverse order, because `add-to-list' adds to front.
(dolist (checker '(clojure-cider-typed
clojure-cider-kibit
clojure-cider-eastwood))
(add-to-list 'flycheck-checkers checker))
(flycheck-clojure-inject-jack-in-dependencies))
(provide 'flycheck-clojure)
;; Local Variables:
;; indent-tabs-mode: nil
;; End:
;;; flycheck-clojure.el ends here

View File

@ -73,7 +73,7 @@
("e6h" . "http://www.e6h.org/packages/"))))
'(package-selected-packages
(quote
(gobgen goto-last-change wakatime-mode command-log-mode magithub nyan-prompt zone-nyan helm-google helm-projectile helm-spotify helm-swoop helm-unicode id-manager identica-mode mc-extras multiple-cursors electric-spacing flycheck-clojure flycheck-pkg-config focus git-messenger gitconfig github-notifier gnome-calendar gnugo google helm-chrome helm-company helm-flycheck clojure-quick-repls electric-case emamux flycheck drag-stuff django-manage clojure-mode hyde org-jekyll smart-mode-line-powerline-theme yaml-mode xlicense vala-mode sass-mode nyan-mode muse markdown-mode mark magit-gerrit json-mode js2-mode jinja2-mode helm-make helm-gtags helm-flyspell helm-ag go-mode gitignore-mode gitconfig-mode git-gutter ggtags fiplr erlang django-mode company-shell company-quickhelp company-c-headers coffee-mode buffer-move ag)))
(gobgen goto-last-change wakatime-mode command-log-mode magithub nyan-prompt zone-nyan helm-google helm-projectile helm-spotify helm-swoop helm-unicode id-manager identica-mode mc-extras multiple-cursors electric-spacing flycheck-pkg-config focus git-messenger gitconfig github-notifier gnome-calendar gnugo google helm-chrome helm-company helm-flycheck electric-case emamux flycheck drag-stuff django-manage hyde org-jekyll smart-mode-line-powerline-theme yaml-mode xlicense vala-mode sass-mode nyan-mode muse markdown-mode mark magit-gerrit json-mode js2-mode jinja2-mode helm-make helm-gtags helm-flyspell helm-ag go-mode gitignore-mode gitconfig-mode git-gutter ggtags fiplr erlang django-mode company-shell company-quickhelp company-c-headers coffee-mode buffer-move ag)))
'(safe-local-variable-values
(quote
((company-clang-arguments "-I.." "-I/home/polesz/jhbuild/install/include/atk-1.0" "-I/home/polesz/jhbuild/install/include/at-spi-2.0" "-I/home/polesz/jhbuild/install/include/at-spi2-atk/2.0" "-I/home/polesz/jhbuild/install/include/cairo" "-I/home/polesz/jhbuild/install/include/gdk-pixbuf-2.0" "-I/home/polesz/jhbuild/install/include/gio-unix-2.0/" "-I/home/polesz/jhbuild/install/include/glib-2.0" "-I/home/polesz/jhbuild/install/include/gtk-3.0" "-I/home/polesz/jhbuild/install/include/harfbuzz" "-I/home/polesz/jhbuild/install/include/libgda-5.0" "-I/home/polesz/jhbuild/install/include/libgda-5.0/libgda" "-I/home/polesz/jhbuild/install/include/librsvg-2.0" "-I/home/polesz/jhbuild/install/include/libsoup-2.4" "-I/home/polesz/jhbuild/install/include/pango-1.0" "-I/home/polesz/jhbuild/install/include/swe-glib" "-I/home/polesz/jhbuild/install/include/webkitgtk-4.0" "-I/home/polesz/jhbuild/install/lib/glib-2.0/include" "-I/usr/include/dbus-1.0" "-I/usr/include/freetype2" "-I/usr/include/libdrm" "-I/usr/include/libpng16" "-I/usr/include/libxml2" "-I/usr/include/pixman-1" "-I/usr/lib64/dbus-1.0/include")