Skip to content

Commit cabaf34

Browse files
lukaszsamsonmsaraiva
authored andcommitted
add support for __MODULE__ special form in alias, require and import expressions (#56)
1 parent f14848d commit cabaf34

File tree

2 files changed

+203
-36
lines changed

2 files changed

+203
-36
lines changed

lib/elixir_sense/core/metadata_builder.ex

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,11 @@ defmodule ElixirSense.Core.MetadataBuilder do
268268
end
269269

270270
defp pre(
271-
{:@, [line: line, column: _column], [{:behaviour, _, [{:__aliases__, _, module_atoms}]}]} =
272-
ast,
271+
{:@, [line: line, column: _column],
272+
[{:behaviour, _, [{:__aliases__, _, module_expression}]}]} = ast,
273273
state
274274
) do
275-
module = module_atoms |> Module.concat()
275+
module = concat_module_expression(state, module_expression)
276276
pre_behaviour(ast, state, line, module)
277277
end
278278

@@ -288,10 +288,10 @@ defmodule ElixirSense.Core.MetadataBuilder do
288288
# import with v1.2 notation
289289
defp pre(
290290
{:import, [line: line, column: _column],
291-
[{{:., _, [{:__aliases__, _, prefix_atoms}, :{}]}, _, imports}]} = ast,
291+
[{{:., _, [prefix_expression, :{}]}, _, imports}]} = ast,
292292
state
293293
) do
294-
imports_modules = modules_from_12_syntax(imports, prefix_atoms)
294+
imports_modules = modules_from_12_syntax(state, imports, prefix_expression)
295295

296296
pre_import(ast, state, line, imports_modules)
297297
end
@@ -303,12 +303,11 @@ defmodule ElixirSense.Core.MetadataBuilder do
303303

304304
# import with options
305305
defp pre(
306-
{:import, [line: line, column: _column], [{_, _, module_atoms = [mod | _]}, _opts]} =
306+
{:import, [line: line, column: _column], [{_, _, module_expression = [_ | _]}, _opts]} =
307307
ast,
308308
state
309-
)
310-
when is_atom(mod) do
311-
module = module_atoms |> Module.concat()
309+
) do
310+
module = concat_module_expression(state, module_expression)
312311
pre_import(ast, state, line, module)
313312
end
314313

@@ -321,10 +320,10 @@ defmodule ElixirSense.Core.MetadataBuilder do
321320
# require with v1.2 notation
322321
defp pre(
323322
{:require, [line: line, column: _column],
324-
[{{:., _, [{:__aliases__, _, prefix_atoms}, :{}]}, _, requires}]} = ast,
323+
[{{:., _, [prefix_expression, :{}]}, _, requires}]} = ast,
325324
state
326325
) do
327-
requires_modules = modules_from_12_syntax(requires, prefix_atoms)
326+
requires_modules = modules_from_12_syntax(state, requires, prefix_expression)
328327

329328
pre_require(ast, state, line, requires_modules)
330329
end
@@ -337,12 +336,12 @@ defmodule ElixirSense.Core.MetadataBuilder do
337336
# require with `as` option
338337
defp pre(
339338
{:require, [line: line, column: _column],
340-
[{_, _, module_atoms = [mod | _]}, [as: alias_expression]]} = ast,
339+
[{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast,
341340
state
342-
)
343-
when is_atom(mod) do
344-
alias_tuple = alias_tuple(Module.concat(module_atoms), alias_expression)
345-
module = module_atoms |> Module.concat()
341+
) do
342+
module = concat_module_expression(state, module_expression)
343+
alias_tuple = alias_tuple(module, alias_expression)
344+
346345
{_, new_state} = pre_alias(ast, state, line, alias_tuple)
347346
pre_require(ast, new_state, line, module)
348347
end
@@ -357,12 +356,11 @@ defmodule ElixirSense.Core.MetadataBuilder do
357356

358357
# require with options
359358
defp pre(
360-
{:require, [line: line, column: _column], [{_, _, module_atoms = [mod | _]}, _opts]} =
359+
{:require, [line: line, column: _column], [{_, _, module_expression = [_ | _]}, _opts]} =
361360
ast,
362361
state
363-
)
364-
when is_atom(mod) do
365-
module = module_atoms |> Module.concat()
362+
) do
363+
module = concat_module_expression(state, module_expression)
366364
pre_require(ast, state, line, module)
367365
end
368366

