diff --git a/apps/debug_adapter/lib/debug_adapter/server.ex b/apps/debug_adapter/lib/debug_adapter/server.ex index ca1f1fa70..ba9d67ed7 100644 --- a/apps/debug_adapter/lib/debug_adapter/server.ex +++ b/apps/debug_adapter/lib/debug_adapter/server.ex @@ -1644,8 +1644,9 @@ defmodule ElixirLS.DebugAdapter.Server do # Get the existing evaluator registry existing_registry = - case Map.fetch!(state.paused_processes, :evaluator) do - %PausedProcess{registry: registry} -> registry + case Map.fetch(state.paused_processes, :evaluator) do + {:ok, %PausedProcess{registry: registry}} -> registry + :error -> %PausedProcess{}.registry end {updated_registry, var_id} = @@ -1847,13 +1848,15 @@ defmodule ElixirLS.DebugAdapter.Server do end # continue erlang debugger paused processes - for {paused_pid, %PausedProcess{ref: ref}} <- state.paused_processes do + for {paused_pid, %PausedProcess{ref: ref}} <- state.paused_processes, is_pid(paused_pid) do safe_int_action(paused_pid, :continue) if ref, do: Process.demonitor(ref, [:flush]) paused_pid end - %{state | paused_processes: %{}} + # the :evaluator entry is not a real process - it holds variables from + # evaluate requests and needs to outlive paused processes + %{state | paused_processes: Map.take(state.paused_processes, [:evaluator])} end # Defensive wrapper around :int actions. :int is finicky and can raise from diff --git a/apps/debug_adapter/test/debugger_test.exs b/apps/debug_adapter/test/debugger_test.exs index 96adb843a..ef044af30 100644 --- a/apps/debug_adapter/test/debugger_test.exs +++ b/apps/debug_adapter/test/debugger_test.exs @@ -4203,6 +4203,41 @@ defmodule ElixirLS.DebugAdapter.ServerTest do assert Process.alive?(server) end) end + + test "evaluate expression after continuing all processes", %{server: server} do + in_fixture(__DIR__, "mix_project", fn -> + Server.receive_packet(server, initialize_req_(1)) + assert_receive(response(_, 1, "initialize", _)) + + Server.receive_packet( + server, + launch_req(2, %{ + "request" => "launch", + "type" => "mix_task", + "task" => "run", + "taskArgs" => ["-e", "MixProject.Dbg.simple()"], + "projectDir" => File.cwd!() + }) + ) + + assert_receive(response(_, 2, "launch"), 3000) + assert_receive(event(_, "initialized", _), 5000) + + Server.receive_packet(server, request(5, "configurationDone", %{})) + assert_receive(response(_, 5, "configurationDone")) + + assert_receive event(_, "stopped", %{"threadId" => thread_id}), 5_000 + + # continuing all processes must not drop the evaluator entry + Server.receive_packet(server, continue_req(6, thread_id)) + assert_receive response(_, 6, "continue", %{"allThreadsContinued" => true}) + + Server.receive_packet(server, gen_watch_expression_packet(7, "1 + 2 + 3 + 4")) + assert_receive(%{"body" => %{"result" => "10"}}, 5000) + + assert Process.alive?(server) + end) + end end test "completions", %{server: server} do