Permalink
Browse files

[lsp] implement a no-op LSP server

  • Loading branch information...
vladdu committed Mar 17, 2017
1 parent 5ced1ae commit 41ac5ea58ec794cb3c6c16dfcc099b5cf19bf06e
View
@@ -26,3 +26,4 @@ TEST-*.*
server/erlide_ide
*.crashdump
View
@@ -1,6 +1,6 @@
#! /bin/bash -e
declare -A OTP_VSNS=( ["17"]="17.5" ["18"]="18.3" ["19"]="19.2")
declare -A OTP_VSNS=( ["17"]="17.5" ["18"]="18.3" ["19"]="19.3")
build_project() {
REBAR=$1
@@ -0,0 +1,157 @@
-module(cancellable_worker).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% ====================================================================
%% API functions
%% ====================================================================
-export([
start/1,
start/3,
cancel/1,
cancel/2,
yield/1,
yield/2,
check/1
]).
%% Implement a worker process that can be cancelled and then may return a
%% partial answer.
%% The function doing the actual work takes as argument a Reporter function to
%% use to report results:
%% - Reporter(partial, Value) for a partial result
%% - Reporter(final, Value) for the whole result (if partial results are not
%% possible); do not report this after any partial values
%% If partial results are sent, they are aggregated in a list, which is returned
start(WorkerFun) ->
gen_server:start(?MODULE, WorkerFun, []).
start(Module, Function, Args) ->
start(fun() -> apply(Module, Function, Args) end).
%% Check/1 checks if there are any answers from the worker. It can return
%% - {partial, Values} : the list of all currently reported values
%% - {final, Value} : the final result
%% - {error, {Value1, Value2}} : unexpected 'final' Value2 reported (either
%% after another 'final' or after 'partial's Value1)
check(MonPid) when is_pid(MonPid) ->
case is_process_alive(MonPid) of
true ->
gen_server:call(MonPid, check);
false ->
{error, noproc}
end.
%% Cancels the worker and returns the current results.
cancel(MonPid) when is_pid(MonPid) ->
case is_process_alive(MonPid) of
true ->
io:format("*** ~p~n", [{MonPid}]),
catch gen_server:call(MonPid, cancel);
false ->
{error, noproc}
end.
cancel(MonPid, Timeout) when is_pid(MonPid) ->
gen_server:call(MonPid, cancel, Timeout).
%% Wait until the the worker has finished and return the final result.
%% TODO don't return partial/final
yield(MonPid) when is_pid(MonPid) ->
gen_server:call(MonPid, yield).
yield(MonPid, Timeout) when is_pid(MonPid) ->
gen_server:call(MonPid, yield, Timeout).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
-record(state, {
worker_pid,
results = {partial, undefined},
yielding = false
}).
init(WorkerFun) ->
process_flag(trap_exit, true),
Monitor = self(),
Report = fun(partial, V) -> gen_server:cast(Monitor, {partial, V});
(final, V) -> gen_server:cast(Monitor, {final, V})
end,
{WrkPid, _Ref} = spawn_monitor(fun() ->
WorkerFun(Report)
end),
{ok, #state{worker_pid=WrkPid}}.
handle_call(check, _From, State=#state{results=Results, worker_pid=Pid}) when is_pid(Pid) ->
Reply = adjust(Results),
{reply, Reply, State};
handle_call(check, _From, State=#state{results=Results}) ->
{_, Reply} = adjust(Results),
{reply, {final, Reply}, State};
handle_call(cancel, _From, State=#state{results=Results, worker_pid=Pid}) when is_pid(Pid) ->
exit(Pid, kill),
{_, Reply} = adjust(Results),
{reply, {ok, Reply}, State};
handle_call(cancel, _From, State=#state{results=Results}) ->
{_, Reply} = adjust(Results),
{reply, {ok, Reply}, State};
handle_call(yield, _From, State=#state{worker_pid=false, results=Results}) ->
{_, Reply} = adjust(Results),
{stop, normal, {ok, Reply}, State};
handle_call(yield, From, State) ->
{noreply, State#state{yielding=From}};
handle_call(Request, _From, State) ->
io:format("HUH???... ~p~n", [Request]),
Reply = {error, {unknown, Request}},
{reply, Reply, State}.
handle_cast(V, State=#state{results=Results}) ->
NewResults = merge_result(V, Results),
{noreply, State#state{results=NewResults}};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({'DOWN', _, process, Pid, _Reason},
State=#state{worker_pid=Pid,
yielding=From,
results=Results}) when From /= false ->
{_, Reply} = adjust(Results),
gen_server:reply(From, {ok, Reply}),
{noreply, State#state{worker_pid=false}};
handle_info({'DOWN', _, process, Pid, _Reason}, State=#state{worker_pid=Pid}) ->
{noreply, State#state{worker_pid=false}};
handle_info(_Info, State) ->
io:format("@@@ ~p~n", [_Info]),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ====================================================================
%% Internal functions
%% ====================================================================
adjust({K, Results}=Arg) ->
if is_list(Results) ->
{K, lists:reverse(Results)};
true ->
Arg
end.
merge_result({final, V}, {partial, undefined}) ->
{final, V};
merge_result({partial, V}, {partial, undefined}) ->
{partial, [V]};
merge_result({final, V}, {partial, R}) ->
{final, [V|R]};
merge_result({partial, V}, {partial, R}) ->
{partial, [V|R]};
merge_result(_V, R) ->
R.
@@ -0,0 +1,94 @@
%%%
-module(erlide_doc_server).
-export([
get_raw_documentation/3,
get_documentation/3
]).
-type root() ::
{'lib', string()} |
{'app', string()}.
-type configuration() ::
#{
'roots' => [root()]
}.
-type doc_ref() ::
{'application', atom()} |
{'module', atom()} |
{'function', {atom(), atom(), integer()}} |
{'macro', {atom(), atom(), integer()}} |
{'record', {atom(), atom(), integer()}} |
{'behaviour', atom()} |
{'type', {atom(), atom(), integer()}}.
-type doc_tree() :: any().
-type doc_result() :: {'ok', [{atom(), doc_tree()}]} | {'error', any()}.
-type raw_doc_result() :: {'ok', [{atom(), iolist()}]} | {'error', any()}.
-type provider() :: fun((doc_ref()) -> {'ok', {atom(), iolist()}} | {'error', any()}).
-spec get_documentation(configuration(), doc_ref(), [provider()]) -> doc_result().
get_documentation(Config, Ref, Providers) ->
RawDocs = get_raw_documentation(Config, Ref, Providers),
convert(Config, Providers, RawDocs).
-spec get_raw_documentation(configuration(), doc_ref(), [provider()]) -> raw_doc_result().
get_raw_documentation(Config, Ref, Providers) ->
traverse(Config, Providers, Ref).
%% ====================================================================
%% Internal functions
%% ====================================================================
traverse(Config, L, Args) ->
traverse(Config, L, Args, [], []).
traverse(_Config, [], _Args, Result, []) ->
{ok, lists:reverse(Result)};
traverse(_Config, [], _Args, Result, Errs) ->
{error, {lists:reverse(Errs), lists:reverse(Result)}};
traverse(Config, [M|T], Args, Result, Err) ->
case catch apply(M, Args) of
{error, E} ->
traverse(Config, T, Args, Result, [E|Err]);
{'EXIT', E} ->
traverse(Config, T, Args, Result, [E|Err]);
{ok, V} ->
traverse(Config, T, Args, [V|Result], Err);
V ->
traverse(Config, T, Args, [V|Result], Err)
end.
convert(Config, Providers, {ok, Docs}) ->
{ok, convert1(Config, Providers, Docs, [])};
convert(Config, Providers, {error, Errors, Docs}) ->
{error, Errors, convert1(Config, Providers, Docs, [])}.
convert1(_Config, _Providers, [], Result) ->
lists:reverse(Result);
convert1(Config, Providers, [{M, D}|T], Result) ->
case lists:keyfind(M, 1, Providers) of
{M, P} ->
V = P:convert(D),
convert1(Config, Providers, T, [V|Result]);
false ->
convert1(Config, Providers, T, Result)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-include_lib("eunit/include/eunit.hrl").
traverse_test_() ->
[
?_assertEqual({ok, [[1,2]]},traverse(#{}, [fun lists:seq/2], [1,2])),
?_assertEqual({ok, []},traverse(#{}, [], [])),
?_assertEqual({ok, []},traverse(#{}, [], [])),
?_assertEqual({ok, []},traverse(#{}, [], [])),
?_assertEqual({ok, []},traverse(#{}, [], [])),
?_assertEqual({ok, []},traverse(#{}, [], []))
].
@@ -0,0 +1,12 @@
-module(erlide_edoc_doc_provider).
-export([
get_documentation/1
]).
get_documentation({Kind, Ref}) ->
R = lists:flatten(io_lib:format("EDOC:: ~p ~p", [Kind, Ref])),
{ok, {edoc, R}};
get_documentation(Arg) ->
{error, {badarg, Arg}}.
@@ -3,7 +3,7 @@
{vsn, "0.113.0"},
{erlide_context, ide},
{registered, []},
{applications, [kernel, stdlib, erlide_common, erlide_server]},
{applications, [kernel, stdlib, erlide_common, erlide_server, jsx]},
{env, []},
{mod, {erlide_ide, []}}
]}
@@ -5,14 +5,16 @@
-behaviour(application).
-export([start/2, stop/1]).
main(_) ->
io:format("Start erlide server. ...Not implemented yet... ~n"),
main(Args) ->
io:format("Start erlide server. ~p~n", [Args]),
application:set_env(erlide_ide, main_args, Args, [{persistent, true}]),
_R = application:ensure_all_started(erlide_ide),
receive stop -> ok end,
ok.
start(_Type, _StartArgs) ->
io:format("Start app ~n"),
io:format("Start app ~p~n", [application:get_all_env()]),
erlide_ide_sup:start_link().
stop(_State) ->
Oops, something went wrong.

0 comments on commit 41ac5ea

Please sign in to comment.