+ ;;; ^:src-dep [foo/bar "0.1.1"]
+
+ ;;; Until we cross the dividing line, take a sorted line, find
+ ;;; its equal in the raw content below and move that vector with meta and
+ ;;; comments to the end of the buffer
+ (goto-char (point-min))
+ (while (not (looking-at dividing-line))
+ (let ((dep (string-trim (cljr--extract-region (point) (point-at-eol))))
+ start end vector-and-meta)
+ (forward-line)
+ (join-line)
+ (re-search-forward dividing-line)
+ (re-search-forward (concat "\\[" dep "\\s-+\""))
+ (paredit-backward-up 2)
+ (while (not (looking-back "^\\s-*" (point-at-bol)))
+ (forward-char -1))
+ (while (save-excursion (forward-line -1) (cljr--comment-line-p))
+ (forward-line -1))
+ (setq start (point))
+ (re-search-forward (concat "\\[" dep "\\s-+\""))
+ (setq end (max (point-at-eol)
+ (cljr--point-after
+ '(paredit-forward-up 2) '(move-end-of-line 1))))
+ (setq vector-and-meta (buffer-substring-no-properties start end))
+ (delete-region start end)
+ (forward-line)
+ (join-line)
+ (goto-char (point-max))
+ (open-line 1)
+ (forward-line)
+ (insert vector-and-meta)
+ (goto-char (point-min))))
+ (cljr--delete-line))
+
+(defun cljr--sort-dependency-vectors (sorted-names vectors-and-meta)
+ (with-temp-buffer
+ (let ((dividing-line "<===============================>"))
+ (cljr--prepare-sort-buffer sorted-names vectors-and-meta dividing-line)
+ (cljr--sort-dependency-vectors-with-meta-and-comments dividing-line)
+ (concat "[" (string-trim (buffer-substring-no-properties (point) (point-max))) "]"))))
+
+;;;###autoload
+(defun cljr-sort-project-dependencies ()
+ "Sorts all dependency vectors in project.clj
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-sort-project-dependencies"
+ (interactive)
+ (cljr--update-file (cljr--project-file)
+ (goto-char (point-min))
+ (while (re-search-forward ":dependencies" (point-max) t)
+ (forward-char)
+ (thread-first (buffer-substring-no-properties (point)
+ (cljr--point-after 'paredit-forward))
+ cljr--get-sorted-dependency-names
+ (cljr--sort-dependency-vectors (thread-last (clojure-delete-and-extract-sexp)
+ (string-remove-prefix "[")
+ (string-remove-suffix "]")))
+ insert))
+ (indent-region (point-min) (point-max))
+ (save-buffer)))
+
+(defun cljr--call-middleware-sync (request &optional key)
+ "Call the middleware with REQUEST.
+
+If it's present KEY indicates the key to extract from the response."
+ (let* ((nrepl-sync-request-timeout (if cljr-warn-on-eval nil 25))
+ (response (thread-first request cider-nrepl-send-sync-request cljr--maybe-rethrow-error)))
+ (if key
+ (nrepl-dict-get response key)
+ response)))
+
+(defun cljr--call-middleware-async (request &optional callback)
+ (cider-nrepl-send-request request callback))
+
+(defun cljr--get-artifacts-from-middleware (force)
+ (message "Retrieving list of available libraries...")
+ (let* ((request (cljr--create-msg "artifact-list" "force" (if force "true" "false")))
+ (artifacts (cljr--call-middleware-sync request "artifacts")))
+ (if artifacts
+ artifacts
+ (user-error "Empty artifact list received from middleware!"))))
+
+(defun cljr--update-artifact-cache ()
+ (cljr--call-middleware-async (cljr--create-msg "artifact-list"
+ "force" "true")
+ (lambda (_)
+ (when cljr--debug-mode
+ (message "Artifact cache updated")))))
+
+(defun cljr--dictionary-lessp (str1 str2)
+ "return t if STR1 is < STR2 when doing a dictionary compare
+(splitting the string at numbers and doing numeric compare with them).
+It is optimized for version comparisons, in that empty strings are sorted
+before non-empty. This lets 1.7.0 be sorted above 1.7.0-RC1."
+ (let ((str1-components (cljr--dict-split (downcase str1)))
+ (str2-components (cljr--dict-split (downcase str2))))
+ (cljr--dict-lessp str1-components str2-components)))
+
+(defun cljr--dict-lessp (slist1 slist2)
+ "compare the two lists of strings & numbers"
+ (cond ((null slist1)
+ (not (null slist2)))
+ ((null slist2)
+ t)
+ ((and (numberp (car slist1))
+ (stringp (car slist2)))
+ t)
+ ((and (numberp (car slist2))
+ (stringp (car slist1)))
+ nil)
+ ((and (numberp (car slist1))
+ (numberp (car slist2)))
+ (or (< (car slist1) (car slist2))
+ (and (= (car slist1) (car slist2))
+ (cljr--dict-lessp (cdr slist1) (cdr slist2)))))
+ (t
+ (or (string-lessp (car slist1) (car slist2))
+ (and (string-equal (car slist1) (car slist2))
+ (cljr--dict-lessp (cdr slist1) (cdr slist2)))))))
+
+(defun cljr--dict-split (str)
+ "split a string into a list of number and non-number components"
+ (save-match-data
+ (let ((res nil))
+ (while (and str (not (string-equal "" str)))
+ (let ((p (string-match "[0-9]+" str)))
+ (cond ((null p)
+ (setq res (cons str res))
+ (setq str nil))
+ ((= p 0)
+ (setq res (cons (string-to-number (match-string 0 str)) res))
+ (setq str (substring str (match-end 0))))
+ (t
+ (setq res (cons (substring str 0 (match-beginning 0)) res))
+ (setq str (substring str (match-beginning 0)))))))
+ (reverse res))))
+
+(defun cljr--get-versions-from-middleware (artifact)
+ (let* ((request (cljr--create-msg "artifact-versions" "artifact" artifact))
+ (versions (nreverse (sort (cljr--call-middleware-sync request "versions") 'cljr--dictionary-lessp))))
+ (if versions
+ versions
+ (error "Empty version list received from middleware!"))))
+
+(defun cljr--prompt-user-for (prompt &optional choices)
+ "Prompt the user with PROMPT.
+
+If CHOICES is provided provide a completed read among the
+possible choices. If the choice is trivial, return it."
+ (if choices
+ (if (= (length choices) 1)
+ (cl-first choices)
+ (completing-read prompt choices nil nil nil nil (car choices)))
+ (read-from-minibuffer prompt)))
+
+(defun cljr--add-project-dependency (artifact version)
+ (cljr--update-file (cljr--project-file)
+ (goto-char (point-min))
+ (re-search-forward ":dependencies")
+ (paredit-forward)
+ (paredit-backward-down)
+ (newline-and-indent)
+ (insert "[" artifact " \"" version "\"]")
+ (cljr--post-command-message "Added %s version %s as a project dependency" artifact version)
+ (when cljr-hotload-dependencies
+ (paredit-backward-down)
+ (cljr-hotload-dependency))))
+
+;;;###autoload
+(defun cljr-add-project-dependency (force)
+ "Add a dependency to the project.clj file.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-project-dependency"
+ (interactive "P")
+ (cljr--ensure-op-supported "artifact-list")
+ (when-let ((lib-name (thread-last (cljr--get-artifacts-from-middleware force)
+ (cljr--prompt-user-for "Artifact: ")))
+ (version (thread-last (cljr--get-versions-from-middleware lib-name)
+ (cljr--prompt-user-for "Version: "))))
+ (cljr--add-project-dependency lib-name version)))
+
+;;;###autoload
+(defun cljr-update-project-dependency ()
+ "Update the version of the dependency at point."
+ (interactive)
+ (cljr--ensure-op-supported "artifact-list")
+ (unless (cljr--looking-at-dependency-vector-p)
+ (user-error "Place cursor in front of dependency vector to update."))
+ (save-excursion
+ (let (lib-name)
+ (paredit-forward-down)
+ (setq lib-name (cljr--extract-sexp))
+ (paredit-forward)
+ (skip-syntax-forward " ")
+ (let ((version (thread-last (cljr--get-versions-from-middleware lib-name)
+ (cljr--prompt-user-for (concat lib-name " version: ")))))
+ (cljr--delete-sexp)
+ (insert "\"" version "\""))))
+ (when cljr-hotload-dependencies
+ (cljr-hotload-dependency)
+ (cljr--ensure-op-supported "artifact-list")))
+
+;;;###autoload
+(defun cljr-update-project-dependencies ()
+ "Update all project dependencies.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-update-project-dependencies"
+ (interactive)
+ (cljr--ensure-op-supported "artifact-list")
+ (find-file (cljr--project-file))
+ (goto-char (point-min))
+ (let (cljr-hotload-dependencies)
+ (while (re-search-forward ":dependencies" (point-max) t)
+ (paredit-forward-down)
+ (cljr--skip-past-whitespace-and-comments)
+ (while (not (looking-at "]"))
+ (let ((highlight (cljr--highlight-sexp)))
+ (unwind-protect
+ (cljr-update-project-dependency)
+ (delete-overlay highlight)))
+ (paredit-forward)
+ (cljr--skip-past-whitespace-and-comments)))))
+
+(defun cljr--skip-past-whitespace-and-comments ()
+ (skip-syntax-forward " >")
+ (while (looking-at ";")
+ (move-end-of-line 1)
+ (forward-char)
+ (skip-syntax-forward " >")))
+
+(defun cljr--extract-anon-fn-name (sexp-str)
+ (when (string-match "(fn \\(\\_<[^ ]+\\_>\\)?" sexp-str)
+ (match-string-no-properties 1 sexp-str)))
+
+(defun cljr--highlight (start end)
+ (let ((overlay (make-overlay start end)))
+ (overlay-put overlay 'face 'secondary-selection)
+ (overlay-put overlay 'priority 100)
+ overlay))
+
+(defun cljr--highlight-sexp ()
+ (cljr--highlight (point) (cljr--point-after 'paredit-forward)))
+
+(defun cljr--string-present-p (s)
+ (not (or (null s) (string-empty-p s))))
+
+(defun cljr--promote-fn ()
+ (save-excursion
+ (let* ((locals (save-excursion (paredit-forward-down)
+ (cljr--call-middleware-to-find-used-locals
+ (buffer-file-name) (line-number-at-pos)
+ (1+ (current-column)))))
+ (fn (cljr--extract-sexp))
+ (namedp (cljr--extract-anon-fn-name fn))
+ (name (or namedp
+ (unless (cljr--use-multiple-cursors-p)
+ (let ((highlight (cljr--highlight-sexp)))
+ (unwind-protect
+ (read-string "Name: ")
+ (delete-overlay highlight))))))
+ fn-start)
+ (cljr--delete-sexp)
+ (save-excursion
+ (cljr--new-toplevel-form fn)
+ (paredit-backward-down)
+ (cljr--goto-fn-definition)
+ (setq fn-start (point))
+ (forward-char)
+ (insert "de")
+ (paredit-forward)
+ (when cljr-favor-private-functions
+ (if clojure-use-metadata-for-privacy
+ (insert " ^:private")
+ (insert "-")))
+ (when (not namedp)
+ (insert " ")
+ (newline)
+ (backward-char)
+ (if name
+ (insert name)
+ (mc/create-fake-cursor-at-point)))
+ (re-search-forward "\\[")
+ (when (cljr--string-present-p locals)
+ (insert locals)
+ (unless (looking-at-p "\\]")
+ (insert " ")))
+ (paredit-forward-up)
+ (unless (looking-at "\s*?$")
+ (newline))
+ (indent-region fn-start (cljr--point-after 'paredit-forward-up)))
+ (when (cljr--string-present-p locals)
+ (insert (format "(partial %s)" locals))
+ (backward-char (length (concat " " locals ")"))))
+ (if name
+ (insert name)
+ (mc/maybe-multiple-cursors-mode)))))
+
+(defun cljr--append-fn-parameter (param)
+ (cljr--goto-fn-definition)
+ (paredit-forward-down)
+ (paredit-forward 2)
+ (paredit-backward-down)
+ (if (looking-back "\\[" 1)
+ (insert param)
+ (insert " " param)))
+
+(defun cljr--promote-function-literal ()
+ (delete-char 1)
+ (let ((body (clojure-delete-and-extract-sexp)))
+ (insert "(fn [] " body ")"))
+ (backward-char)
+ (cljr--goto-fn-definition)
+ (let ((fn-start (point))
+ var replacement)
+ (while (re-search-forward "%[1-9&]?" (cljr--point-after 'paredit-forward) t)
+ (setq var (buffer-substring (point) (cljr--point-after 'paredit-backward)))
+ (setq replacement (read-string (format "%s => " var)))
+ (cljr--append-fn-parameter (if (string= "%&" var)
+ (format "& %s" replacement)
+ replacement))
+ (goto-char (1+ fn-start))
+ (let ((end (cljr--point-after '(paredit-forward-up 2))))
+ (while (re-search-forward (format "\\s-%s\\(\\s-\\|\\|\n)\\)" var)
+ end :no-error)
+ (replace-match (format " %s\\1" replacement))))
+ (goto-char fn-start))))
+
+;;;###autoload
+(defun cljr-promote-function (promote-to-defn)
+ "Promote a function literal to an fn, or an fn to a defn.
+With prefix PROMOTE-TO-DEFN, promote to a defn even if it is a
+function literal.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-promote-function"
+ (interactive "P")
+ (cljr--ensure-op-supported "find-used-locals")
+ (when (cljr--asts-y-or-n-p)
+ (save-excursion
+ (cond
+ ;; Already in the right place.
+ ((or (looking-at-p "#(")
+ (looking-at-p "(fn")))
+ ;; Right after the #.
+ ((and (eq (char-after) ?\()
+ (eq (char-before) ?#))
+ (forward-char -1))
+ ;; Possibly inside a function.
+ (t (cljr--goto-fn-definition)))
+ ;; Now promote it.
+ (if (looking-at "#(")
+ (cljr--promote-function-literal)
+ (cljr--promote-fn)))
+ (when promote-to-defn
+ (cljr--promote-fn))))
+
+(add-to-list 'mc--default-cmds-to-run-once 'cljr-promote-function)
+
+(defun cljr--insert-in-find-symbol-buffer (occurrence)
+ (save-excursion
+ (pop-to-buffer cljr--find-symbol-buffer)
+ (goto-char (point-max))
+ (insert occurrence)))
+
+(defun cljr--end-of-buffer-p ()
+ "True if point is at end of buffer"
+ (= (point) (cljr--point-after 'end-of-buffer)))
+
+(defun cljr--find-symbol-sync (symbol ns)
+ (let* ((filename (funcall cider-to-nrepl-filename-function (buffer-file-name)))
+ (line (line-number-at-pos))
+ (column (1+ (current-column)))
+ (dir (cljr--project-dir))
+ (request (cljr--create-msg "find-symbol"
+ "ns" ns
+ "dir" dir
+ "file" filename
+ "line" line
+ "column" column
+ "name" symbol
+ "ignore-errors"
+ (when cljr-ignore-analyzer-errors "true")))
+ occurrences)
+ (with-temp-buffer
+ (insert (cljr--call-middleware-sync request "occurrence"))
+ (unless (cljr--empty-buffer-p)
+ (goto-char (point-min))
+ (while (not (cljr--end-of-buffer-p))
+ (push (edn-read) occurrences))))
+ occurrences))
+
+(defun cljr--find-symbol (symbol ns callback)
+ (let* ((filename (funcall cider-to-nrepl-filename-function (buffer-file-name)))
+ (line (line-number-at-pos))
+ (column (1+ (current-column)))
+ (dir (cljr--project-dir))
+ (find-symbol-request
+ (cljr--create-msg "find-symbol"
+ "ns" ns
+ "dir" dir
+ "file" filename
+ "line" line
+ "column" column
+ "name" symbol
+ "ignore-errors"
+ (when (or cljr-find-usages-ignore-analyzer-errors cljr-ignore-analyzer-errors) "true"))))
+ (with-current-buffer (cider-current-repl-buffer)
+ (setq cjr--occurrence-count 0)
+ (setq cljr--num-syms -1)
+ (setq cljr--occurrence-ids '()))
+ (cljr--call-middleware-async find-symbol-request callback)))
+
+(defun cljr--first-line (s)
+ (thread-first s (split-string "\\(\r\n\\|[\n\r]\\)") car string-trim))
+
+(defun cljr--project-relative-path (path)
+ "Denormalize PATH to make to make it relative to the project
+root."
+ (string-remove-prefix (cljr--project-dir) path))
+
+(defun cljr--get-valid-filename (hash)
+ "Get :file value from the hash table and convert path if necessary."
+ (funcall cider-from-nrepl-filename-function (gethash :file hash)))
+
+(defun cljr--format-symbol-occurrence (occurrence)
+ (let ((file (cljr--get-valid-filename occurrence))
+ (line (gethash :line-beg occurrence))
+ (col (1- (gethash :col-beg occurrence)))
+ (match (gethash :match occurrence)))
+ (format "%s:%s:%s: %s\n" (cljr--project-relative-path file) line col
+ (cljr--first-line match))))
+
+(defun cljr--format-and-insert-symbol-occurrence (occurrence-resp)
+ ;; The middleware sends either an occurrence or a final count, never
+ ;; both in the same message.
+ (cljr--maybe-rethrow-error occurrence-resp)
+ (if-let (count (nrepl-dict-get occurrence-resp "count"))
+ (progn
+ (setq cljr--num-syms count)
+ (when (= cjr--occurrence-count cljr--num-syms)
+ (cljr--finalise-find-symbol-buffer cljr--num-syms)))
+ (when-let (occurrence-data (nrepl-dict-get occurrence-resp "occurrence"))
+ (let* ((occurrence (edn-read occurrence-data))
+ (occurrence-id (format "%s%s"
+ (cljr--get-valid-filename occurrence)
+ (gethash :line-beg occurrence))))
+ (cl-incf cjr--occurrence-count)
+ (unless (member occurrence-id cljr--occurrence-ids)
+ (setq cljr--occurrence-ids
+ (cons occurrence-id cljr--occurrence-ids))
+ (thread-last occurrence
+ cljr--format-symbol-occurrence
+ cljr--insert-in-find-symbol-buffer))))))
+
+(defun cljr--finalise-find-symbol-buffer (total)
+ (with-current-buffer "*cljr-find-usages*"
+ (goto-char (point-max))
+ (insert (format "\nFind symbol finished: %d occurrence%s found"
+ total (if (> total 1) "s" "")))
+ ;; Place point on first occurrence
+ (goto-char (point-min))
+ (forward-line 2)))
+
+(defun cljr--setup-find-symbol-buffer (symbol-name)
+ (save-window-excursion
+ (when (get-buffer cljr--find-symbol-buffer)
+ (kill-buffer cljr--find-symbol-buffer))
+ (pop-to-buffer cljr--find-symbol-buffer)
+ (with-current-buffer "*cljr-find-usages*"
+ (insert (format "'%s' occurs in the following places:\n\n" symbol-name))
+ (grep-mode)
+ (set (make-local-variable 'compilation-error-regexp-alist)
+ (list 'compilation-cljr-nogroup))
+ (set (make-local-variable 'compilation-error-regexp-alist-alist)
+ (list (cons 'compilation-cljr-nogroup (list cljr--file-column-pattern 1 2 3))))
+ (setq buffer-read-only nil)
+ (setq-local compilation-search-path (list (cljr--project-dir))))))
+
+(defun cljr--asts-y-or-n-p ()
+ (or
+ (not cljr-warn-on-eval)
+ (y-or-n-p "To perform this op the project needs to be evaluated.\n Analyzing a large project might take a while: hit C-g to abort.\n (Set cljr-warn-on-eval to nil to analyze the project without warning)\n Do you want to proceed?")))
+
+;;;###autoload
+(defun cljr-find-usages ()
+ "Find all usages of the symbol at point in the project.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-find-usages"
+ (interactive)
+ (cljr--ensure-op-supported "find-symbol")
+ (when (cljr--asts-y-or-n-p)
+ (save-buffer)
+ (let* ((cljr--find-symbol-buffer "*cljr-find-usages*")
+ (symbol (cider-symbol-at-point))
+ (var-info (cljr--var-info symbol))
+ (ns (nrepl-dict-get var-info "ns"))
+ (symbol-name (nrepl-dict-get var-info "name")))
+ (cljr--setup-find-symbol-buffer (or symbol-name symbol))
+ (cljr--find-symbol (or symbol-name symbol) ns
+ #'cljr--format-and-insert-symbol-occurrence))))
+
+(defun cljr--rename-occurrence (file line-beg col-beg line-end col-end name new-name)
+ (save-excursion
+ (with-current-buffer
+ (find-file-noselect file)
+ (let* ((name (thread-last name cljr--symbol-suffix regexp-quote))
+ (search-limit (save-excursion
+ (progn
+ (goto-char (point-min))
+ (forward-line (1- line-end))
+ (move-to-column (1- col-end))
+ (point)))))
+ (goto-char (point-min))
+ (forward-line (1- line-beg))
+ (move-to-column (1- col-beg))
+ ;; When the match is a definition, the position of the symbol
+ ;; isn't returned but the beginning of the defining form
+ (when (looking-at-p "(\\s-*def")
+ (re-search-forward name)
+ (paredit-backward))
+ (when (re-search-forward (format "\\(.+/\\)?\\(%s\\)" name) search-limit t)
+ (replace-match (format "\\1%s" new-name) t)))
+ (save-buffer))))
+
+(defun cljr--rename-occurrences (occurrences new-name)
+ (dolist (symbol-meta occurrences)
+ (let* ((file (cljr--get-valid-filename symbol-meta))
+ (line-beg (gethash :line-beg symbol-meta))
+ (col-beg (gethash :col-beg symbol-meta))
+ (line-end (gethash :line-end symbol-meta))
+ (col-end (gethash :col-end symbol-meta))
+ (name (gethash :name symbol-meta))
+ (new-name* (if (stringp new-name)
+ new-name
+ (funcall new-name name))))
+ (cljr--rename-occurrence file line-beg col-beg line-end col-end name new-name*))))
+
+;;;###autoload
+(defun cljr-rename-symbol (&optional new-name)
+ "Rename the symbol at point and all of its occurrences.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-rename-symbol"
+ (interactive)
+ (cljr--ensure-op-supported "find-symbol")
+ (when (cljr--asts-y-or-n-p)
+ (save-buffer)
+ (let* ((symbol (cider-symbol-at-point))
+ (var-info (cljr--var-info symbol))
+ (symbol-name (nrepl-dict-get var-info "name"))
+ (ns (nrepl-dict-get var-info "ns"))
+ (name (or symbol-name symbol))
+ (_ (unless *cljr--noninteractive* (message "Fetching symbol occurrences...")))
+ (occurrences (cljr--find-symbol-sync name ns))
+ (new-name (or new-name (read-from-minibuffer "New name: "
+ (cljr--symbol-suffix symbol)))))
+ (cljr--rename-occurrences occurrences new-name)
+ (cljr--post-command-message "Renamed %s occurrences of %s" (length occurrences) name)
+ (when (and (> (length occurrences) 0) (not cljr-warn-on-eval))
+ (cljr--warm-ast-cache)))))
+
+(defun cljr--replace-refer-all-with-alias (ns publics-occurrences alias)
+ (let ((ns-last-token (car (last (split-string ns "\\.")))))
+ (when (re-search-forward (format "\\(%s\\).*?\\([\]\)]\\)" ns-last-token) nil t)
+ (replace-match (format "\\1 :as %s\\2" alias)))
+ (perform-replace (format "%s/" alias) "" nil nil t)
+ (cljr--rename-occurrences publics-occurrences (lambda (old-name) (concat alias "/" old-name)))))
+
+(defun cljr--replace-refer-all (ns alias)
+ "Replaces :refer :all style require with alias :as style require.
+
+Also adds the alias prefix to all occurrences of public symbols in the namespace.
+"
+ (cljr--ensure-op-supported "find-used-publics")
+ (let ((filename (funcall cider-to-nrepl-filename-function (buffer-file-name))))
+ (let* ((alias (or alias
+ (cljr--prompt-user-for (format "alias for [%s]: " ns))))
+ (request
+ (cljr--create-msg "find-used-publics"
+ "used-ns" ns
+ "file" filename))
+ (occurrences (thread-last (cljr--call-middleware-sync request "used-publics")
+ (edn-read))))
+ (cljr--replace-refer-all-with-alias ns occurrences alias))))
+
+(defun cljr--maybe-nses-in-bad-state (response)
+ (let ((asts-in-bad-state (seq-filter
+ (lambda (it)
+ (not (stringp (car (last it)))))
+ (thread-first (nrepl-dict-get response "ast-statuses")
+ edn-read
+ (seq-partition 2)))))
+ (when (not (= 0 (length asts-in-bad-state)))
+ (user-error (concat "Some namespaces are in a bad state: "
+ (string-join
+ (seq-map
+ (lambda (it)
+ (format "error \"%s\" in %s" (car (last (car (last it)))) (car it)))
+ asts-in-bad-state) "; "))))))
+
+(defun cljr--warm-ast-cache ()
+ (cljr--call-middleware-async
+ (cljr--create-msg "warm-ast-cache")
+ (lambda (res)
+ (cljr--maybe-rethrow-error res)
+ (cljr--maybe-nses-in-bad-state res)
+ (when cljr--debug-mode
+ (message "AST index updated")))))
+
+(defun cljr--replace-ns (new-ns)
+ (save-excursion
+ (cljr--goto-ns)
+ (clojure-delete-and-extract-sexp)
+ (insert new-ns)
+ (cljr--just-one-blank-line)))
+
+(defun cljr--clean-ns (&optional path no-prune?)
+ "If PATH is passed use that instead of the path to the current buffer
+
+If NO-PRUNE is passed, the default is overridden and unused stuff isn't \
+removed."
+ ;; Don't save prematurely when called from `cljr-project-clean'
+ (unless (and *cljr--noninteractive*
+ (not (buffer-modified-p)))
+ (save-buffer))
+ (let ((path (or path (cljr--project-relative-path (buffer-file-name)))))
+ (when-let (new-ns (cljr--call-middleware-sync
+ (cljr--create-msg "clean-ns"
+ "path" path
+ "libspec-whitelist" cljr-libspec-whitelist
+ "prune-ns-form" (if no-prune? "false"
+ "true"))
+ "ns"))
+ (cljr--replace-ns new-ns))
+ (unless *cljr--noninteractive*
+ (cljr--post-command-message "Namespace form cleaned!"))))
+
+;;;###autoload
+(defun cljr-clean-ns ()
+ "Clean the ns form for the current buffer.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-clean-ns"
+ (interactive)
+ (cljr--ensure-op-supported "clean-ns")
+ (cider-eval-ns-form)
+ (cljr--clean-ns))
+
+(defun cljr--narrow-candidates (candidates symbol)
+ (if (= (length candidates) 0)
+ (error "Couldn't find any symbols matching %s on classpath."
+ (cljr--symbol-suffix symbol))
+ (let* ((names (seq-map (lambda (c) (gethash :name c)) candidates))
+ (name (intern-soft (cljr--prompt-user-for "Require: " names))))
+ (seq-find (lambda (c) (equal name (gethash :name c))) candidates))))
+
+(defun cljr--insert-libspec-verbosely (libspec)
+ (insert (format "%s" libspec))
+ (cljr--indent-defun)
+ (cljr--post-command-message "%s added to ns" libspec))
+
+(defun cljr--insert-missing-import (missing)
+ (save-excursion
+ (cljr--insert-in-ns ":import")
+ (cljr--insert-libspec-verbosely missing)))
+
+(defun cljr--qualified-symbol-p (symbol)
+ (thread-last symbol
+ (format "%s")
+ regexp-quote
+ (string-match-p "/")
+ null
+ not))
+
+(defun cljr--symbol-prefix (symbol)
+ "java.util.Date => java.util
+str/split => str
+split => ''"
+ (cond ((cljr--qualified-symbol-p symbol) (car (split-string symbol "/")))
+ ((string-match-p "\\w+\\.\\w+" symbol)
+ (string-join (butlast (split-string symbol "\\.")) "."))
+ (t "")))
+
+(defun cljr--insert-missing-require (symbol missing-symbol type)
+ "Require MISSING-SYMBOL.
+
+Inspect SYMBOL, the thing at point, to find out whether we have
+to create an alias or refer."
+ (save-excursion
+ (cljr--insert-in-ns ":require")
+ (let ((missing (format "%s" missing-symbol))
+ (alias? (cljr--qualified-symbol-p symbol)))
+ (cond
+ ;; defrecord / deftype where the package must be required
+ ((eq type :type)
+ (cljr--insert-libspec-verbosely (cljr--symbol-prefix missing)))
+ ;; Fully qualified symbol
+ ((and (cljr--qualified-symbol-p symbol)
+ (string= (cljr--symbol-prefix symbol) missing))
+ (cljr--insert-libspec-verbosely missing))
+ (alias?
+ (cljr--insert-libspec-verbosely (format "[%s :as %s]" missing
+ (cljr--symbol-prefix symbol))))
+ (t (cljr--insert-libspec-verbosely (format "[%s :refer [%s]]"
+ missing symbol)))))))
+
+(defun cljr--add-missing-libspec (symbol candidates)
+ (let* ((candidate (cljr--narrow-candidates candidates symbol))
+ (missing-symbol (gethash :name candidate))
+ (type (gethash :type candidate)))
+ (cond ((eq type :ns) (cljr--insert-missing-require symbol missing-symbol type))
+ ((eq type :type)
+ ;; We need to both require the ns, to trigger compilation,
+ ;; and then import the java class
+
+ ;; In the line below we're assuming that all clojure code
+ ;; will prefer - over _ when naming namespaces :(
+ (progn (cljr--insert-missing-require
+ symbol
+ (replace-regexp-in-string "_" "-" (format "%s" missing-symbol)) type)
+ (cljr--insert-missing-import missing-symbol)))
+ ((eq type :class) (cljr--insert-missing-import missing-symbol))
+ (t (error (format "Unknown type %s" type))))))
+
+(defun cljr--symbol-suffix (symbol)
+ "java.util.Date => Date
+Date => Date
+clojure.string/split => split
+str/split => split"
+ (let ((name (cljr--normalize-symbol-name symbol)))
+ (cond
+ ((string-match-p "\\w+\\.\\w+" name)
+ (thread-first name (split-string "\\.") last car cljr--symbol-suffix))
+ ((cljr--qualified-symbol-p name)
+ (thread-first name (split-string "/") cadr cljr--symbol-suffix))
+ (t name))))
+
+(defun cljr--normalize-symbol-name (name)
+ "Removes reader macros and quoting
+
+Date. -> Date
+@sym => sym
+#'sym => sym
+'sym => sym
+~sym => sym
+~@sym => sym"
+ (cond
+ ((string-suffix-p "." name)
+ (thread-last name (string-remove-suffix ".") cljr--normalize-symbol-name))
+ ((string-prefix-p "#'" name)
+ (thread-last name (string-remove-prefix "#'") cljr--normalize-symbol-name))
+ ((string-prefix-p "'" name)
+ (thread-last name (string-remove-prefix "'") cljr--normalize-symbol-name))
+ ((string-prefix-p "~" name)
+ (thread-last name (string-remove-prefix "~") cljr--normalize-symbol-name))
+ ((string-prefix-p "~@" name)
+ (thread-last name (string-remove-prefix "~@") cljr--normalize-symbol-name))
+ ((string-prefix-p "@" name)
+ (thread-last name (string-remove-prefix "@") cljr--normalize-symbol-name))
+ (t name)))
+
+(defun cljr--call-middleware-to-resolve-missing (symbol)
+ ;; Just so this part can be mocked out in a step definition
+ (when-let (candidates (thread-first (cljr--create-msg "resolve-missing"
+ "symbol" symbol
+ "session" (cider-current-session))
+ (cljr--call-middleware-sync
+ "candidates")))
+ (edn-read candidates)))
+
+(defun cljr--get-error-value (response)
+ "Gets the error value from the middleware response.
+
+We can't simply call `nrepl-dict-get' because the error value
+itself might be `nil'."
+ (cl-assert (nrepl-dict-p response) nil
+ "Response from middleware isn't an nrepl-dict!")
+ (if-let (err (nrepl-dict-get response "err"))
+ (error (format "Error in nrepl-refactor: %s" err))
+ (let* ((maybe-error-and-rest
+ (seq-drop-while (lambda (e)
+ (not (and (stringp e) (string-equal e "error"))))
+ response))
+ (maybe-error (car maybe-error-and-rest)))
+ (when (and (stringp maybe-error) (string-equal maybe-error "error"))
+ (or (cadr maybe-error-and-rest)
+ (format "Error 'nil' returned from middleware. %s"
+ "Please contact your local administrator."))))))
+
+(defun cljr--format-escape (msg)
+ "Make the message consumable by format."
+ (replace-regexp-in-string "%" "%%" msg))
+
+(defun cljr--maybe-rethrow-error (response)
+ (if-let (err (cljr--get-error-value response))
+ (error (cljr--format-escape err))
+ response))
+
+(defun cljr--maybe-eval-ns-form ()
+ (when (and cljr-auto-eval-ns-form (cider-connected-p))
+ (cider-eval-ns-form)))
+
+;;;###autoload
+(defun cljr-add-missing-libspec ()
+ "Requires or imports the symbol at point.
+
+If the symbol at point is of the form str/join then the ns
+containing join will be aliased to str.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-missing-libspec"
+ (interactive)
+ (cljr--ensure-op-supported "resolve-missing")
+ (let* ((symbol (cider-symbol-at-point))
+ (candidates (cljr--call-middleware-to-resolve-missing symbol)))
+ (if (and candidates (< 0 (length candidates)))
+ (cljr--add-missing-libspec symbol candidates)
+ (cljr--post-command-message "Can't find %s on classpath" (cljr--symbol-suffix symbol))))
+ (cljr--maybe-clean-ns)
+ (cljr--maybe-eval-ns-form))
+
+(defun cljr--dependency-vector-at-point ()
+ (save-excursion
+ (ignore-errors
+ (while (not (cljr--looking-at-dependency-vector-p))
+ (paredit-backward-up))
+ (buffer-substring-no-properties (point)
+ (cljr--point-after 'paredit-forward)))))
+
+(defun cljr--hotload-dependency-callback (response)
+ (cljr--maybe-rethrow-error response)
+ (cljr--post-command-message "Hotloaded %s" (nrepl-dict-get response "dependency")))
+
+(defun cljr--call-middleware-to-hotload-dependency (dep)
+ (cljr--call-middleware-async
+ (cljr--create-msg "hotload-dependency"
+ "coordinates" dep)
+ #'cljr--hotload-dependency-callback))
+
+(defun cljr--assert-dependency-vector (string)
+ (with-temp-buffer
+ (insert string)
+ (goto-char (point-min))
+ (cl-assert (cljr--looking-at-dependency-vector-p) nil
+ (format
+ (concat "Expected dependency vector of type "
+ "[org.clojure \"1.7.0\"], but got '%s'")
+ string)))
+ string)
+
+;;;###autoload
+(defun cljr-hotload-dependency ()
+ "Download a dependency (if needed) and hotload it into the current repl session.
+
+Defaults to the dependency vector at point, but prompts if none is found.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-hotload-dependency"
+ (interactive)
+ (cljr--ensure-op-supported "hotload-dependency")
+ (let ((dependency-vector (or (cljr--dependency-vector-at-point)
+ (cljr--prompt-user-for "Dependency vector: "))))
+ (cljr--assert-dependency-vector dependency-vector)
+ (cljr--call-middleware-async
+ (cljr--create-msg "hotload-dependency" "coordinates" dependency-vector)
+ #'cljr--hotload-dependency-callback)))
+
+(defun cljr--defn-str (&optional public)
+ (if public
+ "(defn "
+ (concat "(defn"
+ (if cljr-favor-private-functions
+ (if clojure-use-metadata-for-privacy
+ " ^:private "
+ "- ")
+ " "))))
+
+(defun cljr--call-middleware-to-find-used-locals (file line column)
+ (string-join
+ (cljr--call-middleware-sync
+ (cljr--create-msg "find-used-locals" "file" file "line" line
+ "column" column)
+ "used-locals") " "))
+
+(defun cljr--goto-enclosing-sexp ()
+ (let ((sexp-regexp (rx (or "(" "#{" "{" "["))))
+ (unless (looking-at sexp-regexp)
+ (paredit-backward-up))
+ (when (looking-back "#" 1)
+ (forward-char -1))))
+
+;;;###autoload
+(defun cljr-extract-function ()
+ "Extract the form at (or above) point as a top-level defn.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-extract-function"
+ (interactive)
+ (cljr--ensure-op-supported "find-used-locals")
+ (when (cljr--asts-y-or-n-p)
+ (save-buffer)
+ (cljr--goto-enclosing-sexp)
+ (let* ((unbound (cljr--call-middleware-to-find-used-locals
+ (buffer-file-name) (line-number-at-pos)
+ ;; +1 because the middleware expects indexing from 1
+ ;; +1 more because point has to be inside the sexp,
+ ;; not on the opening paren
+ (+ (current-column) 2)))
+ (name (unless (cljr--use-multiple-cursors-p)
+ (let ((highlight (cljr--highlight-sexp)))
+ (unwind-protect
+ (cljr--prompt-user-for "Name: ")
+ (delete-overlay highlight)))))
+ (body (clojure-delete-and-extract-sexp)))
+ (save-excursion
+ (cljr--make-room-for-toplevel-form)
+ (insert (cljr--defn-str))
+ (if name
+ (insert name)
+ (mc/create-fake-cursor-at-point))
+ (newline)
+ (indent-according-to-mode)
+ (insert "[" unbound "]")
+ (newline-and-indent)
+ (insert body ")"))
+ (insert "(")
+ (when name (insert name))
+ (save-excursion
+ (unless (string-blank-p unbound)
+ (insert " " unbound))
+ (insert ")"))
+ (unless name
+ (mc/maybe-multiple-cursors-mode)))))
+
+(add-to-list 'mc--default-cmds-to-run-once 'cljr-extract-function)
+
+(defun cljr--at-end-of-symbol-at-point ()
+ (looking-back (regexp-quote (cider-symbol-at-point)) (point-at-bol)))
+
+(defun cljr--insert-function-stubs (functions)
+ (unless (cljr--at-end-of-symbol-at-point)
+ (paredit-forward))
+ (save-excursion
+ (dolist (fn functions)
+ (newline-and-indent)
+ (insert "(" (gethash :name fn) " " (gethash :parameter-list fn) ")")))
+ (when (> (length functions) 0)
+ ;; Move cursor to point where the first functino body goes
+ (paredit-forward-down)
+ (paredit-forward 2)))
+
+;;;###autoload
+(defun cljr-add-stubs ()
+ "Adds implementation stubs for the interface or protocol at point.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-add-stubs"
+ (interactive)
+ (cljr--ensure-op-supported "stubs-for-interface")
+ (let* ((interface (cider-symbol-at-point))
+ (prefix? (cljr--symbol-prefix interface))
+ (alias? (cljr--resolve-alias prefix?))
+ (interface (if (not (string-blank-p prefix?))
+ (if alias?
+ (format "%s/%s" alias? (cljr--symbol-suffix interface))
+ interface)
+ (format "%s/%s" (cider-current-ns) interface)))
+ (functions (edn-read (cljr--call-middleware-sync
+ (cljr--create-msg "stubs-for-interface"
+ "interface" interface)
+ "functions"))))
+ (cljr--insert-function-stubs functions)))
+
+(defun cljr--delete-definition (definition)
+ "Delete a definition as part of inlining a symbol."
+ (let ((file (cljr--get-valid-filename definition))
+ (line-beg (gethash :line-beg definition))
+ (col-beg (gethash :col-beg definition)))
+ (with-current-buffer (find-file-noselect file)
+ (goto-char (point-min))
+ (forward-line (1- line-beg))
+ (forward-char (1- col-beg))
+ (clojure-delete-and-extract-sexp)
+ (when (clojure--inside-let-binding-p)
+ (clojure-delete-and-extract-sexp)
+ (if (save-excursion (cljr--get-let-bindings))
+ (progn
+ (while (looking-at-p "\s*\n")
+ (forward-line)
+ (join-line))
+ (when (looking-at-p "]")
+ ;; we just deleted the last binding in the vector
+ (join-line)))
+ (cljr--eliminate-let))
+ (cljr--indent-defun))
+ (when (looking-at-p "\s*\n")
+ (cljr--just-one-blank-line))
+ (save-buffer))))
+
+(defun cljr--sort-occurrences (occurrences)
+ "Sort the OCCURRENCES so the last ones in the file comes first."
+ (seq-sort (lambda (o1 o2)
+ (let ((o1-line (gethash :line-beg o1))
+ (o2-line (gethash :line-beg o2))
+ (o1-col (gethash :col-beg o1))
+ (o2-col (gethash :col-beg o2)))
+ (cond
+ ((< o1-line o2-line) o2)
+ ((> o1-line o2-line) o1)
+ ((< o1-col o2-col ) o2)
+ ((> o1-col o2-col) o1)
+ (t (error "Sort occurrences failed to compare %s %s %s %s"
+ o1-line o2-line o1-col o2-col)))))
+ occurrences))
+
+(defun cljr--inline-fn-at-call-site (def call-site)
+ "Point is at a call site, where the sexp call-site has just
+ been extracted."
+ (let ((args (cl-rest call-site))
+ (params (with-temp-buffer
+ (insert def)
+ (goto-char (point-min))
+ (paredit-forward-down 2)
+ (cljr--extract-sexp-as-list)))
+ (def (with-temp-buffer
+ (insert def)
+ (goto-char (point-min))
+ (paredit-forward-down 2)
+ (paredit-forward-up)
+ (paredit-splice-sexp-killing-backward)
+ (buffer-string))))
+ (dotimes (i (length args))
+ (setq def (replace-regexp-in-string (format "\\_<%s\\_>" (nth i params))
+ (nth i args) def t t)))
+ (insert def)))
+
+(defun cljr--inline-symbol (definition occurrences)
+ (let* ((def (gethash :definition definition))
+ (inline-fn-p (string-prefix-p "(fn" def)))
+ (dolist (symbol-meta (cljr--sort-occurrences occurrences))
+ (let* ((file (cljr--get-valid-filename symbol-meta))
+ (line-beg (gethash :line-beg symbol-meta))
+ (col-beg (gethash :col-beg symbol-meta)))
+ (with-current-buffer (find-file-noselect file)
+ (goto-char (point-min))
+ (forward-line (1- line-beg))
+ (forward-char (1- col-beg))
+ (let* ((call-site-p (and inline-fn-p
+ (looking-back "(\s*" (point-at-bol))))
+ (sexp (if call-site-p
+ (prog1 (cljr--extract-sexp-as-list)
+ (paredit-backward-up)
+ (clojure-delete-and-extract-sexp))
+ (clojure-delete-and-extract-sexp))))
+ (if call-site-p
+ (cljr--inline-fn-at-call-site def sexp)
+ (insert def)))))))
+ (save-buffer)
+ (cljr--delete-definition definition))
+
+(defun cljr--var-info (&optional symbol all)
+ "Like `cider-var-info' but also handles locally bound vars.
+
+If SYMBOL is passed we assume it's a global var and look it up.
+
+If SYMBOL is nil the symbol at point is used and we consider
+locals in that context.
+
+If the symbol is bound locally nil will be returned.
+
+ALL has the same meaning as for `cider-var-info'"
+ (if symbol
+ (cider-var-info symbol all)
+ (let ((used-locals (split-string (cljr--call-middleware-to-find-used-locals
+ (expand-file-name (buffer-file-name))
+ (line-number-at-pos) (1+ (current-column))) " "))
+ (symbol (cider-symbol-at-point)))
+ (unless (member symbol used-locals)
+ (cider-var-info symbol all)))))
+
+;;;###autoload
+(defun cljr-inline-symbol ()
+ "Inline the symbol at point.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-inline-symbol"
+ (interactive)
+ (cljr--ensure-op-supported "extract-definition")
+ (when (cljr--asts-y-or-n-p)
+ (save-buffer)
+ (save-excursion
+ (let* ((filename (funcall cider-to-nrepl-filename-function (buffer-file-name)))
+ (line (line-number-at-pos))
+ (column (1+ (current-column)))
+ (dir (cljr--project-dir))
+ (symbol (cider-symbol-at-point))
+ (var-info (cljr--var-info))
+ (ns (or (nrepl-dict-get var-info "ns") (cider-current-ns)))
+ (symbol-name (or (nrepl-dict-get var-info "name") symbol))
+ (extract-definition-request (list
+ "op" "extract-definition"
+ "ns" ns
+ "dir" dir
+ "file" filename
+ "line" line
+ "column" column
+ "name" symbol-name
+ "ignore-errors"
+ (when cljr-ignore-analyzer-errors "true")))
+ (response (edn-read (cljr--call-middleware-sync
+ extract-definition-request "definition")))
+ (definition (gethash :definition response))
+ (occurrences (gethash :occurrences response)))
+ (cljr--inline-symbol definition occurrences)
+ (unless *cljr--noninteractive* ; don't spam when called from `cljr-remove-let'
+ (if occurrences
+ (cljr--post-command-message "Inlined %s occurrence(s) of '%s'" (length occurrences) symbol)
+ (cljr--post-command-message "No occurrences of '%s' found. Deleted the definition." symbol)))))
+ (cljr--indent-defun)))
+
+(defun cljr--version (&optional remove-package-version)
+ "Get the version of `clj-refactor' from the package header.
+
+if REMOVE-PACKAGE_VERSION is t get rid of the (package: 20150828.1048) suffix."
+ (let ((version (replace-regexp-in-string "snapshot" "-SNAPSHOT"
+ (string-trim (pkg-info-version-info 'clj-refactor)))))
+ (if remove-package-version
+ (replace-regexp-in-string " (.*)" "" version)
+ version)))
+
+(defun cljr--middleware-version ()
+ (cljr--call-middleware-sync (cljr--create-msg "version") "version"))
+
+(defun cljr--check-middleware-version ()
+ "Check whether clj-refactor and nrepl-refactor versions are the same"
+ (let ((refactor-nrepl-version (or (cljr--middleware-version)
+ "n/a")))
+ (unless (string-equal (downcase refactor-nrepl-version)
+ (downcase (cljr--version :remove-package-version)))
+ (cider-repl-emit-interactive-stderr
+ (format "WARNING: clj-refactor and refactor-nrepl are out of sync.
+Their versions are %s and %s, respectively.
+You can mute this warning by changing cljr-suppress-middleware-warnings."
+ (cljr--version) refactor-nrepl-version)))))
+
+;;;###autoload
+(defun cljr-version ()
+ "Returns the version of the middleware as well as this package."
+ (interactive)
+ (message "clj-refactor %s, refactor-nrepl %s"
+ (cljr--version)
+ (or (ignore-errors (cljr--middleware-version))
+ "is unreachable")))
+
+;;;###autoload
+(defun cljr-toggle-debug-mode ()
+ (interactive)
+ (setq cljr--debug-mode (not cljr--debug-mode))
+ (if cljr--debug-mode
+ (message "Debug mode on")
+ (message "Debug mode off")))
+
+(defun cljr--check-clojure-version ()
+ (if-let ((clojure-version (cider--clojure-version)))
+ (when (version< clojure-version cljr-minimum-clojure-version)
+ (cider-repl-emit-interactive-stderr
+ (format "WARNING: Clojure version (%s) is not supported (minimum %s). The refactor-nrepl middleware won't work and has been disabled!"
+ clojure-version cljr-minimum-clojure-version)))
+ (cider-repl-emit-interactive-stderr
+ (format "WARNING: Can't determine Clojure version. The refactor-nrepl middleware requires clojure %s (or newer)" cljr-minimum-clojure-version))))
+
+(defun cljr--check-project-context ()
+ (unless (cljr--project-dir)
+ (cider-repl-emit-interactive-stderr
+ (format "WARNING: No clojure project detected. The refactor-nrepl middleware won't work and has been disabled!"))))
+
+(defun cljr--init-middleware ()
+ (unless cljr-suppress-middleware-warnings
+ (cljr--check-clojure-version)
+ (cljr--check-middleware-version))
+ (cljr--check-project-context)
+ ;; Best effort; don't freak people out with errors
+ (ignore-errors
+ (when (cljr--middleware-version) ; check if middleware is running
+ (when cljr-populate-artifact-cache-on-startup
+ (cljr--update-artifact-cache))
+ (when (and (not cljr-warn-on-eval)
+ cljr-eagerly-build-asts-on-startup)
+ (cljr--warm-ast-cache)))))
+
+(defvar cljr--list-fold-function-names
+ '("map" "mapv" "pmap" "keep" "mapcat" "filter" "remove" "take-while" "drop-while"
+ "group-by" "partition-by" "some" "every?" "not-every?" "not-any?"))
+
+(defvar cljr--list-fold-function-names-with-index
+ '("map-indexed" "keep-indexed"))
+
+(defun cljr--ns-path (ns-name)
+ "Find the file path to the ns named NS-NAME."
+ (cider-ensure-connected)
+ (cider-ensure-op-supported "ns-path")
+ (cider-sync-request:ns-path ns-name))
+
+;;;###autoload
+(defun cljr-create-fn-from-example ()
+ "Create a top-level defn for the symbol at point.
+The context in which symbol is being used should be that of a
+function, and the arglist of the defn is guessed from this
+context.
+
+For instance, if the symbol is the first argument of a `map'
+call, the defn is created with one argument. If it is the first
+argument of a `reduce', the defn will take two arguments.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-create-fn-from-example"
+ (interactive)
+ (while (cljr--keywordp (car (cljr--extract-sexp-as-list)))
+ (paredit-backward-up))
+ (let* ((sexp-forms* (cljr--extract-sexp-as-list))
+ (fn-name (car sexp-forms*))
+ (symbol-at-point (or (cider-symbol-at-point) ""))
+ (parent-fn (ignore-errors
+ (save-excursion
+ (paredit-backward-up 2)
+ (forward-char)
+ (cljr--extract-sexp))))
+ (args (cl-rest (cond
+ ((or (string= parent-fn "->")
+ (string= parent-fn "->>"))
+ (save-excursion
+ (paredit-backward-up)
+ (cljr--unwind-and-extract-this-as-list fn-name)))
+
+ ((or (string= fn-name "->")
+ (string= fn-name "->>"))
+ (save-excursion
+ (setq fn-name symbol-at-point)
+ (cljr--unwind-and-extract-this-as-list fn-name)))
+
+ (:else sexp-forms*))))
+ (prefix (cljr--symbol-prefix symbol-at-point))
+ (path (when (cljr--string-present-p prefix)
+ (cljr--ns-path (cljr--resolve-alias prefix)))))
+ (push-mark)
+ (if (cljr--symbolp symbol-at-point)
+ (cond ((string= fn-name "update")
+ (cljr--create-fn-from-update args path))
+
+ ((string= fn-name "update-in")
+ (cljr--create-fn-from-update-in path))
+
+ ((string= fn-name "sort-by")
+ (cljr--create-fn-from-sort-by args path))
+
+ ((string= fn-name "sort")
+ (cljr--create-fn-from-sort args path))
+
+ ((string= fn-name "reduce")
+ (cljr--create-fn-from-reduce args path))
+
+ ((string= fn-name "repeatedly")
+ (cljr--insert-example-fn symbol-at-point nil path))
+
+ ((member fn-name cljr--list-fold-function-names)
+ (cljr--create-fn-from-list-fold args path))
+
+ ((member fn-name cljr--list-fold-function-names-with-index)
+ (cljr--create-fn-from-list-fold-with-index args path))
+
+ ((and (featurep 'cider) (cider-connected-p)
+ (cljr--var-info fn-name :all))
+ (cljr--insert-example-fn symbol-at-point (list "args") path))
+
+ (:else
+ (cljr--insert-example-fn fn-name args path)))
+ (cljr--insert-example-fn fn-name args path))))
+
+(defun cljr--create-fn-from-list-fold (args path)
+ (cljr--insert-example-fn (car args)
+ (seq-map
+ (lambda (it)
+ (when-let (name (cljr--guess-param-name it))
+ (inflection-singularize-string name)))
+ (cdr args))
+ path))
+
+(defun cljr--create-fn-from-list-fold-with-index (args path)
+ (cljr--insert-example-fn (car args)
+ (cons "index"
+ (seq-map
+ (lambda (it)
+ (when-let (name (cljr--guess-param-name it))
+ (inflection-singularize-string name)))
+ (cdr args)))
+ path))
+
+(defun cljr--create-fn-from-update (args path)
+ (let ((keyfn (cadr args)))
+ (cljr--insert-example-fn (cider-symbol-at-point)
+ (if (cljr--keywordp keyfn)
+ (list (string-remove-prefix ":" keyfn))
+ (list 0))
+ path)))
+
+(defun cljr--create-fn-from-update-in (path)
+ (let ((last-path-entry (save-excursion
+ (paredit-backward-down)
+ (cider-symbol-at-point))))
+ (cljr--insert-example-fn (cider-symbol-at-point)
+ (if (cljr--keywordp last-path-entry)
+ (list (string-remove-prefix ":" last-path-entry))
+ (list 0))
+ path)))
+
+(defun cljr--create-fn-from-sort (args path)
+ (let* ((fn-name (cider-symbol-at-point))
+ (param-name (when-let (coll-name (cljr--guess-param-name (car (last args))))
+ (inflection-singularize-string coll-name))))
+ (cljr--insert-example-fn fn-name
+ (if param-name
+ (list (concat param-name "-a")
+ (concat param-name "-b"))
+ (list "a" "b"))
+ path)))
+
+(defun cljr--create-fn-from-sort-by (args path)
+ (let* ((fn-name (cider-symbol-at-point))
+ (making-comparator? (and (string= fn-name (cadr args))
+ (= 3 (length args))))
+ (param-name (if making-comparator?
+ (when (cljr--keywordp (car args))
+ (string-remove-prefix ":" (car args)))
+ (when-let (coll-name (cljr--guess-param-name (car (last args))))
+ (inflection-singularize-string coll-name)))))
+ (cljr--insert-example-fn fn-name
+ (if making-comparator?
+ (if param-name
+ (list (concat param-name "-a")
+ (concat param-name "-b"))
+ (list "a" "b"))
+ (list param-name))
+ path)))
+
+(defun cljr--create-fn-from-reduce (args path)
+ (cljr--insert-example-fn
+ (car args)
+ (list (or (and (= 3 (length args))
+ (cljr--guess-param-name (nth 1 args)))
+ "acc")
+ (when-let (name (cljr--guess-param-name (car (last args))))
+ (inflection-singularize-string name)))
+ path))
+
+(defun cljr--unwind-and-extract-this-as-list (name)
+ (let* ((parent-sexp (progn
+ (paredit-backward-up)
+ (cljr--extract-sexp)))
+ (unwound (cljr--unwind-s parent-sexp)))
+ (cljr--with-string-content unwound
+ (search-forward (concat "(" name))
+ (cljr--extract-sexp-as-list))))
+
+(defun cljr--unwind-s (s)
+ (if (string-prefix-p "(->" s)
+ (cljr--with-string-content s
+ (clojure-unwind-all)
+ (buffer-substring (point-min) (point-max)))
+ s))
+
+(defun cljr--keywordp (s)
+ (string-match-p "^::?[^0-9:[{(\"][^[{(\"]*$"
+ (replace-regexp-in-string "\n" " " s)))
+
+(defun cljr--symbolp (s)
+ "True when S is a symbol."
+ (string-match-p "^[^0-9:[{(\"][^[{(\"]*$"
+ (replace-regexp-in-string "\n" " " s)))
+
+(defun cljr--keyword-lookup-p (s)
+ (string-match "^(:\\([^ 0-9:[{(\"][^[{(\"]+\\) " s))
+
+(defun cljr--first-fn-call-s (s)
+ (cljr--with-string-content s
+ (when (looking-at "(")
+ (paredit-forward-down)
+ (cljr--extract-sexp))))
+
+(defun cljr--first-arg-s (s)
+ (cljr--with-string-content s
+ (paredit-forward-down)
+ (paredit-forward)
+ (cljr--skip-past-whitespace-and-comments)
+ (cljr--extract-sexp)))
+
+(defun cljr--last-arg-s (s)
+ (cljr--with-string-content s
+ (paredit-forward)
+ (paredit-backward-down)
+ (paredit-backward)
+ (cljr--extract-sexp)))
+
+(defvar cljr--fns-that-get-item-out-of-coll
+ (list "first" "second" "last" "fnext" "nth" "rand-nth"))
+
+(defun cljr--strip-keyword-ns (s)
+ (when (string-match "[^/]+$" s)
+ (substring s (car (match-data)) (car (cdr (match-data))))))
+
+(defun cljr--dashed-words (s)
+ "Take the string S and replace all the word separators with '-'
+and make the whole string lower-cased."
+ (with-temp-buffer
+ (insert s)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (subword-forward)
+ (insert " "))
+ (mapconcat 'identity (split-string (downcase (buffer-string))) "-")))
+
+(defun cljr--guess-param-name (form)
+ (let* ((prepped-form (cljr--strip-off-semantic-noops
+ (cljr--unwind-s form)))
+ (fn-call (cljr--first-fn-call-s prepped-form)))
+ (cond
+ ((cljr--symbolp prepped-form)
+ prepped-form)
+ ((cljr--keyword-lookup-p prepped-form)
+ (cljr--strip-keyword-ns (match-string 1 prepped-form)))
+ ((and fn-call (string-suffix-p "." fn-call))
+ (cljr--dashed-words (car (last (split-string fn-call "\\." t)))))
+ ((and fn-call (string-prefix-p "create-" fn-call))
+ (string-remove-prefix "create-" fn-call))
+ ((and fn-call (string-prefix-p ".get" fn-call))
+ (cljr--dashed-words (string-remove-prefix ".get" fn-call)))
+ ((string= "get-in" fn-call)
+ (cljr--find-param-name-from-get-in prepped-form))
+ ((string= "get" fn-call)
+ (cljr--find-param-name-from-get prepped-form))
+ ((string= "repeat" fn-call)
+ (inflection-pluralize-string
+ (cljr--guess-param-name (cljr--last-arg-s prepped-form))))
+ ((member fn-call cljr--fns-that-get-item-out-of-coll)
+ (inflection-singularize-string
+ (cljr--guess-param-name (cljr--first-arg-s prepped-form)))))))
+
+(defvar cljr--semantic-noops--first-position
+ (list "assoc" "assoc-in" "update" "update-in" "dissoc" "conj" "concat"
+ "cycle" "rest" "nthrest" "nthnext" "next" "nnext" "butlast"
+ "reverse" "vec" "set" "distinct"))
+
+(defvar cljr--semantic-noops--last-position
+ (list "filter" "filterv" "remove" "take-nth" "cons" "drop" "drop-while"
+ "take-last" "take" "take-while" "drop-last" "sort" "sort-by"))
+
+(defun cljr--strip-off-semantic-noops (form)
+ "The idea here is that each of these functions, when called on
+ something, doesn't truly change what that something is - so we
+ can ignore them when trying to figure out a name for a parameter."
+ (cljr--with-string-content form
+ (let* ((fn-at-point (lambda ()
+ (ignore-errors
+ (save-excursion
+ (paredit-forward-down)
+ (cljr--extract-sexp)))))
+ (fn (funcall fn-at-point)))
+ (while (or (member fn cljr--semantic-noops--first-position)
+ (member fn cljr--semantic-noops--last-position))
+ (if (member fn cljr--semantic-noops--first-position)
+ (progn
+ (paredit-forward-down)
+ (paredit-forward)
+ (cljr--skip-past-whitespace-and-comments))
+ (paredit-forward)
+ (paredit-backward-down)
+ (paredit-backward))
+ (setq fn (funcall fn-at-point))))
+ (cljr--extract-sexp)))
+
+(defun cljr--find-param-name-from-get-in (form)
+ (let ((last-path-entry (cljr--with-string-content form
+ (paredit-forward-down)
+ (paredit-forward 3)
+ (paredit-backward-down)
+ (cider-symbol-at-point))))
+ (when (cljr--keywordp last-path-entry)
+ (string-remove-prefix ":" last-path-entry))))
+
+(defun cljr--find-param-name-from-get (form)
+ (let ((key (cljr--with-string-content form
+ (paredit-forward-down)
+ (paredit-forward 2)
+ (cljr--skip-past-whitespace-and-comments)
+ (cljr--extract-sexp))))
+ (when (cljr--keywordp key)
+ (string-remove-prefix ":" key))))
+
+(defun cljr--insert-example-fn (name args path)
+ "Create a new function from NAME and ARGS.
+
+If PATH is non-nil append the new function to the end of the file
+at PATH."
+ (let* ((params (lambda (word i)
+ (format "${%s:%s}" (+ i 1)
+ (or (and word (cljr--guess-param-name word))
+ (format "arg%s" i)))))
+ (stub (concat (cljr--defn-str path)
+ (if path (cljr--symbol-suffix name) name)
+ " ["
+ (string-join (seq-map-indexed params args) " ")
+ "]\n$0)")))
+ (when path
+ (find-file-other-window path)
+ (goto-char (point-max)))
+ (cljr--make-room-for-toplevel-form)
+ (yas-expand-snippet stub)))
+
+(defun cljr--extract-wiki-description (description-buffer)
+ (with-current-buffer description-buffer
+ (goto-char (point-min))
+ (while (not (looking-at-p " (current-column) breakpoint)
+ (paredit-backward-up)
+ (if (and (not (looking-back "^\\s-*" (point-at-bol))) (looking-at-p "\\["))
+ (newline-and-indent) ; Put lambdalist on its own line
+ (paredit-forward-down)
+ (cljr--forward-parameter) ; don't break right after ( or [
+ (while (save-excursion (cljr--forward-parameter)
+ (< (current-column) breakpoint))
+ (cljr--forward-parameter))
+ (newline-and-indent)))))
+
+(defun cljr--update-signature-order (signature-changes)
+ "Point is assumed to be at the first character in the lambda list.
+
+Updates the ordering of the function parameters."
+ (unless (seq-every-p (lambda (c) (= (gethash :new-index c) (gethash :old-index c)))
+ signature-changes)
+ (let (parameters)
+ ;; extract parameters
+ (dolist (_ signature-changes)
+ (push (cljr--delete-and-extract-function-parameter)
+ parameters))
+ (setq parameters (nreverse parameters))
+ ;; leave point in empty lambda list
+ (paredit-backward-up)
+ (paredit-forward-down)
+ (delete-region (point) (cljr--point-after 'cljr--skip-past-whitespace-and-comments))
+ ;; insert parameters in new order
+ (dotimes (i (length parameters))
+ (let ((old-name (gethash :old-name
+ (cljr--signature-change-at-index
+ signature-changes i))))
+ (insert (seq-find (lambda (param)
+ (string-prefix-p old-name param))
+ parameters)))
+ (unless (= (1+ i) (length parameters))
+ (insert " ")))
+ (cljr--maybe-wrap-form))))
+
+(defun cljr--goto-lambda-list ()
+ "Move into the lambda list of the function definition beginning
+at point.
+
+E.g. move point from here: |(defn foo [bar baz] ...)
+to here: (defn foo [|bar baz] ...)"
+ (paredit-forward-down)
+ (cljr--skip-past-whitespace-and-comments)
+ (while (not (looking-at-p "\\["))
+ (paredit-forward)
+ (cljr--skip-past-whitespace-and-comments))
+ (paredit-forward-down))
+
+(defun cljr--update-function-signature (signature-changes)
+ "Point is assumed to be just prior to the function definition
+ we're about to update."
+ (cljr--goto-lambda-list)
+ (cljr--update-signature-names signature-changes)
+ (cljr--goto-toplevel)
+ (cljr--goto-lambda-list)
+ (cljr--update-signature-order signature-changes))
+
+(defun cljr--call-site-p (fn)
+ "Is point at a call-site for FN?"
+ (save-excursion
+ (ignore-errors
+ (paredit-backward-up)
+ (paredit-forward-down)
+ (string-suffix-p (cljr--symbol-suffix fn) (cider-symbol-at-point)))))
+
+(defun cljr--no-changes-to-parameter-order-p (signature-changes)
+ (seq-every-p (lambda (e) (= (gethash :new-index e) (gethash :old-index e)))
+ signature-changes))
+
+(defun cljr--update-call-site (signature-changes)
+ "Point is assumed to be at the name of the function being
+called."
+ (unless (cljr--no-changes-to-parameter-order-p signature-changes)
+ (cljr--forward-parameter)
+ (let (args)
+ (dotimes (_ (length signature-changes))
+ (push (cljr--delete-and-extract-function-parameter) args))
+ (setq args (nreverse args))
+ (dotimes (i (length args))
+ (insert (nth (gethash :old-index
+ (seq-find (lambda (c) (= (gethash :new-index c) i))
+ signature-changes))
+ args))
+ (unless (= (1+ i) (length args))
+ (insert " ")))
+ (cljr--maybe-wrap-form))))
+
+(defun cljr--append-to-manual-intervention-buffer ()
+ "Append the current line to the buffer of stuff requiring
+manual intervention."
+ (let ((line (string-trim (buffer-substring-no-properties
+ (point-at-bol) (point-at-eol))))
+ (linum (line-number-at-pos))
+ (file (buffer-file-name)))
+ (with-current-buffer (get-buffer-create cljr--manual-intervention-buffer)
+ (goto-char (point-max))
+ (insert (format "%s:%s: %s\n" file linum line)))))
+
+(defun cljr--update-apply-call-site (signature-changes)
+ "Update a call-site where apply is used to call the function
+ whose signature we're currently editing.
+
+point is assumed to be at the function name"
+ (unless (cljr--no-changes-to-parameter-order-p signature-changes)
+ (let ((num-args 0)
+ (max-index (thread-last signature-changes
+ (seq-map (lambda (c) (let ((new (gethash :new-index c))
+ (old (gethash :old-index c)))
+ (if (/= old new)
+ (max old new)))))
+ (seq-remove #'null)
+ (apply #'max)))
+ beg end)
+ (cljr--skip-past-whitespace-and-comments)
+ (setq beg (point))
+ (paredit-forward)
+ (setq end (cljr--point-after 'paredit-forward-up))
+ (while (< (save-excursion (cljr--point-after 'cljr--forward-parameter)) end)
+ (cljr--forward-parameter)
+ (setq num-args (1+ num-args)))
+ (if (>= max-index (1- num-args))
+ ;; Some of the arguments in the final list of args to apply have changed
+ (cljr--append-to-manual-intervention-buffer)
+ (goto-char beg)
+ (cljr--update-call-site signature-changes)))))
+
+(defun cljr--update-partial-call-site (signature-changes)
+ "Update a call-site with partial application of the function
+ whose signature we're currently editing.
+
+This only handles the case where we have (partial my-fn a b c)
+and only parameters a b or c are affected.
+
+point is assumed to be at the function name"
+ (unless (cljr--no-changes-to-parameter-order-p signature-changes)
+ (let ((num-partials 0)
+ (max-index (thread-last signature-changes
+ (seq-map (lambda (c) (let ((new (gethash :new-index c))
+ (old (gethash :old-index c)))
+ (when (/= old new)
+ (max old new)))))
+ (seq-remove #'null)
+ (apply #'max)))
+ beg end)
+ (setq beg (point))
+ (cljr--skip-past-whitespace-and-comments)
+ (paredit-forward 1)
+ (setq end (cljr--point-after 'paredit-forward-up))
+ (while (< (save-excursion (cljr--point-after 'cljr--forward-parameter)) end)
+ (cljr--forward-parameter)
+ (setq num-partials (1+ num-partials)))
+ (if (>= max-index num-partials)
+ (cljr--append-to-manual-intervention-buffer)
+ (goto-char beg)
+ (cljr--update-call-site (seq-remove (lambda (c)
+ (>= (gethash :new-index c) num-partials))
+ signature-changes))))))
+
+(defun cljr--apply-call-site-p ()
+ "Is the function invocation at this place being done using
+ apply?
+
+Point is assumed to be at the function being called."
+ (ignore-errors
+ (save-excursion
+ (paredit-backward-up)
+ (paredit-forward-down)
+ (looking-at-p "apply"))))
+
+(defun cljr--partial-call-site-p ()
+ "Is the function invocation at this place being done using
+ partial.
+
+Point is assumed to be at the function being called."
+ (ignore-errors
+ (save-excursion
+ (paredit-backward-up)
+ (paredit-forward-down)
+ (looking-at-p "partial"))))
+
+(defun cljr--ignorable-occurrence-p ()
+ (save-excursion
+ (cljr--goto-toplevel)
+ (looking-at-p "\\s-*(ns")))
+
+(defun cljr--change-function-signature (occurrences signature-changes)
+ ;; SIGNATURE-CHANGES is a list of hashmaps with keys:
+ ;; :old-index, :new-index, :old-name :new-name
+ ;; Indexing is from 0
+ ;; The OCCURRENCES are the same as those returned by `cljr--find-symbol'
+ (let ((*cljr--noninteractive* t))
+ (dolist (symbol-meta occurrences)
+ (let ((file (cljr--get-valid-filename symbol-meta))
+ (line-beg (gethash :line-beg symbol-meta))
+ (col-beg (gethash :col-beg symbol-meta))
+ (name (gethash :name symbol-meta))
+ (match (gethash :match symbol-meta)))
+ (with-current-buffer
+ (find-file-noselect file)
+ (goto-char (point-min))
+ (forward-line (1- line-beg))
+ (move-to-column (1- col-beg))
+ (cond
+ ((cljr--ignorable-occurrence-p) :do-nothing)
+ ((cljr--call-site-p name) (cljr--update-call-site signature-changes))
+ ((cljr--partial-call-site-p)
+ (cljr--update-partial-call-site signature-changes))
+ ((cljr--apply-call-site-p)
+ (cljr--update-apply-call-site signature-changes))
+ ((cljr--defnp match)
+ (cljr--update-function-signature signature-changes))
+ (t (cljr--append-to-manual-intervention-buffer)))
+ (save-buffer)))))
+ (unless (cljr--empty-buffer-p (get-buffer-create cljr--manual-intervention-buffer))
+ (pop-to-buffer cljr--manual-intervention-buffer)
+ (goto-char (point-min))
+ (insert "The following occurrence(s) couldn't be handled automatically:\n\n")
+ (grep-mode)
+ (setq-local compilation-search-path (list (cljr--project-dir)))))
+
+(defun cljr--commit-signature-edit ()
+ (interactive)
+ (cljr--change-function-signature cljr--occurrences cljr--signature-changes)
+ (kill-buffer cljr--change-signature-buffer))
+
+(define-derived-mode cljr--change-signature-mode fundamental-mode
+ "Change Signature"
+ "Major mode for refactoring function signatures.")
+
+(defun cljr--setup-change-signature-buffer (control-buffer params)
+ (when (get-buffer control-buffer)
+ (kill-buffer control-buffer))
+ (pop-to-buffer control-buffer)
+ (delete-region (point-min) (point-max))
+ (insert "
+
+# M-n and M-p to re-order parameters.
+# e or C-c C-e to edit a name.
+# RET or C-c C-c when you're happy with your changes.
+# q or C-c C-k to abort. ")
+ (goto-char (point-min))
+ (insert (string-join params "\n"))
+ (forward-line -1)
+ (when (looking-at-p "&")
+ (forward-line 1)
+ (join-line))
+ (setq cljr--signature-changes (let (signature-changes)
+ (dotimes (i (length params))
+ (let ((h (make-hash-table)))
+ (puthash :old-index i h)
+ (puthash :new-index i h)
+ (puthash :old-name (nth i params) h)
+ (puthash :new-name (nth i params) h)
+ (push h signature-changes)))
+ (nreverse signature-changes)))
+ (cljr--change-signature-mode)
+ (view-mode))
+
+;;;###autoload
+(defun cljr-change-function-signature ()
+ "Change the function signature of the function at point.
+
+See: https://github.com/clojure-emacs/clj-refactor.el/wiki/cljr-change-function-signature"
+ (interactive)
+ (cljr--ensure-op-supported "find-symbol")
+ (when (cljr--asts-y-or-n-p)
+ (let* ((fn (cider-symbol-at-point))
+ (params (cljr--get-function-params fn))
+ (var-info (cljr--var-info fn))
+ (ns (nrepl-dict-get var-info "ns")))
+ (setq cljr--occurrences (cljr--find-symbol-sync fn ns)
+ cljr--signature-changes nil)
+ (cljr--setup-change-signature-buffer cljr--change-signature-buffer params)
+ (when (get-buffer cljr--manual-intervention-buffer)
+ (kill-buffer cljr--manual-intervention-buffer))
+ (pop-to-buffer cljr--change-signature-buffer))))
+
+;;;###autoload
+(defun cljr--inject-jack-in-dependencies ()
+ "Inject the REPL dependencies of clj-refactor at `cider-jack-in'.
+If injecting the dependencies is not preferred set `cljr-inject-dependencies-at-jack-in' to nil."
+ (when (and cljr-inject-dependencies-at-jack-in
+ (boundp 'cider-jack-in-lein-plugins)
+ (boundp 'cider-jack-in-nrepl-middlewares))
+ (add-to-list 'cider-jack-in-lein-plugins `("refactor-nrepl" ,(cljr--version t)))
+ (add-to-list 'cider-jack-in-nrepl-middlewares "refactor-nrepl.middleware/wrap-refactor")))
+
+;;;###autoload
+(eval-after-load 'cider
+ '(cljr--inject-jack-in-dependencies))
+
+(add-hook 'cider-connected-hook #'cljr--init-middleware)
+
+;; moved to Clojure mode, made obsolete here
+(define-obsolete-variable-alias 'cljr-thread-all-but-last 'clojure-thread-all-but-last "2.3.0-SNAPSHOT")
+(define-obsolete-variable-alias 'cljr-use-metadata-for-privacy 'clojure-use-metadata-for-privacy "2.3.0-SNAPSHOT")
+
+(define-obsolete-function-alias 'cljr-thread 'clojure-thread "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-thread-first-all 'clojure-thread-first-all "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-thread-last-all 'clojure-thread-last-all "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-unwind 'clojure-unwind "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-unwind-all 'clojure-unwind-all "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-cycle-privacy 'clojure-cycle-privacy "2.3.0-SNAPSHOT")
+(define-obsolete-function-alias 'cljr-cycle-if 'clojure-cycle-if "2.3.0-SNAPSHOT")
+(make-obsolete 'cljr-cycle-coll "reworked into convert collection to list, quoted list, map, vector, set in Clojure mode." "2.3.0-SNAPSHOT")
+
+;; ------ minor mode -----------
+;;;###autoload
+(define-minor-mode clj-refactor-mode
+ "A mode to keep the clj-refactor keybindings.
+
+\\{clj-refactor-map}"
+ nil " cljr" clj-refactor-map
+ (if clj-refactor-mode
+ (add-hook 'post-command-hook #'cljr--post-command-hook :append :local)
+ (remove-hook 'post-command-hook #'cljr--post-command-hook :local)))
+
+(provide 'clj-refactor)
+;;; clj-refactor.el ends here
diff --git a/init.el b/init.el
index 0a0be65..64e22e1 100644
--- a/init.el
+++ b/init.el
@@ -342,7 +342,7 @@
'(nil nil t)
'(package-selected-packages
(quote
- (leuven-theme smartparens helm-projectile projectile helm-ag helm yaml-mode web-mode tide meghanada markdown-mode magit json-mode groovy-mode elpy dockerfile-mode ac-cider cider clojure-snippets ac-helm)))
+ (clj-refactor leuven-theme smartparens helm-projectile projectile helm-ag helm yaml-mode web-mode tide meghanada markdown-mode magit json-mode groovy-mode elpy dockerfile-mode ac-cider cider clojure-snippets ac-helm)))
'(savehist-mode t)
'(select-enable-clipboard t)
'(sp-base-key-bindings (quote sp))