@@ -8,9 +8,10 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
8
8
alias ElixirLS.LanguageServer.Providers.SymbolUtils
9
9
alias ElixirLS.LanguageServer . { SourceFile , Parser }
10
10
require ElixirLS.LanguageServer.Protocol , as: Protocol
11
+ alias ElixirSense.Core.Normalized.Module , as: NormalizedModule
11
12
12
13
defmodule Info do
13
- defstruct [ :type , :name , :location , :children , :selection_location , :symbol ]
14
+ defstruct [ :type , :name , :detail , : location, :children , :selection_location , :symbol ]
14
15
end
15
16
16
17
@ macro_defs [ :defmacro , :defmacrop , :defguard , :defguardp ]
@@ -68,34 +69,34 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
68
69
# Modules, protocols
69
70
70
71
defp extract_symbol ( _module_name , { defname , location , arguments } )
71
- when defname in [ :defmodule , :defprotocol ] do
72
+ when defname in [ :defmodule , :defprotocol , :defimpl_transformed ] do
72
73
module_info =
73
74
case arguments do
74
75
# Handles `defmodule do\nend` type compile errors
75
76
[ [ do: module_body ] ] ->
76
77
# The LSP requires us to return a non-empty name
77
78
case defname do
78
- :defmodule -> { "MISSING_MODULE_NAME" , nil , module_body }
79
- :defprotocol -> { "MISSING_PROTOCOL_NAME" , nil , module_body }
79
+ :defmodule -> { "MISSING_MODULE_NAME" , nil , nil , module_body }
80
+ :defprotocol -> { "MISSING_PROTOCOL_NAME" , nil , nil , module_body }
80
81
end
81
82
82
83
[ module_expression , [ do: module_body ] ] ->
83
- module_name_location =
84
+ { module_name_location , symbol } =
84
85
case module_expression do
85
- { _ , location , _ } -> location
86
- _ -> nil
86
+ { _ , location , _ } -> { location , Macro . to_string ( module_expression ) }
87
+ _ -> { nil , nil }
87
88
end
88
89
89
90
# TODO extract module name location from Code.Fragment.surround_context?
90
91
# TODO better selection ranges for defimpl?
91
- { extract_module_name ( module_expression ) , module_name_location , module_body }
92
+ { extract_module_name ( module_expression ) , symbol , module_name_location , module_body }
92
93
93
94
_ ->
94
95
nil
95
96
end
96
97
97
98
if module_info do
98
- { module_name , module_name_location , module_body } = module_info
99
+ { module_name , symbol , module_name_location , module_body } = module_info
99
100
100
101
mod_defns =
101
102
case module_body do
@@ -114,16 +115,18 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
114
115
type =
115
116
case defname do
116
117
:defmodule -> :module
118
+ :defimpl_transformed -> :module
117
119
:defprotocol -> :interface
118
120
end
119
121
120
122
% Info {
121
123
type: type ,
122
- name: module_name ,
124
+ name: symbol || module_name ,
125
+ detail: if ( defname == :defimpl_transformed , do: :defimpl , else: defname ) ,
123
126
location: location ,
124
127
selection_location: module_name_location ,
125
128
children: module_symbols ,
126
- symbol: module_name
129
+ symbol: symbol || module_name
127
130
}
128
131
end
129
132
end
@@ -135,7 +138,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
135
138
) do
136
139
extract_symbol (
137
140
module_name ,
138
- { :defmodule , location ,
141
+ { :defimpl_transformed , location ,
139
142
[ [ protocol: protocol_expression , implementations: for_expression ] , [ do: module_body ] ] }
140
143
)
141
144
end
@@ -155,6 +158,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
155
158
% Info {
156
159
type: :struct ,
157
160
name: "#{ defname } #{ module_name } " ,
161
+ detail: defname ,
158
162
location: location ,
159
163
children: children
160
164
}
@@ -169,45 +173,39 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
169
173
not is_nil ( type_expression ) do
170
174
type_name_location =
171
175
case type_expression do
172
- [ { :"::" , _ , [ { _ , type_head_location , _ } = type_head | _ ] } ] ->
173
- { Macro . to_string ( type_head ) , type_head_location }
176
+ [ { :"::" , _ , [ { name , type_head_location , args } = type_head | _ ] } ] ->
177
+ { { name , args } , type_head_location }
174
178
175
- [ { :when , _ , [ { :"::" , _ , [ { _ , type_head_location , _ } = type_head , _ ] } , _ ] } ] ->
176
- { Macro . to_string ( type_head ) , type_head_location }
179
+ [ { :when , _ , [ { :"::" , _ , [ { name , type_head_location , args } = type_head , _ ] } , _ ] } ] ->
180
+ { { name , args } , type_head_location }
177
181
178
- [ { _ , type_head_location , _ } = type_head | _ ] ->
179
- { Macro . to_string ( type_head ) , type_head_location }
182
+ [ { name , type_head_location , args } = type_head | _ ] ->
183
+ { { name , args } , type_head_location }
180
184
181
185
_ ->
182
186
nil
183
187
end
184
188
185
189
if type_name_location do
186
- { type_name , type_head_location } = type_name_location
187
-
188
- type_name =
189
- type_name
190
- |> String . replace ( ~r/ ,*\n \s */ u , fn
191
- "," <> _ -> ", "
192
- _ -> ""
193
- end )
190
+ { { name , args } , type_head_location } = type_name_location
194
191
195
192
type = if type_kind in [ :type , :typep , :opaque ] , do: :class , else: :event
196
193
197
194
% Info {
198
195
type: type ,
199
- name: "@#{ type_kind } #{ type_name } " ,
196
+ name: "#{ name } /#{ length ( args || [ ] ) } " ,
197
+ detail: "@#{ type_kind } " ,
200
198
location: location ,
201
199
selection_location: type_head_location ,
202
- symbol: " #{ type_name } " ,
200
+ symbol: to_string ( name ) ,
203
201
children: [ ]
204
202
}
205
203
end
206
204
end
207
205
208
206
# @behaviour BehaviourModule
209
207
defp extract_symbol ( _current_module , { :@ , location , [ { :behaviour , _ , [ behaviour_expression ] } ] } ) do
210
- module_name = extract_module_name ( behaviour_expression )
208
+ module_name = Macro . to_string ( behaviour_expression )
211
209
212
210
% Info { type: :interface , name: "@behaviour #{ module_name } " , location: location , children: [ ] }
213
211
end
@@ -220,10 +218,10 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
220
218
# Function, macro, guard with when
221
219
defp extract_symbol (
222
220
_current_module ,
223
- { defname , location , [ { :when , _ , [ { _ , head_location , _ } = fn_head , _ ] } | _ ] }
221
+ { defname , location , [ { :when , _ , [ { name , head_location , args } = fn_head , _ ] } | _ ] }
224
222
)
225
223
when defname in @ defs do
226
- name =
224
+ head =
227
225
Macro . to_string ( fn_head )
228
226
|> String . replace ( ~r/ ,*\n \s */ u , fn
229
227
"," <> _ -> ", "
@@ -232,18 +230,22 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
232
230
233
231
% Info {
234
232
type: if ( defname in @ macro_defs , do: :constant , else: :function ) ,
235
- symbol: "#{ name } " ,
236
- name: "#{ defname } #{ name } " ,
233
+ symbol: to_string ( name ) ,
234
+ name: "#{ to_string ( name ) } /#{ length ( args || [ ] ) } " ,
235
+ detail: defname ,
237
236
location: location ,
238
237
selection_location: head_location ,
239
238
children: [ ]
240
239
}
241
240
end
242
241
243
242
# Function, macro, delegate
244
- defp extract_symbol ( _current_module , { defname , location , [ { _ , head_location , _ } = fn_head | _ ] } )
243
+ defp extract_symbol (
244
+ _current_module ,
245
+ { defname , location , [ { name , head_location , args } = fn_head | _ ] }
246
+ )
245
247
when defname in @ defs do
246
- name =
248
+ head =
247
249
Macro . to_string ( fn_head )
248
250
|> String . replace ( ~r/ ,*\n \s */ u , fn
249
251
"," <> _ -> ", "
@@ -252,8 +254,9 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
252
254
253
255
% Info {
254
256
type: if ( defname in @ macro_defs , do: :constant , else: :function ) ,
255
- symbol: "#{ name } " ,
256
- name: "#{ defname } #{ name } " ,
257
+ symbol: to_string ( name ) ,
258
+ name: "#{ to_string ( name ) } /#{ length ( args || [ ] ) } " ,
259
+ detail: defname ,
257
260
location: location ,
258
261
selection_location: head_location ,
259
262
children: [ ]
@@ -278,7 +281,8 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
278
281
279
282
% Info {
280
283
type: :class ,
281
- name: "defrecord #{ name } " ,
284
+ name: "#{ name } " ,
285
+ detail: :defrecord ,
282
286
location: location |> Keyword . merge ( Keyword . take ( alias_location , [ :line , :column ] ) ) ,
283
287
children: children
284
288
}
@@ -288,7 +292,8 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
288
292
defp extract_symbol ( _current_module , { :test , location , [ name | _ ] } ) do
289
293
% Info {
290
294
type: :function ,
291
- name: "test #{ Macro . to_string ( name ) } " ,
295
+ name: Macro . to_string ( name ) ,
296
+ detail: :test ,
292
297
location: location ,
293
298
children: [ ]
294
299
}
@@ -320,7 +325,8 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
320
325
321
326
% Info {
322
327
type: :function ,
323
- name: "describe #{ Macro . to_string ( name ) } " ,
328
+ name: Macro . to_string ( name ) ,
329
+ detail: :describe ,
324
330
location: location ,
325
331
children: module_symbols
326
332
}
@@ -374,6 +380,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
374
380
375
381
% Protocol.DocumentSymbol {
376
382
name: info . name ,
383
+ detail: info . detail ,
377
384
kind: SymbolUtils . symbol_kind_to_code ( info . type ) ,
378
385
range: range ,
379
386
selectionRange: selection_range ,
@@ -481,7 +488,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
481
488
)
482
489
483
490
symbol != nil ->
484
- end_char = SourceFile . elixir_character_to_lsp ( symbol , String . length ( symbol ) )
491
+ end_char = SourceFile . elixir_character_to_lsp ( symbol , String . length ( to_string ( symbol ) ) )
485
492
{ start_line , start_character + end_char + 1 }
486
493
487
494
parent_end_line =
@@ -537,30 +544,17 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
537
544
"[" <> list_stringified <> "]"
538
545
end
539
546
540
- defp extract_module_name ( { :__aliases__ , location , [ head | tail ] } ) when not is_atom ( head ) do
541
- extract_module_name ( head ) <> "." <> extract_module_name ( { :__aliases__ , location , tail } )
542
- end
543
-
544
- defp extract_module_name ( { :__aliases__ , _location , module_names } ) do
545
- if Enum . all? ( module_names , & is_atom / 1 ) do
546
- Enum . join ( module_names , "." )
547
- else
548
- "# unknown"
549
- end
550
- end
551
-
552
- defp extract_module_name ( { :__MODULE__ , _location , nil } ) do
553
- "__MODULE__"
554
- end
555
-
556
547
defp extract_module_name ( module ) when is_atom ( module ) do
557
548
case Atom . to_string ( module ) do
558
- "Elixir." <> elixir_module_rest -> elixir_module_rest
559
- erlang_module -> erlang_module
549
+ "Elixir." <> elixir_module_rest ->
550
+ elixir_module_rest
551
+
552
+ erlang_module ->
553
+ erlang_module
560
554
end
561
555
end
562
556
563
- defp extract_module_name ( _ ) , do: "# unknown"
557
+ defp extract_module_name ( other ) , do: Macro . to_string ( other )
564
558
565
559
defp extract_property ( property_name , location ) when is_atom ( property_name ) do
566
560
% Info {
0 commit comments