@@ -374,9 +372,11 @@ defmodule ElixirSense.Core.MetadataBuilder do
374372
# alias with v1.2 notation
375373
defp pre(
376374
{:alias, [line: line, column: _column],
377-
[{{:., _, [{:__aliases__, _, prefix_atoms}, :{}]}, _, aliases}]} = ast,
375+
[{{:., _, [prefix_expression, :{}]}, _, aliases}]} = ast,
378376
state
379377
) do
378+
prefix_atoms = split_module_expression(state, prefix_expression)
379+
380380
aliases_tuples =
381381
aliases
382382
|> Enum.map(fn
@@ -389,23 +389,23 @@ defmodule ElixirSense.Core.MetadataBuilder do
389389

390390
# alias without options
391391
defp pre(
392-
{:alias, [line: line, column: _column], [{:__aliases__, _, module_atoms = [mod | _]}]} =
392+
{:alias, [line: line, column: _column], [{:__aliases__, _, module_expression = [_ | _]}]} =
393393
ast,
394394
state
395-
)
396-
when is_atom(mod) do
397-
alias_tuple = {Module.concat([List.last(module_atoms)]), Module.concat(module_atoms)}
395+
) do
396+
module = concat_module_expression(state, module_expression)
397+
alias_tuple = {Module.concat([List.last(module_expression)]), module}
398398
pre_alias(ast, state, line, alias_tuple)
399399
end
400400

