Skip to content

Commit e49a3a5

Browse files
committed
move mix project caching to Build and make sure it's under with_diagnostics
1 parent a0abfd5 commit e49a3a5

File tree

4 files changed

+95
-66
lines changed

4 files changed

+95
-66
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,46 +12,65 @@ defmodule ElixirLS.LanguageServer.Build do
1212

1313
# read cache before cleaning up mix state in reload_project
1414
cached_deps = read_cached_deps()
15+
mixfile = Path.absname(MixfileHelpers.mix_exs())
1516

16-
case reload_project() do
17+
case reload_project(mixfile) do
1718
{:ok, mixfile_diagnostics} ->
18-
ElixirLS.LanguageServer.MixProject.store()
19-
# FIXME: Private API
20-
21-
try do
22-
# this call can raise
23-
current_deps = Mix.Dep.load_on_environment([])
24-
25-
purge_changed_deps(current_deps, cached_deps)
26-
27-
if Keyword.get(opts, :fetch_deps?) and current_deps != cached_deps do
28-
fetch_deps(current_deps)
19+
{deps_result, deps_raw_diagnostics} = with_diagnostics([log: true], fn ->
20+
try do
21+
# this call can raise
22+
current_deps = Mix.Dep.load_on_environment([])
23+
24+
purge_changed_deps(current_deps, cached_deps)
25+
26+
if Keyword.get(opts, :fetch_deps?) and current_deps != cached_deps do
27+
fetch_deps(current_deps)
28+
end
29+
30+
state = %{
31+
get: Mix.Project.get(),
32+
# project_file: Mix.Project.project_file(),
33+
config: Mix.Project.config(),
34+
# config_files: Mix.Project.config_files(),
35+
config_mtime: Mix.Project.config_mtime(),
36+
umbrella?: Mix.Project.umbrella?(),
37+
apps_paths: Mix.Project.apps_paths(),
38+
# deps_path: Mix.Project.deps_path(),
39+
# deps_apps: Mix.Project.deps_apps(),
40+
# deps_scms: Mix.Project.deps_scms(),
41+
deps_paths: Mix.Project.deps_paths(),
42+
# build_path: Mix.Project.build_path(),
43+
manifest_path: Mix.Project.manifest_path()
44+
}
45+
ElixirLS.LanguageServer.MixProject.store(state)
46+
47+
:ok
48+
catch
49+
kind, err ->
50+
{payload, stacktrace} = Exception.blame(kind, err, __STACKTRACE__)
51+
{:error, kind, payload, stacktrace}
2952
end
30-
31-
if Keyword.get(opts, :compile?) do
32-
{status, diagnostics} = run_mix_compile()
33-
34-
diagnostics = Diagnostics.normalize(diagnostics, root_path)
35-
Server.build_finished(parent, {status, mixfile_diagnostics ++ diagnostics})
36-
:"mix_compile_#{status}"
37-
else
38-
Server.build_finished(parent, {:ok, mixfile_diagnostics})
39-
:mix_compile_disabled
40-
end
41-
catch
42-
kind, payload ->
43-
{payload, stacktrace} = Exception.blame(kind, payload, __STACKTRACE__)
44-
message = Exception.format(kind, payload, stacktrace)
45-
Logger.warning("Mix.Dep.load_on_environment([]) failed: #{message}")
46-
47-
JsonRpc.telemetry(
48-
"build_error",
49-
%{"elixir_ls.build_error" => message},
50-
%{}
51-
)
52-
53-
# TODO pass diagnostic
54-
Server.build_finished(parent, {:error, []})
53+
end)
54+
55+
deps_diagnostics =
56+
deps_raw_diagnostics
57+
|> Enum.map(&Diagnostics.code_diagnostic/1)
58+
59+
case deps_result do
60+
:ok ->
61+
if Keyword.get(opts, :compile?) do
62+
{status, compile_diagnostics} = run_mix_compile()
63+
64+
compile_diagnostics = Diagnostics.normalize(compile_diagnostics, root_path)
65+
Server.build_finished(parent, {status, mixfile_diagnostics ++ deps_diagnostics ++ compile_diagnostics})
66+
:"mix_compile_#{status}"
67+
else
68+
Server.build_finished(parent, {:ok, mixfile_diagnostics ++ deps_diagnostics})
69+
:mix_compile_disabled
70+
end
71+
{:error, kind, err, stacktrace} ->
72+
# TODO get path from exception message
73+
Server.build_finished(parent, {:error, mixfile_diagnostics ++ deps_diagnostics ++ [Diagnostics.error_to_diagnostic(kind, err, stacktrace, mixfile)]})
5574
:deps_error
5675
end
5776

