Skip to content

Commit

Permalink
remove imports from state
Browse files Browse the repository at this point in the history
  • Loading branch information
lukaszsamson committed Apr 21, 2024
1 parent 4913025 commit aee6316
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 106 deletions.
213 changes: 121 additions & 92 deletions lib/elixir_sense/core/introspection.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1171,117 +1171,146 @@ defmodule ElixirSense.Core.Introspection do
def is_function_type(type), do: type in [:def, :defp, :defdelegate]
def is_macro_type(type), do: type in [:defmacro, :defmacrop, :defguard, :defguardp]

def expand_imports(list, mods_funs) do
{functions, macros} =
list
|> Enum.reduce([], fn {module, opts}, acc ->
opts =
if Keyword.keyword?(opts) do
opts
else
[]
end
def expand_import({functions, macros}, module, opts, mods_funs) do
opts =
if Keyword.keyword?(opts) do
opts
else
[]
end

all_exported =
if acc[module] != nil and Keyword.keyword?(opts[:except]) do
acc[module]
{all_exported_functions, all_exported_macros} =
if (functions[module] != nil or macros[module] != nil) and Keyword.keyword?(opts[:except]) do
{functions[module], macros[module]}
else
if Map.has_key?(mods_funs, {module, nil, nil}) do
funs_macros =
mods_funs
|> Enum.filter(fn {{m, _f, a}, info} ->
m == module and a != nil and is_pub(info.type)
end)
|> Enum.split_with(fn {_, info} -> is_macro_type(info.type) end)

functions =
funs_macros
|> elem(1)
|> Enum.flat_map(fn {{_m, f, _a}, info} ->
for {arity, default_args} <- State.ModFunInfo.get_arities(info),
args <- (arity - default_args)..arity do
{f, args}
end
end)

macros =
funs_macros
|> elem(0)
|> Enum.flat_map(fn {{_m, f, _a}, info} ->
for {arity, default_args} <- State.ModFunInfo.get_arities(info),
args <- (arity - default_args)..arity do
{f, args}
end
end)

{functions, macros}
else
{macros, functions} =
get_exports(module)
|> Enum.split_with(fn {_, {_, kind}} -> kind == :macro end)

{functions |> Enum.map(fn {f, {a, _}} -> {f, a} end),
macros |> Enum.map(fn {f, {a, _}} -> {f, a} end)}
end
end

imported_functions =
all_exported_functions
|> Enum.reject(fn
{:__info__, 1} ->
true

{:module_info, arity} when arity in [0, 1] ->
true

{:behaviour_info, 1} ->
if Version.match?(System.version(), ">= 1.15.0-dev") do
true
else
if Map.has_key?(mods_funs, {module, nil, nil}) do
mods_funs
|> Enum.filter(fn {{m, _f, a}, info} ->
m == module and a != nil and is_pub(info.type)
end)
|> Enum.flat_map(fn {{_m, f, _a}, info} ->
kind = if(is_macro_type(info.type), do: :macro, else: :function)

for {arity, default_args} <- State.ModFunInfo.get_arities(info),
args <- (arity - default_args)..arity do
{f, {args, kind}}
end
end)
else
get_exports(module)
end
# elixir < 1.15 imports behaviour_info from erlang behaviours
# https://github.com/elixir-lang/elixir/commit/4b26edd8c164b46823e1dc1ec34b639cc3563246
elixir_module?(module)
end

imported =
all_exported
|> Enum.reject(fn
{:__info__, {1, :function}} ->
true

{:module_info, {arity, :function}} when arity in [0, 1] ->
true

{:behaviour_info, {1, :function}} ->
if Version.match?(System.version(), ">= 1.15.0-dev") do
true
else
# elixir < 1.15 imports behaviour_info from erlang behaviours
# https://github.com/elixir-lang/elixir/commit/4b26edd8c164b46823e1dc1ec34b639cc3563246
elixir_module?(module)
end
{:orelse, 2} ->
module == :erlang

{:orelse, {2, :function}} ->
module == :erlang
{:andalso, 2} ->
module == :erlang

{:andalso, {2, :function}} ->
module == :erlang
{name, arity} ->
reject_import(name, arity, :function, opts)
end)

{name, {arity, kind}} ->
name_string = name |> Atom.to_string()
imported_macros =
all_exported_macros
|> Enum.reject(fn
{name, arity} ->
reject_import(name, arity, :macro, opts)
end)

rejected_after_only? =
cond do
opts[:only] == :sigils and not String.starts_with?(name_string, "sigil_") ->
true
functions =
case imported_functions do
[] ->
Keyword.delete(functions, module)

opts[:only] == :macros and kind != :macro ->
true
_ ->
Keyword.put(functions, module, imported_functions)
end

opts[:only] == :functions and kind != :function ->
true
macros =
case imported_macros do
[] ->
Keyword.delete(macros, module)

Keyword.keyword?(opts[:only]) ->
{name, arity} not in opts[:only]
_ ->
Keyword.put(macros, module, imported_macros)
end

String.starts_with?(name_string, "_") ->
true
{functions, macros}
end

true ->
false
end
defp reject_import(name, arity, kind, opts) do
name_string = name |> Atom.to_string()

if rejected_after_only? do
true
else
if Keyword.keyword?(opts[:except]) do
{name, arity} in opts[:except]
else
false
end
end
end)
rejected_after_only? =
cond do
opts[:only] == :sigils and not String.starts_with?(name_string, "sigil_") ->
true

Keyword.put(acc, module, imported)
end)
|> Enum.reduce({[], []}, fn {module, imported}, {functions_acc, macros_acc} ->
{functions, macros} =
imported
|> Enum.split_with(fn {_name, {_arity, kind}} -> kind == :function end)
opts[:only] == :macros and kind != :macro ->
true

{append_expanded_module_imports(functions, module, functions_acc),
append_expanded_module_imports(macros, module, macros_acc)}
end)
opts[:only] == :functions and kind != :function ->
true

