Go-to-definition & hover lost when using dynamic module calls via Application.get_env/3

On vscode, I’ve noticed that ElixirLS navigation and hover only work when I call modules directly, but break as soon as I switch to a dynamic lookup via Application.get_env/3.

Direct calls (works)

defmodule TaskProcessor.Events do
  alias TaskProcessor.Segments
  alias TaskProcessor.Events.Queue.Oban,         as: EventsQueue
  alias TaskProcessor.Events.Persistance.Ecto,   as: Repo

  def enqueue_create_event(event_id) when is_integer(event_id) and event_id > 0 do
    EventsQueue.enqueue_create_event(event_id)
  end

  def enqueue_create_event(bad_event_id), do: {:error, {:invalid_event_id, bad_event_id}}

  def process_event(%{event_id: id, isCreated: true}) do
    case Repo.fetch_with_segments(id) do
      :not_found -> {:error, :not_found}
      {:ok, %{date: date, segments: segs}} ->
        Segments.schedule_segments(segs, id, date)
    end
  end
end
  • Cmd+Click on EventsQueue.enqueue_create_event/1 or Repo.fetch_with_segments/1 jumps to the implementation.
  • Hover shows the function signature.

Dynamic calls (Not working, or am i not doing right ?)

defmodule TaskProcessor.Events.Impl do
  @behaviour TaskProcessor.Events.Behavior

  alias TaskProcessor.Segments

  @impl true
  @spec enqueue_create_event(any()) :: {:ok, Oban.Job.t()} | {:error, any()}
  def enqueue_create_event(event_id) when is_integer(event_id) and event_id > 0 do
    queue_impl().enqueue_create_event(event_id)
  end

  def enqueue_create_event(bad_event_id), do: {:error, {:invalid_event_id, bad_event_id}}

  @impl true
  def process_event(%{event_id: id, isCreated: true}) do
    case repo_impl().fetch_with_segments(id) do
      :not_found -> {:error, :not_found}
      {:ok, %{date: date, segments: segs}} ->
        Segments.schedule_segments(segs, id, date)
    end
  end

  defp queue_impl do
    Application.get_env(:task_processor, :events_queue, TaskProcessor.Events.Queue.Oban)
  end

  defp repo_impl do
    Application.get_env(:task_processor, :events_repo,  TaskProcessor.Events.Persistance.Ecto)
  end
end

  • Cmd+Click on queue_impl().enqueue_create_event/1 or repo_impl().fetch_with_segments/1 does nothing.
  • Hovering shows nothing.

Environment

  • ElixirLS version: 0.28.0
  • Elixir version: 1.18.4
  • Erlang/OTP version: 28
  • Macbook M1

Is there any way to configure ElixirLS (or leverage Dialyzer/Elixir compiler hints) so that go-to-definition and hover info work for modules resolved at runtime via Application.get_env/3? I need this functionality so that I can dynamically inject mocks during tests using the Mox library.

Thank you!

If you can, use Application.compile_env/2 and assign to a module attibute. This works.

If you really need to change the implementationat runtime then it cannot work as far as I know. Better use a callback in thas case.

1 Like

Hi, thanks for ur reply.

I like the .compile_env suggestion. I followed it, and refactored the code like this:

defmodule TaskProcessor.Events.Impl do
  @behaviour TaskProcessor.Events.Behavior

  alias TaskProcessor.Segments
  alias TaskProcessor.Events.Queue.Oban, as: Queue

  @repo Application.compile_env(
          :task_processor,
          :events_repo,
          TaskProcessor.Events.Persistance.Ecto
        )

  @impl true
  def enqueue_create_event(event_id) when is_integer(event_id) and event_id > 0 do
    Queue.enqueue_create_event(event_id)
  end

  def enqueue_create_event(bad_event_id), do: {:error, {:invalid_event_id, bad_event_id}}

  @impl true
  def process_event(%{event_id: id, isCreated: true}) do
    case @repo.fetch_with_segments(id) do
      :not_found -> {:error, :not_found}
      {:ok, %{date: date, segments: segs}} -> Segments.schedule_segments(segs, id, date)
    end
  end
end

I can’t use go to definition or hover here: @repo.fetch_with_segments

Update:

Installed elixir tools, that contains next-ls. Now I can use go to definition on @repo.fetch_with_segments.

I’m wondering, if next-ls can do it, elixir-ls which is more mature supposed to do it too.

1 Like

I don’t have my dev machine right now but I’m using ElixirLS and this generally works. Maybe try rm -rf .elixir_ls sometimes it helps.

1 Like

Thanks for your suggestions.

Tried that, also removing deps _build multiple times.

If anything else pops into your mind, let me know.