Skip to content

Commit

Permalink
fix: prompt to run mix deps.get if deps are out of sync on start (#338)
Browse files Browse the repository at this point in the history
Partially addresses #53 #115 #285

Co-authored-by: Mitchell Hanberg <mitch@mitchellhanberg.com>
  • Loading branch information
jjcarstens and mhanberg committed Feb 28, 2024
1 parent 8b0b7bd commit 55e91ac
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 238 deletions.
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
src = self.outPath;
inherit version elixir;
pname = "next-ls-deps";
hash = "sha256-BteNxUWcubVZ/SrFeBxKKV7KHmR39H50kUVaUz53dJs=";
hash = "sha256-G0OZlg3CInKPbmBPWCVnFTzudFdOr9yTwWsKXk+7zVg=";
mixEnv = "prod";
};

Expand Down
138 changes: 104 additions & 34 deletions lib/next_ls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,11 @@ defmodule NextLS do

GenLSP.log(lsp, "[NextLS] Booting runtimes...")

parent = self()

for %{uri: uri, name: name} <- lsp.assigns.workspace_folders do
token = Progress.token()
Progress.start(lsp, token, "Initializing NextLS runtime for folder #{name}...")
parent = self()
working_dir = URI.parse(uri).path

{:ok, _} =
Expand Down Expand Up @@ -778,6 +779,9 @@ defmodule NextLS do
send(parent, msg)
else
Progress.stop(lsp, token)

send(parent, {:runtime_failed, name, status})

GenLSP.error(lsp, "[NextLS] Runtime for folder #{name} failed to initialize")
end
end,
Expand Down Expand Up @@ -865,47 +869,47 @@ defmodule NextLS do
parent = self()
working_dir = URI.parse(uri).path

# TODO: probably extract this to the Runtime module
{:ok, _} =
DynamicSupervisor.start_child(
lsp.assigns.dynamic_supervisor,
{NextLS.Runtime.Supervisor,
path: Path.join(working_dir, ".elixir-tools"),
name: name,
registry: lsp.assigns.registry,
runtime: [
task_supervisor: lsp.assigns.runtime_task_supervisor,
working_dir: working_dir,
uri: uri,
mix_env: lsp.assigns.init_opts.mix_env,
mix_target: lsp.assigns.init_opts.mix_target,
on_initialized: fn status ->
if status == :ready do
Progress.stop(lsp, token, "NextLS runtime for folder #{name} has initialized!")
GenLSP.log(lsp, "[NextLS] Runtime for folder #{name} is ready...")
msg = {:runtime_ready, name, self()}

dispatch(lsp.assigns.registry, :extensions, fn entries ->
for {pid, _} <- entries, do: send(pid, msg)
end)

send(parent, msg)
else
Progress.stop(lsp, token)
GenLSP.error(lsp, "[NextLS] Runtime for folder #{name} failed to initialize")
end
end,
logger: lsp.assigns.logger
]}
NextLS.Runtime.boot(lsp.assigns.dynamic_supervisor,
path: Path.join(working_dir, ".elixir-tools"),
name: name,
registry: lsp.assigns.registry,
runtime: [
task_supervisor: lsp.assigns.runtime_task_supervisor,
working_dir: working_dir,
uri: uri,
mix_env: lsp.assigns.init_opts.mix_env,
mix_target: lsp.assigns.init_opts.mix_target,
on_initialized: fn status ->
if status == :ready do
Progress.stop(lsp, token, "NextLS runtime for folder #{name} has initialized!")
GenLSP.log(lsp, "[NextLS] Runtime for folder #{name} is ready...")

msg = {:runtime_ready, name, self()}

dispatch(lsp.assigns.registry, :extensions, fn entries ->
for {pid, _} <- entries, do: send(pid, msg)
end)

send(parent, msg)
else
Progress.stop(lsp, token)

send(parent, {:runtime_failed, name, status})

GenLSP.error(lsp, "[NextLS] Runtime for folder #{name} failed to initialize")
end
end,
logger: lsp.assigns.logger
]
)
end

names = Enum.map(removed, & &1.name)

for {pid, %{name: name}} <- entries, name in names do
GenLSP.log(lsp, "[NextLS] Removing workspace folder #{name}")
# TODO: probably extract this to the Runtime module
DynamicSupervisor.terminate_child(lsp.assigns.dynamic_supervisor, pid)
NextLS.Runtime.stop(lsp.assigns.dynamic_supervisor, pid)
end
end)

