Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for didChangeWatchedFiles #1247

Merged
merged 1 commit into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions apps/els_core/include/els_core.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,18 @@
, command => els_command:command()
}.

%%------------------------------------------------------------------------------
%% Workspace
%%------------------------------------------------------------------------------

-define(FILE_CHANGE_TYPE_CREATED, 1).
-define(FILE_CHANGE_TYPE_CHANGED, 2).
-define(FILE_CHANGE_TYPE_DELETED, 3).

-type file_change_type() :: ?FILE_CHANGE_TYPE_CREATED
| ?FILE_CHANGE_TYPE_CHANGED
| ?FILE_CHANGE_TYPE_DELETED.

%%------------------------------------------------------------------------------
%% Internals
%%------------------------------------------------------------------------------
Expand Down
31 changes: 22 additions & 9 deletions apps/els_core/src/els_client.erl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
, definition/3
, did_open/4
, did_save/1
, did_change_watched_files/1
, did_close/1
, document_symbol/1
, exit/0
Expand Down Expand Up @@ -180,6 +181,10 @@ did_open(Uri, LanguageId, Version, Text) ->
did_save(Uri) ->
gen_server:call(?SERVER, {did_save, {Uri}}).

-spec did_change_watched_files([{uri(), file_change_type()}]) -> ok.
did_change_watched_files(Changes) ->
gen_server:call(?SERVER, {did_change_watched_files, {Changes}}).

-spec did_close(uri()) -> ok.
did_close(Uri) ->
gen_server:call(?SERVER, {did_close, {Uri}}).
Expand Down Expand Up @@ -269,13 +274,15 @@ init(#{io_device := IoDevice}) ->
{ok, State}.

-spec handle_call(any(), any(), state()) -> {reply, any(), state()}.
handle_call({Action, Opts}, _From, State) when Action =:= did_save
orelse Action =:= did_close
orelse Action =:= did_open
orelse Action =:= initialized ->
handle_call({Action, Opts}, _From, State) when
Action =:= did_save;
Action =:= did_close;
Action =:= did_open;
Action =:= did_change_watched_files;
Action =:= initialized ->
#state{io_device = IoDevice} = State,
Method = method_lookup(Action),
Params = notification_params(Opts),
Params = notification_params(Action, Opts),
Content = els_protocol:notification(Method, Params),
send(IoDevice, Content),
{reply, ok, State};
Expand Down Expand Up @@ -422,6 +429,8 @@ method_lookup(callhierarchy_incomingcalls) -> <<"callHierarchy/incomingCalls">>;
method_lookup(callhierarchy_outgoingcalls) -> <<"callHierarchy/outgoingCalls">>;
method_lookup(workspace_symbol) -> <<"workspace/symbol">>;
method_lookup(workspace_executecommand) -> <<"workspace/executeCommand">>;
method_lookup(did_change_watched_files) ->
<<"workspace/didChangeWatchedFiles">>;
method_lookup(initialize) -> <<"initialize">>;
method_lookup(initialized) -> <<"initialized">>.

Expand Down Expand Up @@ -495,18 +504,22 @@ request_params({_Action, {Uri, Line, Char}}) ->
}
}.

-spec notification_params(tuple()) -> map().
notification_params({Uri}) ->
-spec notification_params(atom(), tuple()) -> map().
notification_params(did_change_watched_files, {Changes}) ->
#{changes => [#{ uri => Uri
, type => Type
} || {Uri, Type} <- Changes]};
notification_params(_Action, {Uri}) ->
TextDocument = #{ uri => Uri },
#{textDocument => TextDocument};
notification_params({Uri, LanguageId, Version, Text}) ->
notification_params(_Action, {Uri, LanguageId, Version, Text}) ->
TextDocument = #{ uri => Uri
, languageId => LanguageId
, version => Version
, text => Text
},
#{textDocument => TextDocument};
notification_params({}) ->
notification_params(_Action, {}) ->
#{}.

-spec is_notification(map()) -> boolean().
Expand Down
6 changes: 6 additions & 0 deletions apps/els_lsp/priv/code_navigation/src/watched_file_a.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(watched_file_a).

-export([ main/0 ]).

main() ->
ok.
6 changes: 6 additions & 0 deletions apps/els_lsp/priv/code_navigation/src/watched_file_b.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(watched_file_b).

-export([ main/0 ]).

main() ->
watched_file_a:main().
5 changes: 5 additions & 0 deletions apps/els_lsp/src/els_dt_document.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

-export([ insert/1
, lookup/1
, delete/1
]).

