Skip to content

Commit cadd374

Browse files
committed
Load all modules after first build
1 parent ea4b938 commit cadd374

File tree

5 files changed

+111
-6
lines changed

5 files changed

+111
-6
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule ElixirLS.LanguageServer.Build do
22
alias ElixirLS.LanguageServer.{Server, JsonRpc, SourceFile}
33

4-
def build(parent, root_path, fetch_deps?) do
4+
def build(parent, root_path, opts) do
55
if Path.absname(File.cwd!()) != Path.absname(root_path) do
66
IO.puts("Skipping build because cwd changed from #{root_path} to #{File.cwd!()}")
77
{nil, nil}
@@ -19,10 +19,16 @@ defmodule ElixirLS.LanguageServer.Build do
1919
case reload_project() do
2020
{:ok, mixfile_diagnostics} ->
2121
# FIXME: Private API
22-
if fetch_deps? and Mix.Dep.load_on_environment([]) != prev_deps,
23-
do: fetch_deps()
22+
if Keyword.get(opts, :fetch_deps?) and
23+
Mix.Dep.load_on_environment([]) != prev_deps,
24+
do: fetch_deps()
2425

2526
{status, diagnostics} = compile()
27+
28+
if status in [:ok, :noop] and Keyword.get(opts, :load_all_modules?) do
29+
load_all_modules()
30+
end
31+
2632
Server.build_finished(parent, {status, mixfile_diagnostics ++ diagnostics})
2733

2834
{:error, mixfile_diagnostics} ->
@@ -160,6 +166,29 @@ defmodule ElixirLS.LanguageServer.Build do
160166
end
161167
end
162168

169+
defp load_all_modules do
170+
apps =
171+
cond do
172+
Mix.Project.umbrella?() ->
173+
Mix.Project.apps_paths() |> Map.keys()
174+
175+
app = Keyword.get(Mix.Project.config(), :app) ->
176+
[app]
177+
178+
true ->
179+
[]
180+
end
181+
182+
Enum.each(apps, fn app ->
183+
true = Code.prepend_path(Path.join(Mix.Project.build_path(), "lib/#{app}/ebin"))
184+
185+
case Application.load(app) do
186+
:ok -> :ok
187+
{:error, {:already_loaded, _}} -> :ok
188+
end
189+
end)
190+
end
191+
163192
defp compile do
164193
case Mix.Task.run("compile", ["--return-errors", "--ignore-module-conflict"]) do
165194
{status, diagnostics} when status in [:ok, :error, :noop] and is_list(diagnostics) ->

apps/language_server/lib/language_server/protocol.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@ defmodule ElixirLS.LanguageServer.Protocol do
191191
end
192192
end
193193

194+
defmacro shutdown_req(id) do
195+
quote do
196+
request(unquote(id), "shutdown", %{})
197+
end
198+
end
199+
200+
defmacro exit_req(id) do
201+
quote do
202+
request(unquote(id), "exit", %{})
203+
end
204+
end
205+
194206
# Other utilities
195207

196208
defmacro range(start_line, start_character, end_line, end_character) do

apps/language_server/lib/language_server/server.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ defmodule ElixirLS.LanguageServer.Server do
4444
build_diagnostics: [],
4545
dialyzer_diagnostics: [],
4646
needs_build?: false,
47+
load_all_modules?: false,
4748
build_running?: false,
4849
analysis_ready?: false,
4950
received_shutdown?: false,
@@ -567,14 +568,20 @@ defmodule ElixirLS.LanguageServer.Server do
567568
defp trigger_build(state) do
568569
if build_enabled?(state) and not state.build_running? do
569570
fetch_deps? = Map.get(state.settings || %{}, "fetchDeps", true)
570-
{_pid, build_ref} = Build.build(self(), state.project_dir, fetch_deps?)
571+
572+
{_pid, build_ref} =
573+
Build.build(self(), state.project_dir,
574+
fetch_deps?: fetch_deps?,
575+
load_all_modules?: state.load_all_modules?
576+
)
571577

572578
%__MODULE__{
573579
state
574580
| build_ref: build_ref,
575581
needs_build?: false,
576582
build_running?: true,
577-
analysis_ready?: false
583+
analysis_ready?: false,
584+
load_all_modules?: false
578585
}
579586
else
580587
%__MODULE__{state | needs_build?: true, analysis_ready?: false}
@@ -771,7 +778,7 @@ defmodule ElixirLS.LanguageServer.Server do
771778

772779
is_nil(prev_project_dir) ->
773780
File.cd!(project_dir)
774-
put_in(state.project_dir, project_dir)
781+
Map.merge(state, %{project_dir: project_dir, load_all_modules?: true})
775782

776783
prev_project_dir != project_dir ->
777784
JsonRpc.show_message(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
defmodule App2.Foo do
2+
def hello do
3+
:foo
4+
end
5+
end

apps/language_server/test/server_test.exs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,4 +336,56 @@ defmodule ElixirLS.LanguageServer.ServerTest do
336336
]) = resp
337337
end)
338338
end
339+
340+
test "loading of umbrella app dependencies", %{server: server} do
341+
in_fixture(__DIR__, "umbrella", fn ->
342+
# We test this by opening the umbrella project twice.
343+
# First to compile the applications and build the cache.
344+
# Second time to see if loads modules
345+
initialize(server)
346+
347+
# Upon first vist, server complies and loads all umbrella applications and modules
348+
Process.sleep(1_500)
349+
350+
Server.receive_packet(server, shutdown_req(2))
351+
assert response(2, nil) = assert_receive(%{"id" => 2}, 5000)
352+
Server.receive_packet(server, exit_req(3))
353+
354+
Process.sleep(500)
355+
# unload App2.Foo
356+
purge([App2.Foo])
357+
358+
# Upon re-visiting, server does not attempt to compile umbrella applications
359+
initialize(server)
360+
Process.sleep(1_500)
361+
362+
file_path = "apps/app1/lib/bar.ex"
363+
uri = SourceFile.path_to_uri(file_path)
364+
365+
code = """
366+
defmodule Bar do
367+
def fnuc, do: App2.Fo
368+
#____________________^
369+
end
370+
"""
371+
372+
Server.receive_packet(server, did_open(uri, "elixir", 1, code))
373+
Server.receive_packet(server, completion_req(2, uri, 1, 23))
374+
375+
resp = assert_receive(%{"id" => 2}, 5000)
376+
377+
assert response(2, %{
378+
"isIncomplete" => false,
379+
"items" => [
380+
%{
381+
"detail" => "module",
382+
"documentation" => _,
383+
"kind" => 9,
384+
"label" => "Foo"
385+
}
386+
| _
387+
]
388+
}) = resp
389+
end)
390+
end
339391
end

0 commit comments

Comments
 (0)