Expand Down Expand Up @@ -995,6 +999,72 @@ defmodule NextLS do
{:noreply, assign(lsp, ready: true, refresh_refs: refresh_refs)}
end

def handle_info({:runtime_failed, name, status}, lsp) do
{pid, %{init_arg: init_arg}} =
dispatch(lsp.assigns.registry, :runtime_supervisors, fn entries ->
Enum.find(entries, fn {_pid, %{name: n}} -> n == name end)
end)

:ok = DynamicSupervisor.terminate_child(lsp.assigns.dynamic_supervisor, pid)

if status == {:error, :deps} do
resp =
GenLSP.request(
lsp,
%GenLSP.Requests.WindowShowMessageRequest{
id: System.unique_integer([:positive]),
params: %GenLSP.Structures.ShowMessageRequestParams{
type: GenLSP.Enumerations.MessageType.error(),
message: "The NextLS runtime failed with errors on dependencies. Would you like to re-fetch them?",
actions: [
%GenLSP.Structures.MessageActionItem{title: "yes"},
%GenLSP.Structures.MessageActionItem{title: "no"}
]
}
},
:infinity
)

case resp do
%GenLSP.Structures.MessageActionItem{title: "yes"} ->
NextLS.Logger.info(
lsp.assigns.logger,
"Running `mix deps.get` in directory #{init_arg[:runtime][:working_dir]}"
)

File.rm_rf!(Path.join(init_arg[:runtime][:working_dir], ".elixir-tools/_build"))

case System.cmd("mix", ["deps.get"],
env: [{"MIX_ENV", "dev"}, {"MIX_BUILD_ROOT", ".elixir-tools/_build"}],
cd: init_arg[:runtime][:working_dir],
stderr_to_stdout: true
) do
{msg, 0} ->
NextLS.Logger.info(
lsp.assigns.logger,
"Restarting runtime #{name} for directory #{init_arg[:runtime][:working_dir]}"
)

NextLS.Logger.info(lsp.assigns.logger, msg)

{:ok, _} =
DynamicSupervisor.start_child(lsp.assigns.dynamic_supervisor, {NextLS.Runtime.Supervisor, init_arg})

{msg, _} ->
NextLS.Logger.warning(
lsp.assigns.logger,
"Failed to run `mix deps.get` in directory #{init_arg[:runtime][:working_dir]} with message: #{msg}"
)
end

_ ->
NextLS.Logger.info(lsp.assigns.logger, "Not running `mix deps.get`")
end
end

{:noreply, lsp}
end

def handle_info({ref, _resp}, %{assigns: %{refresh_refs: refs}} = lsp) when is_map_key(refs, ref) do
Process.demonitor(ref, [:flush])
{{token, msg}, refs} = Map.pop(refs, ref)
Expand Down
21 changes: 21 additions & 0 deletions lib/next_ls/runtime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ defmodule NextLS.Runtime do
GenServer.call(server, {:compile, opts}, :infinity)
end

def boot(supervisor, opts) do
DynamicSupervisor.start_child(supervisor, {NextLS.Runtime.Supervisor, opts})
end

def stop(supervisor, pid) do
DynamicSupervisor.terminate_child(supervisor, pid)
end

defmacro execute!(runtime, block) do
quote do
{:ok, result} = NextLS.Runtime.execute(unquote_splicing([runtime, block]))
Expand Down Expand Up @@ -294,6 +302,9 @@ defmodule NextLS.Runtime do

diagnostics

{:error, %Mix.Error{message: "Can't continue due to errors on dependencies"}} ->
nil

