diff --git a/lib/elixir_sense.ex b/lib/elixir_sense.ex index 4532ddf8..5f7a9cfe 100644 --- a/lib/elixir_sense.ex +++ b/lib/elixir_sense.ex @@ -69,7 +69,7 @@ defmodule ElixirSense do %{begin: begin_pos, end: end_pos, context: context} -> metadata = Parser.parse_string(code, true, true, line) - env = Metadata.get_env(metadata, line) + env = Metadata.get_env(metadata, {line, column}) case Docs.all(context, env, metadata.mods_funs_to_positions, metadata.types) do {actual_subject, docs} -> @@ -101,7 +101,7 @@ defmodule ElixirSense do ...> ''' iex> %{file: path, line: line, column: column} = ElixirSense.definition(code, 3, 11) iex> "#{Path.basename(path)}:#{to_string(line)}:#{to_string(column)}" - "module_with_functions.ex:6:7" + "module_with_functions.ex:6:3" """ @spec definition(String.t(), pos_integer, pos_integer) :: Location.t() | nil def definition(code, line, column) do @@ -112,7 +112,7 @@ defmodule ElixirSense do %{context: context, begin: {line, col}} -> buffer_file_metadata = Parser.parse_string(code, true, true, line) - env = Metadata.get_env(buffer_file_metadata, line) + env = Metadata.get_env(buffer_file_metadata, {line, column}) calls = buffer_file_metadata.calls[line] @@ -143,7 +143,7 @@ defmodule ElixirSense do ...> ''' iex> [%{file: path, line: line, column: column}, _] = ElixirSense.implementations(code, 1, 37) |> Enum.sort iex> "#{Path.basename(path)}:#{to_string(line)}:#{to_string(column)}" - "example_protocol.ex:7:7" + "example_protocol.ex:7:3" """ @spec implementations(String.t(), pos_integer, pos_integer) :: [Location.t()] def implementations(code, line, column) do @@ -154,7 +154,7 @@ defmodule ElixirSense do %{context: context} -> buffer_file_metadata = Parser.parse_string(code, true, true, line) - env = Metadata.get_env(buffer_file_metadata, line) + env = Metadata.get_env(buffer_file_metadata, {line, column}) Implementation.find( context, @@ -225,13 +225,13 @@ defmodule ElixirSense do buffer_file_metadata = maybe_fix_autocomple_on_cursor(buffer_file_metadata, text_before, text_after, line) - env = Metadata.get_env(buffer_file_metadata, line) + env = Metadata.get_env(buffer_file_metadata, {line, column}) module_store = ModuleStore.build() cursor_context = %{ text_before: text_before, text_after: text_after, - at_module_body?: Metadata.at_module_body?(buffer_file_metadata, env) + at_module_body?: Metadata.at_module_body?(env) } Suggestion.find(hint, env, buffer_file_metadata, cursor_context, module_store, opts) @@ -267,7 +267,7 @@ defmodule ElixirSense do prefix = Source.text_before(code, line, column) buffer_file_metadata = Parser.parse_string(code, true, true, line) - env = Metadata.get_env(buffer_file_metadata, line) + env = Metadata.get_env(buffer_file_metadata, {line, column}) Signature.find(prefix, env, buffer_file_metadata) end @@ -350,7 +350,7 @@ defmodule ElixirSense do def expand_full(buffer, code, line) do buffer_file_metadata = Parser.parse_string(buffer, true, true, line) - env = Metadata.get_env(buffer_file_metadata, line) + env = Metadata.get_env(buffer_file_metadata, {line, 1}) Expand.expand_full(code, env) end @@ -432,7 +432,7 @@ defmodule ElixirSense do %State.Env{ module: module, vars: vars - } = Metadata.get_env(buffer_file_metadata, line) + } = Metadata.get_env(buffer_file_metadata, {line, column}) # find last env of current module attributes = get_attributes(buffer_file_metadata.lines_to_env, module) @@ -492,7 +492,7 @@ defmodule ElixirSense do ...> end ...> ''' iex> ElixirSense.string_to_quoted(code, 1) - {:ok, {:defmodule, [line: 1, column: 1], [[do: {:__block__, [], []}]]}} + {:ok, {:defmodule, [do: [line: 1, column: 11], end: [line: 2, column: 1], line: 1, column: 1], [[do: {:__block__, [], []}]]}} """ @spec string_to_quoted(String.t(), pos_integer | nil, non_neg_integer, keyword) :: {:ok, Macro.t()} | {:error, {line :: pos_integer(), term(), term()}} diff --git a/lib/elixir_sense/core/metadata.ex b/lib/elixir_sense/core/metadata.ex index a51c7394..07bc6494 100644 --- a/lib/elixir_sense/core/metadata.ex +++ b/lib/elixir_sense/core/metadata.ex @@ -41,29 +41,139 @@ defmodule ElixirSense.Core.Metadata do documentation: String.t() } - @spec get_env(__MODULE__.t(), pos_integer) :: State.Env.t() - def get_env(%__MODULE__{} = metadata, line) do - case Map.get(metadata.lines_to_env, line) do - nil -> State.default_env() - ctx -> ctx + @spec get_env(__MODULE__.t(), {pos_integer, pos_integer}) :: State.Env.t() + def get_env(%__MODULE__{} = metadata, {line, column}) do + all_scopes = + Enum.to_list(metadata.types) ++ + Enum.to_list(metadata.specs) ++ + Enum.to_list(metadata.mods_funs_to_positions) + + closest_scopes = + all_scopes + |> Enum.map(fn + {{_, fun, nil}, _} when fun != nil -> + nil + + {key, %type{positions: positions, end_positions: end_positions, generated: generated}} -> + closest_scope = + Enum.zip([positions, end_positions, generated]) + |> Enum.map(fn + {_, _, true} -> + nil + + {{begin_line, begin_column}, {end_line, end_column}, _} + when (line > begin_line or (line == begin_line and column >= begin_column)) and + (line < end_line or (line == end_line and column <= end_column)) -> + {{begin_line, begin_column}, {end_line, end_column}} + + {{begin_line, begin_column}, nil, _} + when line > begin_line or (line == begin_line and column >= begin_column) -> + case find_closest_ending(all_scopes, {begin_line, begin_column}) do + nil -> + {{begin_line, begin_column}, nil} + + {end_line, end_column} -> + if line < end_line or (line == end_line and column < end_column) do + {{begin_line, begin_column}, {end_line, end_column}} + end + end + + _ -> + nil + end) + |> Enum.filter(&(&1 != nil)) + |> Enum.max(fn -> nil end) + + if closest_scope do + {key, type, closest_scope} + end + end) + |> Enum.filter(&(&1 != nil)) + |> Enum.sort_by( + fn {_key, _type, {begin_position, _end_position}} -> + begin_position + end, + :desc + ) + + # |> dbg() + + case closest_scopes do + [_ | _] = scopes -> + metadata.lines_to_env + |> Enum.filter(fn {metadata_line, env} -> + Enum.any?(scopes, fn {key, type, {{begin_line, _begin_column}, _}} -> + if metadata_line >= begin_line do + case {key, type} do + {{module, nil, nil}, _} -> + module in env.module_variants and is_atom(env.scope) and env.scope != Elixir + + {{module, fun, arity}, State.ModFunInfo} -> + module in env.module_variants and env.scope == {fun, arity} + + {{module, fun, arity}, type} when type in [State.TypeInfo, State.SpecInfo] -> + module in env.module_variants and env.scope == {:typespec, fun, arity} + end + end + end) + end) + + # |> dbg() + [] -> + metadata.lines_to_env end + |> Enum.max_by( + fn + {metadata_line, _env} when metadata_line <= line -> metadata_line + _ -> 0 + end, + fn -> + {line, State.default_env()} + end + ) + |> elem(1) end - @spec at_module_body?(__MODULE__.t(), State.Env.t()) :: boolean() - def at_module_body?(%__MODULE__{} = metadata, env) do - mod_info = Map.get(metadata.mods_funs_to_positions, {env.module, nil, nil}) + defp find_closest_ending(all_scopes, {line, column}) do + all_scopes + |> Enum.map(fn + {{_, fun, nil}, _} when fun != nil -> + nil - with %State.ModFunInfo{positions: [{line, _}]} <- mod_info, - %State.Env{scope_id: mod_scope_id} <- metadata.lines_to_env[line] do - env.scope_id in mod_scope_id..(mod_scope_id + 1) and not match?({_, _}, env.scope) - else - _ -> - false - end + {_key, %{positions: positions, end_positions: end_positions, generated: generated}} -> + Enum.zip([positions, end_positions, generated]) + |> Enum.map(fn + {_, _, true} -> + nil + + {{begin_line, begin_column}, {end_line, end_column}, _} -> + if {begin_line, begin_column} > {line, column} do + {begin_line, begin_column} + else + if {end_line, end_column} > {line, column} do + {end_line, end_column} + end + end + + {{begin_line, begin_column}, nil, _} -> + if {begin_line, begin_column} > {line, column} do + {begin_line, begin_column} + end + end) + |> Enum.filter(&(&1 != nil)) + |> Enum.min(fn -> nil end) + end) + |> Enum.filter(&(&1 != nil)) + |> Enum.min(fn -> nil end) + end + + @spec at_module_body?(State.Env.t()) :: boolean() + def at_module_body?(env) do + is_atom(env.scope) and env.scope != Elixir end - def get_position_to_insert_alias(%__MODULE__{} = metadata, line) do - env = get_env(metadata, line) + def get_position_to_insert_alias(%__MODULE__{} = metadata, {line, column}) do + env = get_env(metadata, {line, column}) module = env.module cond do @@ -80,7 +190,7 @@ defmodule ElixirSense.Core.Metadata do %State.ModFunInfo{positions: [{line, column}]} -> # Hacky :shrug line_offset = 1 - column_offset = -8 + column_offset = 2 {line + line_offset, column + column_offset} _ -> diff --git a/lib/elixir_sense/core/metadata_builder.ex b/lib/elixir_sense/core/metadata_builder.ex index d9388db1..3b882627 100644 --- a/lib/elixir_sense/core/metadata_builder.ex +++ b/lib/elixir_sense/core/metadata_builder.ex @@ -32,14 +32,21 @@ defmodule ElixirSense.Core.MetadataBuilder do defguardp is_call(call, params) when is_atom(call) and is_list(params) and - call not in [:., :__aliases__, :"::", :{}, :|>] + call not in [:., :__aliases__, :"::", :{}, :|>, :%, :%{}] defguard is_call_meta_simple(list) when elem(hd(list), 0) == :line and elem(hd(tl(list)), 0) == :column defguard is_call_meta(list) - when (elem(hd(list), 0) == :no_parens and is_call_meta_simple(tl(list))) or - is_call_meta_simple(list) + when is_call_meta_simple(list) or + (elem(hd(list), 0) == :no_parens and is_call_meta_simple(tl(list))) or + (elem(hd(list), 0) == :closing and is_call_meta_simple(tl(list))) or + (elem(hd(list), 0) == :end_of_expression and elem(hd(tl(list)), 0) == :closing and + is_call_meta_simple(tl(tl(list)))) or + (elem(hd(list), 0) == :end_of_expression and elem(hd(tl(list)), 0) == :no_parens and + is_call_meta_simple(tl(tl(list)))) + + # defguard is_call_meta(list) when is_list(list) @doc """ Traverses the AST building/retrieving the environment information. @@ -47,6 +54,7 @@ defmodule ElixirSense.Core.MetadataBuilder do """ @spec build(Macro.t()) :: State.t() def build(ast) do + # dbg(ast) {_ast, state} = Macro.traverse(ast, %State{}, safe_call(&pre/2, :pre), safe_call(&post/2, :post)) @@ -77,14 +85,16 @@ defmodule ElixirSense.Core.MetadataBuilder do end end - defp pre_module(ast, state, {line, column} = position, module, types \\ [], functions \\ []) do + defp pre_module(ast, state, meta, module, types \\ [], functions \\ []) do module = normalize_module(module) + {position = {line, column}, end_position} = extract_range(meta) + state = state |> maybe_add_protocol_implementation(module) |> add_namespace(module) - |> add_current_module_to_index(position) + |> add_current_module_to_index(position, end_position, generated: state.generated) |> alias_submodule(module) |> new_alias_scope |> new_attributes_scope @@ -98,7 +108,7 @@ defmodule ElixirSense.Core.MetadataBuilder do types |> Enum.reduce(state, fn {type_name, type_args, spec, kind}, acc -> acc - |> add_type(type_name, type_args, kind, spec, position) + |> add_type(type_name, type_args, kind, spec, position, generated: true) end) state = @@ -111,7 +121,9 @@ defmodule ElixirSense.Core.MetadataBuilder do name, mapped_args, position, - kind + nil, + kind, + generated: true ) end) @@ -132,11 +144,11 @@ defmodule ElixirSense.Core.MetadataBuilder do |> result(ast) end - def pre_protocol(ast, state, position, module) do + def pre_protocol(ast, state, meta, module) do # protocol defines a type `@type t :: term` # and functions __protocol__/1, impl_for/1, impl_for!/1 - pre_module(ast, state, position, module, @protocol_types, @protocol_functions) + pre_module(ast, state, meta, module, @protocol_types, @protocol_functions) end def post_protocol(ast, state) do @@ -185,7 +197,9 @@ defmodule ElixirSense.Core.MetadataBuilder do args: args, specs: specs, kind: :callback, - positions: positions + positions: positions, + end_positions: Enum.map(positions, fn _ -> nil end), + generated: Enum.map(positions, fn _ -> true end) } spec = %State.SpecInfo{specs: specs} -> @@ -212,25 +226,59 @@ defmodule ElixirSense.Core.MetadataBuilder do post_module(ast, state) end - defp pre_func({type, _, _} = ast, state, %{line: line, col: col}, name, params, options \\ []) + defp pre_func({type, _, _} = ast, state, meta, name, params, options \\ []) when is_atom(name) do vars = state |> find_vars(params) |> merge_same_name_vars() + {position, end_position} = extract_range(meta) + + options = Keyword.put(options, :generated, state.generated) + state |> new_named_func(name, length(params || [])) - |> add_func_to_index(name, params || [], {line, col}, type, options) + |> add_func_to_index(name, params || [], position, end_position, type, options) |> new_alias_scope |> new_import_scope |> new_require_scope |> new_func_vars_scope |> add_vars(vars, true) - |> add_current_env_to_line(line) + |> add_current_env_to_line(Keyword.fetch!(meta, :line)) |> result(ast) end + defp extract_range(meta) do + position = { + Keyword.fetch!(meta, :line), + Keyword.fetch!(meta, :column) + } + + end_position = + case meta[:end] do + nil -> + case meta[:end_of_expression] do + nil -> + nil + + end_of_expression_meta -> + { + Keyword.fetch!(end_of_expression_meta, :line), + Keyword.fetch!(end_of_expression_meta, :column) + } + end + + end_meta -> + { + Keyword.fetch!(end_meta, :line), + Keyword.fetch!(end_meta, :column) + 3 + } + end + + {position, end_position} + end + defp post_func(ast, state) do state |> remove_alias_scope @@ -310,7 +358,9 @@ defmodule ElixirSense.Core.MetadataBuilder do |> result(ast) end - defp pre_clause({_clause, [line: line, column: _column], _} = ast, state, lhs) do + defp pre_clause({_clause, meta, _} = ast, state, lhs) do + line = meta |> Keyword.fetch!(:line) + vars = state |> find_vars(lhs, Enum.at(state.binding_context, 0)) @@ -393,7 +443,7 @@ defmodule ElixirSense.Core.MetadataBuilder do spec = TypeInfo.typespec_to_string(kind, spec) state - |> add_type(type_name, type_args, spec, kind, pos) + |> add_type(type_name, type_args, spec, kind, pos, generated: state.generated) |> add_typespec_namespace(type_name, length(type_args)) |> add_current_env_to_line(line) |> result(ast) @@ -409,14 +459,16 @@ defmodule ElixirSense.Core.MetadataBuilder do :behaviour_info, [{:atom, [line: line, column: column], nil}], pos, - :def + nil, + :def, + generated: true ) else state end state - |> add_spec(type_name, type_args, spec, kind, pos) + |> add_spec(type_name, type_args, spec, kind, pos, generated: state.generated) |> add_typespec_namespace(type_name, length(type_args)) |> add_current_env_to_line(line) |> result(ast) @@ -431,47 +483,46 @@ defmodule ElixirSense.Core.MetadataBuilder do end defp pre( - {:defmodule, _, [{:__aliases__, [line: line, column: column], module}, _]} = ast, + {:defmodule, meta, [{:__aliases__, _, module}, _]} = ast, state ) do - pre_module(ast, state, {line, column}, module) + pre_module(ast, state, meta, module) end - defp pre({:defmodule, [line: line, column: column], [module, _]} = ast, state) + defp pre({:defmodule, meta, [module, _]} = ast, state) when is_atom(module) do - pre_module(ast, state, {line, column}, module) + pre_module(ast, state, meta, module) end defp pre( - {:defprotocol, _, [{:__aliases__, [line: line, column: column], module}, _]} = ast, + {:defprotocol, meta, [{:__aliases__, _, module}, _]} = ast, state ) do - pre_protocol(ast, state, {line, column}, module) + pre_protocol(ast, state, meta, module) end - defp pre({:defprotocol, [line: line, column: column], [module, _]} = ast, state) + defp pre({:defprotocol, meta, [module, _]} = ast, state) when is_atom(module) do - pre_protocol(ast, state, {line, column}, module) + pre_protocol(ast, state, meta, module) end defp pre( - {:defimpl, _, [{:__aliases__, [line: line, column: column], protocol}, impl_args | _]} = - ast, + {:defimpl, meta, [{:__aliases__, _, protocol}, impl_args | _]} = ast, state ) do - pre_protocol_implementation(ast, state, {line, column}, protocol, impl_args) + pre_protocol_implementation(ast, state, meta, protocol, impl_args) end defp pre( - {:defimpl, [line: line, column: column], [protocol, impl_args | _]} = ast, + {:defimpl, meta, [protocol, impl_args | _]} = ast, state ) when is_atom(protocol) do - pre_protocol_implementation(ast, state, {line, column}, protocol, impl_args) + pre_protocol_implementation(ast, state, meta, protocol, impl_args) end defp pre( - {:defdelegate, meta, [{name, [line: line, column: column] = meta2, params}, body]}, + {:defdelegate, meta, [{name, meta2, params}, body]}, state ) when is_atom(name) do @@ -493,7 +544,7 @@ defmodule ElixirSense.Core.MetadataBuilder do _ -> [] end - pre_func(ast_without_params, state, %{line: line, col: column}, name, params, options) + pre_func(ast_without_params, state, meta, name, params, options) end # quote do @@ -505,63 +556,64 @@ defmodule ElixirSense.Core.MetadataBuilder do # function head with guards defp pre( - {def_name, meta, - [{:when, _, [{name, [line: line, column: column] = meta2, params}, guards]}, body]}, + {def_name, meta, [{:when, _, [{name, meta2, params}, guards]}, body]}, state ) when def_name in @defs do ast_without_params = {def_name, meta, [{name, add_no_call(meta2), []}, guards, body]} - pre_func(ast_without_params, state, %{line: line, col: column}, name, params) + pre_func(ast_without_params, state, meta, name, params) end defp pre( - {def_name, meta, [{name, [line: line, column: column] = meta2, params}, body]}, + {def_name, meta, [{name, meta2, params}, body]}, state ) when def_name in @defs and is_atom(name) do ast_without_params = {def_name, meta, [{name, add_no_call(meta2), []}, body]} - pre_func(ast_without_params, state, %{line: line, col: column}, name, params) + pre_func(ast_without_params, state, meta, name, params) end # defguard and defguardp defp pre( {def_name, meta, [ - {:when, [line: _, column: _], - [{name, [line: line, column: column] = meta2, params}, body]} + {:when, _meta, [{name, meta2, params}, body]} ]}, state ) when def_name in @defs do ast_without_params = {def_name, meta, [{name, add_no_call(meta2), []}, body]} - pre_func(ast_without_params, state, %{line: line, col: column}, name, params) + pre_func(ast_without_params, state, meta, name, params) end # function head - defp pre({def_name, meta, [{name, [line: line, column: column] = meta2, params}]}, state) + defp pre({def_name, meta, [{name, meta2, params}]}, state) when def_name in @defs and is_atom(name) do ast_without_params = {def_name, meta, [{name, add_no_call(meta2), []}, nil]} - pre_func(ast_without_params, state, %{line: line, col: column}, name, params) + pre_func(ast_without_params, state, meta, name, params) end defp pre( - {:@, [line: line, column: _column], - [{:behaviour, _, [{:__aliases__, _, module_expression}]}]} = ast, + {:@, meta, [{:behaviour, _, [{:__aliases__, _, module_expression}]}]} = ast, state ) do + line = Keyword.fetch!(meta, :line) module = concat_module_expression(state, module_expression) pre_behaviour(ast, state, line, module) end - defp pre({:@, [line: line, column: _column], [{:behaviour, _, [erlang_module]}]} = ast, state) do + defp pre({:@, meta, [{:behaviour, _, [erlang_module]}]} = ast, state) do + line = Keyword.fetch!(meta, :line) pre_behaviour(ast, state, line, erlang_module) end # protocol derive defp pre( - {:@, [line: _line, column: _column] = position, [{:derive, _, [derived_protos]}]} = ast, + {:@, meta, [{:derive, _, [derived_protos]}]} = ast, state ) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) current_module_variants = state |> get_current_module_variants List.wrap(derived_protos) @@ -582,12 +634,12 @@ defmodule ElixirSense.Core.MetadataBuilder do case acc_1.mods_funs_to_positions[{mod_any, nil, nil}] do nil -> - # implemantation for: Any not detected (is in other file etc.) + # implementation for: Any not detected (is in other file etc.) acc_1 - |> add_module_to_index(mod, position) + |> add_module_to_index(mod, {line, column}, nil, generated: true) _any_mods_funs -> - # copy implemantation for: Any + # copy implementation for: Any copied_mods_funs_to_positions = for {{module, fun, arity}, val} <- acc_1.mods_funs_to_positions, module == mod_any, @@ -610,13 +662,16 @@ defmodule ElixirSense.Core.MetadataBuilder do end defp pre( - {:@, [line: line, column: column] = _meta_attr, + {:@, meta_attr, [{kind, _, [{:"::", _meta, _params = [{name, _, type_args}, _type_def]} = spec]}]} = ast, state ) when kind in [:type, :typep, :opaque] and is_atom(name) and (is_nil(type_args) or is_list(type_args)) do + line = Keyword.fetch!(meta_attr, :line) + column = Keyword.fetch!(meta_attr, :column) + pre_type( ast, state, @@ -629,7 +684,7 @@ defmodule ElixirSense.Core.MetadataBuilder do end defp pre( - {:@, [line: line, column: column] = _meta_attr, + {:@, meta_attr, [ {kind, _, [{:when, _, [{:"::", _meta, _params = [{name, _, type_args}, _type_def]}, _]} = spec]} @@ -638,6 +693,9 @@ defmodule ElixirSense.Core.MetadataBuilder do ) when kind in [:spec, :callback, :macrocallback] and is_atom(name) and (is_nil(type_args) or is_list(type_args)) do + line = Keyword.fetch!(meta_attr, :line) + column = Keyword.fetch!(meta_attr, :column) + pre_spec( ast, state, @@ -650,13 +708,16 @@ defmodule ElixirSense.Core.MetadataBuilder do end defp pre( - {:@, [line: line, column: column] = _meta_attr, + {:@, meta_attr, [{kind, _, [{:"::", _meta, _params = [{name, _, type_args}, _type_def]} = spec]}]} = ast, state ) when kind in [:spec, :callback, :macrocallback] and is_atom(name) and (is_nil(type_args) or is_list(type_args)) do + line = Keyword.fetch!(meta_attr, :line) + column = Keyword.fetch!(meta_attr, :column) + pre_spec( ast, state, @@ -671,12 +732,14 @@ defmodule ElixirSense.Core.MetadataBuilder do # incomplete spec # @callback my(integer) defp pre( - {:@, [line: line, column: column] = _meta_attr, - [{kind, _, [{name, _, type_args}]} = spec]} = ast, + {:@, meta_attr, [{kind, _, [{name, _, type_args}]} = spec]} = ast, state ) when kind in [:spec, :callback, :macrocallback] and is_atom(name) and (is_nil(type_args) or is_list(type_args)) do + line = Keyword.fetch!(meta_attr, :line) + column = Keyword.fetch!(meta_attr, :column) + pre_spec( ast, state, @@ -688,38 +751,54 @@ defmodule ElixirSense.Core.MetadataBuilder do ) end - defp pre({:@, [line: line, column: column] = meta_attr, [{name, meta, params}]}, state) do - {type, is_definition} = + defp pre({:@, meta_attr, [{name, meta, params}]}, state) do + line = Keyword.fetch!(meta_attr, :line) + column = Keyword.fetch!(meta_attr, :column) + + binding = case List.wrap(params) do [] -> {nil, false} [param] -> {get_binding_type(state, param), true} + + _ -> + :error end - state = - add_moduledoc_positions(state, [line: line, column: column], [{name, meta, params}], line) + case binding do + {type, is_definition} -> + state = + add_moduledoc_positions( + state, + [line: line, column: column], + [{name, meta, params}], + line + ) + + new_ast = {:@, meta_attr, [{name, add_no_call(meta), params}]} + pre_module_attribute(new_ast, state, {line, column}, name, type, is_definition) - new_ast = {:@, meta_attr, [{name, add_no_call(meta), params}]} - pre_module_attribute(new_ast, state, {line, column}, name, type, is_definition) + _ -> + {[], state} + end end # transform 1.2 alias/require/import/use syntax ast into regular defp pre( - {directive, [line: line, column: column], - [{{:., _, [prefix_expression, :{}]}, _, postfix_expressions} | _]}, + {directive, meta, [{{:., _, [prefix_expression, :{}]}, _, postfix_expressions} | _]}, state ) when directive in [:alias, :require, :import, :use] do directives = modules_from_12_syntax(state, postfix_expressions, prefix_expression) |> Enum.map(fn module_list -> - {directive, [line: line, column: column], [{:__aliases__, [], module_list}]} + {directive, meta, [{:__aliases__, [], module_list}]} end) state - |> result({:__block__, [line: line, column: column], directives}) + |> result({:__block__, meta, directives}) end # transform alias/require/import/use without options into with empty options @@ -730,26 +809,27 @@ defmodule ElixirSense.Core.MetadataBuilder do # import with options defp pre( - {:import, [line: line, column: _column], - [{:__aliases__, _, module_expression = [_ | _]}, opts]} = ast, + {:import, meta, [{:__aliases__, _, module_expression = [_ | _]}, opts]} = ast, state ) do + line = Keyword.fetch!(meta, :line) module = concat_module_expression(state, module_expression) pre_import(ast, state, line, module, opts) end # atom module - defp pre({:import, [line: line, column: _column], [atom, opts] = ast}, state) + defp pre({:import, meta, [atom, opts] = ast}, state) when is_atom(atom) do + line = Keyword.fetch!(meta, :line) pre_import(ast, state, line, atom, opts) end # require with `as` option defp pre( - {:require, [line: line, column: _column], - [{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast, + {:require, meta, [{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast, state ) do + line = Keyword.fetch!(meta, :line) module = concat_module_expression(state, module_expression) alias_tuple = alias_tuple(module, alias_expression) @@ -758,8 +838,9 @@ defmodule ElixirSense.Core.MetadataBuilder do end # require erlang module with `as` option - defp pre({:require, [line: line, column: _column], [mod, [as: alias_expression]]} = ast, state) + defp pre({:require, meta, [mod, [as: alias_expression]]} = ast, state) when is_atom(mod) do + line = Keyword.fetch!(meta, :line) alias_tuple = alias_tuple(mod, alias_expression) {_, new_state} = pre_alias(ast, state, line, alias_tuple) pre_require(ast, new_state, line, mod) @@ -767,25 +848,27 @@ defmodule ElixirSense.Core.MetadataBuilder do # require with options defp pre( - {:require, [line: line, column: _column], [{_, _, module_expression = [_ | _]}, _opts]} = - ast, + {:require, meta, [{_, _, module_expression = [_ | _]}, _opts]} = ast, state ) do + line = Keyword.fetch!(meta, :line) module = concat_module_expression(state, module_expression) pre_require(ast, state, line, module) end - defp pre({:require, [line: line, column: _column], [mod, _opts]} = ast, state) + defp pre({:require, meta, [mod, _opts]} = ast, state) when is_atom(mod) do + line = Keyword.fetch!(meta, :line) pre_require(ast, state, line, mod) end # alias with `as` option defp pre( - {:alias, [line: line, column: column], - [{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast, + {:alias, meta, [{_, _, module_expression = [_ | _]}, [as: alias_expression]]} = ast, state ) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) module = concat_module_expression(state, module_expression) alias_tuple = alias_tuple(module, alias_expression) state = add_first_alias_positions(state, line, column) @@ -794,9 +877,11 @@ defmodule ElixirSense.Core.MetadataBuilder do # alias for __MODULE__ defp pre( - {:alias, [line: line, column: column], [{:__MODULE__, _, nil}, []]} = ast, + {:alias, meta, [{:__MODULE__, _, nil}, []]} = ast, state ) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) module = get_current_module(state) if module == Elixir do @@ -817,10 +902,11 @@ defmodule ElixirSense.Core.MetadataBuilder do # alias for submodule of __MODULE__ with `as` option defp pre( - {:alias, [line: line, column: column], [{:__MODULE__, _, nil}, [as: alias_expression]]} = - ast, + {:alias, meta, [{:__MODULE__, _, nil}, [as: alias_expression]]} = ast, state ) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) module = get_current_module(state) alias_tuple = alias_tuple(module, alias_expression) state = add_first_alias_positions(state, line, column) @@ -828,8 +914,10 @@ defmodule ElixirSense.Core.MetadataBuilder do end # alias atom module with `as` option - defp pre({:alias, [line: line, column: column], [mod, [as: alias_expression]]} = ast, state) + defp pre({:alias, meta, [mod, [as: alias_expression]]} = ast, state) when is_atom(mod) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) alias_tuple = alias_tuple(mod, alias_expression) state = add_first_alias_positions(state, line, column) pre_alias(ast, state, line, alias_tuple) @@ -837,10 +925,11 @@ defmodule ElixirSense.Core.MetadataBuilder do # alias defp pre( - {:alias, [line: line, column: column], - [{:__aliases__, _, module_expression = [_ | _]}, _opts]} = ast, + {:alias, meta, [{:__aliases__, _, module_expression = [_ | _]}, _opts]} = ast, state ) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) module = concat_module_expression(state, module_expression) alias_tuple = {Module.concat([List.last(module_expression)]), module} state = add_first_alias_positions(state, line, column) @@ -848,8 +937,11 @@ defmodule ElixirSense.Core.MetadataBuilder do end # alias atom module - defp pre({:alias, [line: line, column: column], [mod, _opts]} = ast, state) + defp pre({:alias, meta, [mod, _opts]} = ast, state) when is_atom(mod) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) + if Introspection.elixir_module?(mod) do alias_tuple = {Module.concat([List.last(Module.split(mod))]), mod} @@ -894,8 +986,9 @@ defmodule ElixirSense.Core.MetadataBuilder do {ast, state} end - defp pre({atom, [line: line, column: _column], [_ | _]} = ast, state) + defp pre({atom, meta, [_ | _]} = ast, state) when atom in @scope_keywords do + line = Keyword.fetch!(meta, :line) pre_scope_keyword(ast, state, line) end @@ -915,13 +1008,16 @@ defmodule ElixirSense.Core.MetadataBuilder do pre_clause({:->, meta, [:_, rhs]}, state, lhs) end - defp pre({atom, [line: _line, column: _column] = meta, [lhs, rhs]}, state) + defp pre({atom, meta, [lhs, rhs]}, state) when atom in [:=, :<-] do result(state, {atom, meta, [lhs, rhs]}) end - defp pre({var_or_call, [line: line, column: column], nil} = ast, state) + defp pre({var_or_call, meta, nil} = ast, state) when is_atom(var_or_call) and var_or_call != :__MODULE__ do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) + if Enum.any?(get_current_vars(state), &(&1.name == var_or_call)) do vars = state @@ -932,7 +1028,11 @@ defmodule ElixirSense.Core.MetadataBuilder do else # pre Elixir 1.4 local call syntax # TODO remove on Elixir 2.0 - add_call_to_line(state, {nil, var_or_call, 0}, {line, column}) + if is_call_meta(meta) do + add_call_to_line(state, {nil, var_or_call, 0}, {line, column}) + else + state + end end |> add_current_env_to_line(line) |> result(ast) @@ -962,11 +1062,14 @@ defmodule ElixirSense.Core.MetadataBuilder do meta |> Keyword.take([:line, :column]) ) - {expanded_ast, state} + {{:__generated__, [], [expanded_ast]}, %{state | generated: true}} end - defp pre({type, [line: line, column: column], fields} = ast, state) + defp pre({type, meta, fields} = ast, state) when type in [:defstruct, :defexception] do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) + fields = case fields do [fields] when is_list(fields) -> @@ -1075,16 +1178,24 @@ defmodule ElixirSense.Core.MetadataBuilder do :defrecordp -> :defmacrop end - options = [] + options = [generated: true] state |> new_named_func(name, 1) - |> add_func_to_index(name, [{:\\, [], [{:args, [], nil}, []]}], {line, column}, type, options) + |> add_func_to_index( + name, + [{:\\, [], [{:args, [], nil}, []]}], + {line, column}, + nil, + type, + options + ) |> new_named_func(name, 2) |> add_func_to_index( name, [{:record, [], nil}, {:args, [], nil}], {line, column}, + nil, type, options ) @@ -1154,13 +1265,18 @@ defmodule ElixirSense.Core.MetadataBuilder do end # Any other tuple with a line - defp pre({_, [line: line, column: _column], _} = ast, state) do - state - |> add_current_env_to_line(line) - |> result(ast) + defp pre({_, meta, _} = ast, state) do + case Keyword.get(meta, :line) do + nil -> + {ast, state} + + line -> + state + |> add_current_env_to_line(line) + |> result(ast) + end end - # No line defined defp pre(ast, state) do {ast, state} end @@ -1190,13 +1306,13 @@ defmodule ElixirSense.Core.MetadataBuilder do post_module(ast, state) end - defp post({def_name, [line: _line, column: _column], [{name, _, _params}, _]} = ast, state) + defp post({def_name, _meta, [{name, _, _params}, _]} = ast, state) when def_name in @defs and is_atom(name) do post_func(ast, state) end defp post( - {def_name, [line: _line, column: _column], [{name, _, _params}, _guards, _]} = ast, + {def_name, _meta, [{name, _, _params}, _guards, _]} = ast, state ) when def_name in @defs and is_atom(name) do @@ -1280,12 +1396,17 @@ defmodule ElixirSense.Core.MetadataBuilder do post_block_keyword(ast, state) end - defp post({:->, [line: _line, column: _column], [_lhs, _rhs]} = ast, state) do + defp post({:->, _meta, [_lhs, _rhs]} = ast, state) do post_clause(ast, state) end - defp post({atom, [line: line, column: _column], [lhs, rhs]} = ast, state) + defp post({:__generated__, _meta, inner}, state) do + {inner, %{state | generated: false}} + end + + defp post({atom, meta, [lhs, rhs]} = ast, state) when atom in [:=, :<-] do + line = Keyword.fetch!(meta, :line) match_context_r = get_binding_type(state, rhs) match_context_r = @@ -1344,7 +1465,9 @@ defmodule ElixirSense.Core.MetadataBuilder do defp find_vars(state, ast, match_context \\ nil) - defp find_vars(_state, {var, [line: line, column: column], nil}, :rescue) when is_atom(var) do + defp find_vars(_state, {var, meta, nil}, :rescue) when is_atom(var) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) match_context = {:struct, [], {:atom, Exception}, nil} [%VarInfo{name: var, positions: [{line, column}], type: match_context, is_definition: true}] end @@ -1407,20 +1530,25 @@ defmodule ElixirSense.Core.MetadataBuilder do defp match_var( _state, - {:^, _meta, [{var, [line: line, column: column], nil}]}, + {:^, _meta, [{var, meta, nil}]}, {vars, match_context} = ast ) when is_atom(var) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) var_info = %VarInfo{name: var, positions: [{line, column}], type: match_context} {ast, {[var_info | vars], nil}} end defp match_var( _state, - {var, [line: line, column: column], nil} = ast, + {var, meta, nil} = ast, {vars, match_context} ) when is_atom(var) do + line = Keyword.fetch!(meta, :line) + column = Keyword.fetch!(meta, :column) + var_info = %VarInfo{ name: var, positions: [{line, column}], @@ -1709,13 +1837,13 @@ defmodule ElixirSense.Core.MetadataBuilder do defp pre_protocol_implementation( ast, state, - position, + meta, protocol, for_expression ) do implementations = get_implementations_from_for_expression(state, for_expression) - pre_module(ast, state, position, {protocol, implementations}, [], [{:__impl__, [:atom], :def}]) + pre_module(ast, state, meta, {protocol, implementations}, [], [{:__impl__, [:atom], :def}]) end defp get_implementations_from_for_expression(state, for: for_expression) do @@ -1810,6 +1938,8 @@ defmodule ElixirSense.Core.MetadataBuilder do [] end + options = [generated: true] + state = if type == :defexception do state = @@ -1822,13 +1952,17 @@ defmodule ElixirSense.Core.MetadataBuilder do :exception, [{:msg, [line: line, column: column], nil}], {line, column}, - :def + nil, + :def, + options ) |> add_func_to_index( :message, [{:exception, [line: line, column: column], nil}], {line, column}, - :def + nil, + :def, + options ) else state @@ -1837,17 +1971,21 @@ defmodule ElixirSense.Core.MetadataBuilder do :exception, [{:args, [line: line, column: column], nil}], {line, column}, - :def + nil, + :def, + options ) else state end - |> add_func_to_index(:__struct__, [], {line, column}, :def) + |> add_func_to_index(:__struct__, [], {line, column}, nil, :def, options) |> add_func_to_index( :__struct__, [{:kv, [line: line, column: column], nil}], {line, column}, - :def + nil, + :def, + options ) state diff --git a/lib/elixir_sense/core/parser.ex b/lib/elixir_sense/core/parser.ex index b483aff1..1792b402 100644 --- a/lib/elixir_sense/core/parser.ex +++ b/lib/elixir_sense/core/parser.ex @@ -107,7 +107,7 @@ defmodule ElixirSense.Core.Parser do original_error \\ nil, opts \\ [] ) do - case Code.string_to_quoted(source, opts |> Keyword.put(:columns, true)) do + case Code.string_to_quoted(source, opts |> Keyword.merge(columns: true, token_metadata: true)) do {:ok, ast} -> {:ok, ast, source} diff --git a/lib/elixir_sense/core/state.ex b/lib/elixir_sense/core/state.ex index 53341107..848c28b8 100644 --- a/lib/elixir_sense/core/state.ex +++ b/lib/elixir_sense/core/state.ex @@ -52,6 +52,7 @@ defmodule ElixirSense.Core.State do calls: calls_t, structs: structs_t, types: types_t, + generated: boolean, first_alias_positions: map(), moduledoc_positions: map(), # TODO @@ -81,6 +82,7 @@ defmodule ElixirSense.Core.State do calls: %{}, structs: %{}, types: %{}, + generated: false, binding_context: [], first_alias_positions: %{}, moduledoc_positions: %{} @@ -148,9 +150,17 @@ defmodule ElixirSense.Core.State do args: list(list(String.t())), specs: [String.t()], kind: :type | :typep | :opaque, - positions: [ElixirSense.Core.State.position_t()] + positions: [ElixirSense.Core.State.position_t()], + end_positions: [ElixirSense.Core.State.position_t() | nil], + generated: list(boolean) } - defstruct name: nil, args: [], specs: [], kind: :type, positions: [] + defstruct name: nil, + args: [], + specs: [], + kind: :type, + positions: [], + end_positions: [], + generated: [] end defmodule SpecInfo do @@ -162,9 +172,17 @@ defmodule ElixirSense.Core.State do args: list(list(String.t())), specs: [String.t()], kind: :spec | :callback | :macrocallback, - positions: [ElixirSense.Core.State.position_t()] + positions: [ElixirSense.Core.State.position_t()], + end_positions: [ElixirSense.Core.State.position_t() | nil], + generated: list(boolean) } - defstruct name: nil, args: [], specs: [], kind: :spec, positions: [] + defstruct name: nil, + args: [], + specs: [], + kind: :spec, + positions: [], + end_positions: [], + generated: [] end defmodule StructInfo do @@ -215,8 +233,10 @@ defmodule ElixirSense.Core.State do @type t :: %ModFunInfo{ params: list(list(term)), positions: list(ElixirSense.Core.State.position_t()), + end_positions: list(ElixirSense.Core.State.position_t() | nil), target: nil | {module, atom}, overridable: false | {true, module}, + generated: list(boolean), # TODO defmodule defprotocol defimpl? type: :def @@ -231,8 +251,10 @@ defmodule ElixirSense.Core.State do defstruct params: [], positions: [], + end_positions: [], target: nil, type: nil, + generated: [], overridable: false def get_arities(%ModFunInfo{params: params_variants}) do @@ -377,7 +399,7 @@ defmodule ElixirSense.Core.State do when is_integer(line) and is_integer(column) do current_scope = hd(hd(state.scopes)) - is_module? = is_atom(current_scope) + is_module? = is_atom(current_scope) and current_scope != Elixir if is_module? do module_name = module_name(state) @@ -486,15 +508,19 @@ defmodule ElixirSense.Core.State do %__MODULE__{} = state, {module, fun, arity}, position, + end_position, params, type, options \\ [] - ) do + ) + when is_tuple(position) do current_info = Map.get(state.mods_funs_to_positions, {module, fun, arity}, %ModFunInfo{}) current_params = current_info |> Map.get(:params, []) current_positions = current_info |> Map.get(:positions, []) + current_end_positions = current_info |> Map.get(:end_positions, []) new_params = [params | current_params] new_positions = [position | current_positions] + new_end_positions = [end_position | current_end_positions] info_type = if fun != nil and arity == nil and @@ -509,8 +535,10 @@ defmodule ElixirSense.Core.State do info = %ModFunInfo{ positions: new_positions, + end_positions: new_end_positions, params: new_params, type: info_type, + generated: [Keyword.get(options, :generated, false) | current_info.generated], overridable: current_info |> Map.get(:overridable, false) } @@ -686,30 +714,66 @@ defmodule ElixirSense.Core.State do %__MODULE__{state | scopes: tl(state.scopes)} end - def add_current_module_to_index(%__MODULE__{} = state, position) do + def add_current_module_to_index(%__MODULE__{} = state, position, end_position, options) + when (is_tuple(position) and is_tuple(end_position)) or is_nil(end_position) do current_module_variants = get_current_module_variants(state) current_module_variants |> Enum.reduce(state, fn variant, acc -> acc - |> add_module_to_index(variant, position) + |> add_module_to_index(variant, position, end_position, options) end) end - def add_module_to_index(%__MODULE__{} = state, module, position) do + def add_module_to_index(%__MODULE__{} = state, module, position, end_position, options) + when (is_tuple(position) and is_tuple(end_position)) or is_nil(end_position) do # TODO :defprotocol, :defimpl? - add_mod_fun_to_position(state, {module, nil, nil}, position, nil, :defmodule) + add_mod_fun_to_position( + state, + {module, nil, nil}, + position, + end_position, + nil, + :defmodule, + options + ) end - def add_func_to_index(%__MODULE__{} = state, func, params, position, type, options \\ []) do + # TODO require end position + def add_func_to_index(state, func, params, position, end_position \\ nil, type, options \\ []) + + def add_func_to_index( + %__MODULE__{} = state, + func, + params, + position, + end_position, + type, + options + ) + when (is_tuple(position) and is_tuple(end_position)) or is_nil(end_position) do current_module_variants = get_current_module_variants(state) arity = length(params) current_module_variants |> Enum.reduce(state, fn variant, acc -> acc - |> add_mod_fun_to_position({variant, func, arity}, position, params, type, options) - |> add_mod_fun_to_position({variant, func, nil}, position, params, type, options) + |> add_mod_fun_to_position( + {variant, func, arity}, + position, + end_position, + params, + type, + options + ) + |> add_mod_fun_to_position( + {variant, func, nil}, + position, + end_position, + params, + type, + options + ) end) end @@ -1001,7 +1065,7 @@ defmodule ElixirSense.Core.State do Enum.reduce(modules, state, fn mod, state -> add_require(state, mod) end) end - def add_type(%__MODULE__{} = state, type_name, type_args, spec, kind, pos) do + def add_type(%__MODULE__{} = state, type_name, type_args, spec, kind, pos, options \\ []) do arg_names = type_args |> Enum.map(&Macro.to_string/1) @@ -1011,7 +1075,10 @@ defmodule ElixirSense.Core.State do args: [arg_names], kind: kind, specs: [spec], - positions: [pos] + generated: [Keyword.get(options, :generated, false)], + positions: [pos], + # no end pos info in AST as of elixir 1.15 + end_positions: [nil] } current_module_variants = get_current_module_variants(state) @@ -1028,6 +1095,8 @@ defmodule ElixirSense.Core.State do %TypeInfo{ ti | positions: [pos | positions], + end_positions: [nil | ti.end_positions], + generated: [Keyword.get(options, :generated, false) | ti.generated], args: [arg_names | args], specs: [spec | specs], # in case there are multiple definitions for nil arity prefer public ones @@ -1043,7 +1112,7 @@ defmodule ElixirSense.Core.State do %__MODULE__{state | types: types} end - def add_spec(%__MODULE__{} = state, type_name, type_args, spec, kind, pos) do + def add_spec(%__MODULE__{} = state, type_name, type_args, spec, kind, pos, options) do arg_names = type_args |> Enum.map(&Macro.to_string/1) @@ -1053,7 +1122,10 @@ defmodule ElixirSense.Core.State do args: [arg_names], specs: [spec], kind: kind, - positions: [pos] + generated: [Keyword.get(options, :generated, false)], + positions: [pos], + # no end pos info in AST as of elixir 1.15 + end_positions: [nil] } current_module_variants = get_current_module_variants(state) @@ -1070,6 +1142,8 @@ defmodule ElixirSense.Core.State do %SpecInfo{ ti | positions: [pos | positions], + end_positions: [nil | ti.end_positions], + generated: [Keyword.get(options, :generated, false) | ti.generated], args: [arg_names | args], specs: [spec | specs] } diff --git a/lib/elixir_sense/providers/suggestion/reducers/docs_snippets.ex b/lib/elixir_sense/providers/suggestion/reducers/docs_snippets.ex index 39dd25d1..b626f24f 100644 --- a/lib/elixir_sense/providers/suggestion/reducers/docs_snippets.ex +++ b/lib/elixir_sense/providers/suggestion/reducers/docs_snippets.ex @@ -18,7 +18,7 @@ defmodule ElixirSense.Providers.Suggestion.Reducers.DocsSnippets do @doc """ A reducer that adds suggestions for @doc, @moduledoc and @typedoc. """ - def add_snippets(hint, _env, _metadata, %{at_module_body?: true}, acc) do + def add_snippets(hint, env, _metadata, %{at_module_body?: true}, acc) do list = for {label, snippet, doc, priority} <- @module_attr_snippets, Matcher.match?(label, hint) do diff --git a/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex b/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex index 7361ab7d..4a6c44c9 100644 --- a/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex +++ b/lib/elixir_sense/providers/suggestion/reducers/type_specs.ex @@ -26,7 +26,7 @@ defmodule ElixirSense.Providers.Suggestion.Reducers.TypeSpecs do """ # We only list type specs when inside typespec scope - def add_types(hint, env, file_metadata, %{at_module_body?: true}, acc) do + def add_types(hint, env, file_metadata, %{at_module_body?: _}, acc) do if match?({:typespec, _, _}, env.scope) do %State.Env{ aliases: aliases, diff --git a/test/elixir_sense/core/metadata_builder/alias_test.exs b/test/elixir_sense/core/metadata_builder/alias_test.exs index 0bab37b9..e56ecc63 100644 --- a/test/elixir_sense/core/metadata_builder/alias_test.exs +++ b/test/elixir_sense/core/metadata_builder/alias_test.exs @@ -39,7 +39,7 @@ defmodule ElixirSense.Core.MetadataBuilder.AliasTest do state = unquote(module).module_info()[:compile][:source] |> File.read!() - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() env = unquote(module).env() @@ -57,7 +57,7 @@ defmodule ElixirSense.Core.MetadataBuilder.AliasTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) @@ -75,7 +75,7 @@ defmodule ElixirSense.Core.MetadataBuilder.AliasTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) diff --git a/test/elixir_sense/core/metadata_builder/import_test.exs b/test/elixir_sense/core/metadata_builder/import_test.exs index 86dc1395..7ed8187c 100644 --- a/test/elixir_sense/core/metadata_builder/import_test.exs +++ b/test/elixir_sense/core/metadata_builder/import_test.exs @@ -38,7 +38,7 @@ defmodule ElixirSense.Core.MetadataBuilder.ImportTest do state = unquote(module).module_info()[:compile][:source] |> File.read!() - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() env = unquote(module).env() @@ -64,7 +64,7 @@ defmodule ElixirSense.Core.MetadataBuilder.ImportTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) @@ -84,7 +84,7 @@ defmodule ElixirSense.Core.MetadataBuilder.ImportTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) diff --git a/test/elixir_sense/core/metadata_builder/require_test.exs b/test/elixir_sense/core/metadata_builder/require_test.exs index c3993528..418efce9 100644 --- a/test/elixir_sense/core/metadata_builder/require_test.exs +++ b/test/elixir_sense/core/metadata_builder/require_test.exs @@ -28,7 +28,7 @@ defmodule ElixirSense.Core.MetadataBuilder.RequireTest do state = unquote(module).module_info()[:compile][:source] |> File.read!() - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() env = unquote(module).env() @@ -46,7 +46,7 @@ defmodule ElixirSense.Core.MetadataBuilder.RequireTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) @@ -64,7 +64,7 @@ defmodule ElixirSense.Core.MetadataBuilder.RequireTest do state = code - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> MetadataBuilder.build() {env, _} = Code.eval_string(code, []) diff --git a/test/elixir_sense/core/metadata_builder_test.exs b/test/elixir_sense/core/metadata_builder_test.exs index c560864a..adfcb326 100644 --- a/test/elixir_sense/core/metadata_builder_test.exs +++ b/test/elixir_sense/core/metadata_builder_test.exs @@ -2779,177 +2779,34 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert %{ {Enumerable.MyOtherStruct, nil, nil} => %ModFunInfo{ params: [nil], - positions: [[line: 17, column: 3]], type: :defmodule }, - {MyOtherStruct, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{16, 11}], - type: :defmodule - }, - {MyStruct, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{9, 11}], - type: :defmodule - }, - {Proto, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{1, 13}], - type: :defmodule - }, - {Proto, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 2, column: 15], nil}]], - positions: [{2, 7}], - type: :def - }, - {Proto, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 2, column: 15], nil}]], - positions: [{2, 7}], - type: :def - }, {Proto.Any, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{5, 9}], type: :defmodule }, - {Proto.Any, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], - type: :def - }, - {Proto.Any, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], - type: :def - }, {Proto.MyOtherStruct, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{5, 9}], type: :defmodule }, {Proto.MyOtherStruct, :reverse, 1} => %ModFunInfo{ params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], type: :def }, {Proto.MyOtherStruct, :reverse, nil} => %ModFunInfo{ params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], type: :def }, {Proto.MyStruct, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{5, 9}], type: :defmodule }, {Proto.MyStruct, :reverse, 1} => %ModFunInfo{ params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], type: :def }, {Proto.MyStruct, :reverse, nil} => %ModFunInfo{ params: [[{:term, [line: 6, column: 15], nil}]], - positions: [{6, 7}], - type: :def - }, - {MyOtherStruct, :__struct__, 0} => %ModFunInfo{ - params: [[]], - positions: [{18, 3}], - type: :def - }, - {MyOtherStruct, :__struct__, 1} => %ModFunInfo{ - params: [[{:kv, [line: 18, column: 3], nil}]], - positions: [{18, 3}], - type: :def - }, - {MyOtherStruct, :__struct__, nil} => %ModFunInfo{ - params: [[{:kv, [line: 18, column: 3], nil}], []], - positions: [{18, 3}, {18, 3}], - type: :def - }, - {MyStruct, :__struct__, 0} => %ModFunInfo{ - params: [[]], - positions: [{11, 3}], - type: :def - }, - {MyStruct, :__struct__, 1} => %ModFunInfo{ - params: [[{:kv, [line: 11, column: 3], nil}]], - positions: [{11, 3}], - type: :def - }, - {MyStruct, :__struct__, nil} => %ModFunInfo{ - params: [[{:kv, [line: 11, column: 3], nil}], []], - positions: [{11, 3}, {11, 3}], - type: :def - }, - {Proto, :__protocol__, 1} => %ModFunInfo{ - params: [[{:atom, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :__protocol__, nil} => %ModFunInfo{ - params: [[{:atom, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :impl_for, 1} => %ModFunInfo{ - params: [[{:data, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :impl_for, nil} => %ModFunInfo{ - params: [[{:data, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :impl_for!, 1} => %ModFunInfo{ - params: [[{:data, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :impl_for!, nil} => %ModFunInfo{ - params: [[{:data, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto.MyStruct, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto.Any, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto.MyOtherStruct, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto.MyOtherStruct, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto.MyStruct, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto.Any, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 5, column: 9], nil}]], - positions: [{5, 9}], - type: :def - }, - {Proto, :behaviour_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 13], nil}]], - positions: [{1, 13}], - type: :def - }, - {Proto, :behaviour_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 13], nil}]], - positions: [{1, 13}], type: :def } } = state.mods_funs_to_positions @@ -2974,6 +2831,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :callback, name: :with_spec, positions: [{3, 3}], + end_positions: [nil], + generated: [false], specs: [ "@callback with_spec(t, boolean) :: number", "@spec with_spec(t, boolean) :: number" @@ -2984,6 +2843,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :callback, name: :with_spec, positions: [{3, 3}, {2, 3}], + end_positions: [nil, nil], + generated: [false, false], specs: [ "@callback with_spec(t, boolean) :: number", "@callback with_spec(t, integer) :: String.t", @@ -2995,14 +2856,18 @@ defmodule ElixirSense.Core.MetadataBuilderTest do args: [["t", "integer"]], kind: :callback, name: :without_spec, - positions: [{6, 7}], + positions: [{6, 3}], + end_positions: [nil], + generated: [true], specs: ["@callback without_spec(t, integer) :: term"] }, {Proto, :without_spec, 2} => %ElixirSense.Core.State.SpecInfo{ args: [["t", "integer"]], kind: :callback, name: :without_spec, - positions: [{6, 7}], + positions: [{6, 3}], + end_positions: [nil], + generated: [true], specs: ["@callback without_spec(t, integer) :: term"] } } @@ -3052,162 +2917,37 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert %{ {OuterModule, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{2, 11}], + positions: [{2, 1}], type: :defmodule }, {OuterModule.InnerModule, :func, 0} => %ModFunInfo{ params: [[]], - positions: [{5, 9}], + positions: [{5, 5}], type: :def }, {OuterModule.InnerModule, :func, nil} => %ModFunInfo{ params: [[]], - positions: [{5, 9}], + positions: [{5, 5}], type: :def }, - {OuterModule.InnerModule, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{4, 13}], - type: :defmodule - }, {Impls, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{28, 11}], + positions: [{28, 1}], type: :defmodule }, {Reversible, :reverse, 1} => %ModFunInfo{ params: [[{:term, [line: 19, column: 15], nil}]], - positions: [{19, 7}], - type: :def - }, - {Reversible, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 19, column: 15], nil}]], - positions: [{19, 7}], - type: :def - }, - {Reversible, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{18, 13}], - type: :defmodule - }, - {Reversible.Map, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{31, 11}], - type: :defmodule - }, - {Reversible.Map, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 32, column: 17], nil}]], - positions: [{32, 9}], - type: :def - }, - {Reversible.Map, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 32, column: 17], nil}]], - positions: [{32, 9}], - type: :def - }, - {Reversible.My.List, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{31, 11}], - type: :defmodule - }, - {Reversible.My.List, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 32, column: 17], nil}]], - positions: [{32, 9}], - type: :def - }, - {Reversible.My.List, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 32, column: 17], nil}]], - positions: [{32, 9}], - type: :def - }, - {Reversible.String, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{23, 9}], - type: :defmodule - }, - {Reversible.String, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 24, column: 15], nil}]], - positions: [{24, 7}], - type: :def - }, - {Reversible.String, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 24, column: 15], nil}]], - positions: [{24, 7}], - type: :def - }, - {Some.Nested, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{14, 11}], - type: :defmodule - }, - {Reversible, :__protocol__, 1} => %ModFunInfo{ - params: [[{:atom, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :__protocol__, nil} => %ModFunInfo{ - params: [[{:atom, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :impl_for, 1} => %ModFunInfo{ - params: [[{:data, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :impl_for, nil} => %ModFunInfo{ - params: [[{:data, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :impl_for!, 1} => %ModFunInfo{ - params: [[{:data, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :impl_for!, nil} => %ModFunInfo{ - params: [[{:data, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible.Map, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 31, column: 11], nil}]], - positions: [{31, 11}], - type: :def - }, - {Reversible.Map, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 31, column: 11], nil}]], - positions: [{31, 11}], - type: :def - }, - {Reversible.My.List, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 31, column: 11], nil}]], - positions: [{31, 11}], - type: :def - }, - {Reversible.My.List, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 31, column: 11], nil}]], - positions: [{31, 11}], + positions: [{19, 3}], type: :def }, {Reversible.String, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 23, column: 9], nil}]], - positions: [{23, 9}], - type: :def - }, - {Reversible.String, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 23, column: 9], nil}]], - positions: [{23, 9}], + params: [[{:atom, [line: 23, column: 1], nil}]], + positions: [{23, 1}], type: :def }, {Reversible, :behaviour_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 18, column: 13], nil}]], - positions: [{18, 13}], - type: :def - }, - {Reversible, :behaviour_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 18, column: 13], nil}]], - positions: [{18, 13}], + params: [[{:atom, [line: 18, column: 1], nil}]], + positions: [{18, 1}], type: :def } } = state.mods_funs_to_positions @@ -3437,33 +3177,31 @@ defmodule ElixirSense.Core.MetadataBuilderTest do |> string_to_state assert %{ - mods_funs_to_positions: %{ - {MyModule, :is_even, 1} => %{ - params: [[{:value, [line: 2, column: 20], nil}]], - positions: [{2, 12}] - }, - {MyModule, :is_even, nil} => %{ - params: [[{:value, [line: 2, column: 20], nil}]], - positions: [{2, 12}] - }, - {MyModule, :is_odd, 1} => %{ - params: [[{:value, [line: 3, column: 20], nil}]], - positions: [{3, 13}] - }, - {MyModule, :is_odd, nil} => %{ - params: [[{:value, [line: 3, column: 20], nil}]], - positions: [{3, 13}] - }, - {MyModule, :useless, 0} => %{ - params: [[]], - positions: [{4, 13}] - }, - {MyModule, :useless, nil} => %{ - params: [[]], - positions: [{4, 13}] - } + {MyModule, :is_even, 1} => %{ + params: [[{:value, [line: 2, column: 20], nil}]], + positions: [{2, 3}] + }, + {MyModule, :is_even, nil} => %{ + params: [[{:value, [line: 2, column: 20], nil}]], + positions: [{2, 3}] + }, + {MyModule, :is_odd, 1} => %{ + params: [[{:value, [line: 3, column: 20], nil}]], + positions: [{3, 3}] + }, + {MyModule, :is_odd, nil} => %{ + params: [[{:value, [line: 3, column: 20], nil}]], + positions: [{3, 3}] + }, + {MyModule, :useless, 0} => %{ + params: [[]], + positions: [{4, 3}] + }, + {MyModule, :useless, nil} => %{ + params: [[]], + positions: [{4, 3}] } - } = state + } = state.mods_funs_to_positions end test "registers mods and func" do @@ -3496,155 +3234,125 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert %{ {MyModuleWithFuns, :func, 0} => %ModFunInfo{ params: [[]], - positions: [{4, 7}], type: :def }, {MyModuleWithFuns, :func, nil} => %ModFunInfo{ params: [[]], - positions: [{4, 7}], type: :def }, {MyModuleWithFuns, :funcp, 0} => %ModFunInfo{ params: [[]], - positions: [{7, 8}], type: :defp }, {MyModuleWithFuns, :funcp, nil} => %ModFunInfo{ params: [[]], - positions: [{7, 8}], type: :defp }, {MyModuleWithFuns, :is_even, 1} => %ModFunInfo{ params: [[{:value, [line: 16, column: 20], nil}]], - positions: [{16, 12}], type: :defguard }, {MyModuleWithFuns, :is_even, nil} => %ModFunInfo{ params: [[{:value, [line: 16, column: 20], nil}]], - positions: [{16, 12}], type: :defguard }, {MyModuleWithFuns, :is_evenp, 1} => %ModFunInfo{ params: [[{:value, [line: 17, column: 22], nil}]], - positions: [{17, 13}], type: :defguardp }, {MyModuleWithFuns, :is_evenp, nil} => %ModFunInfo{ params: [[{:value, [line: 17, column: 22], nil}]], - positions: [{17, 13}], type: :defguardp }, {MyModuleWithFuns, :macro1, 1} => %ModFunInfo{ params: [[{:ast, [line: 10, column: 19], nil}]], - positions: [{10, 12}], type: :defmacro }, {MyModuleWithFuns, :macro1, nil} => %ModFunInfo{ params: [[{:ast, [line: 10, column: 19], nil}]], - positions: [{10, 12}], type: :defmacro }, {MyModuleWithFuns, :macro1p, 1} => %ModFunInfo{ params: [[{:ast, [line: 13, column: 21], nil}]], - positions: [{13, 13}], type: :defmacrop }, {MyModuleWithFuns, :macro1p, nil} => %ModFunInfo{ params: [[{:ast, [line: 13, column: 21], nil}]], - positions: [{13, 13}], type: :defmacrop }, {MyModuleWithFuns, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{3, 11}], type: :defmodule }, {MyModuleWithFuns.Nested, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{19, 13}], type: :defmodule }, {MyModuleWithoutFuns, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{1, 11}], type: :defmodule }, {MyModuleWithFuns, :__info__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 3, column: 11], nil}]], - positions: [{3, 11}], + params: [[{:atom, [line: 3, column: 1], nil}]], type: :def }, {MyModuleWithFuns, :__info__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 3, column: 11], nil}]], - positions: [{3, 11}], + params: [[{:atom, [line: 3, column: 1], nil}]], type: :def }, {MyModuleWithFuns, :module_info, 0} => %ElixirSense.Core.State.ModFunInfo{ params: [[]], - positions: [{3, 11}], type: :def }, {MyModuleWithFuns, :module_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 3, column: 11], nil}]], - positions: [{3, 11}], + params: [[{:atom, [line: 3, column: 1], nil}]], type: :def }, {MyModuleWithFuns, :module_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 3, column: 11], nil}], []], - positions: [{3, 11}, {3, 11}], + params: [[{:atom, [line: 3, column: 1], nil}], []], type: :def }, {MyModuleWithFuns.Nested, :__info__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 19, column: 13], nil}]], - positions: [{19, 13}], + params: [[{:atom, [line: 19, column: 3], nil}]], type: :def }, {MyModuleWithFuns.Nested, :__info__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 19, column: 13], nil}]], - positions: [{19, 13}], + params: [[{:atom, [line: 19, column: 3], nil}]], type: :def }, {MyModuleWithFuns.Nested, :module_info, 0} => %ElixirSense.Core.State.ModFunInfo{ params: [[]], - positions: [{19, 13}], type: :def }, {MyModuleWithFuns.Nested, :module_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 19, column: 13], nil}]], - positions: [{19, 13}], + params: [[{:atom, [line: 19, column: 3], nil}]], type: :def }, {MyModuleWithFuns.Nested, :module_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 19, column: 13], nil}], []], - positions: [{19, 13}, {19, 13}], + params: [[{:atom, [line: 19, column: 3], nil}], []], type: :def }, {MyModuleWithoutFuns, :__info__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 11], nil}]], - positions: [{1, 11}], + params: [[{:atom, [line: 1, column: 1], nil}]], type: :def }, {MyModuleWithoutFuns, :__info__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 11], nil}]], - positions: [{1, 11}], + params: [[{:atom, [line: 1, column: 1], nil}]], type: :def }, {MyModuleWithoutFuns, :module_info, 0} => %ElixirSense.Core.State.ModFunInfo{ params: [[]], - positions: [{1, 11}], type: :def }, {MyModuleWithoutFuns, :module_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 11], nil}]], - positions: [{1, 11}], + params: [[{:atom, [line: 1, column: 1], nil}]], type: :def }, {MyModuleWithoutFuns, :module_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 1, column: 11], nil}], []], - positions: [{1, 11}, {1, 11}], + params: [[{:atom, [line: 1, column: 1], nil}], []], type: :def } - } == state.mods_funs_to_positions + } = state.mods_funs_to_positions end test "registers delegated func" do @@ -3663,49 +3371,49 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert %{ {MyModuleWithFuns, :func_delegated, 1} => %ModFunInfo{ params: [[{:par, [line: 3, column: 30], nil}]], - positions: [{3, 15}], + positions: [{3, 3}], target: {OtherModule, :func_delegated}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated, nil} => %ModFunInfo{ params: [[{:par, [line: 3, column: 30], nil}]], - positions: [{3, 15}], + positions: [{3, 3}], target: {OtherModule, :func_delegated}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_alias, 1} => %ModFunInfo{ params: [[{:par, [line: 6, column: 36], nil}]], - positions: [{6, 15}], + positions: [{6, 3}], target: {Enum, :func_delegated_alias}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_alias, nil} => %ModFunInfo{ params: [[{:par, [line: 6, column: 36], nil}]], - positions: [{6, 15}], + positions: [{6, 3}], target: {Enum, :func_delegated_alias}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_as, 1} => %ModFunInfo{ params: [[{:par, [line: 5, column: 33], nil}]], - positions: [{5, 15}], + positions: [{5, 3}], target: {MyModuleWithFuns.Sub, :my_func}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_as, nil} => %ModFunInfo{ params: [[{:par, [line: 5, column: 33], nil}]], - positions: [{5, 15}], + positions: [{5, 3}], target: {MyModuleWithFuns.Sub, :my_func}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_erlang, 1} => %ModFunInfo{ params: [[{:par, [line: 4, column: 37], nil}]], - positions: [{4, 15}], + positions: [{4, 3}], target: {:erlang_module, :func_delegated_erlang}, type: :defdelegate }, {MyModuleWithFuns, :func_delegated_erlang, nil} => %ModFunInfo{ params: [[{:par, [line: 4, column: 37], nil}]], - positions: [{4, 15}], + positions: [{4, 3}], target: {:erlang_module, :func_delegated_erlang}, type: :defdelegate } @@ -3758,228 +3466,9 @@ defmodule ElixirSense.Core.MetadataBuilderTest do """ |> string_to_state - assert %{ - {Impls, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{33, 11}], - type: :defmodule - }, - {MyModuleWithFuns, :func, 0} => %ModFunInfo{ - params: [[]], - positions: [{4, 7}], - type: :def - }, - {MyModuleWithFuns, :func, nil} => %ModFunInfo{ - params: [[]], - positions: [{4, 7}], - type: :def - }, - {MyModuleWithFuns, :func_delegated, 1} => %ModFunInfo{ - params: [[{:par, [line: 18, column: 30], nil}]], - positions: [{18, 15}], - type: :defdelegate - }, - {MyModuleWithFuns, :func_delegated, nil} => %ModFunInfo{ - params: [[{:par, [line: 18, column: 30], nil}]], - positions: [{18, 15}], - type: :defdelegate - }, - {MyModuleWithFuns, :funcp, 0} => %ModFunInfo{ - params: [[]], - positions: [{7, 8}], - type: :defp - }, - {MyModuleWithFuns, :funcp, nil} => %ModFunInfo{ - params: [[]], - positions: [{7, 8}], - type: :defp - }, - {MyModuleWithFuns, :is_even, 1} => %ModFunInfo{ - params: [[{:value, [line: 16, column: 20], nil}]], - positions: [{16, 12}], - type: :defguard - }, - {MyModuleWithFuns, :is_even, nil} => %ModFunInfo{ - params: [[{:value, [line: 16, column: 20], nil}]], - positions: [{16, 12}], - type: :defguard - }, - {MyModuleWithFuns, :is_evenp, 1} => %ModFunInfo{ - params: [[{:value, [line: 17, column: 22], nil}]], - positions: [{17, 13}], - type: :defguardp - }, - {MyModuleWithFuns, :is_evenp, nil} => %ModFunInfo{ - params: [[{:value, [line: 17, column: 22], nil}]], - positions: [{17, 13}], - type: :defguardp - }, - {MyModuleWithFuns, :macro1, 1} => %ModFunInfo{ - params: [[{:ast, [line: 10, column: 19], nil}]], - positions: [{10, 12}], - type: :defmacro - }, - {MyModuleWithFuns, :macro1, nil} => %ModFunInfo{ - params: [[{:ast, [line: 10, column: 19], nil}]], - positions: [{10, 12}], - type: :defmacro - }, - {MyModuleWithFuns, :macro1p, 1} => %ModFunInfo{ - params: [[{:ast, [line: 13, column: 21], nil}]], - positions: [{13, 13}], - type: :defmacrop - }, - {MyModuleWithFuns, :macro1p, nil} => %ModFunInfo{ - params: [[{:ast, [line: 13, column: 21], nil}]], - positions: [{13, 13}], - type: :defmacrop - }, - {MyModuleWithFuns, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{3, 11}], - type: :defmodule - }, - {MyModuleWithFuns.Nested, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{19, 13}], - type: :defmodule - }, - {MyModuleWithoutFuns, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{1, 11}], - type: :defmodule - }, - {Reversible, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{23, 13}], - type: :defmodule - }, - {Reversible, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 24, column: 15], nil}]], - positions: [{24, 7}], - type: :def - }, - {Reversible, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 24, column: 15], nil}]], - positions: [{24, 7}], - type: :def - }, - {Reversible.Map, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{36, 11}], - type: :defmodule - }, - {Reversible.Map, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 37, column: 17], nil}]], - positions: [{37, 9}], - type: :def - }, - {Reversible.Map, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 37, column: 17], nil}]], - positions: [{37, 9}], - type: :def - }, - {Reversible.My.List, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{36, 11}], - type: :defmodule - }, - {Reversible.My.List, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 37, column: 17], nil}]], - positions: [{37, 9}], - type: :def - }, - {Reversible.My.List, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 37, column: 17], nil}]], - positions: [{37, 9}], - type: :def - }, - {Reversible.String, nil, nil} => %ModFunInfo{ - params: [nil], - positions: [{28, 9}], - type: :defmodule - }, - {Reversible.String, :reverse, 1} => %ModFunInfo{ - params: [[{:term, [line: 29, column: 15], nil}]], - positions: [{29, 7}], - type: :def - }, - {Reversible.String, :reverse, nil} => %ModFunInfo{ - params: [[{:term, [line: 29, column: 15], nil}]], - positions: [{29, 7}], - type: :def - }, - {Reversible, :impl_for!, nil} => %ModFunInfo{ - params: [[{:data, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :__protocol__, nil} => %ModFunInfo{ - params: [[{:atom, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :impl_for!, 1} => %ModFunInfo{ - params: [[{:data, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :impl_for, nil} => %ModFunInfo{ - params: [[{:data, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :__protocol__, 1} => %ModFunInfo{ - params: [[{:atom, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :impl_for, 1} => %ModFunInfo{ - params: [[{:data, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible.Map, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 36, column: 11], nil}]], - positions: [{36, 11}], - type: :def - }, - {Reversible.String, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 28, column: 9], nil}]], - positions: [{28, 9}], - type: :def - }, - {Reversible.Map, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 36, column: 11], nil}]], - positions: [{36, 11}], - type: :def - }, - {Reversible.String, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 28, column: 9], nil}]], - positions: [{28, 9}], - type: :def - }, - {Reversible.My.List, :__impl__, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 36, column: 11], nil}]], - positions: [{36, 11}], - type: :def - }, - {Reversible.My.List, :__impl__, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 36, column: 11], nil}]], - positions: [{36, 11}], - type: :def - }, - {Reversible, :behaviour_info, 1} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - }, - {Reversible, :behaviour_info, nil} => %ElixirSense.Core.State.ModFunInfo{ - params: [[{:atom, [line: 23, column: 13], nil}]], - positions: [{23, 13}], - type: :def - } - } = state.mods_funs_to_positions + assert Map.has_key?(state.mods_funs_to_positions, {Impls, :__info__, 1}) + assert Map.has_key?(state.mods_funs_to_positions, {Reversible, :__protocol__, 1}) + assert Map.has_key?(state.mods_funs_to_positions, {Reversible.My.List, :__impl__, 1}) end test "first_alias_positions" do @@ -4074,7 +3563,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do {InheritMod, :handle_call, nil} => %ModFunInfo{}, {InheritMod, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{1, 11}], + positions: [{1, 1}], type: :defmodule }, {InheritMod, :private_func, 0} => %ModFunInfo{ @@ -4398,7 +3887,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do }, {MyStruct, nil, nil} => %ModFunInfo{ params: [nil], - positions: [{1, 11}], + positions: [{1, 1}], type: :defmodule } } = state.mods_funs_to_positions @@ -4977,6 +4466,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :type, name: :no_arg_no_parens, positions: [{2, 3}], + end_positions: [nil], + generated: [false], specs: ["@type no_arg_no_parens :: integer"] }, {My, :no_arg_no_parens, nil} => %ElixirSense.Core.State.TypeInfo{ @@ -4984,6 +4475,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :type, name: :no_arg_no_parens, positions: [{2, 3}], + end_positions: [nil], + generated: [false], specs: ["@type no_arg_no_parens :: integer"] }, {My, :no_args, 0} => %ElixirSense.Core.State.TypeInfo{ @@ -4991,6 +4484,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :typep, name: :no_args, positions: [{3, 3}], + end_positions: [nil], + generated: [false], specs: ["@typep no_args :: integer"] }, {My, :no_args, nil} => %ElixirSense.Core.State.TypeInfo{ @@ -4998,6 +4493,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :typep, name: :no_args, positions: [{3, 3}], + end_positions: [nil], + generated: [false], specs: ["@typep no_args :: integer"] }, {My, :overloaded, 0} => %ElixirSense.Core.State.TypeInfo{ @@ -5005,12 +4502,16 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :type, name: :overloaded, positions: [{5, 3}], + end_positions: [nil], + generated: [false], specs: ["@type overloaded :: {}"] }, {My, :overloaded, 1} => %ElixirSense.Core.State.TypeInfo{ kind: :type, name: :overloaded, positions: [{6, 3}], + end_positions: [nil], + generated: [false], args: [["a"]], specs: ["@type overloaded(a) :: {a}"] }, @@ -5018,6 +4519,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :type, name: :overloaded, positions: [{6, 3}, {5, 3}], + end_positions: [nil, nil], + generated: [false, false], args: [["a"], []], specs: ["@type overloaded(a) :: {a}", "@type overloaded :: {}"] }, @@ -5025,6 +4528,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :opaque, name: :with_args, positions: [{4, 3}], + end_positions: [nil], + generated: [false], args: [["a", "b"]], specs: ["@opaque with_args(a, b) :: {a, b}"] }, @@ -5032,6 +4537,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :opaque, name: :with_args, positions: [{4, 3}], + end_positions: [nil], + generated: [false], args: [["a", "b"]], specs: ["@opaque with_args(a, b) :: {a, b}"] } @@ -5069,14 +4576,18 @@ defmodule ElixirSense.Core.MetadataBuilderTest do args: [[]], kind: :type, name: :t, - positions: [{1, 13}], + positions: [{1, 1}], + end_positions: [nil], + generated: [true], specs: ["@type t :: term"] }, {Proto, :t, 0} => %ElixirSense.Core.State.TypeInfo{ args: [[]], kind: :type, name: :t, - positions: [{1, 13}], + positions: [{1, 1}], + end_positions: [nil], + generated: [true], specs: ["@type t :: term"] } } @@ -5103,6 +4614,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do kind: :spec, name: :abc, positions: [{3, 3}], + end_positions: [nil], + generated: [false], specs: ["@spec abc :: reference"] }, {Proto, :abc, nil} => %ElixirSense.Core.State.SpecInfo{ @@ -5110,6 +4623,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do name: :abc, args: [[], []], positions: [{3, 3}, {2, 3}], + end_positions: [nil, nil], + generated: [false, false], specs: ["@spec abc :: reference", "@spec abc :: atom | integer"] }, {Proto, :my, 1} => %ElixirSense.Core.State.SpecInfo{ @@ -5117,6 +4632,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do name: :my, args: [["a :: integer"]], positions: [{4, 3}], + end_positions: [nil], + generated: [false], specs: ["@callback my(a :: integer) :: atom"] }, {Proto, :my, nil} => %ElixirSense.Core.State.SpecInfo{ @@ -5124,6 +4641,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do name: :my, args: [["a :: integer"]], positions: [{4, 3}], + end_positions: [nil], + generated: [false], specs: ["@callback my(a :: integer) :: atom"] }, {Proto, :other, 1} => %ElixirSense.Core.State.SpecInfo{ @@ -5131,6 +4650,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do name: :other, args: [["x"]], positions: [{5, 3}], + end_positions: [nil], + generated: [false], specs: ["@macrocallback other(x) :: Macro.t when x: integer"] }, {Proto, :other, nil} => %ElixirSense.Core.State.SpecInfo{ @@ -5138,6 +4659,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do name: :other, args: [["x"]], positions: [{5, 3}], + end_positions: [nil], + generated: [false], specs: ["@macrocallback other(x) :: Macro.t when x: integer"] } } @@ -5354,7 +4877,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do [{:baz, [line: 8, column: 21], nil}], [{:var, [line: 2, column: 3], nil}] ], - positions: [{8, 12}, {2, 3}], + positions: [{8, 3}, {2, 3}], target: nil, type: :defmacro, overridable: {true, ElixirSenseExample.OverridableFunctions} @@ -5364,7 +4887,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do [{:a, [line: 4, column: 12], nil}, {:b, [line: 4, column: 15], nil}], [{:x, [line: 2, column: 3], nil}, {:y, [line: 2, column: 3], nil}] ], - positions: [{4, 7}, {2, 3}], + positions: [{4, 3}, {2, 3}], target: nil, type: :def, overridable: {true, ElixirSenseExample.OverridableFunctions} @@ -5390,7 +4913,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do assert %{ {My, :foo, 0} => %ModFunInfo{ params: [[], []], - positions: [{4, 7}, {2, 3}], + positions: [{4, 3}, {2, 3}], target: nil, type: :def, overridable: {true, ElixirSenseExample.OverridableImplementation} @@ -5400,7 +4923,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do [{:baz, [line: 8, column: 16], nil}], [{:var, [line: 2, column: 3], nil}] ], - positions: [{8, 12}, {2, 3}], + positions: [{8, 3}, {2, 3}], target: nil, type: :defmacro, overridable: {true, ElixirSenseExample.OverridableImplementation} @@ -5429,7 +4952,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do [{:baz, [line: 8, column: 22], nil}], [{:var, [line: 2, column: 3], nil}] ], - positions: [{8, 13}, {2, 3}], + positions: [{8, 3}, {2, 3}], target: nil, type: :defmacrop, overridable: {true, ElixirSenseExample.OverridableFunctions} @@ -5442,7 +4965,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do [{:a, [line: 4, column: 13], nil}, {:b, [line: 4, column: 16], nil}], [{:x, [line: 2, column: 3], nil}, {:y, [line: 2, column: 3], nil}] ], - positions: [{4, 8}, {2, 3}], + positions: [{4, 3}, {2, 3}], target: nil, type: :defp, overridable: {true, ElixirSenseExample.OverridableFunctions} @@ -5497,7 +5020,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do defp string_to_state(string) do string - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) |> (fn {:ok, ast} -> ast end).() |> MetadataBuilder.build() end @@ -5578,7 +5101,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do {:ok, ast} = File.read!(file) - |> Code.string_to_quoted(columns: true) + |> Code.string_to_quoted(columns: true, token_metadata: true) acc = MetadataBuilder.build(ast) @@ -5604,7 +5127,7 @@ defmodule ElixirSense.Core.MetadataBuilderTest do for path <- elixir_sense_src_path ++ elixir_src_path do case File.read!(path) - |> Code.string_to_quoted(columns: true) do + |> Code.string_to_quoted(columns: true, token_metadata: true) do {:ok, ast} -> MetadataBuilder.build(ast) _ -> :ok end diff --git a/test/elixir_sense/core/metadata_test.exs b/test/elixir_sense/core/metadata_test.exs index 5d03b17c..007ef7e9 100644 --- a/test/elixir_sense/core/metadata_test.exs +++ b/test/elixir_sense/core/metadata_test.exs @@ -110,33 +110,217 @@ defmodule ElixirSense.Core.MetadataTest do def go, # 16 do: :ok # 17 end + IO.puts "" """ metadata = Parser.parse_string(code, true, true, 1) - env = Metadata.get_env(metadata, 1) - assert Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {1, 22}) + assert Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 2) - assert Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {2, 20}) + refute Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 8) - assert Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {8, 13}) + assert Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 5) - refute Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {5, 5}) + refute Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 11) - refute Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {11, 5}) + assert Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 14) - refute Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {14, 18}) + refute Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 16) - refute Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {16, 10}) + refute Metadata.at_module_body?(env) - env = Metadata.get_env(metadata, 17) - refute Metadata.at_module_body?(metadata, env) + env = Metadata.get_env(metadata, {17, 12}) + refute Metadata.at_module_body?(env) + + env = Metadata.get_env(metadata, {19, 1}) + refute Metadata.at_module_body?(env) + end + + test "env is correct in scopes" do + code = """ + IO.puts "" + + defmodule MyModule1 do + IO.puts "" + end + + IO.puts "" + + defmodule MyModule2 do + @type a :: atom() + + defp func(1) do + IO.puts "" + end # + + IO.puts "" + + def go1, do: :ok + + def go2(a) when is_integer(a), + do: :ok + + IO.puts "" + + def go2, do: :ok # + + IO.puts "" + + @spec go31(:a) :: :ok + def go31(:a), do: :ok + def go32(:b), do: :ok + def go33(:c) do + :ok + end + + @spec some(1) :: :ok + defp some(1) do + IO.puts "" + end + + @type x :: atom() + + @type y(a) :: + atom() + + IO.puts "" + end + + defprotocol Pr do + @spec x(t) :: :ok + def x(t) + end + + defimpl Pr, for: [String, List] do + def x(t), do: :ok + end + """ + + metadata = Parser.parse_string(code, true, true, 1) + + env = Metadata.get_env(metadata, {1, 1}) + assert env.scope == Elixir + + env = Metadata.get_env(metadata, {4, 3}) + assert env.scope == :MyModule1 + + env = Metadata.get_env(metadata, {7, 1}) + assert env.scope == Elixir + + env = Metadata.get_env(metadata, {9, 1}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {10, 2}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {10, 3}) + assert env.scope == {:typespec, :a, 0} + + env = Metadata.get_env(metadata, {10, 20}) + assert env.scope == {:typespec, :a, 0} + + env = Metadata.get_env(metadata, {12, 3}) + assert env.scope == {:func, 1} + + env = Metadata.get_env(metadata, {14, 6}) + assert env.scope == {:func, 1} + + env = Metadata.get_env(metadata, {14, 7}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {16, 3}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {18, 3}) + assert env.scope == {:go1, 0} + + env = Metadata.get_env(metadata, {20, 3}) + assert env.scope == {:go2, 1} + + env = Metadata.get_env(metadata, {21, 12}) + assert env.scope == {:go2, 1} + + env = Metadata.get_env(metadata, {23, 3}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {25, 3}) + assert env.scope == {:go2, 0} + + env = Metadata.get_env(metadata, {25, 3}) + assert env.scope == {:go2, 0} + + env = Metadata.get_env(metadata, {25, 19}) + assert env.scope == {:go2, 0} + + env = Metadata.get_env(metadata, {25, 20}) + assert env.scope == {:go2, 0} + + env = Metadata.get_env(metadata, {27, 3}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {29, 3}) + assert env.scope == {:typespec, :go31, 1} + + env = Metadata.get_env(metadata, {29, 23}) + assert env.scope == {:typespec, :go31, 1} + + env = Metadata.get_env(metadata, {30, 3}) + assert env.scope == {:go31, 1} + + env = Metadata.get_env(metadata, {30, 23}) + assert env.scope == {:go31, 1} + + env = Metadata.get_env(metadata, {31, 3}) + assert env.scope == {:go32, 1} + + env = Metadata.get_env(metadata, {32, 3}) + assert env.scope == {:go33, 1} + + env = Metadata.get_env(metadata, {36, 3}) + assert env.scope == {:typespec, :some, 1} + + env = Metadata.get_env(metadata, {37, 3}) + assert env.scope == {:some, 1} + + env = Metadata.get_env(metadata, {41, 3}) + assert env.scope == {:typespec, :x, 0} + + env = Metadata.get_env(metadata, {43, 3}) + assert env.scope == {:typespec, :y, 1} + + env = Metadata.get_env(metadata, {43, 3}) + assert env.scope == {:typespec, :y, 1} + + env = Metadata.get_env(metadata, {44, 11}) + assert env.scope == {:typespec, :y, 1} + + env = Metadata.get_env(metadata, {46, 3}) + assert env.scope == :MyModule2 + + env = Metadata.get_env(metadata, {49, 1}) + assert env.scope == :Pr + + env = Metadata.get_env(metadata, {50, 3}) + assert env.scope == {:typespec, :x, 1} + + env = Metadata.get_env(metadata, {51, 3}) + assert env.scope == {:x, 1} + + env = Metadata.get_env(metadata, {51, 11}) + assert env.scope == {:x, 1} + + env = Metadata.get_env(metadata, {54, 3}) + assert env.scope == :"String(__or__)List" + + env = Metadata.get_env(metadata, {55, 3}) + assert env.scope == {:x, 1} end test "get_position_to_insert_alias when aliases exist" do @@ -159,13 +343,13 @@ defmodule ElixirSense.Core.MetadataTest do line_number = 5 metadata = Parser.parse_string(code, true, true, line_number) - position = Metadata.get_position_to_insert_alias(metadata, line_number) + position = Metadata.get_position_to_insert_alias(metadata, {line_number, 6}) assert {2, 3} == position line_number = 11 metadata = Parser.parse_string(code, true, true, line_number) - position = Metadata.get_position_to_insert_alias(metadata, line_number) + position = Metadata.get_position_to_insert_alias(metadata, {line_number, 8}) assert {9, 5} == position end @@ -185,7 +369,7 @@ defmodule ElixirSense.Core.MetadataTest do line_number = 7 metadata = Parser.parse_string(code, true, true, line_number) - position = Metadata.get_position_to_insert_alias(metadata, line_number) + position = Metadata.get_position_to_insert_alias(metadata, {line_number, 6}) assert {5, 3} == position end @@ -201,7 +385,7 @@ defmodule ElixirSense.Core.MetadataTest do line_number = 3 metadata = Parser.parse_string(code, true, true, line_number) - position = Metadata.get_position_to_insert_alias(metadata, line_number) + position = Metadata.get_position_to_insert_alias(metadata, {line_number, 6}) assert {2, 3} == position end diff --git a/test/elixir_sense/core/parser_test.exs b/test/elixir_sense/core/parser_test.exs index 615133d6..601b5611 100644 --- a/test/elixir_sense/core/parser_test.exs +++ b/test/elixir_sense/core/parser_test.exs @@ -15,7 +15,7 @@ defmodule ElixirSense.Core.ParserTest do assert %Metadata{ error: nil, - mods_funs_to_positions: %{{MyModule, nil, nil} => %{positions: [{1, 11}]}}, + mods_funs_to_positions: %{{MyModule, nil, nil} => %{positions: [{1, 1}]}}, lines_to_env: %{ 1 => %Env{imports: [{Kernel, []}]}, 3 => %Env{imports: [{Kernel, []}, {List, []}]} @@ -352,7 +352,7 @@ defmodule ElixirSense.Core.ParserTest do assert %Metadata{ error: nil, - mods_funs_to_positions: %{{MyModule, nil, nil} => %{positions: [{1, 11}]}}, + mods_funs_to_positions: %{{MyModule, nil, nil} => %{positions: [{1, 1}]}}, lines_to_env: %{ 1 => %Env{imports: [{Kernel, []}]}, 3 => %Env{imports: [{Kernel, []}, {List, []}]} @@ -427,7 +427,7 @@ defmodule ElixirSense.Core.ParserTest do defmodule MyModule do def func() do %{ - data: "foo" + data: foo() } end end @@ -435,7 +435,7 @@ defmodule ElixirSense.Core.ParserTest do assert %ElixirSense.Core.Metadata{ calls: %{ - 3 => [%{func: :%{}}] + 4 => [%{func: :foo}] } } = parse_string(source, true, true, 4) end @@ -445,7 +445,7 @@ defmodule ElixirSense.Core.ParserTest do defmodule MyModule do def func() do %{ - data: "foo" + data: foo() end end @@ -453,7 +453,7 @@ defmodule ElixirSense.Core.ParserTest do assert %ElixirSense.Core.Metadata{ calls: %{ - 3 => [%{func: :%{}}] + 4 => [%{func: :foo}] } } = parse_string(source, true, true, 4) end diff --git a/test/elixir_sense/definition_test.exs b/test/elixir_sense/definition_test.exs index 2dd7673b..8bbe0a86 100644 --- a/test/elixir_sense/definition_test.exs +++ b/test/elixir_sense/definition_test.exs @@ -167,7 +167,7 @@ defmodule ElixirSense.Providers.DefinitionTest do end """ - assert %Location{type: :function, file: nil, line: 5, column: 7} = + assert %Location{type: :function, file: nil, line: 5, column: 3} = ElixirSense.definition(buffer, 2, 18) end @@ -179,7 +179,7 @@ defmodule ElixirSense.Providers.DefinitionTest do end """ - assert %Location{type: :function, file: nil, line: 3, column: 7} = + assert %Location{type: :function, file: nil, line: 3, column: 3} = ElixirSense.definition(buffer, 3, 9) end @@ -454,7 +454,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -474,7 +474,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -771,7 +771,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -791,7 +791,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -812,7 +812,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -832,7 +832,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 2, - column: 7 + column: 3 } end @@ -851,7 +851,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :macro, file: nil, line: 2, - column: 13 + column: 3 } end @@ -873,7 +873,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :function, file: nil, line: 3, - column: 7 + column: 3 } end @@ -910,7 +910,7 @@ defmodule ElixirSense.Providers.DefinitionTest do type: :module, file: nil, line: 2, - column: 13 + column: 3 } end diff --git a/test/elixir_sense/suggestions_test.exs b/test/elixir_sense/suggestions_test.exs index d3ff621e..8714cc54 100644 --- a/test/elixir_sense/suggestions_test.exs +++ b/test/elixir_sense/suggestions_test.exs @@ -1562,6 +1562,33 @@ defmodule ElixirSense.SuggestionsTest do ] 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 "lists doc snippets in module body" do buffer = """ defmodule MyModule do @@ -1575,15 +1602,10 @@ defmodule ElixirSense.SuggestionsTest do @m # ^ end - - schema do - @m - # ^ - end end """ - [cursor_1, cursor_2, cursor_3, cursor_4] = cursors(buffer) + [cursor_1, cursor_2, cursor_3] = cursors(buffer) list = suggestions_by_kind(buffer, cursor_1, :snippet) @@ -1603,7 +1625,6 @@ defmodule ElixirSense.SuggestionsTest do assert [%{label: ~S(@moduledoc """""")}, %{label: "@moduledoc false"}] = list assert suggestions_by_kind(buffer, cursor_3, :snippet) == [] - assert suggestions_by_kind(buffer, cursor_4, :snippet) == [] end test "fuzzy suggestions for doc snippets" do