Skip to content

Commit fd725b7

Browse files
committed
Multialias in hover, definition and references
Fixes #1075
1 parent a388ab0 commit fd725b7

File tree

6 files changed

+290
-0
lines changed

6 files changed

+290
-0
lines changed

apps/language_server/lib/language_server/providers/definition/locator.ex

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,49 @@ defmodule ElixirLS.LanguageServer.Providers.Definition.Locator do
118118
}
119119
end
120120

121+
{{:atom, alias}, nil} ->
122+
# Handle multialias syntax
123+
text_before =
124+
Source.text_before(metadata.source, context.end |> elem(0), context.end |> elem(1))
125+
126+
case Code.Fragment.container_cursor_to_quoted(text_before) do
127+
{:ok, quoted} ->
128+
case Macro.path(quoted, fn
129+
{:., _, [{:__aliases__, _, _}, :{}]} -> true
130+
_ -> false
131+
end) do
132+
[{:., _, [{:__aliases__, _, outer_alias}, :{}]} | _] ->
133+
# Combine outer alias with the one under cursor
134+
expanded = Module.concat(outer_alias ++ [alias])
135+
136+
find_function_or_module(
137+
{{:atom, expanded}, nil},
138+
context,
139+
env,
140+
metadata,
141+
binding_env
142+
)
143+
144+
_ ->
145+
find_function_or_module(
146+
{{:atom, alias}, nil},
147+
context,
148+
env,
149+
metadata,
150+
binding_env
151+
)
152+
end
153+
154+
_ ->
155+
find_function_or_module(
156+
{{:atom, alias}, nil},
157+
context,
158+
env,
159+
metadata,
160+
binding_env
161+
)
162+
end
163+
121164
{module, function} ->
122165
find_function_or_module(
123166
{module, function},

apps/language_server/lib/language_server/providers/hover/docs.ex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.Docs do
1919
alias ElixirSense.Core.State.{ModFunInfo, SpecInfo}
2020
alias ElixirSense.Core.TypeInfo
2121
alias ElixirSense.Core.Parser
22+
alias ElixirSense.Core.Source
2223

2324
@type markdown :: String.t()
2425

@@ -150,6 +151,30 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.Docs do
150151
)
151152
end
152153

