Skip to content

Commit 7006f41

Browse files
committed
make server more stable with autoBuild disabled
1 parent 3551de8 commit 7006f41

File tree

4 files changed

+56
-36
lines changed

4 files changed

+56
-36
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,16 @@ defmodule ElixirLS.LanguageServer.Build do
2828
fetch_deps(current_deps)
2929
end
3030

31-
{status, diagnostics} = run_mix_compile()
32-
33-
diagnostics = Diagnostics.normalize(diagnostics, root_path)
34-
Server.build_finished(parent, {status, mixfile_diagnostics ++ diagnostics})
35-
:"mix_compile_#{status}"
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
3641
catch
3742
kind, payload ->
3843
{payload, stacktrace} = Exception.blame(kind, payload, __STACKTRACE__)
@@ -60,8 +65,12 @@ defmodule ElixirLS.LanguageServer.Build do
6065
end
6166
end)
6267

63-
Tracer.save()
64-
Logger.info("Compile took #{div(us, 1000)} milliseconds")
68+
if Keyword.get(opts, :compile?) do
69+
Tracer.save()
70+
Logger.info("Compile took #{div(us, 1000)} milliseconds")
71+
else
72+
Logger.info("Mix project load took #{div(us, 1000)} milliseconds")
73+
end
6574

