Skip to content

Commit d9cc23a

Browse files
committed
Purge project apps on recompile
we rely on application controller being able to return app modules elixir loads apps only on initial compilation and and without purging we end up with outdated module list workaround elixir-lang/elixir#13001
1 parent 4c616ad commit d9cc23a

File tree

3 files changed

+54
-28
lines changed

3 files changed

+54
-28
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,14 @@ defmodule ElixirLS.LanguageServer.Build do
297297
end
298298

299299
defp purge_app(app) do
300+
Logger.debug("Stopping #{app}")
301+
302+
case Application.stop(app) do
303+
:ok -> :ok
304+
{:error, {:not_started, _}} -> :ok
305+
{:error, error} -> Logger.warning("Application.stop failed for #{app}: #{inspect(error)}")
306+
end
307+
300308
modules =
301309
case :application.get_key(app, :modules) do
302310
{:ok, modules} -> modules
@@ -310,31 +318,11 @@ defmodule ElixirLS.LanguageServer.Build do
310318

311319
Logger.debug("Unloading #{app}")
312320

313-
case Application.stop(app) do
314-
:ok -> :ok
315-
{:error, :not_started} -> :ok
316-
{:error, error} -> Logger.warning("Application.stop failed for #{app}: #{inspect(error)}")
317-
end
318-
319-
lib_dir = :code.lib_dir(app)
320-
321321
case Application.unload(app) do
322322
:ok -> :ok
323+
{:error, {:not_loaded, _}} -> :ok
323324
{:error, error} -> Logger.warning("Application.unload failed for #{app}: #{inspect(error)}")
324325
end
325-
326-
if is_list(lib_dir) do
327-
case :code.del_path(:filename.join(lib_dir, ~c"ebin")) do
328-
true ->
329-
:ok
330-
331-
false ->
332-
:ok
333-
334-
{:error, reason} ->
335-
Logger.warning("Unable to clean code path for #{app}: #{inspect(reason)}")
336-
end
337-
end
338326
end
339327

340328
defp get_deps_by_app(deps), do: get_deps_by_app(deps, %{})
@@ -396,16 +384,46 @@ defmodule ElixirLS.LanguageServer.Build do
396384
cached_deps_by_app = get_deps_by_app(cached_deps)
397385
removed_apps = Map.keys(cached_deps_by_app) -- Map.keys(current_deps_by_app)
398386

399-
removed_deps = cached_deps_by_app |> Map.take(removed_apps)
387+
removed_deps =
388+
cached_deps_by_app
389+
|> Map.take(removed_apps)
390+
|> Enum.flat_map(&elem(&1, 1))
391+
|> Enum.uniq()
400392

401-
for {_app, deps} <- removed_deps,
402-
dep <- deps do
393+
# purge removed dependencies
394+
for dep <- removed_deps do
403395
purge_dep(dep)
404396
end
405397

398+
# purge current dependencies in invalid state
406399
for dep <- current_deps do
407400
maybe_purge_dep(dep)
408401
end
402+
403+
mix_project_apps =
404+
if Mix.Project.umbrella?() do
405+
Mix.Project.apps_paths() |> Enum.map(&elem(&1, 0))
406+
else
407+
# in umbrella Mix.Project.apps_paths() returns nil
408+
# get app from config instead
409+
[Mix.Project.config()[:app]]
410+
end
411+
412+
mix_project_apps_deps =
413+
current_deps_by_app
414+
|> Map.take(mix_project_apps)
415+
|> Enum.flat_map(&elem(&1, 1))
416+
|> Enum.uniq()
417+
418+
# purge mix project apps
419+
# elixir compiler loads apps only on initial compilation
420+
# on subsequent ones it does not update application controller state
421+
# if we don't purge the apps we end up with invalid state
422+
# e.g. :application.get_key(app, :modules) returns outdated module list
423+
# see https://github.com/elixir-lang/elixir/issues/13001
424+
for dep <- mix_project_apps_deps do
425+
purge_dep(dep)
426+
end
409427
end
410428

411429
defp fetch_deps(current_deps) do
@@ -462,7 +480,6 @@ defmodule ElixirLS.LanguageServer.Build do
462480
options =
463481
if Version.match?(System.version(), ">= 1.14.0") do
464482
Keyword.merge(options,
465-
# we are running the server with consolidated protocols
466483
# this disables warnings `X has already been consolidated`
467484
# when running `compile` task
468485
ignore_already_consolidated: true

apps/language_server/lib/language_server/doc_links.ex

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,29 @@ defmodule ElixirLS.LanguageServer.DocLinks do
3232
case get_app(module) do
3333
{app, vsn} ->
3434
"#{@hex_base_url}/#{app}/#{vsn}/#{inspect(module)}.html"
35-
nil -> nil
35+
36+
nil ->
37+
nil
3638
end
3739
end
3840

3941
def hex_docs_function_link(module, function, arity) do
4042
case get_app(module) do
4143
{app, vsn} ->
4244
"#{@hex_base_url}/#{app}/#{vsn}/#{inspect(module)}.html##{function}/#{arity}"
43-
nil -> nil
45+
46+
nil ->
47+
nil
4448
end
4549
end
4650

4751
def hex_docs_type_link(module, type, arity) do
4852
case get_app(module) do
4953
{app, vsn} ->
5054
"#{@hex_base_url}/#{app}/#{vsn}/#{inspect(module)}.html#t:#{type}/#{arity}"
51-
nil -> nil
55+
56+
nil ->
57+
nil
5258
end
5359
end
5460
end

apps/language_server/lib/language_server/providers/hover.ex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ defmodule ElixirLS.LanguageServer.Providers.Hover do
5353
defp build_module_link(module) do
5454
if ElixirSense.Core.Introspection.elixir_module?(module) do
5555
url = DocLinks.hex_docs_module_link(module)
56+
5657
if url do
5758
"[View on hexdocs](#{url})\n\n"
5859
else
@@ -66,6 +67,7 @@ defmodule ElixirLS.LanguageServer.Providers.Hover do
6667
defp build_function_link(module, function, arity) do
6768
if ElixirSense.Core.Introspection.elixir_module?(module) do
6869
url = DocLinks.hex_docs_function_link(module, function, arity)
70+
6971
if url do
7072
"[View on hexdocs](#{url})\n\n"
7173
else
@@ -79,6 +81,7 @@ defmodule ElixirLS.LanguageServer.Providers.Hover do
7981
defp build_type_link(module, type, arity) do
8082
if module != nil and ElixirSense.Core.Introspection.elixir_module?(module) do
8183
url = DocLinks.hex_docs_type_link(module, type, arity)
84+
8285
if url do
8386
"[View on hexdocs](#{url})\n\n"
8487
else

0 commit comments

Comments
 (0)