{Enum.reverse(functions), Enum.reverse(macros)}
end
Keyword.keyword?(opts[:only]) ->
{name, arity} not in opts[:only]

defp append_expanded_module_imports([], _module, acc), do: acc
String.starts_with?(name_string, "_") ->
true

defp append_expanded_module_imports(list, module, acc) do
list = list |> Enum.map(fn {name, {arity, _kind}} -> {name, arity} end)
[{module, list} | acc]
true ->
false
end

if rejected_after_only? do
true
else
if Keyword.keyword?(opts[:except]) do
{name, arity} in opts[:except]
else
false
end
end
end

def combine_imports({functions, macros}) do
Expand Down
20 changes: 7 additions & 13 deletions lib/elixir_sense/core/state.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ defmodule ElixirSense.Core.State do
@type t :: %ElixirSense.Core.State{
namespace: [[atom]],
scopes: [[scope]],
imports: list(list(module)),
requires: list(list(module)),
aliases: list(list(alias_t)),
attributes: list(list(ElixirSense.Core.State.AttributeInfo.t())),
Expand Down Expand Up @@ -64,7 +63,6 @@ defmodule ElixirSense.Core.State do
binding_context: list
}

@auto_imported [{Kernel, []}]
@auto_imported_functions :elixir_env.new().functions
@auto_imported_macros :elixir_env.new().macros
@auto_required [Application, Kernel] ++
Expand All @@ -76,7 +74,6 @@ defmodule ElixirSense.Core.State do

defstruct namespace: [[:"Elixir"]],
scopes: [[:"Elixir"]],
imports: [@auto_imported],
functions: [@auto_imported_functions],
macros: [@auto_imported_macros],
requires: [@auto_required],
Expand Down Expand Up @@ -1159,8 +1156,7 @@ defmodule ElixirSense.Core.State do
def new_import_scope(%__MODULE__{} = state) do
%__MODULE__{
state
| imports: [[] | state.imports],
functions: [hd(state.functions) | state.functions],
| functions: [hd(state.functions) | state.functions],
macros: [hd(state.macros) | state.macros]
}
end
Expand All @@ -1172,8 +1168,7 @@ defmodule ElixirSense.Core.State do
def remove_import_scope(%__MODULE__{} = state) do
%__MODULE__{
state
| imports: tl(state.imports),
functions: tl(state.functions),
| functions: tl(state.functions),
macros: tl(state.macros)
}
end
Expand All @@ -1184,19 +1179,18 @@ defmodule ElixirSense.Core.State do

def add_import(%__MODULE__{} = state, module, opts) when is_atom(module) or is_list(module) do
module = expand_alias(state, module)
[imports_from_scope | inherited_imports] = state.imports
combined_imports = [[imports_from_scope ++ [{module, opts}]] | inherited_imports]

{functions, macros} =
Introspection.expand_imports(
combined_imports |> :lists.reverse() |> List.flatten(),
Introspection.expand_import(
{hd(state.functions), hd(state.macros)},
module,
opts,
state.mods_funs_to_positions
)

%__MODULE__{
state
| imports: combined_imports,
functions: [functions | tl(state.functions)],
| functions: [functions | tl(state.functions)],
macros: [macros | tl(state.macros)]
}
end
Expand Down
3 changes: 2 additions & 1 deletion test/elixir_sense/core/metadata_builder_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ defmodule ElixirSense.Core.MetadataBuilderTest do

assert state.namespace == []
assert state.scopes == []
assert state.imports == []
assert state.functions == []
assert state.macros == []
assert state.requires == []
assert state.aliases == []
assert state.attributes == []
Expand Down

0 comments on commit aee6316

Please sign in to comment.