diff --git a/SPAWNFEST.md b/SPAWNFEST.md index a81bbd30f..7c61fbdf4 100644 --- a/SPAWNFEST.md +++ b/SPAWNFEST.md @@ -111,6 +111,64 @@ While working on the project, we noticed a few issues with third-party projects ;; Erlang DAP ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(require 'dap-mode) + +;; Log io to *Messages*. Seems to be the only option at the moment. Optional +(setq dap-inhibit-io nil) +(setq dap-print-io t) + +(defun dap-erlang--populate-start-file-args (conf) + "Populate CONF with the required arguments." + (-> conf + (dap--put-if-absent :dap-server-path '("els_dap")) + (dap--put-if-absent :request "launch") + (dap--put-if-absent :projectDir (lsp-find-session-folder (lsp-session) (buffer-file-name))) + (dap--put-if-absent :cwd (lsp-find-session-folder (lsp-session) (buffer-file-name))) + )) + +;; Add a Run Configuration for running 'rebar3 shell' +(dap-register-debug-provider "Erlang" 'dap-erlang--populate-start-file-args) +(dap-register-debug-template "Erlang rebar3 shell" + (list :type "Erlang" + :program "rebar3" + :args "shell" + :name "Erlang::Run")) + +;; Add a Run Configuration for executing a given MFA +(dap-register-debug-template "Erlang MFA" + (list :type "Erlang" + :module "daptoy_fact" + :function "fact" + ;; :function "dummy" + :args "[3]" + :name "Erlang::Run MFA")) + +;; Add a Run Configuration for running 'rebar3 shell' +;; in an integrated terminal in the client +;; NOTE: set the projectnode hostname in two places +(dap-register-debug-template "Erlang Terminal" + (list :type "Erlang" + :runinterminal '("rebar3" "shell" "--name" "dapnode@alanzimm-mbp") + :projectnode "dapnode@alanzimm-mbp" + :name "Erlang::Terminal")) + +;; Add a Run Configuration for running 'rebar3 shell' +;; in an integrated terminal in the client, running a given MFA +;; NOTE: set the projectnode hostname in two places +(dap-register-debug-template "Erlang Terminal MFA" + (list :type "Erlang" + :runinterminal '("rebar3" "shell" "--name" "dapnode@alanzimm-mbp") + :projectnode "dapnode@alanzimm-mbp" + :module "daptoy_fact" + :function "fact" + ;; :function "dummy" + :args "[4]" + :name "Erlang::Terminal MFA")) + + + + + (require 'dap-mode) ;; Show debug logs diff --git a/src/els_dap_general_provider.erl b/src/els_dap_general_provider.erl index 57f6972da..56f04a45b 100644 --- a/src/els_dap_general_provider.erl +++ b/src/els_dap_general_provider.erl @@ -64,13 +64,31 @@ handle_request({<<"launch">>, Params}, State) -> #{<<"cwd">> := Cwd} = Params, ok = file:set_cwd(Cwd), - ProjectNode = node_name("dap_project_", Cwd), - spawn(fun() -> els_utils:cmd("rebar3", ["shell", "--name", ProjectNode]) end), + ProjectNode = case Params of + #{ <<"projectnode">> := Node } -> binary_to_atom(Node, utf8); + _ -> node_name("dap_project_", Cwd) + end, + case Params of + #{ <<"runinterminal">> := Cmd + } -> + ParamsR + = #{ <<"kind">> => <<"integrated">> + , <<"title">> => ProjectNode + , <<"cwd">> => Cwd + , <<"args">> => Cmd + }, + lager:info("Sending runinterminal request: [~p]", [ParamsR]), + els_dap_server:send_request(<<"runInTerminal">>, ParamsR), + ok; + _ -> + lager:info("launching 'rebar3 shell`", []), + spawn(fun() -> + els_utils:cmd("rebar3", ["shell", "--name", ProjectNode]) end) + end, LocalNode = node_name("dap_", Cwd), els_distribution_server:start_distribution(LocalNode), - - els_distribution_server:wait_connect_and_monitor(ProjectNode, 5), + lager:info("Distribution up on: [~p]", [LocalNode]), els_dap_server:send_event(<<"initialized">>, #{}), @@ -79,6 +97,9 @@ handle_request( {<<"configurationDone">>, _Params} , #{ project_node := ProjectNode , launch_params := LaunchParams} = State ) -> + lager:info("Connecting to: [~p]", [ProjectNode]), + els_distribution_server:wait_connect_and_monitor(ProjectNode), + inject_dap_agent(ProjectNode), %% TODO: Fetch stack_trace mode from Launch Config @@ -107,6 +128,10 @@ handle_request( {<<"setBreakpoints">>, Params} _SourceModified = maps:get(<<"sourceModified">>, Params, false), Module = els_uri:module(els_uri:uri(Path)), + %% AZ: we should have something like `ensure_connected` + lager:info("Connecting to: [~p]", [ProjectNode]), + els_distribution_server:wait_connect_and_monitor(ProjectNode), + %% TODO: Keep a list of interpreted modules, not to re-interpret them els_dap_rpc:i(ProjectNode, Module), [els_dap_rpc:break(ProjectNode, Module, Line) || @@ -174,6 +199,15 @@ handle_request( {<<"stepIn">>, Params} Pid = to_pid(ThreadId, Threads), ok = els_dap_rpc:step(ProjectNode, Pid), {#{}, State}; +handle_request( {<<"stepOut">>, Params} + , #{ threads := Threads + , project_node := ProjectNode + } = State + ) -> + #{<<"threadId">> := ThreadId} = Params, + Pid = to_pid(ThreadId, Threads), + ok = els_dap_rpc:next(ProjectNode, Pid), + {#{}, State}; handle_request({<<"evaluate">>, #{ <<"context">> := <<"hover">> , <<"frameId">> := FrameId , <<"expression">> := Input @@ -189,7 +223,10 @@ handle_request({<<"evaluate">>, #{ <<"context">> := <<"hover">> {#{<<"result">> => Result}, State}; handle_request({<<"variables">>, _Params}, State) -> %% TODO: Return variables - {#{<<"variables">> => []}, State}. + {#{<<"variables">> => []}, State}; +handle_request({<<"disconnect">>, _Params}, State) -> + els_utils:halt(0), + {#{}, State}. -spec handle_info(any(), state()) -> state(). handle_info( {int_cb, ThreadPid} diff --git a/src/els_dap_protocol.erl b/src/els_dap_protocol.erl index 3821f3e24..ff22f77d3 100644 --- a/src/els_dap_protocol.erl +++ b/src/els_dap_protocol.erl @@ -32,19 +32,19 @@ -spec event(number(), binary(), any()) -> binary(). %% TODO: Body is optional event(Seq, EventType, Body) -> - Message = #{ seq => Seq - , type => <<"event">> + Message = #{ type => <<"event">> + , seq => Seq , event => EventType , body => Body }, content(jsx:encode(Message)). -spec request(number(), binary(), any()) -> binary(). -request(RequestId, Method, Params) -> - Message = #{ jsonrpc => ?JSONRPC_VSN - , method => Method - , id => RequestId - , params => Params +request(RequestSeq, Method, Params) -> + Message = #{ type => <<"request">> + , seq => RequestSeq + , command => Method + , arguments => Params }, content(jsx:encode(Message)). diff --git a/src/els_dap_server.erl b/src/els_dap_server.erl index dc326914e..265048ddb 100644 --- a/src/els_dap_server.erl +++ b/src/els_dap_server.erl @@ -165,7 +165,7 @@ do_send_event(EventType, Body, #state{seq = Seq0} = State0) -> -spec do_send_request(binary(), map(), state()) -> state(). do_send_request(Method, Params, #state{seq = RequestId0} = State0) -> RequestId = RequestId0 + 1, - Request = els_protocol:request(RequestId, Method, Params), + Request = els_dap_protocol:request(RequestId, Method, Params), lager:debug( "[SERVER] Sending request [request=~p]" , [Request] ), diff --git a/src/els_distribution_server.erl b/src/els_distribution_server.erl index d1c31aafb..f6a235afd 100644 --- a/src/els_distribution_server.erl +++ b/src/els_distribution_server.erl @@ -51,7 +51,10 @@ start_distribution(Name) -> {ok, _Pid} -> lager:info("Distribution enabled [name=~p]", [Name]); {error, {already_started, _Pid}} -> - lager:info("Distribution already enabled [name=~p]", [Name]) + lager:info("Distribution already enabled [name=~p]", [Name]); + {error, {{shutdown, {failed_to_start_child, net_kernel, E1}}, E2}} -> + lager:info("Distribution shutdown [errs=~p]", [{E1, E2}]), + lager:info("Distribution shut down [name=~p]", [Name]) end. %% @doc Connect to an existing runtime node, if available, or start one.