From f4e80898ce069d529d9bc6fbb9b3ee4c0b8d5961 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Wed, 6 Mar 2024 20:22:44 +0100 Subject: [PATCH] deprecate providers --- lib/elixir_sense.ex | 9 + test/elixir_sense/definition_test.exs | 1845 ------- test/elixir_sense/docs_test.exs | 2059 -------- test/elixir_sense/eval_test.exs | 185 - test/elixir_sense/implementation_test.exs | 728 --- test/elixir_sense/location_test.exs | 46 - .../providers/suggestion/complete_test.exs | 2248 -------- .../providers/suggestion/matcher_test.exs | 4 - .../providers/suggestion_test.exs | 328 -- test/elixir_sense/references_test.exs | 1815 ------- test/elixir_sense/signature_test.exs | 1591 ------ test/elixir_sense/suggestions_test.exs | 4703 ----------------- test/elixir_sense_test.exs | 5 - 13 files changed, 9 insertions(+), 15557 deletions(-) delete mode 100644 test/elixir_sense/definition_test.exs delete mode 100644 test/elixir_sense/docs_test.exs delete mode 100644 test/elixir_sense/eval_test.exs delete mode 100644 test/elixir_sense/implementation_test.exs delete mode 100644 test/elixir_sense/location_test.exs delete mode 100644 test/elixir_sense/providers/suggestion/complete_test.exs delete mode 100644 test/elixir_sense/providers/suggestion/matcher_test.exs delete mode 100644 test/elixir_sense/providers/suggestion_test.exs delete mode 100644 test/elixir_sense/references_test.exs delete mode 100644 test/elixir_sense/signature_test.exs delete mode 100644 test/elixir_sense/suggestions_test.exs delete mode 100644 test/elixir_sense_test.exs diff --git a/lib/elixir_sense.ex b/lib/elixir_sense.ex index bcff3218..3c7a7821 100644 --- a/lib/elixir_sense.ex +++ b/lib/elixir_sense.ex @@ -49,6 +49,7 @@ defmodule ElixirSense do iex> doc.docs |> String.split("\n") |> Enum.at(0) "Converts `enumerable` to a list." """ + @deprecated "providers will be dropped in the future" @spec docs(String.t(), pos_integer, pos_integer, keyword) :: %{ docs: nonempty_list(Docs.doc()), @@ -102,6 +103,7 @@ defmodule ElixirSense do iex> "#{Path.basename(path)}:#{to_string(line)}:#{to_string(column)}" "module_with_functions.ex:6:3" """ + @deprecated "providers will be dropped in the future" @spec definition(String.t(), pos_integer, pos_integer, keyword) :: Location.t() | nil def definition(code, line, column, options \\ []) do case NormalizedCode.Fragment.surround_context(code, {line, column}) do @@ -138,6 +140,7 @@ defmodule ElixirSense do iex> "#{Path.basename(path)}:#{to_string(line)}:#{to_string(column)}" "example_protocol.ex:7:3" """ + @deprecated "providers will be dropped in the future" @spec implementations(String.t(), pos_integer, pos_integer, keyword) :: [Location.t()] def implementations(code, line, column, options \\ []) do case NormalizedCode.Fragment.surround_context(code, {line, column}) do @@ -211,6 +214,7 @@ defmodule ElixirSense do name: "insert_at", metadata: %{app: :elixir}, snippet: nil, visibility: :public, spec: "@spec insert_at(list(), integer(), any()) :: list()", summary: "Returns a list with `value` inserted at the specified `index`."}] """ + @deprecated "providers will be dropped in the future" @spec suggestions(String.t(), pos_integer, pos_integer, keyword()) :: [Suggestion.suggestion()] def suggestions(code, line, column, options \\ []) do hint = Source.prefix(code, line, column) @@ -294,6 +298,7 @@ defmodule ElixirSense do ] } """ + @deprecated "providers will be dropped in the future" @spec signature(String.t(), pos_integer, pos_integer, keyword) :: Signature.signature_info() | :none def signature(code, line, column, options \\ []) do @@ -383,6 +388,7 @@ defmodule ElixirSense do ``` """ + @deprecated "providers will be dropped in the future" @spec expand_full(String.t(), String.t(), pos_integer) :: Expand.expanded_code_map() def expand_full(buffer, code, line) do buffer_file_metadata = Parser.parse_string(buffer, true, true, {line, 1}) @@ -395,6 +401,7 @@ defmodule ElixirSense do @doc """ Converts a string to its quoted form. """ + @deprecated "providers will be dropped in the future" @spec quote(String.t()) :: String.t() def quote(code) do Eval.quote(code) @@ -412,6 +419,7 @@ defmodule ElixirSense do iex> ElixirSense.match(code) "# Bindings\n\nstatus = 404\n\nmessage = \"Not found\"\n\narg1 = 1" """ + @deprecated "providers will be dropped in the future" @spec match(String.t()) :: String.t() def match(code) do Eval.match_and_format(code) @@ -448,6 +456,7 @@ defmodule ElixirSense do } ] """ + @deprecated "providers will be dropped in the future" @spec references( String.t(), pos_integer, diff --git a/test/elixir_sense/definition_test.exs b/test/elixir_sense/definition_test.exs deleted file mode 100644 index e0df9f42..00000000 --- a/test/elixir_sense/definition_test.exs +++ /dev/null @@ -1,1845 +0,0 @@ -defmodule ElixirSense.Providers.DefinitionTest do - use ExUnit.Case, async: true - alias ElixirSense.Providers.Definition - alias ElixirSense.Location - alias ElixirSense.Core.Source - - doctest Definition - - test "don't crash on empty buffer" do - refute ElixirSense.definition("", 1, 1) - end - - test "don't error on __MODULE__ when no module" do - assert nil == ElixirSense.definition("__MODULE__", 1, 1) - end - - @tag requires_elixir_1_14: true - test "find module definition inside Phoenix's scope" do - _define_existing_atom = ExampleWeb - - buffer = """ - defmodule ExampleWeb.Router do - import Phoenix.Router - - scope "/", ExampleWeb do - get "/", PageController, :home - end - end - """ - - %Location{type: :module, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 5, 15) - - assert file =~ "elixir_sense/test/support/plugins/phoenix/page_controller.ex" - assert read_line(file, {line, column}) =~ "ExampleWeb.PageController" - end - - test "find definition of aliased modules in `use`" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.UseExample - use UseExample - # ^ - end - """ - - %Location{type: :module, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 12) - - assert file =~ "elixir_sense/test/support/use_example.ex" - assert read_line(file, {line, column}) =~ "ElixirSenseExample.UseExample" - end - - @tag requires_source: true - test "find definition of functions from Kernel" do - buffer = """ - defmodule MyModule do - #^ - end - """ - - %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 1, 2) - - assert file =~ "lib/elixir/lib/kernel.ex" - assert read_line(file, {line, column}) =~ "defmodule(" - end - - @tag requires_source: true - test "find definition of functions from Kernel.SpecialForms" do - buffer = """ - defmodule MyModule do - import List - ^ - end - """ - - %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 2, 4) - - assert file =~ "lib/elixir/lib/kernel/special_forms.ex" - assert read_line(file, {line, column}) =~ "import" - end - - test "find definition of functions from imports" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ModuleWithFunctions - function_arity_zero() - #^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 4) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "function_arity_zero" - end - - test "find definition of functions from aliased modules" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.function_arity_one(42) - # ^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "function_arity_one" - end - - test "find definition of macros from required modules" do - buffer = """ - defmodule MyModule do - require ElixirSenseExample.BehaviourWithMacrocallback.Impl, as: Macros - Macros.some(1) - # ^ - end - """ - - %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 13) - - assert file =~ "elixir_sense/test/support/behaviour_with_macrocallbacks.ex" - assert read_line(file, {line, column}) =~ "some" - end - - test "find definition of functions piped from aliased modules" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - 42 |> MyMod.function_arity_one() - # ^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 17) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "function_arity_one" - end - - test "find definition of functions captured from aliased modules" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - &MyMod.function_arity_one/1 - # ^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 17) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "function_arity_one" - end - - test "find function definition macro generated" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.MacroGenerated, as: Local - Local.my_fun() - # ^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 12) - - assert file =~ "elixir_sense/test/support/macro_generated.ex" - assert read_line(file, {line, column}) =~ "ElixirSenseExample.Macros.go" - end - - test "find metadata module" do - buffer = """ - defmodule Some do - def my_func, do: "not this one" - end - - defmodule MyModule do - def main, do: Some.my_func() - # ^ - end - """ - - assert %Location{type: :module, file: nil, line: 1, column: 1} = - ElixirSense.definition(buffer, 6, 19) - end - - @tag capture_log: true - test "find remote module" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: MyMod - def main, do: MyMod.my_func() - # ^ - end - """ - - assert %Location{type: :module, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 19) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - - assert read_line(file, {line, column}) =~ - "defmodule ElixirSenseExample.FunctionsWithDefaultArgs do" - end - - @tag capture_log: true - test "find remote module - fallback to docs" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: MyMod - def main, do: MyMod.my_func() - # ^ - end - """ - - assert %Location{type: :module, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 19) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "@moduledoc \"example module\"" - end - - test "find definition for the correct arity of function - on fn call" do - buffer = """ - defmodule MyModule do - def main, do: my_func("a", "b") - # ^ - def my_func, do: "not this one" - def my_func(a, b), do: a <> b - end - """ - - assert %Location{type: :function, file: nil, line: 5, column: 3} = - ElixirSense.definition(buffer, 2, 18) - end - - test "find definition for the correct arity of function - on fn call with default arg" do - buffer = """ - defmodule MyModule do - def main, do: my_func("a") - # ^ - def my_func, do: "not this one" - def my_func(a, b \\\\ ""), do: a <> b - end - """ - - assert %Location{type: :function, file: nil, line: 5, column: 3} = - ElixirSense.definition(buffer, 2, 18) - end - - test "find metadata function head for the correct arity of function - on fn call with default arg" do - buffer = """ - defmodule MyModule do - def main, do: {my_func(), my_func("a"), my_func(1, 2, 3)} - # ^ - def my_func, do: "not this one" - def my_func(a, b \\\\ "") - def my_func(1, b), do: "1" <> b - def my_func(2, b), do: "2" <> b - def my_func(1, 2, 3), do: :ok - end - """ - - assert %Location{type: :function, file: nil, line: 5, column: 3} = - ElixirSense.definition(buffer, 2, 30) - end - - @tag capture_log: true - test "find remote function head for the correct arity of function - on fn call with default arg" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: {F.my_func(), F.my_func("a"), F.my_func(1, 2, 3)} - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 34) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "my_func(a, b \\\\ \"\")" - end - - @tag capture_log: true - test "find remote function head for the lowest matching arity of function in incomplete code" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func( - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "def my_func," - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1 - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "def my_func(a, b \\\\ \"\")" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1, 2, - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "def my_func(1, 2, 3)" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1, 2, 3, - end - """ - - # too many arguments - - assert nil == ElixirSense.definition(buffer, 3, 20) - end - - @tag capture_log: true - test "find remote function head for the correct arity of function - on fn call with default arg - fallback to docs" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: F - def main, do: {F.my_func(), F.my_func("a"), F.my_func(1, 2, 3)} - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 34) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "@doc \"2 params version\"" - end - - @tag capture_log: true - test "find remote function head for the lowest matching arity of function in incomplete code - fallback to docs" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: F - def main, do: F.my_func( - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "@doc \"no params version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: F - def main, do: F.my_func(1 - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "@doc \"2 params version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: F - def main, do: F.my_func(1, 2, - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "@doc \"3 params version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs1, as: F - def main, do: F.my_func(1, 2, 3, - end - """ - - # too many arguments - - assert nil == ElixirSense.definition(buffer, 3, 20) - end - - @tag capture_log: true - test "find remote function head for the correct arity of function - on function capture" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: &F.my_func/1 - end - """ - - assert %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 21) - - assert file =~ "elixir_sense/test/support/functions_with_default_args.ex" - assert read_line(file, {line, column}) =~ "my_func(a, b \\\\ \"\")" - end - - test "find definition for the correct arity of function - on fn call with pipe" do - buffer = """ - defmodule MyModule do - def main, do: "a" |> my_func("b") - # ^ - def my_func, do: "not this one" - def my_func(a, b), do: a <> b - end - """ - - assert %Location{type: :function, file: nil, line: 5, column: 3} = - ElixirSense.definition(buffer, 2, 24) - end - - test "find definition for the correct arity of function - on fn definition" do - buffer = """ - defmodule MyModule do - def my_func, do: "not this one" - def my_func(a, b \\\\ nil) - def my_func(a, b), do: a <> b - end - """ - - assert %Location{type: :function, file: nil, line: 3, column: 3} = - ElixirSense.definition(buffer, 4, 9) - end - - test "find definition for function - on var call" do - buffer = """ - defmodule A do - @callback abc() :: any() - end - - defmodule B do - @behaviour A - - def abc, do: :ok - end - - b = B - b.abc() - """ - - assert %Location{type: :function, file: nil, line: 8, column: 3} = - ElixirSense.definition(buffer, 12, 4) - end - - test "find definition of delegated functions" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function() - # ^ - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function" - end - - test "find definition of modules" do - buffer = """ - defmodule MyModule do - alias List, as: MyList - ElixirSenseExample.ModuleWithFunctions.function_arity_zero() - # ^ - end - """ - - %Location{type: :module, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 23) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "ElixirSenseExample.ModuleWithFunctions do" - end - - test "find definition of modules in multi alias syntax" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithDocs - alias ElixirSenseExample.{Some, ModuleWithDocs} - end - """ - - %Location{type: :module, file: file_1, line: line_1} = ElixirSense.definition(buffer, 2, 30) - - %Location{type: :module, file: file_2, line: line_2} = ElixirSense.definition(buffer, 3, 38) - - assert file_1 == file_2 - assert line_1 == line_2 - end - - test "find definition of erlang modules" do - buffer = """ - defmodule MyModule do - def dup(x) do - :lists.duplicate(2, x) - # ^ - end - end - """ - - %Location{type: :module, file: file, line: 20, column: 1} = - ElixirSense.definition(buffer, 3, 7) - - assert file =~ "/src/lists.erl" - end - - test "find definition of remote erlang functions" do - buffer = """ - defmodule MyModule do - def dup(x) do - :lists.duplicate(2, x) - # ^ - end - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 15) - - assert file =~ "/src/lists.erl" - assert read_line(file, {line, column}) =~ "duplicate(N, X)" - end - - test "find definition of remote erlang functions from preloaded module" do - buffer = """ - defmodule MyModule do - def dup(x) do - :erlang.start_timer(2, x, 4) - # ^ - end - end - """ - - %Location{type: :function, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 15) - - assert file =~ "/src/erlang.erl" - assert read_line(file, {line, column}) =~ "start_timer(_Time, _Dest, _Msg)" - end - - test "non existing modules" do - buffer = """ - defmodule MyModule do - SilverBulletModule.run - end - """ - - refute ElixirSense.definition(buffer, 2, 24) - end - - test "cannot find map field calls" do - buffer = """ - defmodule MyModule do - env = __ENV__ - IO.puts(env.file) - # ^ - end - """ - - refute ElixirSense.definition(buffer, 3, 16) - end - - test "cannot find map fields" do - buffer = """ - defmodule MyModule do - var = %{count: 1} - # ^ - end - """ - - refute ElixirSense.definition(buffer, 2, 12) - end - - test "preloaded modules" do - buffer = """ - defmodule MyModule do - :erlang.node - # ^ - end - """ - - assert %Location{line: 20, column: 1, type: :module, file: file} = - ElixirSense.definition(buffer, 2, 5) - - assert file =~ "/src/erlang.erl" - end - - test "find built-in functions" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - ElixirSenseExample.ModuleWithFunctions.module_info() - # ^ - ElixirSenseExample.ModuleWithFunctions.__info__(:macros) - # ^ - ElixirSenseExample.ExampleBehaviour.behaviour_info(:callbacks) - # ^ - end - """ - - assert %{column: column, file: file, line: line, type: :function} = - ElixirSense.definition(buffer, 2, 42) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "ElixirSenseExample.ModuleWithFunctions do" - - assert %Location{type: :function} = ElixirSense.definition(buffer, 4, 42) - - assert %Location{type: :function} = ElixirSense.definition(buffer, 6, 42) - end - - test "built-in functions cannot be called locally" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - import GenServer - @ callback cb() :: term - module_info() - #^ - __info__(:macros) - #^ - behaviour_info(:callbacks) - #^ - end - """ - - refute ElixirSense.definition(buffer, 4, 5) - - refute ElixirSense.definition(buffer, 6, 5) - - refute ElixirSense.definition(buffer, 8, 5) - end - - test "does not find built-in erlang functions" do - buffer = """ - defmodule MyModule do - :erlang.orelse() - # ^ - :erlang.or() - # ^ - end - """ - - refute ElixirSense.definition(buffer, 2, 14) - - refute ElixirSense.definition(buffer, 4, 12) - end - - test "find definition of variables" do - buffer = """ - defmodule MyModule do - def func do - var1 = 1 - var2 = 2 - var1 = 3 - IO.puts(var1 + var2) - end - end - """ - - assert ElixirSense.definition(buffer, 6, 13) == %Location{ - type: :variable, - file: nil, - line: 5, - column: 5 - } - - assert ElixirSense.definition(buffer, 6, 21) == %Location{ - type: :variable, - file: nil, - line: 4, - column: 5 - } - end - - test "find definition of variables defined on the next line" do - buffer = """ - defmodule MyModule do - def func do - var1 = - 1 - end - end - """ - - assert ElixirSense.definition(buffer, 3, 5) == %Location{ - type: :variable, - file: nil, - line: 3, - column: 5 - } - end - - test "find definition of functions when name not same as variable" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun1 = 1 - my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 6) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of functions when name same as variable - parens preferes function" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun = 1 - my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 6) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of variables when name same as function - no parens preferes variable" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun = 1 - my_fun - end - end - """ - - assert ElixirSense.definition(buffer, 6, 6) == %Location{ - type: :variable, - file: nil, - line: 5, - column: 5 - } - end - - test "find definition of variables when name same as function" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :error - - def a do - my_fun = fn -> :ok end - my_fun.() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 6) == %Location{ - type: :variable, - file: nil, - line: 5, - column: 5 - } - end - - test "find definition for a redefined variable" do - buffer = """ - defmodule MyModule do - def my_fun(var) do - var = 1 + var - - var - end - end - """ - - # `var` defined in the function header - assert ElixirSense.definition(buffer, 3, 15) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 14 - } - - # `var` redefined in the function body - assert ElixirSense.definition(buffer, 5, 5) == %Location{ - type: :variable, - file: nil, - line: 3, - column: 5 - } - end - - test "find definition of a variable in a guard" do - buffer = """ - defmodule MyModule do - def my_fun(var) when is_atom(var) do - case var do - var when var > 0 -> var - end - - Enum.map([1, 2], fn x when x > 0 -> x end) - end - end - """ - - assert ElixirSense.definition(buffer, 2, 32) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 14 - } - - assert ElixirSense.definition(buffer, 4, 16) == %Location{ - type: :variable, - file: nil, - line: 4, - column: 7 - } - - assert ElixirSense.definition(buffer, 7, 32) == %Location{ - type: :variable, - file: nil, - line: 7, - column: 25 - } - end - - test "find definition of variables when variable is a function parameter" do - buffer = """ - defmodule MyModule do - def my_fun([h | t]) do - sum = h + my_fun(t) - - if h > sum do - h + sum - else - h = my_fun(t) + sum - h - end - end - end - """ - - # `h` from the function header - assert ElixirSense.definition(buffer, 3, 11) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 15 - } - - assert ElixirSense.definition(buffer, 6, 7) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 15 - } - - # `h` from the if-else scope - assert ElixirSense.definition(buffer, 9, 7) == %Location{ - type: :variable, - file: nil, - line: 8, - column: 7 - } - - # `t` - assert ElixirSense.definition(buffer, 8, 18) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 19 - } - - # `sum` - assert ElixirSense.definition(buffer, 8, 23) == %Location{ - type: :variable, - file: nil, - line: 3, - column: 5 - } - end - - test "find definition of variables from the scope of an anonymous function" do - buffer = """ - defmodule MyModule do - def my_fun(x, y) do - x = Enum.map(x, fn x -> x + y end) - end - end - """ - - # `x` from the `my_fun` function header - assert ElixirSense.definition(buffer, 3, 18) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 14 - } - - # `y` from the `my_fun` function header - assert ElixirSense.definition(buffer, 3, 33) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 17 - } - - # `x` from the anonymous function - assert ElixirSense.definition(buffer, 3, 29) == %Location{ - type: :variable, - file: nil, - line: 3, - column: 24 - } - - # redefined `x` - assert ElixirSense.definition(buffer, 3, 5) == %Location{ - type: :variable, - file: nil, - line: 3, - column: 5 - } - end - - test "find definition of variables inside multiline struct" do - buffer = """ - defmodule MyModule do - def go do - %Some{ - filed: var - } = abc() - end - end - """ - - assert ElixirSense.definition(buffer, 4, 15) == %Location{ - type: :variable, - file: nil, - line: 4, - column: 14 - } - end - - test "find definition of a variable when using pin operator" do - buffer = """ - defmodule MyModule do - def my_fun(a, b) do - case a do - ^b -> b - %{b: ^b} = a -> b - end - end - end - """ - - # `b` - assert ElixirSense.definition(buffer, 4, 8) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 17 - } - - assert ElixirSense.definition(buffer, 4, 13) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 17 - } - - assert ElixirSense.definition(buffer, 5, 13) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 17 - } - - assert ElixirSense.definition(buffer, 5, 23) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 17 - } - - # `a` redefined in a case clause - assert ElixirSense.definition(buffer, 5, 18) == %Location{ - type: :variable, - file: nil, - line: 5, - column: 18 - } - end - - test "find definition of attributes" do - buffer = """ - defmodule MyModule do - def func do - @var1 1 - @var2 2 - @var1 3 - IO.puts(@var1 + @var2) - end - end - """ - - assert ElixirSense.definition(buffer, 6, 15) == %Location{ - type: :attribute, - file: nil, - line: 3, - column: 5 - } - - assert ElixirSense.definition(buffer, 6, 24) == %Location{ - type: :attribute, - file: nil, - line: 4, - column: 5 - } - end - - test "find definition of local functions with default args" do - buffer = """ - defmodule MyModule do - def my_fun(a \\\\ 0, b \\\\ nil), do: :ok - - def a do - my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 5, 6) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of local __MODULE__" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun1 = 1 - __MODULE__.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 6) == %Location{ - type: :module, - file: nil, - line: 1, - column: 1 - } - end - - test "find definition of local functions with __MODULE__" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun1 = 1 - __MODULE__.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 17) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - @tag requires_elixir_1_14: true - test "find definition of local functions with __MODULE__ submodule" do - buffer = """ - defmodule MyModule do - defmodule Sub do - def my_fun(), do: :ok - end - - def a do - my_fun1 = 1 - __MODULE__.Sub.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 8, 22) == %Location{ - type: :function, - file: nil, - line: 3, - column: 5 - } - end - - @tag requires_elixir_1_14: true - test "find definition of local __MODULE__ submodule" do - buffer = """ - defmodule MyModule do - defmodule Sub do - def my_fun(), do: :ok - end - - def a do - my_fun1 = 1 - __MODULE__.Sub.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 8, 17) == %Location{ - type: :module, - file: nil, - line: 2, - column: 3 - } - end - - @tag requires_elixir_1_14: true - test "find definition of local functions with @attr" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - @attr MyModule - def a do - my_fun1 = 1 - @attr.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 13) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of local functions with current module" do - buffer = """ - defmodule MyModule do - def my_fun(), do: :ok - - def a do - my_fun1 = 1 - MyModule.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 6, 14) == %Location{ - type: :function, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of local macro" do - buffer = """ - defmodule MyModule do - defmacrop some(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some(1) - end - end - """ - - assert ElixirSense.definition(buffer, 5, 6) == %Location{ - type: :macro, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of local macro on definition" do - buffer = """ - defmodule MyModule do - defmacrop some(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some(1) - end - end - """ - - assert ElixirSense.definition(buffer, 2, 14) == %Location{ - type: :macro, - file: nil, - line: 2, - column: 3 - } - end - - test "does not find definition of local macro if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - defmacro other do - some(1) - end - - defmacrop some(var), do: Macro.expand(var, __CALLER__) - end - """ - - assert ElixirSense.definition(buffer, 3, 6) == nil - end - - test "find definition of local function even if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - def other do - some(1) - end - - defp some(var), do: :ok - end - """ - - assert ElixirSense.definition(buffer, 3, 6) == %Location{ - type: :function, - file: nil, - line: 6, - column: 3 - } - end - - test "find definition of local functions with alias" do - buffer = """ - defmodule MyModule do - alias MyModule, as: M - def my_fun(), do: :ok - def my_fun(a), do: :ok - - def a do - my_fun1 = 1 - M.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 8, 7) == %Location{ - type: :function, - file: nil, - line: 3, - column: 3 - } - end - - test "do not find private function definition" do - buffer = """ - defmodule MyModule do - defmodule Submodule do - defp my_fun(), do: :ok - end - - def a do - MyModule.Submodule.my_fun() - end - end - """ - - refute ElixirSense.definition(buffer, 7, 25) - end - - test "find definition of local module" do - buffer = """ - defmodule MyModule do - defmodule Submodule do - def my_fun(), do: :ok - end - - def a do - MyModule.Submodule.my_fun() - end - end - """ - - assert ElixirSense.definition(buffer, 7, 16) == %Location{ - type: :module, - file: nil, - line: 2, - column: 3 - } - end - - test "find definition of params" do - buffer = """ - defmodule MyModule do - def func(%{a: [var2|_]}) do - var1 = 3 - IO.puts(var1 + var2) - # ^ - end - end - """ - - assert ElixirSense.definition(buffer, 4, 21) == %Location{ - type: :variable, - file: nil, - line: 2, - column: 18 - } - end - - test "find remote type definition" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithTypespecs.Remote - @type a :: Remote.remote_t - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 24) - - assert file =~ "elixir_sense/test/support/module_with_typespecs.ex" - assert read_line(file, {line, column}) =~ ~r/^@type remote_t/ - end - - test "find type definition without @typedoc" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithTypespecs.Remote - @type a :: Remote.remote_option_t - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 24) - - assert file =~ "elixir_sense/test/support/module_with_typespecs.ex" - assert read_line(file, {line, column}) =~ ~r/@type remote_option_t ::/ - end - - test "find opaque type definition" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithTypespecs.Local - @type a :: Local.opaque_t - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 23) - - assert file =~ "elixir_sense/test/support/module_with_typespecs.ex" - assert read_line(file, {line, column}) =~ ~r/@opaque opaque_t/ - end - - test "find type definition macro generated" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.MacroGenerated, as: Local - @type a :: Local.my_type - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 23) - - assert file =~ "elixir_sense/test/support/macro_generated.ex" - assert read_line(file, {line, column}) =~ "ElixirSenseExample.Macros.go" - end - - test "find erlang type definition" do - buffer = """ - defmodule MyModule do - @type a :: :ets.tab - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 2, 20) - - assert file =~ "/src/ets.erl" - assert read_line(file, {line, column}) =~ "-type tab()" - end - - test "find erlang type definition from preloaded module" do - buffer = """ - defmodule MyModule do - @type a :: :erlang.time_unit - # ^ - end - """ - - %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 2, 23) - - assert file =~ "/src/erlang.erl" - assert read_line(file, {line, column}) =~ "-type time_unit()" - end - - test "do not find erlang private type" do - buffer = """ - defmodule MyModule do - @type a :: :erlang.memory_type - # ^ - end - """ - - refute ElixirSense.definition(buffer, 2, 23) - end - - test "builtin types cannot be found" do - buffer = """ - defmodule MyModule do - @type my_type :: integer - # ^ - end - """ - - refute ElixirSense.definition(buffer, 2, 23) - end - - test "builtin elixir types cannot be found" do - buffer = """ - defmodule MyModule do - @type my_type :: Elixir.keyword - # ^ - end - """ - - refute ElixirSense.definition(buffer, 2, 29) - end - - test "find local metadata type definition" do - buffer = """ - defmodule MyModule do - @typep my_t :: integer - - @type remote_list_t :: [my_t] - # ^ - end - """ - - %Location{type: :typespec, file: nil, line: 2, column: 3} = - ElixirSense.definition(buffer, 4, 29) - end - - test "find local metadata type definition even if it's defined after cursor" do - buffer = """ - defmodule MyModule do - @type remote_list_t :: [my_t] - # ^ - - @typep my_t :: integer - end - """ - - %Location{type: :typespec, file: nil, line: 5, column: 3} = - ElixirSense.definition(buffer, 2, 29) - end - - test "find remote metadata type definition" do - buffer = """ - defmodule MyModule.Other do - @type my_t :: integer - @type my_t(a) :: {a, integer} - end - - defmodule MyModule do - alias MyModule.Other - - @type remote_list_t :: [Other.my_t] - # ^ - end - """ - - %Location{type: :typespec, file: nil, line: 2, column: 3} = - ElixirSense.definition(buffer, 9, 35) - end - - test "do not find remote private type definition" do - buffer = """ - defmodule MyModule.Other do - @typep my_t :: integer - @typep my_t(a) :: {a, integer} - end - - defmodule MyModule do - alias MyModule.Other - - @type remote_list_t :: [Other.my_t] - # ^ - end - """ - - refute ElixirSense.definition(buffer, 9, 35) - end - - test "find metadata type for the correct arity" do - buffer = """ - defmodule MyModule do - @type my_type :: integer - @type my_type(a) :: {integer, a} - @type my_type(a, b) :: {integer, a, b} - @type some :: {my_type, my_type(boolean), my_type(integer, integer)} - end - """ - - assert %Location{type: :typespec, file: nil, line: 3, column: 3} = - ElixirSense.definition(buffer, 5, 28) - end - - test "find metadata type for the correct arity - on type definition" do - buffer = """ - defmodule MyModule do - @type my_type :: integer - @type my_type(a) :: {integer, a} - @type my_type(a, b) :: {integer, a, b} - end - """ - - assert %Location{type: :typespec, file: nil, line: 3, column: 3} = - ElixirSense.definition(buffer, 3, 10) - end - - test "find remote type for the correct arity" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: {T.my_type, T.my_type(boolean), T.my_type(1, 2)} - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 32) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "my_type(a)" - end - - test "find remote type for lowest matching arity in incomplete code" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type( - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@type my_type :: integer" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@type my_type(a) :: {integer, a}" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer, - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@type my_type(a, b) :: {integer, a, b}" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer, integer, - end - """ - - # too many arguments - - assert nil == ElixirSense.definition(buffer, 3, 20) - end - - @tag capture_log: true - test "find remote type for the correct arity - fallback to docs" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity1, as: T - @type some :: {T.my_type, T.my_type(boolean), T.my_type(1, 2)} - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 32) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@typedoc \"one param version\"" - end - - @tag capture_log: true - test "find remote type for lowest matching arity in incomplete code - fallback to docs" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity1, as: T - @type some :: T.my_type( - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@typedoc \"no params version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity1, as: T - @type some :: T.my_type(integer - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@typedoc \"one param version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity1, as: T - @type some :: T.my_type(integer, - end - """ - - assert %Location{type: :typespec, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 3, 20) - - assert file =~ "elixir_sense/test/support/types_with_multiple_arity.ex" - assert read_line(file, {line, column}) =~ "@typedoc \"two params version\"" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity1, as: T - @type some :: T.my_type(integer, integer, - end - """ - - # too many arguments - - assert nil == ElixirSense.definition(buffer, 3, 20) - end - - test "find super inside overridable function" do - buffer = """ - defmodule MyModule do - use ElixirSenseExample.OverridableFunctions - - def test(x, y) do - super(x, y) - end - - defmacro required(v) do - super(v) - end - end - """ - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 5, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 9, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - end - - test "find super inside overridable callback" do - buffer = """ - defmodule MyModule do - use ElixirSenseExample.OverridableImplementation - - def foo do - super() - end - - defmacro bar(any) do - super(any) - end - end - """ - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 5, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 9, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - end - - test "find super inside overridable callback when module is compiled" do - buffer = """ - defmodule ElixirSenseExample.OverridableImplementation.Overrider do - use ElixirSenseExample.OverridableImplementation - - def foo do - super() - end - - defmacro bar(any) do - super(any) - end - end - """ - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 5, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - - assert %Location{type: :macro, file: file, line: line, column: column} = - ElixirSense.definition(buffer, 9, 6) - - assert file =~ "elixir_sense/test/support/overridable_function.ex" - assert read_line(file, {line, column}) =~ "__using__(_opts)" - end - - test "find local type in typespec local def elsewhere" do - buffer = """ - defmodule ElixirSenseExample.Some do - @type some_local :: integer - - def some_local(), do: :ok - - @type user :: {some_local, integer} - - def foo do - some_local - end - end - """ - - assert %Location{type: :typespec, file: nil, line: 2} = ElixirSense.definition(buffer, 6, 20) - - assert %Location{type: :function, file: nil, line: 4} = ElixirSense.definition(buffer, 9, 9) - end - - test "find variable with the same name as special form" do - buffer = """ - defmodule ElixirSenseExample.Some do - def foo do - quote = 123 - abc(quote) - end - end - """ - - assert %Location{type: :variable, file: nil, line: 3} = ElixirSense.definition(buffer, 4, 10) - end - - defp read_line(file, {line, column}) do - file - |> File.read!() - |> Source.split_lines() - |> Enum.at(line - 1) - |> String.slice((column - 1)..-1//1) - end -end diff --git a/test/elixir_sense/docs_test.exs b/test/elixir_sense/docs_test.exs deleted file mode 100644 index 8b5b90e6..00000000 --- a/test/elixir_sense/docs_test.exs +++ /dev/null @@ -1,2059 +0,0 @@ -defmodule ElixirSense.DocsTest do - use ExUnit.Case, async: true - - test "when no docs do not return Built-in type" do - buffer = """ - hkjnjknjk - """ - - refute ElixirSense.docs(buffer, 1, 2) - end - - test "when empty buffer" do - assert nil == ElixirSense.docs("", 1, 1) - end - - describe "module docs" do - test "module with @moduledoc false" do - %{ - docs: [doc] - } = ElixirSense.docs("ElixirSenseExample.ModuleWithDocFalse", 1, 22) - - assert doc == %{ - module: ElixirSenseExample.ModuleWithDocFalse, - metadata: %{hidden: true, app: :elixir_sense}, - docs: "", - kind: :module - } - end - - test "module with no @moduledoc" do - %{ - docs: [doc] - } = ElixirSense.docs("ElixirSenseExample.ModuleWithNoDocs", 1, 22) - - assert doc == %{ - module: ElixirSenseExample.ModuleWithNoDocs, - metadata: %{app: :elixir_sense}, - docs: "", - kind: :module - } - end - - test "retrieve documentation from modules" do - buffer = """ - defmodule MyModule do - use GenServer - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 8) - - assert doc.module == GenServer - assert doc.metadata == %{app: :elixir} - assert doc.kind == :module - - assert doc.docs =~ """ - A behaviour module for implementing the server of a client-server relation.\ - """ - end - - test "retrieve documentation from metadata modules" do - buffer = """ - defmodule MyLocalModule do - @moduledoc "Some example doc" - @moduledoc since: "1.2.3" - - @callback some() :: :ok - end - - defmodule MyModule do - @behaviour MyLocalModule - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 9, 15) - - assert doc == %{ - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - docs: "Some example doc", - kind: :module - } - end - - test "retrieve documentation from metadata modules on __MODULE__" do - buffer = """ - defmodule MyLocalModule do - @moduledoc "Some example doc" - @moduledoc since: "1.2.3" - - def self() do - __MODULE__ - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 6, 6) - - assert doc == %{ - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - docs: "Some example doc", - kind: :module - } - end - - @tag requires_elixir_1_14: true - test "retrieve documentation from metadata modules on __MODULE__ submodule" do - buffer = """ - defmodule MyLocalModule do - defmodule Sub do - @moduledoc "Some example doc" - @moduledoc since: "1.2.3" - end - - def self() do - __MODULE__.Sub - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 8, 17) - - assert doc == %{ - module: MyLocalModule.Sub, - metadata: %{since: "1.2.3"}, - docs: "Some example doc", - kind: :module - } - end - - test "retrieve documentation from metadata modules with @moduledoc false" do - buffer = """ - defmodule MyLocalModule do - @moduledoc false - - @callback some() :: :ok - end - - defmodule MyModule do - @behaviour MyLocalModule - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 8, 15) - - assert doc == %{module: MyLocalModule, metadata: %{hidden: true}, docs: "", kind: :module} - end - - test "retrieve documentation from erlang modules" do - buffer = """ - defmodule MyModule do - alias :erlang, as: Erl - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 13) - - assert doc.module == :erlang - assert doc.kind == :module - - if ExUnitConfig.erlang_eep48_supported() do - assert doc.docs =~ """ - By convention,\ - """ - - assert %{name: "erlang", otp_doc_vsn: {1, 0, 0}} = doc.metadata - end - end - - test "retrieve documentation from modules in 1.2 alias syntax" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithDocs - alias ElixirSenseExample.{Some, ModuleWithDocs} - end - """ - - %{ - docs: docs_1 - } = ElixirSense.docs(buffer, 2, 30) - - %{ - docs: docs_2 - } = ElixirSense.docs(buffer, 2, 38) - - assert docs_1 == docs_2 - end - - test "not existing module docs" do - buffer = """ - defmodule MyModule do - raise NotExistingError, "Error" - end - """ - - refute ElixirSense.docs(buffer, 2, 11) - end - end - - describe "functions and macros" do - test "retrieve documentation from Kernel macro" do - buffer = """ - defmodule MyModule do - - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 1, 2) - - assert %{ - args: ["alias", "do_block"], - function: :defmodule, - module: Kernel, - metadata: %{}, - specs: [], - kind: :macro - } = doc - - assert doc.module == Kernel - assert doc.function == :defmodule - - assert doc.docs =~ """ - Defines a module given by name with the given contents. - """ - end - - test "retrieve documentation from Kernel.SpecialForm macro" do - buffer = """ - defmodule MyModule do - import List - ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 4) - - assert %{ - args: ["module", "opts"], - function: :import, - module: Kernel.SpecialForms, - metadata: %{}, - specs: [], - kind: :macro - } = doc - - assert doc.docs =~ """ - Imports functions and macros\ - """ - end - - test "function with @doc false" do - %{ - docs: [doc] - } = ElixirSense.docs("ElixirSenseExample.ModuleWithDocs.some_fun_doc_false(1)", 1, 40) - - assert doc == %{ - module: ElixirSenseExample.ModuleWithDocs, - metadata: %{hidden: true, defaults: 1, app: :elixir_sense}, - docs: "", - kind: :function, - args: ["a", "b \\\\ nil"], - arity: 2, - function: :some_fun_doc_false, - specs: [] - } - end - - test "function no @doc" do - %{ - docs: [doc] - } = ElixirSense.docs("ElixirSenseExample.ModuleWithDocs.some_fun_no_doc(1)", 1, 40) - - assert doc == %{ - docs: "", - kind: :function, - metadata: %{defaults: 1, app: :elixir_sense}, - module: ElixirSenseExample.ModuleWithDocs, - args: ["a", "b \\\\ nil"], - arity: 2, - function: :some_fun_no_doc, - specs: [] - } - end - - test "retrieve function documentation" do - buffer = """ - defmodule MyModule do - def func(list) do - List.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 12) - - assert %{ - args: ["list"], - function: :flatten, - module: List, - metadata: %{}, - specs: ["@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]"], - kind: :function - } = doc - - assert doc.docs =~ """ - Flattens the given `list` of nested lists. - """ - end - - test "retrieve metadata function documentation" do - buffer = """ - defmodule MyLocalModule do - @doc "Sample doc" - @doc since: "1.2.3" - @spec flatten(list()) :: list() - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 12, 20) - - assert doc == %{ - args: ["list"], - function: :flatten, - arity: 1, - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - specs: ["@spec flatten(list()) :: list()"], - docs: "Sample doc", - kind: :function - } - end - - test "retrieve local private metadata function documentation" do - buffer = """ - defmodule MyLocalModule do - @doc "Sample doc" - @doc since: "1.2.3" - @spec flatten(list()) :: list() - defp flatten(list) do - [] - end - - def func(list) do - flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 10, 7) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - specs: ["@spec flatten(list()) :: list()"], - docs: "", - kind: :function - } - end - - test "retrieve metadata function documentation - fallback to callback in metadata" do - buffer = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @callback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 18, 20) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - kind: :function, - metadata: %{implementing: MyBehaviour, since: "1.2.3", hidden: true}, - module: MyLocalModule, - specs: ["@callback flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "retrieve metadata function documentation - fallback to callback in metadata no @impl" do - buffer = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @callback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 17, 20) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - kind: :function, - metadata: %{implementing: MyBehaviour, since: "1.2.3"}, - module: MyLocalModule, - specs: ["@callback flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "retrieve metadata function documentation - fallback to protocol function in metadata" do - buffer = """ - defprotocol BB do - @doc "asdf" - @doc since: "1.2.3" - @spec go(t) :: integer() - def go(t) - end - - defimpl BB, for: String do - def go(t), do: "" - end - - defmodule MyModule do - def func(list) do - BB.String.go(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 14, 16) - - assert doc == %{ - args: ["t"], - function: :go, - arity: 1, - kind: :function, - metadata: %{implementing: BB, since: "1.2.3"}, - module: BB.String, - specs: ["@callback go(t) :: integer()"], - docs: "asdf" - } - end - - test "retrieve documentation of local macro" do - buffer = """ - defmodule MyModule do - defmacrop some(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some(1) - end - end - """ - - assert %{ - docs: [_doc] - } = ElixirSense.docs(buffer, 5, 6) - end - - test "find definition of local macro on definition" do - buffer = """ - defmodule MyModule do - defmacrop some(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some(1) - end - end - """ - - assert %{ - docs: [_doc] - } = ElixirSense.docs(buffer, 2, 14) - end - - test "does not find definition of local macro if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - defmacro other do - some(1) - end - - defmacrop some(var), do: Macro.expand(var, __CALLER__) - end - """ - - assert ElixirSense.docs(buffer, 3, 6) == nil - end - - test "find definition of local function even if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - def other do - some(1) - end - - defp some(var), do: :ok - end - """ - - assert %{ - docs: [_doc] - } = ElixirSense.docs(buffer, 3, 6) - end - - test "retrieve metadata macro documentation - fallback to macrocallback in metadata" do - buffer = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @macrocallback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - defmacro flatten(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 19, 20) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - kind: :macro, - metadata: %{implementing: MyBehaviour, since: "1.2.3", hidden: true}, - module: MyLocalModule, - specs: ["@macrocallback flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "retrieve metadata function documentation - fallback to callback" do - buffer = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 12, 20) - - assert doc == %{ - args: ["list"], - function: :flatten, - arity: 1, - kind: :function, - metadata: %{ - implementing: ElixirSenseExample.BehaviourWithMeta, - implementing_module_app: :elixir_sense, - since: "1.2.3", - hidden: true - }, - module: MyLocalModule, - specs: ["@callback flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "retrieve metadata function documentation - fallback to callback no @impl" do - buffer = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 11, 20) - - assert doc == %{ - args: ["list"], - function: :flatten, - arity: 1, - kind: :function, - metadata: %{ - implementing: ElixirSenseExample.BehaviourWithMeta, - implementing_module_app: :elixir_sense, - since: "1.2.3" - }, - module: MyLocalModule, - specs: ["@callback flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "retrieve metadata function documentation - fallback to erlang callback" do - buffer = """ - defmodule MyLocalModule do - @behaviour :gen_statem - - @impl true - def init(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.init(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 12, 20) - - assert %{ - args: ["list"], - function: :init, - module: MyLocalModule, - kind: :function - } = doc - - if ExUnitConfig.erlang_eep48_supported() do - assert doc.docs =~ - "this function is called by" - - assert %{since: "OTP 19.0", implementing: :gen_statem} = doc.metadata - end - end - - test "retrieve metadata macro documentation - fallback to macrocallback" do - buffer = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - defmacro bar(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.bar(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 13, 20) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :bar, - module: MyLocalModule, - metadata: %{ - since: "1.2.3", - implementing: ElixirSenseExample.BehaviourWithMeta, - implementing_module_app: :elixir_sense, - hidden: true - }, - specs: ["@macrocallback bar(integer()) :: Macro.t()"], - docs: "Docs for bar", - kind: :macro - } - end - - test "retrieve local private metadata function documentation on __MODULE__ call" do - buffer = """ - defmodule MyLocalModule do - @doc "Sample doc" - @doc since: "1.2.3" - @spec flatten(list()) :: list() - def flatten(list) do - [] - end - - def func(list) do - __MODULE__.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 10, 17) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - specs: ["@spec flatten(list()) :: list()"], - docs: "Sample doc", - kind: :function - } - end - - @tag requires_elixir_1_14: true - test "retrieve local private metadata function documentation on __MODULE__ submodule call" do - buffer = """ - defmodule MyLocalModule do - defmodule Sub do - @doc "Sample doc" - @doc since: "1.2.3" - @spec flatten(list()) :: list() - def flatten(list) do - [] - end - end - - def func(list) do - __MODULE__.Sub.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 12, 20) - - assert doc == %{ - args: ["list"], - function: :flatten, - arity: 1, - kind: :function, - metadata: %{since: "1.2.3"}, - module: MyLocalModule.Sub, - specs: ["@spec flatten(list()) :: list()"], - docs: "Sample doc" - } - end - - test "does not retrieve remote private metadata function documentation" do - buffer = """ - defmodule MyLocalModule do - @doc "Sample doc" - @doc since: "1.2.3" - @spec flatten(list()) :: list() - defp flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - assert nil == ElixirSense.docs(buffer, 12, 20) - end - - test "retrieve metadata function documentation with @doc false" do - buffer = """ - defmodule MyLocalModule do - @doc false - @spec flatten(list()) :: list() - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 11, 20) - - assert doc == %{ - args: ["list"], - arity: 1, - function: :flatten, - kind: :function, - metadata: %{hidden: true}, - module: MyLocalModule, - specs: ["@spec flatten(list()) :: list()"], - docs: "" - } - end - - test "retrieve function documentation on @attr call" do - buffer = """ - defmodule MyModule do - @attr List - @attr.flatten(list) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 12) - - assert %{ - args: ["list"], - function: :flatten, - module: List, - metadata: %{}, - specs: ["@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]"], - kind: :function - } = doc - - assert doc.docs =~ """ - Flattens the given `list` of nested lists. - """ - end - - test "retrieve erlang function documentation" do - buffer = """ - defmodule MyModule do - def func(list) do - :lists.flatten(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 12) - - assert %{ - args: ["deepList"], - function: :flatten, - module: :lists, - kind: :function - } = doc - - if ExUnitConfig.erlang_eep48_supported() do - assert doc.docs =~ """ - Returns a flattened version of `DeepList`\\. - """ - - assert %{signature: _} = doc.metadata - end - end - - @tag requires_otp_23: true - test "retrieve fallback erlang builtin function documentation" do - buffer = """ - defmodule MyModule do - def func(list) do - :erlang.or(a, b) - :erlang.orelse(a, b) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 14) - - assert %{ - arity: 2, - function: :or, - module: :erlang, - specs: ["@spec boolean() or boolean() :: boolean()"], - docs: "", - kind: :function - } = doc - - if String.to_integer(System.otp_release()) < 25 do - assert doc.args == ["boolean", "boolean"] - assert doc.metadata == %{app: :erts} - else - assert doc.args == ["term", "term"] - assert doc.metadata == %{hidden: true, app: :erts} - end - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 4, 14) - - assert %{ - args: ["term", "term"], - arity: 2, - function: :orelse, - module: :erlang, - metadata: %{builtin: true, app: :erts}, - specs: [], - docs: "", - kind: :function - } = doc - end - - test "retrieve macro documentation" do - buffer = """ - defmodule MyModule do - require ElixirSenseExample.BehaviourWithMacrocallback.Impl, as: Macros - Macros.some({}) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 12) - - assert doc == %{ - args: ["var"], - function: :some, - arity: 1, - module: ElixirSenseExample.BehaviourWithMacrocallback.Impl, - metadata: %{app: :elixir_sense}, - specs: [ - "@spec some(integer()) :: Macro.t()\n@spec some(b) :: Macro.t() when b: float()" - ], - docs: "some macro\n", - kind: :macro - } - end - - @tag requires_elixir_1_14: true - test "retrieve function documentation with __MODULE__ submodule call" do - buffer = """ - defmodule Inspect do - def func(list) do - __MODULE__.Algebra.string(list) - end - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 26) - - assert %{ - args: ["string"], - function: :string, - module: Inspect.Algebra, - metadata: %{since: "1.6.0"}, - specs: ["@spec string(String.t()) :: doc_string()"], - kind: :function - } = doc - - assert doc.docs =~ "Creates a document" - end - - test "retrieve function documentation from aliased modules" do - buffer = """ - defmodule MyModule do - alias List, as: MyList - MyList.flatten([]) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 12) - - assert %{ - args: ["list"], - function: :flatten, - module: List, - metadata: %{}, - specs: ["@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]"], - kind: :function - } = doc - - assert doc.docs =~ """ - Flattens the given `list` of nested lists. - """ - end - - test "retrieve function documentation from imported modules" do - buffer = """ - defmodule MyModule do - import Mix.Generator - create_file("a", "b") - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert %{ - args: ["path", "contents", "opts \\\\ []"], - function: :create_file, - module: Mix.Generator, - metadata: %{defaults: 1}, - specs: ["@spec create_file(Path.t(), iodata(), keyword()) :: boolean()"], - kind: :function - } = doc - - assert doc.docs =~ "Creates a file with the given contents" - end - - test "find built-in functions" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - ElixirSenseExample.ModuleWithFunctions.module_info() - # ^ - ElixirSenseExample.ModuleWithFunctions.module_info(:exports) - # ^ - ElixirSenseExample.ModuleWithFunctions.__info__(:macros) - # ^ - ElixirSenseExample.ExampleBehaviour.behaviour_info(:callbacks) - # ^ - end - """ - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 42) - - assert %{ - args: [], - function: :module_info, - module: ElixirSenseExample.ModuleWithFunctions, - arity: 0, - metadata: %{builtin: true}, - specs: [ - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]" - ], - docs: "The `module_info/0` function in each module" <> _, - kind: :function - } = doc - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 4, 42) - - assert %{ - args: ["key"], - arity: 1, - function: :module_info, - module: ElixirSenseExample.ModuleWithFunctions, - metadata: %{builtin: true}, - specs: [ - "@spec module_info(:module) :: atom", - "@spec module_info(:attributes | :compile) :: [{atom, term}]", - "@spec module_info(:md5) :: binary", - "@spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}]", - "@spec module_info(:native) :: boolean" - ], - docs: "The call `module_info(Key)`" <> _, - kind: :function - } = doc - - assert %{docs: [%{function: :__info__}]} = - ElixirSense.docs(buffer, 6, 42) - - assert %{docs: [%{function: :behaviour_info}]} = - ElixirSense.docs(buffer, 8, 42) - end - - test "built-in functions cannot be called locally" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - import GenServer - @ callback cb() :: term - module_info() - #^ - __info__(:macros) - #^ - behaviour_info(:callbacks) - #^ - end - """ - - refute ElixirSense.docs(buffer, 4, 5) - - refute ElixirSense.docs(buffer, 6, 5) - - refute ElixirSense.docs(buffer, 8, 5) - end - - test "retrieve function documentation from behaviour if available" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ExampleBehaviourWithDocCallbackNoImpl - foo() - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert doc == %{ - args: [], - function: :foo, - arity: 0, - module: ElixirSenseExample.ExampleBehaviourWithDocCallbackNoImpl, - metadata: %{ - implementing: ElixirSenseExample.ExampleBehaviourWithDoc, - implementing_module_app: :elixir_sense, - app: :elixir_sense - }, - specs: ["@callback foo() :: :ok"], - docs: "Docs for foo", - kind: :function - } - end - - test "retrieve function documentation from behaviour even if @doc is set to false vie @impl" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl - baz(1) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert doc == %{ - args: ["a"], - function: :baz, - arity: 1, - module: ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl, - specs: ["@callback baz(integer()) :: :ok"], - metadata: %{ - implementing: ElixirSenseExample.ExampleBehaviourWithDoc, - hidden: true, - implementing_module_app: :elixir_sense, - app: :elixir_sense - }, - docs: "Docs for baz", - kind: :function - } - end - - test "retrieve function documentation from behaviour when callback has @doc false" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ExampleBehaviourWithNoDocCallbackImpl - foo() - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert doc == %{ - args: [], - function: :foo, - arity: 0, - module: ElixirSenseExample.ExampleBehaviourWithNoDocCallbackImpl, - metadata: %{ - implementing: ElixirSenseExample.ExampleBehaviourWithNoDoc, - implementing_module_app: :elixir_sense, - hidden: true, - app: :elixir_sense - }, - specs: ["@callback foo() :: :ok"], - docs: "", - kind: :function - } - end - - test "retrieve macro documentation from behaviour if available" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ExampleBehaviourWithDocCallbackNoImpl - bar(1) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert doc == %{ - args: ["b"], - arity: 1, - function: :bar, - module: ElixirSenseExample.ExampleBehaviourWithDocCallbackNoImpl, - metadata: %{ - implementing: ElixirSenseExample.ExampleBehaviourWithDoc, - implementing_module_app: :elixir_sense, - app: :elixir_sense - }, - specs: ["@macrocallback bar(integer()) :: Macro.t()"], - docs: "Docs for bar", - kind: :macro - } - end - - @tag requires_otp_25: true - test "retrieve erlang behaviour implementation" do - buffer = """ - :file_server.init(a) - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 1, 16) - - assert %{ - args: ["args"], - function: :init, - module: :file_server, - specs: ["@callback init(args :: term())" <> _], - metadata: %{implementing: :gen_server, implementing_module_app: :stdlib}, - kind: :function - } = doc - - assert doc.docs =~ "Whenever a `gen_server` process is started" - end - - test "do not crash for erlang behaviour callbacks" do - buffer = """ - defmodule MyModule do - import ElixirSenseExample.ExampleBehaviourWithDocCallbackErlang - init(:ok) - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 5) - - assert %{ - args: ["_"], - function: :init, - module: ElixirSenseExample.ExampleBehaviourWithDocCallbackErlang - } = doc - - if ExUnitConfig.erlang_eep48_supported() do - assert doc.docs =~ "called by the new process" - assert %{since: "OTP 19.0", implementing: :gen_statem, app: :elixir_sense} = doc.metadata - else - assert doc.docs == "" - assert doc.metadata == %{app: :elixir_sense} - end - end - end - - describe "types" do - test "type with @typedoc false" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithDocs, as: Remote - @type my_list :: Remote.some_type_doc_false - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 31) - - assert doc == %{ - args: [], - arity: 0, - docs: "", - kind: :type, - metadata: %{hidden: true, app: :elixir_sense}, - module: ElixirSenseExample.ModuleWithDocs, - spec: "@type some_type_doc_false() :: integer()", - type: :some_type_doc_false - } - end - - test "type no @typedoc" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithDocs, as: Remote - @type my_list :: Remote.some_type_no_doc - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 31) - - assert doc == %{ - args: [], - arity: 0, - docs: "", - kind: :type, - metadata: %{app: :elixir_sense}, - module: ElixirSenseExample.ModuleWithDocs, - spec: "@type some_type_no_doc() :: integer()", - type: :some_type_no_doc - } - end - - test "retrieve type documentation" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithTypespecs.Remote - @type my_list :: Remote.remote_t - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 31) - - assert doc == %{ - args: [], - arity: 0, - docs: "Remote type", - kind: :type, - metadata: %{app: :elixir_sense}, - module: ElixirSenseExample.ModuleWithTypespecs.Remote, - spec: "@type remote_t() :: atom()", - type: :remote_t - } - end - - test "retrieve metadata type documentation" do - buffer = """ - defmodule MyLocalModule do - @typedoc "My example type" - @typedoc since: "1.2.3" - @type some(a) :: {a} - end - - defmodule MyModule do - @type my_list :: MyLocalModule.some(:a) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 8, 35) - - assert doc == %{ - args: ["a"], - type: :some, - arity: 1, - module: MyLocalModule, - metadata: %{since: "1.2.3"}, - spec: "@type some(a) :: {a}", - docs: "My example type", - kind: :type - } - end - - test "retrieve local private metadata type documentation" do - buffer = """ - defmodule MyLocalModule do - @typedoc "My example type" - @typedoc since: "1.2.3" - @typep some(a) :: {a} - - @type my_list :: some(:a) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 6, 22) - - assert doc == %{ - args: ["a"], - type: :some, - arity: 1, - module: MyLocalModule, - spec: "@typep some(a) :: {a}", - metadata: %{since: "1.2.3"}, - docs: "", - kind: :type - } - end - - test "retrieve local metadata type documentation even if it's defined after cursor" do - buffer = """ - defmodule MyModule do - @type remote_list_t :: [my_t] - # ^ - - @typep my_t :: integer - end - """ - - assert %{docs: [_]} = - ElixirSense.docs(buffer, 2, 29) - end - - test "does not retrieve remote private metadata type documentation" do - buffer = """ - defmodule MyLocalModule do - @typedoc "My example type" - @typedoc since: "1.2.3" - @typep some(a) :: {a} - end - - defmodule MyModule do - @type my_list :: MyLocalModule.some(:a) - # ^ - end - """ - - assert nil == ElixirSense.docs(buffer, 8, 35) - end - - test "does not reveal details for opaque metadata type" do - buffer = """ - defmodule MyLocalModule do - @typedoc "My example type" - @typedoc since: "1.2.3" - @opaque some(a) :: {a} - end - - defmodule MyModule do - @type my_list :: MyLocalModule.some(:a) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 8, 35) - - assert doc == %{ - args: ["a"], - type: :some, - arity: 1, - module: MyLocalModule, - spec: "@opaque some(a)", - metadata: %{since: "1.2.3", opaque: true}, - docs: "My example type", - kind: :type - } - end - - test "retrieve metadata type documentation with @typedoc false" do - buffer = """ - defmodule MyLocalModule do - @typedoc false - @type some(a) :: {a} - end - - defmodule MyModule do - @type my_list :: MyLocalModule.some(:a) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 7, 35) - - assert doc == %{ - args: ["a"], - type: :some, - arity: 1, - module: MyLocalModule, - spec: "@type some(a) :: {a}", - metadata: %{hidden: true}, - docs: "", - kind: :type - } - end - - test "does not reveal opaque type details" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.CallbackOpaque - @type my_list :: CallbackOpaque.t(integer) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 35) - - assert doc == %{ - args: ["x"], - type: :t, - arity: 1, - module: ElixirSenseExample.CallbackOpaque, - spec: "@opaque t(x)", - metadata: %{opaque: true, app: :elixir_sense}, - docs: "Opaque type\n", - kind: :type - } - end - - test "retrieve erlang type documentation" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithTypespecs.Remote - @type my_list :: :erlang.time_unit - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 31) - - assert %{ - args: [], - type: :time_unit, - module: :erlang, - spec: "@type time_unit() ::\n pos_integer()" <> _, - kind: :type - } = doc - - if ExUnitConfig.erlang_eep48_supported() do - assert doc.docs =~ """ - Supported time unit representations: - """ - - assert %{signature: _} = doc.metadata - end - end - - test "retrieve builtin type documentation" do - buffer = """ - defmodule MyModule do - @type options :: keyword - # ^ - @type options1 :: keyword(integer) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 23) - - assert doc == %{ - args: [], - type: :keyword, - arity: 0, - module: nil, - spec: "@type keyword() :: [{atom(), any()}]", - metadata: %{builtin: true}, - docs: "A keyword list", - kind: :type - } - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 4, 23) - - assert doc == %{ - args: ["t"], - type: :keyword, - arity: 1, - module: nil, - metadata: %{builtin: true}, - spec: "@type keyword(t()) :: [{atom(), t()}]", - docs: "A keyword list with values of type `t`", - kind: :type - } - end - - test "retrieve basic type documentation" do - buffer = """ - defmodule MyModule do - @type num :: integer - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 19) - - assert doc == %{ - args: [], - type: :integer, - module: nil, - arity: 0, - spec: "@type integer()", - metadata: %{builtin: true}, - docs: "An integer number", - kind: :type - } - end - - test "retrieve basic and builtin type documentation" do - buffer = """ - defmodule MyModule do - @type num :: list() - # ^ - @type num1 :: list(atom) - # ^ - end - """ - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 18) - - assert doc == %{ - args: [], - type: :list, - arity: 0, - module: nil, - spec: "@type list() :: [any()]", - metadata: %{builtin: true}, - docs: "A list", - kind: :type - } - - %{ - docs: [doc] - } = ElixirSense.docs(buffer, 4, 18) - - assert doc == %{ - args: ["t"], - type: :list, - arity: 1, - module: nil, - spec: "@type list(t())", - metadata: %{builtin: true}, - docs: "Proper list ([]-terminated)", - kind: :type - } - end - end - - describe "attributes" do - test "retrieve reserved module attributes documentation" do - buffer = """ - defmodule MyModule do - @on_load :on_load - - def on_load(), do: :ok - end - """ - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 6) - - assert doc == %{ - name: "on_load", - docs: "A hook that will be invoked whenever the module is loaded.", - kind: :attribute - } - end - - test "retrieve unreserved module attributes documentation" do - buffer = """ - defmodule MyModule do - @my_attribute nil - end - """ - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 6) - - assert doc == %{name: "my_attribute", docs: "", kind: :attribute} - end - end - - test "retrieve docs on reserved words" do - buffer = """ - defmodule MyModule do - end - """ - - if Version.match?(System.version(), ">= 1.14.0") do - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 1, 21) - - assert doc == %{name: "do", docs: "do-end block control keyword", kind: :keyword} - else - assert nil == ElixirSense.docs(buffer, 1, 21) - end - end - - describe "variables" do - test "retrieve docs on variables" do - buffer = """ - defmodule MyModule do - def fun(my_var) do - other_var = 5 - abc(my_var, other_var) - end - end - """ - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 2, 12) - - assert doc == %{name: "my_var", kind: :variable} - - assert %{ - docs: [doc] - } = ElixirSense.docs(buffer, 3, 6) - - assert doc == %{name: "other_var", kind: :variable} - end - - test "variables shadow builtin functions" do - buffer = """ - defmodule Vector do - @spec magnitude(Vec2.t()) :: number() - def magnitude(%Vec2{} = v), do: :math.sqrt(:math.pow(v.x, 2) + :math.pow(v.y, 2)) - - @spec normalize(Vec2.t()) :: Vec2.t() - def normalize(%Vec2{} = v) do - length = magnitude(v) - %{v | x: v.x / length, y: v.y / length} - end - end - """ - - assert %{ - docs: [%{kind: :variable}] - } = ElixirSense.docs(buffer, 7, 6) - - assert %{ - docs: [%{kind: :variable}] - } = ElixirSense.docs(buffer, 8, 21) - end - end - - test "find local type in typespec local def elsewhere" do - buffer = """ - defmodule ElixirSenseExample.Some do - @type some_local :: integer - - def some_local(), do: :ok - - @type user :: {some_local, integer} - - def foo do - some_local - end - end - """ - - assert %{docs: [%{kind: :type}]} = - ElixirSense.docs(buffer, 6, 20) - - assert %{docs: [%{kind: :function}]} = - ElixirSense.docs(buffer, 9, 9) - end - - describe "arity" do - test "retrieves documentation for correct arity function" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: {F.my_func(), F.my_func("a"), F.my_func(1, 2, 3), F.my_func(1, 2, 3, 4)} - end - """ - - assert %{docs: [doc]} = - ElixirSense.docs(buffer, 3, 34) - - assert doc.docs =~ "2 params version" - - assert doc.specs == [ - "@spec my_func(1 | 2) :: binary()", - "@spec my_func(1 | 2, binary()) :: binary()" - ] - - # too many arguments - assert nil == ElixirSense.docs(buffer, 3, 70) - end - - test "retrieves documentation for all matching arities with incomplete code" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func( - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 3 - assert Enum.at(docs, 0).docs =~ "no params version" - assert Enum.at(docs, 1).docs =~ "2 params version" - assert Enum.at(docs, 2).docs =~ "3 params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1 - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 2 - assert Enum.at(docs, 0).docs =~ "2 params version" - assert Enum.at(docs, 1).docs =~ "3 params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1, 2, - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 1 - assert Enum.at(docs, 0).docs =~ "3 params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1, 2, 3 - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 1 - assert Enum.at(docs, 0).docs =~ "3 params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: F.my_func(1, 2, 3, - end - """ - - # too many arguments - assert nil == ElixirSense.docs(buffer, 3, 20) - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: 1 |> F.my_func( - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 26) - - assert length(docs) == 2 - assert Enum.at(docs, 0).docs =~ "2 params version" - assert Enum.at(docs, 1).docs =~ "3 params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def main, do: 1 |> F.my_func(1, - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 26) - - assert length(docs) == 1 - assert Enum.at(docs, 0).docs =~ "3 params version" - end - - test "retrieves documentation for correct arity function capture" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def go, do: &F.my_func/1 - end - """ - - assert %{docs: [doc]} = - ElixirSense.docs(buffer, 3, 19) - - assert doc.docs =~ "2 params version" - - assert doc.specs == [ - "@spec my_func(1 | 2) :: binary()", - "@spec my_func(1 | 2, binary()) :: binary()" - ] - end - - test "retrieves documentation for correct arity type" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: {T.my_type, T.my_type(boolean), T.my_type(1, 2), T.my_type(1, 2, 3)} - end - """ - - assert %{docs: [doc]} = - ElixirSense.docs(buffer, 3, 32) - - assert doc.docs =~ "one param version" - assert doc.spec == "@type my_type(a) :: {integer(), a}" - - # too many arguments - assert nil == ElixirSense.docs(buffer, 3, 68) - end - - test "retrieves documentation for all matching type arities with incomplete code" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type( - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 3 - assert Enum.at(docs, 0).docs =~ "no params version" - assert Enum.at(docs, 1).docs =~ "one param version" - assert Enum.at(docs, 2).docs =~ "two params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 2 - assert Enum.at(docs, 0).docs =~ "one param version" - assert Enum.at(docs, 1).docs =~ "two params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer, integer - end - """ - - assert %{docs: docs} = - ElixirSense.docs(buffer, 3, 20) - - assert length(docs) == 1 - assert Enum.at(docs, 0).docs =~ "two params version" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.TypesWithMultipleArity, as: T - @type some :: T.my_type(integer, integer, - end - """ - - # too many arguments - assert nil == ElixirSense.docs(buffer, 3, 20) - end - end -end diff --git a/test/elixir_sense/eval_test.exs b/test/elixir_sense/eval_test.exs deleted file mode 100644 index 227ce08f..00000000 --- a/test/elixir_sense/eval_test.exs +++ /dev/null @@ -1,185 +0,0 @@ -defmodule ElixirSense.Evaltest do - use ExUnit.Case, async: true - - describe "match" do - test "with bindings" do - code = - "{name, _, [par1, par2]} = {:func, [line: 1], [{:par1, [line: 1], nil}, {:par2, [line: 1], nil}]}" - - assert ElixirSense.match(code) =~ - """ - # Bindings - - name = :func - - par1 = {:par1, [line: 1], nil} - - par2 = {:par2, [line: 1], nil} - """ - |> String.trim() - end - - test "without bindings" do - code = - "{_, _, [_, _]} = {:func, [line: 1], [{:par1, [line: 1], nil}, {:par2, [line: 1], nil}]}" - - assert ElixirSense.match(code) == """ - # No bindings - - """ - end - - test "with token missing error" do - code = "{ = {:func, [line: 1], [{:par1, [line: 1], nil}, {:par2, [line: 1], nil}]}" - - assert ElixirSense.match(code) =~ - """ - # TokenMissingError on line 1: - # ↳ missing terminator: }\ - """ - |> String.trim() - end - - test "EVAL request match with match error" do - code = "{var} = {:func, [line: 1], [{:par1, [line: 1], nil}, {:par2, [line: 1], nil}]}" - assert ElixirSense.match(code) == "# No match" - end - end - - describe "expand full" do - test "without errors" do - buffer = """ - defmodule MyModule do - - end - """ - - code = "use Application" - result = ElixirSense.expand_full(buffer, code, 2) - - assert result.expand_once =~ - """ - ( - require Application - Application.__using__([]) - ) - """ - |> String.trim() - - assert result.expand =~ - """ - ( - require Application - Application.__using__([]) - ) - """ - |> String.trim() - - assert result.expand_partial =~ - """ - ( - require Application - - ( - @behaviour Application - @doc false - def stop(_state) do - :ok - end - - defoverridable Application - ) - ) - """ - |> String.trim() - - assert result.expand_all =~ - (if Version.match?(System.version(), ">= 1.14.0") do - """ - ( - require Application - - ( - Module.__put_attribute__(MyModule, :behaviour, Application, nil, []) - Module.__put_attribute__(MyModule, :doc, {0, false}, nil, []) - - def stop(_state) do - :ok - end - - Module.make_overridable(MyModule, Application) - """ - else - """ - ( - require Application - - ( - Module.__put_attribute__(MyModule, :behaviour, Application, nil) - Module.__put_attribute__(MyModule, :doc, {0, false}, nil) - - def stop(_state) do - :ok - end - - Module.make_overridable(MyModule, Application) - """ - end) - |> String.trim() - end - - test "with errors" do - buffer = """ - defmodule MyModule do - - end - """ - - code = "{" - result = ElixirSense.expand_full(buffer, code, 2) - - assert result.expand_once =~ - """ - "missing terminator: }\ - """ - |> String.trim() - - assert result.expand =~ - """ - "missing terminator: }\ - """ - |> String.trim() - - assert result.expand_partial =~ - """ - "missing terminator: }\ - """ - |> String.trim() - - assert result.expand_all =~ - """ - "missing terminator: }\ - """ - |> String.trim() - end - end - - describe "quote" do - test "without error" do - code = "func(par1, par2)" - - assert ElixirSense.quote(code) =~ - "{:func, [line: 1], [{:par1, [line: 1], nil}, {:par2, [line: 1], nil}]}" - end - - test "with error" do - code = "func(par1, par2" - - assert ElixirSense.quote(code) =~ - """ - "missing terminator: )\ - """ - |> String.trim() - end - end -end diff --git a/test/elixir_sense/implementation_test.exs b/test/elixir_sense/implementation_test.exs deleted file mode 100644 index 67b87678..00000000 --- a/test/elixir_sense/implementation_test.exs +++ /dev/null @@ -1,728 +0,0 @@ -defmodule ElixirSense.Providers.ImplementationTest do - use ExUnit.Case, async: true - alias ElixirSense.Providers.Implementation - alias ElixirSense.Location - alias ElixirSense.Core.Source - - doctest Implementation - - test "don't crash on empty buffer" do - assert [] == ElixirSense.implementations("", 1, 1) - end - - test "don't error on __MODULE__ when no module" do - assert [] == ElixirSense.implementations("__MODULE__", 1, 1) - end - - test "don't error on Elixir" do - assert [] == ElixirSense.implementations("Elixir", 1, 1) - end - - test "don't error on not existing module" do - assert [] == ElixirSense.implementations("SomeNotExistingMod", 1, 1) - end - - test "don't error on non behaviour module" do - assert [] == ElixirSense.implementations("ElixirSenseExample.EmptyModule", 1, 32) - end - - test "don't error on erlang function calls" do - assert [] == ElixirSense.implementations(":ets.new", 1, 8) - end - - test "don't return implementations for non callback functions on behaviour" do - assert [] == ElixirSense.implementations("GenServer.start_link", 1, 12) - end - - test "don't error on non behaviour module function" do - buffer = """ - defmodule ElixirSenseExample.EmptyModule do - def abc(), do: :ok - end - """ - - assert [] == ElixirSense.implementations(buffer, 2, 8) - end - - test "don't error on builtin macro" do - buffer = """ - defmodule ElixirSenseExample.EmptyModule do - def abc(), do: :ok - end - """ - - assert [] == ElixirSense.implementations(buffer, 1, 8) - end - - test "find implementations of behaviour module" do - buffer = """ - defmodule ElixirSenseExample.ExampleBehaviourWithDoc do - end - """ - - [ - %Location{type: :module, file: file1, line: line1, column: column1}, - %Location{type: :module, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 1, 32) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file1, {line1, column1}) =~ - "ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file2, {line2, column2}) =~ - "ElixirSenseExample.ExampleBehaviourWithDocCallbackNoImpl" - end - - test "find protocol implementations" do - buffer = """ - defprotocol ElixirSenseExample.ExampleProtocol do - end - """ - - [ - %Location{type: :module, file: file1, line: line1, column: column1}, - %Location{type: :module, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 1, 32) |> Enum.sort() - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "ElixirSenseExample.ExampleProtocol, for: List" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "ElixirSenseExample.ExampleProtocol, for: Map" - end - - test "find implementations of behaviour module callback" do - buffer = """ - defmodule ElixirSenseExample.ExampleBehaviourWithDoc do - @callback foo() :: :ok - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 2, 14) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file1, {line1, column1}) =~ - "foo(), do: :ok" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file2, {line2, column2}) =~ - "foo(), do: :ok" - end - - test "find implementations of behaviour module macrocallback" do - buffer = """ - defmodule ElixirSenseExample.ExampleBehaviourWithDoc do - @macrocallback bar(integer()) :: Macro.t() - end - """ - - [ - %Location{type: :macro, file: file1, line: line1, column: column1}, - %Location{type: :macro, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 2, 19) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file1, {line1, column1}) =~ - "defmacro bar(_b)" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file2, {line2, column2}) =~ - "defmacro bar(_b)" - end - - test "find implementations of behaviour module on callback in implementation" do - buffer = """ - defmodule Some do - @behaviour ElixirSenseExample.ExampleBehaviourWithDoc - def foo(), do: :ok - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2}, - %Location{type: :function, file: nil, line: 3, column: 3} - ] = ElixirSense.implementations(buffer, 3, 8) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file1, {line1, column1}) =~ - "foo(), do: :ok" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - - assert read_line(file2, {line2, column2}) =~ - "foo(), do: :ok" - end - - test "find implementations of metadata behaviour module callback" do - buffer = """ - defmodule MetadataBehaviour do - @callback foo() :: :ok - end - - defmodule Some do - @behaviour MetadataBehaviour - def foo(), do: :ok - end - """ - - [ - %Location{type: :function, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 2, 14) - end - - test "find implementations of metadata behaviour module macrocallback" do - buffer = """ - defmodule MetadataBehaviour do - @macrocallback foo(arg :: any) :: Macro.t - end - - defmodule Some do - @behaviour MetadataBehaviour - defmacro foo(arg), do: :ok - end - """ - - [ - %Location{type: :macro, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 2, 19) - end - - test "find implementations of metadata behaviour module macrocallback when implementation is a guard" do - buffer = """ - defmodule MetadataBehaviour do - @macrocallback foo(arg :: any) :: Macro.t - end - - defmodule Some do - @behaviour MetadataBehaviour - defguard foo(arg) when is_nil(arg) - end - """ - - [ - %Location{type: :macro, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 2, 19) - end - - test "find implementations of metadata behaviour" do - buffer = """ - defmodule MetadataBehaviour do - @callback foo() :: :ok - end - - defmodule Some do - @behaviour MetadataBehaviour - def foo(), do: :ok - end - """ - - [ - %Location{type: :module, file: nil, line: 5, column: 1} - ] = ElixirSense.implementations(buffer, 1, 14) - end - - test "find protocol implementation functions" do - buffer = """ - defprotocol ElixirSenseExample.ExampleProtocol do - @spec some(t) :: any - def some(t) - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 3, 8) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find protocol implementation functions on spec" do - buffer = """ - defprotocol ElixirSenseExample.ExampleProtocol do - @spec some(t) :: any - def some(t) - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 2, 10) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find metadata protocol implementation functions on spec" do - buffer = """ - defprotocol MetadataProtocol do - @spec some(t) :: any - def some(t) - end - - defimpl MetadataProtocol, for: String do - def some(t), do: :ok - end - """ - - [ - %Location{type: :function, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 2, 10) - end - - test "find protocol implementation functions on implementation function" do - buffer = """ - defimpl ElixirSenseExample.ExampleProtocol, for: String do - def some(t), do: t - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2}, - %Location{type: :function, file: nil, line: 2, column: 3} - ] = ElixirSense.implementations(buffer, 2, 8) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find metadata protocol implementation functions on function" do - buffer = """ - defprotocol MetadataProtocol do - @spec some(t) :: any - def some(t) - end - - defimpl MetadataProtocol, for: String do - def some(t), do: :ok - end - """ - - [ - %Location{type: :function, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 3, 8) - end - - test "find metadata protocol implementation functions on function when implementation is a delegate" do - buffer = """ - defprotocol MetadataProtocol do - @spec some(t) :: any - def some(t) - end - - defimpl MetadataProtocol, for: String do - defdelegate some(t), to: Impl - end - """ - - [ - %Location{type: :function, file: nil, line: 7, column: 3} - ] = ElixirSense.implementations(buffer, 3, 8) - end - - test "find metadata protocol implementation" do - buffer = """ - defprotocol MetadataProtocol do - @spec some(t) :: any - def some(t) - end - - defimpl MetadataProtocol, for: String do - def some(t), do: :ok - end - """ - - [ - %Location{type: :module, file: nil, line: 6, column: 1} - ] = ElixirSense.implementations(buffer, 1, 14) - end - - test "find protocol implementation functions on implementation function - incomplete code" do - buffer = """ - defimpl ElixirSenseExample.ExampleProtocol, for: String do - def some(t - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2}, - %Location{type: :function, file: nil, line: 2, column: 3} - ] = ElixirSense.implementations(buffer, 2, 8) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - - buffer = """ - defimpl ElixirSenseExample.ExampleProtocol, for: String do - def some(t, 1, - end - """ - - # too many arguments - - assert [] = ElixirSense.implementations(buffer, 2, 8) - end - - test "find protocol implementation functions on call" do - buffer = """ - ElixirSenseExample.ExampleProtocol.some(1) - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 1, 37) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find protocol implementation functions on call with incomplete code" do - buffer = """ - ElixirSenseExample.ExampleProtocol.some( - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 1, 37) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - - buffer = """ - ElixirSenseExample.ExampleProtocol.some(a, - """ - - # too many arguments - - assert [] = ElixirSense.implementations(buffer, 1, 37) - end - - test "find protocol implementation functions on call with alias" do - buffer = """ - defmodule Some do - alias ElixirSenseExample.ExampleProtocol, as: A - A.some(1) - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 3, 6) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find protocol implementation functions on call via @attr" do - buffer = """ - defmodule Some do - @attr ElixirSenseExample.ExampleProtocol - @attr.some(1) - end - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2} - ] = ElixirSense.implementations(buffer, 3, 10) - - assert file1 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file1, {line1, column1}) =~ "some(t), do: t" - - assert file2 =~ "elixir_sense/test/support/example_protocol.ex" - assert read_line(file2, {line2, column2}) =~ "some(t), do: t" - end - - test "find behaviour implementation functions on call" do - buffer = """ - ElixirSenseExample.DummyBehaviourImplementation.foo() - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1} - ] = ElixirSense.implementations(buffer, 1, 49) - - assert file1 =~ "elixir_sense/test/support/behaviour_implementations.ex" - assert read_line(file1, {line1, column1}) =~ "def foo(), do: :ok" - end - - test "find behaviour implementation functions on call metadata" do - buffer = """ - defmodule Some do - @behaviour ElixirSenseExample.ExampleBehaviourWithDoc - def foo(), do: :ok - end - - Some.foo() - """ - - [ - %Location{type: :function, file: file1, line: line1, column: column1}, - %Location{type: :function, file: file2, line: line2, column: column2}, - %Location{type: :function, file: nil, line: 3, column: 3} - ] = ElixirSense.implementations(buffer, 6, 7) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - assert read_line(file1, {line1, column1}) =~ "def foo(), do: :ok" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - assert read_line(file2, {line2, column2}) =~ "def foo(), do: :ok" - end - - test "find behaviour macrocallback implementation functions on call metadata" do - buffer = """ - defmodule Some do - @behaviour ElixirSenseExample.ExampleBehaviourWithDoc - defmacro bar(a), do: :ok - end - - Some.bar() - Some.bar(1) - Some.bar(1, 2) - """ - - [ - %Location{type: :macro, file: file1, line: line1, column: column1}, - %Location{type: :macro, file: file2, line: line2, column: column2}, - %Location{type: :macro, file: nil, line: 3, column: 3} - ] = ElixirSense.implementations(buffer, 7, 7) - - assert file1 =~ "elixir_sense/test/support/example_behaviour.ex" - assert read_line(file1, {line1, column1}) =~ "defmacro bar(_b)" - - assert file2 =~ "elixir_sense/test/support/example_behaviour.ex" - assert read_line(file2, {line2, column2}) =~ "defmacro bar(_b)" - - # too little arguments - - [] = ElixirSense.implementations(buffer, 6, 7) - - # too many arguments - - [] = ElixirSense.implementations(buffer, 8, 7) - end - - test "find implementation of delegated functions" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function() - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function do" - end - - test "find implementation of delegated functions in incomplete code" do - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function( - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function do" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function(1 - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function(a) do" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function(1, - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 3, 11) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function(a, b) do" - - buffer = """ - defmodule MyModule do - alias ElixirSenseExample.ModuleWithFunctions, as: MyMod - MyMod.delegated_function(1, 2, - # ^ - end - """ - - # too many arguments - - assert [] = ElixirSense.implementations(buffer, 3, 11) - end - - test "find implementation of delegated functions via @attr" do - buffer = """ - defmodule MyModule do - @attr ElixirSenseExample.ModuleWithFunctions - def a do - @attr.delegated_function() - end - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 4, 13) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function do" - end - - test "handle defdelegate" do - buffer = """ - defmodule MyModule do - defdelegate delegated_function, to: ElixirSenseExample.ModuleWithFunctions.DelegatedModule - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 2, 15) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "def delegated_function do" - end - - test "handle defdelegate - navigate to correct arity" do - buffer = """ - defmodule MyModule do - defdelegate delegated_function(a), to: ElixirSenseExample.ModuleWithFunctions.DelegatedModule - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 2, 15) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "def delegated_function(a) do" - end - - test "handle defdelegate - navigate to correct arity on default args" do - buffer = """ - defmodule MyModule do - defdelegate delegated_function(a \\\\ nil), to: ElixirSenseExample.ModuleWithFunctions.DelegatedModule - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 2, 15) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "def delegated_function(a) do" - end - - test "handle defdelegate with `as`" do - buffer = """ - defmodule MyModule do - defdelegate my_function, to: ElixirSenseExample.ModuleWithFunctions.DelegatedModule, as: :delegated_function - # ^ - end - """ - - [%Location{type: :function, file: file, line: line, column: column}] = - ElixirSense.implementations(buffer, 2, 15) - - assert file =~ "elixir_sense/test/support/module_with_functions.ex" - assert read_line(file, {line, column}) =~ "delegated_function" - end - - test "defdelegate to metadata module" do - buffer = """ - defmodule SomeModWithDelegatee do - def delegated_function, do: :ok - end - - defmodule MyModule do - defdelegate delegated_function, to: SomeModWithDelegatee - # ^ - end - """ - - assert [%Location{type: :function, file: nil, line: 2, column: 3}] == - ElixirSense.implementations(buffer, 6, 15) - end - - test "handle recursion in defdelegate" do - buffer = """ - defmodule MyModule do - defdelegate delegated_function, to: MyModule - # ^ - end - """ - - assert [] == ElixirSense.implementations(buffer, 2, 15) - end - - defp read_line(file, {line, column}) do - file - |> File.read!() - |> Source.split_lines() - |> Enum.at(line - 1) - |> String.slice((column - 1)..-1//1) - end -end diff --git a/test/elixir_sense/location_test.exs b/test/elixir_sense/location_test.exs deleted file mode 100644 index 783ecb0c..00000000 --- a/test/elixir_sense/location_test.exs +++ /dev/null @@ -1,46 +0,0 @@ -defmodule ElixirSense.LocationTest do - use ExUnit.Case, async: false - - import ElixirSense.Location - - setup do - elixir_src = Path.join(File.cwd!(), "/test/misc/mock_elixir_src") - Application.put_env(:elixir_sense, :elixir_src, elixir_src) - - on_exit(fn -> - Application.delete_env(:elixir_sense, :elixir_src) - end) - end - - describe "find_mod_fun_source/3" do - test "returns location of a core Elixir function" do - if not File.exists?(String.module_info(:compile)[:source]) do - assert %ElixirSense.Location{type: :function, line: 26, column: 3, file: file} = - find_mod_fun_source(String, :length, 1) - - assert String.ends_with?(file, "/mock_elixir_src/lib/elixir/lib/string.ex") - else - assert %ElixirSense.Location{type: :function, file: file} = - find_mod_fun_source(String, :length, 1) - - assert file == Path.expand(String.module_info(:compile)[:source]) - end - end - end - - describe "find_type_source/3" do - test "returns location of a core Elixir type" do - if not File.exists?(String.module_info(:compile)[:source]) do - assert %ElixirSense.Location{type: :typespec, line: 11, column: 3, file: file} = - find_type_source(String, :t, 0) - - assert String.ends_with?(file, "/mock_elixir_src/lib/elixir/lib/string.ex") - else - assert %ElixirSense.Location{type: :typespec, file: file} = - find_type_source(String, :t, 0) - - assert file == Path.expand(String.module_info(:compile)[:source]) - end - end - end -end diff --git a/test/elixir_sense/providers/suggestion/complete_test.exs b/test/elixir_sense/providers/suggestion/complete_test.exs deleted file mode 100644 index b2729b91..00000000 --- a/test/elixir_sense/providers/suggestion/complete_test.exs +++ /dev/null @@ -1,2248 +0,0 @@ -# This file includes modified code extracted from the elixir project. Namely: -# -# https://github.com/elixir-lang/elixir/blob/v1.9/lib/iex/test/iex/autocomplete_test.exs -# -# The original code is licensed as follows: -# -# Copyright 2012 Plataformatec -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -defmodule ElixirSense.Providers.Suggestion.CompleteTest do - use ExUnit.Case, async: true - - alias ElixirSense.Providers.Suggestion.Complete - alias ElixirSense.Core.Metadata - alias ElixirSense.Core.State.Env - alias ElixirSense.Core.State.{ModFunInfo, SpecInfo, VarInfo, AttributeInfo} - - def expand( - expr, - env \\ %Env{ - imports: [{Kernel, []}] - }, - metadata \\ %Metadata{}, - opts \\ [] - ) do - Complete.do_expand(expr, env, metadata, {1, 1}, opts) - end - - test "erlang module completion" do - assert [ - %{ - name: ":zlib", - full_name: ":zlib", - subtype: nil, - summary: summary, - type: :module, - metadata: metadata - } - ] = expand(~c":zl") - - if ExUnitConfig.erlang_eep48_supported() do - assert summary =~ "zlib" - assert %{otp_doc_vsn: {1, 0, 0}} = metadata - end - end - - test "erlang module no completion" do - assert expand(~c":unknown") == [] - end - - test "erlang module multiple values completion" do - list = expand(~c":logger") - assert list |> Enum.find(&(&1.name == ":logger")) - assert list |> Enum.find(&(&1.name == ":logger_proxy")) - end - - test "erlang root completion" do - list = expand(~c":") - assert is_list(list) - assert list |> Enum.find(&(&1.name == ":lists")) - assert [] == list |> Enum.filter(&(&1.name |> String.contains?("Elixir.List"))) - end - - test "elixir proxy" do - list = expand(~c"E") - assert list |> Enum.find(&(&1.name == "Elixir" and &1.full_name == "Elixir")) - end - - test "elixir completion" do - assert [_ | _] = expand(~c"En") - - assert [%{name: "Enumerable", full_name: "Enumerable", subtype: :protocol, type: :module}] = - expand(~c"Enumera") - end - - test "elixir module completion with @moduledoc false" do - assert [%{name: "ModuleWithDocFalse", summary: ""}] = - expand(~c"ElixirSenseExample.ModuleWithDocFals") - end - - test "elixir function completion with @doc false" do - assert [ - %{ - name: "some_fun_doc_false", - summary: "", - args: "a, b \\\\ nil", - arity: 1, - origin: "ElixirSenseExample.ModuleWithDocs", - spec: "", - type: :function - }, - %{ - args: "a, b \\\\ nil", - arity: 2, - name: "some_fun_doc_false", - origin: "ElixirSenseExample.ModuleWithDocs", - spec: "", - summary: "", - type: :function - }, - %{ - args: "a, b \\\\ nil", - arity: 1, - name: "some_fun_no_doc", - origin: "ElixirSenseExample.ModuleWithDocs", - spec: "", - summary: "", - type: :function - }, - %{ - args: "a, b \\\\ nil", - arity: 2, - name: "some_fun_no_doc", - origin: "ElixirSenseExample.ModuleWithDocs", - spec: "", - summary: "", - type: :function - } - ] = expand(~c"ElixirSenseExample.ModuleWithDocs.some_fun_") - end - - test "elixir completion with self" do - assert [%{name: "Enumerable", subtype: :protocol}] = expand(~c"Enumerable") - end - - test "elixir completion macro with default args" do - assert [ - %{ - args: "a \\\\ :asdf, b, var \\\\ 0", - arity: 1, - name: "with_default", - origin: "ElixirSenseExample.BehaviourWithMacrocallback.Impl", - spec: "@spec with_default(atom(), list(), integer()) :: Macro.t()", - summary: "some macro with default arg\n", - type: :macro - }, - %{ - args: "a \\\\ :asdf, b, var \\\\ 0", - arity: 2, - name: "with_default", - origin: "ElixirSenseExample.BehaviourWithMacrocallback.Impl", - spec: "@spec with_default(atom(), list(), integer()) :: Macro.t()", - summary: "some macro with default arg\n", - type: :macro - }, - %{ - args: "a \\\\ :asdf, b, var \\\\ 0", - arity: 3, - name: "with_default", - origin: "ElixirSenseExample.BehaviourWithMacrocallback.Impl", - spec: "@spec with_default(atom(), list(), integer()) :: Macro.t()", - summary: "some macro with default arg\n", - type: :macro - } - ] = expand(~c"ElixirSenseExample.BehaviourWithMacrocallback.Impl.wit") - end - - test "elixir completion on modules from load path" do - assert [ - %{name: "Stream", subtype: :struct, type: :module}, - %{name: "String", subtype: nil, type: :module}, - %{name: "StringIO", subtype: nil, type: :module} - ] = expand(~c"Str") |> Enum.filter(&(&1.name |> String.starts_with?("Str"))) - - assert [ - %{name: "Macro"}, - %{name: "Map"}, - %{name: "MapSet"}, - %{name: "MatchError"} - ] = expand(~c"Ma") |> Enum.filter(&(&1.name |> String.starts_with?("Ma"))) - - assert [%{name: "Dict"}] = - expand(~c"Dic") |> Enum.filter(&(&1.name |> String.starts_with?("Dic"))) - - assert suggestions = expand(~c"Ex") - assert Enum.any?(suggestions, &(&1.name == "ExUnit")) - assert Enum.any?(suggestions, &(&1.name == "Exception")) - end - - test "Elixir no completion for underscored functions with no doc" do - {:module, _, bytecode, _} = - defmodule Elixir.Sample do - def __foo__(), do: 0 - @doc "Bar doc" - def __bar__(), do: 1 - end - - File.write!("Elixir.Sample.beam", bytecode) - - case Code.fetch_docs(Sample) do - {:docs_v1, _, _, _, _, _, _} -> :ok - {:error, :chunk_not_found} -> :ok - end - - # IEx version asserts expansion on Sample._ but we also include :__info__ and there is more than 1 match - assert [%{name: "__bar__"}] = expand(~c"Sample.__b") - after - File.rm("Elixir.Sample.beam") - :code.purge(Sample) - :code.delete(Sample) - end - - test "completion for functions added when compiled module is reloaded" do - {:module, _, bytecode, _} = - defmodule Sample do - def foo(), do: 0 - end - - File.write!("ElixirSense.Providers.Suggestion.CompleteTest.Sample.beam", bytecode) - - assert [%{name: "foo"}] = expand(~c"ElixirSense.Providers.Suggestion.CompleteTest.Sample.foo") - - Code.compiler_options(ignore_module_conflict: true) - - defmodule Sample do - def foo(), do: 0 - def foobar(), do: 0 - end - - assert [%{name: "foo"}, %{name: "foobar"}] = - expand(~c"ElixirSense.Providers.Suggestion.CompleteTest.Sample.foo") - after - File.rm("ElixirSense.Providers.Suggestion.CompleteTest.Sample.beam") - Code.compiler_options(ignore_module_conflict: false) - :code.purge(Sample) - :code.delete(Sample) - end - - test "elixir no completion" do - assert expand(~c".") == [] - assert expand(~c"Xyz") == [] - assert expand(~c"x.Foo") == [] - assert expand(~c"x.Foo.get_by") == [] - # assert expand('@foo.bar') == {:no, '', []} - end - - test "elixir root submodule completion" do - assert [ - %{ - name: "Access", - full_name: "Access", - summary: "Key-based access to data structures." - } - ] = expand(~c"Elixir.Acce") - - assert [_ | _] = expand(~c"Elixir.") - end - - test "elixir submodule completion" do - assert [ - %{ - name: "Chars", - full_name: "String.Chars", - subtype: :protocol, - summary: - "The `String.Chars` protocol is responsible for\nconverting a structure to a binary (only if applicable)." - } - ] = expand(~c"String.Cha") - end - - @tag requires_elixir_1_14: true - test "elixir submodule completion with __MODULE__" do - assert [ - %{ - name: "Chars", - full_name: "String.Chars", - subtype: :protocol, - summary: - "The `String.Chars` protocol is responsible for\nconverting a structure to a binary (only if applicable)." - } - ] = expand(~c"__MODULE__.Cha", %Env{module: String}) - end - - @tag requires_elixir_1_14: true - test "elixir submodule completion with attribute bound to module" do - assert [ - %{ - name: "Chars", - full_name: "String.Chars", - subtype: :protocol, - summary: - "The `String.Chars` protocol is responsible for\nconverting a structure to a binary (only if applicable)." - } - ] = - expand(~c"@my_attr.Cha", %Env{ - attributes: [ - %AttributeInfo{ - name: :my_attr, - type: {:atom, String} - } - ] - }) - end - - test "find elixir modules that require alias" do - assert [ - %{ - metadata: %{}, - name: "CallerWithAliasesAndImports", - full_name: - "ElixirSense.Providers.ReferencesTest.Modules.CallerWithAliasesAndImports", - required_alias: - "ElixirSense.Providers.ReferencesTest.Modules.CallerWithAliasesAndImports" - }, - %{ - metadata: %{}, - name: "Chars", - full_name: "List.Chars", - required_alias: "List.Chars" - }, - %{ - metadata: %{}, - name: "Chars", - full_name: "String.Chars", - required_alias: "String.Chars" - } - ] = expand(~c"Char", %Env{}, %Metadata{}, required_alias: true) - end - - test "does not suggest required_alias when alias already exists" do - env = %Env{ - aliases: [{MyChars, String.Chars}] - } - - results = expand(~c"Char", env, %Metadata{}, required_alias: true) - - refute Enum.find(results, fn expansion -> expansion[:required_alias] == String.Chars end) - end - - test "does not suggest required_alias for Elixir proxy" do - env = %Env{ - aliases: [] - } - - results = expand(~c"Elixi", env, %Metadata{}, required_alias: true) - - refute Enum.find(results, fn expansion -> expansion[:required_alias] == Elixir end) - end - - test "does not suggest required_alias for when hint has more than one part" do - results = expand(~c"Elixir.Char", %Env{}, %Metadata{}, required_alias: true) - - refute Enum.find(results, fn expansion -> expansion[:required_alias] == String.Chars end) - - results = expand(~c"String.", %Env{}, %Metadata{}, required_alias: true) - - refute Enum.find(results, fn expansion -> - expansion[:required_alias] == String.Tokenizer.Security - end) - end - - test "elixir submodule no completion" do - assert expand(~c"IEx.Xyz") == [] - end - - test "function completion" do - assert [%{name: "version", origin: "System"}] = expand(~c"System.ve") - assert [%{name: "fun2ms", origin: ":ets"}] = expand(~c":ets.fun2") - end - - @tag requires_elixir_1_14: true - test "function completion on __MODULE__" do - assert [%{name: "version", origin: "System"}] = - expand(~c"__MODULE__.ve", %Env{module: System}) - end - - @tag requires_elixir_1_14: true - test "function completion on __MODULE__ submodules" do - assert [%{name: "to_string", origin: "String.Chars"}] = - expand(~c"__MODULE__.Chars.to", %Env{module: String}) - end - - @tag requires_elixir_1_14: true - test "function completion on attribute bound to module" do - assert [%{name: "version", origin: "System"}] = - expand(~c"@my_attr.ve", %Env{ - attributes: [ - %AttributeInfo{ - name: :my_attr, - type: {:atom, System} - } - ] - }) - end - - test "function completion with arity" do - assert [ - %{ - name: "printable?", - arity: 1, - spec: - "@spec printable?(t(), 0) :: true\n@spec printable?(t(), pos_integer() | :infinity) :: boolean()", - summary: - "Checks if a string contains only printable characters up to `character_limit`." - }, - %{ - name: "printable?", - arity: 2, - spec: - "@spec printable?(t(), 0) :: true\n@spec printable?(t(), pos_integer() | :infinity) :: boolean()", - summary: - "Checks if a string contains only printable characters up to `character_limit`." - } - ] = expand(~c"String.printable?") - - assert [%{name: "printable?", arity: 1}, %{name: "printable?", arity: 2}] = - expand(~c"String.printable?/") - - assert [ - %{ - name: "count", - arity: 1 - }, - %{ - name: "count", - arity: 2 - }, - %{ - name: "count_until", - arity: 2 - }, - %{ - name: "count_until", - arity: 3 - } - ] = expand(~c"Enum.count") - - assert [ - %{ - name: "count", - arity: 1 - }, - %{ - name: "count", - arity: 2 - } - ] = expand(~c"Enum.count/") - end - - @tag requires_elixir_1_13: true - test "operator completion" do - assert [%{name: "+", arity: 1}, %{name: "+", arity: 2}, %{name: "++", arity: 2}] = - expand(~c"+") - - assert [%{name: "+", arity: 1}, %{name: "+", arity: 2}] = expand(~c"+/") - assert [%{name: "++", arity: 2}] = expand(~c"++/") - - assert entries = expand(~c"+ ") - assert entries |> Enum.any?(&(&1.name == "div")) - end - - @tag requires_elixir_1_13: true - test "sigil completion" do - sigils = expand(~c"~") - assert sigils |> Enum.any?(fn s -> s.name == "~C" end) - # We choose not to provide sigil quotations - # {:yes, '', sigils} = expand('~r') - # assert '"' in sigils - # assert '(' in sigils - assert [] == expand(~c"~r") - end - - test "function completion using a variable bound to a module" do - env = %Env{ - vars: [ - %VarInfo{ - name: :mod, - type: {:atom, String} - } - ] - } - - assert [%{name: "printable?", arity: 1}, %{name: "printable?", arity: 2}] = - expand(~c"mod.print", env) - end - - test "map atom key completion is supported" do - env = %Env{ - vars: [ - %VarInfo{ - name: :map, - type: {:map, [foo: 1, bar_1: 23, bar_2: 14], nil} - } - ] - } - - assert expand(~c"map.f", env) == - [ - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert [_ | _] = expand(~c"map.b", env) - - assert expand(~c"map.bar_", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"map.c", env) == [] - - assert expand(~c"map.", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"map.foo", env) == [ - %{ - call?: true, - name: "foo", - origin: nil, - subtype: :map_key, - type: :field, - type_spec: nil - } - ] - end - - test "struct key completion is supported" do - env = %Env{ - vars: [ - %VarInfo{ - name: :struct, - type: {:struct, [], {:atom, DateTime}, nil} - }, - %VarInfo{ - name: :other, - type: {:call, {:atom, DateTime}, :utc_now, []} - }, - %VarInfo{ - name: :from_metadata, - type: {:struct, [], {:atom, MyStruct}, nil} - }, - %VarInfo{ - name: :var, - type: {:variable, :struct} - }, - %VarInfo{ - name: :yyyy, - type: {:map, [date: {:struct, [], {:atom, DateTime}, nil}], []} - }, - %VarInfo{ - name: :xxxx, - type: {:call, {:atom, Map}, :fetch!, [{:variable, :yyyy}, {:atom, :date}]} - } - ] - } - - metadata = %Metadata{ - types: %{ - {MyStruct, :t, 0} => %ElixirSense.Core.State.TypeInfo{ - name: :t, - args: [[]], - specs: ["@type t :: %MyStruct{some: integer}"], - kind: :type - } - }, - structs: %{ - MyStruct => %ElixirSense.Core.State.StructInfo{type: :defstruct, fields: [some: 1]} - } - } - - assert expand(~c"struct.h", env, metadata) == - [ - %{ - call?: true, - name: "hour", - origin: "DateTime", - subtype: :struct_field, - type: :field, - type_spec: "Calendar.hour()" - } - ] - - assert expand(~c"other.d", env, metadata) == - [ - %{ - call?: true, - name: "day", - origin: "DateTime", - subtype: :struct_field, - type: :field, - type_spec: "Calendar.day()" - } - ] - - assert expand(~c"from_metadata.s", env, metadata) == - [ - %{ - call?: true, - name: "some", - origin: "MyStruct", - subtype: :struct_field, - type: :field, - type_spec: "integer" - } - ] - - assert expand(~c"var.h", env, metadata) == - [ - %{ - call?: true, - name: "hour", - origin: "DateTime", - subtype: :struct_field, - type: :field, - type_spec: "Calendar.hour()" - } - ] - - assert expand(~c"xxxx.h", env, metadata) == - [ - %{ - call?: true, - name: "hour", - origin: "DateTime", - subtype: :struct_field, - type: :field, - type_spec: "Calendar.hour()" - } - ] - end - - test "map atom key completion is supported on attributes" do - env = %Env{ - attributes: [ - %AttributeInfo{ - name: :map, - type: {:map, [foo: 1, bar_1: 23, bar_2: 14], nil} - } - ] - } - - assert expand(~c"@map.f", env) == - [ - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert [_ | _] = expand(~c"@map.b", env) - - assert expand(~c"@map.bar_", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"@map.c", env) == [] - - assert expand(~c"@map.", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"@map.foo", env) == [ - %{ - call?: true, - name: "foo", - origin: nil, - subtype: :map_key, - type: :field, - type_spec: nil - } - ] - end - - test "nested map atom key completion is supported" do - env = %Env{ - vars: [ - %VarInfo{ - name: :map, - type: - {:map, - [ - nested: - {:map, - [ - deeply: - {:map, - [ - foo: 1, - bar_1: 23, - bar_2: 14, - mod: {:atom, String}, - num: 1 - ], nil} - ], nil} - ], nil} - } - ] - } - - assert expand(~c"map.nested.deeply.f", env) == - [ - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert [_ | _] = expand(~c"map.nested.deeply.b", env) - - assert expand(~c"map.nested.deeply.bar_", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"map.nested.deeply.", env) == - [ - %{ - name: "bar_1", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "bar_2", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "foo", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "mod", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - }, - %{ - name: "num", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert [_ | _] = expand(~c"map.nested.deeply.mod.print", env) - - assert expand(~c"map.nested", env) == - [ - %{ - name: "nested", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"map.nested.deeply", env) == - [ - %{ - name: "deeply", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"map.nested.deeply.foo", env) == [ - %{ - call?: true, - name: "foo", - origin: nil, - subtype: :map_key, - type: :field, - type_spec: nil - } - ] - - assert expand(~c"map.nested.deeply.c", env) == [] - assert expand(~c"map.a.b.c.f", env) == [] - end - - test "map string key completion is not supported" do - env = %Env{ - vars: [ - %VarInfo{ - name: :map, - type: {:map, [{"foo", 124}], nil} - } - ] - } - - assert expand(~c"map.f", env) == [] - end - - test "autocompletion off a bound variable only works for modules and maps" do - env = %Env{ - vars: [ - %VarInfo{ - name: :map, - type: {:map, [nested: {:map, [num: 23], nil}], nil} - } - ] - } - - assert expand(~c"num.print", env) == [] - assert expand(~c"map.nested.num.f", env) == [] - assert expand(~c"map.nested.num.key.f", env) == [] - end - - test "autocomplete map fields from call binding" do - env = %Env{ - vars: [ - %VarInfo{ - name: :map, - type: {:map, [{:foo, {:atom, String}}], nil} - }, - %VarInfo{ - name: :call, - type: {:call, {:variable, :map}, :foo, []} - } - ] - } - - assert [_ | _] = expand(~c"call.print", env) - end - - test "autocomplete call return binding" do - env = %Env{ - vars: [ - %VarInfo{ - name: :call, - type: {:call, {:atom, DateTime}, :utc_now, []} - } - ] - } - - assert [_ | _] = expand(~c"call.ho", env) - assert [_ | _] = expand(~c"DateTime.utc_now.ho", env) - # TODO expand expression {:dot, :expr, []} {:dot, :expr, ~c"ho"} on 1.15+ - # Code.cursor_context returns :none for those cases - assert [] == expand(~c"DateTime.utc_now().", env) - assert [] == expand(~c"DateTime.utc_now().ho", env) - assert [] == expand(~c"DateTime.utc_now().calendar.da", env) - end - - test "autocompletion off of unbound variables is not supported" do - assert expand(~c"other_var.f") == [] - assert expand(~c"a.b.c.d") == [] - end - - test "macro completion" do - assert [_ | _] = expand(~c"Kernel.is_") - end - - test "imports completion" do - list = expand(~c"") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - # IEX version asserts IEx.Helpers are imported - # assert list |> Enum.find(& &1.name == "h") - # assert list |> Enum.find(& &1.name == "pwd") - end - - test "imports completion in call arg" do - # local call - list = expand(~c"asd(") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - - list = expand(~c"asd(un") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - - # remote call - - list = expand(~c"Abc.asd(") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - - list = expand(~c"Abc.asd(un") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - - # local call on var - - if Version.match?(System.version(), "< 1.16.0-dev") do - assert [] == expand(~c"asd.(") - assert [] == expand(~c"@asd.(") - else - expr_suggestions = expand(~c"") - assert expr_suggestions == expand(~c"asd.(") - assert expr_suggestions == expand(~c"@asd.(") - end - - # list = expand('asd.(') - # assert is_list(list) - - # assert list |> Enum.find(&(&1.name == "unquote")) - - list = expand(~c"asd.(un") - assert is_list(list) - - assert list |> Enum.find(&(&1.name == "unquote")) - end - - test "kernel import completion" do - assert [ - %{ - args: "fields", - arity: 1, - name: "defstruct", - origin: "Kernel", - spec: "", - summary: "Defines a struct.", - type: :macro - } - ] = expand(~c"defstru") - - assert [ - %{arity: 3, name: "put_elem"}, - %{arity: 2, name: "put_in"}, - %{arity: 3, name: "put_in"} - ] = expand(~c"put_") - end - - test "variable name completion" do - env = %Env{ - vars: [ - %VarInfo{ - name: :numeral - }, - %VarInfo{ - name: :number - }, - %VarInfo{ - name: :nothing - } - ] - } - - assert expand(~c"numb", env) == [%{type: :variable, name: "number"}] - - assert expand(~c"num", env) == - [%{type: :variable, name: "number"}, %{type: :variable, name: "numeral"}] - - assert [%{type: :variable, name: "nothing"} | _] = expand(~c"no", env) - end - - test "variable name completion after pin" do - env = %Env{ - vars: [ - %VarInfo{ - name: :number - } - ] - } - - assert expand(~c"^numb", env) == [%{type: :variable, name: "number"}] - assert expand(~c"^", env) == [%{type: :variable, name: "number"}] - end - - test "attribute name completion" do - env = %Env{ - attributes: [ - %AttributeInfo{ - name: :numeral - }, - %AttributeInfo{ - name: :number - }, - %AttributeInfo{ - name: :nothing - } - ], - scope: {:some, 0} - } - - assert expand(~c"@numb", env) == [%{type: :attribute, name: "@number", summary: nil}] - - assert expand(~c"@num", env) == - [ - %{type: :attribute, name: "@number", summary: nil}, - %{type: :attribute, name: "@numeral", summary: nil} - ] - - assert expand(~c"@", env) == - [ - %{name: "@nothing", type: :attribute, summary: nil}, - %{type: :attribute, name: "@number", summary: nil}, - %{type: :attribute, name: "@numeral", summary: nil} - ] - end - - test "builtin attribute name completion" do - env_function = %Env{ - attributes: [], - scope: {:some, 0} - } - - env_module = %Env{ - attributes: [], - scope: Some.Module - } - - env_outside_module = %Env{ - attributes: [], - scope: Elixir - } - - assert expand(~c"@befo", env_function) == [] - assert expand(~c"@befo", env_outside_module) == [] - - assert expand(~c"@befo", env_module) == - [ - %{ - type: :attribute, - name: "@before_compile", - summary: "A hook that will be invoked before the module is compiled." - } - ] - end - - test "kernel special form completion" do - assert [%{name: "unquote_splicing", origin: "Kernel.SpecialForms"}] = expand(~c"unquote_spl") - end - - test "completion inside expression" do - assert [_ | _] = expand(~c"1 En") - assert [_ | _] = expand(~c"Test(En") - assert [_] = expand(~c"Test :zl") - assert [_] = expand(~c"[:zl") - assert [_] = expand(~c"{:zl") - end - - test "ampersand completion" do - assert [_ | _] = expand(~c"&Enu") - - assert [ - %{name: "all?", arity: 1}, - %{name: "all?", arity: 2}, - %{name: "any?", arity: 1}, - %{name: "any?", arity: 2}, - %{name: "at", arity: 2}, - %{name: "at", arity: 3} - ] = expand(~c"&Enum.a") - - assert [ - %{name: "all?", arity: 1}, - %{name: "all?", arity: 2}, - %{name: "any?", arity: 1}, - %{name: "any?", arity: 2}, - %{name: "at", arity: 2}, - %{name: "at", arity: 3} - ] = expand(~c"f = &Enum.a") - end - - defmodule SublevelTest.LevelA.LevelB do - end - - test "elixir completion sublevel" do - assert [%{name: "LevelA"}] = - expand(~c"ElixirSense.Providers.Suggestion.CompleteTest.SublevelTest.") - end - - defmodule MyServer do - def current_env do - %Macro.Env{aliases: [{MyList, List}, {EList, :lists}]} - end - end - - test "complete aliases of elixir modules" do - env = %Env{ - aliases: [{MyList, List}] - } - - assert [%{name: "MyList"}] = expand(~c"MyL", env) - assert [%{name: "MyList"}] = expand(~c"MyList", env) - - assert [%{arity: 1, name: "to_integer"}, %{arity: 2, name: "to_integer"}] = - expand(~c"MyList.to_integer", env) - end - - test "complete aliases of erlang modules" do - env = %Env{ - aliases: [{EList, :lists}] - } - - assert [%{name: "EList"}] = expand(~c"EL", env) - assert [%{name: "EList"}] = expand(~c"EList", env) - - assert [ - %{arity: 2, name: "map"}, - %{arity: 3, name: "mapfoldl"}, - %{arity: 3, name: "mapfoldr"} - ] = expand(~c"EList.map", env) - end - - test "complete local funs from scope module" do - env = %Env{ - module: MyModule - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {MyModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {MyModule, :my_fun_priv, nil} => %ModFunInfo{type: :defp}, - {MyModule, :my_fun_priv, 2} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}, {:other, [], nil}]] - }, - {MyModule, :my_fun_pub, nil} => %ModFunInfo{type: :def}, - {MyModule, :my_fun_pub, 1} => %ModFunInfo{type: :def, params: [[{:some, [], nil}]]}, - {MyModule, :my_macro_priv, nil} => %ModFunInfo{type: :defmacrop}, - {MyModule, :my_macro_priv, 1} => %ModFunInfo{ - type: :defmacrop, - params: [[{:some, [], nil}]] - }, - {MyModule, :my_macro_pub, nil} => %ModFunInfo{type: :defmacro}, - {MyModule, :my_macro_pub, 1} => %ModFunInfo{type: :defmacro, params: [[{:some, [], nil}]]}, - {MyModule, :my_guard_priv, nil} => %ModFunInfo{type: :defguardp}, - {MyModule, :my_guard_priv, 1} => %ModFunInfo{ - type: :defguardp, - params: [[{:some, [], nil}]] - }, - {MyModule, :my_guard_pub, nil} => %ModFunInfo{type: :defguard}, - {MyModule, :my_guard_pub, 1} => %ModFunInfo{type: :defguard, params: [[{:some, [], nil}]]}, - {MyModule, :my_delegated, nil} => %ModFunInfo{type: :defdelegate}, - {MyModule, :my_delegated, 1} => %ModFunInfo{ - type: :defdelegate, - params: [[{:some, [], nil}]] - }, - {OtherModule, nil, nil} => %ModFunInfo{}, - {OtherModule, :my_fun_pub_other, nil} => %ModFunInfo{type: :def}, - {OtherModule, :my_fun_pub_other, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - } - }, - specs: %{ - {MyModule, :my_fun_priv, 2} => %SpecInfo{ - kind: :spec, - specs: ["@spec my_fun_priv(atom, integer) :: boolean"] - } - } - } - - assert [_ | _] = expand(~c"my_f", env, metadata) - - assert [ - %{ - name: "my_fun_priv", - origin: "MyModule", - args: "some, other", - type: :function, - spec: "@spec my_fun_priv(atom, integer) :: boolean" - } - ] = expand(~c"my_fun_pr", env, metadata) - - assert [ - %{name: "my_fun_pub", origin: "MyModule", type: :function} - ] = expand(~c"my_fun_pu", env, metadata) - - assert [ - %{name: "my_macro_priv", origin: "MyModule", type: :macro} - ] = expand(~c"my_macro_pr", env, metadata) - - assert [ - %{name: "my_macro_pub", origin: "MyModule", type: :macro} - ] = expand(~c"my_macro_pu", env, metadata) - - assert [ - %{name: "my_guard_priv", origin: "MyModule", type: :macro} - ] = expand(~c"my_guard_pr", env, metadata) - - assert [ - %{name: "my_guard_pub", origin: "MyModule", type: :macro} - ] = expand(~c"my_guard_pu", env, metadata) - - assert [ - %{name: "my_delegated", origin: "MyModule", type: :function} - ] = expand(~c"my_de", env, metadata) - end - - test "complete remote funs from imported module" do - env = %Env{ - module: MyModule, - imports: [{OtherModule, []}, {Kernel, []}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {OtherModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {OtherModule, :my_fun_other_pub, nil} => %ModFunInfo{type: :def}, - {OtherModule, :my_fun_other_pub, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {OtherModule, :my_fun_other_priv, nil} => %ModFunInfo{type: :defp}, - {OtherModule, :my_fun_other_priv, 1} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}]] - } - } - } - - assert [ - %{name: "my_fun_other_pub", origin: "OtherModule", needed_import: nil} - ] = expand(~c"my_f", env, metadata) - end - - test "complete remote funs from imported module - needed import" do - env = %Env{ - module: MyModule, - imports: [{OtherModule, [only: [{:my_fun_other_pub, 1}]]}, {Kernel, []}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {OtherModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {OtherModule, :my_fun_other_pub, nil} => %ModFunInfo{type: :def}, - {OtherModule, :my_fun_other_pub, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {OtherModule, :my_fun_other_pub, 2} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {OtherModule, :my_fun_other_priv, nil} => %ModFunInfo{type: :defp}, - {OtherModule, :my_fun_other_priv, 1} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}]] - } - } - } - - assert [ - %{name: "my_fun_other_pub", origin: "OtherModule", needed_import: nil}, - %{ - name: "my_fun_other_pub", - origin: "OtherModule", - needed_import: {"OtherModule", {"my_fun_other_pub", 2}} - } - ] = expand(~c"my_f", env, metadata) - end - - test "complete remote funs" do - env = %Env{ - module: MyModule - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {Some.OtherModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {Some.OtherModule, :my_fun_other_pub, nil} => %ModFunInfo{type: :def}, - {Some.OtherModule, :my_fun_other_pub, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {Some.OtherModule, :my_fun_other_priv, nil} => %ModFunInfo{type: :defp}, - {Some.OtherModule, :my_fun_other_priv, 1} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}]] - } - } - } - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"Some.OtherModule.my_f", env, metadata) - end - - test "complete remote funs from aliased module" do - env = %Env{ - module: MyModule, - aliases: [{S, Some.OtherModule}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {Some.OtherModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {Some.OtherModule, :my_fun_other_pub, nil} => %ModFunInfo{type: :def}, - {Some.OtherModule, :my_fun_other_pub, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {Some.OtherModule, :my_fun_other_priv, nil} => %ModFunInfo{type: :defp}, - {Some.OtherModule, :my_fun_other_priv, 1} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}]] - } - } - } - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"S.my_f", env, metadata) - end - - test "complete remote funs from injected module" do - env = %Env{ - module: MyModule, - attributes: [ - %AttributeInfo{ - name: :get_module, - type: - {:call, {:atom, Application}, :get_env, - [atom: :elixir_sense, atom: :an_attribute, atom: Some.OtherModule]} - }, - %AttributeInfo{ - name: :compile_module, - type: - {:call, {:atom, Application}, :compile_env, - [atom: :elixir_sense, atom: :an_attribute, atom: Some.OtherModule]} - }, - %AttributeInfo{ - name: :fetch_module, - type: - {:call, {:atom, Application}, :fetch_env!, - [atom: :elixir_sense, atom: :other_attribute]} - }, - %AttributeInfo{ - name: :compile_bang_module, - type: - {:call, {:atom, Application}, :compile_env!, - [atom: :elixir_sense, atom: :other_attribute]} - } - ] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {Some.OtherModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {Some.OtherModule, :my_fun_other_pub, nil} => %ModFunInfo{type: :def}, - {Some.OtherModule, :my_fun_other_pub, 1} => %ModFunInfo{ - type: :def, - params: [[{:some, [], nil}]] - }, - {Some.OtherModule, :my_fun_other_priv, nil} => %ModFunInfo{type: :defp}, - {Some.OtherModule, :my_fun_other_priv, 1} => %ModFunInfo{ - type: :defp, - params: [[{:some, [], nil}]] - } - } - } - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"@get_module.my_f", env, metadata) - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"@compile_module.my_f", env, metadata) - - Application.put_env(:elixir_sense, :other_attribute, Some.OtherModule) - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"@fetch_module.my_f", env, metadata) - - assert [ - %{name: "my_fun_other_pub", origin: "Some.OtherModule"} - ] = expand(~c"@compile_bang_module.my_f", env, metadata) - after - Application.delete_env(:elixir_sense, :other_attribute) - end - - test "complete modules" do - env = %Env{ - module: MyModule, - aliases: [{MyAlias, Some.OtherModule.Nested}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {Some.OtherModule, nil, nil} => %ModFunInfo{type: :defmodule} - } - } - - assert [%{name: "Some", full_name: "Some", type: :module}] = expand(~c"Som", env, metadata) - - assert [%{name: "OtherModule", full_name: "Some.OtherModule", type: :module}] = - expand(~c"Some.", env, metadata) - - assert [%{name: "MyAlias", full_name: "Some.OtherModule.Nested", type: :module}] = - expand(~c"MyA", env, metadata) - end - - test "alias rules" do - env = %Env{ - module: MyModule, - aliases: [{Keyword, MyKeyword}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {MyKeyword, nil, nil} => %ModFunInfo{type: :defmodule}, - {MyKeyword, :values1, 0} => %ModFunInfo{type: :def, params: [[]]}, - {MyKeyword, :values1, nil} => %ModFunInfo{type: :def} - } - } - - assert [ - %{ - name: "values1", - type: :function, - args: "", - arity: 0, - origin: "MyKeyword", - spec: "", - summary: "" - } - ] = expand(~c"Keyword.valu", env, metadata) - - assert [%{name: "values", type: :function, arity: 1, origin: "Keyword"}] = - expand(~c"Elixir.Keyword.valu", env, metadata) - end - - defmodule MyStruct do - defstruct [:my_val, :some_map, :a_mod, :str, :unknown_str] - end - - test "completion for struct names" do - assert [%{name: "MyStruct"}] = - expand(~c"%ElixirSense.Providers.Suggestion.CompleteTest.MyStr") - - assert entries = expand(~c"%") - assert entries |> Enum.any?(&(&1.name == "URI")) - - assert [%{name: "MyStruct"}] = expand(~c"%ElixirSense.Providers.Suggestion.CompleteTest.") - - env = %Env{ - aliases: [{MyDate, Date}] - } - - entries = expand(~c"%My", env, %Metadata{}, required_alias: true) - assert Enum.any?(entries, &(&1.name == "MyDate" and &1.subtype == :struct)) - end - - @tag requires_elixir_1_14: true - test "completion for struct names with __MODULE__" do - assert [%{name: "__MODULE__"}] = expand(~c"%__MODU", %Env{module: Date.Range}) - assert [%{name: "Range"}] = expand(~c"%__MODULE__.Ra", %Env{module: Date}) - end - - @tag requires_elixir_1_14: true - test "completion for struct attributes" do - assert [%{name: "@my_attr"}] = - expand(~c"%@my", %Env{ - attributes: [ - %AttributeInfo{ - name: :my_attr, - type: {:atom, Date} - } - ], - scope: MyMod - }) - - assert [%{name: "Range"}] = - expand(~c"%@my_attr.R", %Env{ - attributes: [ - %AttributeInfo{ - name: :my_attr, - type: {:atom, Date} - } - ], - scope: MyMod - }) - end - - # handled elsewhere - # TODO consider moving struct key completion here after elixir 1.13+ is required - # test "completion for struct keys" do - # assert {:yes, '', entries} = expand('%URI{') - # assert 'path:' in entries - # assert 'query:' in entries - - # assert {:yes, '', entries} = expand('%URI{path: "foo",') - # assert 'path:' not in entries - # assert 'query:' in entries - - # assert {:yes, 'ry: ', []} = expand('%URI{path: "foo", que') - # assert {:no, [], []} = expand('%URI{path: "foo", unkno') - # assert {:no, [], []} = expand('%Unkown{path: "foo", unkno') - # end - - test "completion for struct keys" do - env = %Env{ - vars: [ - %VarInfo{ - name: :struct, - type: - {:struct, - [ - a_mod: {:atom, String}, - some_map: {:map, [asdf: 1], nil}, - str: {:struct, [], {:atom, MyStruct}, nil}, - unknown_str: {:struct, [abc: nil], nil, nil} - ], {:atom, MyStruct}, nil} - } - ] - } - - assert expand(~c"struct.my", env) == - [ - %{ - name: "my_val", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - } - ] - - assert expand(~c"struct.some_m", env) == - [ - %{ - name: "some_map", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - } - ] - - assert expand(~c"struct.some_map.", env) == - [ - %{ - name: "asdf", - subtype: :map_key, - type: :field, - origin: nil, - call?: true, - type_spec: nil - } - ] - - assert expand(~c"struct.str.", env) == - [ - %{ - name: "__struct__", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - }, - %{ - name: "a_mod", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - }, - %{ - name: "my_val", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - }, - %{ - name: "some_map", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - }, - %{ - name: "str", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - }, - %{ - name: "unknown_str", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - } - ] - - assert expand(~c"struct.str", env) == - [ - %{ - name: "str", - subtype: :struct_field, - type: :field, - origin: "ElixirSense.Providers.Suggestion.CompleteTest.MyStruct", - call?: true, - type_spec: nil - } - ] - - assert expand(~c"struct.unknown_str.", env) == - [ - %{ - call?: true, - name: "__struct__", - origin: nil, - subtype: :struct_field, - type: :field, - type_spec: nil - }, - %{ - call?: true, - name: "abc", - origin: nil, - subtype: :struct_field, - type: :field, - type_spec: nil - } - ] - end - - test "ignore invalid Elixir module literals" do - defmodule :"ElixirSense.Providers.Suggestion.CompleteTest.Unicodé", do: nil - assert expand(~c"ElixirSense.Providers.Suggestion.CompleteTest.Unicod") == [] - after - :code.purge(:"ElixirSense.Providers.Suggestion.CompleteTest.Unicodé") - :code.delete(:"ElixirSense.Providers.Suggestion.CompleteTest.Unicodé") - end - - test "complete built in functions on non local calls" do - assert [] = expand(~c"module_") - assert [] = expand(~c"__in") - - assert [] = expand(~c"Elixir.mo") - assert [] = expand(~c"Elixir.__in") - - assert [ - %{ - name: "module_info", - type: :function, - arity: 0, - spec: - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]" - }, - %{ - name: "module_info", - type: :function, - arity: 1, - spec: - "@spec module_info(:module) :: atom\n@spec module_info(:attributes | :compile) :: [{atom, term}]\n@spec module_info(:md5) :: binary\n@spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}]\n@spec module_info(:native) :: boolean" - } - ] = expand(~c"String.mo") - - assert [ - %{ - name: "__info__", - type: :function, - spec: - "@spec __info__(:attributes) :: keyword()\n@spec __info__(:compile) :: [term()]\n@spec __info__(:functions) :: [{atom, non_neg_integer}]\n@spec __info__(:macros) :: [{atom, non_neg_integer}]\n@spec __info__(:md5) :: binary()\n@spec __info__(:module) :: module()" - } - ] = expand(~c"String.__in") - - assert [ - %{ - name: "module_info", - type: :function, - arity: 0, - spec: - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]" - }, - %{ - name: "module_info", - type: :function, - arity: 1, - spec: - "@spec module_info(:module) :: atom\n@spec module_info(:attributes | :compile) :: [{atom, term}]\n@spec module_info(:md5) :: binary\n@spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}]\n@spec module_info(:native) :: boolean" - } - ] = expand(~c":ets.module_") - - assert [] = expand(~c":ets.__in") - - env = %Env{ - module: MyModule, - aliases: [{MyAlias, Some.OtherModule.Nested}] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {MyModule, nil, nil} => %ModFunInfo{type: :defmodule}, - {MyModule, :module_info, nil} => %ModFunInfo{type: :def}, - {MyModule, :module_info, 0} => %ModFunInfo{type: :def, params: [[]]}, - {MyModule, :module_info, 1} => %ModFunInfo{type: :def, params: [[{:atom, [], nil}]]}, - {MyModule, :__info__, nil} => %ModFunInfo{type: :def}, - {MyModule, :__info__, 1} => %ModFunInfo{type: :def, params: [[{:atom, [], nil}]]} - } - } - - assert [] = expand(~c"module_", env, metadata) - assert [] = expand(~c"__in", env, metadata) - - assert [ - %{ - name: "module_info", - type: :function, - arity: 0, - spec: - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]" - }, - %{ - name: "module_info", - type: :function, - arity: 1, - spec: - "@spec module_info(:module) :: atom\n@spec module_info(:attributes | :compile) :: [{atom, term}]\n@spec module_info(:md5) :: binary\n@spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}]\n@spec module_info(:native) :: boolean" - } - ] = expand(~c"MyModule.mo", env, metadata) - - assert [ - %{ - name: "__info__", - type: :function, - spec: - "@spec __info__(:attributes) :: keyword()\n@spec __info__(:compile) :: [term()]\n@spec __info__(:functions) :: [{atom, non_neg_integer}]\n@spec __info__(:macros) :: [{atom, non_neg_integer}]\n@spec __info__(:md5) :: binary()\n@spec __info__(:module) :: module()" - } - ] = expand(~c"MyModule.__in", env, metadata) - end - - test "complete build in behaviour functions" do - assert [] = expand(~c"Elixir.beh") - - assert [ - %{ - name: "behaviour_info", - type: :function, - arity: 1, - spec: - "@spec behaviour_info(:callbacks | :optional_callbacks) :: [{atom, non_neg_integer}]" - } - ] = expand(~c":gen_server.beh") - - assert [ - %{ - name: "behaviour_info", - type: :function, - arity: 1, - spec: - "@spec behaviour_info(:callbacks | :optional_callbacks) :: [{atom, non_neg_integer}]" - } - ] = expand(~c"GenServer.beh") - end - - test "complete build in protocol functions" do - assert [] = expand(~c"Elixir.__pr") - - assert [ - %{ - name: "__protocol__", - type: :function, - arity: 1, - spec: - "@spec __protocol__(:module) :: module\n@spec __protocol__(:functions) :: [{atom, non_neg_integer}]\n@spec __protocol__(:consolidated?) :: boolean\n@spec __protocol__(:impls) :: :not_consolidated | {:consolidated, [module]}" - } - ] = expand(~c"Enumerable.__pro") - - assert [_, _] = expand(~c"Enumerable.imp") - - assert [ - %{ - name: "impl_for!", - type: :function, - arity: 1, - spec: "@spec impl_for!(term) :: atom" - } - ] = expand(~c"Enumerable.impl_for!") - end - - test "complete build in protocol implementation functions" do - assert [] = expand(~c"Elixir.__im") - - assert [ - %{ - name: "__impl__", - type: :function, - arity: 1, - spec: "@spec __impl__(:for | :target | :protocol) :: module" - } - ] = expand(~c"Enumerable.List.__im") - end - - test "complete build in struct functions" do - assert [] = expand(~c"Elixir.__str") - - assert [ - %{ - name: "__struct__", - type: :function, - arity: 0, - spec: - "@spec __struct__() :: %{required(:__struct__) => module, optional(any) => any}" - }, - %{ - name: "__struct__", - type: :function, - arity: 1, - spec: - "@spec __struct__(keyword) :: %{required(:__struct__) => module, optional(any) => any}" - } - ] = expand(~c"ElixirSenseExample.ModuleWithStruct.__str") - end - - test "complete build in exception functions" do - assert [] = expand(~c"Elixir.mes") - - assert [ - %{ - name: "message", - type: :function, - arity: 1, - spec: "@callback message(t()) :: String.t()" - } - ] = expand(~c"ArgumentError.mes") - - assert [] = expand(~c"Elixir.exce") - - assert [ - %{ - name: "exception", - type: :function, - arity: 1, - spec: "@callback exception(term()) :: t()" - } - ] = expand(~c"ArgumentError.exce") - - assert [] = expand(~c"Elixir.bla") - - assert [ - %{name: "blame", type: :function, arity: 2} - ] = expand(~c"ArgumentError.bla") - end - - @tag requires_otp_23: true - test "complete build in :erlang functions" do - assert [ - %{arity: 2, name: "open_port", origin: ":erlang"}, - %{ - arity: 2, - name: "or", - spec: "@spec boolean() or boolean() :: boolean()", - type: :function, - args: "boolean, boolean", - origin: ":erlang", - summary: "" - }, - %{ - args: "term, term", - arity: 2, - name: "orelse", - origin: ":erlang", - spec: "", - summary: "", - type: :function - } - ] = expand(~c":erlang.or") - - assert [ - %{ - arity: 2, - name: "and", - spec: "@spec boolean() and boolean() :: boolean()", - type: :function, - args: "boolean, boolean", - origin: ":erlang", - summary: "" - }, - %{ - args: "term, term", - arity: 2, - name: "andalso", - origin: ":erlang", - spec: "", - summary: "", - type: :function - }, - %{arity: 2, name: "append", origin: ":erlang"}, - %{arity: 2, name: "append_element", origin: ":erlang"} - ] = expand(~c":erlang.and") - end - - test "provide doc and specs for erlang functions" do - assert [ - %{ - arity: 1, - name: "whereis", - origin: ":erlang", - spec: "@spec whereis(regName) :: pid() | port() | :undefined when regName: atom()", - type: :function - } - ] = expand(~c":erlang.where") - - assert [ - %{ - arity: 1, - name: "cancel_timer", - spec: "@spec cancel_timer(timerRef) :: result" <> _, - type: :function, - args: "timerRef", - origin: ":erlang", - summary: summary1 - }, - %{ - arity: 2, - name: "cancel_timer", - spec: "@spec cancel_timer(timerRef, options) :: result | :ok" <> _, - type: :function, - args: "timerRef, options", - origin: ":erlang", - summary: summary2 - } - ] = expand(~c":erlang.cancel_time") - - if ExUnitConfig.erlang_eep48_supported() do - assert "Cancels a timer\\." <> _ = summary1 - assert "Cancels a timer that has been created by" <> _ = summary2 - end - end - - test "provide doc and specs for erlang functions with args from typespec" do - if String.to_integer(System.otp_release()) >= 26 do - assert [ - %{ - name: "handle_call", - args_list: ["call", "from", "state"] - }, - %{ - name: "handle_cast", - args_list: ["tuple", "state"] - }, - %{ - name: "handle_info", - args_list: ["term", "state"] - } - ] = expand(~c":pg.handle_") - else - if String.to_integer(System.otp_release()) >= 23 do - assert [_, _, _] = expand(~c":pg.handle_") - else - assert [] = expand(~c":pg.handle_") - end - end - end - - test "complete after ! operator" do - assert [%{name: "is_binary"}] = expand(~c"!is_bina") - end - - test "correctly find subtype and doc for modules that have submodule" do - assert [ - %{ - name: "File", - full_name: "File", - type: :module, - metadata: %{}, - subtype: nil, - summary: "This module contains functions to manipulate files." - } - ] = expand(~c"Fi") |> Enum.filter(&(&1.name == "File")) - end - - test "complete only struct modules after %" do - assert list = expand(~c"%") - refute Enum.any?(list, &(&1.type != :module)) - assert Enum.any?(list, &(&1.name == "ArithmeticError")) - assert Enum.any?(list, &(&1.name == "URI")) - refute Enum.any?(list, &(&1.name == "File")) - refute Enum.any?(list, &(&1.subtype not in [:struct, :exception])) - - assert [_ | _] = expand(~c"%Fi") - assert list = expand(~c"%File.") - assert Enum.any?(list, &(&1.name == "CopyError")) - refute Enum.any?(list, &(&1.type != :module)) - refute Enum.any?(list, &(&1.subtype not in [:struct, :exception])) - end - - test "complete modules and local funs after &" do - assert list = expand(~c"&") - assert Enum.any?(list, &(&1.type == :module)) - assert Enum.any?(list, &(&1.type == :function)) - refute Enum.any?(list, &(&1.type not in [:function, :module, :macro])) - end - - test "complete Kernel.SpecialForms macros with fixed argument list" do - assert [%{args_list: ["term"]}] = expand(~c"Kernel.SpecialForms.fn") - end - - test "macros from not required modules should add needed_require" do - assert [ - %{ - name: "info", - arity: 1, - type: :macro, - origin: "Logger", - needed_require: "Logger", - visibility: :public - }, - _ - ] = expand(~c"Logger.inf") - - assert [ - %{ - name: "info", - arity: 1, - type: :macro, - origin: "Logger", - needed_require: nil, - visibility: :public - }, - _ - ] = expand(~c"Logger.inf", %Env{requires: [Logger]}) - end - - test "macros from not required metadata modules should add needed_require" do - macro_info = %ElixirSense.Core.State.ModFunInfo{ - type: :defmacro, - params: [[:_]] - } - - metadata = %Metadata{ - mods_funs_to_positions: %{ - {MyModule, nil, nil} => %ElixirSense.Core.State.ModFunInfo{}, - {MyModule, :info, nil} => macro_info, - {MyModule, :info, 1} => macro_info - } - } - - assert [ - %{ - name: "info", - arity: 1, - type: :macro, - origin: "MyModule", - needed_require: "MyModule", - visibility: :public - } - ] = expand(~c"MyModule.inf", %Env{requires: []}, metadata) - - assert [ - %{ - name: "info", - arity: 1, - type: :macro, - origin: "MyModule", - needed_require: nil, - visibility: :public - } - ] = expand(~c"MyModule.inf", %Env{requires: [MyModule]}, metadata) - end - - test "macros from Kernel.SpecialForms should not add needed_require" do - assert [ - %{ - name: "unquote", - arity: 1, - type: :macro, - origin: "Kernel.SpecialForms", - needed_require: nil, - visibility: :public - }, - _ - ] = expand(~c"unquote", %Env{requires: []}) - end - - @tag requires_elixir_1_14: true - test "Application.compile_env classified as macro" do - assert [ - %{ - name: "compile_env", - arity: 2, - type: :macro, - origin: "Application", - needed_require: "Application" - }, - %{ - name: "compile_env", - arity: 3, - type: :macro, - origin: "Application", - needed_require: "Application" - }, - %{ - name: "compile_env", - arity: 4, - type: :function, - origin: "Application", - needed_require: nil - }, - %{ - name: "compile_env!", - arity: 2, - type: :macro, - origin: "Application", - needed_require: "Application" - }, - %{ - name: "compile_env!", - arity: 3, - type: :function, - origin: "Application", - needed_require: nil - } - ] = expand(~c"Application.compile_e") - end -end diff --git a/test/elixir_sense/providers/suggestion/matcher_test.exs b/test/elixir_sense/providers/suggestion/matcher_test.exs deleted file mode 100644 index e66f6e99..00000000 --- a/test/elixir_sense/providers/suggestion/matcher_test.exs +++ /dev/null @@ -1,4 +0,0 @@ -defmodule ElixirSense.Providers.Suggestion.MatcherTest do - use ExUnit.Case, async: true - doctest ElixirSense.Providers.Suggestion.Matcher -end diff --git a/test/elixir_sense/providers/suggestion_test.exs b/test/elixir_sense/providers/suggestion_test.exs deleted file mode 100644 index 2e214fbb..00000000 --- a/test/elixir_sense/providers/suggestion_test.exs +++ /dev/null @@ -1,328 +0,0 @@ -defmodule ElixirSense.Providers.SuggestionTest do - use ExUnit.Case, async: true - alias ElixirSense.Providers.Suggestion - alias ElixirSense.Core.State.StructInfo - alias ElixirSense.Core.Metadata - - doctest Suggestion - - defmodule MyModule do - def say_hi, do: true - end - - @env %ElixirSense.Core.State.Env{ - module: SomeModule, - scope: SomeModule - } - - @env_func %ElixirSense.Core.State.Env{ - module: SomeModule, - scope: {:func, 0} - } - - @cursor_context %{text_before: "", text_after: "", cursor_position: {1, 1}} - @module_store %ElixirSense.Core.ModuleStore{} - - test "find definition of built-in functions" do - result = - Suggestion.find( - "ElixirSenseExample.EmptyModule.", - @env, - %Metadata{}, - @cursor_context, - @module_store - ) - - assert %{ - args: "atom", - args_list: ["atom"], - arity: 1, - def_arity: 1, - name: "__info__", - origin: "ElixirSenseExample.EmptyModule", - spec: - "@spec __info__(:attributes) :: keyword()\n@spec __info__(:compile) :: [term()]\n@spec __info__(:functions) :: [{atom, non_neg_integer}]\n@spec __info__(:macros) :: [{atom, non_neg_integer}]\n@spec __info__(:md5) :: binary()\n@spec __info__(:module) :: module()", - summary: "Provides runtime information" <> _, - type: :function, - metadata: %{builtin: true}, - snippet: nil, - visibility: :public - } = Enum.at(result, 0) - - assert %{ - args: "", - args_list: [], - arity: 0, - def_arity: 0, - name: "module_info", - origin: "ElixirSenseExample.EmptyModule", - spec: - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]", - summary: "The `module_info/0` function" <> _, - type: :function, - metadata: %{builtin: true}, - snippet: nil, - visibility: :public - } = Enum.at(result, 1) - - assert %{ - args: "key", - args_list: ["key"], - arity: 1, - def_arity: 1, - name: "module_info", - origin: "ElixirSenseExample.EmptyModule", - spec: - "@spec module_info(:module) :: atom\n@spec module_info(:attributes | :compile) :: [{atom, term}]\n@spec module_info(:md5) :: binary\n@spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}]\n@spec module_info(:native) :: boolean", - summary: "The call `module_info(Key)`" <> _, - type: :function, - metadata: %{builtin: true}, - snippet: nil, - visibility: :public - } = Enum.at(result, 2) - end - - test "return completion candidates for 'List.del'" do - assert [ - %{ - args: "list," <> _, - arity: 2, - name: "delete", - origin: "List", - spec: "@spec delete(" <> _, - summary: "Deletes the given" <> _, - type: :function - }, - %{ - args: "list, index", - arity: 2, - name: "delete_at", - origin: "List", - spec: "@spec delete_at(list(), integer()) :: list()", - summary: "Produces a new list by " <> _, - type: :function - } - ] = Suggestion.find("List.del", @env, %Metadata{}, @cursor_context, @module_store) - end - - test "return completion candidates for module with alias" do - assert [ - %{ - args: "list," <> _, - arity: 2, - name: "delete", - origin: "List", - spec: "@spec delete(" <> _, - summary: "Deletes the given " <> _, - type: :function - }, - %{ - args: "list, index", - arity: 2, - name: "delete_at", - origin: "List", - spec: "@spec delete_at(list(), integer()) :: list()", - summary: "Produces a new list " <> _, - type: :function - } - ] = - Suggestion.find( - "MyList.del", - %{@env | aliases: [{MyList, List}]}, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "return completion candidates for functions from import" do - assert [ - %{ - args: "", - args_list: [], - arity: 0, - def_arity: 0, - name: "say_hi", - origin: "ElixirSense.Providers.SuggestionTest.MyModule", - spec: "", - summary: "", - type: :function, - metadata: %{}, - snippet: nil, - visibility: :public - } - ] = - Suggestion.find( - "say", - %{@env | imports: [{MyModule, []}]}, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "local calls should not return built-in functions" do - list = - Suggestion.find( - # Trying to find module_info - "module_", - @env, - %Metadata{}, - @cursor_context, - @module_store - ) - |> Enum.filter(fn item -> item.type in [:function] end) - - assert list == [] - end - - test "empty hint should not return built-in functions" do - suggestions_names = - Suggestion.find("", @env, %Metadata{}, @cursor_context, @module_store) - |> Enum.filter(&Map.has_key?(&1, :name)) - |> Enum.map(& &1.name) - - refute "module_info" in suggestions_names - end - - # TODO change that to only output max arity - test "a function with default args generate multiple derived entries with same info, except arity" do - assert [ - %{ - arity: 1, - def_arity: 2, - name: "all?", - summary: "all?/2 docs", - type: :function - }, - %{ - arity: 2, - def_arity: 2, - name: "all?", - summary: "all?/2 docs", - type: :function - } - ] = - Suggestion.find( - "ElixirSenseExample.FunctionsWithTheSameName.all", - @env, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "functions with the same name but different arities generates independent entries" do - assert [ - %{ - arity: 1, - def_arity: 1, - name: "concat", - summary: "concat/1 docs", - type: :function - }, - %{ - arity: 2, - def_arity: 2, - name: "concat", - summary: "concat/2 docs", - type: :function - } - ] = - Suggestion.find( - "ElixirSenseExample.FunctionsWithTheSameName.conca", - @env, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - defmodule MyStruct do - defstruct [:my_val] - end - - test "return completion candidates for struct starting with %" do - assert [%{type: :module, name: "MyStruct"} | _] = - Suggestion.find( - "%ElixirSense.Providers.SuggestionTest.MyStr", - @env_func, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "return completion candidates for &func" do - assert [%{type: :function, name: "all?", origin: "Enum"} | _] = - Suggestion.find( - "f = &Enum.al", - @env_func, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "do not return completion candidates for unknown modules" do - assert [] = - Suggestion.find( - "x.Foo.get_by", - @env_func, - %Metadata{}, - @cursor_context, - @module_store - ) - end - - test "return completion candidates for metadata modules" do - assert [%{type: :function, name: "my_func"} | _] = - Suggestion.find( - "my_f", - @env_func, - %Metadata{ - mods_funs_to_positions: %{ - {SomeModule, nil, nil} => %ElixirSense.Core.State.ModFunInfo{type: :defmodule}, - {SomeModule, :my_func, nil} => %ElixirSense.Core.State.ModFunInfo{type: :defp}, - {SomeModule, :my_func, 1} => %ElixirSense.Core.State.ModFunInfo{ - type: :defp, - params: [[[:a, [], nil]]] - } - } - }, - @cursor_context, - @module_store - ) - - assert [%{type: :module, name: "SomeModule"} | _] = - Suggestion.find( - "So", - @env_func, - %Metadata{ - mods_funs_to_positions: %{ - {SomeModule, nil, nil} => %ElixirSense.Core.State.ModFunInfo{type: :defmodule} - } - }, - @cursor_context, - @module_store - ) - end - - test "return completion candidates for metadata structs" do - assert [ - %{name: "str_field", origin: "SomeModule", type: :field} - ] = - Suggestion.find( - "str_", - @env_func, - %Metadata{ - structs: %{SomeModule => %StructInfo{type: :defstruct, fields: [str_field: 1]}}, - mods_funs_to_positions: %{ - {SomeModule, nil, nil} => %ElixirSense.Core.State.ModFunInfo{type: :defmodule} - } - }, - %{text_before: "%SomeModule{st", text_after: "", cursor_position: {1, 1}}, - @module_store - ) - end -end diff --git a/test/elixir_sense/references_test.exs b/test/elixir_sense/references_test.exs deleted file mode 100644 index 3303bcb2..00000000 --- a/test/elixir_sense/references_test.exs +++ /dev/null @@ -1,1815 +0,0 @@ -defmodule ElixirSense.Providers.ReferencesTest do - use ExUnit.Case, async: true - alias ElixirSense.Core.References.Tracer - alias ElixirSense.Core.Source - - setup_all do - {:ok, _} = Tracer.start_link() - - Code.compiler_options( - tracers: [Tracer], - ignore_module_conflict: true, - parser_options: [columns: true] - ) - - Code.compile_file("./test/support/modules_with_references.ex") - Code.compile_file("./test/support/module_with_builtin_type_shadowing.ex") - Code.compile_file("./test/support/subscriber.ex") - Code.compile_file("./test/support/functions_with_default_args.ex") - - trace = Tracer.get() - - %{trace: trace} - end - - test "finds reference to local function shadowing builtin type", %{trace: trace} do - buffer = """ - defmodule B.Callee do - def fun() do - # ^ - :ok - end - def my_fun() do - :ok - end - end - """ - - references = ElixirSense.references(buffer, 2, 8, trace) - - assert [ - %{ - range: range_1, - uri: "test/support/module_with_builtin_type_shadowing.ex" - } - ] = references - - assert range_1 == %{start: %{column: 14, line: 4}, end: %{column: 17, line: 4}} - end - - test "find references with cursor over a function call", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee1.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{range: %{end: %{column: 62, line: 3}, start: %{column: 58, line: 3}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_3 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - end - - test "find references with cursor over a function definition", %{trace: trace} do - buffer = """ - defmodule ElixirSense.Providers.ReferencesTest.Modules.Callee1 do - def func() do - # ^ - IO.puts "" - end - def func(par1) do - # ^ - IO.puts par1 - end - end - """ - - references = ElixirSense.references(buffer, 2, 10, trace) - - assert [ - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_3 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - - references = ElixirSense.references(buffer, 6, 10, trace) - - assert [ - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - } - ] = references - - assert range_1 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - assert range_2 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - end - - test "find references with cursor over a function definition with default arg", %{trace: trace} do - buffer = """ - defmodule ElixirSenseExample.Subscription do - def check(resource, models, user, opts \\\\ []) do - IO.inspect({resource, models, user, opts}) - end - end - """ - - references = ElixirSense.references(buffer, 2, 10, trace) - - assert [ - %{ - range: range_1, - uri: "test/support/subscriber.ex" - }, - %{ - range: range_2, - uri: "test/support/subscriber.ex" - } - ] = references - - assert range_1 == %{end: %{column: 42, line: 3}, start: %{column: 37, line: 3}} - assert range_2 == %{end: %{column: 42, line: 4}, start: %{column: 37, line: 4}} - end - - test "find references with cursor over a function with arity 1", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee1.func("test") - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{range: %{end: %{column: 62, line: 3}, start: %{column: 58, line: 3}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - } - ] = references - - assert range_1 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - assert range_2 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - end - - test "find references with cursor over a function called via @attr.call", %{trace: trace} do - buffer = """ - defmodule Caller do - @attr ElixirSense.Providers.ReferencesTest.Modules.Callee1 - def func() do - @attr.func("test") - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 12, trace) - - assert [ - %{range: %{end: %{column: 15, line: 4}, start: %{column: 11, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - } - ] = references - - assert range_1 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - assert range_2 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - end - - # TODO crashes metadata builder - # test "find references with cursor over a function called via @attr.Submodule.call", %{trace: trace} do - # buffer = """ - # defmodule Caller do - # @attr ElixirSense.Providers.ReferencesTest.Modules - # def func() do - # @attr.Callee1.func("test") - # # ^ - # end - # end - # """ - - # references = ElixirSense.references(buffer, 4, 20, trace) - - # assert [ - # %{range: %{end: %{column: 15, line: 4}, start: %{column: 11, line: 4}}, uri: nil}, - # %{ - # uri: "test/support/modules_with_references.ex", - # range: range_1 - # }, - # %{ - # uri: "test/support/modules_with_references.ex", - # range: range_2 - # } - # ] = references - - # assert range_1 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - # assert range_2 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - # end - - test "find references to function called via @attr.call", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee7.func_noarg() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 68, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{end: %{column: 23, line: 114}, start: %{column: 13, line: 114}} - end - - test "find references with cursor over a function with arity 1 called via pipe operator", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def func() do - "test" - |> ElixirSense.Providers.ReferencesTest.Modules.Callee4.func_arg() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 62, trace) - - assert [ - %{ - range: %{end: %{column: 69, line: 4}, start: %{column: 61, line: 4}}, - uri: nil - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 49, column: 63}, end: %{line: 49, column: 71}} - end - - test "find references with cursor over a function with arity 1 captured", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - Task.start(&ElixirSense.Providers.ReferencesTest.Modules.Callee4.func_arg/1) - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 72, trace) - - assert [ - %{ - range: %{end: %{column: 78, line: 3}, start: %{column: 70, line: 3}}, - uri: nil - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 49, column: 63}, end: %{line: 49, column: 71}} - end - - test "find references with cursor over a function when caller uses pipe operator", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee4.func_arg("test") - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 66, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 49, column: 63}, end: %{line: 49, column: 71}} - end - - test "find references with cursor over a function when caller uses capture operator", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee4.func_no_arg() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 69, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range - } - ] = references - - if Version.match?(System.version(), ">= 1.14.0-rc.0") do - # before 1.14 tracer reports invalid positions for captures - # https://github.com/elixir-lang/elixir/issues/12023 - assert range == %{start: %{line: 55, column: 72}, end: %{line: 55, column: 83}} - end - end - - test "find references with cursor over a function with default argument when caller uses default arguments", - %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee5.func_arg() - ElixirSense.Providers.ReferencesTest.Modules.Callee5.func_arg("test") - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 66, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{range: %{end: %{column: 66, line: 4}, start: %{column: 58, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 90, column: 60}, end: %{line: 90, column: 68}} - - references = ElixirSense.references(buffer, 4, 59, trace) - - assert [ - %{ - range: %{end: %{column: 66, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{range: %{end: %{column: 66, line: 4}, start: %{column: 58, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 90, column: 60}, end: %{line: 90, column: 68}} - end - - test "find references with cursor over a function with default argument when caller does not uses default arguments", - %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee5.func_arg1("test") - ElixirSense.Providers.ReferencesTest.Modules.Callee5.func_arg1() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 67, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{range: %{end: %{column: 67, line: 4}, start: %{column: 58, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 91, column: 60}, end: %{line: 91, column: 69}} - - references = ElixirSense.references(buffer, 4, 59, trace) - - assert [ - %{ - range: %{end: %{column: 67, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{range: %{end: %{column: 67, line: 4}, start: %{column: 58, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - } - ] = references - - assert range_1 == %{start: %{line: 91, column: 60}, end: %{line: 91, column: 69}} - end - - test "find references with cursor over a module with funs with default argument", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee5.func_arg1("test") - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 55, trace) - - assert [ - %{range: %{end: %{column: 67, line: 3}, start: %{column: 58, line: 3}}, uri: nil}, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - }, - %{ - range: range_2, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{end: %{column: 68, line: 90}, start: %{column: 60, line: 90}} - assert range_2 == %{end: %{column: 69, line: 91}, start: %{column: 60, line: 91}} - end - - test "find references for the correct arity version", %{trace: trace} do - buffer = """ - defmodule Caller do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def func() do - F.my_func(1) - F.my_func(1, "") - F.my_func() - F.my_func(1, 2, 3) - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [ - %{ - range: %{end: %{column: 14, line: 4}, start: %{column: 7, line: 4}}, - uri: nil - }, - %{ - range: %{end: %{column: 14, line: 5}, start: %{column: 7, line: 5}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/functions_with_default_args.ex" - }, - %{ - range: range_2, - uri: "test/support/functions_with_default_args.ex" - } - ] = references - - assert read_line("test/support/functions_with_default_args.ex", range_1) == "my_func(1)" - - assert read_line("test/support/functions_with_default_args.ex", range_2) == - "my_func(1, \"a\")" - - references = ElixirSense.references(buffer, 5, 8, trace) - - assert [ - %{ - range: %{end: %{column: 14, line: 4}, start: %{column: 7, line: 4}}, - uri: nil - }, - %{ - range: %{end: %{column: 14, line: 5}, start: %{column: 7, line: 5}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/functions_with_default_args.ex" - }, - %{ - range: range_2, - uri: "test/support/functions_with_default_args.ex" - } - ] = references - - assert read_line("test/support/functions_with_default_args.ex", range_1) == "my_func(1)" - - assert read_line("test/support/functions_with_default_args.ex", range_2) == - "my_func(1, \"a\")" - end - - test "find references for the correct arity version in incomplete code", %{trace: trace} do - buffer = """ - defmodule Caller do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def func() do - F.my_func( - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [ - %{ - range: %{end: %{column: 14, line: 4}, start: %{column: 7, line: 4}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/functions_with_default_args.ex" - }, - %{ - range: range_2 - }, - %{ - range: range_3 - }, - %{ - range: range_4 - } - ] = references - - assert read_line("test/support/functions_with_default_args.ex", range_1) == "my_func()" - assert read_line("test/support/functions_with_default_args.ex", range_2) == "my_func(1)" - - assert read_line("test/support/functions_with_default_args.ex", range_3) == - "my_func(1, \"a\")" - - assert read_line("test/support/functions_with_default_args.ex", range_4) == "my_func(1, 2, 3)" - - buffer = """ - defmodule Caller do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def func() do - F.my_func(1 - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [ - %{ - range: %{end: %{column: 14, line: 4}, start: %{column: 7, line: 4}}, - uri: nil - }, - %{ - range: range_2, - uri: "test/support/functions_with_default_args.ex" - }, - %{ - range: range_3 - }, - %{ - range: range_4 - } - ] = references - - assert read_line("test/support/functions_with_default_args.ex", range_2) == "my_func(1)" - - assert read_line("test/support/functions_with_default_args.ex", range_3) == - "my_func(1, \"a\")" - - assert read_line("test/support/functions_with_default_args.ex", range_4) == "my_func(1, 2, 3)" - - buffer = """ - defmodule Caller do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def func() do - F.my_func(1, 2, - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [ - %{ - range: %{end: %{column: 14, line: 4}, start: %{column: 7, line: 4}}, - uri: nil - }, - %{ - range: range_4, - uri: "test/support/functions_with_default_args.ex" - } - ] = references - - assert read_line("test/support/functions_with_default_args.ex", range_4) == "my_func(1, 2, 3)" - - buffer = """ - defmodule Caller do - alias ElixirSenseExample.FunctionsWithDefaultArgs, as: F - def func() do - F.my_func(1, 2, 3, - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [] == references - end - - test "find references for the correct arity version for metadata calls", %{trace: trace} do - buffer = """ - defmodule SomeCallee do - def my_func(), do: :ok - def my_func(a, b \\\\ ""), do: :ok - def my_func(1, 2, 3), do: :ok - end - - defmodule Caller do - alias SomeCallee, as: F - def func() do - F.my_func(1) - F.my_func(1, "") - F.my_func() - F.my_func(1, 2, 3) - end - end - """ - - references = ElixirSense.references(buffer, 3, 8, trace) - - assert [ - %{ - range: %{ - end: %{column: 14, line: 10}, - start: %{column: 7, line: 10} - }, - uri: nil - }, - %{ - range: %{ - end: %{column: 14, line: 11}, - start: %{column: 7, line: 11} - }, - uri: nil - } - ] = references - - references = ElixirSense.references(buffer, 10, 8, trace) - - assert [ - %{ - range: %{ - end: %{column: 14, line: 10}, - start: %{column: 7, line: 10} - }, - uri: nil - }, - %{ - range: %{ - end: %{column: 14, line: 11}, - start: %{column: 7, line: 11} - }, - uri: nil - } - ] = references - end - - test "does not find references for private remote calls in metadata", %{trace: trace} do - buffer = """ - defmodule SomeCallee do - defp my_func(), do: :ok - defp my_func(a, b \\\\ ""), do: :ok - defp my_func(1, 2, 3), do: :ok - end - - defmodule Caller do - alias SomeCallee, as: F - def func() do - F.my_func(1) - F.my_func(1, "") - F.my_func() - F.my_func(1, 2, 3) - end - end - """ - - references = ElixirSense.references(buffer, 3, 9, trace) - - assert [] == references - - references = ElixirSense.references(buffer, 10, 8, trace) - - assert [] == references - end - - test "find references for metadata calls on variable or attribute", - %{trace: trace} do - buffer = """ - defmodule A do - @callback abc() :: any() - end - - defmodule B do - @behaviour A - - def abc, do: :ok - end - - defmodule X do - @b B - @b.abc() - def a do - b = B - b.abc() - end - end - """ - - references = ElixirSense.references(buffer, 8, 8, trace) - - assert [ - %{ - range: %{ - end: %{column: 9, line: 13}, - start: %{column: 6, line: 13} - }, - uri: nil - }, - %{ - range: %{ - end: %{column: 10, line: 16}, - start: %{column: 7, line: 16} - }, - uri: nil - } - ] = references - end - - test "find references for the correct arity version for metadata calls with cursor over module", - %{trace: trace} do - buffer = """ - defmodule SomeCallee do - def my_func(), do: :ok - def my_func(a, b \\\\ ""), do: :ok - def my_func(1, 2, 3), do: :ok - end - - defmodule Caller do - alias SomeCallee, as: F - def func() do - F.my_func(1) - F.my_func(1, "") - F.my_func() - F.my_func(1, 2, 3) - end - end - """ - - references = ElixirSense.references(buffer, 1, 13, trace) - - assert [ - %{ - range: %{ - end: %{column: 14, line: 10}, - start: %{column: 7, line: 10} - }, - uri: nil - }, - %{ - range: %{ - end: %{column: 14, line: 11}, - start: %{column: 7, line: 11} - }, - uri: nil - }, - %{range: %{end: %{column: 14, line: 12}, start: %{column: 7, line: 12}}, uri: nil}, - %{range: %{end: %{column: 14, line: 13}, start: %{column: 7, line: 13}}, uri: nil} - ] = references - - references = ElixirSense.references(buffer, 10, 8, trace) - - assert [ - %{ - range: %{ - end: %{column: 14, line: 10}, - start: %{column: 7, line: 10} - }, - uri: nil - }, - %{ - range: %{ - end: %{column: 14, line: 11}, - start: %{column: 7, line: 11} - }, - uri: nil - } - ] = references - end - - test "find references with cursor over a module with multi alias syntax", %{trace: trace} do - buffer = """ - defmodule Caller do - alias ElixirSense.Providers.ReferencesTest.Modules.Callee5 - alias ElixirSense.Providers.ReferencesTest.Modules.{Callee5} - end - """ - - references_1 = ElixirSense.references(buffer, 2, 57, trace) - references_2 = ElixirSense.references(buffer, 3, 58, trace) - - assert references_1 == references_2 - assert [_, _] = references_1 - end - - test "find references with cursor over a function call from an aliased module", %{trace: trace} do - buffer = """ - defmodule Caller do - def my() do - alias ElixirSense.Providers.ReferencesTest.Modules.Callee1, as: C - C.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 8, trace) - - assert [ - %{range: %{end: %{column: 11, line: 4}, start: %{column: 7, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_3 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - end - - test "find references with cursor over a function call from an imported module", %{trace: trace} do - buffer = """ - defmodule Caller do - def my() do - import ElixirSense.Providers.ReferencesTest.Modules.Callee1 - func() - #^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 6, trace) - - assert [ - %{range: %{end: %{column: 9, line: 4}, start: %{column: 5, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_3 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - end - - test "find references with cursor over a function call pipe from an imported module", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def my() do - import ElixirSense.Providers.ReferencesTest.Modules.Callee1 - "" |> func - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 12, trace) - - assert [ - %{range: %{end: %{column: 15, line: 4}, start: %{column: 11, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - } - ] = references - - assert range_1 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - assert range_2 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - end - - test "find references with cursor over a function capture from an imported module", %{ - trace: trace - } do - buffer = """ - defmodule Caller do - def my() do - import ElixirSense.Providers.ReferencesTest.Modules.Callee1 - &func/0 - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 4, 7, trace) - - assert [ - %{range: %{end: %{column: 10, line: 4}, start: %{column: 6, line: 4}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_3 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - end - - test "find imported references", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee3.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert references == [ - %{range: %{end: %{column: 62, line: 3}, start: %{column: 58, line: 3}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: %{start: %{line: 65, column: 47}, end: %{line: 65, column: 51}} - }, - %{ - range: %{end: %{column: 13, line: 70}, start: %{column: 9, line: 70}}, - uri: "test/support/modules_with_references.ex" - } - ] - end - - test "find references from remote calls with the function in the next line", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee3.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 59, trace) - - assert [ - %{ - range: %{end: %{column: 62, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - range: %{end: %{column: 51, line: 65}, start: %{column: 47, line: 65}}, - uri: "test/support/modules_with_references.ex" - }, - %{ - range: %{end: %{column: 13, line: 70}, start: %{column: 9, line: 70}}, - uri: "test/support/modules_with_references.ex" - } - ] = references - end - - @tag requires_elixir_1_14: true - test "find references when module with __MODULE__ special form submodule function", %{ - trace: trace - } do - buffer = """ - defmodule ElixirSense.Providers.ReferencesTest.Modules do - def func() do - __MODULE__.Callee3.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 25, trace) - - assert references == [ - %{range: %{end: %{column: 28, line: 3}, start: %{column: 24, line: 3}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: %{start: %{line: 65, column: 47}, end: %{line: 65, column: 51}} - }, - %{ - range: %{end: %{column: 13, line: 70}, start: %{column: 9, line: 70}}, - uri: "test/support/modules_with_references.ex" - } - ] - end - - @tag requires_elixir_1_14: true - test "find references when module with __MODULE__ special form submodule", %{trace: trace} do - buffer = """ - defmodule MyLocalModule do - defmodule Some do - def func() do - :ok - end - end - __MODULE__.Some.func() - end - """ - - references = ElixirSense.references(buffer, 7, 15, trace) - - assert references == [ - %{range: %{start: %{column: 19, line: 7}, end: %{column: 23, line: 7}}, uri: nil} - ] - end - - @tag requires_elixir_1_14: true - test "find references when module with __MODULE__ special form function", %{trace: trace} do - buffer = """ - defmodule ElixirSense.Providers.ReferencesTest.Modules do - def func() do - __MODULE__.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 18, trace) - - assert references == [ - %{ - uri: nil, - range: %{ - end: %{column: 20, line: 3}, - start: %{column: 16, line: 3} - } - } - ] - end - - test "find references when module with __MODULE__ special form", %{trace: trace} do - buffer = """ - defmodule MyLocalModule do - def func() do - __MODULE__.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 10, trace) - - assert references == [ - %{ - uri: nil, - range: %{ - end: %{column: 20, line: 3}, - start: %{column: 16, line: 3} - } - } - ] - end - - test "find references of variables", %{trace: trace} do - buffer = """ - defmodule MyModule do - def func do - var1 = 1 - var2 = 2 - var1 = 3 - IO.puts(var1 + var2) - end - def func4(ppp) do - - end - end - """ - - references = ElixirSense.references(buffer, 6, 13, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 5, column: 5}, end: %{line: 5, column: 9}}}, - %{uri: nil, range: %{start: %{line: 6, column: 13}, end: %{line: 6, column: 17}}} - ] - - references = ElixirSense.references(buffer, 3, 6, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 9}}} - ] - end - - test "find references of variables outside module", %{trace: trace} do - buffer = """ - bas = B - bas.abc() - """ - - references = ElixirSense.references(buffer, 1, 2, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 1, column: 1}, end: %{line: 1, column: 4}}}, - %{uri: nil, range: %{start: %{line: 2, column: 1}, end: %{line: 2, column: 4}}} - ] - end - - test "find reference for variable split across lines", %{trace: trace} do - buffer = """ - defmodule MyModule do - def func do - var1 = - 1 - var1 - end - end - """ - - references = ElixirSense.references(buffer, 3, 6, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 9}}}, - %{uri: nil, range: %{start: %{line: 5, column: 5}, end: %{line: 5, column: 9}}} - ] - end - - test "find references of variables in arguments", %{trace: trace} do - buffer = """ - defmodule MyModule do - def call(conn) do - if true do - conn - end - end - end - """ - - references = ElixirSense.references(buffer, 2, 13, trace) - - assert references == [ - %{range: %{end: %{column: 16, line: 2}, start: %{column: 12, line: 2}}, uri: nil}, - %{range: %{end: %{column: 11, line: 4}, start: %{column: 7, line: 4}}, uri: nil} - ] - end - - test "find references for a redefined variable", %{trace: trace} do - buffer = """ - defmodule MyModule do - def my_fun(var) do - var = 1 + var - - var - end - end - """ - - # `var` defined in the function header - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 14}, end: %{line: 2, column: 17}}}, - %{uri: nil, range: %{start: %{line: 3, column: 15}, end: %{line: 3, column: 18}}} - ] - - assert ElixirSense.references(buffer, 2, 14, trace) == expected_references - assert ElixirSense.references(buffer, 3, 15, trace) == expected_references - - # `var` redefined in the function body - expected_references = [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 8}}}, - %{uri: nil, range: %{start: %{line: 5, column: 5}, end: %{line: 5, column: 8}}} - ] - - assert ElixirSense.references(buffer, 3, 5, trace) == expected_references - assert ElixirSense.references(buffer, 5, 5, trace) == expected_references - end - - test "find references for a variable in a guard", %{trace: trace} do - buffer = """ - defmodule MyModule do - def my_fun(var) when is_atom(var) do - case var do - var when var > 0 -> var - end - - Enum.map([1, 2], fn x when x > 0 -> x end) - end - end - """ - - # `var` defined in the function header - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 14}, end: %{line: 2, column: 17}}}, - %{uri: nil, range: %{start: %{line: 2, column: 32}, end: %{line: 2, column: 35}}}, - %{uri: nil, range: %{start: %{line: 3, column: 10}, end: %{line: 3, column: 13}}} - ] - - assert ElixirSense.references(buffer, 2, 14, trace) == expected_references - assert ElixirSense.references(buffer, 2, 32, trace) == expected_references - assert ElixirSense.references(buffer, 3, 10, trace) == expected_references - - # `var` defined in the case clause - expected_references = [ - %{uri: nil, range: %{start: %{line: 4, column: 7}, end: %{line: 4, column: 10}}}, - %{uri: nil, range: %{start: %{line: 4, column: 16}, end: %{line: 4, column: 19}}}, - %{uri: nil, range: %{start: %{line: 4, column: 27}, end: %{line: 4, column: 30}}} - ] - - assert ElixirSense.references(buffer, 4, 7, trace) == expected_references - assert ElixirSense.references(buffer, 4, 16, trace) == expected_references - assert ElixirSense.references(buffer, 4, 27, trace) == expected_references - - # `x` - expected_references = [ - %{uri: nil, range: %{start: %{line: 7, column: 25}, end: %{line: 7, column: 26}}}, - %{uri: nil, range: %{start: %{line: 7, column: 32}, end: %{line: 7, column: 33}}}, - %{uri: nil, range: %{start: %{line: 7, column: 41}, end: %{line: 7, column: 42}}} - ] - - assert ElixirSense.references(buffer, 7, 25, trace) == expected_references - assert ElixirSense.references(buffer, 7, 32, trace) == expected_references - assert ElixirSense.references(buffer, 7, 41, trace) == expected_references - end - - test "find references for variable in inner scopes", %{trace: trace} do - buffer = """ - defmodule MyModule do - def my_fun([h | t]) do - sum = h + my_fun(t) - - if h > sum do - h + sum - else - h = my_fun(t) + sum - h - end - end - end - """ - - # `h` from the function header - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 15}, end: %{line: 2, column: 16}}}, - %{uri: nil, range: %{start: %{line: 3, column: 11}, end: %{line: 3, column: 12}}}, - %{uri: nil, range: %{start: %{line: 5, column: 8}, end: %{line: 5, column: 9}}}, - %{uri: nil, range: %{start: %{line: 6, column: 7}, end: %{line: 6, column: 8}}} - ] - - Enum.each([{2, 15}, {3, 11}, {5, 8}, {6, 7}], fn {line, column} -> - assert ElixirSense.references(buffer, line, column, trace) == expected_references - end) - - # `h` from the if-else scope - expected_references = [ - %{uri: nil, range: %{start: %{line: 8, column: 7}, end: %{line: 8, column: 8}}}, - %{uri: nil, range: %{start: %{line: 9, column: 7}, end: %{line: 9, column: 8}}} - ] - - assert ElixirSense.references(buffer, 8, 7, trace) == expected_references - assert ElixirSense.references(buffer, 9, 7, trace) == expected_references - - # `sum` - expected_references = [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 8}}}, - %{uri: nil, range: %{start: %{line: 5, column: 12}, end: %{line: 5, column: 15}}}, - %{uri: nil, range: %{start: %{line: 6, column: 11}, end: %{line: 6, column: 14}}}, - %{uri: nil, range: %{start: %{line: 8, column: 23}, end: %{line: 8, column: 26}}} - ] - - Enum.each([{3, 5}, {5, 12}, {6, 11}, {8, 23}], fn {line, column} -> - assert ElixirSense.references(buffer, line, column, trace) == expected_references - end) - end - - test "find references for variable from the scope of an anonymous function", %{trace: trace} do - buffer = """ - defmodule MyModule do - def my_fun(x, y) do - x = Enum.map(x, fn x -> x + y end) - end - end - """ - - # `x` from the `my_fun` function header - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 14}, end: %{line: 2, column: 15}}}, - %{uri: nil, range: %{start: %{line: 3, column: 18}, end: %{line: 3, column: 19}}} - ] - - assert ElixirSense.references(buffer, 2, 14, trace) == expected_references - assert ElixirSense.references(buffer, 3, 18, trace) == expected_references - - # `y` from the `my_fun` function header - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 17}, end: %{line: 2, column: 18}}}, - %{uri: nil, range: %{start: %{line: 3, column: 33}, end: %{line: 3, column: 34}}} - ] - - assert ElixirSense.references(buffer, 2, 17, trace) == expected_references - assert ElixirSense.references(buffer, 3, 33, trace) == expected_references - - # `x` from the anonymous function - expected_references = [ - %{uri: nil, range: %{start: %{line: 3, column: 24}, end: %{line: 3, column: 25}}}, - %{uri: nil, range: %{start: %{line: 3, column: 29}, end: %{line: 3, column: 30}}} - ] - - assert ElixirSense.references(buffer, 3, 24, trace) == expected_references - assert ElixirSense.references(buffer, 3, 29, trace) == expected_references - - # redefined `x` - expected_references = [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 6}}} - ] - - assert ElixirSense.references(buffer, 3, 5, trace) == expected_references - end - - test "find references of a variable when using pin operator", %{trace: trace} do - buffer = """ - defmodule MyModule do - def my_fun(a, b) do - case a do - ^b -> b - %{b: ^b} = a -> b - end - end - end - """ - - # `b` - expected_references = [ - %{uri: nil, range: %{start: %{line: 2, column: 17}, end: %{line: 2, column: 18}}}, - %{uri: nil, range: %{start: %{line: 4, column: 8}, end: %{line: 4, column: 9}}}, - %{uri: nil, range: %{start: %{line: 4, column: 13}, end: %{line: 4, column: 14}}}, - %{uri: nil, range: %{start: %{line: 5, column: 13}, end: %{line: 5, column: 14}}}, - %{uri: nil, range: %{start: %{line: 5, column: 23}, end: %{line: 5, column: 24}}} - ] - - assert ElixirSense.references(buffer, 2, 17, trace) == expected_references - assert ElixirSense.references(buffer, 4, 8, trace) == expected_references - assert ElixirSense.references(buffer, 4, 13, trace) == expected_references - assert ElixirSense.references(buffer, 5, 13, trace) == expected_references - assert ElixirSense.references(buffer, 5, 23, trace) == expected_references - - # `a` redefined in a case clause - expected_references = [ - %{uri: nil, range: %{start: %{line: 5, column: 18}, end: %{line: 5, column: 19}}} - ] - - assert ElixirSense.references(buffer, 5, 18, trace) == expected_references - end - - test "find references of a variable in multiline struct", %{trace: trace} do - buffer = """ - defmodule MyServer do - def go do - %Some{ - filed: my_var, - other: some, - other: my_var - } = abc() - fun(my_var, some) - end - end - """ - - # `my_var` - expected_references = [ - %{uri: nil, range: %{start: %{line: 4, column: 14}, end: %{line: 4, column: 20}}}, - %{uri: nil, range: %{start: %{line: 6, column: 14}, end: %{line: 6, column: 20}}}, - %{uri: nil, range: %{start: %{line: 8, column: 9}, end: %{line: 8, column: 15}}} - ] - - assert ElixirSense.references(buffer, 4, 15, trace) == expected_references - assert ElixirSense.references(buffer, 6, 15, trace) == expected_references - assert ElixirSense.references(buffer, 8, 10, trace) == expected_references - end - - test "find references of a variable shadowing function", %{trace: trace} do - buffer = """ - defmodule Vector do - @spec magnitude(Vec2.t()) :: number() - def magnitude(%Vec2{} = v), do: :math.sqrt(:math.pow(v.x, 2) + :math.pow(v.y, 2)) - - @spec normalize(Vec2.t()) :: Vec2.t() - def normalize(%Vec2{} = v) do - length = magnitude(v) - %{v | x: v.x / length, y: v.y / length} - end - end - """ - - # `my_var` - expected_references = [ - %{uri: nil, range: %{start: %{line: 7, column: 5}, end: %{line: 7, column: 11}}}, - %{uri: nil, range: %{start: %{line: 8, column: 20}, end: %{line: 8, column: 26}}}, - %{uri: nil, range: %{start: %{line: 8, column: 37}, end: %{line: 8, column: 43}}} - ] - - assert ElixirSense.references(buffer, 7, 6, trace) == expected_references - assert ElixirSense.references(buffer, 8, 21, trace) == expected_references - end - - test "find references of attributes", %{trace: trace} do - buffer = """ - defmodule MyModule do - @attr "abc" - def fun do - @attr - end - end - """ - - references = ElixirSense.references(buffer, 4, 7, trace) - - assert references == [ - %{range: %{end: %{column: 8, line: 2}, start: %{column: 3, line: 2}}, uri: nil}, - %{range: %{end: %{column: 10, line: 4}, start: %{column: 5, line: 4}}, uri: nil} - ] - - references = ElixirSense.references(buffer, 2, 4, trace) - - assert references == [ - %{range: %{end: %{column: 8, line: 2}, start: %{column: 3, line: 2}}, uri: nil}, - %{range: %{end: %{column: 10, line: 4}, start: %{column: 5, line: 4}}, uri: nil} - ] - end - - test "find references of private functions from definition", %{trace: trace} do - buffer = """ - defmodule MyModule do - def calls_private do - private_fun() - end - - defp also_calls_private do - private_fun() - end - - defp private_fun do - # ^ - :ok - end - end - """ - - references = ElixirSense.references(buffer, 10, 15, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 16}}}, - %{uri: nil, range: %{start: %{line: 7, column: 5}, end: %{line: 7, column: 16}}} - ] - end - - test "find references of private functions from invocation", %{trace: trace} do - buffer = """ - defmodule MyModule do - def calls_private do - private_fun() - # ^ - end - - defp also_calls_private do - private_fun() - end - - defp private_fun do - :ok - end - end - """ - - references = ElixirSense.references(buffer, 3, 15, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 3, column: 5}, end: %{line: 3, column: 16}}}, - %{uri: nil, range: %{start: %{line: 8, column: 5}, end: %{line: 8, column: 16}}} - ] - end - - test "find references of public metadata functions from definition", %{trace: trace} do - buffer = """ - defmodule MyModule do - def calls_public do - MyCalleeModule.Some.public_fun() - end - - defp also_calls_public do - alias MyCalleeModule.Some - Some.public_fun() - end - - defp also_calls_public_import do - import MyCalleeModule.Some - public_fun() - end - end - - defmodule MyCalleeModule.Some do - def public_fun do - # ^ - :ok - end - end - """ - - references = ElixirSense.references(buffer, 18, 15, trace) - - assert references == [ - %{uri: nil, range: %{start: %{line: 3, column: 25}, end: %{line: 3, column: 35}}}, - %{uri: nil, range: %{start: %{line: 8, column: 10}, end: %{line: 8, column: 20}}}, - %{uri: nil, range: %{start: %{line: 13, column: 5}, end: %{line: 13, column: 15}}} - ] - end - - test "does not find references of private metadata functions from definition", %{trace: trace} do - buffer = """ - defmodule MyModule do - def calls_public do - MyCalleeModule.Some.public_fun() - end - - defp also_calls_public do - alias MyCalleeModule.Some - Some.public_fun() - end - - defp also_calls_public_import do - import MyCalleeModule.Some - public_fun() - end - end - - defmodule MyCalleeModule.Some do - defp public_fun do - # ^ - :ok - end - end - """ - - references = ElixirSense.references(buffer, 18, 15, trace) - - assert references == [] - end - - test "find references with cursor over a module", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee1.func() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 53, trace) - - assert [ - %{range: %{end: %{column: 62, line: 3}, start: %{column: 58, line: 3}}, uri: nil}, - %{ - uri: "test/support/modules_with_references.ex", - range: range_1 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_2 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_3 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_4 - }, - %{ - uri: "test/support/modules_with_references.ex", - range: range_5 - } - ] = references - - assert range_1 == %{start: %{line: 36, column: 60}, end: %{line: 36, column: 64}} - assert range_2 == %{start: %{line: 42, column: 60}, end: %{line: 42, column: 64}} - assert range_3 == %{start: %{line: 65, column: 16}, end: %{line: 65, column: 20}} - assert range_4 == %{start: %{line: 65, column: 63}, end: %{line: 65, column: 67}} - assert range_5 == %{start: %{line: 65, column: 79}, end: %{line: 65, column: 83}} - end - - test "find references with cursor over an erlang module", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - :ets.new(:s, []) - # ^ - end - end - """ - - references = - ElixirSense.references(buffer, 3, 7, trace) - |> Enum.filter(&(&1.uri == nil or &1.uri =~ "modules_with_references")) - - assert [ - %{ - range: %{end: %{column: 13, line: 3}, start: %{column: 10, line: 3}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{start: %{column: 12, line: 74}, end: %{column: 15, line: 74}} - end - - test "find references with cursor over an erlang function call", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - :ets.new(:s, []) - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 11, trace) - - assert [ - %{ - range: %{end: %{column: 13, line: 3}, start: %{column: 10, line: 3}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{start: %{column: 12, line: 74}, end: %{column: 15, line: 74}} - end - - test "find references with cursor over builtin function call", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee6.module_info() - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 60, trace) - - assert [ - %{ - range: %{end: %{column: 69, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{start: %{column: 60, line: 101}, end: %{column: 71, line: 101}} - end - - test "find references with cursor over builtin function call incomplete code", %{trace: trace} do - buffer = """ - defmodule Caller do - def func() do - ElixirSense.Providers.ReferencesTest.Modules.Callee6.module_info( - # ^ - end - end - """ - - references = ElixirSense.references(buffer, 3, 60, trace) - - assert [ - %{ - range: %{end: %{column: 69, line: 3}, start: %{column: 58, line: 3}}, - uri: nil - }, - %{ - range: range_1, - uri: "test/support/modules_with_references.ex" - } - ] = references - - assert range_1 == %{start: %{column: 60, line: 101}, end: %{column: 71, line: 101}} - end - - defp read_line(file, range) do - {line, column} = {range.start.line, range.start.column} - - file - |> File.read!() - |> Source.split_lines() - |> Enum.at(line - 1) - |> String.slice((column - 1)..-1//1) - end -end diff --git a/test/elixir_sense/signature_test.exs b/test/elixir_sense/signature_test.exs deleted file mode 100644 index 813b2cc6..00000000 --- a/test/elixir_sense/signature_test.exs +++ /dev/null @@ -1,1591 +0,0 @@ -defmodule ElixirSense.SignatureTest do - use ExUnit.Case, async: true - alias ElixirSense.Providers.Signature - - doctest Signature - - describe "type signature" do - test "find signatures from local type" do - code = """ - defmodule MyModule do - @typep my(a) :: {a, nil} - @typep my(a, b) :: {a, b} - @type a :: my( - end - """ - - assert ElixirSense.signature(code, 4, 19) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "my", - params: ["a"], - spec: "@typep my(a) :: {a, nil}" - }, - %{ - documentation: "", - name: "my", - params: ["a", "b"], - spec: "@typep my(a, b) :: {a, b}" - } - ] - } - end - - test "find signatures from local type, filter by arity" do - code = """ - defmodule MyModule do - @typep my(a) :: {a, nil} - @typep my(a, b) :: {a, b} - @type a :: my(atom, - end - """ - - assert ElixirSense.signature(code, 4, 25) == %{ - active_param: 1, - signatures: [ - %{ - documentation: "", - name: "my", - params: ["a", "b"], - spec: "@typep my(a, b) :: {a, b}" - } - ] - } - end - - test "find signatures from local type, filter by arity unfinished param" do - code = """ - defmodule MyModule do - @typep my(a) :: {a, nil} - @typep my(a, b) :: {a, b} - @type a :: my(atom - end - """ - - assert ElixirSense.signature(code, 4, 24) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "my", - params: ["a"], - spec: "@typep my(a) :: {a, nil}" - }, - %{ - documentation: "", - name: "my", - params: ["a", "b"], - spec: "@typep my(a, b) :: {a, b}" - } - ] - } - end - - test "find signatures from local type, filter by arity unfinished params" do - code = """ - defmodule MyModule do - @typep my(a) :: {a, nil} - @typep my(a, b) :: {a, b} - @type a :: my(atom, atom - end - """ - - assert ElixirSense.signature(code, 4, 30) == %{ - active_param: 1, - signatures: [ - %{ - documentation: "", - name: "my", - params: ["a", "b"], - spec: "@typep my(a, b) :: {a, b}" - } - ] - } - end - - test "find local metadata type signature even if it's defined after cursor" do - buffer = """ - defmodule MyModule do - @type remote_list_t :: [my_t(a)] - # ^ - - @typep my_t(abc) :: integer - end - """ - - assert %{ - active_param: 0 - } = - ElixirSense.signature(buffer, 2, 32) - end - - test "find type signatures" do - code = """ - defmodule MyModule do - @type a :: ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t( - end - """ - - assert ElixirSense.signature(code, 2, 69) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "Remote type", - name: "remote_t", - params: [], - spec: "@type remote_t() :: atom()" - }, - %{ - documentation: "Remote type with params", - name: "remote_t", - params: ["a", "b"], - spec: "@type remote_t(a, b) ::\n {a, b}" - } - ] - } - end - - test "does not reveal opaque type details" do - code = """ - defmodule MyModule do - @type a :: ElixirSenseExample.ModuleWithTypespecs.Remote.some_opaque_options_t( - end - """ - - assert ElixirSense.signature(code, 2, 82) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "some_opaque_options_t", - params: [], - spec: "@opaque some_opaque_options_t()" - } - ] - } - end - - test "does not reveal local opaque type details" do - code = """ - defmodule Some do - @opaque my(a, b) :: {a, b} - end - defmodule MyModule do - @type a :: Some.my( - end - """ - - assert ElixirSense.signature(code, 5, 22) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "my", - params: ["a", "b"], - spec: "@opaque my(a, b)" - } - ] - } - end - - test "find type signatures with @typedoc false" do - code = """ - defmodule MyModule do - @type a :: ElixirSenseExample.ModuleWithDocs.some_type_doc_false( - end - """ - - assert ElixirSense.signature(code, 2, 68) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "some_type_doc_false", - params: ~c"", - spec: "@type some_type_doc_false() :: integer()" - } - ] - } - end - - test "does not find builtin type signatures with Elixir prefix" do - code = """ - defmodule MyModule do - @type a :: Elixir.keyword( - end - """ - - assert ElixirSense.signature(code, 2, 29) == :none - end - - test "find type signatures from erlang module" do - code = """ - defmodule MyModule do - @type a :: :erlang.time_unit( - end - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: summary, - name: "time_unit", - params: [], - spec: "@type time_unit() ::" <> _ - } - ] - } = ElixirSense.signature(code, 2, 32) - - if ExUnitConfig.erlang_eep48_supported() do - assert summary =~ "Supported time unit representations:" - end - end - - test "find type signatures from builtin type" do - code = """ - defmodule MyModule do - @type a :: number( - end - """ - - assert ElixirSense.signature(code, 2, 21) == %{ - active_param: 0, - signatures: [ - %{ - params: [], - documentation: "An integer or a float", - name: "number", - spec: "@type number() :: integer() | float()" - } - ] - } - end - end - - describe "macro signature" do - test "find signatures from aliased modules" do - code = """ - defmodule MyModule do - require ElixirSenseExample.BehaviourWithMacrocallback.Impl, as: Macros - Macros.some( - end - """ - - assert ElixirSense.signature(code, 3, 15) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "some macro\n", - name: "some", - params: ["var"], - spec: - "@spec some(integer()) :: Macro.t()\n@spec some(b) :: Macro.t() when b: float()" - } - ] - } - end - - test "find signatures special forms" do - code = """ - defmodule MyModule do - __MODULE__( - end - """ - - assert ElixirSense.signature(code, 2, 14) == %{ - active_param: 0, - signatures: [ - %{ - documentation: - "Returns the current module name as an atom or `nil` otherwise.", - name: "__MODULE__", - params: [], - spec: "" - } - ] - } - end - end - - describe "function signature" do - test "find signatures from erlang module" do - code = """ - defmodule MyModule do - :lists.flatten( - end - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: summary1, - name: "flatten", - params: ["deepList"], - spec: - "@spec flatten(deepList) :: list when deepList: [term() | deepList], list: [term()]" - }, - %{ - documentation: summary2, - name: "flatten", - params: ["deepList", "tail"], - spec: - "@spec flatten(deepList, tail) :: list when deepList: [term() | deepList], tail: [term()], list: [term()]" - } - ] - } = ElixirSense.signature(code, 2, 24) - - if ExUnitConfig.erlang_eep48_supported() do - assert "Returns a flattened version of `DeepList`\\." <> _ = summary1 - - assert "Returns a flattened version of `DeepList` with tail `Tail` appended\\." <> _ = - summary2 - end - end - - test "find signatures from aliased modules" do - code = """ - defmodule MyModule do - alias List, as: MyList - MyList.flatten( - end - """ - - assert ElixirSense.signature(code, 3, 23) == %{ - active_param: 0, - signatures: [ - %{ - name: "flatten", - params: ["list"], - documentation: "Flattens the given `list` of nested lists.", - spec: "@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]" - }, - %{ - name: "flatten", - params: ["list", "tail"], - documentation: - "Flattens the given `list` of nested lists.\nThe list `tail` will be added at the end of\nthe flattened list.", - spec: - "@spec flatten(deep_list, [elem]) :: [elem] when deep_list: [elem | deep_list], elem: var" - } - ] - } - end - - test "find signatures from aliased modules aaa" do - code = """ - defmodule MyModule do - alias NonExisting, as: List - Elixir.List.flatten( - end - """ - - assert ElixirSense.signature(code, 3, 28) == %{ - active_param: 0, - signatures: [ - %{ - name: "flatten", - params: ["list"], - documentation: "Flattens the given `list` of nested lists.", - spec: "@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]" - }, - %{ - name: "flatten", - params: ["list", "tail"], - documentation: - "Flattens the given `list` of nested lists.\nThe list `tail` will be added at the end of\nthe flattened list.", - spec: - "@spec flatten(deep_list, [elem]) :: [elem] when deep_list: [elem | deep_list], elem: var" - } - ] - } - end - - test "find signatures from imported modules" do - code = """ - defmodule MyModule do - import List - flatten( - end - """ - - assert ElixirSense.signature(code, 3, 16) == %{ - active_param: 0, - signatures: [ - %{ - name: "flatten", - params: ["list"], - documentation: "Flattens the given `list` of nested lists.", - spec: "@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]" - }, - %{ - name: "flatten", - params: ["list", "tail"], - documentation: - "Flattens the given `list` of nested lists.\nThe list `tail` will be added at the end of\nthe flattened list.", - spec: - "@spec flatten(deep_list, [elem]) :: [elem] when deep_list: [elem | deep_list], elem: var" - } - ] - } - end - - test "find signatures when function with default args" do - code = """ - defmodule MyModule do - List.pop_at(par1, - end - """ - - assert ElixirSense.signature(code, 2, 21) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Returns and removes the value at the specified `index` in the `list`.", - name: "pop_at", - params: ["list", "index", "default \\\\ nil"], - spec: "@spec pop_at(list(), integer(), any()) :: {any(), list()}" - } - ] - } - end - - test "find signatures when function with many clauses" do - code = """ - defmodule MyModule do - List.starts_with?( - end - """ - - assert ElixirSense.signature(code, 2, 21) == %{ - active_param: 0, - signatures: [ - %{ - documentation: - "Returns `true` if `list` starts with the given `prefix` list; otherwise returns `false`.", - name: "starts_with?", - params: ["list", "prefix"], - spec: - "@spec starts_with?([...], [...]) :: boolean()\n@spec starts_with?(list(), []) :: true\n@spec starts_with?([], [...]) :: false" - } - ] - } - end - - test "find signatures for function with @doc false" do - code = """ - defmodule MyModule do - ElixirSenseExample.ModuleWithDocs.some_fun_doc_false( - end - """ - - assert ElixirSense.signature(code, 2, 56) == %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "some_fun_doc_false", - params: ["a", "b \\\\ nil"], - spec: "" - } - ] - } - end - - test "find signatures from atom modules" do - code = """ - defmodule MyModule do - :"Elixir.List".flatten( - end - """ - - assert ElixirSense.signature(code, 2, 31) == %{ - active_param: 0, - signatures: [ - %{ - name: "flatten", - params: ["list"], - documentation: "Flattens the given `list` of nested lists.", - spec: "@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]" - }, - %{ - name: "flatten", - params: ["list", "tail"], - documentation: - "Flattens the given `list` of nested lists.\nThe list `tail` will be added at the end of\nthe flattened list.", - spec: - "@spec flatten(deep_list, [elem]) :: [elem] when deep_list: [elem | deep_list], elem: var" - } - ] - } - end - - test "find signatures from __MODULE__" do - code = """ - defmodule Inspect.Algebra do - __MODULE__.glue(par1, - end - """ - - assert ElixirSense.signature(code, 2, 24) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.", - name: "glue", - params: ["doc1", "break_string \\\\ \" \"", "doc2"], - spec: "@spec glue(t(), binary(), t()) :: t()", - active_param: 2 - } - ] - } - end - - test "find signatures from __MODULE__ submodule" do - code = """ - defmodule Inspect do - __MODULE__.Algebra.glue(par1, - end - """ - - assert ElixirSense.signature(code, 2, 32) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.", - name: "glue", - params: ["doc1", "break_string \\\\ \" \"", "doc2"], - spec: "@spec glue(t(), binary(), t()) :: t()", - active_param: 2 - } - ] - } - end - - test "find signatures from attribute" do - code = """ - defmodule MyMod do - @attribute Inspect.Algebra - @attribute.glue(par1, - end - """ - - assert ElixirSense.signature(code, 3, 24) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.", - name: "glue", - params: ["doc1", "break_string \\\\ \" \"", "doc2"], - spec: "@spec glue(t(), binary(), t()) :: t()", - active_param: 2 - } - ] - } - end - - @tag :capture_log - test "find signatures from attribute submodule" do - code = """ - defmodule Inspect do - @attribute Inspect - @attribute.Algebra.glue(par1, - end - """ - - assert ElixirSense.signature(code, 3, 32) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.", - name: "glue", - params: ["doc1", "break_string \\\\ \" \"", "doc2"], - spec: "@spec glue(t(), binary(), t()) :: t()", - active_param: 2 - } - ] - } - end - - test "find signatures from variable" do - code = """ - defmodule MyMod do - myvariable = Inspect.Algebra - myvariable.glue(par1, - end - """ - - assert ElixirSense.signature(code, 3, 24) == %{ - active_param: 1, - signatures: [ - %{ - documentation: - "Glues two documents (`doc1` and `doc2`) inserting the given\nbreak `break_string` between them.", - name: "glue", - params: ["doc1", "break_string \\\\ \" \"", "doc2"], - spec: "@spec glue(t(), binary(), t()) :: t()", - active_param: 2 - } - ] - } - end - - @tag :capture_log - test "find signatures from variable submodule - don't crash" do - code = """ - defmodule Inspect do - myvariable = Inspect - myvariable.Algebra.glue(par1, - end - """ - - assert ElixirSense.signature(code, 3, 32) == :none - end - - test "find signatures from variable call" do - code = """ - defmodule Inspect do - myvariable = &Inspect.Algebra.glue/2 - myvariable.(par1, - end - """ - - # TODO https://github.com/elixir-lsp/elixir_sense/issues/255 - # Type system needs to handle function captures - assert ElixirSense.signature(code, 3, 20) == :none - end - - test "find signatures from attribute call" do - code = """ - defmodule Inspect do - @attribute &Inspect.Algebra.glue/2 - @attribute.(par1, - end - """ - - # TODO https://github.com/elixir-lsp/elixir_sense/issues/255 - # Type system needs to handle function captures - assert ElixirSense.signature(code, 3, 20) == :none - end - - test "finds signatures from Kernel functions" do - code = """ - defmodule MyModule do - apply(par1, - end - """ - - assert ElixirSense.signature(code, 2, 14) == %{ - active_param: 1, - signatures: [ - %{ - name: "apply", - params: ["fun", "args"], - documentation: - "Invokes the given anonymous function `fun` with the list of\narguments `args`.", - spec: "@spec apply((... -> any()), [any()]) :: any()" - }, - %{ - name: "apply", - params: ["module", "function_name", "args"], - documentation: - "Invokes the given function from `module` with the list of\narguments `args`.", - spec: "@spec apply(module(), function_name :: atom(), [any()]) :: any()" - } - ] - } - end - - test "finds signatures from local functions" do - code = """ - defmodule MyModule do - - def run do - sum( - end - - defp sum(a, b) do - a + b - end - - defp sum({a, b}) do - a + b - end - end - """ - - assert ElixirSense.signature(code, 4, 9) == %{ - active_param: 0, - signatures: [ - %{ - name: "sum", - params: ["tuple"], - documentation: "", - spec: "" - }, - %{ - name: "sum", - params: ["a", "b"], - documentation: "", - spec: "" - } - ] - } - end - - test "finds signatures from local functions, filter by arity" do - code = """ - defmodule MyModule do - - def run do - sum(a, - end - - defp sum(a, b) do - a + b - end - - defp sum({a, b}) do - a + b - end - end - """ - - assert ElixirSense.signature(code, 4, 12) == %{ - active_param: 1, - signatures: [ - %{ - name: "sum", - params: ["a", "b"], - documentation: "", - spec: "" - } - ] - } - end - - test "finds signatures from module with many function clauses" do - code = """ - defmodule Other do - alias ElixirSenseExample.ModuleWithManyClauses, as: MyModule - def run do - MyModule.sum(a, - end - end - """ - - assert ElixirSense.signature(code, 4, 21) == %{ - active_param: 1, - signatures: [ - %{ - documentation: "", - name: "sum", - spec: "", - params: ["s \\\\ nil", "f"], - active_param: 0 - }, - %{documentation: "", name: "sum", spec: "", params: ["arg", "x", "y"]} - ] - } - end - - test "finds signatures from metadata module functions" do - code = """ - defmodule MyModule do - def sum(s \\\\ nil, f) - def sum(a, nil), do: nil - def sum(a, b) do - a + b - end - - def sum({a, b}, x, y) do - a + b + x + y - end - end - - defmodule Other do - def run do - MyModule.sum(a, - end - end - """ - - assert ElixirSense.signature(code, 15, 21) == %{ - active_param: 1, - signatures: [ - %{ - documentation: "", - name: "sum", - params: ["s \\\\ nil", "f"], - spec: "", - active_param: 0 - }, - %{documentation: "", name: "sum", params: ["tuple", "x", "y"], spec: ""} - ] - } - end - - test "does not finds signatures from metadata module private functions" do - code = """ - defmodule MyModule do - defp sum(a, nil), do: nil - defp sum(a, b) do - a + b - end - - defp sum({a, b}) do - a + b - end - end - - defmodule Other do - def run do - MyModule.sum(a, - end - end - """ - - assert ElixirSense.signature(code, 14, 21) == :none - end - - test "finds signatures from metadata module functions with default param" do - code = """ - defmodule MyModule do - @spec sum(integer, integer) :: integer - defp sum(a, b \\\\ 0) do - a + b - end - - def run do - sum(a, - end - end - """ - - assert ElixirSense.signature(code, 8, 11) == %{ - active_param: 1, - signatures: [ - %{ - name: "sum", - params: ["a", "b \\\\ 0"], - documentation: "", - spec: "@spec sum(integer, integer) :: integer" - } - ] - } - end - - test "finds signatures from metadata module functions with default param - correctly highlight active param" do - code = """ - defmodule MyModule do - @spec sum(integer, integer, integer, integer, integer, integer) :: integer - defp sum(a \\\\ 1, b \\\\ 1, c, d, e \\\\ 1, f \\\\ 1) do - a + b - end - - def run do - sum(1, 2, 3, 4, 5, 6) - end - end - """ - - assert ElixirSense.signature(code, 8, 10) == %{ - active_param: 0, - signatures: [ - %{ - name: "sum", - params: [ - "a \\\\ 1", - "b \\\\ 1", - "c", - "d", - "e \\\\ 1", - "f \\\\ 1" - ], - documentation: "", - spec: - "@spec sum(integer, integer, integer, integer, integer, integer) :: integer", - active_param: 2 - } - ] - } - - assert %{ - active_param: 1, - signatures: [%{active_param: 3}] - } = ElixirSense.signature(code, 8, 13) - - assert %{ - active_param: 2, - signatures: [%{active_param: 0}] - } = ElixirSense.signature(code, 8, 16) - - assert %{ - active_param: 3, - signatures: [%{active_param: 1}] - } = ElixirSense.signature(code, 8, 19) - - assert %{ - active_param: 4, - signatures: [signature] - } = ElixirSense.signature(code, 8, 22) - - refute Map.has_key?(signature, :active_param) - - assert %{ - active_param: 5, - signatures: [signature] - } = ElixirSense.signature(code, 8, 25) - - refute Map.has_key?(signature, :active_param) - end - - test "finds signatures from metadata elixir behaviour call" do - code = """ - defmodule MyModule do - use GenServer - - def handle_call(request, _from, state) do - terminate() - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - name: "terminate", - params: ["_reason", "_state"], - documentation: "Invoked when the server is about to exit" <> _, - spec: "@callback terminate(reason, state :: term()) :: term()" <> _ - } - ] - } = ElixirSense.signature(code, 5, 15) - end - - test "finds signatures from metadata erlang behaviour call" do - code = """ - defmodule MyModule do - @behaviour :gen_server - - def handle_call(request, _from, state) do - init() - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - name: "init", - params: ["arg"], - documentation: summary, - spec: "@callback init(args :: term()) ::" <> _ - } - ] - } = ElixirSense.signature(code, 5, 10) - - if ExUnitConfig.erlang_eep48_supported() do - assert "- Args = term\\(\\)\n- Result" <> _ = summary - end - end - - test "finds signatures from metadata elixir behaviour call from outside" do - code = """ - require ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl - ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl.bar() - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Docs for bar", - name: "bar", - params: ["b"], - spec: "@macrocallback bar(integer()) :: Macro.t()" - } - ] - } = ElixirSense.signature(code, 2, 60) - end - - test "finds signatures from metadata erlang behaviour implemented in elixir call from outside" do - code = """ - ElixirSenseExample.ExampleBehaviourWithDocCallbackErlang.init() - """ - - res = ElixirSense.signature(code, 1, 63) - - if ExUnitConfig.erlang_eep48_supported() do - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "- Args = " <> _, - name: "init", - params: ["_"], - spec: "@callback init(args :: term()) :: init_result(state())" - } - ] - } = res - end - end - - @tag requires_otp_25: true - test "finds signatures from metadata erlang behaviour call from outside" do - code = """ - :file_server.init() - """ - - res = ElixirSense.signature(code, 1, 19) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "- Args = " <> _, - name: "init", - params: ["args"], - spec: "@callback init(args :: term()) ::" <> _ - } - ] - } = res - end - - test "retrieve metadata function signature - fallback to callback in metadata" do - code = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @callback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - res = ElixirSense.signature(code, 18, 27) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Sample doc", - name: "flatten", - params: ["list"], - spec: "@callback flatten(list()) :: list()" - } - ] - } = res - end - - test "retrieve metadata function signature - fallback to protocol function in metadata" do - code = """ - defprotocol BB do - @doc "asdf" - @spec go(t) :: integer() - def go(t) - end - - defimpl BB, for: String do - def go(t), do: "" - end - - defmodule MyModule do - def func(list) do - BB.String.go(list) - end - end - """ - - res = ElixirSense.signature(code, 13, 18) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "asdf", - name: "go", - params: ["t"], - spec: "@callback go(t) :: integer()" - } - ] - } = res - end - - test "retrieve metadata macro signature - fallback to macrocallback in metadata" do - code = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @macrocallback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - defmacro flatten(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - res = ElixirSense.signature(code, 19, 27) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Sample doc", - name: "flatten", - params: ["list"], - spec: "@macrocallback flatten(list()) :: list()" - } - ] - } = res - end - - test "retrieve metadata function signature - fallback to callback" do - code = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - res = ElixirSense.signature(code, 12, 27) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Sample doc", - name: "flatten", - params: ["list"], - spec: "@callback flatten(list()) :: list()" - } - ] - } = res - end - - test "retrieve metadata function signature - fallback to erlang callback" do - code = """ - defmodule MyLocalModule do - @behaviour :gen_statem - - @impl true - def init(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.init(list) - end - end - """ - - res = ElixirSense.signature(code, 12, 27) - - if ExUnitConfig.erlang_eep48_supported() do - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "- Args = term" <> _, - name: "init", - params: ["list"], - spec: "@callback init(args :: term()) :: init_result(state())" - } - ] - } = res - end - end - - test "retrieve metadata macro signature - fallback to macrocallback" do - code = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - defmacro bar(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.bar(list) - end - end - """ - - res = ElixirSense.signature(code, 13, 27) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Docs for bar", - name: "bar", - params: ["list"], - spec: "@macrocallback bar(integer()) :: Macro.t()" - } - ] - } = res - end - - test "find signature of local macro" do - code = """ - defmodule MyModule do - defmacrop some(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some(1) - end - end - """ - - res = ElixirSense.signature(code, 5, 10) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "some", - params: ["var"], - spec: "" - } - ] - } = res - end - - test "does not find signature of local macro if it's defined after the cursor" do - code = """ - defmodule MyModule do - defmacro other do - some(1) - end - - defmacrop some(var), do: Macro.expand(var, __CALLER__) - end - """ - - assert ElixirSense.signature(code, 3, 10) == :none - end - - test "find signature of local function even if it's defined after the cursor" do - code = """ - defmodule MyModule do - def other do - some(1) - end - - defp some(var), do: :ok - end - """ - - assert res = ElixirSense.signature(code, 3, 10) - - assert res == %{ - active_param: 0, - signatures: [%{documentation: "", name: "some", params: ["var"], spec: ""}] - } - end - - test "returns :none when it cannot identify a function call" do - code = """ - defmodule MyModule do - fn(a, - end - """ - - assert ElixirSense.signature(code, 2, 8) == :none - end - - test "return :none when no signature is found" do - code = """ - defmodule MyModule do - a_func( - end - """ - - assert ElixirSense.signature(code, 2, 10) == :none - end - - test "after |>" do - code = """ - defmodule MyModule do - {1, 2} |> IO.inspect( - end - """ - - assert %{ - active_param: 1, - signatures: [ - %{ - name: "inspect", - params: ["item", "opts \\\\ []"], - documentation: "Inspects and writes the given `item` to the device.", - spec: "@spec inspect(" <> _ - }, - %{ - name: "inspect", - params: ["device", "item", "opts"], - documentation: - "Inspects `item` according to the given options using the IO `device`.", - spec: "@spec inspect(device(), item, keyword()) :: item when item: var" - } - ] - } = ElixirSense.signature(code, 2, 24) - end - - test "after |> variable" do - code = """ - s |> String.replace_prefix( - """ - - assert %{ - active_param: 1 - } = ElixirSense.signature(code, 1, 28) - end - - test "find built-in functions" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - ElixirSenseExample.ModuleWithFunctions.module_info() - # ^ - ElixirSenseExample.ModuleWithFunctions.__info__(:macros) - # ^ - ElixirSenseExample.ExampleBehaviour.behaviour_info(:callbacks) - # ^ - end - """ - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "The `module_info/0` function" <> _, - name: "module_info", - params: [], - spec: - "@spec module_info :: [{:module | :attributes | :compile | :exports | :md5 | :native, term}]" - }, - %{ - documentation: "The call `module_info(Key)`" <> _, - name: "module_info", - params: ["key"], - spec: """ - @spec module_info(:module) :: atom - @spec module_info(:attributes | :compile) :: [{atom, term}] - @spec module_info(:md5) :: binary - @spec module_info(:exports | :functions | :nifs) :: [{atom, non_neg_integer}] - @spec module_info(:native) :: boolean\ - """ - } - ] - } = ElixirSense.signature(buffer, 2, 54) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "Provides runtime informatio" <> _, - name: "__info__", - params: ["atom"], - spec: """ - @spec __info__(:attributes) :: keyword() - @spec __info__(:compile) :: [term()] - @spec __info__(:functions) :: [{atom, non_neg_integer}] - @spec __info__(:macros) :: [{atom, non_neg_integer}] - @spec __info__(:md5) :: binary() - @spec __info__(:module) :: module()\ - """ - } - ] - } = ElixirSense.signature(buffer, 4, 51) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "The `behaviour_info(Key)`" <> _, - name: "behaviour_info", - params: ["key"], - spec: - "@spec behaviour_info(:callbacks | :optional_callbacks) :: [{atom, non_neg_integer}]" - } - ] - } = ElixirSense.signature(buffer, 6, 54) - end - - test "built-in functions cannot be called locally" do - # module_info is defined by default for every elixir and erlang module - # __info__ is defined for every elixir module - # behaviour_info is defined for every behaviour and every protocol - buffer = """ - defmodule MyModule do - import GenServer - @ callback cb() :: term - module_info() - # ^ - __info__(:macros) - # ^ - behaviour_info(:callbacks) - # ^ - end - """ - - assert :none = ElixirSense.signature(buffer, 4, 15) - - assert :none = ElixirSense.signature(buffer, 6, 12) - - assert :none = ElixirSense.signature(buffer, 8, 18) - end - - @tag requires_otp_23: true - test "find built-in erlang functions" do - buffer = """ - defmodule MyModule do - :erlang.orelse() - # ^ - :erlang.or() - # ^ - end - """ - - %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "orelse", - params: ["term", "term"], - spec: "" - } - ] - } = ElixirSense.signature(buffer, 2, 18) - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: "", - name: "or", - params: [_, _], - spec: "@spec boolean() or boolean() :: boolean()" - } - ] - } = ElixirSense.signature(buffer, 4, 14) - end - - test "find :erlang module functions with different forms of typespecs" do - buffer = """ - defmodule MyModule do - :erlang.date() - # ^ - :erlang.cancel_timer() - # ^ - end - """ - - %{ - active_param: 0, - signatures: [ - %{ - documentation: summary, - name: "date", - params: [], - spec: "@spec date() :: date when date: :calendar.date()" - } - ] - } = ElixirSense.signature(buffer, 2, 16) - - if ExUnitConfig.erlang_eep48_supported() do - assert "Returns the current date as" <> _ = summary - end - - assert %{ - active_param: 0, - signatures: [ - %{ - documentation: summary1, - name: "cancel_timer", - params: ["timerRef"], - spec: "@spec cancel_timer(timerRef) :: result" <> _ - }, - %{ - documentation: summary2, - name: "cancel_timer", - params: ["timerRef", "options"], - spec: "@spec cancel_timer(timerRef, options) :: result" <> _ - } - ] - } = ElixirSense.signature(buffer, 4, 24) - - if ExUnitConfig.erlang_eep48_supported() do - assert "Cancels a timer\\." <> _ = summary1 - assert "Cancels a timer that has been created by" <> _ = summary2 - end - end - end -end diff --git a/test/elixir_sense/suggestions_test.exs b/test/elixir_sense/suggestions_test.exs deleted file mode 100644 index 98051f16..00000000 --- a/test/elixir_sense/suggestions_test.exs +++ /dev/null @@ -1,4703 +0,0 @@ -defmodule ElixirSense.SuggestionsTest do - use ExUnit.Case, async: true - alias ElixirSense.Core.Source - - import TestHelper - import ExUnit.CaptureIO - - test "empty hint" do - buffer = """ - defmodule MyModule do - - end - """ - - list = ElixirSense.suggestions(buffer, 2, 7) - - assert %{ - args: "module, opts", - args_list: ["module", "opts"], - arity: 2, - def_arity: 2, - name: "import", - origin: "Kernel.SpecialForms", - spec: "", - summary: "Imports functions and macros from other modules.", - type: :macro, - metadata: %{}, - snippet: nil, - visibility: :public - } = Enum.find(list, fn s -> match?(%{name: "import", arity: 2}, s) end) - - assert %{ - arity: 2, - def_arity: 2, - origin: "Kernel.SpecialForms", - spec: "", - type: :macro, - args: "opts, block", - args_list: ["opts", "block"], - name: "quote", - summary: "Gets the representation of any expression.", - metadata: %{}, - snippet: nil, - visibility: :public - } = Enum.find(list, fn s -> match?(%{name: "quote", arity: 2}, s) end) - - assert %{ - arity: 2, - def_arity: 2, - origin: "Kernel.SpecialForms", - spec: "", - type: :macro, - args: "module, opts", - args_list: ["module", "opts"], - name: "require", - metadata: %{}, - snippet: nil, - visibility: :public - } = Enum.find(list, fn s -> match?(%{name: "require", arity: 2}, s) end) - end - - test "without empty hint" do - buffer = """ - defmodule MyModule do - is_b - end - """ - - list = ElixirSense.suggestions(buffer, 2, 7) - - assert [ - %{ - name: "is_binary", - origin: "Kernel", - arity: 1 - }, - %{ - name: "is_bitstring", - origin: "Kernel", - arity: 1 - }, - %{ - name: "is_boolean", - origin: "Kernel", - arity: 1 - }, - %{ - name: "is_number", - origin: "Kernel", - arity: 1 - } - ] = list - end - - test "capture hint" do - buffer = """ - defmodule MyModule do - @attr "asd" - def a(arg) do - arg - |> Enum.filter(&) - end - end - """ - - list = ElixirSense.suggestions(buffer, 5, 21) - - assert list |> Enum.any?(&(&1.type == :module)) - assert list |> Enum.any?(&(&1.type == :function)) - assert list |> Enum.any?(&(&1.type == :variable)) - assert list |> Enum.any?(&(&1.type == :attribute)) - end - - test "pin hint 1" do - buffer = """ - defmodule MyModule do - @attr "asd" - def a(arg) do - case x() do - {^} -> :ok - end - end - end - """ - - list = ElixirSense.suggestions(buffer, 5, 9) - - refute list |> Enum.any?(&(&1.type == :module)) - refute list |> Enum.any?(&(&1.type == :function)) - assert list |> Enum.any?(&(&1.type == :variable)) - refute list |> Enum.any?(&(&1.type == :attribute)) - end - - test "pin hint 2" do - buffer = """ - defmodule MyModule do - @attr "asd" - def a(arg) do - with ^ <- abc(), - x <- cde(), - y <- efg() do - :ok - end - end - end - """ - - list = ElixirSense.suggestions(buffer, 4, 11) - - refute list |> Enum.any?(&(&1.type == :module)) - refute list |> Enum.any?(&(&1.type == :function)) - assert list |> Enum.any?(&(&1.type == :variable)) - refute list |> Enum.any?(&(&1.type == :attribute)) - end - - test "pin hint 3" do - buffer = """ - defmodule MyModule do - @attr "asd" - def a(arg) do - with {^} <- abc(), - x <- cde(), - y <- efg() do - :ok - end - end - end - """ - - list = ElixirSense.suggestions(buffer, 4, 12) - - refute list |> Enum.any?(&(&1.type == :module)) - refute list |> Enum.any?(&(&1.type == :function)) - assert list |> Enum.any?(&(&1.type == :variable)) - refute list |> Enum.any?(&(&1.type == :attribute)) - end - - test "pin hint 4" do - buffer = """ - defmodule MyModule do - @attr "asd" - def a(arg) do - with a <- abc(), - x <- cde(), - y <- efg() do - :ok - else - ^ -> :ok - :ok -> :ok - end - end - end - """ - - list = ElixirSense.suggestions(buffer, 9, 8) - - refute list |> Enum.any?(&(&1.type == :module)) - refute list |> Enum.any?(&(&1.type == :function)) - assert list |> Enum.any?(&(&1.type == :variable)) - refute list |> Enum.any?(&(&1.type == :attribute)) - end - - test "no typespecs in function scope" do - buffer = """ - defmodule MyModule do - def go, do: - end - """ - - list = ElixirSense.suggestions(buffer, 2, 15) - - refute list |> Enum.any?(&(&1.type == :type_spec)) - assert list |> Enum.any?(&(&1.type == :function)) - end - - test "functions from unicode module" do - buffer = """ - defmodule :你好 do - def 运行 do - IO.puts("你好") - end - end - - :你好. - """ - - list = ElixirSense.suggestions(buffer, 7, 5) - - assert list |> Enum.any?(&(&1.type == :function && &1.name == "运行")) - end - - test "with an alias" do - buffer = """ - defmodule MyModule do - alias List, as: MyList - MyList.flat - end - """ - - list = ElixirSense.suggestions(buffer, 3, 14) - - assert [ - %{ - args: "list", - args_list: ["list"], - arity: 1, - def_arity: 1, - name: "flatten", - origin: "List", - spec: "@spec flatten(deep_list) :: list() when deep_list: [any() | deep_list]", - summary: "Flattens the given `list` of nested lists.", - type: :function, - metadata: %{}, - visibility: :public, - snippet: nil - }, - %{ - args: "list, tail", - args_list: ["list", "tail"], - arity: 2, - def_arity: 2, - name: "flatten", - origin: "List", - spec: - "@spec flatten(deep_list, [elem]) :: [elem] when deep_list: [elem | deep_list], elem: var", - summary: - "Flattens the given `list` of nested lists.\nThe list `tail` will be added at the end of\nthe flattened list.", - type: :function, - metadata: %{}, - visibility: :public, - snippet: nil - } - ] = list - end - - test "with a require" do - buffer = """ - defmodule MyModule do - require ElixirSenseExample.BehaviourWithMacrocallback.Impl, as: Macros - Macros.so - end - """ - - list = ElixirSense.suggestions(buffer, 3, 12) - - assert [ - %{ - args: "var", - args_list: ["var"], - arity: 1, - def_arity: 1, - name: "some", - origin: "ElixirSenseExample.BehaviourWithMacrocallback.Impl", - spec: - "@spec some(integer()) :: Macro.t()\n@spec some(b) :: Macro.t() when b: float()", - summary: "some macro\n", - type: :macro, - metadata: %{}, - snippet: nil, - visibility: :public - } - ] = list - end - - test "with a module hint" do - buffer = """ - defmodule MyModule do - ElixirSenseExample.ModuleWithDo - end - """ - - list = ElixirSense.suggestions(buffer, 2, 34) - - assert [ - %{ - name: "ModuleWithDocFalse", - full_name: "ElixirSenseExample.ModuleWithDocFalse", - subtype: nil, - summary: "", - type: :module, - metadata: %{} - }, - %{ - name: "ModuleWithDocs", - full_name: "ElixirSenseExample.ModuleWithDocs", - subtype: :behaviour, - summary: "An example module\n", - type: :module, - metadata: %{since: "1.2.3"} - }, - %{ - metadata: %{}, - name: "ModuleWithNoDocs", - full_name: "ElixirSenseExample.ModuleWithNoDocs", - subtype: nil, - summary: "", - type: :module - } - ] = list - end - - test "lists metadata modules" do - buffer = """ - defmodule MyServer do - @moduledoc "Some" - @moduledoc since: "1.2.3" - end - MySe - """ - - list = - ElixirSense.suggestions(buffer, 5, 5) - |> Enum.filter(fn s -> s.type == :module end) - - assert [ - %{ - name: "MyServer", - summary: "Some", - type: :module, - full_name: "MyServer", - metadata: %{since: "1.2.3"}, - required_alias: nil, - subtype: nil - } - ] = list - end - - test "returns subtype on local modules" do - buffer = """ - defprotocol MyProto do - end - MyPr - """ - - list = - ElixirSense.suggestions(buffer, 3, 5) - |> Enum.filter(fn s -> s.type == :module end) - - assert [ - %{ - name: "MyProto", - subtype: :protocol - } - ] = list - end - - test "lists callbacks" do - buffer = """ - defmodule MyServer do - use GenServer - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 7) - |> Enum.filter(fn s -> s.type == :callback && s.name == "code_change" end) - - assert [ - %{ - args: "old_vsn, state, extra", - arity: 3, - name: "code_change", - origin: "GenServer", - spec: "@callback code_change(old_vsn, state :: term(), extra :: term()) ::" <> _, - summary: - "Invoked to change the state of the `GenServer` when a different version of a\nmodule is loaded (hot code swapping) and the state's term structure should be\nchanged.", - type: :callback - } - ] = list - end - - test "lists metadata behaviour callbacks" do - buffer = """ - defmodule MyBehaviour do - @doc "Some callback" - @callback my_callback(integer()) :: any() - - @callback my_callback_optional(integer(), atom()) :: any() - - @deprecated "Replace me" - @macrocallback my_macrocallback(integer()) :: Macro.t() - - @optional_callbacks my_callback_optional: 2 - end - - defmodule MyServer do - @behaviour MyBehaviour - - end - """ - - list = - ElixirSense.suggestions(buffer, 15, 3) - |> Enum.filter(fn s -> s.type == :callback end) - - assert [ - %{ - args: "integer()", - arity: 1, - name: "my_callback", - origin: "MyBehaviour", - spec: "@callback my_callback(integer()) :: any()", - summary: "Some callback", - type: :callback, - args_list: ["integer()"], - metadata: %{}, - subtype: :callback - }, - %{ - args: "integer()", - args_list: ["integer()"], - arity: 1, - metadata: %{deprecated: "Replace me"}, - name: "my_macrocallback", - origin: "MyBehaviour", - spec: "@macrocallback my_macrocallback(integer()) :: Macro.t()", - subtype: :macrocallback, - summary: "", - type: :callback - }, - %{ - args: "integer(), atom()", - args_list: ["integer()", "atom()"], - arity: 2, - metadata: %{optional: true}, - name: "my_callback_optional", - origin: "MyBehaviour", - spec: "@callback my_callback_optional(integer(), atom()) :: any()", - subtype: :callback, - summary: "", - type: :callback - } - ] = list - end - - test "lists metadata protocol functions" do - buffer = """ - defprotocol MyProto do - @doc "Some callback" - @doc since: "1.2.3" - def my_fun(t) - - @doc deprecated: "1.2.3" - @spec my_fun_other(t(), integer()) :: any() - def my_fun_other(t, a) - end - - defimpl MyProto, for: List do - - end - """ - - list = - ElixirSense.suggestions(buffer, 11, 3) - |> Enum.filter(fn s -> s.type == :protocol_function end) - - assert [ - %{ - args: "t", - args_list: ["t"], - arity: 1, - metadata: %{since: "1.2.3"}, - name: "my_fun", - origin: "MyProto", - spec: "@callback my_fun(t) :: term", - summary: "Some callback", - type: :protocol_function - }, - %{ - args: "t(), integer()", - args_list: ["t()", "integer()"], - arity: 2, - metadata: %{deprecated: "1.2.3"}, - name: "my_fun_other", - origin: "MyProto", - spec: "@spec my_fun_other(t(), integer()) :: any()", - summary: "", - type: :protocol_function - } - ] = list - end - - test "lists callbacks + def macros after de" do - buffer = """ - defmodule MyServer do - use GenServer - - de - # ^ - end - """ - - list = ElixirSense.suggestions(buffer, 4, 5) - assert Enum.any?(list, fn s -> s.type == :callback end) - assert Enum.any?(list, fn s -> s.type == :macro end) - assert Enum.all?(list, fn s -> s.type in [:callback, :macro] end) - end - - test "lists callbacks + def macros after def" do - buffer = """ - defmodule MyServer do - use GenServer - - def - # ^ - end - """ - - list = ElixirSense.suggestions(buffer, 4, 6) - assert Enum.any?(list, fn s -> s.type == :callback end) - assert Enum.any?(list, fn s -> s.type == :macro end) - assert Enum.all?(list, fn s -> s.type in [:callback, :macro] end) - end - - test "lists only callbacks after def + space" do - buffer = """ - defmodule MyServer do - use GenServer - - def t - # ^ - end - """ - - assert ElixirSense.suggestions(buffer, 4, 7) |> Enum.all?(fn s -> s.type == :callback end) - - buffer = """ - defmodule MyServer do - use GenServer - - def t - # ^ - end - """ - - assert [%{name: "terminate", type: :callback}] = ElixirSense.suggestions(buffer, 4, 8) - end - - test "do not list callbacks inside functions" do - buffer = """ - defmodule MyServer do - use GenServer - - def init(_) do - t - # ^ - end - end - """ - - list = ElixirSense.suggestions(buffer, 5, 6) - assert Enum.any?(list, fn s -> s.type == :function end) - refute Enum.any?(list, fn s -> s.type == :callback end) - end - - test "lists macrocallbacks" do - buffer = """ - defmodule MyServer do - @behaviour ElixirSenseExample.BehaviourWithMacrocallback - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 7) - |> Enum.filter(fn s -> s.type == :callback end) - - assert [ - %{ - args: "a", - args_list: ["a"], - arity: 1, - name: "optional", - subtype: :macrocallback, - origin: "ElixirSenseExample.BehaviourWithMacrocallback", - spec: "@macrocallback optional(a) :: Macro.t() when a: atom()", - summary: "An optional macrocallback\n", - type: :callback, - metadata: %{optional: true, app: :elixir_sense} - }, - %{ - args: "atom", - args_list: ["atom"], - arity: 1, - name: "required", - subtype: :macrocallback, - origin: "ElixirSenseExample.BehaviourWithMacrocallback", - spec: "@macrocallback required(atom()) :: Macro.t()", - summary: "A required macrocallback\n", - type: :callback, - metadata: %{optional: false, app: :elixir_sense} - } - ] == list - end - - test "lists macrocallbacks + def macros after defma" do - buffer = """ - defmodule MyServer do - @behaviour ElixirSenseExample.BehaviourWithMacrocallback - - defma - # ^ - end - """ - - list = ElixirSense.suggestions(buffer, 4, 8) - assert Enum.any?(list, fn s -> s.type == :callback end) - assert Enum.any?(list, fn s -> s.type == :macro end) - assert Enum.all?(list, fn s -> s.type in [:callback, :macro] end) - end - - test "lists erlang callbacks" do - buffer = """ - defmodule MyServer do - @behaviour :gen_statem - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 7) - |> Enum.filter(fn s -> s.type == :callback && s.name == "code_change" end) - - assert [ - %{ - args: "oldVsn, oldState, oldData, extra", - arity: 4, - name: "code_change", - origin: ":gen_statem", - spec: "@callback code_change" <> _, - summary: summary, - type: :callback, - subtype: :callback - } - ] = list - - if ExUnitConfig.erlang_eep48_supported() do - assert "- OldVsn = Vsn" <> _ = summary - end - end - - test "callback suggestions should not crash with unquote(__MODULE__)" do - buffer = """ - defmodule Dummy do - @doc false - defmacro __using__() do - quote location: :keep do - @behaviour unquote(__MODULE__) - end - end - end - """ - - assert [%{} | _] = ElixirSense.suggestions(buffer, 8, 5) - end - - test "lists overridable callbacks" do - buffer = """ - defmodule MyServer do - use ElixirSenseExample.OverridableImplementation - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 7) - |> Enum.filter(fn s -> s.type == :callback end) - - assert [ - %{ - args: "", - arity: 0, - name: "foo", - origin: "ElixirSenseExample.OverridableBehaviour", - spec: "@callback foo() :: any()", - summary: "", - type: :callback, - subtype: :callback, - metadata: %{optional: false, overridable: true} - }, - %{ - args: "any", - arity: 1, - metadata: %{optional: false, overridable: true}, - name: "bar", - origin: "ElixirSenseExample.OverridableBehaviour", - spec: "@macrocallback bar(any()) :: Macro.t()", - subtype: :macrocallback, - summary: "", - type: :callback - } - ] = list - end - - test "lists overridable functions and macros" do - buffer = """ - defmodule MyServer do - use ElixirSenseExample.OverridableFunctions - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 7) - |> Enum.filter(fn s -> s.type == :callback end) - - assert [ - %{ - args: "var", - arity: 1, - metadata: %{overridable: true}, - name: "required", - origin: "ElixirSenseExample.OverridableFunctions", - spec: "", - summary: "", - type: :callback, - subtype: :macrocallback - }, - %{ - args: "x, y", - arity: 2, - metadata: %{since: "1.2.3", overridable: true}, - name: "test", - origin: "ElixirSenseExample.OverridableFunctions", - spec: "@spec test(number, number) :: number", - summary: "Some overridable", - type: :callback, - subtype: :callback - } - ] = list - end - - test "fuzzy match overridable functions" do - buffer = """ - defmodule MyServer do - use ElixirSenseExample.OverridableFunctions - - rqui - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 5) - |> Enum.filter(fn s -> s.type == :callback end) - - assert [ - %{ - args: "var", - arity: 1, - metadata: %{}, - name: "required", - origin: "ElixirSenseExample.OverridableFunctions", - spec: "", - summary: "", - type: :callback, - subtype: :macrocallback - } - ] = list - end - - test "lists protocol functions" do - buffer = """ - defimpl Enumerable, for: MyStruct do - - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 3) - |> Enum.filter(fn s -> s[:name] == "reduce" end) - - assert [ - %{ - args: "enumerable, acc, fun", - arity: 3, - name: "reduce", - origin: "Enumerable", - spec: "@callback reduce(t(), acc(), reducer()) :: result()", - summary: "Reduces the `enumerable` into an element.", - type: :protocol_function, - metadata: %{} - } - ] = list - end - - test "lists fuzzy protocol functions" do - buffer = """ - defimpl Enumerable, for: MyStruct do - reu - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 5) - |> Enum.filter(fn s -> s[:type] == :protocol_function end) - - assert [ - %{ - args: "enumerable, acc, fun", - arity: 3, - name: "reduce", - origin: "Enumerable", - spec: "@callback reduce(t(), acc(), reducer()) :: result()", - summary: "Reduces the `enumerable` into an element.", - type: :protocol_function, - metadata: %{} - } - ] = list - end - - test "lists callback return values" do - buffer = """ - defmodule MyServer do - use ElixirSenseExample.ExampleBehaviour - - def handle_call(request, from, state) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 5) - |> Enum.filter(fn s -> s.type == :return end) - - assert [ - %{ - description: "{:reply, reply, new_state}", - snippet: "{:reply, \"${1:reply}$\", \"${2:new_state}$\"}", - spec: - "{:reply, reply, new_state} when reply: term(), new_state: term(), reason: term()", - type: :return - }, - %{ - description: - "{:reply, reply, new_state, timeout() | :hibernate | {:continue, term()}}", - snippet: - "{:reply, \"${1:reply}$\", \"${2:new_state}$\", \"${3:timeout() | :hibernate | {:continue, term()}}$\"}", - spec: - "{:reply, reply, new_state, timeout() | :hibernate | {:continue, term()}}" <> _, - type: :return - }, - %{ - description: "{:noreply, new_state}", - snippet: "{:noreply, \"${1:new_state}$\"}", - spec: - "{:noreply, new_state} when reply: term(), new_state: term(), reason: term()", - type: :return - }, - %{ - description: "{:noreply, new_state, timeout() | :hibernate | {:continue, term()}}", - snippet: - "{:noreply, \"${1:new_state}$\", \"${2:timeout() | :hibernate | {:continue, term()}}$\"}", - spec: "{:noreply, new_state, timeout() | :hibernate | {:continue, term()}}" <> _, - type: :return - }, - %{ - description: "{:stop, reason, reply, new_state}", - snippet: "{:stop, \"${1:reason}$\", \"${2:reply}$\", \"${3:new_state}$\"}", - spec: - "{:stop, reason, reply, new_state} when reply: term(), new_state: term(), reason: term()", - type: :return - }, - %{ - description: "{:stop, reason, new_state}", - snippet: "{:stop, \"${1:reason}$\", \"${2:new_state}$\"}", - spec: - "{:stop, reason, new_state} when reply: term(), new_state: term(), reason: term()", - type: :return - } - ] = list - end - - test "lists macrocallback return values" do - buffer = """ - defmodule MyServer do - @behaviour ElixirSenseExample.BehaviourWithMacrocallback - - defmacro required(arg) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 5) - |> Enum.filter(fn s -> s.type == :return end) - - assert list == [ - %{ - description: "Macro.t()", - snippet: "\"${1:Macro.t()}$\"", - spec: "Macro.t()", - type: :return - } - ] - end - - test "lists metadata callback return values" do - buffer = """ - defmodule MyBehaviour do - @callback required(term()) :: {:ok, term()} | :error - end - - defmodule MyServer do - @behaviour MyBehaviour - - def required(arg) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 9, 5) - |> Enum.filter(fn s -> s.type == :return end) - - assert list == [ - %{ - description: "{:ok, term()}", - snippet: "{:ok, term()}", - spec: "{:ok, term()}", - type: :return - }, - %{description: ":error", snippet: ":error", spec: ":error", type: :return} - ] - end - - test "lists protocol implementation return values" do - buffer = """ - defimpl Enumerable, for: MyStruct do - def count(t) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 6) - |> Enum.filter(fn s -> s.type == :return end) - - assert [ - %{ - description: "{:ok, non_neg_integer()}", - snippet: "{:ok, non_neg_integer()}", - spec: "{:ok, non_neg_integer()}", - type: :return - }, - %{ - description: "{:error, module()}", - snippet: "{:error, module()}", - spec: "{:error, module()}", - type: :return - } - ] == list - end - - test "lists metadata protocol implementation return values" do - buffer = """ - defprotocol MyProto do - @spec count(t()) :: {:ok, term()} | :error - def count(t) - end - - defimpl MyProto, for: MyStruct do - def count(t) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 8, 6) - |> Enum.filter(fn s -> s.type == :return end) - - assert [ - %{ - description: "{:ok, term()}", - snippet: "{:ok, term()}", - spec: "{:ok, term()}", - type: :return - }, - %{description: ":error", snippet: ":error", spec: ":error", type: :return} - ] == list - end - - test "lists function with spec return values" do - buffer = """ - defmodule SomeModule do - @spec count(atom) :: :ok | {:error, any} - def count(t) do - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 6) - |> Enum.filter(fn s -> s.type == :return end) - - assert [ - %{description: ":ok", snippet: ":ok", spec: ":ok", type: :return}, - %{ - description: "{:error, any}", - snippet: "{:error, \"${1:any}$\"}", - spec: "{:error, any}", - type: :return - } - ] == list - end - - test "list metadata function - fallback to callback in metadata" do - buffer = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @callback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flat - end - end - """ - - list = - ElixirSense.suggestions(buffer, 18, 23) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "list", - arity: 1, - def_arity: 1, - metadata: %{implementing: MyBehaviour, hidden: true, since: "1.2.3"}, - name: "flatten", - origin: "MyLocalModule", - spec: "@callback flatten(list()) :: list()", - summary: "Sample doc", - type: :function, - visibility: :public - } - ] = list - end - - test "retrieve metadata function documentation - fallback to protocol function in metadata" do - buffer = """ - defprotocol BB do - @doc "asdf" - @spec go(t) :: integer() - def go(t) - end - - defimpl BB, for: String do - def go(t), do: "" - end - - defmodule MyModule do - def func(list) do - BB.String.go(list) - end - end - """ - - list = - ElixirSense.suggestions(buffer, 13, 16) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "t", - arity: 1, - def_arity: 1, - metadata: %{implementing: BB}, - name: "go", - origin: "BB.String", - spec: "@callback go(t) :: integer()", - summary: "asdf", - type: :function, - visibility: :public - } - ] = list - - # TODO docs and metadata - end - - test "list metadata macro - fallback to macrocallback in metadata" do - buffer = """ - defmodule MyBehaviour do - @doc "Sample doc" - @doc since: "1.2.3" - @macrocallback flatten(list()) :: list() - end - - defmodule MyLocalModule do - @behaviour MyBehaviour - - @impl true - defmacro flatten(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.flatten(list) - end - end - """ - - list = - ElixirSense.suggestions(buffer, 19, 23) - |> Enum.filter(fn s -> s.type == :macro end) - - assert [ - %{ - args: "list", - arity: 1, - def_arity: 1, - metadata: %{implementing: MyBehaviour, hidden: true, since: "1.2.3"}, - name: "flatten", - origin: "MyLocalModule", - spec: "@macrocallback flatten(list()) :: list()", - summary: "Sample doc", - type: :macro, - visibility: :public - } - ] = list - end - - test "list metadata function - fallback to callback" do - buffer = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - def flatten(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.flat - end - end - """ - - list = - ElixirSense.suggestions(buffer, 12, 23) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "list", - arity: 1, - def_arity: 1, - metadata: %{implementing: ElixirSenseExample.BehaviourWithMeta}, - name: "flatten", - origin: "MyLocalModule", - spec: "@callback flatten(list()) :: list()", - summary: "Sample doc", - type: :function, - visibility: :public - } - ] = list - end - - test "list metadata function - fallback to erlang callback" do - buffer = """ - defmodule MyLocalModule do - @behaviour :gen_statem - - @impl true - def init(list) do - [] - end - end - - defmodule MyModule do - def func(list) do - MyLocalModule.ini - end - end - """ - - list = - ElixirSense.suggestions(buffer, 12, 22) - |> Enum.filter(fn s -> s.type == :function end) - - if ExUnitConfig.erlang_eep48_supported() do - assert [ - %{ - args: "list", - arity: 1, - def_arity: 1, - metadata: %{implementing: :gen_statem, since: "OTP 19.0"}, - name: "init", - origin: "MyLocalModule", - spec: "@callback init(args :: term()) ::" <> _, - summary: "- Args = term" <> _, - type: :function, - visibility: :public - } - ] = list - end - end - - test "list metadata macro - fallback to macrocallback" do - buffer = """ - defmodule MyLocalModule do - @behaviour ElixirSenseExample.BehaviourWithMeta - - @impl true - defmacro bar(list) do - [] - end - end - - defmodule MyModule do - require MyLocalModule - def func(list) do - MyLocalModule.ba - end - end - """ - - list = - ElixirSense.suggestions(buffer, 13, 21) - |> Enum.filter(fn s -> s.type == :macro end) - - assert [ - %{ - args: "list", - arity: 1, - def_arity: 1, - metadata: %{implementing: ElixirSenseExample.BehaviourWithMeta}, - name: "bar", - origin: "MyLocalModule", - spec: "@macrocallback bar(integer()) :: Macro.t()", - summary: "Docs for bar", - type: :macro, - visibility: :public - } - ] = list - end - - test "lists callbacks in function suggestion - elixir behaviour" do - buffer = """ - defmodule MyServer do - use GenServer - - def handle_call(request, _from, state) do - term - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 9) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "_reason, _state", - arity: 2, - def_arity: 2, - metadata: %{implementing: GenServer}, - name: "terminate", - origin: "MyServer", - spec: "@callback terminate(reason, state :: term()) :: term()" <> _, - summary: - "Invoked when the server is about to exit. It should do any cleanup required.", - type: :function, - visibility: :public - } - ] = list - end - - test "lists callbacks in function suggestion - erlang behaviour" do - buffer = """ - defmodule MyServer do - @behaviour :gen_event - - def handle_call(request, _from, state) do - ini - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 8) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{name: "init", origin: "MyServer", arity: 1} = init_res, - %{name: "is_function", origin: "Kernel", arity: 1}, - %{name: "is_function", origin: "Kernel", arity: 2} - ] = list - - if ExUnitConfig.erlang_eep48_supported() do - assert %{ - summary: "- InitArgs = Args" <> _, - metadata: %{implementing: :gen_event}, - spec: "@callback init(initArgs :: term()) ::" <> _, - args_list: ["arg"] - } = init_res - end - end - - test "lists fuzzy callbacks in function suggestion - erlang behaviour" do - buffer = """ - defmodule MyServer do - @behaviour :gen_server - - def handle_call(request, _from, state) do - iit - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 8) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{name: "init", origin: "MyServer", arity: 1}, - %{name: "is_bitstring", origin: "Kernel", arity: 1}, - %{name: "is_integer", origin: "Kernel", arity: 1}, - %{name: "is_list", origin: "Kernel", arity: 1} - ] = list - end - - test "suggest elixir behaviour callbacks on implementation" do - buffer = """ - ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl.ba - """ - - list = - ElixirSense.suggestions(buffer, 1, 57) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "a", - args_list: ["a"], - arity: 1, - def_arity: 1, - metadata: %{implementing: ElixirSenseExample.ExampleBehaviourWithDoc}, - name: "baz", - origin: "ElixirSenseExample.ExampleBehaviourWithDocCallbackImpl", - snippet: nil, - spec: "@callback baz(integer()) :: :ok", - summary: "Docs for baz", - type: :function, - visibility: :public - } - ] = list - end - - test "suggest erlang behaviour callbacks on implementation" do - buffer = """ - ElixirSenseExample.ExampleBehaviourWithDocCallbackErlang.ini - """ - - list = - ElixirSense.suggestions(buffer, 1, 60) - |> Enum.filter(fn s -> s.type == :function end) - - if ExUnitConfig.erlang_eep48_supported() do - assert [ - %{ - args: "_", - args_list: ["_"], - arity: 1, - def_arity: 1, - metadata: %{implementing: :gen_statem}, - name: "init", - origin: "ElixirSenseExample.ExampleBehaviourWithDocCallbackErlang", - snippet: nil, - spec: "@callback init(args :: term()) :: init_result(state())", - summary: "- Args = term" <> _, - type: :function, - visibility: :public - } - ] = list - end - end - - @tag requires_otp_25: true - test "suggest erlang behaviour callbacks on erlang implementation" do - buffer = """ - :file_server.ini - """ - - list = - ElixirSense.suggestions(buffer, 1, 17) - |> Enum.filter(fn s -> s.type == :function end) - - assert [ - %{ - args: "args", - args_list: ["args"], - arity: 1, - def_arity: 1, - metadata: %{implementing: :gen_server}, - name: "init", - origin: ":file_server", - snippet: nil, - spec: "@callback init(args :: term()) ::" <> _, - summary: "- Args = term" <> _, - type: :function, - visibility: :public - } - ] = list - end - - test "lists params and vars" do - buffer = """ - defmodule MyServer do - use GenServer - - def handle_call(request, _from, state) do - var1 = true - - end - - def init(arg), do: arg - - def handle_cast(arg, _state) when is_atom(arg) do - :ok - end - end - """ - - list = - ElixirSense.suggestions(buffer, 6, 5) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable}, - %{name: "state", type: :variable}, - %{name: "var1", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 9, 22) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "arg", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 11, 45) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "arg", type: :variable} - ] - end - - test "lists params in fn's" do - buffer = """ - defmodule MyServer do - my = fn arg -> arg + 1 end - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 19) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "arg", type: :variable} - ] - end - - test "lists params in protocol implementations" do - buffer = """ - defimpl Enum, for: [MyStruct, MyOtherStruct] do - def count(term), do: - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 24) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "term", type: :variable} - ] - end - - test "lists vars in []" do - buffer = """ - defmodule MyServer do - my = %{} - x = 4 - my[] - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 6) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "my", type: :variable}, - %{name: "x", type: :variable} - ] - end - - test "lists vars in unfinished []" do - buffer = """ - defmodule MyServer do - my = %{} - x = 4 - my[ - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 6) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "my", type: :variable}, - %{name: "x", type: :variable} - ] - end - - test "lists vars in unfinished fn" do - buffer = """ - defmodule MyServer do - [] - |> Enum.min_by(fn x -> - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 26) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - end - - test "lists vars in string interpolation" do - buffer = """ - defmodule MyServer do - x = 4 - "abc\#{}" - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - end - - test "lists vars in unfinished string interpolation" do - buffer = """ - defmodule MyServer do - x = 4 - "abc\#{ - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - - buffer = """ - defmodule MyServer do - x = 4 - "abc\#{" - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - - buffer = """ - defmodule MyServer do - x = 4 - "abc\#{} - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - - buffer = """ - defmodule MyServer do - x = 4 - "abc\#{x[ - - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - end - - test "lists vars in heredoc interpolation" do - buffer = """ - defmodule MyServer do - x = 4 - \"\"\" - abc\#{} - \"\"\" - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 8) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - end - - test "lists vars in unfinished heredoc interpolation" do - buffer = """ - defmodule MyServer do - x = 4 - \"\"\" - abc\#{ - \"\"\" - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 8) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - - buffer = """ - defmodule MyServer do - x = 4 - \"\"\" - abc\#{ - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 8) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - - buffer = """ - defmodule MyServer do - x = 4 - \"\"\" - abc\#{} - - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 8) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "x", type: :variable} - ] - end - - test "lists params in fn's not finished multiline" do - buffer = """ - defmodule MyServer do - my = fn arg -> - - end - """ - - assert capture_io(:stderr, fn -> - list = - ElixirSense.suggestions(buffer, 3, 5) - |> Enum.filter(fn s -> s.type == :variable end) - - send(self(), {:result, list}) - end) =~ "an expression is always required on the right side of ->" - - assert_received {:result, list} - - assert list == [%{name: "arg", type: :variable}] - end - - test "lists params in fn's not finished" do - buffer = """ - defmodule MyServer do - my = fn arg -> - end - """ - - assert capture_io(:stderr, fn -> - list = - ElixirSense.suggestions(buffer, 2, 19) - |> Enum.filter(fn s -> s.type == :variable end) - - send(self(), {:result, list}) - end) =~ "an expression is always required on the right side of ->" - - assert_received {:result, list} - - assert list == [ - %{name: "arg", type: :variable}, - # FIXME my is not defined, should not be in the list - %{name: "my", type: :variable} - ] - end - - test "lists params in defs not finished" do - buffer = """ - defmodule MyServer do - def my(arg), do: - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 20) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "arg", type: :variable} - ] - end - - test "lists params and vars in case clauses" do - buffer = """ - defmodule MyServer do - def fun(request) do - case request do - {:atom1, vara} -> - :ok - {:atom2, varb} -> :ok - abc when is_atom(a) - end - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable}, - %{name: "vara", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 6, 25) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable}, - %{name: "varb", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 9, 4) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 7, 25) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "abc", type: :variable} - ] - end - - test "lists params and vars in cond clauses" do - buffer = """ - defmodule MyServer do - def fun(request) do - cond do - vara = Enum.find(request, 4) -> - :ok - varb = Enum.find(request, 5) -> :ok - true -> :error - end - - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 9) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable}, - %{name: "vara", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 6, 39) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable}, - %{name: "varb", type: :variable} - ] - - list = - ElixirSense.suggestions(buffer, 9, 4) - |> Enum.filter(fn s -> s.type == :variable end) - - assert list == [ - %{name: "request", type: :variable} - ] - end - - test "only list defined params in guard" do - buffer = """ - defmodule MyServer do - def new(my_var) when is_integer(my - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 37) - |> Enum.filter(fn s -> s.type in [:variable] end) - - assert list == [%{name: "my_var", type: :variable}] - end - - test "list vars in multiline struct" do - buffer = """ - defmodule MyServer do - def go do - %Some{ - filed: my_var, - other: my - } = abc() - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 16) - |> Enum.filter(fn s -> s.type in [:variable] end) - - assert list == [%{name: "my_var", type: :variable}] - end - - test "tuple destructuring" do - buffer = """ - defmodule MyServer do - def new() do - case NaiveDateTime.new(1, 2) do - {:ok, x} -> x.h - end - case NaiveDateTime.new(1, 2) do - {:ok, x} -> %{x | h} - end - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 22) - |> Enum.filter(fn s -> s.type == :field end) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - - list = - ElixirSense.suggestions(buffer, 7, 26) - |> Enum.filter(fn s -> s.type == :field end) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - end - - test "nested binding" do - buffer = """ - defmodule State do - defstruct [formatted: nil] - def new(socket) do - %State{formatted: formatted} = state = socket.assigns.state - state.for - state = %{state | form} - end - end - """ - - list = - ElixirSense.suggestions(buffer, 5, 14) - |> Enum.filter(fn s -> s.type == :field end) - - assert [%{name: "formatted", origin: "State"}] = list - - list = - ElixirSense.suggestions(buffer, 6, 27) - |> Enum.filter(fn s -> s.type == :field end) - - assert [%{name: "formatted", origin: "State"}] = list - end - - test "variable shadowing function" do - buffer = """ - defmodule Mod do - def my_fun(), do: :ok - def some() do - my_fun = 1 - my_f - end - end - """ - - assert [ - %{name: "my_fun", type: :variable}, - %{name: "my_fun", type: :function} - ] = ElixirSense.suggestions(buffer, 5, 9) - end - - describe "suggestions for module attributes" do - test "lists attributes" do - buffer = """ - defmodule MyModule do - @my_attribute1 true - @my_attribute2 false - @ - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 4) - |> Enum.filter(fn s -> s.type == :attribute and s.name |> String.starts_with?("@my") end) - |> Enum.map(fn %{name: name} -> name end) - - assert list == ["@my_attribute1", "@my_attribute2"] - end - - test "lists module attributes in module scope" do - buffer = """ - defmodule MyModule do - @myattr "asd" - @moduledoc "asdf" - def some do - @m - end - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 5) - |> Enum.filter(fn s -> s.type == :attribute end) - |> Enum.map(fn %{name: name} -> name end) - - assert list == ["@macrocallback", "@moduledoc", "@myattr"] - - list = - ElixirSense.suggestions(buffer, 5, 7) - |> Enum.filter(fn s -> s.type == :attribute end) - |> Enum.map(fn %{name: name} -> name end) - - assert list == ["@myattr"] - end - - test "built-in attributes should include documentation" do - buffer = """ - defmodule MyModule do - @call - @enfor - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 7) - |> Enum.filter(fn s -> s.type == :attribute end) - - assert [%{summary: "Provides a specification for a behaviour callback."}] = list - - list = - ElixirSense.suggestions(buffer, 3, 8) - |> Enum.filter(fn s -> s.type == :attribute end) - - assert [ - %{ - summary: - "Ensures the given keys are always set when building the struct defined in the current module." - } - ] = list - end - - test "non built-in attributes should not include documentation" do - buffer = """ - defmodule MyModule do - @myattr "asd" - def some do - @m - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 6) - |> Enum.filter(fn s -> s.type == :attribute end) - - assert [%{summary: nil}] = list - end - end - - test "lists builtin module attributes on incomplete code" do - buffer = """ - defmodule My do - def start_link(id) do - GenServer.start_link(__MODULE__, id, name: via_tuple(id)) - end - - @ - def init(id) do - {:ok, - %Some.Mod{ - id: id, - events: [], - version: 0 - }} - end - end - """ - - list = - ElixirSense.suggestions(buffer, 6, 4) - |> Enum.filter(fn s -> s.type == :attribute end) - - assert Enum.any?(list, &(&1.name == "@impl")) - assert Enum.any?(list, &(&1.name == "@spec")) - end - - test "do not suggest @@" do - buffer = """ - defmodule MyModule do - @ - @my_attribute1 true - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 4) - |> Enum.filter(fn s -> s.type == :attribute end) - |> Enum.map(fn %{name: name} -> name end) - - refute "@@" in list - end - - test "lists doc snippets in module body" do - buffer = """ - defmodule MyModule do - @ - #^ - - @m - # ^ - - def some do - @m - # ^ - end - end - """ - - [cursor_1, cursor_2, cursor_3] = cursors(buffer) - - list = suggestions_by_kind(buffer, cursor_1, :snippet) - - assert [ - %{label: ~s(@doc """"""), detail: detail, documentation: doc}, - %{label: ~s(@moduledoc """""")}, - %{label: ~s(@typedoc """""")}, - %{label: "@doc false"}, - %{label: "@moduledoc false"}, - %{label: "@typedoc false"} - ] = list - - assert detail == "module attribute snippet" - assert doc == "Documents a function/macro/callback" - - list = suggestions_by_kind(buffer, cursor_2, :snippet) - assert [%{label: ~S(@moduledoc """""")}, %{label: "@moduledoc false"}] = list - - assert suggestions_by_kind(buffer, cursor_3, :snippet) == [] - end - - test "fuzzy suggestions for doc snippets" do - buffer = """ - defmodule MyModule do - @tydo - # ^ - end - """ - - list = ElixirSense.suggestions(buffer, 2, 7) - - assert [ - %{label: ~s(@typedoc """""")}, - %{label: "@typedoc false"} - ] = list |> Enum.filter(&(&1.type == :generic and &1.kind == :snippet)) - end - - test "functions defined in the module" do - buffer = """ - defmodule ElixirSenseExample.ModuleA do - def test_fun_pub(a), do: :ok - defp test_fun_priv(), do: :ok - defp is_boo_overlaps_kernel(), do: :ok - defdelegate delegate_defined, to: Kernel, as: :is_binary - defdelegate delegate_not_defined, to: Dummy, as: :hello - defguard my_guard_pub(value) when is_integer(value) and rem(value, 2) == 0 - defguardp my_guard_priv(value) when is_integer(value) - defmacro a_macro(a) do - quote do: :ok - end - defmacrop a_macro_priv(a) do - quote do: :ok - end - - def some_fun() do - test - a = &test_fun_pr - is_bo - delegate_ - my_ - a_m - end - end - """ - - assert [ - %{ - arity: 0, - name: "test_fun_priv", - origin: "ElixirSenseExample.ModuleA", - type: :function, - visibility: :private - }, - %{ - arity: 1, - name: "test_fun_pub", - origin: "ElixirSenseExample.ModuleA", - type: :function, - visibility: :public - } - ] = ElixirSense.suggestions(buffer, 17, 9) - - assert [ - %{ - arity: 0, - name: "test_fun_priv", - origin: "ElixirSenseExample.ModuleA", - type: :function - } - ] = ElixirSense.suggestions(buffer, 18, 21) - - assert [ - %{ - arity: 0, - name: "is_boo_overlaps_kernel", - origin: "ElixirSenseExample.ModuleA", - type: :function - }, - %{ - arity: 1, - name: "is_boolean", - origin: "Kernel", - type: :function - } - ] = ElixirSense.suggestions(buffer, 19, 10) - - assert [ - %{ - arity: 0, - name: "delegate_defined", - origin: "ElixirSenseExample.ModuleA", - type: :function - }, - %{ - arity: 0, - name: "delegate_not_defined", - origin: "ElixirSenseExample.ModuleA", - type: :function - } - ] = ElixirSense.suggestions(buffer, 20, 14) - - assert [ - %{ - args: "value", - arity: 1, - name: "my_guard_priv", - origin: "ElixirSenseExample.ModuleA", - spec: "", - summary: "", - type: :macro, - visibility: :private - }, - %{ - args: "value", - arity: 1, - name: "my_guard_pub", - origin: "ElixirSenseExample.ModuleA", - spec: "", - summary: "", - type: :macro - } - ] = ElixirSense.suggestions(buffer, 21, 8) - - assert [ - %{ - args: "a", - arity: 1, - name: "a_macro", - origin: "ElixirSenseExample.ModuleA", - spec: "", - summary: "", - type: :macro, - visibility: :public - }, - %{ - args: "a", - arity: 1, - name: "a_macro_priv", - origin: "ElixirSenseExample.ModuleA", - spec: "", - summary: "", - type: :macro - } - ] = ElixirSense.suggestions(buffer, 22, 8) - end - - test "suggest local macro" do - buffer = """ - defmodule MyModule do - defmacrop some_macro(var), do: Macro.expand(var, __CALLER__) - - defmacro other do - some_ma - end - end - """ - - assert [%{name: "some_macro"}] = ElixirSense.suggestions(buffer, 5, 12) - end - - test "does not suggest local macro if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - defmacro other do - some_ma - end - - defmacrop some_macro(var), do: Macro.expand(var, __CALLER__) - end - """ - - assert [] == ElixirSense.suggestions(buffer, 3, 12) - end - - test "suggest local function even if it's defined after the cursor" do - buffer = """ - defmodule MyModule do - def other do - some_fu - end - - defp some_fun(var), do: :ok - end - """ - - assert [%{name: "some_fun"}] = ElixirSense.suggestions(buffer, 3, 12) - end - - test "functions defined in other module fully qualified" do - buffer = """ - defmodule ElixirSenseExample.ModuleO do - def test_fun_pub(a), do: :ok - defp test_fun_priv(), do: :ok - end - - defmodule ElixirSenseExample.ModuleA do - def some_fun() do - ElixirSenseExample.ModuleO.te - end - end - """ - - assert [ - %{ - arity: 1, - name: "test_fun_pub", - origin: "ElixirSenseExample.ModuleO", - type: :function - } - ] = ElixirSense.suggestions(buffer, 8, 34) - end - - test "functions defined in other module aliased" do - buffer = """ - defmodule ElixirSenseExample.ModuleO do - def test_fun_pub(a), do: :ok - defp test_fun_priv(), do: :ok - end - - defmodule ElixirSenseExample.ModuleA do - alias ElixirSenseExample.ModuleO - def some_fun() do - ModuleO.te - end - end - """ - - assert [ - %{ - arity: 1, - name: "test_fun_pub", - origin: "ElixirSenseExample.ModuleO", - type: :function - } - ] = ElixirSense.suggestions(buffer, 9, 15) - end - - test "functions defined in other module imported" do - buffer = """ - defmodule ElixirSenseExample.ModuleO do - @spec test_fun_pub(integer) :: atom - def test_fun_pub(a), do: :ok - defp test_fun_priv(), do: :ok - end - - defmodule ElixirSenseExample.ModuleA do - import ElixirSenseExample.ModuleO - def some_fun() do - test - __info - end - end - """ - - assert [ - %{ - arity: 1, - def_arity: 1, - name: "test_fun_pub", - origin: "ElixirSenseExample.ModuleO", - type: :function, - args: "a", - args_list: ["a"], - spec: "@spec test_fun_pub(integer) :: atom", - summary: "", - metadata: %{}, - snippet: nil, - visibility: :public - } - ] = ElixirSense.suggestions(buffer, 10, 9) - - # builtin functions not called locally - assert [] == ElixirSense.suggestions(buffer, 11, 11) - end - - test "functions and module suggestions with __MODULE__" do - buffer = """ - defmodule ElixirSenseExample.SmodO do - def test_fun_pub(a), do: :ok - defp test_fun_priv(), do: :ok - end - - defmodule ElixirSenseExample do - defp test_fun_priv1(a), do: :ok - def some_fun() do - __MODULE__.Sm - __MODULE__.SmodO.te - __MODULE__.te - __MODULE__.__in - end - end - """ - - assert [ - %{ - name: "SmodO", - type: :module - } - ] = - ElixirSense.suggestions(buffer, 9, 18) - |> Enum.filter(&(&1.name |> String.starts_with?("Smo"))) - - assert [ - %{ - arity: 1, - name: "test_fun_pub", - origin: "ElixirSenseExample.SmodO", - type: :function - } - ] = ElixirSense.suggestions(buffer, 10, 24) - - # no private on external call - assert [] = ElixirSense.suggestions(buffer, 11, 18) - - assert [ - %{ - arity: 1, - name: "__info__", - origin: "ElixirSenseExample", - type: :function - } - ] = ElixirSense.suggestions(buffer, 12, 20) - end - - test "Elixir module" do - buffer = """ - defmodule MyModule do - El - end - """ - - list = ElixirSense.suggestions(buffer, 2, 5) - - assert %{ - type: :module, - name: "Elixir", - full_name: "Elixir", - subtype: :alias, - summary: "", - metadata: %{} - } = Enum.at(list, 0) - end - - test "suggestion for aliases modules defined by require clause" do - buffer = """ - defmodule Mod do - require Integer, as: I - I.is_o - end - """ - - list = ElixirSense.suggestions(buffer, 3, 9) - assert Enum.at(list, 0).name == "is_odd" - end - - test "suggestion for struct fields" do - buffer = """ - defmodule Mod do - %ElixirSenseExample.IO.Stream{} - %ArgumentError{} - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 33) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "ElixirSenseExample.IO.Stream" - }, - %{ - name: "device", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "IO.device()" - }, - %{ - name: "line_or_bytes", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: ":line | non_neg_integer()" - }, - %{ - name: "raw", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "boolean()" - } - ] - - list = - ElixirSense.suggestions(buffer, 3, 18) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__exception__", - origin: "ArgumentError", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "true" - }, - %{ - name: "__struct__", - origin: "ArgumentError", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "ArgumentError" - }, - %{ - name: "message", - origin: "ArgumentError", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for aliased struct fields" do - buffer = """ - defmodule Mod do - alias ElixirSenseExample.IO.Stream - %Stream{ - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 11) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "ElixirSenseExample.IO.Stream" - }, - %{ - name: "device", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "IO.device()" - }, - %{ - name: "line_or_bytes", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: ":line | non_neg_integer()" - }, - %{ - name: "raw", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "boolean()" - } - ] - end - - test "suggestion for builtin fields in struct pattern match" do - buffer = """ - defmodule Mod do - def my(%_{}), do: :ok - def my(%var{}), do: var - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 13) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: nil, - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "atom()" - } - ] - - list = - ElixirSense.suggestions(buffer, 3, 15) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: nil, - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "atom()" - } - ] - end - - test "suggestion for aliased struct fields atom module" do - buffer = """ - defmodule Mod do - alias ElixirSenseExample.IO.Stream - %:"Elixir.Stream"{ - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 21) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "ElixirSenseExample.IO.Stream" - }, - %{ - name: "device", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "IO.device()" - }, - %{ - name: "line_or_bytes", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: ":line | non_neg_integer()" - }, - %{ - name: "raw", - origin: "ElixirSenseExample.IO.Stream", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "boolean()" - } - ] - end - - test "suggestion for metadata struct fields" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - field_2: "" - ] - - def func do - %MyServer{} - %MyServer{field_2: "2", } - end - end - """ - - list = - ElixirSense.suggestions(buffer, 8, 15) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "MyServer" - }, - %{ - name: "field_1", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - }, - %{ - name: "field_2", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - - list = ElixirSense.suggestions(buffer, 9, 28) - - assert list == [ - %{ - name: "__struct__", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "MyServer" - }, - %{ - name: "field_1", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for metadata struct fields atom module" do - buffer = """ - defmodule :my_server do - defstruct [ - field_1: nil, - field_2: "" - ] - - def func do - %:my_server{} - %:my_server{field_2: "2", } - end - end - """ - - list = - ElixirSense.suggestions(buffer, 8, 17) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "__struct__", - origin: ":my_server", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: ":my_server" - }, - %{ - name: "field_1", - origin: ":my_server", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - }, - %{ - name: "field_2", - origin: ":my_server", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - - list = ElixirSense.suggestions(buffer, 9, 30) - - assert list == [ - %{ - name: "__struct__", - origin: ":my_server", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: ":my_server" - }, - %{ - name: "field_1", - origin: ":my_server", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for metadata struct fields multiline" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - field_2: "" - ] - - def func do - %MyServer{ - field_2: "2", - - } - end - end - """ - - list = ElixirSense.suggestions(buffer, 10, 7) - - assert list == [ - %{ - name: "__struct__", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "MyServer" - }, - %{ - name: "field_1", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for metadata struct fields when using `__MODULE__`" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - field_2: "" - ] - - def func do - %__MODULE__{field_2: "2", } - end - end - """ - - list = ElixirSense.suggestions(buffer, 8, 31) - - assert list == [ - %{ - name: "__struct__", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: "MyServer" - }, - %{ - name: "field_1", - origin: "MyServer", - type: :field, - call?: false, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for struct fields in variable.key call syntax" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - field_2: "" - ] - - def func do - var_1 = %MyServer{} - var_1.f - end - end - """ - - list = - ElixirSense.suggestions(buffer, 9, 12) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "field_1", - origin: "MyServer", - type: :field, - call?: true, - subtype: :struct_field, - type_spec: nil - }, - %{ - name: "field_2", - origin: "MyServer", - type: :field, - call?: true, - subtype: :struct_field, - type_spec: nil - } - ] - end - - test "suggestion for map fields in variable.key call syntax" do - buffer = """ - defmodule MyServer do - def func do - var_1 = %{key_1: 1, key_2: %{abc: 123}} - var_1.k - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 12) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "key_1", - origin: nil, - type: :field, - call?: true, - subtype: :map_key, - type_spec: nil - }, - %{ - name: "key_2", - origin: nil, - type: :field, - call?: true, - subtype: :map_key, - type_spec: nil - } - ] - end - - test "suggestion for map fields in @attribute.key call syntax" do - buffer = """ - defmodule MyServer do - @var_1 %{key_1: 1, key_2: %{abc: 123}} - def func do - @var_1.k - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 13) - |> Enum.filter(&(&1.type in [:field])) - - assert list == [ - %{ - name: "key_1", - origin: nil, - type: :field, - call?: true, - subtype: :map_key, - type_spec: nil - }, - %{ - name: "key_2", - origin: nil, - type: :field, - call?: true, - subtype: :map_key, - type_spec: nil - } - ] - end - - test "suggestion for functions in variable.key call syntax" do - buffer = """ - defmodule MyServer do - def func do - var_1 = Atom - var_1.to_str - end - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 17) - |> Enum.filter(&(&1.type in [:function])) - - assert [%{name: "to_string", origin: "Atom", type: :function}] = list - end - - test "suggestion for vars in struct update" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - some_field: "" - ] - - def some_func() do - false - end - - def func(%MyServer{} = some_arg) do - %MyServer{some - end - end - """ - - list = ElixirSense.suggestions(buffer, 12, 19) - - assert [ - %{ - origin: "MyServer", - type: :field, - name: "some_field", - call?: false, - subtype: :struct_field - }, - %{name: "some_arg", type: :variable}, - %{name: "some_func", type: :function} - ] = list - end - - test "suggestion for fields in struct update" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - some_field: "" - ] - - def func(%MyServer{} = some_arg) do - %MyServer{some_arg | fiel - end - end - """ - - list = ElixirSense.suggestions(buffer, 8, 30) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: "MyServer", - subtype: :struct_field, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for fields in struct update variable when module not set" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - some_field: "" - ] - - def func(%MyServer{} = some_arg) do - %{some_arg | fiel - end - end - """ - - list = ElixirSense.suggestions(buffer, 8, 22) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: "MyServer", - subtype: :struct_field, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for fields in struct update attribute when module not set" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - some_field: "" - ] - - @str %MyServer{} - - %{@str | fiel - end - """ - - list = ElixirSense.suggestions(buffer, 9, 16) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: "MyServer", - subtype: :struct_field, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for fields in struct update when struct type is var" do - buffer = """ - defmodule MyServer do - def func(%var{field_1: "asd"} = some_arg) do - %{some_arg | fiel - end - end - """ - - list = ElixirSense.suggestions(buffer, 3, 22) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: nil, - subtype: :struct_field, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for fields in struct when struct type is attribute" do - buffer = """ - defmodule MyServer do - @t Time - %@t{ho - end - """ - - list = ElixirSense.suggestions(buffer, 3, 9) - - assert list == [ - %{ - call?: false, - name: "hour", - origin: "Time", - subtype: :struct_field, - type: :field, - type_spec: "Calendar.hour()" - } - ] - end - - test "suggestion for keys in map update" do - buffer = """ - defmodule MyServer do - def func(%{field_1: "asd"} = some_arg) do - %{some_arg | fiel - end - end - """ - - list = ElixirSense.suggestions(buffer, 3, 22) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: nil, - subtype: :map_key, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for fuzzy struct fields" do - buffer = """ - defmodule MyServer do - def func(%{field_1: "asd"} = some_arg) do - %{some_arg | fie1 - end - end - """ - - list = ElixirSense.suggestions(buffer, 3, 22) - - assert list == [ - %{ - call?: false, - name: "field_1", - origin: nil, - subtype: :map_key, - type: :field, - type_spec: nil - } - ] - end - - test "suggestion for funcs and vars in struct" do - buffer = """ - defmodule MyServer do - defstruct [ - field_1: nil, - some_field: "" - ] - - def other_func(), do: :ok - - def func(%MyServer{} = some_arg, other_arg) do - %MyServer{some_arg | - field_1: ot - end - end - """ - - list = ElixirSense.suggestions(buffer, 11, 18) - - assert [ - %{name: "other_arg", type: :variable}, - %{ - name: "other_func", - type: :function, - args: "", - args_list: [], - arity: 0, - def_arity: 0, - origin: "MyServer", - spec: "", - summary: "", - visibility: :public, - snippet: nil, - metadata: %{} - } - ] = list - end - - test "no suggestion of fields when the module is not a struct" do - buffer = """ - defmodule Mod do - %Enum{ - end - """ - - list = ElixirSense.suggestions(buffer, 2, 9) - assert Enum.any?(list, fn %{type: type} -> type == :field end) == false - end - - test "suggest struct fields when metadata function evaluates to struct" do - buffer = """ - defmodule Mod do - defstruct [field: nil] - @type t :: %__MODULE__{} - - @spec fun() :: t - def fun(), do: %Mod{} - - def some do - var = fun() - var. - end - end - """ - - list = ElixirSense.suggestions(buffer, 10, 9) - - assert [ - %{call?: true, name: "__struct__", origin: "Mod"}, - %{call?: true, name: "field", origin: "Mod", subtype: :struct_field, type: :field} - ] = list - end - - test "suggest struct fields when metadata function evaluates to remote type" do - buffer = """ - defmodule Mod do - @spec fun() :: NaiveDateTime.t() - def fun(), do: NaiveDateTime.new(1, 2) - - def some do - var = fun() - var.h - end - end - """ - - list = ElixirSense.suggestions(buffer, 7, 10) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - end - - test "suggest struct fields when metadata function evaluates to remote type aliased" do - buffer = """ - defmodule Mod do - alias NaiveDateTime, as: MyType - @spec fun() :: MyType.t() - def fun(), do: MyType.new(1, 2) - - def some do - var = fun() - var.h - end - end - """ - - list = ElixirSense.suggestions(buffer, 8, 10) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - end - - test "suggest struct fields when metadata function evaluates to remote type __MODULE__" do - buffer = """ - defmodule Mod do - @type t :: NaiveDateTime.t() - - @spec fun() :: __MODULE__.t() - def fun(), do: nil - - def some do - var = fun() - var.h - end - end - """ - - list = ElixirSense.suggestions(buffer, 9, 10) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - end - - test "suggest struct fields when metadata function evaluates to remote type __MODULE__.Submodule" do - buffer = """ - defmodule Mod do - defmodule Sub do - @type t :: NaiveDateTime.t() - end - - @spec fun() :: __MODULE__.Sub.t() - def fun(), do: nil - - def some do - var = fun() - var.h - end - end - """ - - list = ElixirSense.suggestions(buffer, 11, 10) - - assert [%{name: "hour", origin: "NaiveDateTime"}] = list - end - - test "suggest struct fields when variable is struct" do - buffer = """ - defmodule Abc do - defstruct [:cde] - end - - defmodule Mod do - def my() do - some(abc) - abc = %Abc{cde: 1} - abc. - end - end - """ - - list = ElixirSense.suggestions(buffer, 9, 9) - - assert [ - %{call?: true, name: "__struct__", origin: "Abc"}, - %{call?: true, name: "cde", origin: "Abc", subtype: :struct_field, type: :field} - ] = list - end - - test "suggest struct fields when variable is rebound to struct" do - buffer = """ - defmodule Abc do - defstruct [:cde] - end - - defmodule Mod do - def my() do - abc = 1 - some(abc) - abc = %Abc{cde: 1} - abc.cde - abc = 1 - end - end - """ - - list = ElixirSense.suggestions(buffer, 10, 9) - - assert [ - %{call?: true, name: "__struct__", origin: "Abc"}, - %{call?: true, name: "cde", origin: "Abc", subtype: :struct_field, type: :field} - ] = list - end - - test "suggest struct fields when attribute is struct" do - buffer = """ - defmodule Abc do - defstruct [:cde] - end - - defmodule Mod do - @abc %Abc{cde: 1} - @abc. - end - """ - - list = ElixirSense.suggestions(buffer, 7, 8) - - assert [ - %{call?: true, name: "__struct__", origin: "Abc"}, - %{call?: true, name: "cde", origin: "Abc", subtype: :struct_field, type: :field} - ] = list - end - - test "suggest struct fields when attribute is rebound to struct" do - buffer = """ - defmodule Abc do - defstruct [:cde] - end - - defmodule Mod do - @abc 1 - @abc %Abc{cde: 1} - @abc. - end - """ - - list = ElixirSense.suggestions(buffer, 8, 8) - - assert [ - %{call?: true, name: "__struct__", origin: "Abc"}, - %{call?: true, name: "cde", origin: "Abc", subtype: :struct_field, type: :field} - ] = list - end - - test "suggest modules to alias" do - buffer = """ - defmodule MyModule do - alias Str - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 12) - |> Enum.filter(fn s -> s.type == :module end) - - assert [ - %{name: "Stream"}, - %{name: "String"}, - %{name: "StringIO"} - ] = list |> Enum.filter(&(&1.name |> String.starts_with?("Str"))) - end - - test "suggest modules to alias with __MODULE__" do - buffer = """ - defmodule Stream do - alias __MODULE__.Re - end - """ - - list = ElixirSense.suggestions(buffer, 2, 22) - - assert [%{name: "Reducers", type: :module} | _] = list - end - - test "suggest modules to alias in multi alias syntax" do - buffer = """ - defmodule MyModule do - alias Stream.{Re - end - """ - - list = ElixirSense.suggestions(buffer, 2, 19) - - assert [%{name: "Reducers", type: :module}] = list - end - - test "suggest modules to alias in multi alias syntax with __MODULE__" do - buffer = """ - defmodule Stream do - alias __MODULE__.{Re - end - """ - - list = ElixirSense.suggestions(buffer, 2, 23) - - assert [%{name: "Reducers", type: :module}] = list - end - - describe "suggestion for param options" do - test "suggest more than one option" do - buffer = "Local.func_with_options(" - - list = suggestions_by_type(:param_option, buffer) - assert length(list) > 1 - end - - test "are fuzzy" do - buffer = "Local.func_with_options(remo_wi" - list = suggestions_by_type(:param_option, buffer) - assert [%{name: "remote_with_params_o"}] = list - end - - test "handles macros" do - buffer = """ - require Local - Local.macro_with_options(remo_wi\ - """ - - list = suggestions_by_type(:param_option, buffer) - assert [%{name: "remote_with_params_o"}] = list - end - - test "suggest the same list when options are already set" do - buffer1 = "Local.func_with_options(" - buffer2 = "Local.func_with_options(local_o: :an_atom, " - - capture_io(:stderr, fn -> - result1 = suggestions_by_type(:param_option, buffer1) - result2 = suggestions_by_type(:param_option, buffer2) - send(self(), {:results, result1, result2}) - end) - - assert_received {:results, result1, result2} - assert result1 == result2 - end - - test "options as inline list" do - buffer = "Local.func_with_options_as_inline_list(" - - assert %{type_spec: "local_t()", expanded_spec: "@type local_t() :: atom()"} = - suggestion_by_name("local_o", buffer) - - assert %{ - type_spec: "keyword()", - expanded_spec: """ - @type keyword() :: [ - {atom(), any()} - ]\ - """ - } = suggestion_by_name("builtin_o", buffer) - end - - test "options vars defined in when" do - type_spec = "local_t()" - origin = "ElixirSenseExample.ModuleWithTypespecs.Local" - spec = "@type local_t() :: atom()" - - buffer = "Local.func_with_option_var_defined_in_when(" - suggestion = suggestion_by_name("local_o", buffer) - - assert suggestion.type_spec == type_spec - assert suggestion.origin == origin - assert suggestion.expanded_spec == spec - - buffer = "Local.func_with_options_var_defined_in_when(" - suggestion = suggestion_by_name("local_o", buffer) - - assert suggestion.type_spec == type_spec - assert suggestion.origin == origin - assert suggestion.expanded_spec == spec - end - - test "opaque type internal structure is not revealed" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("opaque_o", buffer) - - assert suggestion.type_spec == "opaque_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - assert suggestion.expanded_spec == "@opaque opaque_t()" - assert suggestion.doc == "Local opaque type" - end - - test "private type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("private_o", buffer) - - assert suggestion.type_spec == "private_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - assert suggestion.expanded_spec == "@typep private_t() :: atom()" - assert suggestion.doc == "" - end - - test "local type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("local_o", buffer) - - assert suggestion.type_spec == "local_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - assert suggestion.expanded_spec == "@type local_t() :: atom()" - assert suggestion.doc == "Local type" - end - - test "local type with params" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("local_with_params_o", buffer) - - assert suggestion.type_spec == "local_t(atom(), integer())" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - assert suggestion.expanded_spec =~ "@type local_t(a, b) ::" - end - - test "basic type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("basic_o", buffer) - - assert suggestion.type_spec == "pid()" - assert suggestion.origin == "" - assert suggestion.expanded_spec == "" - assert suggestion.doc == "A process identifier, pid, identifies a process" - end - - test "basic type with params" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("basic_with_params_o", buffer) - - assert suggestion.type_spec == "[atom(), ...]" - assert suggestion.origin == "" - assert suggestion.expanded_spec == "" - assert suggestion.doc == "Non-empty proper list" - end - - test "built-in type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("builtin_o", buffer) - - assert suggestion.type_spec == "keyword()" - assert suggestion.origin == "" - - assert suggestion.expanded_spec == """ - @type keyword() :: [ - {atom(), any()} - ]\ - """ - - assert suggestion.doc == "A keyword list" - end - - test "built-in type with params" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("builtin_with_params_o", buffer) - - assert suggestion.type_spec == "keyword(term())" - assert suggestion.origin == "" - assert suggestion.expanded_spec =~ "@type keyword(t()) ::" - assert suggestion.doc == "A keyword list with values of type `t`" - end - - test "union type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("union_o", buffer) - - assert suggestion.type_spec == "union_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - - assert suggestion.expanded_spec == """ - @type union_t() :: - atom() | integer()\ - """ - end - - test "list type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("list_o", buffer) - - assert suggestion.type_spec == "list_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - assert suggestion.expanded_spec =~ "@type list_t() ::" - end - - test "remote type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("remote_o", buffer) - - assert suggestion.type_spec == "ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - assert suggestion.expanded_spec == "@type remote_t() :: atom()" - assert suggestion.doc == "Remote type" - end - - test "remote type with args" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("remote_with_params_o", buffer) - - assert suggestion.type_spec == - "ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t(atom(), integer())" - - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - assert suggestion.expanded_spec =~ "@type remote_t(a, b) ::" - assert suggestion.doc == "Remote type with params" - end - - test "remote erlang type with doc" do - buffer = "Local.func_with_erlang_type_options(" - suggestion = suggestion_by_name("erlang_t", buffer) - - assert suggestion.type_spec == - ":erlang.time_unit()" - - assert suggestion.origin == ":erlang" - - assert suggestion.expanded_spec == - "@type time_unit() ::\n pos_integer()\n | :second\n | :millisecond\n | :microsecond\n | :nanosecond\n | :native\n | :perf_counter\n | deprecated_time_unit()" - - if ExUnitConfig.erlang_eep48_supported() do - assert suggestion.doc =~ "Supported time unit representations" - end - end - - test "remote aliased type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("remote_aliased_o", buffer) - - assert suggestion.type_spec == "remote_aliased_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - - assert suggestion.expanded_spec == """ - @type remote_aliased_t() :: - ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t() - | ElixirSenseExample.ModuleWithTypespecs.Remote.remote_list_t()\ - """ - - assert suggestion.doc == "Remote type from aliased module" - end - - test "remote aliased inline type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("remote_aliased_inline_o", buffer) - - assert suggestion.type_spec == "ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t()" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - assert suggestion.expanded_spec == "@type remote_t() :: atom()" - assert suggestion.doc == "Remote type" - end - - test "inline list type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("inline_list_o", buffer) - - assert suggestion.type_spec == "[:trace | :log]" - assert suggestion.origin == "" - assert suggestion.expanded_spec == "" - assert suggestion.doc == "" - end - - test "non existent type" do - buffer = "Local.func_with_options(" - suggestion = suggestion_by_name("non_existent_o", buffer) - - assert suggestion.type_spec == - "ElixirSenseExample.ModuleWithTypespecs.Remote.non_existent()" - - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - assert suggestion.expanded_spec == "" - assert suggestion.doc == "" - end - - test "named options" do - buffer = "Local.func_with_named_options(" - assert suggestion_by_name("local_o", buffer).type_spec == "local_t()" - end - - test "options with only one option" do - buffer = "Local.func_with_one_option(" - assert suggestion_by_name("option_1", buffer).type_spec == "integer()" - end - - test "union of options" do - buffer = "Local.func_with_union_of_options(" - - assert suggestion_by_name("local_o", buffer).type_spec == "local_t()" - assert suggestion_by_name("option_1", buffer).type_spec == "atom()" - end - - test "union of options inline" do - buffer = "Local.func_with_union_of_options_inline(" - - assert suggestion_by_name("local_o", buffer).type_spec == "local_t()" - assert suggestion_by_name("option_1", buffer).type_spec == "atom()" - end - - test "union of options (local and remote) as type + inline" do - buffer = "Local.func_with_union_of_options_as_type(" - assert suggestion_by_name("option_1", buffer).type_spec == "boolean()" - - suggestion = suggestion_by_name("remote_option_1", buffer) - assert suggestion.type_spec == "ElixirSenseExample.ModuleWithTypespecs.Remote.remote_t()" - assert suggestion.expanded_spec == "@type remote_t() :: atom()" - assert suggestion.doc == "Remote type" - end - - test "atom only options" do - buffer = ":ets.new(:name," - - assert suggestion_by_name("duplicate_bag", buffer).type_spec == "" - assert suggestion_by_name("named_table", buffer).doc == "" - end - - test "format type spec" do - buffer = "Local.func_with_options(" - - assert suggestion_by_name("large_o", buffer).expanded_spec == """ - @type large_t() :: - pid() - | port() - | (registered_name :: - atom()) - | {registered_name :: - atom(), node()}\ - """ - end - end - - describe "suggestions for typespecs" do - test "remote types - filter list of typespecs" do - buffer = """ - defmodule My do - @type a :: Remote.remote_t\ - """ - - list = suggestions_by_type(:type_spec, buffer) - assert length(list) == 4 - end - - test "remote types - retrieve info from typespecs" do - buffer = """ - defmodule My do - @type a :: Remote.\ - """ - - suggestion = suggestion_by_name("remote_list_t", buffer) - - assert suggestion.spec == """ - @type remote_list_t() :: [ - remote_t() - ]\ - """ - - assert suggestion.signature == "remote_list_t()" - assert suggestion.arity == 0 - assert suggestion.doc == "Remote list type" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - end - - test "on specs" do - buffer = """ - defmodule My do - @spec a() :: Remote.\ - """ - - assert %{name: "remote_list_t"} = suggestion_by_name("remote_list_t", buffer) - - buffer = """ - defmodule My do - @spec a(Remote.) :: integer - end - """ - - assert %{name: "remote_list_t"} = suggestion_by_name("remote_list_t", buffer, 2, 18) - - buffer = """ - defmodule My do - @spec a(Remote.) - end - """ - - assert %{name: "remote_list_t"} = suggestion_by_name("remote_list_t", buffer, 2, 18) - end - - test "on callbacks" do - buffer = """ - defmodule My do - @callback a() :: none - end - """ - - assert [_, _] = suggestions_by_name("nonempty_list", buffer, 2, 24) - - buffer = """ - defmodule My do - @callback a(none) :: integer - end - """ - - assert [_, _] = suggestions_by_name("nonempty_list", buffer, 2, 19) - - buffer = """ - defmodule My do - @callback a(none) - end - """ - - assert [_, _] = suggestions_by_name("nonempty_list", buffer, 2, 19) - end - - test "remote types - by attribute" do - buffer = """ - defmodule My do - @type my_type :: integer - @attr My - @type some :: @attr.my\ - """ - - [suggestion_1] = suggestions_by_name("my_type", buffer) - - assert suggestion_1.signature == "my_type()" - end - - test "remote types - by __MODULE__" do - buffer = """ - defmodule My do - @type my_type :: integer - @type some :: __MODULE__.my\ - """ - - [suggestion_1] = suggestions_by_name("my_type", buffer) - - assert suggestion_1.signature == "my_type()" - end - - test "remote types - retrieve info from typespecs with params" do - buffer = """ - defmodule My do - @type a :: Remote.\ - """ - - [suggestion_1, suggestion_2] = suggestions_by_name("remote_t", buffer) - - assert suggestion_1.spec == "@type remote_t() :: atom()" - assert suggestion_1.signature == "remote_t()" - assert suggestion_1.arity == 0 - assert suggestion_1.doc == "Remote type" - assert suggestion_1.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - - assert suggestion_2.spec =~ "@type remote_t(a, b) ::" - assert suggestion_2.signature == "remote_t(a, b)" - assert suggestion_2.arity == 2 - assert suggestion_2.doc == "Remote type with params" - assert suggestion_2.origin == "ElixirSenseExample.ModuleWithTypespecs.Remote" - end - - test "local types - filter list of typespecs" do - buffer = """ - defmodule ElixirSenseExample.ModuleWithTypespecs.Local do - # The types are defined in `test/support/module_with_typespecs.ex` - @type my_type :: local_ - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 26) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert length(list) == 2 - end - - test "typespec fuzzy match" do - buffer = """ - defmodule ElixirSenseExample.ModuleWithTypespecs.Local do - # The types are defined in `test/support/module_with_typespecs.ex` - @type fuzzy_type :: loca_ - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 27) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - [suggestion, _] = list - - assert suggestion.spec == "@type local_t() :: atom()" - assert suggestion.signature == "local_t()" - assert suggestion.arity == 0 - assert suggestion.doc == "Local type" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - end - - test "local types - retrieve info from typespecs" do - buffer = """ - defmodule ElixirSenseExample.ModuleWithTypespecs.Local do - # The types are defined in `test/support/module_with_typespecs.ex` - @type my_type :: local_t - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 3, 27) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - [suggestion, _] = list - - assert suggestion.spec == "@type local_t() :: atom()" - assert suggestion.signature == "local_t()" - assert suggestion.arity == 0 - assert suggestion.doc == "Local type" - assert suggestion.origin == "ElixirSenseExample.ModuleWithTypespecs.Local" - end - - test "builtin types - filter list of typespecs" do - buffer = "defmodule My, do: @type my_type :: lis" - - list = suggestions_by_type(:type_spec, buffer) - assert length(list) == 2 - end - - test "builtin types - retrieve info from typespecs" do - buffer = "defmodule My, do: @type my_type :: lis" - - [suggestion | _] = suggestions_by_type(:type_spec, buffer) - - assert suggestion.spec == "@type list() :: [any()]" - assert suggestion.signature == "list()" - assert suggestion.arity == 0 - assert suggestion.doc == "A list" - assert suggestion.origin == nil - end - - test "builtin types - retrieve info from typespecs with params" do - buffer = "defmodule My, do: @type my_type :: lis" - - [_, suggestion | _] = suggestions_by_type(:type_spec, buffer) - - assert suggestion.spec == "@type list(t())" - assert suggestion.signature == "list(t())" - assert suggestion.arity == 1 - assert suggestion.doc == "Proper list ([]-terminated)" - assert suggestion.origin == nil - end - - test "builtin types - retrieve info from basic types" do - buffer = "defmodule My, do: @type my_type :: int" - - [_, suggestion | _] = suggestions_by_type(:type_spec, buffer) - - assert suggestion.spec == "@type integer()" - assert suggestion.signature == "integer()" - assert suggestion.arity == 0 - assert suggestion.doc == "An integer number" - assert suggestion.origin == nil - end - - test "erlang types" do - buffer = "defmodule My, do: @type my_type :: :erlang.time_" - - suggestions = suggestions_by_type(:type_spec, buffer) - - assert [ - %{ - arity: 0, - doc: summary, - name: "time_unit", - origin: ":erlang", - signature: "time_unit()", - spec: - "@type time_unit() ::\n pos_integer()\n | :second\n | :millisecond\n | :microsecond\n | :nanosecond\n | :native\n | :perf_counter\n | deprecated_time_unit()", - type: :type_spec - } - ] = suggestions - - if ExUnitConfig.erlang_eep48_supported() do - assert summary =~ "Supported time unit representations:" - end - end - - test "no erlang private types" do - buffer = "defmodule My, do: @type my_type :: :erlang.cpu_topo" - - suggestions = suggestions_by_type(:type_spec, buffer) - - assert [] == suggestions - end - - test "type with @typedoc false" do - buffer = - "defmodule My, do: @type my_type :: ElixirSenseExample.ModuleWithDocs.some_type_doc_false" - - suggestions = suggestions_by_type(:type_spec, buffer) - - assert [ - %{ - arity: 0, - doc: "", - name: "some_type_doc_false", - origin: "ElixirSenseExample.ModuleWithDocs", - signature: "some_type_doc_false()", - spec: "@type some_type_doc_false() ::" <> _, - type: :type_spec, - metadata: %{} - } - ] = suggestions - end - - test "local types from metadata" do - buffer = """ - defmodule MyModule do - @typep my_local_t :: integer - @typep my_local_arg_t(a, b) :: {a, b} - @type my_type :: my_loc - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 26) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert [suggestion1, suggestion2] = list - - assert %{ - arity: 0, - name: "my_local_t", - origin: "MyModule", - type: :type_spec, - signature: "my_local_t()", - args_list: [], - doc: "", - spec: "@typep my_local_t :: integer", - metadata: %{} - } == suggestion2 - - assert %{ - arity: 2, - name: "my_local_arg_t", - origin: "MyModule", - type: :type_spec, - signature: "my_local_arg_t(a, b)", - args_list: ["a", "b"], - doc: "", - spec: "@typep my_local_arg_t(a, b) :: {a, b}", - metadata: %{} - } == suggestion1 - end - - test "suggest local types from metadata even if defined after the cursor" do - buffer = """ - defmodule MyModule do - @type my_type :: my_loc - # ^ - - @typep my_local_t :: integer - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 26) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert [%{name: "my_local_t"}] = list - end - - test "return docs and meta on local types" do - buffer = """ - defmodule MyModule do - @type my_type :: my_loc - # ^ - - @typedoc "Some" - @typedoc since: "1.2.3" - @type my_local_t :: integer - end - """ - - list = - ElixirSense.suggestions(buffer, 2, 26) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert [%{name: "my_local_t", doc: "Some", metadata: %{since: "1.2.3"}}] = list - end - - test "local types from metadata external call - private types are not suggested" do - buffer = """ - defmodule MyModule do - @type my_local_t :: integer - @typep my_local_arg_t(a, b) :: {a, b} - @type my_type :: MyModule.my_loc - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 4, 35) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert [suggestion1] = list - - assert %{ - arity: 0, - name: "my_local_t", - origin: "MyModule", - type: :type_spec, - signature: "my_local_t()", - args_list: [], - doc: "", - spec: "@type my_local_t :: integer", - metadata: %{} - } == suggestion1 - end - - test "remote public and opaque types from metadata" do - buffer = """ - defmodule SomeModule do - @typep my_local_priv_t :: integer - @type my_local_pub_t(a, b) :: {a, b} - @opaque my_local_op_t() :: my_local_priv_t - end - - defmodule MyModule do - alias SomeModule, as: Some - @type my_type :: Some.my_loc - # ^ - end - """ - - list = - ElixirSense.suggestions(buffer, 9, 31) - |> Enum.filter(fn %{type: t} -> t == :type_spec end) - - assert [suggestion1, suggestion2] = list - - assert %{ - arity: 2, - name: "my_local_pub_t", - origin: "SomeModule", - type: :type_spec, - signature: "my_local_pub_t(a, b)", - args_list: ["a", "b"], - doc: "", - spec: "@type my_local_pub_t(a, b) :: {a, b}", - metadata: %{} - } == suggestion2 - - assert %{ - arity: 0, - name: "my_local_op_t", - origin: "SomeModule", - type: :type_spec, - signature: "my_local_op_t()", - args_list: [], - doc: "", - spec: "@opaque my_local_op_t()", - metadata: %{opaque: true} - } == suggestion1 - end - end - - test "suggestion understands alias shadowing" do - # ordinary alias - buffer = """ - defmodule ElixirSenseExample.OtherModule do - alias ElixirSenseExample.SameModule - def some_fun() do - SameModule.te - end - end - """ - - assert [ - %{origin: "ElixirSenseExample.SameModule"} - ] = ElixirSense.suggestions(buffer, 4, 17) - - # alias shadowing scope/inherited aliases - buffer = """ - defmodule ElixirSenseExample.Abc.SameModule do - alias List, as: SameModule - alias ElixirSenseExample.SameModule - def some_fun() do - SameModule.te - end - end - """ - - assert [ - %{origin: "ElixirSenseExample.SameModule"} - ] = ElixirSense.suggestions(buffer, 5, 17) - - buffer = """ - defmodule ElixirSenseExample.Abc.SameModule do - require Logger, as: ModuleB - require ElixirSenseExample.SameModule, as: SameModule - SameModule.so - end - """ - - assert [ - %{origin: "ElixirSenseExample.SameModule"} - ] = ElixirSense.suggestions(buffer, 4, 15) - end - - test "operator" do - buffer = """ - defmodule ElixirSenseExample.OtherModule do - def some_fun() do - a + - end - end - """ - - assert [%{name: "+"}, %{name: "+"}, %{name: "++"}] = - ElixirSense.suggestions(buffer, 3, 8) |> Enum.filter(&("#{&1.name}" =~ "+")) - end - - @tag requires_elixir_1_13: true - test "sigil" do - buffer = """ - defmodule ElixirSenseExample.OtherModule do - def some_fun() do - ~ - end - end - """ - - suggestions = ElixirSense.suggestions(buffer, 3, 6) - - assert [ - %{ - args: "term, modifiers", - arity: 2, - name: "~w", - summary: "Handles the sigil `~w` for list of words.", - type: :macro - } - ] = suggestions |> Enum.filter(&(&1.name == "~w")) - end - - test "bitstring options" do - buffer = """ - defmodule ElixirSenseExample.OtherModule do - alias ElixirSenseExample.SameModule - def some_fun() do - <> - end - end - """ - - options = - ElixirSense.suggestions(buffer, 4, 12) - |> Enum.filter(&(&1.type == :bitstring_option)) - |> Enum.map(& &1.name) - - assert "integer" in options - assert "native" in options - assert "signed" in options - - buffer = """ - defmodule ElixirSenseExample.OtherModule do - alias ElixirSenseExample.SameModule - def some_fun() do - <> - end - end - """ - - ["integer"] = - ElixirSense.suggestions(buffer, 4, 15) - |> Enum.filter(&(&1.type == :bitstring_option)) - |> Enum.map(& &1.name) - - buffer = """ - defmodule ElixirSenseExample.OtherModule do - alias ElixirSenseExample.SameModule - def some_fun() do - <> - end - end - """ - - options = - ElixirSense.suggestions(buffer, 4, 33) - |> Enum.filter(&(&1.type == :bitstring_option)) - |> Enum.map(& &1.name) - - assert "unit" in options - assert "size" in options - - buffer = """ - defmodule ElixirSenseExample.OtherModule do - alias ElixirSenseExample.SameModule - def some_fun() do - <> - end - end - """ - - ["native"] = - ElixirSense.suggestions(buffer, 4, 35) - |> Enum.filter(&(&1.type == :bitstring_option)) - |> Enum.map(& &1.name) - end - - test "function with default args from metadata" do - buffer = """ - defmodule SomeSchema do - def my_func(a, b \\\\ "") - def my_func(1, b), do: :ok - def my_func(2, b), do: :ok - - def d() do - my_ - end - end - """ - - suggestions = ElixirSense.suggestions(buffer, 7, 8) - - assert [ - %{args: "a, b \\\\ \"\"", arity: 1, def_arity: 2}, - %{args: "a, b \\\\ \"\"", arity: 2, def_arity: 2} - ] = suggestions - end - - test "records from metadata" do - buffer = """ - defmodule SomeSchema do - require Record - Record.defrecord(:user, name: "john", age: 25) - @type user :: record(:user, name: String.t(), age: integer) - - def d() do - w = us - end - end - """ - - suggestions = ElixirSense.suggestions(buffer, 7, 11) - - assert [ - %{ - args: "args \\\\ []", - arity: 0, - name: "user", - summary: "", - type: :macro, - args_list: ["args \\\\ []"], - def_arity: 1, - metadata: %{}, - origin: "SomeSchema", - snippet: nil, - spec: "", - visibility: :public - }, - %{ - args: "args \\\\ []", - arity: 1, - name: "user", - summary: "", - type: :macro, - args_list: ["args \\\\ []"], - def_arity: 1, - metadata: %{}, - origin: "SomeSchema", - snippet: nil, - spec: "", - visibility: :public - }, - %{ - args: "record, args", - args_list: ["record", "args"], - arity: 2, - def_arity: 2, - metadata: %{}, - name: "user", - origin: "SomeSchema", - snippet: nil, - spec: "", - summary: "", - type: :macro, - visibility: :public - } - ] = suggestions |> Enum.filter(&(&1.name == "user")) - end - - test "records from introspection" do - buffer = """ - defmodule SomeSchema do - require ElixirSenseExample.ModuleWithRecord, as: M - - def d() do - w = M.us - end - end - """ - - suggestions = ElixirSense.suggestions(buffer, 5, 12) - - assert [ - %{ - args: "args \\\\ []", - arity: 0, - name: "user", - summary: "", - type: :macro, - args_list: ["args \\\\ []"], - def_arity: 1, - metadata: %{}, - origin: "ElixirSenseExample.ModuleWithRecord", - snippet: nil, - spec: "", - visibility: :public - }, - %{ - args: "args \\\\ []", - arity: 1, - name: "user", - summary: "", - type: :macro, - args_list: ["args \\\\ []"], - def_arity: 1, - metadata: %{}, - origin: "ElixirSenseExample.ModuleWithRecord", - snippet: nil, - spec: "", - visibility: :public - }, - %{ - args: "record, args", - args_list: ["record", "args"], - arity: 2, - def_arity: 2, - metadata: %{}, - name: "user", - origin: "ElixirSenseExample.ModuleWithRecord", - snippet: nil, - spec: "", - summary: "", - type: :macro, - visibility: :public - } - ] = suggestions |> Enum.filter(&(&1.name == "user")) - end - - defp suggestions_by_type(type, buffer) do - {line, column} = get_last_line_and_column(buffer) - suggestions_by_type(type, buffer, line, column) - end - - defp suggestions_by_type(type, buffer, line, column) do - buffer - |> add_aliases("Local, Remote") - |> ElixirSense.suggestions(line + 1, column) - |> Enum.filter(fn %{type: t} -> t == type end) - |> Enum.sort() - end - - defp suggestions_by_name(name, buffer) do - {line, column} = get_last_line_and_column(buffer) - suggestions_by_name(name, buffer, line, column) - end - - defp suggestions_by_name(name, buffer, line, column) do - buffer - |> add_aliases("Local, Remote") - |> ElixirSense.suggestions(line + 1, column) - |> Enum.filter(fn - %{name: n} -> n == name - _ -> false - end) - |> Enum.sort() - end - - defp suggestion_by_name(name, buffer) do - {line, column} = get_last_line_and_column(buffer) - suggestion_by_name(name, buffer, line, column) - end - - defp suggestion_by_name(name, buffer, line, column) do - [suggestion] = suggestions_by_name(name, buffer, line, column) - suggestion - end - - defp get_last_line_and_column(buffer) do - str_lines = buffer |> Source.split_lines() - line = length(str_lines) - column = (str_lines |> List.last() |> String.length()) + 1 - {line, column} - end - - defp add_aliases(buffer, aliases) do - "alias ElixirSenseExample.ModuleWithTypespecs.{#{aliases}}\n" <> buffer - end -end diff --git a/test/elixir_sense_test.exs b/test/elixir_sense_test.exs deleted file mode 100644 index 0f04af0a..00000000 --- a/test/elixir_sense_test.exs +++ /dev/null @@ -1,5 +0,0 @@ -defmodule ElixirSenseTest do - use ExUnit.Case, async: true - - doctest ElixirSense -end