-
-
Notifications
You must be signed in to change notification settings - Fork 149
/
document_symbol.clj
120 lines (109 loc) · 4.84 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
(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)
(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 (q/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 (q/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)}])))