154+
{{:atom, alias}, nil} ->
155+
# Handle multialias syntax
156+
text_before =
157+
Source.text_before(metadata.source, context.end |> elem(0), context.end |> elem(1))
158+
159+
case Code.Fragment.container_cursor_to_quoted(text_before) do
160+
{:ok, quoted} ->
161+
case Macro.path(quoted, fn
162+
{:., _, [{:__aliases__, _, _}, :{}]} -> true
163+
_ -> false
164+
end) do
165+
[{:., _, [{:__aliases__, _, outer_alias}, :{}]} | _] ->
166+
# Combine outer alias with the one under cursor
167+
expanded = Module.concat(outer_alias ++ [alias])
168+
mod_fun_docs({{:atom, expanded}, nil}, context, binding_env, env, metadata)
169+
170+
_ ->
171+
mod_fun_docs({{:atom, alias}, nil}, context, binding_env, env, metadata)
172+
end
173+
174+
_ ->
175+
mod_fun_docs({{:atom, alias}, nil}, context, binding_env, env, metadata)
176+
end
177+
153178
_ ->
154179
mod_fun_docs(
155180
type,

apps/language_server/lib/language_server/providers/references/locator.ex

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule ElixirLS.LanguageServer.Providers.References.Locator do
1818
alias ElixirSense.Core.State.VarInfo
1919
alias ElixirSense.Core.SurroundContext
2020
alias ElixirSense.Core.Parser
21+
alias ElixirSense.Core.Source
2122

2223
def references(code, line, column, trace, options \\ []) do
2324
case NormalizedCode.Fragment.surround_context(code, {line, column}) do
@@ -210,6 +211,30 @@ defmodule ElixirLS.LanguageServer.Providers.References.Locator do
210211
[]
211212
end
212213

214+
{{:atom, alias}, nil} ->
215+
# Handle multialias syntax
216+
text_before =
217+
Source.text_before(metadata.source, context.end |> elem(0), context.end |> elem(1))
218+
219+
case Code.Fragment.container_cursor_to_quoted(text_before) do
220+
{:ok, quoted} ->
221+
case Macro.path(quoted, fn
222+
{:., _, [{:__aliases__, _, _}, :{}]} -> true
223+
_ -> false
224+
end) do
225+
[{:., _, [{:__aliases__, _, outer_alias}, :{}]} | _] ->
226+
# Combine outer alias with the one under cursor
227+
expanded = Module.concat(outer_alias ++ [alias])
228+
refs_for_mod_fun.({{:atom, expanded}, nil})
229+
230+
_ ->
231+
refs_for_mod_fun.({{:atom, alias}, nil})
232+
end
233+
234+
_ ->
235+
refs_for_mod_fun.({{:atom, alias}, nil})
236+
end
237+
213238
{mod, function} ->
214239
refs_for_mod_fun.({mod, function})
215240
end

apps/language_server/test/providers/definition/locator_test.exs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,42 @@ defmodule ElixirLS.LanguageServer.Providers.Definition.LocatorTest do
308308
assert read_range(location) =~ "@moduledoc \"example module\""
309309
end
310310

311+
test "find definition on module in multialias" do
312+
buffer = """
313+
defmodule Foo.Bar do
314+
end
315+
316+
defmodule Foo.Baz.Boom do
317+
end
318+
319+
defmodule MyModule do
320+
alias Foo.{Bar, Baz.Boom}
321+
alias Foo, as: X
322+
require X.{Bar, Baz.Boom}
323+
alias Foo, as: Y
324+
import Elixir.Foo.{Bar, Baz.Boom}
325+
end
326+
"""
327+
328+
assert %Location{type: :module, file: nil, line: 1, column: 1} =
329+
Locator.definition(buffer, 8, 15)
330+
331+
assert %Location{type: :module, file: nil, line: 4, column: 1} =
332+
Locator.definition(buffer, 8, 20)
333+
334+
assert %Location{type: :module, file: nil, line: 1, column: 1} =
335+
Locator.definition(buffer, 10, 15)
336+
337+
assert %Location{type: :module, file: nil, line: 4, column: 1} =
338+
Locator.definition(buffer, 10, 20)
339+
340+
assert %Location{type: :module, file: nil, line: 1, column: 1} =
341+
Locator.definition(buffer, 12, 23)
342+
343+
assert %Location{type: :module, file: nil, line: 4, column: 1} =
344+
Locator.definition(buffer, 12, 28)
345+
end
346+
311347
test "find definition for the correct arity of function - on fn call" do
312348
buffer = """
313349
defmodule MyModule do

apps/language_server/test/providers/hover/docs_test.exs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,92 @@ defmodule ElixirLS.LanguageServer.Providers.Hover.DocsTest do
214214

215215
refute Docs.docs(buffer, 2, 11)
216216
end
217+
218+
test "retrieve documentation from modules in multialias" do
219+
buffer = """
220+
defmodule Foo.Bar do
221+
@moduledoc "Bar module doc"
222+
end
223+
224+
defmodule Foo.Baz.Boom do
225+
@moduledoc "Boom module doc"
226+
end
227+
228+
defmodule MyModule do
229+
alias Foo.{Bar, Baz.Boom}
230+
alias Foo, as: X
231+
require X.{Bar, Baz.Boom}
232+
alias Foo, as: Y
233+
import Elixir.Foo.{Bar, Baz.Boom}
234+
end
235+
"""
236+
237+
# Test hover on Bar in multialias
238+
%{
239+
docs: [bar_doc]
240+
} = Docs.docs(buffer, 10, 15)
241+
242+
assert %{
243+
module: Foo.Bar,
244+
docs: "Bar module doc",
245+
kind: :module
246+
} = bar_doc
247+
248+
# Test hover on Boom in multialias
249+
%{
250+
docs: [boom_doc]
251+
} = Docs.docs(buffer, 10, 20)
252+
253+
assert %{
254+
module: Foo.Baz.Boom,
255+
docs: "Boom module doc",
256+
kind: :module
257+
} = boom_doc
258+
259+
# Test hover on Bar in require
260+
%{
261+
docs: [require_bar_doc]
262+
} = Docs.docs(buffer, 12, 15)
263+
264+
assert %{
265+
module: Foo.Bar,
266+
docs: "Bar module doc",
267+
kind: :module
268+
} = require_bar_doc
269+
270+
# Test hover on Boom in require
271+
%{
272+
docs: [require_boom_doc]
273+
} = Docs.docs(buffer, 12, 20)
274+
275+
assert %{
276+
module: Foo.Baz.Boom,
277+
docs: "Boom module doc",
278+
kind: :module
279+
} = require_boom_doc
280+
281+
# Test hover on Bar in import
282+
%{
283+
docs: [import_bar_doc]
284+
} = Docs.docs(buffer, 14, 23)
285+
286+
assert %{
287+
module: Foo.Bar,
288+
docs: "Bar module doc",
289+
kind: :module
290+
} = import_bar_doc
291+
292+
# Test hover on Boom in import
293+
%{
294+
docs: [import_boom_doc]
295+
} = Docs.docs(buffer, 14, 28)
296+
297+
assert %{
298+
module: Foo.Baz.Boom,
299+
docs: "Boom module doc",
300+
kind: :module
301+
} = import_boom_doc
302+
end
217303
end
218304

219305
describe "functions and macros" do

apps/language_server/test/providers/references/locator_test.exs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,6 +2200,81 @@ defmodule ElixirLS.LanguageServer.Providers.References.LocatorTest do
22002200
}
22012201
] = references
22022202
end
2203+
2204+
test "find references in multialias", %{trace: trace} do
2205+
buffer = """
2206+
defmodule Foo.Bar do
2207+
def hello(), do: :ok
2208+
end
2209+
2210+
defmodule Foo.Baz.Boom do
2211+
def world(), do: :ok
2212+
end
2213+
2214+
defmodule MyModule do
2215+
alias Foo.{Bar, Baz.Boom}
2216+
2217+
def process() do
2218+
Bar.hello()
2219+
Boom.world()
2220+
end
2221+
end
2222+
"""
2223+
2224+
# Test references for Bar in multialias
2225+
references = Locator.references(buffer, 10, 15, trace)
2226+
2227+
assert [
2228+
%{
2229+
range: %{
2230+
start: %{line: 10, column: 3},
2231+
end: %{line: 10, column: 3}
2232+
},
2233+
uri: nil
2234+
},
2235+
%{
2236+
range: %{
2237+
start: %{line: 13, column: 5},
2238+
end: %{line: 13, column: 5}
2239+
},
2240+
uri: nil
2241+
},
2242+
%{
2243+
range: %{
2244+
start: %{line: 13, column: 9},
2245+
end: %{line: 13, column: 14}
2246+
},
2247+
uri: nil
2248+
}
2249+
] = references
2250+
2251+
# Test references for Boom in multialias
2252+
references = Locator.references(buffer, 10, 20, trace)
2253+
2254+
assert [
2255+
%{
2256+
range: %{
2257+
start: %{line: 10, column: 3},
2258+
end: %{line: 10, column: 3}
2259+
},
2260+
uri: nil
2261+
},
2262+
%{
2263+
range: %{
2264+
start: %{line: 14, column: 5},
2265+
end: %{line: 14, column: 5}
2266+
},
2267+
uri: nil
2268+
},
2269+
%{
2270+
range: %{
2271+
start: %{line: 14, column: 10},
2272+
end: %{line: 14, column: 15}
2273+
},
2274+
uri: nil
2275+
}
2276+
] = references
2277+
end
22032278
end
22042279

22052280
test "local vs remote references" do

0 commit comments

Comments
 (0)