unknown ->
NextLS.Logger.warning(state.logger, "Unexpected compiler response: #{inspect(unknown)}")
[]
Expand Down Expand Up @@ -341,6 +352,16 @@ defmodule NextLS.Runtime do
{:stop, {:shutdown, :nodedown}, state}
end

def handle_info(
{port, {:data, "** (Mix) Can't continue due to errors on dependencies" <> _ = data}},
%{port: port} = state
) do
NextLS.Logger.log(state.logger, data)

state.on_initialized.({:error, :deps})
{:noreply, state}
end

def handle_info({port, {:data, data}}, %{port: port} = state) do
NextLS.Logger.info(state.logger, data)
{:noreply, state}
Expand Down
2 changes: 1 addition & 1 deletion lib/next_ls/runtime/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule NextLS.Runtime.Supervisor do
sidecar_name = :"sidecar-#{name}"
db_activity = :"db-activity-#{name}"

Registry.register(registry, :runtime_supervisors, %{name: name})
Registry.register(registry, :runtime_supervisors, %{name: name, init_arg: init_arg})

children = [
{NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name},
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ defmodule NextLS.MixProject do
defp deps do
[
{:exqlite, "~> 0.13.14"},
{:gen_lsp, "~> 0.7"},
{:gen_lsp, "~> 0.8"},
# {:gen_lsp, path: "../gen_lsp"},
{:req, "~> 0.3"},
{:schematic, "~> 0.2"},
{:spitfire, github: "elixir-tools/spitfire"},
Expand Down
2 changes: 1 addition & 1 deletion mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"exqlite": {:hex, :exqlite, "0.13.15", "a32c0763915e2b0d7ced9dd8638802d38e9569053f3b28b815bd0faef1cbe6d9", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "4afcc870a33b57781a1e57cd4294eef68815059d26b774c7cd075536b21434b7"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
"gen_lsp": {:hex, :gen_lsp, "0.7.3", "08de9b88a8e8e79777bbab78d07ea0d5bbd05d54fdcba2c1d3328a9d091172eb", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "ef7288e55be8889aba263658aa74584af0ea5d04848000cb4e16d3c2e21dde37"},
"gen_lsp": {:hex, :gen_lsp, "0.8.1", "847587dfcb1d6c08c1fc9506a2b47af134e64346b14df551c286623c318705f2", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "33a4890b707025491a0c7c907a71516d3b37e08567231d0b0ac3950cda8bc088"},
"gproc": {:hex, :gproc, "0.9.1", "f1df0364423539cf0b80e8201c8b1839e229e5f9b3ccb944c5834626998f5b8c", [:rebar3], [], "hexpm", "905088e32e72127ed9466f0bac0d8e65704ca5e73ee5a62cb073c3117916d507"},
"grpcbox": {:hex, :grpcbox, "0.17.1", "6e040ab3ef16fe699ffb513b0ef8e2e896da7b18931a1ef817143037c454bcce", [:rebar3], [{:acceptor_pool, "~> 1.0.0", [hex: :acceptor_pool, repo: "hexpm", optional: false]}, {:chatterbox, "~> 0.15.1", [hex: :ts_chatterbox, repo: "hexpm", optional: false]}, {:ctx, "~> 0.6.0", [hex: :ctx, repo: "hexpm", optional: false]}, {:gproc, "~> 0.9.1", [hex: :gproc, repo: "hexpm", optional: false]}], "hexpm", "4a3b5d7111daabc569dc9cbd9b202a3237d81c80bf97212fbc676832cb0ceb17"},
"hpack": {:hex, :hpack_erl, "0.3.0", "2461899cc4ab6a0ef8e970c1661c5fc6a52d3c25580bc6dd204f84ce94669926", [:rebar3], [], "hexpm", "d6137d7079169d8c485c6962dfe261af5b9ef60fbc557344511c1e65e3d95fb0"},
Expand Down
Loading

0 comments on commit 55e91ac

Please sign in to comment.