Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
12 KiB

4 years ago
  1. ;;; cider-ns.el --- Namespace manipulation functionality -*- lexical-binding: t -*-
  2. ;; Copyright © 2013-2019 Bozhidar Batsov, Artur Malabarba and CIDER contributors
  3. ;;
  4. ;; Author: Bozhidar Batsov <bozhidar@batsov.com>
  5. ;; Artur Malabarba <bruce.connor.am@gmail.com>
  6. ;; This program is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; This program is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;; This file is not part of GNU Emacs.
  17. ;;; Commentary:
  18. ;; Smart code refresh functionality based on ideas from:
  19. ;; http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
  20. ;;
  21. ;; Note that refresh with clojure.tools.namespace.repl is a smarter way to
  22. ;; reload code: the traditional way to reload Clojure code without restarting
  23. ;; the JVM is (require ... :reload) or an editor/IDE feature that does the same
  24. ;; thing.
  25. ;;
  26. ;; This has several problems:
  27. ;;
  28. ;; If you modify two namespaces which depend on each other, you must remember to
  29. ;; reload them in the correct order to avoid compilation errors.
  30. ;;
  31. ;; If you remove definitions from a source file and then reload it, those
  32. ;; definitions are still available in memory. If other code depends on those
  33. ;; definitions, it will continue to work but will break the next time you
  34. ;; restart the JVM.
  35. ;;
  36. ;; If the reloaded namespace contains defmulti, you must also reload all of the
  37. ;; associated defmethod expressions.
  38. ;;
  39. ;; If the reloaded namespace contains defprotocol, you must also reload any
  40. ;; records or types implementing that protocol and replace any existing
  41. ;; instances of those records/types with new instances.
  42. ;;
  43. ;; If the reloaded namespace contains macros, you must also reload any
  44. ;; namespaces which use those macros.
  45. ;;
  46. ;; If the running program contains functions which close over values in the
  47. ;; reloaded namespace, those closed-over values are not updated (This is common
  48. ;; in web applications which construct the "handler stack" as a composition of
  49. ;; functions.)
  50. ;;; Code:
  51. (require 'map)
  52. (require 'seq)
  53. (require 'subr-x)
  54. (require 'cider-client)
  55. (require 'cider-popup)
  56. (require 'cider-stacktrace)
  57. (define-obsolete-variable-alias 'cider-save-files-on-cider-ns-refresh 'cider-ns-save-files-on-refresh "0.18")
  58. (defcustom cider-ns-save-files-on-refresh 'prompt
  59. "Controls whether to prompt to save files before refreshing.
  60. If nil, files are not saved.
  61. If 'prompt, the user is prompted to save files if they have been modified.
  62. If t, save the files without confirmation."
  63. :type '(choice (const prompt :tag "Prompt to save files if they have been modified")
  64. (const nil :tag "Don't save the files")
  65. (const t :tag "Save the files without confirmation"))
  66. :group 'cider
  67. :package-version '(cider . "0.15.0"))
  68. (defcustom cider-ns-save-files-on-refresh-modes '(clojure-mode)
  69. "Controls which files might be saved before refreshing.
  70. If a list of modes, any buffers visiting files on the classpath whose major
  71. mode is derived from any of the modes might be saved.
  72. If t, all buffers visiting files on the classpath might be saved."
  73. :type '(choice listp
  74. (const t))
  75. :group 'cider
  76. :package-version '(cider . "0.21.0"))
  77. (defconst cider-ns-refresh-log-buffer "*cider-ns-refresh-log*")
  78. (define-obsolete-variable-alias 'cider-refresh-show-log-buffer 'cider-ns-refresh-show-log-buffer "0.18")
  79. (defcustom cider-ns-refresh-show-log-buffer nil
  80. "Controls when to display the refresh log buffer.
  81. If non-nil, the log buffer will be displayed every time `cider-ns-refresh' is
  82. called. If nil, the log buffer will still be written to, but will never be
  83. displayed automatically. Instead, the most relevant information will be
  84. displayed in the echo area."
  85. :type '(choice (const :tag "always" t)
  86. (const :tag "never" nil))
  87. :group 'cider
  88. :package-version '(cider . "0.10.0"))
  89. (define-obsolete-variable-alias 'cider-refresh-before-fn 'cider-ns-refresh-before-fn "0.18")
  90. (defcustom cider-ns-refresh-before-fn nil
  91. "Clojure function for `cider-ns-refresh' to call before reloading.
  92. If nil, nothing will be invoked before reloading. Must be a
  93. namespace-qualified function of zero arity. Any thrown exception will
  94. prevent reloading from occurring."
  95. :type 'string
  96. :group 'cider
  97. :package-version '(cider . "0.10.0"))
  98. (define-obsolete-variable-alias 'cider-refresh-after-fn 'cider-ns-refresh-after-fn "0.18")
  99. (defcustom cider-ns-refresh-after-fn nil
  100. "Clojure function for `cider-ns-refresh' to call after reloading.
  101. If nil, nothing will be invoked after reloading. Must be a
  102. namespace-qualified function of zero arity."
  103. :type 'string
  104. :group 'cider
  105. :package-version '(cider . "0.10.0"))
  106. (defun cider-ns-refresh--handle-response (response log-buffer)
  107. "Refresh LOG-BUFFER with RESPONSE."
  108. (nrepl-dbind-response response (out err reloading status error error-ns after before)
  109. (cl-flet* ((log (message &optional face)
  110. (cider-emit-into-popup-buffer log-buffer message face t))
  111. (log-echo (message &optional face)
  112. (log message face)
  113. (unless cider-ns-refresh-show-log-buffer
  114. (let ((message-truncate-lines t))
  115. (message "cider-ns-refresh: %s" message)))))
  116. (cond
  117. (out
  118. (log out))
  119. (err
  120. (log err 'font-lock-warning-face))
  121. ((member "invoking-before" status)
  122. (log-echo (format "Calling %s\n" before) 'font-lock-string-face))
  123. ((member "invoked-before" status)
  124. (log-echo (format "Successfully called %s\n" before) 'font-lock-string-face))
  125. ((member "invoked-not-resolved" status)
  126. (log-echo "Could not resolve refresh function\n" 'font-lock-string-face))
  127. (reloading
  128. (log-echo (format "Reloading %s\n" reloading) 'font-lock-string-face))
  129. ((member "reloading" (nrepl-dict-keys response))
  130. (log-echo "Nothing to reload\n" 'font-lock-string-face))
  131. ((member "ok" status)
  132. (log-echo "Reloading successful\n" 'font-lock-string-face))
  133. (error-ns
  134. (log-echo (format "Error reloading %s\n" error-ns) 'font-lock-warning-face))
  135. ((member "invoking-after" status)
  136. (log-echo (format "Calling %s\n" after) 'font-lock-string-face))
  137. ((member "invoked-after" status)
  138. (log-echo (format "Successfully called %s\n" after) 'font-lock-string-face))))
  139. (with-selected-window (or (get-buffer-window cider-ns-refresh-log-buffer)
  140. (selected-window))
  141. (with-current-buffer cider-ns-refresh-log-buffer
  142. (goto-char (point-max))))
  143. (when (member "error" status)
  144. (cider--render-stacktrace-causes error))))
  145. (defun cider-ns-refresh--save-modified-buffers ()
  146. "Ensure any relevant modified buffers are saved before refreshing.
  147. Its behavior is controlled by `cider-ns-save-files-on-refresh' and
  148. `cider-ns-save-files-on-refresh-modes'."
  149. (when cider-ns-save-files-on-refresh
  150. (let ((dirs (seq-filter #'file-directory-p
  151. (cider-classpath-entries))))
  152. (save-some-buffers
  153. (not (eq cider-ns-save-files-on-refresh 'prompt))
  154. (lambda ()
  155. (and (seq-some #'derived-mode-p cider-ns-save-files-on-refresh-modes)
  156. (seq-some (lambda (dir)
  157. (file-in-directory-p buffer-file-name dir))
  158. dirs)))))))
  159. ;;;###autoload
  160. (defun cider-ns-reload (&optional prompt)
  161. "Send a (require 'ns :reload) to the REPL.
  162. With an argument PROMPT, it prompts for a namespace name. This is the
  163. Clojure out of the box reloading experience and does not rely on
  164. org.clojure/tools.namespace. See Commentary of this file for a longer list
  165. of differences. From the Clojure doc: \":reload forces loading of all the
  166. identified libs even if they are already loaded\"."
  167. (interactive "P")
  168. (let ((ns (if prompt
  169. (string-remove-prefix "'" (read-from-minibuffer "Namespace: " (clojure-find-ns)))
  170. (clojure-find-ns))))
  171. (cider-interactive-eval (format "(require '%s :reload)" ns))))
  172. ;;;###autoload
  173. (defun cider-ns-reload-all (&optional prompt)
  174. "Send a (require 'ns :reload-all) to the REPL.
  175. With an argument PROMPT, it prompts for a namespace name. This is the
  176. Clojure out of the box reloading experience and does not rely on
  177. org.clojure/tools.namespace. See Commentary of this file for a longer list
  178. of differences. From the Clojure doc: \":reload-all implies :reload and
  179. also forces loading of all libs that the identified libs directly or
  180. indirectly load via require\"."
  181. (interactive "P")
  182. (let ((ns (if prompt
  183. (string-remove-prefix "'" (read-from-minibuffer "Namespace: " (clojure-find-ns)))
  184. (clojure-find-ns))))
  185. (cider-interactive-eval (format "(require '%s :reload-all)" ns))))
  186. ;;;###autoload
  187. (defun cider-ns-refresh (&optional mode)
  188. "Reload modified and unloaded namespaces on the classpath.
  189. With a single prefix argument, or if MODE is `refresh-all', reload all
  190. namespaces on the classpath unconditionally.
  191. With a double prefix argument, or if MODE is `clear', clear the state of
  192. the namespace tracker before reloading. This is useful for recovering from
  193. some classes of error (for example, those caused by circular dependencies)
  194. that a normal reload would not otherwise recover from. The trade-off of
  195. clearing is that stale code from any deleted files may not be completely
  196. unloaded.
  197. With a negative prefix argument, or if MODE is `inhibit-fns', prevent any
  198. refresh functions (defined in `cider-ns-refresh-before-fn' and
  199. `cider-ns-refresh-after-fn') from being invoked."
  200. (interactive "p")
  201. (cider-ensure-connected)
  202. (cider-ensure-op-supported "refresh")
  203. (cider-ns-refresh--save-modified-buffers)
  204. (let ((clear? (member mode '(clear 16)))
  205. (refresh-all? (member mode '(refresh-all 4)))
  206. (inhibit-refresh-fns (member mode '(inhibit-fns -1))))
  207. (cider-map-repls :clj
  208. (lambda (conn)
  209. ;; Inside the lambda, so the buffer is not created if we error out.
  210. (let ((log-buffer (or (get-buffer cider-ns-refresh-log-buffer)
  211. (cider-make-popup-buffer cider-ns-refresh-log-buffer))))
  212. (when cider-ns-refresh-show-log-buffer
  213. (cider-popup-buffer-display log-buffer))
  214. (when inhibit-refresh-fns
  215. (cider-emit-into-popup-buffer log-buffer
  216. "inhibiting refresh functions\n"
  217. nil
  218. t))
  219. (when clear?
  220. (cider-nrepl-send-sync-request '("op" "refresh-clear") conn))
  221. (cider-nrepl-send-request
  222. (thread-last
  223. (map-merge 'list
  224. `(("op" ,(if refresh-all? "refresh-all" "refresh")))
  225. (cider--nrepl-print-request-map fill-column)
  226. (when (and (not inhibit-refresh-fns) cider-ns-refresh-before-fn)
  227. `(("before" ,cider-ns-refresh-before-fn)))
  228. (when (and (not inhibit-refresh-fns) cider-ns-refresh-after-fn)
  229. `(("after" ,cider-ns-refresh-after-fn))))
  230. (seq-mapcat #'identity))
  231. (lambda (response)
  232. (cider-ns-refresh--handle-response response log-buffer))
  233. conn))))))
  234. ;;;###autoload
  235. (define-obsolete-function-alias 'cider-refresh 'cider-ns-refresh "0.18")
  236. (provide 'cider-ns)
  237. ;;; cider-ns.el ends here