diff --git a/apps/common/lib/lexical/ast/analysis/scope.ex b/apps/common/lib/lexical/ast/analysis/scope.ex index baf62c438..e80a9db19 100644 --- a/apps/common/lib/lexical/ast/analysis/scope.ex +++ b/apps/common/lib/lexical/ast/analysis/scope.ex @@ -42,13 +42,13 @@ defmodule Lexical.Ast.Analysis.Scope do # sorting by line ensures that aliases on later lines # override aliases on earlier lines |> Enum.sort_by(& &1.range.start.line) - |> Enum.take_while(fn %Alias{range: range} -> + |> Enum.take_while(fn %Alias{range: alias_range} -> case position do %Position{} = pos -> - pos.line >= range.end.line + pos.line >= alias_range.start.line line when is_integer(line) -> - line >= range.end.line + line >= alias_range.start.line :end -> true diff --git a/apps/remote_control/lib/lexical/remote_control/analyzer.ex b/apps/remote_control/lib/lexical/remote_control/analyzer.ex index 94ebfcaeb..b6bc660ec 100644 --- a/apps/remote_control/lib/lexical/remote_control/analyzer.ex +++ b/apps/remote_control/lib/lexical/remote_control/analyzer.ex @@ -161,11 +161,27 @@ defmodule Lexical.RemoteControl.Analyzer do end end - defp resolve_alias([first | rest], aliases_mapping) when is_atom(first) do + defp resolve_alias([first | _] = segments, aliases_mapping) when is_atom(first) do + with :error <- fetch_leading_alias(segments, aliases_mapping) do + fetch_trailing_alias(segments, aliases_mapping) + end + end + + defp resolve_alias(_, _), do: :error + + defp fetch_leading_alias([first | rest], aliases_mapping) do with {:ok, resolved} <- Map.fetch(aliases_mapping, first) do {:ok, [resolved | rest]} end end - defp resolve_alias(_, _), do: :error + defp fetch_trailing_alias(segments, aliases_mapping) do + # Trailing aliases happen when you use the curly syntax to define multiple aliases + # in one go, like Foo.{First, Second.Third, Fourth} + # Our alias mapping will have Third mapped to Foo.Second.Third, so we need to look + # for Third, wheras the leading alias will look for Second in the mappings. + with {:ok, resolved} <- Map.fetch(aliases_mapping, List.last(segments)) do + {:ok, List.wrap(resolved)} + end + end end diff --git a/apps/remote_control/lib/lexical/remote_control/search/indexer/extractors/module.ex b/apps/remote_control/lib/lexical/remote_control/search/indexer/extractors/module.ex index ad5d503d2..4408370fc 100644 --- a/apps/remote_control/lib/lexical/remote_control/search/indexer/extractors/module.ex +++ b/apps/remote_control/lib/lexical/remote_control/search/indexer/extractors/module.ex @@ -248,8 +248,7 @@ defmodule Lexical.RemoteControl.Search.Indexer.Extractors.Module do defp resolve_for_block(_, _), do: :error defp resolve_alias(%Reducer{} = reducer, unresolved_alias) do - {line, column} = reducer.position - position = Position.new(reducer.analysis.document, line, column) + position = Reducer.position(reducer) RemoteControl.Analyzer.expand_alias(unresolved_alias, reducer.analysis, position) end diff --git a/apps/remote_control/test/lexical/remote_control/search/indexer/extractors/module_test.exs b/apps/remote_control/test/lexical/remote_control/search/indexer/extractors/module_test.exs index 2fec240f4..b1ea06954 100644 --- a/apps/remote_control/test/lexical/remote_control/search/indexer/extractors/module_test.exs +++ b/apps/remote_control/test/lexical/remote_control/search/indexer/extractors/module_test.exs @@ -139,6 +139,23 @@ defmodule Lexical.RemoteControl.Search.Indexer.Extractors.ModuleTest do assert decorate(doc, module_ref.range) =~ " «Thing.Util» = arg" end + test "can detect a module reference in a nested alias" do + {:ok, [_top_level, _foo, _first, second, fourth], doc} = ~q[ + defmodule TopLevel do + alias Foo.{ + First, + Second, + Third.Fourth + } + end] |> index() + + assert second.subject == Foo.Second + assert decorate(doc, second.range) == " «Second»," + + assert fourth.subject == Foo.Third.Fourth + assert decorate(doc, fourth.range) == " «Third.Fourth»" + end + test "can detect a module reference on the right side of a pattern match" do {:ok, [_module, module_ref], doc} = ~q[