about summary refs log tree commit diff
path: root/configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el
diff options
context:
space:
mode:
Diffstat (limited to 'configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el')
-rw-r--r--configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el357
1 files changed, 357 insertions, 0 deletions
diff --git a/configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el b/configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el
new file mode 100644
index 000000000000..d58352b16896
--- /dev/null
+++ b/configs/shared/emacs/.emacs.d/elpa/cider-20180908.1925/cider-browse-spec.el
@@ -0,0 +1,357 @@
+;;; cider-browse-spec.el --- CIDER spec browser
+
+;; Copyright © 2017 Juan Monetta, Bozhidar Batsov and CIDER contributors
+
+;; Author: Juan Monetta <jpmonettas@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-spec
+;;
+;; Display a spec description you can browse.
+;; Pressing <enter> over a sub spec will take you to the description of that sub spec.
+;; Pressing ^ takes you to the list of all specs.
+
+;; M-x cider-browse-spec-all
+;;
+;; Explore clojure.spec registry by browsing a list of all specs.
+;; Pressing <enter> over a spec display the spec description you can browse.
+
+;;; Code:
+
+(require 'cider-client)
+(require 'cider-compat)
+(require 'cider-util)
+(require 'cl-lib)
+(require 'nrepl-dict)
+(require 'seq)
+(require 'subr-x)
+(require 'help-mode)
+
+;; The buffer names used by the spec browser
+(defconst cider-browse-spec-buffer "*cider-spec-browser*")
+(defconst cider-browse-spec-example-buffer "*cider-spec-example*")
+
+;; Mode Definition
+
+(defvar cider-browse-spec-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map (make-composed-keymap button-buffer-map
+                                                 cider-popup-buffer-mode-map))
+    (define-key map (kbd "RET") #'cider-browse-spec--browse-at)
+    (define-key map "n" #'forward-button)
+    (define-key map "p" #'backward-button)
+    map)
+  "Keymap for `cider-browse-spec-mode'.")
+
+(define-derived-mode cider-browse-spec-mode special-mode "Specs"
+  "Major mode for browsing Clojure specs.
+
+\\{cider-browse-spec-mode-map}"
+  (setq-local electric-indent-chars nil)
+  (setq-local sesman-system 'CIDER)
+  (when cider-special-mode-truncate-lines
+    (setq-local truncate-lines t)))
+
+(defvar cider-browse-spec--current-spec nil)
+
+(defvar cider-browse-spec-view-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map help-mode-map)
+    (define-key map (kbd "RET") #'cider-browse-spec--browse-at)
+    (define-key map "^" #'cider-browse-spec-all)
+    (define-key map "e" #'cider-browse-spec--print-curr-spec-example)
+    (define-key map "n" #'forward-button)
+    (define-key map "p" #'backward-button)
+    map)
+  "Keymap for `cider-browse-spec-view-mode'.")
+
+(define-derived-mode cider-browse-spec-view-mode help-mode "Spec"
+  "Major mode for displaying CIDER spec.
+
+\\{cider-browse-spec-view-mode-map}"
+  (setq-local cider-browse-spec--current-spec nil)
+  (setq-local electric-indent-chars nil)
+  (setq-local sesman-system 'CIDER)
+  (when cider-special-mode-truncate-lines
+    (setq-local truncate-lines t)))
+
+(defvar cider-browse-spec-example-mode-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map cider-popup-buffer-mode-map)
+    (define-key map "^" #'cider-browse-spec-all)
+    (define-key map "e" #'cider-browse-spec--print-curr-spec-example)
+    (define-key map "g" #'revert-buffer)
+    map)
+  "Keymap for `cider-browse-spec-example-mode'.")
+
+(define-derived-mode cider-browse-spec-example-mode special-mode "Example"
+  "Major mode for Clojure spec examples.
+
+\\{cider-browse-spec-example-mode-map}"
+  (setq-local electric-indent-chars nil)
+  (setq-local revert-buffer-function #'cider-browse-spec--example-revert-buffer-function)
+  (setq-local sesman-system 'CIDER)
+  (when cider-special-mode-truncate-lines
+    (setq-local truncate-lines t)))
+
+;; Non interactive functions
+
+(define-button-type 'cider-browse-spec--spec
+  'action #'cider-browse-spec--browse-at
+  'face nil
+  'follow-link t
+  'help-echo "View spec")
+
+(defun cider-browse-spec--draw-list-buffer (buffer title specs)
+  "Reset contents of BUFFER.
+Display TITLE at the top and SPECS are indented underneath."
+  (with-current-buffer buffer
+    (cider-browse-spec-mode)
+    (let ((inhibit-read-only t))
+      (erase-buffer)
+      (goto-char (point-max))
+      (insert (cider-propertize title 'emph) "\n")
+      (dolist (spec-name specs)
+        (insert (propertize "  " 'spec-name spec-name))
+        (thread-first (cider-font-lock-as-clojure spec-name)
+          (insert-text-button 'type 'cider-browse-spec--spec)
+          (button-put 'spec-name spec-name))
+        (insert (propertize "\n" 'spec-name spec-name)))
+      (goto-char (point-min)))))
+
+(defun cider--qualified-keyword-p (str)
+  "Return non nil if STR is a namespaced keyword."
+  (string-match-p "^:.+/.+$" str))
+
+(defun cider--spec-fn-p (value fn-name)
+  "Return non nil if VALUE is clojure.spec.[alpha]/FN-NAME."
+  (string-match-p (concat "^\\(clojure.spec\\|clojure.spec.alpha\\)/" fn-name "$") value))
+
+(defun cider-browse-spec--pprint (form)
+  "Given a spec FORM builds a multi line string with a pretty render of that FORM."
+  (cond ((stringp form)
+         (if (cider--qualified-keyword-p form)
+             (with-temp-buffer
+               (thread-first form
+                 (insert-text-button 'type 'cider-browse-spec--spec)
+                 (button-put 'spec-name form))
+               (buffer-string))
+           ;; to make it easier to read replace all clojure.spec ns with s/
+           ;; and remove all clojure.core ns
+           (thread-last form
+             (replace-regexp-in-string "^\\(clojure.spec\\|clojure.spec.alpha\\)/" "s/")
+             (replace-regexp-in-string "^\\(clojure.core\\)/" ""))))
+
+        ((and (listp form) (stringp (cl-first form)))
+         (let ((form-tag (cl-first form)))
+           (cond
+            ;; prettier fns #()
+            ((string-equal form-tag "clojure.core/fn")
+             (if (equal (cl-second form) '("%"))
+                 (format "#%s" (cl-reduce #'concat (mapcar #'cider-browse-spec--pprint (cl-rest (cl-rest form)))))
+               (format "(fn [%%] %s)" (cl-reduce #'concat (mapcar #'cider-browse-spec--pprint (cl-rest (cl-rest form)))))))
+            ;; prettier (s/and )
+            ((cider--spec-fn-p form-tag "and")
+             (format "(s/and\n%s)" (string-join (thread-last (cl-rest form)
+                                                  (mapcar #'cider-browse-spec--pprint)
+                                                  (mapcar (lambda (x) (format "%s" x))))
+                                                "\n")))
+            ;; prettier (s/or )
+            ((cider--spec-fn-p form-tag "or")
+             (let ((name-spec-pair (seq-partition (cl-rest form) 2)))
+               (format "(s/or\n%s)" (string-join
+                                     (thread-last name-spec-pair
+                                       (mapcar (lambda (s) (format "%s %s" (cl-first s) (cider-browse-spec--pprint (cl-second s))))))
+                                     "\n"))))
+            ;; prettier (s/merge )
+            ((cider--spec-fn-p form-tag "merge")
+             (format "(s/merge\n%s)" (string-join (thread-last (cl-rest form)
+                                                    (mapcar #'cider-browse-spec--pprint)
+                                                    (mapcar (lambda (x) (format "%s" x))))
+                                                  "\n")))
+            ;; prettier (s/keys )
+            ((cider--spec-fn-p form-tag "keys")
+             (let ((keys-args (seq-partition (cl-rest form) 2)))
+               (format "(s/keys%s)" (thread-last
+                                        keys-args
+                                      (mapcar (lambda (s)
+                                                (let ((key-type (cl-first s))
+                                                      (specs-vec (cl-second s)))
+                                                  (concat "\n" key-type
+                                                          " ["
+                                                          (string-join (thread-last specs-vec
+                                                                         (mapcar #'cider-browse-spec--pprint)
+                                                                         (mapcar (lambda (x) (format "%s" x))))
+                                                                       "\n")
+                                                          "]"))))
+                                      (cl-reduce #'concat)))))
+            ;; prettier (s/multi-spec)
+            ((cider--spec-fn-p form-tag "multi-spec")
+             (let ((multi-method (cl-second form))
+                   (retag (cl-third form))
+                   (sub-specs (cl-rest (cl-rest (cl-rest form)))))
+               (format "(s/multi-spec %s %s\n%s)"
+                       multi-method
+                       retag
+                       (string-join
+                        (thread-last sub-specs
+                          (mapcar (lambda (s)
+                                    (concat "\n\n" (cl-first s) " " (cider-browse-spec--pprint (cl-second s))))))
+                        "\n"))))
+            ;; prettier (s/cat )
+            ((cider--spec-fn-p form-tag "cat")
+             (let ((name-spec-pairs (seq-partition (cl-rest form) 2)))
+               (format "(s/cat %s)"
+                       (thread-last name-spec-pairs
+                         (mapcar (lambda (s)
+                                   (concat "\n" (cl-first s) " " (cider-browse-spec--pprint (cl-second s)))))
+                         (cl-reduce #'concat)))))
+            ;; prettier (s/alt )
+            ((cider--spec-fn-p form-tag "alt")
+             (let ((name-spec-pairs (seq-partition (cl-rest form) 2)))
+               (format "(s/alt %s)"
+                       (thread-last name-spec-pairs
+                         (mapcar (lambda (s)
+                                   (concat "\n" (cl-first s) " " (cider-browse-spec--pprint (cl-second s)))))
+                         (cl-reduce #'concat)))))
+            ;; prettier (s/fspec )
+            ((cider--spec-fn-p form-tag "fspec")
+             (thread-last (seq-partition (cl-rest form) 2)
+               (cl-remove-if (lambda (s) (and (stringp (cl-second s))
+                                              (string-empty-p (cl-second s)))))
+               (mapcar (lambda (s)
+                         (format "\n%-11s: %s" (pcase (cl-first s)
+                                                 (":args" "arguments")
+                                                 (":ret" "returns")
+                                                 (":fn" "invariants"))
+                                 (cider-browse-spec--pprint (cl-second s)))))
+               (cl-reduce #'concat)
+               (format "%s")))
+            ;; every other with no special management
+            (t (format "(%s %s)"
+                       (cider-browse-spec--pprint form-tag)
+                       (string-join (mapcar #'cider-browse-spec--pprint (cl-rest form)) " "))))))
+        (t (format "%s" form))))
+
+(defun cider-browse-spec--pprint-indented (spec-form)
+  "Indent (pretty-print) and font-lock SPEC-FORM.
+Return the result as a string."
+  (with-temp-buffer
+    (clojure-mode)
+    (insert (cider-browse-spec--pprint spec-form))
+    (indent-region (point-min) (point-max))
+    (cider--font-lock-ensure)
+    (buffer-string)))
+
+(defun cider-browse-spec--draw-spec-buffer (buffer spec spec-form)
+  "Reset contents of BUFFER and draws everything needed to browse the SPEC-FORM.
+Display SPEC as a title and uses `cider-browse-spec--pprint' to display
+a more user friendly representation of SPEC-FORM."
+  (with-current-buffer buffer
+    (let ((inhibit-read-only t))
+      (cider--help-setup-xref (list #'cider-browse-spec spec) nil buffer)
+      (goto-char (point-max))
+      (insert (cider-font-lock-as-clojure spec) "\n\n")
+      (insert (cider-browse-spec--pprint-indented spec-form))
+      (cider--make-back-forward-xrefs)
+      (current-buffer))))
+
+(defun cider-browse-spec--browse (spec)
+  "Browse SPEC."
+  (cider-ensure-connected)
+  (cider-ensure-op-supported "spec-form")
+  (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer 'select #'cider-browse-spec-view-mode 'ancillary)
+    (setq-local cider-browse-spec--current-spec spec)
+    (cider-browse-spec--draw-spec-buffer (current-buffer)
+                                         spec
+                                         (cider-sync-request:spec-form spec))
+    (goto-char (point-min))
+    (current-buffer)))
+
+(defun cider-browse-spec--browse-at (&optional pos)
+  "View the definition of a spec.
+
+Optional argument POS is the position of a spec, defaulting to point.  POS
+may also be a button, so this function can be used a the button's `action'
+property."
+  (interactive)
+  (let ((pos (or pos (point))))
+    (when-let* ((spec (button-get pos 'spec-name)))
+      (cider-browse-spec--browse spec))))
+
+;; Interactive Functions
+
+(defun cider-browse-spec--print-curr-spec-example ()
+  "Generate and print an example of the current spec."
+  (interactive)
+  (cider-ensure-connected)
+  (cider-ensure-op-supported "spec-example")
+  (if-let* ((spec cider-browse-spec--current-spec))
+      (if-let* ((example (cider-sync-request:spec-example spec)))
+          (with-current-buffer (cider-popup-buffer cider-browse-spec-example-buffer 'select #'cider-browse-spec-example-mode 'ancillary)
+            (setq-local cider-browse-spec--current-spec spec)
+            (let ((inhibit-read-only t))
+              (insert "Example of " (cider-font-lock-as-clojure spec))
+              (insert "\n\n")
+              (insert (cider-font-lock-as-clojure example))
+              (goto-char (point-min))))
+        (error (format "No example for spec %s" spec)))
+    (error "No current spec")))
+
+(defun cider-browse-spec--example-revert-buffer-function (&rest _)
+  "`revert-buffer' function for `cider-browse-spec-example-mode'.
+
+Generates a new example for the current spec."
+  (cider-browse-spec--print-curr-spec-example))
+
+;;;###autoload
+(defun cider-browse-spec (spec)
+  "Browse SPEC definition."
+  (interactive (list (completing-read "Browse spec: "
+                                      (cider-sync-request:spec-list)
+                                      nil nil
+                                      (cider-symbol-at-point))))
+  (cider-browse-spec--browse spec))
+
+(defun cider-browse-spec-regex (regex)
+  "Open the list of specs that matches REGEX in a popup buffer.
+Displays all specs when REGEX is nil."
+  (cider-ensure-connected)
+  (cider-ensure-op-supported "spec-list")
+  (let ((filter-regex (or regex "")))
+    (with-current-buffer (cider-popup-buffer cider-browse-spec-buffer 'select nil 'ancillary)
+      (let ((specs (cider-sync-request:spec-list filter-regex)))
+        (cider-browse-spec--draw-list-buffer (current-buffer)
+                                             (if (string-empty-p filter-regex)
+                                                 "All specs in registry"
+                                               (format "All specs matching regex `%s' in registry" filter-regex))
+                                             specs)))))
+
+;;;###autoload
+(defun cider-browse-spec-all (&optional arg)
+  "Open list of specs in a popup buffer.
+
+With a prefix argument ARG, prompts for a regexp to filter specs.
+No filter applied if the regexp is the empty string."
+  (interactive "P")
+  (cider-browse-spec-regex (if arg (read-string "Filter regex: ") "")))
+
+(provide 'cider-browse-spec)
+
+;;; cider-browse-spec.el ends here