@@ -7,11 +7,14 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
7
7
This is effectively the reverse of the "go to implementations" provider.
8
8
"""
9
9
10
+ alias ElixirSense.Core.Binding
11
+ alias ElixirSense.Core.SurroundContext
10
12
alias ElixirSense.Core.Metadata
11
13
alias ElixirSense.Core.Normalized.Code , as: NormalizedCode
12
14
alias ElixirSense.Core.State
13
15
alias ElixirLS.LanguageServer.Location
14
16
alias ElixirSense.Core.Parser
17
+ alias ElixirSense.Core.State . { ModFunInfo , SpecInfo }
15
18
16
19
require ElixirSense.Core.Introspection , as: Introspection
17
20
@@ -41,33 +44,166 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
41
44
end
42
45
43
46
@ doc false
44
- def find ( _context , % State.Env { module: module } = env , metadata ) do
45
- # Get the binding environment as in the other providers.
46
- # binding_env = Binding.from_env(env, metadata, context.begin)
47
+ def find ( context , % State.Env { module: module } = env , metadata ) do
48
+ binding_env = Binding . from_env ( env , metadata , context . begin )
47
49
48
- case env . function do
50
+ type = SurroundContext . to_binding ( context . context , module )
51
+
52
+ case type do
49
53
nil ->
50
54
nil
51
55
52
- { fun , arity } ->
53
- # Get the behaviours (and possibly protocols) declared for the current module.
54
- behaviours = Metadata . get_module_behaviours ( metadata , env , module )
55
-
56
- # For each behaviour, if the current function is a callback for it,
57
- # try to find the callback’s declaration.
58
- locations =
59
- for behaviour <- behaviours ,
60
- Introspection . is_callback ( behaviour , fun , arity , metadata ) ,
61
- location = get_callback_location ( behaviour , fun , arity , metadata ) ,
62
- location != nil do
63
- location
64
- end
65
-
66
- case locations do
67
- [ ] -> nil
68
- [ single ] -> single
69
- multiple -> multiple
56
+ { :keyword , _ } ->
57
+ nil
58
+
59
+ { :variable , variable , version } ->
60
+ var_info = Metadata . find_var ( metadata , variable , version , context . begin )
61
+
62
+ if var_info == nil do
63
+ # find local call
64
+ find_function (
65
+ { nil , variable } ,
66
+ context ,
67
+ env ,
68
+ metadata ,
69
+ binding_env
70
+ )
70
71
end
72
+
73
+ { :attribute , _attribute } ->
74
+ nil
75
+
76
+ { module , function } ->
77
+ find_function (
78
+ { module , function } ,
79
+ context ,
80
+ env ,
81
+ metadata ,
82
+ binding_env
83
+ )
84
+ end
85
+ end
86
+
87
+ defp find_function (
88
+ { { :variable , _ , _ } = type , function } ,
89
+ context ,
90
+ env ,
91
+ metadata ,
92
+ binding_env
93
+ ) do
94
+ case Binding . expand ( binding_env , type ) do
95
+ { :atom , module } ->
96
+ find_function (
97
+ { { :atom , module } , function } ,
98
+ context ,
99
+ env ,
100
+ metadata ,
101
+ binding_env
102
+ )
103
+
104
+ _ ->
105
+ nil
106
+ end
107
+ end
108
+
109
+ defp find_function (
110
+ { { :attribute , _ } = type , function } ,
111
+ context ,
112
+ env ,
113
+ metadata ,
114
+ binding_env
115
+ ) do
116
+ case Binding . expand ( binding_env , type ) do
117
+ { :atom , module } ->
118
+ find_function (
119
+ { { :atom , module } , function } ,
120
+ context ,
121
+ env ,
122
+ metadata ,
123
+ binding_env
124
+ )
125
+
126
+ _ ->
127
+ nil
128
+ end
129
+ end
130
+
131
+ defp find_function (
132
+ { module , function } ,
133
+ context ,
134
+ env ,
135
+ metadata ,
136
+ _binding_env
137
+ ) do
138
+ m = get_module ( module )
139
+
140
+ case { m , function }
141
+ |> Introspection . actual_mod_fun (
142
+ env ,
143
+ metadata . mods_funs_to_positions ,
144
+ metadata . types ,
145
+ context . begin ,
146
+ true
147
+ ) do
148
+ { mod , fun , false , _ } ->
149
+ { line , column } = context . end
150
+ call_arity = Metadata . get_call_arity ( metadata , mod , fun , line , column ) || :any
151
+
152
+ get_callback_location ( env . module , fun , call_arity , metadata )
153
+
154
+ { mod , fun , true , :mod_fun } ->
155
+ { line , column } = context . end
156
+ call_arity = Metadata . get_call_arity ( metadata , mod , fun , line , column ) || :any
157
+
158
+ find_callback ( mod , fun , call_arity , metadata , env )
159
+
160
+ _ ->
161
+ nil
162
+ end
163
+ end
164
+
165
+ defp get_module ( { :atom , module } ) , do: module
166
+ defp get_module ( _ ) , do: nil
167
+
168
+ def find_callback ( mod , fun , arity , metadata , env ) do
169
+ # Get the behaviours (and possibly protocols) declared for the current module.
170
+ behaviours = Metadata . get_module_behaviours ( metadata , env , mod )
171
+
172
+ # For each behaviour, if the current function is a callback for it,
173
+ # try to find the callback’s declaration.
174
+ locations =
175
+ for behaviour <- behaviours ++ [ mod ] ,
176
+ Introspection . is_callback ( behaviour , fun , arity , metadata ) ,
177
+ location = get_callback_location ( behaviour , fun , arity , metadata ) ,
178
+ location != nil do
179
+ location
180
+ end
181
+
182
+ locations =
183
+ if locations == [ ] do
184
+ # check if function is overridable
185
+ # NOTE we only go over local buffer defs. There is no way to tell if a remote def has been overridden.
186
+ metadata . mods_funs_to_positions
187
+ |> Enum . filter ( fn
188
+ { { ^ mod , ^ fun , a } , % ModFunInfo { overridable: { true , _module_with_overridables } } }
189
+ when Introspection . matches_arity? ( a , arity ) ->
190
+ true
191
+
192
+ { _ , _ } ->
193
+ false
194
+ end )
195
+ |> Enum . map ( fn { _ , % ModFunInfo { overridable: { true , module_with_overridables } } } ->
196
+ # assume overridables are defined by __using__ macro
197
+ get_function_location ( module_with_overridables , :__using__ , :any , metadata )
198
+ end )
199
+ else
200
+ locations
201
+ end
202
+
203
+ case locations do
204
+ [ ] -> nil
205
+ [ single ] -> single
206
+ multiple -> multiple
71
207
end
72
208
end
73
209
@@ -76,7 +212,8 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
76
212
# to trying to locate the source code.
77
213
defp get_callback_location ( behaviour , fun , arity , metadata ) do
78
214
case Enum . find ( metadata . specs , fn
79
- { { ^ behaviour , ^ fun , a } , _spec_info } ->
215
+ { { ^ behaviour , ^ fun , a } , % SpecInfo { kind: kind } }
216
+ when kind in [ :callback , :macrocallback ] ->
80
217
Introspection . matches_arity? ( a , arity )
81
218
82
219
_ ->
@@ -91,7 +228,34 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
91
228
92
229
% Location {
93
230
file: nil ,
94
- type: :callback ,
231
+ type: spec_info . kind ,
232
+ line: line ,
233
+ column: column ,
234
+ end_line: end_line ,
235
+ end_column: end_column
236
+ }
237
+ end
238
+ end
239
+
240
+ defp get_function_location ( mod , fun , arity , metadata ) do
241
+ fn_definition =
242
+ Location . get_function_position_using_metadata (
243
+ mod ,
244
+ fun ,
245
+ arity ,
246
+ metadata . mods_funs_to_positions
247
+ )
248
+
249
+ case fn_definition do
250
+ nil ->
251
+ Location . find_mod_fun_source ( mod , fun , arity )
252
+
253
+ % ModFunInfo { } = info ->
254
+ { { line , column } , { end_line , end_column } } = Location . info_to_range ( info )
255
+
256
+ % Location {
257
+ file: nil ,
258
+ type: ModFunInfo . get_category ( info ) ,
95
259
line: line ,
96
260
column: column ,
97
261
end_line: end_line ,
0 commit comments