1
1
defmodule NextLS.SymbolTable do
2
+
2
3
@ moduledoc false
3
4
use GenServer
4
5
6
+ alias GenLSP.Structures.DocumentSymbol
7
+ alias GenLSP.Structures.Range
8
+ alias GenLSP.Structures.Position
9
+
5
10
defmodule Symbol do
6
- defstruct [ :file , :module , :type , :name , :line , :col ]
11
+ defstruct [ :file , :module , :type , :name , :line , :col , :document ]
7
12
8
13
def new ( args ) do
9
14
struct ( __MODULE__ , args )
@@ -20,6 +25,9 @@ defmodule NextLS.SymbolTable do
20
25
@ spec symbols ( pid ( ) | atom ( ) ) :: list ( struct ( ) )
21
26
def symbols ( server ) , do: GenServer . call ( server , :symbols )
22
27
28
+ @ spec symbols ( pid ( ) | atom ( ) , String . t ( ) ) :: list ( struct ( ) )
29
+ def symbols ( server , file ) , do: GenServer . call ( server , { :symbols , file } )
30
+
23
31
def close ( server ) , do: GenServer . call ( server , :close )
24
32
25
33
def init ( args ) do
@@ -36,10 +44,26 @@ defmodule NextLS.SymbolTable do
36
44
{ :ok , % { table: name } }
37
45
end
38
46
47
+ def handle_call ( { :symbols , file } , _ , state ) do
48
+ symbols =
49
+ case :dets . lookup ( state . table , file ) do
50
+ [ { _ , symbols } | _rest ] -> symbols
51
+ _ -> [ ]
52
+ end
53
+
54
+ { :reply , symbols , state }
55
+ end
56
+
39
57
def handle_call ( :symbols , _ , state ) do
40
58
symbols =
41
59
:dets . foldl (
42
- fn { _key , symbol } , acc -> [ symbol | acc ] end ,
60
+ fn { _key , symbol } , acc ->
61
+ if String . match? ( to_string ( symbol . name ) , ~r/ __.*__/ ) do
62
+ acc
63
+ else
64
+ [ symbol | acc ]
65
+ end
66
+ end ,
43
67
[ ] ,
44
68
state . table
45
69
)
@@ -59,10 +83,17 @@ defmodule NextLS.SymbolTable do
59
83
module_line: module_line ,
60
84
struct: struct ,
61
85
file: file ,
62
- defs: defs
86
+ defs: defs ,
87
+ ast: ast
63
88
} = symbols
64
89
90
+ { _new_ast , acc } = Macro . prewalk ( ast , nil , & walker / 2 )
91
+
92
+ document = File . read! ( file )
65
93
:dets . delete ( state . table , mod )
94
+ :dets . delete ( state . table , file )
95
+
96
+ :dets . insert ( state . table , { file , acc } )
66
97
67
98
:dets . insert (
68
99
state . table ,
@@ -73,7 +104,8 @@ defmodule NextLS.SymbolTable do
73
104
type: :defmodule ,
74
105
name: Macro . to_string ( mod ) ,
75
106
line: module_line ,
76
- col: 1
107
+ col: 1 ,
108
+ document: document
77
109
} }
78
110
)
79
111
@@ -89,14 +121,13 @@ defmodule NextLS.SymbolTable do
89
121
type: :defstruct ,
90
122
name: "%#{ Macro . to_string ( mod ) } {}" ,
91
123
line: meta [ :line ] ,
92
- col: 1
124
+ col: 1 ,
125
+ document: document
93
126
} }
94
127
)
95
128
end
96
129
97
- for { name , { :v1 , type , _meta , clauses } } <- defs ,
98
- not String . match? ( to_string ( name ) , ~r/ __.*__/ ) ,
99
- { meta , _ , _ , _ } <- clauses do
130
+ for { name , { :v1 , type , _meta , clauses } } <- defs , { meta , _ , _ , _ } <- clauses do
100
131
:dets . insert (
101
132
state . table ,
102
133
{ mod ,
@@ -106,11 +137,78 @@ defmodule NextLS.SymbolTable do
106
137
type: type ,
107
138
name: name ,
108
139
line: meta [ :line ] ,
109
- col: meta [ :column ] || 1
140
+ col: meta [ :column ] || 1 ,
141
+ document: document
110
142
} }
111
143
)
112
144
end
113
145
114
146
{ :noreply , state }
115
147
end
148
+
149
+ defp elixir_kind_to_lsp_kind ( :defmodule ) , do: GenLSP.Enumerations.SymbolKind . module ( )
150
+ defp elixir_kind_to_lsp_kind ( :defstruct ) , do: GenLSP.Enumerations.SymbolKind . struct ( )
151
+
152
+ defp elixir_kind_to_lsp_kind ( kind ) when kind in [ :def , :defp , :defmacro , :defmacrop ] ,
153
+ do: GenLSP.Enumerations.SymbolKind . function ( )
154
+
155
+ defp walker ( { :defmodule , meta , [ name | _children ] } = macro , nil ) do
156
+ { macro ,
157
+ % DocumentSymbol {
158
+ name: Macro . to_string ( name ) |> String . replace ( "\n " , "" ) ,
159
+ kind: GenLSP.Enumerations.SymbolKind . module ( ) ,
160
+ children: [ ] ,
161
+ range: % Range {
162
+ start: % Position { line: meta [ :do ] [ :line ] - 1 , character: meta [ :do ] [ :column ] - 1 } ,
163
+ end: % Position { line: meta [ :end ] [ :line ] - 1 , character: meta [ :end ] [ :column ] - 1 }
164
+ } ,
165
+ selection_range: % Range {
166
+ start: % Position { line: meta [ :line ] - 1 , character: meta [ :column ] - 1 } ,
167
+ end: % Position { line: meta [ :line ] - 1 , character: meta [ :column ] - 1 }
168
+ }
169
+ } }
170
+ end
171
+
172
+ # TODO: this needs to be a normal recursive function traversal, so that we don't walk
173
+ # these AST nodes anyway
174
+ defp walker ( { :defmodule , _meta , [ _name | _children ] } = macro , % DocumentSymbol { } = doc ) do
175
+ { _ , child } = Macro . prewalk ( macro , nil , & walker / 2 )
176
+
177
+ dbg ( child )
178
+ { macro , % DocumentSymbol { doc | children: doc . children ++ [ child ] } }
179
+ end
180
+
181
+ defp walker ( { type , meta , [ name | _children ] } = macro , % DocumentSymbol { } = root )
182
+ when type in [ :def , :defp , :defmacro , :defmacro , :defstruct ] do
183
+ { macro ,
184
+ % DocumentSymbol {
185
+ root
186
+ | children:
187
+ root . children ++
188
+ [
189
+ % DocumentSymbol {
190
+ name: Macro . to_string ( name ) |> String . replace ( "\n " , "" ) |> dbg ( ) ,
191
+ kind: elixir_kind_to_lsp_kind ( type ) ,
192
+ range: % Range {
193
+ start: % Position {
194
+ line: meta [ :line ] - 1 ,
195
+ character: meta [ :column ] - 1
196
+ } ,
197
+ end: % Position {
198
+ line: ( meta [ :end ] || meta [ :end_of_expression ] || meta ) [ :line ] - 1 ,
199
+ character: ( meta [ :end ] || meta [ :end_of_expression ] || meta ) [ :column ] - 1
200
+ }
201
+ } ,
202
+ selection_range: % Range {
203
+ start: % Position { line: meta [ :line ] - 1 , character: meta [ :column ] - 1 } ,
204
+ end: % Position { line: meta [ :line ] - 1 , character: meta [ :column ] - 1 }
205
+ }
206
+ }
207
+ ]
208
+ } }
209
+ end
210
+
211
+ defp walker ( other , acc ) do
212
+ { other , acc }
213
+ end
116
214
end
0 commit comments