-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
1,148 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
|
@@ -26,3 +26,4 @@ TEST-*.* | ||
|
|
||
|
|
||
server/erlide_ide | server/erlide_ide | ||
*.crashdump |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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(#{}, [], [])) | |||
]. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -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}}. | |||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.