@@ -90,9 +109,7 @@ defmodule ElixirLS.LanguageServer.Build do
90109
:global.trans({__MODULE__, self()}, func)
91110
end
92111

93-
def reload_project do
94-
mixfile = Path.absname(MixfileHelpers.mix_exs())
95-
112+
def reload_project(mixfile) do
96113
if File.exists?(mixfile) do
97114
if module = Mix.Project.get() do
98115
build_path = Mix.Project.config()[:build_path]
@@ -178,7 +195,7 @@ defmodule ElixirLS.LanguageServer.Build do
178195

179196
# We can get diagnostics if Mixfile fails to load
180197
{mixfile_status, mixfile_diagnostics} =
181-
case Kernel.ParallelCompiler.compile([mixfile]) do
198+
case Kernel.ParallelCompiler.compile([mixfile]) |> dbg do
182199
{:ok, _, warnings} ->
183200
{:ok, Enum.map(warnings, &Diagnostics.mixfile_diagnostic(&1, :warning))}
184201

apps/language_server/lib/language_server/diagnostics.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
defmodule ElixirLS.LanguageServer.Diagnostics do
22
alias ElixirLS.LanguageServer.{SourceFile, JsonRpc}
3-
alias ElixirLS.Utils.MixfileHelpers
43

54
def normalize(diagnostics, root_path) do
65
for diagnostic <- diagnostics do

apps/language_server/lib/language_server/mix_project.ex

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ defmodule ElixirLS.LanguageServer.MixProject do
1212
GenServer.start_link(__MODULE__, args, name: __MODULE__)
1313
end
1414

15-
def store do
16-
GenServer.call(__MODULE__, :store)
15+
def store(state) do
16+
GenServer.call(__MODULE__, {:store, state})
1717
end
1818

1919
def loaded? do
@@ -171,23 +171,7 @@ defmodule ElixirLS.LanguageServer.MixProject do
171171
{:reply, Map.fetch!(state, key), state}
172172
end
173173

174-
def handle_call(:store, _from, _state) do
175-
state = %{
176-
get: Mix.Project.get(),
177-
project_file: Mix.Project.project_file(),
178-
config: Mix.Project.config(),
179-
config_files: Mix.Project.config_files(),
180-
config_mtime: Mix.Project.config_mtime(),
181-
umbrella?: Mix.Project.umbrella?(),
182-
apps_paths: Mix.Project.apps_paths(),
183-
deps_path: Mix.Project.deps_path(),
184-
deps_apps: Mix.Project.deps_apps(),
185-
deps_scms: Mix.Project.deps_scms(),
186-
deps_paths: Mix.Project.deps_paths(),
187-
build_path: Mix.Project.build_path(),
188-
manifest_path: Mix.Project.manifest_path()
189-
}
190-
174+
def handle_call({:store, state}, _from, _state) do
191175
{:reply, :ok, state}
192176
end
193177

apps/language_server/lib/language_server/server.ex

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,12 +1859,41 @@ defmodule ElixirLS.LanguageServer.Server do
18591859
Logger.info("DETS databases will be rebuilt")
18601860
Tracer.clean_dets(project_dir)
18611861

1862-
case Build.reload_project() do
1863-
{:ok, _} ->
1864-
Build.clean(true)
1862+
case File.cwd() do
1863+
{:ok, cwd} ->
1864+
if Path.absname(cwd) == Path.absname(project_dir) do
1865+
mixfile = Path.absname(MixfileHelpers.mix_exs())
1866+
case Build.reload_project(mixfile) do
1867+
{:ok, _} ->
1868+
Build.clean(true)
18651869

1866-
_ ->
1867-
:ok
1870+
_ ->
1871+
# TODO emit diagnostics here?
1872+
:ok
1873+
end
1874+
else
1875+
message = "Unable to reload project: cwd #{inspect(cwd)} is not project dir #{project_dir}"
1876+
Logger.error(message)
1877+
JsonRpc.telemetry(
1878+
"lsp_server_error",
1879+
%{
1880+
"elixir_ls.lsp_process" => inspect(__MODULE__),
1881+
"elixir_ls.lsp_server_error" => message
1882+
},
1883+
%{}
1884+
)
1885+
end
1886+
{:error, reason} ->
1887+
message = "Unable to reload project: #{inspect(reason)}"
1888+
Logger.error(message)
1889+
JsonRpc.telemetry(
1890+
"lsp_server_error",
1891+
%{
1892+
"elixir_ls.lsp_process" => inspect(__MODULE__),
1893+
"elixir_ls.lsp_server_error" => message
1894+
},
1895+
%{}
1896+
)
18681897
end
18691898
end
18701899
end

0 commit comments

Comments
 (0)