-
Notifications
You must be signed in to change notification settings - Fork 69
/
extract_definition.clj
97 lines (88 loc) · 3.51 KB
/
extract_definition.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
(ns refactor-nrepl.extract-definition
(:require [clojure.string :as str]
[refactor-nrepl
[core :refer [suffix]]
[s-expressions :as sexp]]
[refactor-nrepl.find.find-symbol :refer [find-symbol]]
[rewrite-clj.zip :as zip])
(:import [java.io PushbackReader StringReader]
java.util.regex.Pattern))
(defn- extract-definition-from-def
^String [^String sexp]
(let [def-form (read-string sexp)
docstring? (string? (nth def-form 2 :not-found))
sexp-sans-delimiters (.substring (str/trim sexp) 1 (dec (.length sexp)))
rdr (PushbackReader. (StringReader. sexp-sans-delimiters))]
(read rdr) ; discard def
(read rdr) ; discard var name
(when docstring?
(read rdr)) ; discard docstring
(str/trim (slurp rdr))))
(defn- extract-definition-from-defn
^String [^String sexp]
(let [form (read-string sexp)
fn-name (str (second form))]
(-> sexp
(.replaceFirst (if (re-find #"defn-" sexp) "defn-" "defn") "fn")
(.replaceFirst (str "\\s*" (Pattern/quote fn-name)) ""))))
(defn- extract-def-from-binding-vector
^String [^String bindings ^String var-name]
(let [zipper (zip/of-string bindings)
zloc (some (fn [zloc] (when (= (symbol var-name) (zip/sexpr zloc)) zloc))
(sexp/all-zlocs zipper))]
(when zloc
(str/trim (zip/string (zip/right zloc))))))
(defn- -extract-definition
[{:keys [file ^long line-beg ^long col-beg name]}]
(let [literal-sexp (sexp/get-enclosing-sexp (slurp file) (dec line-beg)
col-beg)
form (read-string literal-sexp)]
(.replaceAll
(case (first form)
def (extract-definition-from-def literal-sexp)
def- (extract-definition-from-def literal-sexp)
defn (extract-definition-from-defn literal-sexp)
defn- (extract-definition-from-defn literal-sexp)
(extract-def-from-binding-vector literal-sexp name))
"\r" "")))
(defn- def-form?
"Is FORM a def or defn?"
[form]
(if (and (or (list? form) (instance? clojure.lang.Cons form)) (seq form))
(case (first form)
def true
def- true
defn true
defn- true
false)
false))
(defn- def?
"Is the OCCURRENCE the defining form?"
[{:keys [file name ^long col-beg ^long line-beg]}]
(let [form (read-string (sexp/get-enclosing-sexp (slurp file) (dec line-beg)
col-beg))
name (symbol (suffix (read-string name)))]
(if (def-form? form)
(= (second form) name)
(when (vector? form)
(> (count (drop-while #(not= % name) form)) 1)))))
(defn- sort-by-linum
[occurrences]
(sort #(- (long (:line-beg %1)) (long (:line-beg %2))) occurrences))
(defn- find-definition [occurrences]
(some->> occurrences
(filter def?)
;; When working with let-like bindings we have to sort the
;; occurrences so the ones earlier in the file comes first
sort-by-linum
first))
(defn extract-definition
"Returns the definition of SYMBOL to facilitate inlining."
[msg]
(let [occurrences (find-symbol msg)]
(if-let [definition-occurrence (find-definition occurrences)]
{:definition (merge {:definition (-extract-definition definition-occurrence)}
definition-occurrence)
:occurrences (remove (partial = definition-occurrence) occurrences)}
(throw (IllegalStateException.
(str "Couldn't find definition for " (:name msg)))))))