defmodule CljElixir.NRepl.Handler do @moduledoc "Handles nREPL protocol messages." alias CljElixir.NRepl.SessionManager def handle(msg, session_manager) do op = Map.get(msg, "op") id = Map.get(msg, "id", "unknown") session = Map.get(msg, "session") case op do "clone" -> handle_clone(id, session, session_manager) "close" -> handle_close(id, session, session_manager) "eval" -> handle_eval(msg, id, session, session_manager) "describe" -> handle_describe(id, session) "ls-sessions" -> handle_ls_sessions(id, session_manager) "load-file" -> handle_load_file(msg, id, session, session_manager) "interrupt" -> handle_interrupt(id, session) "completions" -> handle_completions(msg, id, session) _ -> [%{"id" => id, "session" => session || "", "status" => ["done", "error", "unknown-op"]}] end end defp handle_clone(id, _session, manager) do new_id = SessionManager.create_session(manager) [%{"id" => id, "new-session" => new_id, "status" => ["done"]}] end defp handle_close(id, session, manager) do SessionManager.close_session(manager, session) [%{"id" => id, "session" => session, "status" => ["done"]}] end defp handle_eval(msg, id, session, manager) do code = Map.get(msg, "code", "") # Capture stdout inside the Agent process where eval actually runs {output, result} = SessionManager.eval_with_capture(manager, session, code) responses = [] # Send stdout if any responses = if output != "" do responses ++ [%{"id" => id, "session" => session, "out" => output}] else responses end # Send value or error responses = case result do {:ok, value} -> responses ++ [%{"id" => id, "session" => session, "value" => value, "ns" => "user"}] {:error, error} -> responses ++ [%{"id" => id, "session" => session, "err" => error, "status" => ["eval-error"]}] end # Send done responses ++ [%{"id" => id, "session" => session, "status" => ["done"]}] end defp handle_describe(id, session) do [ %{ "id" => id, "session" => session || "", "status" => ["done"], "ops" => %{ "clone" => %{}, "close" => %{}, "eval" => %{}, "describe" => %{}, "ls-sessions" => %{}, "load-file" => %{}, "interrupt" => %{}, "completions" => %{} }, "versions" => %{ "clj-elixir" => %{"major" => 0, "minor" => 1, "incremental" => 0}, "nrepl" => %{"major" => 1, "minor" => 0, "incremental" => 0} } } ] end defp handle_ls_sessions(id, manager) do sessions = SessionManager.list_sessions(manager) [%{"id" => id, "sessions" => sessions, "status" => ["done"]}] end defp handle_load_file(msg, id, session, manager) do code = Map.get(msg, "file", "") handle_eval(Map.put(msg, "code", code), id, session, manager) end defp handle_interrupt(id, session) do # Not truly implemented -- just acknowledge [%{"id" => id, "session" => session, "status" => ["done"]}] end defp handle_completions(msg, id, session) do _prefix = Map.get(msg, "prefix", "") # Basic completions -- return empty for now, can be extended later [%{"id" => id, "session" => session || "", "completions" => [], "status" => ["done"]}] end end