Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prompt to run mix deps.get if deps are out of sync on start #338

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading