diff --git a/README.md b/README.md index 179158e10..f796525ee 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,24 @@ Code completion is available for the following elements: * Atom names * Module attributes +## Customization + +It is possible to customize the behaviour of the `erlang_ls` server by +adding a `erlang_ls.config` file to the root of your projects. + +A sample `erlang_ls.config` file would look like the following: + + otp_path: "/path/to/otp/lib/erlang" + deps_dirs: + - "lib/*" + +Currently, the following customizations are possible: + +| Parameter | Description | +|------------|---------------------------------------------------------------------| +| otp\_path | Path to the OTP installation | +| deps\_dirs | List of directories containing dependencies. It supports wildcards. | + ## Troubleshooting It is possible to compile and start the language server in _debug mode_: diff --git a/include/erlang_ls.hrl b/include/erlang_ls.hrl index b8154cfb9..a50a799a9 100644 --- a/include/erlang_ls.hrl +++ b/include/erlang_ls.hrl @@ -71,12 +71,6 @@ %% Language Server Protocol %%============================================================================== -%%------------------------------------------------------------------------------ -%% URI -%%------------------------------------------------------------------------------ --type uri() :: binary(). --type uri_path() :: binary(). - %%------------------------------------------------------------------------------ %% Position %%------------------------------------------------------------------------------ @@ -156,12 +150,12 @@ %%------------------------------------------------------------------------------ %% Text Document Identifier %%------------------------------------------------------------------------------ --type text_document_id() :: #{ uri := uri() }. +-type text_document_id() :: #{ uri := erlang_ls_uri:uri() }. %%------------------------------------------------------------------------------ %% Text Document Item %%------------------------------------------------------------------------------ --type text_document_item() :: #{ uri := uri() +-type text_document_item() :: #{ uri := erlang_ls_uri:uri() , languageId := binary() , version := number() , text := binary() @@ -210,7 +204,7 @@ %%------------------------------------------------------------------------------ %% Initialize Request %%------------------------------------------------------------------------------ --type workspace_folder() :: #{ uri => uri() +-type workspace_folder() :: #{ uri => erlang_ls_uri:uri() , name => binary() }. @@ -225,7 +219,7 @@ -type initialize_params() :: #{ processId := number() | null , rootPath => binary() | null - , rootUri := uri() | null + , rootUri := erlang_ls_uri:uri() | null , initializationOptions => any() , capabilities := client_capabilities() , trace => off diff --git a/rebar.config b/rebar.config index 96721165a..df3786082 100644 --- a/rebar.config +++ b/rebar.config @@ -12,6 +12,7 @@ , {cowlib, "2.3.0"} , {redbug, "1.2.1"} , {lager, "3.6.8"} + , {yamerl, "0.7.0"} ] }. diff --git a/rebar.lock b/rebar.lock index 1a71dd0ef..d663561ed 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,7 +4,8 @@ {<<"jsx">>,{pkg,<<"jsx">>,<<"2.9.0">>},0}, {<<"lager">>,{pkg,<<"lager">>,<<"3.6.8">>},0}, {<<"ranch">>,{pkg,<<"ranch">>,<<"1.5.0">>},0}, - {<<"redbug">>,{pkg,<<"redbug">>,<<"1.2.1">>},0}]}. + {<<"redbug">>,{pkg,<<"redbug">>,<<"1.2.1">>},0}, + {<<"yamerl">>,{pkg,<<"yamerl">>,<<"0.7.0">>},0}]}. [ {pkg_hash,[ {<<"cowlib">>, <<"BBD58EF537904E4F7C1DD62E6AA8BC831C8183CE4EFA9BD1150164FE15BE4CAA">>}, @@ -12,5 +13,6 @@ {<<"jsx">>, <<"D2F6E5F069C00266CAD52FB15D87C428579EA4D7D73A33669E12679E203329DD">>}, {<<"lager">>, <<"897EFC7679BB82383448646C96768CDC4E747464DD18B999C7AACA485686B0DA">>}, {<<"ranch">>, <<"F04166F456790FEE2AC1AA05A02745CC75783C2BFB26D39FAF6AEFC9A3D3A58A">>}, - {<<"redbug">>, <<"9153EE50E42C39CE3F6EFA65EE746F4A52896DA66862CFB59E7C0F838B7B8414">>}]} + {<<"redbug">>, <<"9153EE50E42C39CE3F6EFA65EE746F4A52896DA66862CFB59E7C0F838B7B8414">>}, + {<<"yamerl">>, <<"E51DBA652DCE74C20A88294130B48051EBBBB0BE7D76F22DE064F0F3CCF0AAF5">>}]} ]. diff --git a/src/erlang_ls.app.src b/src/erlang_ls.app.src index f7092e4ad..1d84104a4 100644 --- a/src/erlang_ls.app.src +++ b/src/erlang_ls.app.src @@ -11,6 +11,7 @@ , jsx , cowlib , lager + , yamerl ]}, {env,[]}, {modules, []}, diff --git a/src/erlang_ls_buffer_server.erl b/src/erlang_ls_buffer_server.erl index 0d35a68d3..4c9be6ff4 100644 --- a/src/erlang_ls_buffer_server.erl +++ b/src/erlang_ls_buffer_server.erl @@ -15,8 +15,12 @@ -export([ start_link/0 , add_buffer/2 , get_buffer/1 + , get_deps_dirs/0 + , get_otp_path/0 , get_root_uri/0 , remove_buffer/1 + , set_deps_dirs/1 + , set_otp_path/1 , set_root_uri/1 , stop/0 ]). @@ -43,13 +47,18 @@ %%============================================================================== %% Record Definitions %%============================================================================== --record(state, { buffers, root_uri }). +-record(state, { buffers + , deps_dirs = [] + , root_uri + , otp_path + }). %%============================================================================== %% Type Definitions %%============================================================================== -type state() :: #state{}. -type buffer() :: pid(). +-type path() :: file:filename(). %%%============================================================================= %%% API @@ -58,23 +67,39 @@ start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, {}, []). --spec add_buffer(uri(), buffer()) -> ok. +-spec add_buffer(erlang_ls_uri:uri(), buffer()) -> ok. add_buffer(Uri, Buffer) -> gen_server:call(?SERVER, {add_buffer, Uri, Buffer}). --spec get_buffer(uri()) -> {ok, buffer() | undefined}. +-spec get_buffer(erlang_ls_uri:uri()) -> {ok, buffer() | undefined}. get_buffer(Uri) -> gen_server:call(?SERVER, {get_buffer, Uri}). --spec get_root_uri() -> {ok, uri() | undefined}. +-spec get_deps_dirs() -> {ok, [path()]}. +get_deps_dirs() -> + gen_server:call(?SERVER, {get_deps_dirs}). + +-spec get_otp_path() -> {ok, path() | undefined}. +get_otp_path() -> + gen_server:call(?SERVER, {get_otp_path}). + +-spec get_root_uri() -> {ok, erlang_ls_uri:uri() | undefined}. get_root_uri() -> gen_server:call(?SERVER, {get_root_uri}). --spec remove_buffer(uri()) -> ok. +-spec remove_buffer(erlang_ls_uri:uri()) -> ok. remove_buffer(Uri) -> gen_server:call(?SERVER, {remove_buffer, Uri}). --spec set_root_uri(uri()) -> ok. +-spec set_deps_dirs([erlang_ls_uri:path()]) -> ok. +set_deps_dirs(DepsDirs) -> + gen_server:call(?SERVER, {set_deps_dirs, DepsDirs}). + +-spec set_otp_path(erlang_ls_uri:uri()) -> ok. +set_otp_path(Uri) -> + gen_server:call(?SERVER, {set_otp_path, Uri}). + +-spec set_root_uri(erlang_ls_uri:uri()) -> ok. set_root_uri(Uri) -> gen_server:call(?SERVER, {set_root_uri, Uri}). @@ -95,12 +120,22 @@ handle_call({add_buffer, Uri, Buffer}, _From, State) -> handle_call({get_buffer, Uri}, _From, State) -> Buffer = proplists:get_value(Uri, State#state.buffers), {reply, {ok, Buffer}, State}; +handle_call({get_deps_dirs}, _From, State) -> + DepsDirs = State#state.deps_dirs, + {reply, {ok, DepsDirs}, State}; +handle_call({get_otp_path}, _From, State) -> + OtpPath = State#state.otp_path, + {reply, {ok, OtpPath}, State}; handle_call({get_root_uri}, _From, State) -> RootUri = State#state.root_uri, {reply, {ok, RootUri}, State}; handle_call({remove_buffer, Uri}, _From, State) -> Buffers = proplists:delete(Uri, State#state.buffers), {reply, ok, State#state{buffers = Buffers}}; +handle_call({set_deps_dirs, DepsDirs}, _From, State) -> + {reply, ok, State#state{deps_dirs = DepsDirs}}; +handle_call({set_otp_path, Path}, _From, State) -> + {reply, ok, State#state{otp_path = Path}}; handle_call({set_root_uri, Uri}, _From, State) -> {reply, ok, State#state{root_uri = Uri}}. diff --git a/src/erlang_ls_client.erl b/src/erlang_ls_client.erl index 969461a35..de3ecdff6 100644 --- a/src/erlang_ls_client.erl +++ b/src/erlang_ls_client.erl @@ -15,7 +15,7 @@ -export([ did_open/4 , did_save/1 , did_close/1 - , initialize/1 + , initialize/2 , start_link/2 , stop/0 ]). @@ -51,28 +51,29 @@ %%============================================================================== %% Type Definitions %%============================================================================== --type state() :: #state{}. --type hostname() :: tuple(). --type port_no() :: pos_integer(). +-type state() :: #state{}. +-type hostname() :: tuple(). +-type port_no() :: pos_integer(). +-type init_options() :: []. %%============================================================================== %% API %%============================================================================== --spec did_open(uri(), binary(), number(), binary()) -> ok. +-spec did_open(erlang_ls_uri:uri(), binary(), number(), binary()) -> ok. did_open(Uri, LanguageId, Version, Text) -> gen_server:call(?SERVER, {did_open, Uri, LanguageId, Version, Text}). --spec did_save(uri()) -> ok. +-spec did_save(erlang_ls_uri:uri()) -> ok. did_save(Uri) -> gen_server:call(?SERVER, {did_save, Uri}). --spec did_close(uri()) -> ok. +-spec did_close(erlang_ls_uri:uri()) -> ok. did_close(Uri) -> gen_server:call(?SERVER, {did_close, Uri}). --spec initialize(uri()) -> ok. -initialize(RootUri) -> - gen_server:call(?SERVER, {initialize, RootUri}). +-spec initialize(erlang_ls_uri:uri(), init_options()) -> ok. +initialize(RootUri, InitOptions) -> + gen_server:call(?SERVER, {initialize, RootUri, InitOptions}). -spec start_link(hostname(), port_no()) -> {ok, pid()}. start_link(Host, Port) -> @@ -117,11 +118,14 @@ handle_call({did_close, Uri}, _From, State) -> Content = erlang_ls_protocol:notification(Method, Params), ok = gen_tcp:send(State#state.socket, Content), {reply, ok, State}; -handle_call({initialize, RootUri}, From, #state{ request_id = RequestId - , socket = Socket - } = State) -> +handle_call({initialize, RootUri, InitOptions}, From, State) -> + #state{ request_id = RequestId + , socket = Socket + } = State, Method = <<"initialize">>, - Params = #{ <<"rootUri">> => RootUri }, + Params = #{ <<"rootUri">> => RootUri + , <<"initializationOptions">> => InitOptions + }, Content = erlang_ls_protocol:request(RequestId, Method, Params), gen_tcp:send(Socket, Content), {noreply, State#state{ request_id = RequestId + 1 diff --git a/src/erlang_ls_code_navigation.erl b/src/erlang_ls_code_navigation.erl index d883bfd95..99839f259 100644 --- a/src/erlang_ls_code_navigation.erl +++ b/src/erlang_ls_code_navigation.erl @@ -8,12 +8,7 @@ %%============================================================================== %% API --export([ goto_definition/2 - , goto_definition/3 - ]). - --export([ otp_path/0 - ]). +-export([ goto_definition/2 ]). %%============================================================================== %% Includes @@ -27,7 +22,7 @@ -spec goto_definition(binary(), erlang_ls_poi:poi()) -> {ok, binary(), erlang_ls_poi:range()} | {error, any()}. goto_definition(Filename, POI) -> - goto_definition(Filename, POI, full_path()). + goto_definition(Filename, POI, include_path()). %% TODO: Abstract pattern -spec goto_definition(binary(), erlang_ls_poi:poi(), [string()]) -> @@ -125,7 +120,7 @@ definition({type_application, {Type, _}}) -> -spec otp_path() -> [string()]. otp_path() -> - Root = code:root_dir(), + {ok, Root} = erlang_ls_buffer_server:get_otp_path(), Sources = filename:join([Root, "lib", "*", "src"]), Includes = filename:join([Root, "lib", "*", "include"]), lists:append([ filelib:wildcard(Sources) @@ -140,9 +135,24 @@ app_path() -> , filename:join([RootPath, "include"]) ]. --spec full_path() -> [string()]. -full_path() -> - lists:append( [ app_path(), otp_path() ]). +-spec deps_path() -> [string()]. +deps_path() -> + {ok, RootUri} = erlang_ls_buffer_server:get_root_uri(), + RootPath = binary_to_list(erlang_ls_uri:path(RootUri)), + {ok, Dirs} = erlang_ls_buffer_server:get_deps_dirs(), + lists:foldl(fun(Dir, Acc) -> + Sources = filename:join([RootPath, Dir, "src"]), + Includes = filename:join([RootPath, Dir, "include"]), + lists:append([ filelib:wildcard(Sources) + , filelib:wildcard(Includes) + , Acc + ]) + end + , [], Dirs). + +-spec include_path() -> [string()]. +include_path() -> + lists:append( [ app_path(), otp_path(), deps_path() ]). %% Look for a definition recursively in a file and its includes. -spec search(binary(), [string()], any()) -> diff --git a/src/erlang_ls_compiler_diagnostics.erl b/src/erlang_ls_compiler_diagnostics.erl index 71ae43e58..59999813a 100644 --- a/src/erlang_ls_compiler_diagnostics.erl +++ b/src/erlang_ls_compiler_diagnostics.erl @@ -32,7 +32,7 @@ %%============================================================================== %% Callback Functions %%============================================================================== --spec diagnostics(uri()) -> [diagnostic()]. +-spec diagnostics(erlang_ls_uri:uri()) -> [diagnostic()]. diagnostics(Uri) -> Path = erlang_ls_uri:path(Uri), case compile:file(binary_to_list(Path), ?COMPILER_OPTS) of diff --git a/src/erlang_ls_diagnostics.erl b/src/erlang_ls_diagnostics.erl index a74cdb6d0..3678e503d 100644 --- a/src/erlang_ls_diagnostics.erl +++ b/src/erlang_ls_diagnostics.erl @@ -11,4 +11,4 @@ %%============================================================================== %% Callback Functions Definitions %%============================================================================== --callback diagnostics(uri()) -> [diagnostic()]. +-callback diagnostics(erlang_ls_uri:uri()) -> [diagnostic()]. diff --git a/src/erlang_ls_dialyzer_diagnostics.erl b/src/erlang_ls_dialyzer_diagnostics.erl index 6db0abf32..9e772a4ae 100644 --- a/src/erlang_ls_dialyzer_diagnostics.erl +++ b/src/erlang_ls_dialyzer_diagnostics.erl @@ -21,7 +21,7 @@ %%============================================================================== %% Callback Functions %%============================================================================== --spec diagnostics(uri()) -> [diagnostic()]. +-spec diagnostics(erlang_ls_uri:uri()) -> [diagnostic()]. diagnostics(Uri) -> Path = erlang_ls_uri:path(Uri), WS = try dialyzer:run([{files, [binary_to_list(Path)]}, {from, src_code}]) diff --git a/src/erlang_ls_server.erl b/src/erlang_ls_server.erl index 043afa7b0..13b28a4fd 100644 --- a/src/erlang_ls_server.erl +++ b/src/erlang_ls_server.erl @@ -41,6 +41,11 @@ %%============================================================================== -type state() :: #state{}. +%%============================================================================== +%% Macros +%%============================================================================== +-define(DEFAULT_CONFIG_PATH, "erlang_ls.config"). + %%============================================================================== %% ranch_protocol callbacks %%============================================================================== @@ -124,8 +129,17 @@ handle_request(Socket, Request) -> -spec handle_method(binary(), map()) -> {response, map()} | {} | {notification, binary(), map()}. handle_method(<<"initialize">>, Params) -> - RootUri = maps:get(<<"rootUri">>, Params), + #{ <<"rootUri">> := RootUri + , <<"initializationOptions">> := InitOptions + } = Params, ok = erlang_ls_buffer_server:set_root_uri(RootUri), + Config = consult_config(filename:join([ erlang_ls_uri:path(RootUri) + , config_path(InitOptions) + ])), + OtpPath = maps:get("otp_path", Config, code:root_dir()), + DepsDirs = maps:get("deps_dirs", Config, []), + ok = erlang_ls_buffer_server:set_otp_path(OtpPath), + ok = erlang_ls_buffer_server:set_deps_dirs(DepsDirs), Result = #{ capabilities => #{ hoverProvider => false , completionProvider => @@ -211,3 +225,23 @@ send_notification(Socket, Method, Params) -> Notification = erlang_ls_protocol:notification(Method, Params), lager:debug("[SERVER] Sending notification [notification=~p]", [Notification]), gen_tcp:send(Socket, Notification). + +-spec config_path(map()) -> erlang_ls_uri:path(). +config_path(#{<<"erlang">> := #{<<"config_path">> := ConfigPath}}) -> + ConfigPath; +config_path(_) -> + ?DEFAULT_CONFIG_PATH. + +-spec consult_config(erlang_ls_uri:path()) -> map(). +consult_config(Path) -> + lager:info("Reading config file. path=~p", [Path]), + Options = [{map_node_format, map}], + try yamerl:decode_file(Path, Options) of + [] -> #{}; + [Config] -> Config + catch + Class:Error -> + lager:warning( "Error reading config file. path=~p class=~p error=~p" + , [Path, Class, Error]), + #{} + end. diff --git a/src/erlang_ls_uri.erl b/src/erlang_ls_uri.erl index c444ab00f..d803e238a 100644 --- a/src/erlang_ls_uri.erl +++ b/src/erlang_ls_uri.erl @@ -13,6 +13,16 @@ , uri/1 ]). +%%============================================================================== +%% Types +%%============================================================================== +-type path() :: binary(). +-type uri() :: binary(). + +-export_type([ path/0 + , uri/0 + ]). + %%============================================================================== %% Includes %%============================================================================== @@ -22,10 +32,10 @@ module(Uri) -> binary_to_atom(filename:basename(path(Uri), <<".erl">>), utf8). --spec path(uri()) -> uri_path(). +-spec path(uri()) -> path(). path(<<"file://", Path/binary>>) -> Path. --spec uri(uri_path()) -> uri(). +-spec uri(path()) -> uri(). uri(Path) -> <<"file://", Path/binary>>. diff --git a/test/erlang_ls_code_navigation_SUITE.erl b/test/erlang_ls_code_navigation_SUITE.erl index 0e89496a0..b97d24b55 100644 --- a/test/erlang_ls_code_navigation_SUITE.erl +++ b/test/erlang_ls_code_navigation_SUITE.erl @@ -59,18 +59,14 @@ suite() -> -spec init_per_suite(config()) -> config(). init_per_suite(Config) -> - RootDir = code:priv_dir(erlang_ls), - AppDir = filename:join([list_to_binary(RootDir), ?TEST_APP]), - OtpPath = erlang_ls_code_navigation:otp_path(), - [ {app_dir, AppDir} - , {include_path, [ filename:join([AppDir, "src"]) - , filename:join([AppDir, "include"]) - | OtpPath - ]} - |Config]. + {ok, Started} = application:ensure_all_started(erlang_ls), + erlang_ls_buffer_server:set_otp_path(code:root_dir()), + erlang_ls_buffer_server:set_root_uri(erlang_ls_uri:uri(root_path())), + [{started, Started}|Config]. -spec end_per_suite(config()) -> ok. -end_per_suite(_Config) -> +end_per_suite(Config) -> + [application:stop(App) || App <- ?config(started, Config)], ok. -spec init_per_testcase(atom(), config()) -> config(). @@ -108,185 +104,165 @@ all() -> %% Testcases %%============================================================================== -spec application_local(config()) -> ok. -application_local(Config) -> +application_local(_Config) -> Thing = #{info => {application, {function_b, 0}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {24, 0}, to => {24, 10}}, Range), ok. -spec application_remote(config()) -> ok. -application_remote(Config) -> +application_remote(_Config) -> Thing = #{info => {application, {code_navigation_extra, do, 1}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. -spec behaviour(config()) -> ok. -behaviour(Config) -> +behaviour(_Config) -> Thing = #{info => {behaviour, 'behaviour_a'}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"behaviour_a.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"behaviour_a.erl">>), FullName), ?assertEqual(#{from => {0, 1}, to => {0, 1}}, Range), ok. -spec export_entry(config()) -> ok. -export_entry(Config) -> +export_entry(_Config) -> Thing = #{info => {exports_entry, {callback_a, 0}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {27, 0}, to => {27, 10}}, Range), ok. -spec fun_local(config()) -> ok. -fun_local(Config) -> +fun_local(_Config) -> Thing = #{info => {implicit_fun, {function_b, 0}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {24, 0}, to => {24, 10}}, Range), ok. -spec fun_remote(config()) -> ok. -fun_remote(Config) -> +fun_remote(_Config) -> Thing = #{info => {implicit_fun, {code_navigation_extra, do, 1}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. -spec import_entry(config()) -> ok. -import_entry(Config) -> +import_entry(_Config) -> Thing = #{info => {import_entry, {code_navigation_extra, do, 1}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. -spec include(config()) -> ok. -include(Config) -> +include(_Config) -> Thing = #{info => {include, "code_navigation.hrl"}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(include, <<"code_navigation.hrl">>), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. -spec include_lib(config()) -> ok. -include_lib(Config) -> +include_lib(_Config) -> Thing = #{info => {include_lib, "code_navigation/include/code_navigation.hrl"}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(include, <<"code_navigation.hrl">>), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. -spec macro(config()) -> ok. -macro(Config) -> +macro(_Config) -> Thing = #{info => {macro, 'MACRO_A'}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {17, 0}, to => {17, 0}}, Range), ok. -spec macro_lowercase(config()) -> ok. -macro_lowercase(Config) -> +macro_lowercase(_Config) -> Thing = #{info => {macro, 'macro_A'}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {44, 0}, to => {44, 0}}, Range), ok. -spec macro_included(config()) -> ok. -macro_included(Config) -> +macro_included(_Config) -> Thing = #{info => {macro, 'INCLUDED_MACRO_A'}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(include, <<"code_navigation.hrl">>), FullName), ?assertEqual(#{from => {2, 0}, to => {2, 0}}, Range), ok. -spec macro_with_args(config()) -> ok. -macro_with_args(Config) -> +macro_with_args(_Config) -> Thing = #{info => {macro, 'MACRO_WITH_ARGS'}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {18, 0}, to => {18, 0}}, Range), ok. -spec macro_with_args_included(config()) -> ok. -macro_with_args_included(Config) -> +macro_with_args_included(_Config) -> Thing = #{info => {macro, 'assertEqual'}}, - Path = ?config(include_path, Config), - {ok, FullName, _Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), + {ok, FullName, _Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(otp_app_path("stdlib", "include", "assert.hrl"), FullName), %% Do not assert on line number to avoid binding to a specific OTP version ok. -spec record_access(config()) -> ok. -record_access(Config) -> +record_access(_Config) -> Thing = #{info => {record_access, {"record_a", "_X"}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {15, 0}, to => {15, 0}}, Range), ok. -spec record_access_included(config()) -> ok. -record_access_included(Config) -> +record_access_included(_Config) -> Thing = #{info => {record_access, {"included_record_a", "_X"}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(include, <<"code_navigation.hrl">>), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. %% TODO: Additional constructors for POI %% TODO: Navigation should return POI, not range -spec record_expr(config()) -> ok. -record_expr(Config) -> +record_expr(_Config) -> Thing = #{info => {record_expr, "record_a"}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {15, 0}, to => {15, 0}}, Range), ok. -spec record_expr_included(config()) -> ok. -record_expr_included(Config) -> +record_expr_included(_Config) -> Thing = #{info => {record_expr, "included_record_a"}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual(full_path(include, <<"code_navigation.hrl">>), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. -spec type_application(config()) -> ok. -type_application(Config) -> +type_application(_Config) -> Thing = #{info => {type_application, {'type_a', undefined}}}, - Path = ?config(include_path, Config), - {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing, Path), - ?assertEqual( full_path(src, <<"code_navigation.erl">>, Config) - , FullName), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), + ?assertEqual( full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {36, 1}, to => {36, 1}}, Range), ok. %%============================================================================== %% Internal Functions %%============================================================================== --spec full_path(binary(), binary(), config()) -> binary(). -full_path(Dir, FileName, Config) -> - filename:join([ ?config(app_dir, Config) +-spec full_path(binary(), binary()) -> binary(). +full_path(Dir, FileName) -> + filename:join([ root_path() , atom_to_binary(Dir, utf8) , FileName ]). @@ -300,7 +276,11 @@ otp_app_path(App, Dir, FileName) -> , FileName ]))). --spec goto_def(binary(), erlang_ls_poi:poi(), [string()]) -> +-spec goto_def(binary(), erlang_ls_poi:poi()) -> {ok, binary(), erlang_ls_poi:range()} | {error, any()}. -goto_def(FileName, Thing, Path) -> - erlang_ls_code_navigation:goto_definition(FileName, Thing, Path). +goto_def(FileName, Thing) -> + erlang_ls_code_navigation:goto_definition(FileName, Thing). + +-spec root_path() -> file:filename(). +root_path() -> + filename:join([list_to_binary(code:priv_dir(erlang_ls)), ?TEST_APP]). diff --git a/test/erlang_ls_proper_gen.erl b/test/erlang_ls_proper_gen.erl index b240f5c5a..2ca76aaa0 100644 --- a/test/erlang_ls_proper_gen.erl +++ b/test/erlang_ls_proper_gen.erl @@ -25,5 +25,8 @@ uri() -> root_uri() -> <<"file:///tmp">>. +init_options() -> + []. + buffer() -> elements([<<"a">>, <<"b">>, <<"c">>]). diff --git a/test/prop_statem.erl b/test/prop_statem.erl index ba6dc3616..8c0d2f0f0 100644 --- a/test/prop_statem.erl +++ b/test/prop_statem.erl @@ -62,11 +62,13 @@ connect_post(_S, _Args, Res) -> %%------------------------------------------------------------------------------ %% Initialize %%------------------------------------------------------------------------------ -initialize(RootUri) -> - erlang_ls_client:initialize(RootUri). +initialize(RootUri, InitOptions) -> + erlang_ls_client:initialize(RootUri, InitOptions). initialize_args(_S) -> - [erlang_ls_proper_gen:root_uri()]. + [ erlang_ls_proper_gen:root_uri() + , erlang_ls_proper_gen:init_options() + ]. initialize_pre(#{connected := Connected} = _S) -> Connected. @@ -183,6 +185,7 @@ setup() -> meck:expect(erlang_ls_compiler_diagnostics, diagnostics, 1, []), meck:expect(erlang_ls_dialyzer_diagnostics, diagnostics, 1, []), application:ensure_all_started(erlang_ls), + file:write_file("/tmp/erlang_ls.config", <<"">>), lager:set_loglevel(lager_console_backend, warning), ok.