-
-
Notifications
You must be signed in to change notification settings - Fork 149
/
document_symbol.clj
147 lines (133 loc) · 5.83 KB
/
document_symbol.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(ns clojure-lsp.feature.document-symbol
(:require
[clojure-lsp.parser :as parser]
[clojure-lsp.queries :as q]
[clojure-lsp.shared :as shared]))
(set! *warn-on-reflection* true)
(def ^:private defines-interface?
'#{clojure.core/defprotocol cljs.core/defprotocol
clojure.core/definterface cljs.core/definterface
clojure.core/defmulti cljs.core/defmulti})
(def ^:private defines-class?
'#{clojure.core/defrecord cljs.core/defrecord
clojure.core/deftype cljs.core/deftype})
(defn element->symbol-kind [{:keys [bucket] :as el}]
(case bucket
(:namespace-usages :namespace-definitions) :namespace
:var-definitions (cond
(or (:fixed-arities el) (:varargs-min-arity el) (:macro el))
#_=> :function
(some->> el q/defined-bys (some defines-interface?))
#_=> :interface
(some->> el q/defined-bys (some defines-class?))
#_=> :class
:else
#_=> :variable)
:var-usages (if (:defmethod el)
:function
:variable)
:keyword-usages :field
:null))
(defn element->name [{elem-name :name symbol :symbol :keys [dispatch-val-str]}]
(cond-> (or (some-> symbol str) (name elem-name))
dispatch-val-str (str " " dispatch-val-str)))
(defn ^:private element->document-symbol [e]
(shared/assoc-some
{:name (element->name e)
:kind (element->symbol-kind e)
:range (shared/->scope-range e)
:selection-range (shared/->range e)
:tags (cond-> []
(:deprecated e) (conj 1))}
:detail (when (:private e)
"private")))
(defn ^:private symbol-order [{:keys [selection-range]}]
[(:line (:start selection-range)) (:character (:start selection-range))])
(defn ^:private remove-first [item-to-remove coll]
(if (empty? coll)
() ; literal name for empty list
(let [item (first coll)]
(if (= item item-to-remove)
(rest coll)
(cons (first coll) (lazy-seq (remove-first item-to-remove (rest coll))))))))
(defn ^:private edn->element-tree [m keyword-elements* symbol-elements*]
;; TODO use tail recur for better performance
(when (coll? m)
(->> m
(reduce
(fn [acc entry]
(cond
(map? entry)
(concat acc (edn->element-tree entry keyword-elements* symbol-elements*))
(or (not (coll? entry))
(< (count entry) 2))
acc
:else
(let [[k v] (vec entry)
element (or (if (keyword? k)
(last (filter #(= (:name %) (name k)) @keyword-elements*))
(last (filter #(= (:symbol %) k) @symbol-elements*)))
;; fallback to dumb element
{:symbol (or (some-> k str) "nil") :row 0 :col 0 :end-row 0 :end-col 0})
_ (swap! keyword-elements* #(remove-first element %))
_ (swap! symbol-elements* #(remove-first element %))
document-symbol (element->document-symbol element)
kind (cond
(string? v) :string
(keyword? v) :field
(boolean? v) :boolean
(number? v) :number
(vector? v) :array
(list? v) :array
(set? v) :array
:else :struct)
document-symbol (assoc document-symbol :kind kind)]
(conj acc
(cond
(map? v)
(assoc document-symbol
:children
(edn->element-tree
v
keyword-elements*
symbol-elements*))
(coll? v)
(shared/assoc-some
document-symbol
:children
(->> v
(keep
#(edn->element-tree
%
keyword-elements*
symbol-elements*))
flatten
seq))
:else
document-symbol)))))
[])
(sort-by symbol-order))))
(defn document-symbols [db uri]
(if (identical? :edn (shared/uri->file-type uri))
(try
(some-> (parser/safe-zloc-of-file db uri)
parser/safe-zloc-sexpr
(edn->element-tree (atom (get-in db [:analysis uri :keyword-usages]))
(atom (get-in db [:analysis uri :symbols]))))
(catch Exception e
(println e)))
(when-let [namespace-definition (q/find-namespace-definition-by-uri db uri)]
[{:name (or (some-> namespace-definition :name name)
;; TODO Consider using URI for display purposes, especially if
;; we support remote LSP connections
(shared/uri->filename uri))
:kind (element->symbol-kind namespace-definition)
:range shared/full-file-range
:selection-range (if namespace-definition
(shared/->scope-range namespace-definition)
shared/full-file-range)
:children (->> (concat (q/find-var-definitions db uri true)
(q/find-defmethods db uri))
(map element->document-symbol)
(sort-by symbol-order)
vec)}])))