Skip to content

Commit

Permalink
Phoenix scopes fixes (#285)
Browse files Browse the repository at this point in the history
* fix crash when hover over non module atom

* fix crash when combining not know atoms

fix combination on nested scopes
previous implementation would concat nils and Elixir on scopes that do not define an alias

* support `alias: false` on phoenix scope

* cleanup
  • Loading branch information
lukaszsamson committed Jan 20, 2024
1 parent 6597139 commit c40c0da
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 33 deletions.
65 changes: 35 additions & 30 deletions lib/elixir_sense/plugins/phoenix/scope.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ defmodule ElixirSense.Plugins.Phoenix.Scope do
alias ElixirSense.Core.Source
alias ElixirSense.Core.Binding

import Module, only: [safe_concat: 2, safe_concat: 1]

def within_scope(buffer, binding_env \\ %Binding{}) do
{:ok, ast} = Code.Fragment.container_cursor_to_quoted(buffer)

Expand Down Expand Up @@ -33,59 +31,66 @@ defmodule ElixirSense.Plugins.Phoenix.Scope do
end
end

defp get_scope_alias(scopes_ast, binding_env, module \\ nil)

# is this possible? scope do ... end
defp get_scope_alias([{:scope, _, []}], _binding_env, module), do: module

# scope "/" do ... end
defp get_scope_alias([{:scope, _, [scope_params]}], _binding_env, module)
when not is_list(scope_params),
do: module

# scope path: "/", alias: ExampleWeb do ... end
defp get_scope_alias([{:scope, _, [scope_params]}], binding_env, module) do
defp get_scope_alias_from_ast_node({:scope, _, [scope_params]}, binding_env, module)
when is_list(scope_params) do
scope_alias = Keyword.get(scope_params, :alias)
scope_alias = get_mod(scope_alias, binding_env)
safe_concat(module, scope_alias)
concat_module(scope_alias, binding_env, module)
end

# scope "/", alias: ExampleWeb do ... end
defp get_scope_alias(
[{:scope, _, [_scope_path, scope_params]}],
defp get_scope_alias_from_ast_node(
{:scope, _, [_scope_path, scope_params]},
binding_env,
module
)
when is_list(scope_params) do
scope_alias = Keyword.get(scope_params, :alias)
scope_alias = get_mod(scope_alias, binding_env)
safe_concat(module, scope_alias)
concat_module(scope_alias, binding_env, module)
end

# scope "/", ExampleWeb do ... end
defp get_scope_alias(
[{:scope, _, [_scope_path, scope_alias]}],
defp get_scope_alias_from_ast_node(
{:scope, _, [_scope_path, scope_alias]},
binding_env,
module
) do
scope_alias = get_mod(scope_alias, binding_env)
safe_concat(module, scope_alias)
concat_module(scope_alias, binding_env, module)
end

# scope "/", ExampleWeb, host: "api." do ... end
defp get_scope_alias(
[{:scope, _, [_scope_path, scope_alias, _scope_params]}],
defp get_scope_alias_from_ast_node(
{:scope, _, [_scope_path, scope_alias, scope_params]},
binding_env,
module
) do
)
when is_list(scope_params) do
concat_module(scope_alias, binding_env, module)
end

defp get_scope_alias_from_ast_node(
_ast,
_binding_env,
module
),
do: module

# no alias - propagate parent
defp concat_module(nil, _binding_env, module), do: module
# alias: false resets all nested aliases
defp concat_module(false, _binding_env, _module), do: nil

defp concat_module(scope_alias, binding_env, module) do
scope_alias = get_mod(scope_alias, binding_env)
safe_concat(module, scope_alias)
Module.concat([module, scope_alias])
end

defp get_scope_alias(scopes_ast, binding_env, module \\ nil)
# recurse
defp get_scope_alias([], _binding_env, module), do: module

defp get_scope_alias([head | tail], binding_env, module) do
scope_alias = get_scope_alias([head], binding_env, module)
safe_concat([module, scope_alias, get_scope_alias(tail, binding_env)])
scope_alias = get_scope_alias_from_ast_node(head, binding_env, module)
get_scope_alias(tail, binding_env, scope_alias)
end

defp get_mod({:__aliases__, _, [scope_alias]}, binding_env) do
Expand Down
10 changes: 7 additions & 3 deletions lib/elixir_sense/providers/definition.ex
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,16 @@ defmodule ElixirSense.Providers.Definition do
end

defp get_module(module, %{end: {line, col}}, env, metadata) do
with {true, module} <- get_phoenix_module(module, env) do
with {true, module} <- get_phoenix_module(module, env),
true <- Introspection.elixir_module?(module) do
text_before = Source.text_before(metadata.source, line, col)

case Scope.within_scope(text_before) do
{false, _} -> module
{true, scope_alias} -> Module.safe_concat(scope_alias, module)
{false, _} ->
module

{true, scope_alias} ->
Module.concat(scope_alias, module)
end
end
end
Expand Down

0 comments on commit c40c0da

Please sign in to comment.