From 966279509e7a1b7b161d0741e2a0de8b26cc1c80 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 30 Jul 2023 10:59:00 +0200 Subject: [PATCH 1/9] fix order of typespec cuggestions and rendering of builtin types --- lib/elixir_sense/core/type_info.ex | 16 +++++++++++++--- .../providers/suggestion/reducers/type_specs.ex | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/elixir_sense/core/type_info.ex b/lib/elixir_sense/core/type_info.ex index 8ee9acce..6314cac9 100644 --- a/lib/elixir_sense/core/type_info.ex +++ b/lib/elixir_sense/core/type_info.ex @@ -113,10 +113,20 @@ defmodule ElixirSense.Core.TypeInfo do for( {key, value} <- BuiltinTypes.all(), - type_ast <- [value[:spec]], - spec <- [format_type_spec_ast(type_ast, :type, line_length: @param_option_spec_line_length)], + # TODO is this line_length needed? + spec = + case value do + %{spec: spec} -> + format_type_spec_ast(spec, :type, line_length: @param_option_spec_line_length) + + %{signature: signature} -> + "@type #{signature}" + + _ -> + "@type #{key}()" + end, signature <- [ - value[:signature] || TypeAst.extract_signature(type_ast) || "#{key}()" + value[:signature] || TypeAst.extract_signature(value[:spec]) || "#{key}()" ], {name, arity} = extract_name_and_arity.(key), doc = value[:doc] || "", diff --git a/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex b/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex index 4a6c44c9..9009c301 100644 --- a/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex +++ b/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex @@ -104,6 +104,7 @@ defmodule ElixirSense.Providers.Suggestion.Reducers.TypeSpecs do defp find_builtin_types({nil, hint}) do TypeInfo.find_all_builtin(&Matcher.match?("#{&1.name}", hint)) |> Enum.map(&type_info_to_suggestion(&1, nil)) + |> Enum.sort_by(fn %{name: name, arity: arity} -> {name, arity} end) end defp find_builtin_types({_mod, _hint}), do: [] @@ -113,6 +114,7 @@ defmodule ElixirSense.Providers.Suggestion.Reducers.TypeSpecs do |> Kernel.++(TypeInfo.find_all(actual_mod, &Matcher.match?("#{&1.name}", hint))) |> Enum.map(&type_info_to_suggestion(&1, actual_mod)) |> Enum.uniq_by(fn %{name: name, arity: arity} -> {name, arity} end) + |> Enum.sort_by(fn %{name: name, arity: arity} -> {name, arity} end) end defp find_metadata_types(actual_mod, {mod, hint}, metadata_types, module) do From 7731434d159d3e19913ed6e74eae3357cff95417 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 30 Jul 2023 11:09:01 +0200 Subject: [PATCH 2/9] fix test --- lib/elixir_sense/core/type_info.ex | 1 - test/elixir_sense/suggestions_test.exs | 14 +++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/elixir_sense/core/type_info.ex b/lib/elixir_sense/core/type_info.ex index 6314cac9..529c4db8 100644 --- a/lib/elixir_sense/core/type_info.ex +++ b/lib/elixir_sense/core/type_info.ex @@ -113,7 +113,6 @@ defmodule ElixirSense.Core.TypeInfo do for( {key, value} <- BuiltinTypes.all(), - # TODO is this line_length needed? spec = case value do %{spec: spec} -> diff --git a/test/elixir_sense/suggestions_test.exs b/test/elixir_sense/suggestions_test.exs index 2fdb0911..cfdfc974 100644 --- a/test/elixir_sense/suggestions_test.exs +++ b/test/elixir_sense/suggestions_test.exs @@ -3424,13 +3424,25 @@ defmodule ElixirSense.SuggestionsTest do [_, suggestion | _] = suggestions_by_type(:type_spec, buffer) - assert suggestion.spec == "" + 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_" From 95a6b02a0a925028a992e04e9ab6dbc2d7b5ddd0 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 30 Jul 2023 14:17:00 +0200 Subject: [PATCH 3/9] fix crashes when getting definition of __MODULE__ --- lib/elixir_sense/core/surround_context.ex | 8 +++- test/elixir_sense/definition_test.exs | 42 ++++++++++++++++++++ test/elixir_sense/references_test.exs | 48 ++++++++++++++++++++++- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/lib/elixir_sense/core/surround_context.ex b/lib/elixir_sense/core/surround_context.ex index ead43ee8..fd49a439 100644 --- a/lib/elixir_sense/core/surround_context.ex +++ b/lib/elixir_sense/core/surround_context.ex @@ -13,7 +13,13 @@ defmodule ElixirSense.Core.SurroundContext do def to_binding({:local_or_var, ~c"__MODULE__"}, current_module) do if current_module not in [nil, Elixir] do - {:atom, current_module} + {{:atom, current_module}, nil} + end + end + + def to_binding({:alias, {:local_or_var, ~c"__MODULE__"}, charlist}, current_module) do + if current_module not in [nil, Elixir] do + {{:atom, :"#{current_module}.#{charlist}"}, nil} end end diff --git a/test/elixir_sense/definition_test.exs b/test/elixir_sense/definition_test.exs index 2f65ecca..be71f266 100644 --- a/test/elixir_sense/definition_test.exs +++ b/test/elixir_sense/definition_test.exs @@ -1046,6 +1046,26 @@ defmodule ElixirSense.Providers.DefinitionTest do } 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 @@ -1089,6 +1109,28 @@ defmodule ElixirSense.Providers.DefinitionTest do } end + 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 = """ diff --git a/test/elixir_sense/references_test.exs b/test/elixir_sense/references_test.exs index 4951a6f5..494eb819 100644 --- a/test/elixir_sense/references_test.exs +++ b/test/elixir_sense/references_test.exs @@ -1036,7 +1036,9 @@ defmodule ElixirSense.Providers.ReferencesTest do end @tag requires_elixir_1_14: true - test "find references when module with __MODULE__ special form submodule", %{trace: trace} do + test "find references when module with __MODULE__ special form submodule function", %{ + trace: trace + } do buffer = """ defmodule ElixirSense.Providers.ReferencesTest.Modules do def func() do @@ -1061,8 +1063,27 @@ defmodule ElixirSense.Providers.ReferencesTest do ] end + 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", %{trace: trace} do + test "find references when module with __MODULE__ special form function", %{trace: trace} do buffer = """ defmodule ElixirSense.Providers.ReferencesTest.Modules do def func() do @@ -1085,6 +1106,29 @@ defmodule ElixirSense.Providers.ReferencesTest do ] 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 From be55f0d67db8509c7c9b6440a5be6c508e95e876 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 30 Jul 2023 21:27:57 +0200 Subject: [PATCH 4/9] remove not needed clause --- lib/elixir_sense/providers/docs.ex | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/lib/elixir_sense/providers/docs.ex b/lib/elixir_sense/providers/docs.ex index 546cc5d9..0f243754 100644 --- a/lib/elixir_sense/providers/docs.ex +++ b/lib/elixir_sense/providers/docs.ex @@ -103,28 +103,6 @@ defmodule ElixirSense.Providers.Docs do end end - defp mod_fun_docs( - {:variable, name} = type, - context, - binding_env, - env, - metadata - ) do - case Binding.expand(binding_env, type) do - :none -> - mod_fun_docs( - {nil, name}, - context, - binding_env, - env, - metadata - ) - - _ -> - nil - end - end - defp mod_fun_docs( {mod, fun}, context, From 92c2a7c707f3542b8e26a23b0d48e1b44e10954c Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 30 Jul 2023 22:25:47 +0200 Subject: [PATCH 5/9] format function docs in one place --- lib/elixir_sense/core/introspection.ex | 69 +++++++++++++++++++++----- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/lib/elixir_sense/core/introspection.ex b/lib/elixir_sense/core/introspection.ex index c7b2268c..1ec61fc2 100644 --- a/lib/elixir_sense/core/introspection.ex +++ b/lib/elixir_sense/core/introspection.ex @@ -128,6 +128,7 @@ defmodule ElixirSense.Core.Introspection do def get_all_docs({mod, fun, arity}, :mod_fun) do get_func_docs_md(mod, fun, arity) + |> Enum.map(&format_func_docs/1) |> join_docs end @@ -271,6 +272,22 @@ defmodule ElixirSense.Core.Introspection do (expected == :any or (is_integer(expected) and expected in (is - defaults)..is) or (is_tuple(expected) and elem(expected, 0) == :gte and is >= elem(expected, 1))) + defp format_func_docs(info) do + mod_str = inspect(info.module) + fun_str = Atom.to_string(info.function) + # fun_args_text = Enum.join(info.args, ", ") + # spec_text = "### Specs\n\n```\n#{info.specs |> Enum.join("\n")}\n```\n\n" + + spec_text = if info.specs != [] do + joined = Enum.join(info.specs, "\n") + "### Specs\n\n```\n#{joined}\n```\n\n" + else + "" + end + + "> #{mod_str}.#{fun_str}(#{info.fun_args_text})\n\n#{get_metadata_md(info.metadata)}#{spec_text}#{info.docs || ""}" + end + @spec get_func_docs_md(nil | module, atom, non_neg_integer | :any) :: list(markdown) def get_func_docs_md(mod, fun, arity) when mod != nil and fun in [:module_info, :behaviour_info, :__info__] do @@ -286,7 +303,16 @@ defmodule ElixirSense.Core.Introspection do spec_text = "### Specs\n\n```\n#{spec |> Enum.join("\n")}\n```\n\n" metadata = %{builtin: true} - "> #{mod_str}.#{fun_str}(#{fun_args_text})\n\n#{get_metadata_md(metadata)}#{spec_text}" + %{ + module: mod, + function: fun, + metadata: metadata, + specs: spec, + # args: args + fun_args_text: fun_args_text, + # TODO provide docs + docs: nil + } end end @@ -313,6 +339,7 @@ defmodule ElixirSense.Core.Introspection do |> String.replace("\\\\", "\\\\\\\\") else # as of otp 23 erlang callback implementation do not have signature metadata + # TODO get that from behaviour typespec? if arity == 0, do: "", else: Enum.map_join(1..arity, ", ", fn _ -> "term" end) end @@ -323,7 +350,15 @@ defmodule ElixirSense.Core.Introspection do TypeInfo.extract_params(params) |> Enum.map_join(", ", &Atom.to_string/1) end - "> #{inspect(mod)}.#{fun}(#{fun_args_text})\n\n#{get_metadata_md(metadata)}#{get_spec_text(mod, fun, arity, kind, metadata)}#{text}" + %{ + module: mod, + function: fun, + metadata: metadata, + # args: args, + specs: get_specs_text(mod, fun, arity, kind, metadata), + fun_args_text: fun_args_text, + docs: text + } end case results do @@ -342,7 +377,15 @@ defmodule ElixirSense.Core.Introspection do TypeInfo.get_function_specs(mod, fun, call_arity) do fun_args_text = TypeInfo.extract_params(params) |> Enum.map_join(", ", &Atom.to_string/1) - "> #{inspect(mod)}.#{fun}(#{fun_args_text})\n\n#{get_metadata_md(%{})}#{get_spec_text(mod, fun, arity, :function, %{})}" + %{ + module: mod, + function: fun, + metadata: %{}, + # args: args, + specs: get_specs_text(mod, fun, arity, :function, %{}), + fun_args_text: fun_args_text, + docs: nil + } end case results do @@ -373,7 +416,15 @@ defmodule ElixirSense.Core.Introspection do %{} end - "> #{inspect(mod)}.#{fun}(#{fun_args_text})\n\n#{get_metadata_md(metadata)}#{get_spec_text(mod, fun, arity, :function, metadata)}" + %{ + module: mod, + function: fun, + metadata: metadata, + # args: args, + specs: get_specs_text(mod, fun, arity, :function, metadata), + fun_args_text: fun_args_text, + docs: nil + } end end @@ -1006,19 +1057,11 @@ defmodule ElixirSense.Core.Introspection do TypeInfo.get_spec(module, function, arity) |> spec_to_string() end - def get_spec_text(mod, fun, arity, kind, metadata) do - specs = + def get_specs_text(mod, fun, arity, kind, metadata) do for arity <- (arity - Map.get(metadata, :defaults, 0))..arity, spec = get_spec_as_string(mod, fun, arity, kind, metadata), spec != "", do: spec - - if specs != [] do - joined = Enum.join(specs, "\n") - "### Specs\n\n```\n#{joined}\n```\n\n" - else - "" - end end # This function is used only for protocols so no macros From 7069fa509752481b5e7f13f3d8ea984153972833 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Tue, 1 Aug 2023 08:32:49 +0200 Subject: [PATCH 6/9] collect multiple spec and callback definitions --- lib/elixir_sense/core/state.ex | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/elixir_sense/core/state.ex b/lib/elixir_sense/core/state.ex index 60b92f84..950f0807 100644 --- a/lib/elixir_sense/core/state.ex +++ b/lib/elixir_sense/core/state.ex @@ -1142,7 +1142,7 @@ defmodule ElixirSense.Core.State do specs = current_module_variants |> Enum.reduce(state.specs, fn current_module, acc -> - info = + nil_info = case acc[{current_module, type_name, nil}] do nil -> type_info @@ -1158,9 +1158,25 @@ defmodule ElixirSense.Core.State do } end + arity_info = + case acc[{current_module, type_name, length(arg_names)}] do + nil -> + type_info + + %SpecInfo{positions: positions, args: args, specs: specs} = ti -> + %SpecInfo{ + ti + | positions: [pos | positions], + end_positions: [end_pos | ti.end_positions], + generated: [Keyword.get(options, :generated, false) | ti.generated], + args: [arg_names | args], + specs: [spec | specs] + } + end + acc - |> Map.put({current_module, type_name, nil}, info) - |> Map.put({current_module, type_name, length(arg_names)}, type_info) + |> Map.put({current_module, type_name, nil}, nil_info) + |> Map.put({current_module, type_name, length(arg_names)}, arity_info) end) %__MODULE__{state | specs: specs} From 1b1737b95ea85d1fa49e7211fd388e1af9fc1448 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Tue, 1 Aug 2023 08:33:59 +0200 Subject: [PATCH 7/9] provide docs for metadata functions, types and modules --- lib/elixir_sense/core/introspection.ex | 277 ++++++++++++----- lib/elixir_sense/providers/docs.ex | 2 +- test/elixir_sense/docs_test.exs | 399 +++++++++++++++++++++++-- 3 files changed, 575 insertions(+), 103 deletions(-) diff --git a/lib/elixir_sense/core/introspection.ex b/lib/elixir_sense/core/introspection.ex index 1ec61fc2..ca866ce9 100644 --- a/lib/elixir_sense/core/introspection.ex +++ b/lib/elixir_sense/core/introspection.ex @@ -45,6 +45,16 @@ defmodule ElixirSense.Core.Introspection do :application => Application } + defguard matches_arity?(is, expected) + when is != nil and + (expected == :any or is == expected or + (is_tuple(expected) and elem(expected, 0) == :gte and is >= elem(expected, 1))) + + defguard matches_arity_with_defaults?(is, defaults, expected) + when is != nil and + (expected == :any or (is_integer(expected) and expected in (is - defaults)..is) or + (is_tuple(expected) and elem(expected, 0) == :gte and is >= elem(expected, 1))) + @spec get_exports(module) :: [{atom, {non_neg_integer, :macro | :function}}] def get_exports(Elixir), do: [] @@ -122,18 +132,122 @@ defmodule ElixirSense.Core.Introspection do Keyword.has_key?(get_exports(module), fun) end - def get_all_docs({mod, nil, _}, :mod_fun) do - get_docs_md(mod) + def get_all_docs({mod, nil, _}, metadata, :mod_fun) do + doc_info = + metadata.mods_funs_to_positions + |> Enum.find_value(fn + {{^mod, nil, nil}, _fun_info} -> + %{ + module: mod, + # TODO + metadata: %{}, + doc: nil + } + + _ -> + false + end) + + if doc_info == nil do + get_docs_md(mod) + else + doc_info + end + |> format_module_doc end - def get_all_docs({mod, fun, arity}, :mod_fun) do - get_func_docs_md(mod, fun, arity) + def get_all_docs({mod, fun, arity}, metadata, :mod_fun) do + doc_infos = + metadata.mods_funs_to_positions + |> Enum.filter(fn + {{^mod, ^fun, a}, fun_info} when not is_nil(a) -> + default_args = fun_info.params |> Enum.at(-1) |> count_defaults() + + matches_arity_with_defaults?(a, default_args, arity) + + _ -> + false + end) + |> Enum.sort_by(fn {{^mod, ^fun, a}, _fun_info} -> a end) + |> Enum.map(fn {{^mod, ^fun, a}, fun_info} -> + fun_args_text = + fun_info.params |> List.last() |> Enum.with_index() |> Enum.map(¶m_to_var/1) + + specs = + case metadata.specs[{mod, fun, a}] do + nil -> + [] + + %State.SpecInfo{specs: specs} -> + specs |> Enum.reverse() + end + + %{ + module: mod, + function: fun, + # TODO + metadata: %{}, + specs: specs, + # args: args + fun_args_text: fun_args_text, + # TODO provide docs + docs: nil + } + end) + + doc_infos = + if doc_infos == [] do + get_func_docs_md(mod, fun, arity) + else + doc_infos + end + + doc_infos |> Enum.map(&format_func_docs/1) |> join_docs end - def get_all_docs({mod, fun, arity}, :type) do - get_type_docs_md(mod, fun, arity) + def get_all_docs({mod, fun, arity}, metadata, :type) do + doc_infos = + metadata.types + |> Enum.filter(fn + {{^mod, ^fun, a}, _type_info} when not is_nil(a) -> + matches_arity?(a, arity) + + _ -> + false + end) + |> Enum.sort_by(fn {{^mod, ^fun, a}, _fun_info} -> a end) + |> Enum.map(fn {{^mod, ^fun, a}, type_info} -> + args = type_info.args |> List.last() |> Enum.join(", ") + + spec = + case type_info.kind do + :opaque -> "@opaque #{fun}(#{args})" + _ -> List.last(type_info.specs) + end + + %{ + module: mod, + type: fun, + # TODO + metadata: %{}, + spec: spec, + args: args, + # TODO provide docs + docs: nil + } + end) + + doc_infos = + if doc_infos == [] do + get_type_docs_md(mod, fun, arity) + else + doc_infos + end + + doc_infos + |> Enum.map(&format_type_docs/1) |> join_docs end @@ -262,28 +376,19 @@ defmodule ElixirSense.Core.Introspection do end end - defguard matches_arity?(is, expected) - when is != nil and - (expected == :any or is == expected or - (is_tuple(expected) and elem(expected, 0) == :gte and is >= elem(expected, 1))) - - defguard matches_arity_with_defaults?(is, defaults, expected) - when is != nil and - (expected == :any or (is_integer(expected) and expected in (is - defaults)..is) or - (is_tuple(expected) and elem(expected, 0) == :gte and is >= elem(expected, 1))) - defp format_func_docs(info) do mod_str = inspect(info.module) fun_str = Atom.to_string(info.function) # fun_args_text = Enum.join(info.args, ", ") # spec_text = "### Specs\n\n```\n#{info.specs |> Enum.join("\n")}\n```\n\n" - spec_text = if info.specs != [] do - joined = Enum.join(info.specs, "\n") - "### Specs\n\n```\n#{joined}\n```\n\n" - else - "" - end + spec_text = + if info.specs != [] do + joined = Enum.join(info.specs, "\n") + "### Specs\n\n```\n#{joined}\n```\n\n" + else + "" + end "> #{mod_str}.#{fun_str}(#{info.fun_args_text})\n\n#{get_metadata_md(info.metadata)}#{spec_text}#{info.docs || ""}" end @@ -296,11 +401,6 @@ defmodule ElixirSense.Core.Introspection do args = BuiltinFunctions.get_args({f, a}) fun_args_text = Enum.join(args, ", ") - - mod_str = inspect(mod) - fun_str = Atom.to_string(fun) - - spec_text = "### Specs\n\n```\n#{spec |> Enum.join("\n")}\n```\n\n" metadata = %{builtin: true} %{ @@ -350,15 +450,15 @@ defmodule ElixirSense.Core.Introspection do TypeInfo.extract_params(params) |> Enum.map_join(", ", &Atom.to_string/1) end - %{ - module: mod, - function: fun, - metadata: metadata, - # args: args, - specs: get_specs_text(mod, fun, arity, kind, metadata), - fun_args_text: fun_args_text, - docs: text - } + %{ + module: mod, + function: fun, + metadata: metadata, + # args: args, + specs: get_specs_text(mod, fun, arity, kind, metadata), + fun_args_text: fun_args_text, + docs: text + } end case results do @@ -416,28 +516,44 @@ defmodule ElixirSense.Core.Introspection do %{} end - %{ - module: mod, - function: fun, - metadata: metadata, - # args: args, - specs: get_specs_text(mod, fun, arity, :function, metadata), - fun_args_text: fun_args_text, - docs: nil - } + %{ + module: mod, + function: fun, + metadata: metadata, + # args: args, + specs: get_specs_text(mod, fun, arity, :function, metadata), + fun_args_text: fun_args_text, + docs: nil + } end end + defp format_module_doc(nil), do: nil + + defp format_module_doc(info) do + mod_str = inspect(info.module) + doc = info.doc || "" + "> #{mod_str}\n\n" <> get_metadata_md(info.metadata) <> doc + end + def get_docs_md(mod) when is_atom(mod) do mod_str = inspect(mod) case NormalizedCode.get_docs(mod, :moduledoc) do {_line, doc, metadata} when is_binary(doc) -> - "> #{mod_str}\n\n" <> get_metadata_md(metadata) <> doc + %{ + module: mod, + metadata: metadata, + doc: doc + } _ -> if Code.ensure_loaded?(mod) do - "> #{mod_str}\n\n" + %{ + module: mod, + metadata: %{}, + doc: nil + } end end end @@ -523,6 +639,20 @@ defmodule ElixirSense.Core.Introspection do "**#{key}**\n#{value}" end + defp format_type_docs(info) do + formatted_spec = "```\n#{info.spec}\n```" + + mod_formatted = + case info.module do + nil -> "" + atom -> inspect(atom) <> "." + end + + docs = info.docs || "" + + "> #{mod_formatted}#{info.type}(#{info.args})\n\n#{get_metadata_md(info.metadata)}### Definition\n\n#{formatted_spec}\n\n#{docs}" + end + @spec get_type_docs_md(nil | module, atom, non_neg_integer | :any) :: list(markdown) defp get_type_docs_md(nil, fun, arity) do for info <- BuiltinTypes.get_builtin_type_info(fun), @@ -540,7 +670,14 @@ defmodule ElixirSense.Core.Introspection do {"@type #{fun}()", ""} end - format_type_doc_md({nil, fun}, args, info[:doc], spec, %{builtin: true}) + %{ + module: nil, + type: fun, + args: args, + metadata: %{builtin: true}, + spec: spec, + docs: info[:doc] + } end end @@ -555,7 +692,14 @@ defmodule ElixirSense.Core.Introspection do type_args = Enum.map_join(args, ", ", &(&1 |> elem(2) |> Atom.to_string())) - format_type_doc_md({mod, fun}, type_args, "", spec, %{}) + %{ + module: mod, + type: fun, + args: type_args, + metadata: %{}, + spec: spec, + docs: nil + } end docs -> @@ -567,13 +711,14 @@ defmodule ElixirSense.Core.Introspection do {_kind, {_name, _def, args}} = spec type_args = Enum.map_join(args, ", ", &(&1 |> elem(2) |> Atom.to_string())) - format_type_doc_md( - {mod, fun}, - type_args, - text, - TypeInfo.format_type_spec(spec), - metadata - ) + %{ + module: mod, + type: fun, + args: type_args, + metadata: metadata, + spec: TypeInfo.format_type_spec(spec), + docs: text + } end end end @@ -1058,10 +1203,10 @@ defmodule ElixirSense.Core.Introspection do end def get_specs_text(mod, fun, arity, kind, metadata) do - for arity <- (arity - Map.get(metadata, :defaults, 0))..arity, - spec = get_spec_as_string(mod, fun, arity, kind, metadata), - spec != "", - do: spec + for arity <- (arity - Map.get(metadata, :defaults, 0))..arity, + spec = get_spec_as_string(mod, fun, arity, kind, metadata), + spec != "", + do: spec end # This function is used only for protocols so no macros @@ -1463,18 +1608,6 @@ defmodule ElixirSense.Core.Introspection do false end - defp format_type_doc_md({mod, fun}, type_args, doc, spec, metadata) when is_binary(type_args) do - formatted_spec = "```\n#{spec}\n```" - - mod_formatted = - case mod do - nil -> "" - atom -> inspect(atom) <> "." - end - - "> #{mod_formatted}#{fun}(#{type_args})\n\n#{get_metadata_md(metadata)}### Definition\n\n#{formatted_spec}\n\n#{doc}" - end - def is_pub(type), do: type in [:def, :defmacro, :defdelegate, :defguard] def is_priv(type), do: type in [:defp, :defmacrop, :defguardp] def is_function_type(type), do: type in [:def, :defp, :defdelegate] diff --git a/lib/elixir_sense/providers/docs.ex b/lib/elixir_sense/providers/docs.ex index 0f243754..e1ed43e8 100644 --- a/lib/elixir_sense/providers/docs.ex +++ b/lib/elixir_sense/providers/docs.ex @@ -127,7 +127,7 @@ defmodule ElixirSense.Providers.Docs do {mod, fun, true, kind} -> {line, column} = context.end call_arity = Metadata.get_call_arity(metadata, mod, fun, line, column) || :any - markdown = Introspection.get_all_docs({mod, fun, call_arity}, kind) + markdown = Introspection.get_all_docs({mod, fun, call_arity}, metadata, kind) if markdown do {mod_fun_to_string({mod, fun}), markdown} diff --git a/test/elixir_sense/docs_test.exs b/test/elixir_sense/docs_test.exs index 9abc4690..4153466f 100644 --- a/test/elixir_sense/docs_test.exs +++ b/test/elixir_sense/docs_test.exs @@ -25,7 +25,7 @@ defmodule ElixirSense.DocsTest do assert docs == "> ElixirSenseExample.ModuleWithDocFalse\n\n" end - test "retrieve documentation" do + test "retrieve documentation from Kernel macro" do buffer = """ defmodule MyModule do @@ -44,6 +44,26 @@ defmodule ElixirSense.DocsTest do """ end + test "retrieve documentation from Kernel.SpecialForm macro" do + buffer = """ + defmodule MyModule do + import List + ^ + end + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 2, 4) + + assert actual_subject == "Kernel.SpecialForms.import" + + assert docs =~ """ + Imports functions and macros\ + """ + end + test "retrieve function documentation" do buffer = """ defmodule MyModule do @@ -73,6 +93,138 @@ defmodule ElixirSense.DocsTest do """ 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 12, 20) + + assert actual_subject == "MyLocalModule.flatten" + + assert docs =~ """ + > MyLocalModule.flatten(list) + + ### Specs + + ``` + @spec flatten(list) :: list + ``` + """ + + # TODO docs and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 10, 7) + + assert actual_subject == "MyLocalModule.flatten" + + assert docs =~ """ + > MyLocalModule.flatten(list) + + ### Specs + + ``` + @spec flatten(list) :: list + ``` + """ + + # TODO docs and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 11, 20) + + assert actual_subject == "MyLocalModule.flatten" + + assert docs =~ """ + > MyLocalModule.flatten(list) + + ### Specs + + ``` + @spec flatten(list) :: list + ``` + """ + + # TODO mark as hidden in metadata + end + test "retrieve function documentation on @attr call" do buffer = """ defmodule MyModule do @@ -206,7 +358,7 @@ defmodule ElixirSense.DocsTest do end @tag requires_elixir_1_14: true - test "retrieve function documentation with __MODULE__" do + test "retrieve function documentation with __MODULE__ submodule call" do buffer = """ defmodule Inspect do def func(list) do @@ -275,22 +427,6 @@ defmodule ElixirSense.DocsTest do """ end - test "request for defmacro" do - buffer = """ - defmodule MyModule do - defmacro my_macro do - end - end - """ - - %{actual_subject: actual_subject, docs: docs} = ElixirSense.docs(buffer, 2, 5) - - assert actual_subject == "Kernel.defmacro" - - assert docs =~ "Kernel.defmacro(" - assert docs =~ "macro with the given name and body." - end - test "retrieve documentation from modules" do buffer = """ defmodule MyModule do @@ -318,6 +454,61 @@ defmodule ElixirSense.DocsTest do """ 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 9, 15) + + assert actual_subject == "MyLocalModule" + + assert docs =~ """ + > MyLocalModule + """ + + # TODO doc and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 8, 15) + + assert actual_subject == "MyLocalModule" + + assert docs =~ """ + > MyLocalModule + """ + + # TODO mark as hidden + end + test "retrieve documentation from erlang modules" do buffer = """ defmodule MyModule do @@ -417,6 +608,164 @@ defmodule ElixirSense.DocsTest do """ 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 8, 35) + + assert actual_subject == "MyLocalModule.some" + + assert docs == """ + > MyLocalModule.some(a) + + ### Definition + + ``` + @type some(a) :: {a} + ``` + + + """ + + # TODO docs and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 6, 22) + + assert actual_subject == "MyLocalModule.some" + + assert docs == """ + > MyLocalModule.some(a) + + ### Definition + + ``` + @typep some(a) :: {a} + ``` + + + """ + + # TODO docs and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 8, 35) + + assert actual_subject == "MyLocalModule.some" + + assert docs == """ + > MyLocalModule.some(a) + + ### Definition + + ``` + @opaque some(a) + ``` + + + """ + + # TODO docs and metadata + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 7, 35) + + assert actual_subject == "MyLocalModule.some" + + assert docs == """ + > MyLocalModule.some(a) + + ### Definition + + ``` + @type some(a) :: {a} + ``` + + + """ + + # TODO mark as hidden + end + test "does not reveal opaque type details" do buffer = """ defmodule MyModule do @@ -1019,21 +1368,11 @@ defmodule ElixirSense.DocsTest do end """ - # TODO requires metadata docs support - - # assert %{actual_subject: "ElixirSenseExample.Some.some_local"} = - # ElixirSense.docs(buffer, 6, 20) - assert nil == + assert %{actual_subject: "ElixirSenseExample.Some.some_local"} = ElixirSense.docs(buffer, 6, 20) - # TODO assert type - # assert %{actual_subject: "ElixirSenseExample.Some.some_local"} = - # ElixirSense.docs(buffer, 9, 9) - - assert nil == + assert %{actual_subject: "ElixirSenseExample.Some.some_local"} = ElixirSense.docs(buffer, 9, 9) - - # TODO assert function end test "retrieves documentation for correct arity function" do From 9f1478eb6646b6c21cac1986fdd32adbf66bd6a1 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Wed, 2 Aug 2023 08:16:21 +0200 Subject: [PATCH 8/9] cleanup code --- lib/elixir_sense/core/introspection.ex | 6 +- lib/elixir_sense/core/state.ex | 46 ++----- test/elixir_sense/core/introspection_test.exs | 20 ++- .../core/metadata_builder_test.exs | 22 +-- test/elixir_sense/docs_test.exs | 128 ++++++++++++++++++ 5 files changed, 174 insertions(+), 48 deletions(-) diff --git a/lib/elixir_sense/core/introspection.ex b/lib/elixir_sense/core/introspection.ex index ca866ce9..dee6a005 100644 --- a/lib/elixir_sense/core/introspection.ex +++ b/lib/elixir_sense/core/introspection.ex @@ -217,8 +217,8 @@ defmodule ElixirSense.Core.Introspection do _ -> false end) - |> Enum.sort_by(fn {{^mod, ^fun, a}, _fun_info} -> a end) - |> Enum.map(fn {{^mod, ^fun, a}, type_info} -> + |> Enum.sort_by(fn {{_mod, _fun, a}, _fun_info} -> a end) + |> Enum.map(fn {_, type_info} -> args = type_info.args |> List.last() |> Enum.join(", ") spec = @@ -537,8 +537,6 @@ defmodule ElixirSense.Core.Introspection do end def get_docs_md(mod) when is_atom(mod) do - mod_str = inspect(mod) - case NormalizedCode.get_docs(mod, :moduledoc) do {_line, doc, metadata} when is_binary(doc) -> %{ diff --git a/lib/elixir_sense/core/state.ex b/lib/elixir_sense/core/state.ex index 950f0807..6860311e 100644 --- a/lib/elixir_sense/core/state.ex +++ b/lib/elixir_sense/core/state.ex @@ -1122,6 +1122,19 @@ defmodule ElixirSense.Core.State do %__MODULE__{state | types: types} end + defp combine_specs(nil, new), do: new + + defp combine_specs(%SpecInfo{} = existing, %SpecInfo{} = new) do + %SpecInfo{ + existing + | positions: [hd(new.positions) | existing.positions], + end_positions: [hd(new.end_positions) | existing.end_positions], + generated: [hd(new.generated) | existing.generated], + args: [hd(new.args) | existing.args], + specs: [hd(new.specs) | existing.specs] + } + end + def add_spec(%__MODULE__{} = state, type_name, type_args, spec, kind, pos, end_pos, options) do arg_names = type_args @@ -1142,37 +1155,8 @@ defmodule ElixirSense.Core.State do specs = current_module_variants |> Enum.reduce(state.specs, fn current_module, acc -> - nil_info = - case acc[{current_module, type_name, nil}] do - nil -> - type_info - - %SpecInfo{positions: positions, args: args, specs: specs} = ti -> - %SpecInfo{ - ti - | positions: [pos | positions], - end_positions: [end_pos | ti.end_positions], - generated: [Keyword.get(options, :generated, false) | ti.generated], - args: [arg_names | args], - specs: [spec | specs] - } - end - - arity_info = - case acc[{current_module, type_name, length(arg_names)}] do - nil -> - type_info - - %SpecInfo{positions: positions, args: args, specs: specs} = ti -> - %SpecInfo{ - ti - | positions: [pos | positions], - end_positions: [end_pos | ti.end_positions], - generated: [Keyword.get(options, :generated, false) | ti.generated], - args: [arg_names | args], - specs: [spec | specs] - } - end + nil_info = combine_specs(acc[{current_module, type_name, nil}], type_info) + arity_info = combine_specs(acc[{current_module, type_name, length(arg_names)}], type_info) acc |> Map.put({current_module, type_name, nil}, nil_info) diff --git a/test/elixir_sense/core/introspection_test.exs b/test/elixir_sense/core/introspection_test.exs index 33e373df..35bccd6a 100644 --- a/test/elixir_sense/core/introspection_test.exs +++ b/test/elixir_sense/core/introspection_test.exs @@ -1,6 +1,7 @@ defmodule ElixirSense.Core.IntrospectionTest do use ExUnit.Case, async: true doctest ElixirSense.Core.Introspection + alias ElixirSense.Core.Metadata alias ElixirSense.Core.TypeInfo import ElixirSense.Core.Introspection @@ -763,6 +764,7 @@ defmodule ElixirSense.Core.IntrospectionTest do assert docs = get_all_docs( {ElixirSenseExample.ModuleWithDelegates, :delegated_fun, 2}, + %Metadata{}, :mod_fun ) @@ -778,7 +780,12 @@ defmodule ElixirSense.Core.IntrospectionTest do end test "returns since metadata on functions" do - assert docs = get_all_docs({ElixirSenseExample.ModuleWithDocs, :some_fun, 2}, :mod_fun) + assert docs = + get_all_docs( + {ElixirSenseExample.ModuleWithDocs, :some_fun, 2}, + %Metadata{}, + :mod_fun + ) assert docs == """ > ElixirSenseExample.ModuleWithDocs.some_fun(a, b \\\\\\\\ nil) @@ -795,6 +802,7 @@ defmodule ElixirSense.Core.IntrospectionTest do assert docs = get_all_docs( {ElixirSenseExample.ModuleWithDocs, :soft_deprecated_fun, 1}, + %Metadata{}, :mod_fun ) @@ -810,7 +818,12 @@ defmodule ElixirSense.Core.IntrospectionTest do end test "returns since metadata on types" do - assert docs = get_all_docs({ElixirSenseExample.ModuleWithDocs, :some_type, 0}, :type) + assert docs = + get_all_docs( + {ElixirSenseExample.ModuleWithDocs, :some_type, 0}, + %Metadata{}, + :type + ) assert docs == """ > ElixirSenseExample.ModuleWithDocs.some_type() @@ -830,7 +843,8 @@ defmodule ElixirSense.Core.IntrospectionTest do end test "returns since metadata on modules" do - assert docs = get_all_docs({ElixirSenseExample.ModuleWithDocs, nil, :any}, :mod_fun) + assert docs = + get_all_docs({ElixirSenseExample.ModuleWithDocs, nil, :any}, %Metadata{}, :mod_fun) assert docs == """ > ElixirSenseExample.ModuleWithDocs diff --git a/test/elixir_sense/core/metadata_builder_test.exs b/test/elixir_sense/core/metadata_builder_test.exs index 475ded6b..458c3833 100644 --- a/test/elixir_sense/core/metadata_builder_test.exs +++ b/test/elixir_sense/core/metadata_builder_test.exs @@ -2863,15 +2863,17 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert state.specs == %{ {Proto, :with_spec, 2} => %ElixirSense.Core.State.SpecInfo{ - args: [["t", "boolean"]], + args: [["t", "boolean"], ["t", "integer"]], kind: :callback, name: :with_spec, - positions: [{3, 3}], - end_positions: [{3, 40}], - generated: [false], + positions: [{3, 3}, {2, 3}], + end_positions: [{3, 40}, {2, 42}], + generated: [false, false], specs: [ "@callback with_spec(t, boolean) :: number", - "@spec with_spec(t, boolean) :: number" + "@callback with_spec(t, integer) :: String.t", + "@spec with_spec(t, boolean) :: number", + "@spec with_spec(t, integer) :: String.t" ] }, {Proto, :with_spec, nil} => %ElixirSense.Core.State.SpecInfo{ @@ -4674,13 +4676,13 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert state.specs == %{ {Proto, :abc, 0} => %ElixirSense.Core.State.SpecInfo{ - args: [[]], + args: [[], []], kind: :spec, name: :abc, - positions: [{3, 3}], - end_positions: [{3, 25}], - generated: [false], - specs: ["@spec abc :: reference"] + positions: [{3, 3}, {2, 3}], + end_positions: [{3, 25}, {2, 30}], + generated: [false, false], + specs: ["@spec abc :: reference", "@spec abc :: atom | integer"] }, {Proto, :abc, nil} => %ElixirSense.Core.State.SpecInfo{ kind: :spec, diff --git a/test/elixir_sense/docs_test.exs b/test/elixir_sense/docs_test.exs index 4153466f..f34d517e 100644 --- a/test/elixir_sense/docs_test.exs +++ b/test/elixir_sense/docs_test.exs @@ -167,6 +167,80 @@ defmodule ElixirSense.DocsTest do # TODO docs and metadata 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 10, 17) + + assert actual_subject == "MyLocalModule.flatten" + + assert docs =~ """ + > MyLocalModule.flatten(list) + + ### Specs + + ``` + @spec flatten(list) :: list + ``` + """ + + # TODO docs and metadata + end + + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 12, 20) + + assert actual_subject == "MyLocalModule.Sub.flatten" + + assert docs =~ """ + > MyLocalModule.Sub.flatten(list) + + ### Specs + + ``` + @spec flatten(list) :: list + ``` + """ + + # TODO docs and metadata + end + test "does not retrieve remote private metadata function documentation" do buffer = """ defmodule MyLocalModule do @@ -482,6 +556,60 @@ defmodule ElixirSense.DocsTest do # TODO doc and metadata 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 6, 6) + + assert actual_subject == "MyLocalModule" + + assert docs =~ """ + > MyLocalModule + """ + + # TODO doc and metadata + end + + 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 + """ + + %{ + actual_subject: actual_subject, + docs: docs + } = ElixirSense.docs(buffer, 8, 17) + + assert actual_subject == "MyLocalModule.Sub" + + assert docs =~ """ + > MyLocalModule.Sub + """ + + # TODO doc and metadata + end + test "retrieve documentation from metadata modules with @moduledoc false" do buffer = """ defmodule MyLocalModule do From 263889cb5d559c7c2f4b2f547368c5d53f75be69 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Thu, 3 Aug 2023 07:55:30 +0200 Subject: [PATCH 9/9] fix tests --- test/elixir_sense/definition_test.exs | 1 + test/elixir_sense/docs_test.exs | 2 ++ test/elixir_sense/references_test.exs | 1 + 3 files changed, 4 insertions(+) diff --git a/test/elixir_sense/definition_test.exs b/test/elixir_sense/definition_test.exs index be71f266..38c84bcf 100644 --- a/test/elixir_sense/definition_test.exs +++ b/test/elixir_sense/definition_test.exs @@ -1109,6 +1109,7 @@ defmodule ElixirSense.Providers.DefinitionTest do } end + @tag requires_elixir_1_14: true test "find definition of local __MODULE__ submodule" do buffer = """ defmodule MyModule do diff --git a/test/elixir_sense/docs_test.exs b/test/elixir_sense/docs_test.exs index f34d517e..20556c09 100644 --- a/test/elixir_sense/docs_test.exs +++ b/test/elixir_sense/docs_test.exs @@ -203,6 +203,7 @@ defmodule ElixirSense.DocsTest do # TODO docs and metadata end + @tag requires_elixir_1_14: true test "retrieve local private metadata function documentation on __MODULE__ submodule call" do buffer = """ defmodule MyLocalModule do @@ -582,6 +583,7 @@ defmodule ElixirSense.DocsTest do # TODO doc and metadata end + @tag requires_elixir_1_14: true test "retrieve documentation from metadata modules on __MODULE__ submodule" do buffer = """ defmodule MyLocalModule do diff --git a/test/elixir_sense/references_test.exs b/test/elixir_sense/references_test.exs index 494eb819..6967d56f 100644 --- a/test/elixir_sense/references_test.exs +++ b/test/elixir_sense/references_test.exs @@ -1063,6 +1063,7 @@ defmodule ElixirSense.Providers.ReferencesTest do ] end + @tag requires_elixir_1_14: true test "find references when module with __MODULE__ special form submodule", %{trace: trace} do buffer = """ defmodule MyLocalModule do