|
| 1 | +;;; cider-resolve.el --- Resolve clojure symbols according to current nREPL connection |
| 2 | + |
| 3 | +;; Copyright © 2015 Artur Malabarba |
| 4 | + |
| 5 | +;; Author: Artur Malabarba <[email protected]> |
| 6 | + |
| 7 | +;; This program is free software; you can redistribute it and/or modify |
| 8 | +;; it under the terms of the GNU General Public License as published by |
| 9 | +;; the Free Software Foundation, either version 3 of the License, or |
| 10 | +;; (at your option) any later version. |
| 11 | + |
| 12 | +;; This program is distributed in the hope that it will be useful, |
| 13 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | +;; GNU General Public License for more details. |
| 16 | + |
| 17 | +;; You should have received a copy of the GNU General Public License |
| 18 | +;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | + |
| 20 | +;;; Commentary: |
| 21 | + |
| 22 | +;; The ns cache is a dict of namespaces stored in the connection buffer. This |
| 23 | +;; file offers functions to easily get information about variables from this |
| 24 | +;; cache, given the variable's name and the file's namespace. This |
| 25 | +;; functionality is similar to that offered by the `cider-var-info' function |
| 26 | +;; (and others). The difference is that all functions in this file operate |
| 27 | +;; without contacting the server (they still rely on an active connection |
| 28 | +;; buffer, but no messages are actually exchanged). |
| 29 | + |
| 30 | +;; For this reason, the functions here are well suited for very |
| 31 | +;; performance-sentitive operations, such as font-locking or |
| 32 | +;; indentation. Meanwhile, operations like code-jumping are better off |
| 33 | +;; communicating with the middleware, just in the off chance that the cache is |
| 34 | +;; outdated. |
| 35 | + |
| 36 | +;; Below is a typical entry on this cache dict. Note that clojure.core symbols |
| 37 | +;; are excluded from the refers to save space. |
| 38 | + |
| 39 | +;; "cider.nrepl.middleware.track-state" |
| 40 | +;; (dict "aliases" |
| 41 | +;; (dict "cljs" "cider.nrepl.middleware.util.cljs" |
| 42 | +;; "misc" "cider.nrepl.middleware.util.misc" |
| 43 | +;; "set" "clojure.set") |
| 44 | +;; "interns" (dict a |
| 45 | +;; "assoc-state" (dict "arglists" |
| 46 | +;; (("response" |
| 47 | +;; (dict "as" "msg" "keys" |
| 48 | +;; ("session"))))) |
| 49 | +;; "filter-core" (dict "arglists" |
| 50 | +;; (("refers"))) |
| 51 | +;; "make-transport" (dict "arglists" |
| 52 | +;; (((dict "as" "msg" "keys" |
| 53 | +;; ("transport"))))) |
| 54 | +;; "ns-as-map" (dict "arglists" |
| 55 | +;; (("ns"))) |
| 56 | +;; "ns-cache" (dict) |
| 57 | +;; "relevant-meta" (dict "arglists" |
| 58 | +;; (("var"))) |
| 59 | +;; "update-vals" (dict "arglists" |
| 60 | +;; (("m" "f"))) |
| 61 | +;; "wrap-tracker" (dict "arglists" |
| 62 | +;; (("handler")))) |
| 63 | +;; "refers" (dict "set-descriptor!" "#'clojure.tools.nrepl.middleware/set-descriptor!")) |
| 64 | + |
| 65 | +;;; Code: |
| 66 | + |
| 67 | +(require 'nrepl-client) |
| 68 | +(require 'cider-interaction) |
| 69 | + |
| 70 | +(defvar cider-repl-ns-cache) |
| 71 | + |
| 72 | +(defun cider-resolve--get-in (&rest keys) |
| 73 | + "Return (nrepl-dict-get-in cider-repl-ns-cache KEYS)." |
| 74 | + (when cider-connections |
| 75 | + (with-current-buffer (cider-current-connection) |
| 76 | + (nrepl-dict-get-in cider-repl-ns-cache keys)))) |
| 77 | + |
| 78 | +(defun cider-resolve-alias (ns alias) |
| 79 | + "Return the namespace that ALIAS refers to in namespace NS. |
| 80 | +If it doesn't point anywhere, returns ALIAS." |
| 81 | + (or (cider-resolve--get-in ns "aliases" alias) |
| 82 | + alias)) |
| 83 | + |
| 84 | +(defconst cider-resolve--prefix-regexp "\\`\\(?:#'\\)?\\([^/]+\\)/") |
| 85 | + |
| 86 | +(defun cider-resolve-var (ns var) |
| 87 | + "Return a dict of the metadata of a clojure var VAR in namespace NS. |
| 88 | +VAR is a string. |
| 89 | +Return nil only if VAR cannot be resolved." |
| 90 | + (let* ((var-ns (when (string-match cider-resolve--prefix-regexp var) |
| 91 | + (cider-resolve-alias ns (match-string 1 var)))) |
| 92 | + (name (replace-regexp-in-string cider-resolve--prefix-regexp "" var))) |
| 93 | + (or |
| 94 | + (cider-resolve--get-in (or var-ns ns) "interns" name) |
| 95 | + (unless var-ns |
| 96 | + ;; If the var had no prefix, it might be referred. |
| 97 | + (-if-let (referal (cider-resolve--get-in ns "refers" name)) |
| 98 | + (cider-resolve-var ns referal) |
| 99 | + ;; Or it might be from core. |
| 100 | + (unless (equal ns "clojure.core") |
| 101 | + (cider-resolve-var "clojure.core" name))))))) |
| 102 | + |
| 103 | +(defun cider-resolve-core-ns () |
| 104 | + "Return a dict of the core namespace for current connection. |
| 105 | +This will be clojure.core or cljs.core depending on `cider-repl-type'." |
| 106 | + (when (cider-connected-p) |
| 107 | + (with-current-buffer (cider-current-connection) |
| 108 | + (cider-resolve--get-in (if (equal cider-repl-type "cljs") |
| 109 | + "cljs.core" |
| 110 | + "clojure.core"))))) |
| 111 | + |
| 112 | +(defun cider-resolve-ns-symbols (ns) |
| 113 | + "Return a dict of all valid symbols in NS. |
| 114 | +Each entry's value is the metadata of the var that the symbol refers to. |
| 115 | +NS can be the namespace name, or a dict of the namespace itself." |
| 116 | + (-when-let (dict (if (stringp ns) |
| 117 | + (cider-resolve--get-in ns) |
| 118 | + ns)) |
| 119 | + (nrepl-dbind-response dict (interns refers aliases) |
| 120 | + (append interns |
| 121 | + (nrepl-dict-flat-map (lambda (sym var) (list sym (cider-resolve-var ns var))) |
| 122 | + refers) |
| 123 | + (nrepl-dict-flat-map (lambda (alias namespace) |
| 124 | + (nrepl-dict-flat-map (lambda (sym meta) |
| 125 | + (list (concat alias "/" sym) meta)) |
| 126 | + (cider-resolve--get-in namespace "interns"))) |
| 127 | + aliases))))) |
| 128 | + |
| 129 | +(provide 'cider-resolve) |
| 130 | +;;; cider-resolve.el ends here |
0 commit comments