From 65c64bd1e27471912b758c1267df27c6dfd91440 Mon Sep 17 00:00:00 2001 From: Juan Facorro Date: Mon, 16 Sep 2019 20:00:29 +0200 Subject: [PATCH 1/6] Configuration file for LSP server --- src/erlang_ls_buffer_server.erl | 23 +++++++++++++++++++++-- src/erlang_ls_code_navigation.erl | 2 +- src/erlang_ls_server.erl | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/erlang_ls_buffer_server.erl b/src/erlang_ls_buffer_server.erl index 0d35a68d3..88e1c3a80 100644 --- a/src/erlang_ls_buffer_server.erl +++ b/src/erlang_ls_buffer_server.erl @@ -16,8 +16,10 @@ , add_buffer/2 , get_buffer/1 , get_root_uri/0 + , get_otp_path/0 , remove_buffer/1 , set_root_uri/1 + , set_otp_path/1 , stop/0 ]). @@ -43,13 +45,14 @@ %%============================================================================== %% Record Definitions %%============================================================================== --record(state, { buffers, root_uri }). +-record(state, { buffers, root_uri, otp_path }). %%============================================================================== %% Type Definitions %%============================================================================== -type state() :: #state{}. -type buffer() :: pid(). +-type path() :: filename:filename(). %%%============================================================================= %%% API @@ -70,6 +73,10 @@ get_buffer(Uri) -> get_root_uri() -> gen_server:call(?SERVER, {get_root_uri}). +-spec get_otp_path() -> {ok, path() | undefined}. +get_otp_path() -> + gen_server:call(?SERVER, {get_otp_path}). + -spec remove_buffer(uri()) -> ok. remove_buffer(Uri) -> gen_server:call(?SERVER, {remove_buffer, Uri}). @@ -78,6 +85,11 @@ remove_buffer(Uri) -> set_root_uri(Uri) -> gen_server:call(?SERVER, {set_root_uri, Uri}). +%% TODO: move it to a separate config server +-spec set_otp_path(uri()) -> ok. +set_otp_path(Uri) -> + gen_server:call(?SERVER, {set_otp_path, Uri}). + -spec stop() -> ok. stop() -> gen_server:stop(?SERVER). @@ -98,11 +110,18 @@ handle_call({get_buffer, Uri}, _From, State) -> handle_call({get_root_uri}, _From, State) -> RootUri = State#state.root_uri, {reply, {ok, RootUri}, State}; +handle_call({get_otp_path}, _From, State) -> + OtpPath = State#state.otp_path, + {reply, {ok, OtpPath}, State}; handle_call({remove_buffer, Uri}, _From, State) -> Buffers = proplists:delete(Uri, State#state.buffers), {reply, ok, State#state{buffers = Buffers}}; handle_call({set_root_uri, Uri}, _From, State) -> - {reply, ok, State#state{root_uri = Uri}}. + {reply, ok, State#state{root_uri = Uri}}; +handle_call({set_otp_path, Path}, _From, State) -> + {reply, ok, State#state{otp_path = Path}}. + + -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast(_Msg, State) -> {noreply, State}. diff --git a/src/erlang_ls_code_navigation.erl b/src/erlang_ls_code_navigation.erl index d883bfd95..a84ac2028 100644 --- a/src/erlang_ls_code_navigation.erl +++ b/src/erlang_ls_code_navigation.erl @@ -125,7 +125,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) diff --git a/src/erlang_ls_server.erl b/src/erlang_ls_server.erl index 043afa7b0..65f6c99cd 100644 --- a/src/erlang_ls_server.erl +++ b/src/erlang_ls_server.erl @@ -124,7 +124,12 @@ 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, + Config = erlang_config(InitOptions, RootUri), + OtpPath = proplists:get_value(otp_path, Config, code:root_dir()), + ok = erlang_ls_buffer_server:set_otp_path(OtpPath), ok = erlang_ls_buffer_server:set_root_uri(RootUri), Result = #{ capabilities => #{ hoverProvider => false @@ -211,3 +216,14 @@ 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 erlang_config(map(), binary()) -> [term()]. +erlang_config(#{<<"erlang">> := #{<<"config">> := Filename}}, RootUri) -> + RootPath = erlang_ls_uri:path(RootUri), + Path = filename:join([RootPath, Filename]), + lager:info("Reading config file from ~p", [Path]), + %% TODO: check the file exits + {ok, Terms} = file:consult(Path), + Terms; +erlang_config(_, _) -> + []. From f0455f59441bb6ad5d4d5667928e9022e47b4aa0 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 20 Sep 2019 19:23:09 +0200 Subject: [PATCH 2/6] [#60] Use OTP path from server --- src/erlang_ls_buffer_server.erl | 4 +- src/erlang_ls_code_navigation.erl | 10 ++- test/erlang_ls_code_navigation_SUITE.erl | 77 +++++++++--------------- 3 files changed, 34 insertions(+), 57 deletions(-) diff --git a/src/erlang_ls_buffer_server.erl b/src/erlang_ls_buffer_server.erl index 88e1c3a80..6520e4592 100644 --- a/src/erlang_ls_buffer_server.erl +++ b/src/erlang_ls_buffer_server.erl @@ -52,7 +52,7 @@ %%============================================================================== -type state() :: #state{}. -type buffer() :: pid(). --type path() :: filename:filename(). +-type path() :: binary(). %%%============================================================================= %%% API @@ -121,8 +121,6 @@ handle_call({set_root_uri, Uri}, _From, State) -> handle_call({set_otp_path, Path}, _From, State) -> {reply, ok, State#state{otp_path = Path}}. - - -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast(_Msg, State) -> {noreply, State}. diff --git a/src/erlang_ls_code_navigation.erl b/src/erlang_ls_code_navigation.erl index a84ac2028..631af22c5 100644 --- a/src/erlang_ls_code_navigation.erl +++ b/src/erlang_ls_code_navigation.erl @@ -8,9 +8,7 @@ %%============================================================================== %% API --export([ goto_definition/2 - , goto_definition/3 - ]). +-export([ goto_definition/2 ]). -export([ otp_path/0 ]). @@ -27,7 +25,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()]) -> @@ -140,8 +138,8 @@ app_path() -> , filename:join([RootPath, "include"]) ]. --spec full_path() -> [string()]. -full_path() -> +-spec include_path() -> [string()]. +include_path() -> lists:append( [ app_path(), otp_path() ]). %% Look for a definition recursively in a file and its includes. diff --git a/test/erlang_ls_code_navigation_SUITE.erl b/test/erlang_ls_code_navigation_SUITE.erl index 0e89496a0..d36d7341b 100644 --- a/test/erlang_ls_code_navigation_SUITE.erl +++ b/test/erlang_ls_code_navigation_SUITE.erl @@ -59,18 +59,18 @@ suite() -> -spec init_per_suite(config()) -> config(). init_per_suite(Config) -> + {ok, Started} = application:ensure_all_started(erlang_ls), RootDir = code:priv_dir(erlang_ls), AppDir = filename:join([list_to_binary(RootDir), ?TEST_APP]), - OtpPath = erlang_ls_code_navigation:otp_path(), + erlang_ls_buffer_server:set_otp_path(code:root_dir()), + erlang_ls_buffer_server:set_root_uri(erlang_ls_uri:uri(AppDir)), [ {app_dir, AppDir} - , {include_path, [ filename:join([AppDir, "src"]) - , filename:join([AppDir, "include"]) - | OtpPath - ]} + , {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(). @@ -110,8 +110,7 @@ all() -> -spec application_local(config()) -> ok. application_local(Config) -> Thing = #{info => {application, {function_b, 0}}}, - 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(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {24, 0}, to => {24, 10}}, Range), ok. @@ -119,8 +118,7 @@ application_local(Config) -> -spec application_remote(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. @@ -128,8 +126,7 @@ application_remote(Config) -> -spec behaviour(config()) -> ok. behaviour(Config) -> Thing = #{info => {behaviour, 'behaviour_a'}}, - 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(full_path(src, <<"behaviour_a.erl">>, Config), FullName), ?assertEqual(#{from => {0, 1}, to => {0, 1}}, Range), ok. @@ -137,8 +134,7 @@ behaviour(Config) -> -spec export_entry(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {27, 0}, to => {27, 10}}, Range), ok. @@ -146,8 +142,7 @@ export_entry(Config) -> -spec fun_local(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {24, 0}, to => {24, 10}}, Range), ok. @@ -155,8 +150,7 @@ fun_local(Config) -> -spec fun_remote(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. @@ -164,8 +158,7 @@ fun_remote(Config) -> -spec import_entry(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), ?assertEqual(#{from => {4, 0}, to => {4, 2}}, Range), ok. @@ -173,8 +166,7 @@ import_entry(Config) -> -spec include(config()) -> ok. include(Config) -> Thing = #{info => {include, "code_navigation.hrl"}}, - 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(full_path(include, <<"code_navigation.hrl">>, Config), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. @@ -182,8 +174,7 @@ include(Config) -> -spec include_lib(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. @@ -191,8 +182,7 @@ include_lib(Config) -> -spec macro(config()) -> ok. macro(Config) -> Thing = #{info => {macro, 'MACRO_A'}}, - 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(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {17, 0}, to => {17, 0}}, Range), ok. @@ -200,8 +190,7 @@ macro(Config) -> -spec macro_lowercase(config()) -> ok. macro_lowercase(Config) -> Thing = #{info => {macro, 'macro_A'}}, - 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(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {44, 0}, to => {44, 0}}, Range), ok. @@ -209,8 +198,7 @@ macro_lowercase(Config) -> -spec macro_included(config()) -> ok. macro_included(Config) -> Thing = #{info => {macro, 'INCLUDED_MACRO_A'}}, - 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(full_path(include, <<"code_navigation.hrl">>, Config), FullName), ?assertEqual(#{from => {2, 0}, to => {2, 0}}, Range), ok. @@ -218,17 +206,15 @@ macro_included(Config) -> -spec macro_with_args(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), 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. @@ -236,8 +222,7 @@ macro_with_args_included(Config) -> -spec record_access(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {15, 0}, to => {15, 0}}, Range), ok. @@ -245,8 +230,7 @@ record_access(Config) -> -spec record_access_included(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. @@ -256,8 +240,7 @@ record_access_included(Config) -> -spec record_expr(config()) -> ok. record_expr(Config) -> Thing = #{info => {record_expr, "record_a"}}, - 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(full_path(src, <<"code_navigation.erl">>, Config), FullName), ?assertEqual(#{from => {15, 0}, to => {15, 0}}, Range), ok. @@ -265,8 +248,7 @@ record_expr(Config) -> -spec record_expr_included(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), ?assertEqual(#{from => {0, 0}, to => {0, 0}}, Range), ok. @@ -274,8 +256,7 @@ record_expr_included(Config) -> -spec type_application(config()) -> ok. 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), + {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), ?assertEqual( full_path(src, <<"code_navigation.erl">>, Config) , FullName), ?assertEqual(#{from => {36, 1}, to => {36, 1}}, Range), @@ -300,7 +281,7 @@ 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). From 23e51653299aa3a722ab6bc142ef514a16d7aa07 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 20 Sep 2019 19:55:23 +0200 Subject: [PATCH 3/6] [#60] Move uri types definition, configure root_uri in tests --- include/erlang_ls.hrl | 14 ++-- src/erlang_ls_buffer_server.erl | 14 ++-- src/erlang_ls_client.erl | 8 +-- src/erlang_ls_compiler_diagnostics.erl | 2 +- src/erlang_ls_diagnostics.erl | 2 +- src/erlang_ls_dialyzer_diagnostics.erl | 2 +- src/erlang_ls_uri.erl | 14 +++- test/erlang_ls_code_navigation_SUITE.erl | 91 ++++++++++++------------ 8 files changed, 75 insertions(+), 72 deletions(-) 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/src/erlang_ls_buffer_server.erl b/src/erlang_ls_buffer_server.erl index 6520e4592..c459bf5e7 100644 --- a/src/erlang_ls_buffer_server.erl +++ b/src/erlang_ls_buffer_server.erl @@ -52,7 +52,7 @@ %%============================================================================== -type state() :: #state{}. -type buffer() :: pid(). --type path() :: binary(). +-type path() :: file:filename(). %%%============================================================================= %%% API @@ -61,15 +61,15 @@ 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_root_uri() -> {ok, erlang_ls_uri:uri() | undefined}. get_root_uri() -> gen_server:call(?SERVER, {get_root_uri}). @@ -77,16 +77,16 @@ get_root_uri() -> get_otp_path() -> gen_server:call(?SERVER, {get_otp_path}). --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_root_uri(erlang_ls_uri:uri()) -> ok. set_root_uri(Uri) -> gen_server:call(?SERVER, {set_root_uri, Uri}). %% TODO: move it to a separate config server --spec set_otp_path(uri()) -> ok. +-spec set_otp_path(erlang_ls_uri:uri()) -> ok. set_otp_path(Uri) -> gen_server:call(?SERVER, {set_otp_path, Uri}). diff --git a/src/erlang_ls_client.erl b/src/erlang_ls_client.erl index 969461a35..0daa24f05 100644 --- a/src/erlang_ls_client.erl +++ b/src/erlang_ls_client.erl @@ -58,19 +58,19 @@ %%============================================================================== %% 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. +-spec initialize(erlang_ls_uri:uri()) -> ok. initialize(RootUri) -> gen_server:call(?SERVER, {initialize, RootUri}). 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_uri.erl b/src/erlang_ls_uri.erl index c444ab00f..48ec2219c 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_types([ 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 d36d7341b..b97d24b55 100644 --- a/test/erlang_ls_code_navigation_SUITE.erl +++ b/test/erlang_ls_code_navigation_SUITE.erl @@ -60,13 +60,9 @@ suite() -> -spec init_per_suite(config()) -> config(). init_per_suite(Config) -> {ok, Started} = application:ensure_all_started(erlang_ls), - RootDir = code:priv_dir(erlang_ls), - AppDir = filename:join([list_to_binary(RootDir), ?TEST_APP]), erlang_ls_buffer_server:set_otp_path(code:root_dir()), - erlang_ls_buffer_server:set_root_uri(erlang_ls_uri:uri(AppDir)), - [ {app_dir, AppDir} - , {started, Started} - |Config]. + 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) -> @@ -108,106 +104,106 @@ all() -> %% Testcases %%============================================================================== -spec application_local(config()) -> ok. -application_local(Config) -> +application_local(_Config) -> Thing = #{info => {application, {function_b, 0}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + ?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'}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"behaviour_a.erl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation_extra.erl">>, Config), FullName), + ?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"}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + ?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"}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + ?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'}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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'}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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'}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + ?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'}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?assertEqual(full_path(src, <<"code_navigation.erl">>), FullName), ?assertEqual(#{from => {18, 0}, to => {18, 0}}, Range), ok. @@ -220,54 +216,53 @@ macro_with_args_included(_Config) -> ok. -spec record_access(config()) -> ok. -record_access(Config) -> +record_access(_Config) -> Thing = #{info => {record_access, {"record_a", "_X"}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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"}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + ?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"}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(src, <<"code_navigation.erl">>, Config), FullName), + ?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"}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual(full_path(include, <<"code_navigation.hrl">>, Config), FullName), + ?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}}}, {ok, FullName, Range} = goto_def(<<"code_navigation.erl">>, Thing), - ?assertEqual( full_path(src, <<"code_navigation.erl">>, Config) - , FullName), + ?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 ]). @@ -285,3 +280,7 @@ otp_app_path(App, Dir, FileName) -> {ok, binary(), erlang_ls_poi:range()} | {error, any()}. 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]). From ebfdef1c6dcbaed530af599c2db486801c5f810d Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 20 Sep 2019 20:11:43 +0200 Subject: [PATCH 4/6] [#60] Add support for initializationOptions in tests --- src/erlang_ls_client.erl | 26 +++++++++++++++----------- src/erlang_ls_uri.erl | 6 +++--- test/erlang_ls_proper_gen.erl | 3 +++ test/prop_statem.erl | 8 +++++--- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/erlang_ls_client.erl b/src/erlang_ls_client.erl index 0daa24f05..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,9 +51,10 @@ %%============================================================================== %% 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 @@ -70,9 +71,9 @@ did_save(Uri) -> did_close(Uri) -> gen_server:call(?SERVER, {did_close, Uri}). --spec initialize(erlang_ls_uri: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_uri.erl b/src/erlang_ls_uri.erl index 48ec2219c..d803e238a 100644 --- a/src/erlang_ls_uri.erl +++ b/src/erlang_ls_uri.erl @@ -19,9 +19,9 @@ -type path() :: binary(). -type uri() :: binary(). --export_types([ path/0 - , uri/0 - ]). +-export_type([ path/0 + , uri/0 + ]). %%============================================================================== %% Includes 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..d340690d0 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. From cd764731089b9afa95293d8eb055c245e9fbbce9 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 20 Sep 2019 21:36:08 +0200 Subject: [PATCH 5/6] [#60] Add support and docs for a YML erlang_ls.config file --- README.md | 16 ++++++++++++++++ rebar.config | 1 + rebar.lock | 6 ++++-- src/erlang_ls.app.src | 1 + src/erlang_ls_server.erl | 41 +++++++++++++++++++++++++++------------- test/prop_statem.erl | 1 + 6 files changed, 51 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 179158e10..6798fe158 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,22 @@ 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" + +Currently, the following customizations are possible: + +| Parameter | Description | +|-----------|------------------------------| +| otp_path | Path to the OTP installation | +| | | + ## Troubleshooting It is possible to compile and start the language server in _debug mode_: 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_server.erl b/src/erlang_ls_server.erl index 65f6c99cd..b78db24d2 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 %%============================================================================== @@ -127,10 +132,12 @@ handle_method(<<"initialize">>, Params) -> #{ <<"rootUri">> := RootUri , <<"initializationOptions">> := InitOptions } = Params, - Config = erlang_config(InitOptions, RootUri), - OtpPath = proplists:get_value(otp_path, Config, code:root_dir()), - ok = erlang_ls_buffer_server:set_otp_path(OtpPath), 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()), + ok = erlang_ls_buffer_server:set_otp_path(OtpPath), Result = #{ capabilities => #{ hoverProvider => false , completionProvider => @@ -217,13 +224,21 @@ send_notification(Socket, Method, Params) -> lager:debug("[SERVER] Sending notification [notification=~p]", [Notification]), gen_tcp:send(Socket, Notification). --spec erlang_config(map(), binary()) -> [term()]. -erlang_config(#{<<"erlang">> := #{<<"config">> := Filename}}, RootUri) -> - RootPath = erlang_ls_uri:path(RootUri), - Path = filename:join([RootPath, Filename]), - lager:info("Reading config file from ~p", [Path]), - %% TODO: check the file exits - {ok, Terms} = file:consult(Path), - Terms; -erlang_config(_, _) -> - []. +-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()) -> [any()]. +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/test/prop_statem.erl b/test/prop_statem.erl index d340690d0..8c0d2f0f0 100644 --- a/test/prop_statem.erl +++ b/test/prop_statem.erl @@ -185,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. From bf893be7e5c75794d72fccd7e6ceabd86c1e8b57 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 20 Sep 2019 22:49:46 +0200 Subject: [PATCH 6/6] [#60] Add support for deps_dirs --- README.md | 10 ++++--- src/erlang_ls_buffer_server.erl | 50 +++++++++++++++++++++---------- src/erlang_ls_code_navigation.erl | 20 ++++++++++--- src/erlang_ls_server.erl | 9 ++++-- 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6798fe158..f796525ee 100644 --- a/README.md +++ b/README.md @@ -106,13 +106,15 @@ 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 | -| | | +| Parameter | Description | +|------------|---------------------------------------------------------------------| +| otp\_path | Path to the OTP installation | +| deps\_dirs | List of directories containing dependencies. It supports wildcards. | ## Troubleshooting diff --git a/src/erlang_ls_buffer_server.erl b/src/erlang_ls_buffer_server.erl index c459bf5e7..4c9be6ff4 100644 --- a/src/erlang_ls_buffer_server.erl +++ b/src/erlang_ls_buffer_server.erl @@ -15,11 +15,13 @@ -export([ start_link/0 , add_buffer/2 , get_buffer/1 - , get_root_uri/0 + , get_deps_dirs/0 , get_otp_path/0 + , get_root_uri/0 , remove_buffer/1 - , set_root_uri/1 + , set_deps_dirs/1 , set_otp_path/1 + , set_root_uri/1 , stop/0 ]). @@ -45,7 +47,11 @@ %%============================================================================== %% Record Definitions %%============================================================================== --record(state, { buffers, root_uri, otp_path }). +-record(state, { buffers + , deps_dirs = [] + , root_uri + , otp_path + }). %%============================================================================== %% Type Definitions @@ -69,27 +75,34 @@ add_buffer(Uri, Buffer) -> get_buffer(Uri) -> gen_server:call(?SERVER, {get_buffer, Uri}). --spec get_root_uri() -> {ok, erlang_ls_uri:uri() | undefined}. -get_root_uri() -> - gen_server:call(?SERVER, {get_root_uri}). +-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(erlang_ls_uri:uri()) -> ok. remove_buffer(Uri) -> gen_server:call(?SERVER, {remove_buffer, Uri}). --spec set_root_uri(erlang_ls_uri:uri()) -> ok. -set_root_uri(Uri) -> - gen_server:call(?SERVER, {set_root_uri, Uri}). +-spec set_deps_dirs([erlang_ls_uri:path()]) -> ok. +set_deps_dirs(DepsDirs) -> + gen_server:call(?SERVER, {set_deps_dirs, DepsDirs}). -%% TODO: move it to a separate config server -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}). + -spec stop() -> ok. stop() -> gen_server:stop(?SERVER). @@ -107,19 +120,24 @@ 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_root_uri}, _From, State) -> - RootUri = State#state.root_uri, - {reply, {ok, RootUri}, 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_root_uri, Uri}, _From, State) -> - {reply, ok, State#state{root_uri = Uri}}; +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}}. + {reply, ok, State#state{otp_path = Path}}; +handle_call({set_root_uri, Uri}, _From, State) -> + {reply, ok, State#state{root_uri = Uri}}. -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast(_Msg, State) -> {noreply, State}. diff --git a/src/erlang_ls_code_navigation.erl b/src/erlang_ls_code_navigation.erl index 631af22c5..99839f259 100644 --- a/src/erlang_ls_code_navigation.erl +++ b/src/erlang_ls_code_navigation.erl @@ -10,9 +10,6 @@ %% API -export([ goto_definition/2 ]). --export([ otp_path/0 - ]). - %%============================================================================== %% Includes %%============================================================================== @@ -138,9 +135,24 @@ app_path() -> , filename:join([RootPath, "include"]) ]. +-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() ]). + 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_server.erl b/src/erlang_ls_server.erl index b78db24d2..13b28a4fd 100644 --- a/src/erlang_ls_server.erl +++ b/src/erlang_ls_server.erl @@ -136,8 +136,10 @@ handle_method(<<"initialize">>, Params) -> Config = consult_config(filename:join([ erlang_ls_uri:path(RootUri) , config_path(InitOptions) ])), - OtpPath = maps:get(<<"otp_path">>, Config, code:root_dir()), + 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 => @@ -230,12 +232,13 @@ config_path(#{<<"erlang">> := #{<<"config_path">> := ConfigPath}}) -> config_path(_) -> ?DEFAULT_CONFIG_PATH. --spec consult_config(erlang_ls_uri:path()) -> [any()]. +-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 + [] -> #{}; + [Config] -> Config catch Class:Error -> lager:warning( "Error reading config file. path=~p class=~p error=~p"