-export([ new/2
Expand Down Expand Up @@ -125,6 +126,10 @@ lookup(Uri) ->
{ok, Items} = els_db:lookup(name(), Uri),
{ok, [to_item(Item) || Item <- Items]}.

-spec delete(uri()) -> ok.
delete(Uri) ->
els_db:delete(name(), Uri).

-spec new(uri(), binary()) -> item().
new(Uri, Text) ->
Extension = filename:extension(Uri),
Expand Down
8 changes: 7 additions & 1 deletion apps/els_lsp/src/els_dt_document_index.erl
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
%% API
%%==============================================================================

-export([new/3]).
-export([ new/3 ]).

-export([ find_by_kind/1
, insert/1
, lookup/1
, delete_by_uri/1
]).

%%==============================================================================
Expand Down Expand Up @@ -78,6 +79,11 @@ lookup(Id) ->
{ok, Items} = els_db:lookup(name(), Id),
{ok, [to_item(Item) || Item <- Items]}.

-spec delete_by_uri(uri()) -> ok | {error, any()}.
delete_by_uri(Uri) ->
Pattern = #els_dt_document_index{uri = Uri, _ = '_'},
ok = els_db:match_delete(name(), Pattern).

-spec find_by_kind(els_dt_document:kind()) -> {ok, [item()]}.
find_by_kind(Kind) ->
Pattern = #els_dt_document_index{kind = Kind, _ = '_'},
Expand Down
10 changes: 8 additions & 2 deletions apps/els_lsp/src/els_dt_signatures.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

-export([ insert/1
, lookup/1
, delete_by_module/1
]).

%%==============================================================================
Expand All @@ -30,8 +31,8 @@
%% Item Definition
%%==============================================================================

-record(els_dt_signatures, { mfa :: mfa() | '_'
, spec :: binary()
-record(els_dt_signatures, { mfa :: mfa() | '_' | {atom(), '_', '_'}
, spec :: binary() | '_'
}).
-type els_dt_signatures() :: #els_dt_signatures{}.

Expand Down Expand Up @@ -74,3 +75,8 @@ insert(Map) when is_map(Map) ->
lookup(MFA) ->
{ok, Items} = els_db:lookup(name(), MFA),
{ok, [to_item(Item) || Item <- Items]}.

-spec delete_by_module(atom()) -> ok.
delete_by_module(Module) ->
Pattern = #els_dt_signatures{mfa = {Module, '_', '_'}, _ = '_'},
ok = els_db:match_delete(name(), Pattern).
5 changes: 2 additions & 3 deletions apps/els_lsp/src/els_methods.erl
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,8 @@ workspace_executecommand(Params, State) ->
%%==============================================================================

-spec workspace_didchangewatchedfiles(map(), state()) -> result().
workspace_didchangewatchedfiles(_Params, State) ->
%% Some clients rely on these notifications to be successful.
%% Let's just ignore them.
workspace_didchangewatchedfiles(Params, State) ->
ok = els_text_synchronization:did_change_watched_files(Params),
{noresponse, State}.

%%==============================================================================
Expand Down
20 changes: 20 additions & 0 deletions apps/els_lsp/src/els_text_synchronization.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
, did_open/1
, did_save/1
, did_close/1
, did_change_watched_files/1
]).

-spec sync_mode() -> text_document_sync_kind().
Expand Down Expand Up @@ -64,6 +65,13 @@ did_save(Params) ->
els_provider:handle_request(Provider, {run_diagnostics, Params}),
ok.

-spec did_change_watched_files(map()) -> ok.
did_change_watched_files(Params) ->
#{<<"changes">> := Changes} = Params,
[handle_file_change(Uri, Type)
|| #{<<"uri">> := Uri, <<"type">> := Type} <- Changes],
ok.

-spec did_close(map()) -> ok.
did_close(_Params) -> ok.

Expand All @@ -75,3 +83,15 @@ to_edit(#{<<"text">> := Text, <<"range">> := Range}) ->
{#{ from => {FromL, FromC}
, to => {ToL, ToC}
}, els_utils:to_list(Text)}.

-spec handle_file_change(uri(), file_change_type()) -> ok.
handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_CREATED;
Type =:= ?FILE_CHANGE_TYPE_CHANGED ->
{ok, Text} = file:read_file(els_uri:path(Uri)),
ok = els_index_buffer:load(Uri, Text),
ok = els_index_buffer:flush(Uri);
handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_DELETED ->
ok = els_dt_document:delete(Uri),
ok = els_dt_document_index:delete_by_uri(Uri),
ok = els_dt_references:delete_by_uri(Uri),
ok = els_dt_signatures:delete_by_module(els_uri:module(Uri)).
90 changes: 90 additions & 0 deletions apps/els_lsp/test/els_references_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,17 @@
, type_local/1
, type_remote/1
, type_included/1
, refresh_after_watched_file_deleted/1
, refresh_after_watched_file_changed/1
, refresh_after_watched_file_added/1
]).

%%==============================================================================
%% Includes
%%==============================================================================
-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/assert.hrl").
-include_lib("els_core/include/els_core.hrl").

%%==============================================================================
%% Types
Expand All @@ -64,10 +68,21 @@ end_per_suite(Config) ->
els_test_utils:end_per_suite(Config).

-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(TestCase, Config0)
when TestCase =:= refresh_after_watched_file_changed ->
Config = els_test_utils:init_per_testcase(TestCase, Config0),
PathB = ?config(watched_file_b_path, Config),
{ok, OldContent} = file:read_file(PathB),
[{old_content, OldContent}|Config];
init_per_testcase(TestCase, Config) ->
els_test_utils:init_per_testcase(TestCase, Config).

-spec end_per_testcase(atom(), config()) -> ok.
end_per_testcase(TestCase, Config)
when TestCase =:= refresh_after_watched_file_changed ->
PathB = ?config(watched_file_b_path, Config),
ok = file:write_file(PathB, ?config(old_content, Config)),
els_test_utils:end_per_testcase(TestCase, Config);
end_per_testcase(TestCase, Config) ->
els_test_utils:end_per_testcase(TestCase, Config).

Expand Down Expand Up @@ -487,6 +502,81 @@ type_included(Config) ->
assert_locations(Locations, ExpectedLocations),
ok.

-spec refresh_after_watched_file_deleted(config()) -> ok.
refresh_after_watched_file_deleted(Config) ->
%% Before
UriA = ?config(watched_file_a_uri, Config),
UriB = ?config(watched_file_b_uri, Config),
ExpectedLocationsBefore = [ #{ uri => UriB
, range => #{from => {6, 3}, to => {6, 22}}
}
],
#{result := LocationsBefore} = els_client:references(UriA, 5, 2),
assert_locations(LocationsBefore, ExpectedLocationsBefore),
%% Delete (Simulate a checkout, rebase or similar)
els_client:did_change_watched_files([{UriB, ?FILE_CHANGE_TYPE_DELETED}]),
%% After
#{result := null} = els_client:references(UriA, 5, 2),
ok.