401401
# alias with `as` option
402402
defp pre(
403403
{:alias, [line: line, column: _column],
404-
[{_, _, module_atoms = [mod | _]}, [as: alias_expression]]} = ast,
404+
[{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast,
405405
state
406-
)
407-
when is_atom(mod) do
408-
alias_tuple = alias_tuple(Module.concat(module_atoms), alias_expression)
406+
) do
407+
module = concat_module_expression(state, module_expression)
408+
alias_tuple = alias_tuple(module, alias_expression)
409409
pre_alias(ast, state, line, alias_tuple)
410410
end
411411

@@ -734,11 +734,19 @@ defmodule ElixirSense.Core.MetadataBuilder do
734734
{Module.concat(alias_atoms), module}
735735
end
736736

737-
defp modules_from_12_syntax(expressions, prefix_atoms) do
737+
defp modules_from_12_syntax(state, expressions, prefix_expression) do
738+
prefix_atoms = split_module_expression(state, prefix_expression)
739+
738740
expressions
739-
|> Enum.map(fn
740-
{:__aliases__, _, mods} -> Module.concat(prefix_atoms ++ mods)
741-
mod when is_atom(mod) -> Module.concat(prefix_atoms ++ [mod])
742-
end)
741+
|> Enum.map(&Module.concat(prefix_atoms ++ split_module_expression(state, &1)))
743742
end
743+
744+
defp split_module_expression(_state, {:__aliases__, _, mods}), do: mods
745+
defp split_module_expression(_state, mod) when is_atom(mod), do: [mod]
746+
defp split_module_expression(state, {:__MODULE__, _, nil}), do: [state |> get_current_module]
747+
748+
defp concat_module_expression(state, [{:__MODULE__, _, nil} | rest]),
749+
do: Module.concat([state |> get_current_module] ++ rest)
750+
751+
defp concat_module_expression(_state, rest), do: Module.concat(rest)
744752
end

test/elixir_sense/core/metadata_builder_test.exs

Lines changed: 161 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,34 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
700700
assert get_line_aliases(state, 3) == [{User, Foo.User}, {Email, Foo.Email}]
701701
end
702702

703+
test "aliases defined with v1.2 notation __MODULE__" do
704+
state =
705+
"""
706+
defmodule MyModule do
707+
alias __MODULE__.{User, Email}
708+
IO.puts ""
709+
end
710+
"""
711+
|> string_to_state
712+
713+
assert get_line_aliases(state, 3) == [{User, MyModule.User}, {Email, MyModule.Email}]
714+
end
715+
716+
test "aliases with __MODULE__" do
717+
state =
718+
"""
719+
defmodule MyModule do
720+
alias __MODULE__.Sub
721+
alias __MODULE__
722+
alias __MODULE__.A.B, as: C
723+
IO.puts ""
724+
end
725+
"""
726+
|> string_to_state
727+
728+
assert get_line_aliases(state, 5) == [{Sub, MyModule.Sub}, {C, MyModule.A.B}]
729+
end
730+
703731
test "aliases of aliases" do
704732
state =
705733
"""
@@ -762,6 +790,25 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
762790
"""
763791
defmodule A do
764792
alias Components.{Dialog, Dialog.Footer, Button, :"Elixir.Other"}
793+
alias Some.{}
794+
IO.puts ""
795+
end
796+
"""
797+
|> string_to_state
798+
799+
assert get_line_aliases(state, 4) == [
800+
{Dialog, Components.Dialog},
801+
{Footer, Components.Dialog.Footer},
802+
{Button, Components.Button},
803+
{Other, Components.Other}
804+
]
805+
end
806+
807+
test "aliases defined with v1.2 notation with atom module" do
808+
state =
809+
"""
810+
defmodule A do
811+
alias :"Elixir.Components".{Dialog, Dialog.Footer, Button, :"Elixir.Other"}
765812
IO.puts ""
766813
end
767814
"""
@@ -821,12 +868,27 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
821868
"""
822869
defmodule MyModule do
823870
import Foo.Bar.{User, Email, :"Elixir.Other"}
871+
import Bar.{}
872+
IO.puts ""
873+
end
874+
"""
875+
|> string_to_state
876+
877+
assert get_line_imports(state, 4) == [Foo.Bar.Other, Foo.Bar.Email, Foo.Bar.User]
878+
end
879+
880+
test "imports defined with v1.2 notation with atom module" do
881+
state =
882+
"""
883+
defmodule MyModule do
884+
import :"Elixir.Foo.Bar".{User, Email, :"Elixir.Other"}
885+
import Bar.{}
824886
IO.puts ""
825887
end
826888
"""
827889
|> string_to_state
828890

829-
assert get_line_imports(state, 3) == [Foo.Bar.Other, Foo.Bar.Email, Foo.Bar.User]
891+
assert get_line_imports(state, 4) == [Foo.Bar.Other, Foo.Bar.Email, Foo.Bar.User]
830892
end
831893

832894
test "imports" do
@@ -880,6 +942,19 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
880942
assert get_line_imports(state, 4) == [List]
881943
end
882944

945+
test "imports with __MODULE__" do
946+
state =
947+
"""
948+
defmodule OuterModule do
949+
import __MODULE__.Sub
950+
IO.puts ""
951+
end
952+
"""
953+
|> string_to_state
954+
955+
assert get_line_imports(state, 3) == [OuterModule.Sub]
956+
end
957+
883958
test "imports aliased module" do
884959
state =
885960
"""
@@ -955,17 +1030,45 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
9551030
assert get_line_requires(state, 3) == [Mod]
9561031
end
9571032

1033+
test "requires with __MODULE__" do
1034+
state =
1035+
"""
1036+
defmodule MyModule do
1037+
require __MODULE__.Sub
1038+
IO.puts ""
1039+
end
1040+
"""
1041+
|> string_to_state
1042+
1043+
assert get_line_requires(state, 3) == [MyModule.Sub]
1044+
end
1045+
9581046
test "requires with 1.2 notation" do
9591047
state =
9601048
"""
9611049
defmodule MyModule do
9621050
require Mod.{Mo1, Mod2, :"Elixir.Mod3"}
1051+
require Foo.{}
1052+
IO.puts ""
1053+
end
1054+
"""
1055+
|> string_to_state
1056+
1057+
assert get_line_requires(state, 4) == [Mod.Mod3, Mod.Mod2, Mod.Mo1]
1058+
end
1059+
1060+
test "requires with 1.2 notation with atom module" do
1061+
state =
1062+
"""
1063+
defmodule MyModule do
1064+
require :"Elixir.Mod".{Mo1, Mod2, :"Elixir.Mod3"}
1065+
require Foo.{}
9631066
IO.puts ""
9641067
end
9651068
"""
9661069
|> string_to_state
9671070

968-
assert get_line_requires(state, 3) == [Mod.Mod3, Mod.Mod2, Mod.Mo1]
1071+
assert get_line_requires(state, 4) == [Mod.Mod3, Mod.Mod2, Mod.Mo1]
9691072
end
9701073

9711074
test "requires duplicated" do
@@ -1502,6 +1605,19 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
15021605
assert get_line_behaviours(state, 3) == [:gen_server]
15031606
end
15041607

1608+
test "behaviour from __MODULE__" do
1609+
state =
1610+
"""
1611+
defmodule OuterModule do
1612+
@behaviour __MODULE__.Sub
1613+
IO.puts ""
1614+
end
1615+
"""
1616+
|> string_to_state
1617+
1618+
assert get_line_behaviours(state, 3) == [OuterModule.Sub]
1619+
end
1620+
15051621
test "behaviour from atom module" do
15061622
state =
15071623
"""
@@ -1836,6 +1952,49 @@ defmodule ElixirSense.Core.MetadataBuilderTest do
18361952
} == state.mods_funs
18371953
end
18381954

1955+
test "use v1.2 notation" do
1956+
state =
1957+
"""
1958+
defmodule InheritMod do
1959+
use ElixirSenseExample.{ExampleBehaviour}
1960+
use Foo.{}
1961+
1962+
IO.puts("")
1963+
end
1964+
"""
1965+
|> string_to_state
1966+
1967+
assert get_line_behaviours(state, 5) == [ElixirSenseExample.ExampleBehaviour]
1968+
end
1969+
1970+
test "use v1.2 notation with atom module" do
1971+
state =
1972+
"""
1973+
defmodule InheritMod do
1974+
use :"Elixir.ElixirSenseExample".{:"Elixir.ExampleBehaviour"}
1975+
1976+
IO.puts("")
1977+
end
1978+
"""
1979+
|> string_to_state
1980+
1981+
assert get_line_behaviours(state, 4) == [ElixirSenseExample.ExampleBehaviour]
1982+
end
1983+
1984+
test "use with __MODULE__" do
1985+
state =
1986+
"""
1987+
defmodule ElixirSenseExample do
1988+
use __MODULE__.ExampleBehaviour
1989+
1990+
IO.puts("")
1991+
end
1992+
"""
1993+
|> string_to_state
1994+
1995+
assert get_line_behaviours(state, 4) == [ElixirSenseExample.ExampleBehaviour]
1996+
end
1997+
18391998
test "use aliased" do
18401999
state =
18412000
"""

0 commit comments

Comments
 (0)