6675
JsonRpc.telemetry("build", %{"elixir_ls.build_result" => result}, %{
6776
"elixir_ls.build_time" => div(us, 1000)

apps/language_server/lib/language_server/mix_project.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ defmodule ElixirLS.LanguageServer.MixProject do
1414
GenServer.call(__MODULE__, :store)
1515
end
1616

17+
def loaded? do
18+
GenServer.call(__MODULE__, :loaded?)
19+
end
20+
1721
@spec get() :: module | nil
1822
def get do
1923
GenServer.call(__MODULE__, {:get, :get})
@@ -154,4 +158,8 @@ defmodule ElixirLS.LanguageServer.MixProject do
154158

155159
{:reply, :ok, state}
156160
end
161+
162+
def handle_call(:loaded?, _from, state) do
163+
{:reply, is_map(state), state}
164+
end
157165
end

apps/language_server/lib/language_server/server.ex

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,7 @@ defmodule ElixirLS.LanguageServer.Server do
587587
end
588588
end
589589

590-
defp handle_notification(did_change_watched_files(changes), state = %__MODULE__{})
591-
when is_binary(state.project_dir) do
590+
defp handle_notification(did_change_watched_files(changes), state = %__MODULE__{mix_project?: true}) do
592591
changes = Enum.filter(changes, &match?(%{"uri" => "file:" <> _}, &1))
593592

594593
# `settings` may not always be available here, like during testing
@@ -657,7 +656,7 @@ defmodule ElixirLS.LanguageServer.Server do
657656
end
658657

659658
defp handle_notification(did_change_watched_files(_changes), state = %__MODULE__{}) do
660-
# swallow notification if project_dir is not yet set
659+
# swallow notification if we are not in mix_project
661660
state
662661
end
663662

@@ -1127,17 +1126,19 @@ defmodule ElixirLS.LanguageServer.Server do
11271126
end
11281127

11291128
defp get_spec_code_lenses(state = %__MODULE__{}, uri, source_file) do
1130-
if is_binary(state.project_dir) and dialyzer_enabled?(state) and !!state.settings["suggestSpecs"] do
1129+
enabled? = Map.get(state.settings || {}, "suggestSpecs", true)
1130+
if state.mix_project? and dialyzer_enabled?(state) and enabled? do
11311131
CodeLens.spec_code_lens(state.server_instance_id, uri, source_file.text)
11321132
else
11331133
{:ok, []}
11341134
end
11351135
end
11361136

11371137
defp get_test_code_lenses(state = %__MODULE__{}, uri, source_file) do
1138-
enabled = state.settings["enableTestLenses"] || false
1138+
# TODO check why test run from lense fails when autoBuild is disabled
1139+
enabled? = Map.get(state.settings || {}, "autoBuild", true) and Map.get(state.settings || {}, "enableTestLenses", false)
11391140

1140-
if is_binary(state.project_dir) and enabled do
1141+
if state.mix_project? and enabled? and ElixirLS.LanguageServer.MixProject.loaded? do
11411142
get_test_code_lenses(
11421143
state,
11431144
uri,
@@ -1232,19 +1233,20 @@ defmodule ElixirLS.LanguageServer.Server do
12321233
# Build
12331234

12341235
defp trigger_build(state = %__MODULE__{project_dir: project_dir}) do
1235-
build_automatically = Map.get(state.settings || %{}, "autoBuild", true)
1236-
12371236
cond do
1238-
not build_enabled?(state) ->
1237+
not state.mix_project? ->
12391238
state
12401239

1241-
not state.build_running? and build_automatically ->
1242-
fetch_deps? = Map.get(state.settings || %{}, "fetchDeps", false)
1240+
not state.build_running? ->
1241+
opts = [
1242+
fetch_deps?: Map.get(state.settings || %{}, "fetchDeps", false),
1243+
compile?: Map.get(state.settings || %{}, "autoBuild", true)
1244+
]
12431245

12441246
{_pid, build_ref} = case File.cwd() do
12451247
{:ok, cwd} ->
12461248
if Path.absname(cwd) == Path.absname(project_dir) do
1247-
Build.build(self(), project_dir, fetch_deps?: fetch_deps?)
1249+
Build.build(self(), project_dir, opts)
12481250
else
12491251
Logger.info("Skipping build because cwd changed from #{project_dir} to #{cwd}")
12501252
{nil, nil}
@@ -1255,7 +1257,7 @@ defmodule ElixirLS.LanguageServer.Server do
12551257
# try to change back to project dir
12561258
case File.cd(project_dir) do
12571259
:ok ->
1258-
Build.build(self(), project_dir, fetch_deps?: fetch_deps?)
1260+
Build.build(self(), project_dir, opts)
12591261
{:error, reason} ->
12601262
message = "Cannot change directory to project dir #{project_dir}: #{inspect(reason)}"
12611263
Logger.error(message)
@@ -1273,6 +1275,7 @@ defmodule ElixirLS.LanguageServer.Server do
12731275
}
12741276

12751277
true ->
1278+
# build already running, schedule another one
12761279
%__MODULE__{state | needs_build?: true, analysis_ready?: false}
12771280
end
12781281
end
@@ -1373,10 +1376,6 @@ defmodule ElixirLS.LanguageServer.Server do
13731376
end
13741377
end
13751378

1376-
defp build_enabled?(state = %__MODULE__{}) do
1377-
is_binary(state.project_dir)
1378-
end
1379-
13801379
defp dialyzer_enabled?(state = %__MODULE__{}) do
13811380
state.dialyzer_sup != nil
13821381
end
@@ -1471,7 +1470,7 @@ defmodule ElixirLS.LanguageServer.Server do
14711470

14721471
defp set_settings(state = %__MODULE__{}, settings) do
14731472
enable_dialyzer =
1474-
Dialyzer.check_support() == :ok && Map.get(settings, "dialyzerEnabled", true)
1473+
Dialyzer.check_support() == :ok and Map.get(settings, "autoBuild", true) and Map.get(settings, "dialyzerEnabled", true)
14751474

14761475
env_vars = Map.get(settings, "envVariables")
14771476
mix_env = Map.get(settings, "mixEnv")
@@ -1584,11 +1583,11 @@ defmodule ElixirLS.LanguageServer.Server do
15841583

15851584
defp set_dialyzer_enabled(state = %__MODULE__{}, enable_dialyzer) do
15861585
cond do
1587-
enable_dialyzer and state.dialyzer_sup == nil and is_binary(state.project_dir) ->
1586+
enable_dialyzer and state.mix_project? and state.dialyzer_sup == nil ->
15881587
{:ok, pid} = Dialyzer.Supervisor.start_link(state.project_dir)
1589-
%{state | dialyzer_sup: pid}
1588+
%{state | dialyzer_sup: pid, analysis_ready?: false}
15901589

1591-
not enable_dialyzer and state.dialyzer_sup != nil ->
1590+
not (enable_dialyzer and state.mix_project?) and state.dialyzer_sup != nil ->
15921591
Process.exit(state.dialyzer_sup, :normal)
15931592
%{state | dialyzer_sup: nil, analysis_ready?: false}
15941593

apps/language_server/lib/language_server/source_file.ex

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,18 @@ defmodule ElixirLS.LanguageServer.SourceFile do
235235

236236
try do
237237
alias ElixirLS.LanguageServer.MixProject
238-
opts = [
239-
deps_paths: MixProject.deps_paths(),
240-
manifest_path: MixProject.manifest_path(),
241-
config_mtime: MixProject.config_mtime(),
242-
mix_project: MixProject.get(),
243-
root: project_dir
244-
]
245-
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
238+
if ElixirLS.LanguageServer.MixProject.loaded? do
239+
opts = [
240+
deps_paths: MixProject.deps_paths(),
241+
manifest_path: MixProject.manifest_path(),
242+
config_mtime: MixProject.config_mtime(),
243+
mix_project: MixProject.get(),
244+
root: project_dir
245+
]
246+
{:ok, Mix.Tasks.ElixirLSFormat.formatter_for_file(path, opts)}
247+
else
248+
{:error, :project_not_loaded}
249+
end
246250
catch
247251
kind, payload ->
248252
{payload, stacktrace} = Exception.blame(kind, payload, __STACKTRACE__)

0 commit comments

Comments
 (0)