-spec refresh_after_watched_file_changed(config()) -> ok.
refresh_after_watched_file_changed(Config) ->
%% Before
UriA = ?config(watched_file_a_uri, Config),
UriB = ?config(watched_file_b_uri, Config),
PathB = ?config(watched_file_b_path, Config),
ExpectedLocationsBefore = [ #{ uri => UriB
, range => #{from => {6, 3}, to => {6, 22}}
}
],
#{result := LocationsBefore} = els_client:references(UriA, 5, 2),
assert_locations(LocationsBefore, ExpectedLocationsBefore),
%% Edit (Simulate a checkout, rebase or similar)
NewContent = re:replace(?config(old_content, Config),
"watched_file_a:main()",
"watched_file_a:main(), watched_file_a:main()"),
ok = file:write_file(PathB, NewContent),
els_client:did_change_watched_files([{UriB, ?FILE_CHANGE_TYPE_CHANGED}]),
%% After
ExpectedLocationsAfter = [ #{ uri => UriB
, range => #{from => {6, 3}, to => {6, 22}}
}
, #{ uri => UriB
, range => #{from => {6, 26}, to => {6, 45}}
}
],
#{result := LocationsAfter} = els_client:references(UriA, 5, 2),
assert_locations(LocationsAfter, ExpectedLocationsAfter),
ok.

-spec refresh_after_watched_file_added(config()) -> ok.
refresh_after_watched_file_added(Config) ->
%% Before
UriA = ?config(watched_file_a_uri, Config),
UriB = ?config(watched_file_b_uri, Config),
ExpectedLocationsBefore = [ #{ uri => UriB
, range => #{from => {6, 3}, to => {6, 22}}
}
],
#{result := LocationsBefore} = els_client:references(UriA, 5, 2),
assert_locations(LocationsBefore, ExpectedLocationsBefore),
%% Add (Simulate a checkout, rebase or similar)
DataDir = ?config(data_dir, Config),
PathC = filename:join([DataDir, "watched_file_c.erl"]),
UriC = els_uri:uri(els_utils:to_binary(PathC)),
els_client:did_change_watched_files([{UriC, ?FILE_CHANGE_TYPE_CREATED}]),
%% After
ExpectedLocationsAfter = [ #{ uri => UriC
, range => #{from => {6, 3}, to => {6, 22}}
}
, #{ uri => UriB
, range => #{from => {6, 3}, to => {6, 22}}
}
],
#{result := LocationsAfter} = els_client:references(UriA, 5, 2),
assert_locations(LocationsAfter, ExpectedLocationsAfter),
ok.

%%==============================================================================
%% Internal functions
%%==============================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-module(watched_file_c).

-export([ main/0 ]).

main() ->
watched_file_a:main().