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.
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
AFAIK Elixir ls wont.
I would suggest a high level api for Repo and queue.
And separate impl. As it looks coupled
High level api
defmodule TaskProcessor.Events do
alias TaskProcessor.EventsQueue
alias TaskProcessor.EventsRepo
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 EventsRepo.fetch_with_segments(id) do
:not_found -> {:error, :not_found}
{:ok, %{date: date, segments: segs}} ->
Segments.schedule_segments(segs, id, date)
end
end
end
defmodule TaskProcessor.EventsQueue do
defp impl do
Application.get_env(:task_processor, :events_queue, TaskProcessor.Events.Queue.Oban)
end
def create_event() do
impl()...function_call()
end
end
defmodule TaskProcessor.EventsRepo do
defp repo_impl do
Application.get_env(:task_processor, :events_repo, TaskProcessor.Events.Persistance.Ecto)
end
def with_segs() do
repo_impl()...function_call()
end
end
defmodule TaskProcessor.Events.Persistance.Ecto do
@behaviour Repo....
def fetch_with_segments(...) do
...
end
end
its an example and wont solve the issue of elixir ls…
x