@@ -7,11 +7,14 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
77 This is effectively the reverse of the "go to implementations" provider.
88 """
99
10+ alias ElixirSense.Core.Binding
11+ alias ElixirSense.Core.SurroundContext
1012 alias ElixirSense.Core.Metadata
1113 alias ElixirSense.Core.Normalized.Code , as: NormalizedCode
1214 alias ElixirSense.Core.State
1315 alias ElixirLS.LanguageServer.Location
1416 alias ElixirSense.Core.Parser
17+ alias ElixirSense.Core.State . { ModFunInfo , SpecInfo }
1518
1619 require ElixirSense.Core.Introspection , as: Introspection
1720
@@ -41,33 +44,166 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
4144 end
4245
4346 @ 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 )
4749
48- case env . function do
50+ type = SurroundContext . to_binding ( context . context , module )
51+
52+ case type do
4953 nil ->
5054 nil
5155
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+ )
7071 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
71207 end
72208 end
73209
@@ -76,7 +212,8 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
76212 # to trying to locate the source code.
77213 defp get_callback_location ( behaviour , fun , arity , metadata ) do
78214 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 ] ->
80217 Introspection . matches_arity? ( a , arity )
81218
82219 _ ->
@@ -91,7 +228,34 @@ defmodule ElixirLS.LanguageServer.Providers.Declaration.Locator do
91228
92229 % Location {
93230 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 ) ,
95259 line: line ,
96260 column: column ,
97261 end_line: end_line ,
0 commit comments