From 2ff5acf2cb85abec5eec3b11137f267f82062e9f Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 30 Mar 2022 15:22:14 +0200 Subject: [PATCH 01/38] Store POIs on-demand --- apps/els_core/src/els_utils.erl | 6 ++-- apps/els_lsp/src/els_dt_document.erl | 31 +++++++++++++++------ apps/els_lsp/src/els_indexing.erl | 41 +++++++++++++++++----------- 3 files changed, 51 insertions(+), 27 deletions(-) diff --git a/apps/els_core/src/els_utils.erl b/apps/els_core/src/els_utils.erl index ded6b42b1..489645a61 100644 --- a/apps/els_core/src/els_utils.erl +++ b/apps/els_core/src/els_utils.erl @@ -154,9 +154,9 @@ lookup_document(Uri) -> case els_dt_document:lookup(Uri) of {ok, [Document]} -> {ok, Document}; - Error -> - ?LOG_INFO("Document lookup failed [error=~p] [uri=~p]", [Error, Uri]), - {error, Error} + {ok, []} -> + ?LOG_INFO("Document lookup failed [uri=~p]", [Uri]), + {error, document_lookup_failed} end end. diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index 6cc4e0376..1e54ce282 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -23,6 +23,7 @@ ]). -export([ new/2 + , new/3 , pois/1 , pois/2 , get_element_at_pos/3 @@ -62,7 +63,7 @@ , kind := kind() , text := binary() , md5 => binary() - , pois => [poi()] + , pois => [poi()] | ondemand }. -export_type([ id/0 , item/0 @@ -132,21 +133,30 @@ delete(Uri) -> -spec new(uri(), binary()) -> item(). new(Uri, Text) -> + new(Uri, Text, _StorePOIs = true). + +-spec new(uri(), binary(), boolean()) -> item(). +new(Uri, Text, StorePOIs) -> Extension = filename:extension(Uri), Id = binary_to_atom(filename:basename(Uri, Extension), utf8), case Extension of <<".erl">> -> - new(Uri, Text, Id, module); + new(Uri, Text, Id, module, StorePOIs); <<".hrl">> -> - new(Uri, Text, Id, header); + new(Uri, Text, Id, header, StorePOIs); _ -> - new(Uri, Text, Id, other) + new(Uri, Text, Id, other, StorePOIs) end. --spec new(uri(), binary(), atom(), kind()) -> item(). -new(Uri, Text, Id, Kind) -> - {ok, POIs} = els_parser:parse(Text), - MD5 = erlang:md5(Text), +-spec new(uri(), binary(), atom(), kind(), boolean()) -> item(). +new(Uri, Text, Id, Kind, StorePOIs) -> + {ok, POIs} = case StorePOIs of + true -> + els_parser:parse(Text); + false -> + {ok, ondemand} + end, + MD5 = erlang:md5(Text), #{ uri => Uri , id => Id , kind => Kind @@ -157,6 +167,11 @@ new(Uri, Text, Id, Kind) -> %% @doc Returns the list of POIs for the current document -spec pois(item()) -> [poi()]. +pois(#{ uri := Uri, text := Text, pois := ondemand }) -> + Document = els_dt_document:new(Uri, Text, _StorePOIs = true), + #{ pois := POIs } = Document, + ok = els_dt_document:insert(Document), + POIs; pois(#{ pois := POIs }) -> POIs. diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index f103698d6..290aa4904 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -46,20 +46,21 @@ find_and_index_file(FileName) -> -spec index_file(binary()) -> {ok, uri()}. index_file(Path) -> GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - try_index_file(Path, 'deep', false, GeneratedFilesTag), + try_index_file(Path, 'deep', _StorePOIs = true, false, GeneratedFilesTag), {ok, els_uri:uri(Path)}. --spec index_if_not_generated(uri(), binary(), mode(), boolean(), string()) -> +-spec index_if_not_generated(uri(), binary(), mode(), + boolean(), boolean(), string()) -> ok | skipped. -index_if_not_generated(Uri, Text, Mode, false, _GeneratedFilesTag) -> - index(Uri, Text, Mode); -index_if_not_generated(Uri, Text, Mode, true, GeneratedFilesTag) -> +index_if_not_generated(Uri, Text, Mode, StorePOIs, false, _GeneratedFilesTag) -> + index(Uri, Text, Mode, StorePOIs); +index_if_not_generated(Uri, Text, Mode, StorePOIs, true, GeneratedFilesTag) -> case is_generated_file(Text, GeneratedFilesTag) of true -> ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), skipped; false -> - ok = index(Uri, Text, Mode) + ok = index(Uri, Text, Mode, StorePOIs) end. -spec is_generated_file(binary(), string()) -> boolean(). @@ -74,21 +75,27 @@ is_generated_file(Text, Tag) -> -spec index(uri(), binary(), mode()) -> ok. index(Uri, Text, Mode) -> + index(Uri, Text, Mode, _StorePOIs = true). + +-spec index(uri(), binary(), mode(), boolean()) -> ok. +index(Uri, Text, Mode, StorePOIs) -> MD5 = erlang:md5(Text), case els_dt_document:lookup(Uri) of {ok, [#{md5 := MD5}]} -> ok; {ok, LookupResult} -> - Document = els_dt_document:new(Uri, Text), - do_index(Document, Mode, LookupResult =/= []) + Document = els_dt_document:new(Uri, Text, StorePOIs), + do_index(Document, Mode, StorePOIs, LookupResult =/= []) end. --spec do_index(els_dt_document:item(), mode(), boolean()) -> ok. -do_index(#{uri := Uri, id := Id, kind := Kind} = Document, Mode, Reset) -> - case Mode of - 'deep' -> +-spec do_index(els_dt_document:item(), mode(), boolean(), boolean()) -> ok. +do_index(#{ uri := Uri + , id := Id + , kind := Kind} = Document, Mode, StorePOIs, Reset) -> + case {Mode, StorePOIs} of + {'deep', true} -> ok = els_dt_document:insert(Document); - 'shallow' -> + _ -> %% Don't store detailed POIs when "shallow" indexing. %% They will be reloaded and inserted when needed %% by calling els_utils:lookup_document/1 @@ -178,14 +185,15 @@ start(Group, Entries) -> %% @doc Try indexing a file. --spec try_index_file(binary(), mode(), boolean(), string()) -> +-spec try_index_file(binary(), mode(), boolean(), boolean(), string()) -> ok | skipped | {error, any()}. -try_index_file(FullName, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> +try_index_file(FullName, Mode, StorePOIs, + SkipGeneratedFiles, GeneratedFilesTag) -> Uri = els_uri:uri(FullName), try ?LOG_DEBUG("Indexing file. [filename=~s, uri=~s]", [FullName, Uri]), {ok, Text} = file:read_file(FullName), - index_if_not_generated(Uri, Text, Mode, + index_if_not_generated(Uri, Text, Mode, StorePOIs, SkipGeneratedFiles, GeneratedFilesTag) catch Type:Reason:St -> ?LOG_ERROR("Error indexing file " @@ -227,6 +235,7 @@ index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> ?LOG_DEBUG("Indexing directory. [dir=~s] [mode=~s]", [Dir, Mode]), F = fun(FileName, {Succeeded, Skipped, Failed}) -> case try_index_file(els_utils:to_binary(FileName), Mode, + _StorePOIs = false, SkipGeneratedFiles, GeneratedFilesTag) of ok -> {Succeeded + 1, Skipped, Failed}; skipped -> {Succeeded, Skipped + 1, Failed}; From a9882c54d5f9743bc8066bb349acffe8b7b29765 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 13:39:44 +0200 Subject: [PATCH 02/38] Store references on demand --- apps/els_lsp/src/els_dt_document.erl | 32 +++----- apps/els_lsp/src/els_dt_references.erl | 93 ++++++++++++++++++++-- apps/els_lsp/src/els_indexing.erl | 89 ++++----------------- apps/els_lsp/test/els_references_SUITE.erl | 50 ++++-------- 4 files changed, 127 insertions(+), 137 deletions(-) diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index 1e54ce282..c51f6ebbc 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -23,7 +23,6 @@ ]). -export([ new/2 - , new/3 , pois/1 , pois/2 , get_element_at_pos/3 @@ -54,7 +53,7 @@ , kind :: kind() | '_' , text :: binary() | '_' , md5 :: binary() | '_' - , pois :: [poi()] | '_' + , pois :: [poi()] | '_' | ondemand }). -type els_dt_document() :: #els_dt_document{}. @@ -133,44 +132,33 @@ delete(Uri) -> -spec new(uri(), binary()) -> item(). new(Uri, Text) -> - new(Uri, Text, _StorePOIs = true). - --spec new(uri(), binary(), boolean()) -> item(). -new(Uri, Text, StorePOIs) -> Extension = filename:extension(Uri), Id = binary_to_atom(filename:basename(Uri, Extension), utf8), case Extension of <<".erl">> -> - new(Uri, Text, Id, module, StorePOIs); + new(Uri, Text, Id, module); <<".hrl">> -> - new(Uri, Text, Id, header, StorePOIs); + new(Uri, Text, Id, header); _ -> - new(Uri, Text, Id, other, StorePOIs) + new(Uri, Text, Id, other) end. --spec new(uri(), binary(), atom(), kind(), boolean()) -> item(). -new(Uri, Text, Id, Kind, StorePOIs) -> - {ok, POIs} = case StorePOIs of - true -> - els_parser:parse(Text); - false -> - {ok, ondemand} - end, +-spec new(uri(), binary(), atom(), kind()) -> item(). +new(Uri, Text, Id, Kind) -> MD5 = erlang:md5(Text), #{ uri => Uri , id => Id , kind => Kind , text => Text , md5 => MD5 - , pois => POIs + , pois => ondemand }. %% @doc Returns the list of POIs for the current document -spec pois(item()) -> [poi()]. -pois(#{ uri := Uri, text := Text, pois := ondemand }) -> - Document = els_dt_document:new(Uri, Text, _StorePOIs = true), - #{ pois := POIs } = Document, - ok = els_dt_document:insert(Document), +pois(#{ text := Text, pois := ondemand } = Document) -> + {ok, POIs} = els_parser:parse(Text), + ok = els_dt_document:insert(Document#{pois => POIs}), POIs; pois(#{ pois := POIs }) -> POIs. diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index 0926f1790..f52bc39c8 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -18,16 +18,23 @@ %%============================================================================== -export([ delete_by_uri/1 - , find_all/0 , find_by/1 , find_by_id/2 , insert/2 ]). +%%============================================================================== +%% Test API +%%============================================================================== + +-export([ find_candidate_uris/1 + ]). + %%============================================================================== %% Includes %%============================================================================== -include("els_lsp.hrl"). +-include_lib("kernel/include/logger.hrl"). %%============================================================================== %% Item Definition @@ -45,6 +52,14 @@ }. -export_type([ item/0 ]). +-type poi_category() :: function + | type + | macro + | record + | include + | include_lib + | behaviour. + %%============================================================================== %% Callbacks for the els_db_table Behaviour %%============================================================================== @@ -82,12 +97,6 @@ insert(Kind, Map) when is_map(Map) -> Record = from_item(Kind, Map), els_db:write(name(), Record). -%% @doc Find all --spec find_all() -> {ok, [item()]} | {error, any()}. -find_all() -> - Pattern = #els_dt_references{_ = '_'}, - find_by(Pattern). - %% @doc Find by id -spec find_by_id(poi_kind(), any()) -> {ok, [item()]} | {error, any()}. find_by_id(Kind, Id) -> @@ -97,10 +106,58 @@ find_by_id(Kind, Id) -> -spec find_by(tuple()) -> {ok, [item()]}. find_by(Pattern) -> + #els_dt_references{id = Id} = Pattern, + Uris = find_candidate_uris(Id), + [begin + {ok, Document} = els_utils:lookup_document(Uri), + POIs = els_dt_document:pois(Document, [ application + , behaviour + , implicit_fun + , include + , include_lib + , type_application + , import_entry + ]), + %% TODO: Only re-register references if necessary + ok = els_dt_references:delete_by_uri(Uri), + [register_reference(Uri, POI) || POI <- POIs] + end || Uri <- Uris], {ok, Items} = els_db:match(name(), Pattern), {ok, [to_item(Item) || Item <- Items]}. --spec kind_to_category(poi_kind()) -> function | type | macro | record. +%% TODO: What about references from header files? +-spec greppable_string({poi_category(), any()}) -> + {ok, string()} | {error, {any(), not_supported}}. +greppable_string({function, {_M, F, _A}}) -> + io_lib:format("~p", [F]); +greppable_string({type, {_M, F, _A}}) -> + io_lib:format("~p", [F]); +greppable_string({macro, {Name, _Arity}}) -> + io_lib:format("~p", [Name]); +greppable_string({macro, Name}) -> + io_lib:format("~p", [Name]); +greppable_string({include, String}) -> + io_lib:format("~s", [String]); +%% TODO: This could be tricky +greppable_string({include_lib, String}) -> + io_lib:format("~s", [String]); +greppable_string({behaviour, Name}) -> + io_lib:format("~p", [Name]). + +-spec find_candidate_uris(any()) -> [uri()]. +find_candidate_uris(Id) -> + IdString = greppable_string(Id), + Paths = els_config:get(apps_paths) ++ els_config:get(deps_paths), + PathsString = string:join(Paths, " "), + Cmd = "grep -l -r \"" ++ IdString ++ "\" " ++ PathsString, + ?LOG_INFO("Command: ~p", [Cmd]), + Result = string:trim(os:cmd(Cmd), trailing), + ?LOG_INFO("Result: ~p", [Result]), + Candidates = string:split(Result, "\n", all), + [els_uri:uri(els_utils:to_binary(Candidate)) || Candidate <- Candidates, + Candidate =/= []]. + +-spec kind_to_category(poi_kind()) -> poi_category(). kind_to_category(Kind) when Kind =:= application; Kind =:= export_entry; Kind =:= function; @@ -124,3 +181,23 @@ kind_to_category(Kind) when Kind =:= include_lib -> include_lib; kind_to_category(Kind) when Kind =:= behaviour -> behaviour. + +-spec register_reference(uri(), poi()) -> ok. +register_reference(Uri, #{id := {F, A}} = POI) -> + M = els_uri:module(Uri), + register_reference(Uri, POI#{id => {M, F, A}}); +register_reference(Uri, #{kind := Kind, id := Id, range := Range}) + when %% Include + Kind =:= include; + Kind =:= include_lib; + %% Function + Kind =:= application; + Kind =:= implicit_fun; + Kind =:= import_entry; + %% Type + Kind =:= type_application; + %% Behaviour + Kind =:= behaviour -> + insert( Kind + , #{id => Id, uri => Uri, range => Range} + ). diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 290aa4904..70321ab15 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -46,21 +46,20 @@ find_and_index_file(FileName) -> -spec index_file(binary()) -> {ok, uri()}. index_file(Path) -> GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - try_index_file(Path, 'deep', _StorePOIs = true, false, GeneratedFilesTag), + try_index_file(Path, 'deep', false, GeneratedFilesTag), {ok, els_uri:uri(Path)}. --spec index_if_not_generated(uri(), binary(), mode(), - boolean(), boolean(), string()) -> +-spec index_if_not_generated(uri(), binary(), mode(), boolean(), string()) -> ok | skipped. -index_if_not_generated(Uri, Text, Mode, StorePOIs, false, _GeneratedFilesTag) -> - index(Uri, Text, Mode, StorePOIs); -index_if_not_generated(Uri, Text, Mode, StorePOIs, true, GeneratedFilesTag) -> +index_if_not_generated(Uri, Text, Mode, false, _GeneratedFilesTag) -> + index(Uri, Text, Mode); +index_if_not_generated(Uri, Text, Mode, true, GeneratedFilesTag) -> case is_generated_file(Text, GeneratedFilesTag) of true -> ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), skipped; false -> - ok = index(Uri, Text, Mode, StorePOIs) + ok = index(Uri, Text, Mode) end. -spec is_generated_file(binary(), string()) -> boolean(). @@ -75,37 +74,21 @@ is_generated_file(Text, Tag) -> -spec index(uri(), binary(), mode()) -> ok. index(Uri, Text, Mode) -> - index(Uri, Text, Mode, _StorePOIs = true). - --spec index(uri(), binary(), mode(), boolean()) -> ok. -index(Uri, Text, Mode, StorePOIs) -> MD5 = erlang:md5(Text), case els_dt_document:lookup(Uri) of {ok, [#{md5 := MD5}]} -> ok; {ok, LookupResult} -> - Document = els_dt_document:new(Uri, Text, StorePOIs), - do_index(Document, Mode, StorePOIs, LookupResult =/= []) + Document = els_dt_document:new(Uri, Text), + ok = els_dt_document:insert(Document), + do_index(Document, Mode, LookupResult =/= []) end. --spec do_index(els_dt_document:item(), mode(), boolean(), boolean()) -> ok. -do_index(#{ uri := Uri - , id := Id - , kind := Kind} = Document, Mode, StorePOIs, Reset) -> - case {Mode, StorePOIs} of - {'deep', true} -> - ok = els_dt_document:insert(Document); - _ -> - %% Don't store detailed POIs when "shallow" indexing. - %% They will be reloaded and inserted when needed - %% by calling els_utils:lookup_document/1 - ok - end, - %% Mapping from document id to uri +-spec do_index(els_dt_document:item(), mode(), boolean()) -> ok. +do_index(#{uri := Uri, id := Id, kind := Kind} = Document, _Mode, _Reset) -> ModuleItem = els_dt_document_index:new(Id, Uri, Kind), ok = els_dt_document_index:insert(ModuleItem), - index_signatures(Document), - index_references(Document, Mode, Reset). + index_signatures(Document). -spec index_signatures(els_dt_document:item()) -> ok. index_signatures(#{id := Id, text := Text} = Document) -> @@ -119,26 +102,6 @@ index_signatures(#{id := Id, text := Text} = Document) -> ], ok. --spec index_references(els_dt_document:item(), mode(), boolean()) -> ok. -index_references(#{uri := Uri} = Document, 'deep', true) -> - %% Optimization to only do (non-optimized) match_delete when necessary - ok = els_dt_references:delete_by_uri(Uri), - index_references(Document, 'deep', false); -index_references(#{uri := Uri} = Document, 'deep', false) -> - %% References - POIs = els_dt_document:pois(Document, [ application - , behaviour - , implicit_fun - , include - , include_lib - , type_application - , import_entry - ]), - [register_reference(Uri, POI) || POI <- POIs], - ok; -index_references(_Document, 'shallow', _) -> - ok. - -spec maybe_start() -> true | false. maybe_start() -> IndexingEnabled = els_config:get(indexing_enabled), @@ -185,15 +148,14 @@ start(Group, Entries) -> %% @doc Try indexing a file. --spec try_index_file(binary(), mode(), boolean(), boolean(), string()) -> +-spec try_index_file(binary(), mode(), boolean(), string()) -> ok | skipped | {error, any()}. -try_index_file(FullName, Mode, StorePOIs, - SkipGeneratedFiles, GeneratedFilesTag) -> +try_index_file(FullName, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> Uri = els_uri:uri(FullName), try ?LOG_DEBUG("Indexing file. [filename=~s, uri=~s]", [FullName, Uri]), {ok, Text} = file:read_file(FullName), - index_if_not_generated(Uri, Text, Mode, StorePOIs, + index_if_not_generated(Uri, Text, Mode, SkipGeneratedFiles, GeneratedFilesTag) catch Type:Reason:St -> ?LOG_ERROR("Error indexing file " @@ -202,26 +164,6 @@ try_index_file(FullName, Mode, StorePOIs, {error, {Type, Reason}} end. --spec register_reference(uri(), poi()) -> ok. -register_reference(Uri, #{id := {F, A}} = POI) -> - M = els_uri:module(Uri), - register_reference(Uri, POI#{id => {M, F, A}}); -register_reference(Uri, #{kind := Kind, id := Id, range := Range}) - when %% Include - Kind =:= include; - Kind =:= include_lib; - %% Function - Kind =:= application; - Kind =:= implicit_fun; - Kind =:= import_entry; - %% Type - Kind =:= type_application; - %% Behaviour - Kind =:= behaviour -> - els_dt_references:insert( Kind - , #{id => Id, uri => Uri, range => Range} - ). - -spec index_dir(string(), mode()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. index_dir(Dir, Mode) -> @@ -235,7 +177,6 @@ index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> ?LOG_DEBUG("Indexing directory. [dir=~s] [mode=~s]", [Dir, Mode]), F = fun(FileName, {Succeeded, Skipped, Failed}) -> case try_index_file(els_utils:to_binary(FileName), Mode, - _StorePOIs = false, SkipGeneratedFiles, GeneratedFilesTag) of ok -> {Succeeded + 1, Skipped, Failed}; skipped -> {Succeeded, Skipped + 1, Failed}; diff --git a/apps/els_lsp/test/els_references_SUITE.erl b/apps/els_lsp/test/els_references_SUITE.erl index eae89560c..0b0fee626 100644 --- a/apps/els_lsp/test/els_references_SUITE.erl +++ b/apps/els_lsp/test/els_references_SUITE.erl @@ -27,7 +27,6 @@ , included_record_field/1 , undefined_record/1 , undefined_record_field/1 - , purge_references/1 , type_local/1 , type_remote/1 , type_included/1 @@ -69,7 +68,8 @@ end_per_suite(Config) -> -spec init_per_testcase(atom(), config()) -> config(). init_per_testcase(TestCase, Config0) - when TestCase =:= refresh_after_watched_file_changed -> + when TestCase =:= refresh_after_watched_file_changed; + TestCase =:= refresh_after_watched_file_deleted -> Config = els_test_utils:init_per_testcase(TestCase, Config0), PathB = ?config(watched_file_b_path, Config), {ok, OldContent} = file:read_file(PathB), @@ -79,10 +79,16 @@ init_per_testcase(TestCase, Config) -> -spec end_per_testcase(atom(), config()) -> ok. end_per_testcase(TestCase, Config) - when TestCase =:= refresh_after_watched_file_changed -> + when TestCase =:= refresh_after_watched_file_changed; + TestCase =:= refresh_after_watched_file_deleted -> 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) + when TestCase =:= refresh_after_watched_file_added -> + PathB = ?config(watched_file_b_path, Config), + ok = file:delete(filename:join(filename:dirname(PathB), + "watched_file_c.erl")); end_per_testcase(TestCase, Config) -> els_test_utils:end_per_testcase(TestCase, Config). @@ -413,34 +419,6 @@ undefined_record_field(Config) -> assert_locations(Locations1, ExpectedLocations), ok. -%% Issue #245 --spec purge_references(config()) -> ok. -purge_references(_Config) -> - els_db:clear_tables(), - Uri = <<"file:///tmp/foo.erl">>, - Text0 = <<"-spec foo() -> ok.\nfoo(_X) -> ok.\nbar() -> foo().">>, - Text1 = <<"\n-spec foo() -> ok.\nfoo(_X)-> ok.\nbar() -> foo().">>, - Doc0 = els_dt_document:new(Uri, Text0), - Doc1 = els_dt_document:new(Uri, Text1), - - ok = els_indexing:index(Uri, Text0, 'deep'), - ?assertEqual({ok, [Doc0]}, els_dt_document:lookup(Uri)), - ?assertEqual({ok, [#{ id => {foo, foo, 0} - , range => #{from => {3, 10}, to => {3, 13}} - , uri => <<"file:///tmp/foo.erl">> - }]} - , els_dt_references:find_all() - ), - - ok = els_indexing:index(Uri, Text1, 'deep'), - ?assertEqual({ok, [Doc1]}, els_dt_document:lookup(Uri)), - ?assertEqual({ok, [#{ id => {foo, foo, 0} - , range => #{from => {4, 10}, to => {4, 13}} - , uri => <<"file:///tmp/foo.erl">> - }]} - , els_dt_references:find_all() - ), - ok. -spec type_local(config()) -> ok. type_local(Config) -> @@ -507,6 +485,7 @@ refresh_after_watched_file_deleted(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}} } @@ -514,6 +493,7 @@ refresh_after_watched_file_deleted(Config) -> #{result := LocationsBefore} = els_client:references(UriA, 5, 2), assert_locations(LocationsBefore, ExpectedLocationsBefore), %% Delete (Simulate a checkout, rebase or similar) + ok = file:delete(PathB), els_client:did_change_watched_files([{UriB, ?FILE_CHANGE_TYPE_DELETED}]), %% After #{result := null} = els_client:references(UriA, 5, 2), @@ -554,6 +534,7 @@ refresh_after_watched_file_added(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}} } @@ -563,13 +544,16 @@ refresh_after_watched_file_added(Config) -> %% Add (Simulate a checkout, rebase or similar) DataDir = ?config(data_dir, Config), PathC = filename:join([DataDir, "watched_file_c.erl"]), + NewPathC = filename:join(filename:dirname(PathB), "watched_file_c.erl"), UriC = els_uri:uri(els_utils:to_binary(PathC)), + NewUriC = els_uri:uri(NewPathC), + {ok, _} = file:copy(PathC, NewPathC), els_client:did_change_watched_files([{UriC, ?FILE_CHANGE_TYPE_CREATED}]), %% After - ExpectedLocationsAfter = [ #{ uri => UriC + ExpectedLocationsAfter = [ #{ uri => UriB , range => #{from => {6, 3}, to => {6, 22}} } - , #{ uri => UriB + , #{ uri => NewUriC , range => #{from => {6, 3}, to => {6, 22}} } ], From 7dd55824d351c8be1137901e31001818bfc6f4d9 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 14:02:58 +0200 Subject: [PATCH 03/38] Store signatures on demand --- apps/els_lsp/src/els_dt_signatures.erl | 12 +++++++++++- apps/els_lsp/src/els_indexing.erl | 17 ++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index f2a2b8694..06c3ce4d5 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -72,7 +72,17 @@ insert(Map) when is_map(Map) -> els_db:write(name(), Record). -spec lookup(mfa()) -> {ok, [item()]}. -lookup(MFA) -> +lookup({M, _F, _A} = MFA) -> + %% TODO: Only do it when necessary + {ok, #{text := Text} = Document} = els_utils:lookup_document(els_uri:uri(M)), + Specs = els_dt_document:pois(Document, [spec]), + [ begin + #{from := From, to := To} = Range, + Spec = els_text:range(Text, From, To), + els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}) + end + || #{id := {F, A}, range := Range} <- Specs + ], {ok, Items} = els_db:lookup(name(), MFA), {ok, [to_item(Item) || Item <- Items]}. diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 70321ab15..884ce0dce 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -85,22 +85,9 @@ index(Uri, Text, Mode) -> end. -spec do_index(els_dt_document:item(), mode(), boolean()) -> ok. -do_index(#{uri := Uri, id := Id, kind := Kind} = Document, _Mode, _Reset) -> +do_index(#{uri := Uri, id := Id, kind := Kind}, _Mode, _Reset) -> ModuleItem = els_dt_document_index:new(Id, Uri, Kind), - ok = els_dt_document_index:insert(ModuleItem), - index_signatures(Document). - --spec index_signatures(els_dt_document:item()) -> ok. -index_signatures(#{id := Id, text := Text} = Document) -> - Specs = els_dt_document:pois(Document, [spec]), - [ begin - #{from := From, to := To} = Range, - Spec = els_text:range(Text, From, To), - els_dt_signatures:insert(#{ mfa => {Id, F, A} , spec => Spec}) - end - || #{id := {F, A}, range := Range} <- Specs - ], - ok. + ok = els_dt_document_index:insert(ModuleItem). -spec maybe_start() -> true | false. maybe_start() -> From fffe202ebe6f4324a24127cb551e928f32b34ba8 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 14:32:50 +0200 Subject: [PATCH 04/38] Patches --- apps/els_lsp/src/els_dt_signatures.erl | 27 +++++---- .../els_lsp/test/els_call_hierarchy_SUITE.erl | 60 +++++++++---------- apps/els_lsp/test/els_hover_SUITE.erl | 2 +- 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index 06c3ce4d5..f09d21daa 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -74,17 +74,22 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> %% TODO: Only do it when necessary - {ok, #{text := Text} = Document} = els_utils:lookup_document(els_uri:uri(M)), - Specs = els_dt_document:pois(Document, [spec]), - [ begin - #{from := From, to := To} = Range, - Spec = els_text:range(Text, From, To), - els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}) - end - || #{id := {F, A}, range := Range} <- Specs - ], - {ok, Items} = els_db:lookup(name(), MFA), - {ok, [to_item(Item) || Item <- Items]}. + case els_utils:find_module(M) of + {ok, Uri} -> + {ok, #{text := Text} = Document} = els_utils:lookup_document(Uri), + Specs = els_dt_document:pois(Document, [spec]), + [ begin + #{from := From, to := To} = Range, + Spec = els_text:range(Text, From, To), + els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}) + end + || #{id := {F, A}, range := Range} <- Specs + ], + {ok, Items} = els_db:lookup(name(), MFA), + {ok, [to_item(Item) || Item <- Items]}; + {error, _} -> + {ok, []} + end. -spec delete_by_module(atom()) -> ok. delete_by_module(Module) -> diff --git a/apps/els_lsp/test/els_call_hierarchy_SUITE.erl b/apps/els_lsp/test/els_call_hierarchy_SUITE.erl index 2242ff508..cab053942 100644 --- a/apps/els_lsp/test/els_call_hierarchy_SUITE.erl +++ b/apps/els_lsp/test/els_call_hierarchy_SUITE.erl @@ -91,7 +91,36 @@ incoming_calls(Config) -> , uri => UriA}, ?assertEqual([Item], PrepareResult), #{result := Result} = els_client:callhierarchy_incomingcalls(Item), - Calls = [#{ from => + Calls = [ #{ from => + #{ data => + els_utils:base64_encode_term( + #{ poi => + #{ data => + #{ args => [{1, "Arg1"}] + , wrapping_range => + #{ from => {7, 1} + , to => {14, 0}}} + , id => {function_a, 1} + , kind => function + , range => #{from => {7, 1}, to => {7, 11}}}}) + , detail => <<"call_hierarchy_b [L11]">> + , kind => 12 + , name => <<"function_a/1">> + , range => + #{ 'end' => #{character => 29, line => 10} + , start => #{character => 2, line => 10} + } + , selectionRange => + #{ 'end' => #{character => 29, line => 10} + , start => #{character => 2, line => 10} + } + , uri => UriB} + , fromRanges => + [#{ 'end' => #{character => 29, line => 10} + , start => #{character => 2, line => 10} + }] + } + , #{ from => #{ data => els_utils:base64_encode_term( #{ poi => @@ -134,35 +163,6 @@ incoming_calls(Config) -> [#{ 'end' => #{character => 12, line => 15} , start => #{character => 2, line => 15} }]} - , #{ from => - #{ data => - els_utils:base64_encode_term( - #{ poi => - #{ data => - #{ args => [{1, "Arg1"}] - , wrapping_range => - #{ from => {7, 1} - , to => {14, 0}}} - , id => {function_a, 1} - , kind => function - , range => #{from => {7, 1}, to => {7, 11}}}}) - , detail => <<"call_hierarchy_b [L11]">> - , kind => 12 - , name => <<"function_a/1">> - , range => - #{ 'end' => #{character => 29, line => 10} - , start => #{character => 2, line => 10} - } - , selectionRange => - #{ 'end' => #{character => 29, line => 10} - , start => #{character => 2, line => 10} - } - , uri => UriB} - , fromRanges => - [#{ 'end' => #{character => 29, line => 10} - , start => #{character => 2, line => 10} - }] - } ], ?assertEqual(Calls, Result). diff --git a/apps/els_lsp/test/els_hover_SUITE.erl b/apps/els_lsp/test/els_hover_SUITE.erl index 312c87184..c05887d84 100644 --- a/apps/els_lsp/test/els_hover_SUITE.erl +++ b/apps/els_lsp/test/els_hover_SUITE.erl @@ -375,4 +375,4 @@ has_eep48(Module) -> case catch code:get_doc(Module) of {ok, _} -> true; _ -> false - end. \ No newline at end of file + end. From 75d30f28afde4b6fbf11857d00fe0ab7eef0dd6e Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 14:33:01 +0200 Subject: [PATCH 05/38] Add missing module --- apps/els_lsp/priv/code_navigation/src/purge_references.erl | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 apps/els_lsp/priv/code_navigation/src/purge_references.erl diff --git a/apps/els_lsp/priv/code_navigation/src/purge_references.erl b/apps/els_lsp/priv/code_navigation/src/purge_references.erl new file mode 100644 index 000000000..b354e94b5 --- /dev/null +++ b/apps/els_lsp/priv/code_navigation/src/purge_references.erl @@ -0,0 +1,5 @@ +-module(purge_references). + +-spec foo(any()) -> ok. +foo(_X) -> ok. +bar() -> foo(42). From 6c20c94e97485d64d7f692ce24f9bba4cb3b9bfd Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 15:31:59 +0200 Subject: [PATCH 06/38] Lower severity for missing config message --- apps/els_core/src/els_config.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/els_core/src/els_config.erl b/apps/els_core/src/els_config.erl index 7c7facf5f..4007f3971 100644 --- a/apps/els_core/src/els_config.erl +++ b/apps/els_core/src/els_config.erl @@ -267,8 +267,8 @@ consult_config([Path | Paths], ReportMissingConfig) -> [Config] -> {Path, Config} catch Class:Error -> - ?LOG_WARNING( "Could not read config file: path=~p class=~p error=~p" - , [Path, Class, Error]), + ?LOG_DEBUG( "Could not read config file: path=~p class=~p error=~p" + , [Path, Class, Error]), consult_config(Paths, ReportMissingConfig) end. From 92c7e62c79ebf4ed2ae3969453da0c12bb6b1a28 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 16:03:36 +0200 Subject: [PATCH 07/38] Fix end-per-testcase --- apps/els_lsp/test/els_references_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/els_lsp/test/els_references_SUITE.erl b/apps/els_lsp/test/els_references_SUITE.erl index 0b0fee626..cc75b407d 100644 --- a/apps/els_lsp/test/els_references_SUITE.erl +++ b/apps/els_lsp/test/els_references_SUITE.erl @@ -88,7 +88,8 @@ end_per_testcase(TestCase, Config) when TestCase =:= refresh_after_watched_file_added -> PathB = ?config(watched_file_b_path, Config), ok = file:delete(filename:join(filename:dirname(PathB), - "watched_file_c.erl")); + "watched_file_c.erl")), + els_test_utils:end_per_testcase(TestCase, Config); end_per_testcase(TestCase, Config) -> els_test_utils:end_per_testcase(TestCase, Config). From 055a9220f50fe4d7487f1d08968550fe9b1206f0 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 1 Apr 2022 16:13:33 +0200 Subject: [PATCH 08/38] Lower verbosity --- apps/els_lsp/src/els_dt_references.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index f52bc39c8..5a5d7e6c6 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -150,9 +150,9 @@ find_candidate_uris(Id) -> Paths = els_config:get(apps_paths) ++ els_config:get(deps_paths), PathsString = string:join(Paths, " "), Cmd = "grep -l -r \"" ++ IdString ++ "\" " ++ PathsString, - ?LOG_INFO("Command: ~p", [Cmd]), + ?LOG_DEBUG("Command: ~p", [Cmd]), Result = string:trim(os:cmd(Cmd), trailing), - ?LOG_INFO("Result: ~p", [Result]), + ?LOG_DEBUG("Result: ~p", [Result]), Candidates = string:split(Result, "\n", all), [els_uri:uri(els_utils:to_binary(Candidate)) || Candidate <- Candidates, Candidate =/= []]. From 8934fadfe0c73b5752caaf69116a12d4636012cc Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 14:14:57 +0200 Subject: [PATCH 09/38] Only index specs when needed --- apps/els_lsp/src/els_dt_signatures.erl | 43 ++++++++++++++++---------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index f09d21daa..a95dabfdd 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -26,6 +26,7 @@ %% Includes %%============================================================================== -include("els_lsp.hrl"). +-include_lib("kernel/include/logger.hrl"). %%============================================================================== %% Item Definition @@ -73,25 +74,35 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> - %% TODO: Only do it when necessary - case els_utils:find_module(M) of - {ok, Uri} -> - {ok, #{text := Text} = Document} = els_utils:lookup_document(Uri), - Specs = els_dt_document:pois(Document, [spec]), - [ begin - #{from := From, to := To} = Range, - Spec = els_text:range(Text, From, To), - els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}) - end - || #{id := {F, A}, range := Range} <- Specs - ], - {ok, Items} = els_db:lookup(name(), MFA), - {ok, [to_item(Item) || Item <- Items]}; - {error, _} -> - {ok, []} + case els_db:lookup(name(), MFA) of + {ok, []} -> + ok = index_signatures(M), + els_db:lookup(name(), MFA); + {ok, Items} -> + {ok, [to_item(Item) || Item <- Items]} end. -spec delete_by_module(atom()) -> ok. delete_by_module(Module) -> Pattern = #els_dt_signatures{mfa = {Module, '_', '_'}, _ = '_'}, ok = els_db:match_delete(name(), Pattern). + +-spec index_signatures(atom()) -> ok. +index_signatures(M) -> + case els_utils:find_module(M) of + {ok, Uri} -> + {ok, #{text := Text} = Document} = els_utils:lookup_document(Uri), + POIs = els_dt_document:pois(Document, [spec]), + [index_signature(M, Text, POI) || POI <- POIs]; + {error, Error} -> + ?LOG_DEBUG("[~p] Cannot find module. [module=~p] [error=~p]", + [?MODULE, M, Error]) + end, + ok. + +-spec index_signature(atom(), binary(), poi()) -> ok. +index_signature(M, Text, POI) -> + #{id := {F, A}, range := Range} = POI, + #{from := From, to := To} = Range, + Spec = els_text:range(Text, From, To), + els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}). From 0af544ff26965b668aeedb138b31987b7170abd0 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 15:13:44 +0200 Subject: [PATCH 10/38] Prune signatures and references during re-indexing --- apps/els_lsp/src/els_dt_signatures.erl | 2 +- apps/els_lsp/src/els_indexing.erl | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index a95dabfdd..ff95cff0c 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -105,4 +105,4 @@ index_signature(M, Text, POI) -> #{id := {F, A}, range := Range} = POI, #{from := From, to := To} = Range, Spec = els_text:range(Text, From, To), - els_dt_signatures:insert(#{ mfa => {M, F, A} , spec => Spec}). + insert(#{ mfa => {M, F, A} , spec => Spec}). diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 884ce0dce..d8249c565 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -81,14 +81,23 @@ index(Uri, Text, Mode) -> {ok, LookupResult} -> Document = els_dt_document:new(Uri, Text), ok = els_dt_document:insert(Document), - do_index(Document, Mode, LookupResult =/= []) + #{id := Id, kind := Kind} = Document, + ModuleItem = els_dt_document_index:new(Id, Uri, Kind), + ok = els_dt_document_index:insert(ModuleItem), + case Mode of + shallow -> + ok; + deep -> + case LookupResult of + [] -> + ok; + _ -> + ok = els_dt_references:delete_by_uri(Uri), + ok = els_dt_signatures:delete_by_module(els_uri:module(Uri)) + end + end end. --spec do_index(els_dt_document:item(), mode(), boolean()) -> ok. -do_index(#{uri := Uri, id := Id, kind := Kind}, _Mode, _Reset) -> - ModuleItem = els_dt_document_index:new(Id, Uri, Kind), - ok = els_dt_document_index:insert(ModuleItem). - -spec maybe_start() -> true | false. maybe_start() -> IndexingEnabled = els_config:get(indexing_enabled), @@ -190,11 +199,11 @@ index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> -spec entries_apps() -> [{string(), 'deep' | 'shallow'}]. entries_apps() -> - [{Dir, 'deep'} || Dir <- els_config:get(apps_paths)]. + [{Dir, 'shallow'} || Dir <- els_config:get(apps_paths)]. -spec entries_deps() -> [{string(), 'deep' | 'shallow'}]. entries_deps() -> - [{Dir, 'deep'} || Dir <- els_config:get(deps_paths)]. + [{Dir, 'shallow'} || Dir <- els_config:get(deps_paths)]. -spec entries_otp() -> [{string(), 'deep' | 'shallow'}]. entries_otp() -> From 9a478ccb572a3a34aa1db774fe50de76fd567d0a Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 15:44:00 +0200 Subject: [PATCH 11/38] Only register references when necessary --- apps/els_lsp/src/els_dt_references.erl | 30 +++++++++++++++----------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index 5a5d7e6c6..b38d4e3f1 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -105,26 +105,30 @@ find_by_id(Kind, Id) -> find_by(Pattern). -spec find_by(tuple()) -> {ok, [item()]}. -find_by(Pattern) -> - #els_dt_references{id = Id} = Pattern, +find_by(#els_dt_references{id = Id} = Pattern) -> Uris = find_candidate_uris(Id), [begin {ok, Document} = els_utils:lookup_document(Uri), - POIs = els_dt_document:pois(Document, [ application - , behaviour - , implicit_fun - , include - , include_lib - , type_application - , import_entry - ]), - %% TODO: Only re-register references if necessary - ok = els_dt_references:delete_by_uri(Uri), - [register_reference(Uri, POI) || POI <- POIs] + index_references(Document) end || Uri <- Uris], {ok, Items} = els_db:match(name(), Pattern), {ok, [to_item(Item) || Item <- Items]}. +-spec index_references(els_dt_document:id()) -> ok. +index_references(#{uri := Uri, pois := ondemand} = Document) -> + POIs = els_dt_document:pois(Document, [ application + , behaviour + , implicit_fun + , include + , include_lib + , type_application + , import_entry + ]), + [register_reference(Uri, POI) || POI <- POIs], + ok; +index_references(_) -> + ok. + %% TODO: What about references from header files? -spec greppable_string({poi_category(), any()}) -> {ok, string()} | {error, {any(), not_supported}}. From 03579f97f2c00d36f375943c8c8f146b853d66e9 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 17:13:33 +0200 Subject: [PATCH 12/38] Refactor text search --- apps/els_core/src/els_config.erl | 5 +- apps/els_lsp/src/els_dt_references.erl | 43 +---------------- apps/els_lsp/src/els_text_search.erl | 66 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 42 deletions(-) create mode 100644 apps/els_lsp/src/els_text_search.erl diff --git a/apps/els_core/src/els_config.erl b/apps/els_core/src/els_config.erl index 4007f3971..7259e046c 100644 --- a/apps/els_core/src/els_config.erl +++ b/apps/els_core/src/els_config.erl @@ -56,7 +56,8 @@ | indexing_enabled | bsp_enabled | compiler_telemetry_enabled - | edoc_custom_tags. + | edoc_custom_tags + | search_command. -type path() :: file:filename(). -type state() :: #{ apps_dirs => [path()] @@ -135,6 +136,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) -> CompilerTelemetryEnabled = maps:get("compiler_telemetry_enabled", Config, false), EDocCustomTags = maps:get("edoc_custom_tags", Config, []), + SearchCommand = maps:get("search_command", Config, "grep -l -r \"~s\" ~s"), IndexingEnabled = maps:get(<<"indexingEnabled">>, InitOptions, true), @@ -159,6 +161,7 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) -> ok = set(bsp_enabled, BSPEnabled), ok = set(compiler_telemetry_enabled, CompilerTelemetryEnabled), ok = set(edoc_custom_tags, EDocCustomTags), + ok = set(search_command, SearchCommand), ok = set(incremental_sync, IncrementalSync), ok = set(indexing, maps:merge( els_config_indexing:default_config() , Indexing)), diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index b38d4e3f1..4bb3c45a9 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -23,18 +23,10 @@ , insert/2 ]). -%%============================================================================== -%% Test API -%%============================================================================== - --export([ find_candidate_uris/1 - ]). - %%============================================================================== %% Includes %%============================================================================== -include("els_lsp.hrl"). --include_lib("kernel/include/logger.hrl"). %%============================================================================== %% Item Definition @@ -59,6 +51,7 @@ | include | include_lib | behaviour. +-export_type([ poi_category/0 ]). %%============================================================================== %% Callbacks for the els_db_table Behaviour @@ -106,7 +99,7 @@ find_by_id(Kind, Id) -> -spec find_by(tuple()) -> {ok, [item()]}. find_by(#els_dt_references{id = Id} = Pattern) -> - Uris = find_candidate_uris(Id), + Uris = els_text_search:find_candidate_uris(Id), [begin {ok, Document} = els_utils:lookup_document(Uri), index_references(Document) @@ -129,38 +122,6 @@ index_references(#{uri := Uri, pois := ondemand} = Document) -> index_references(_) -> ok. -%% TODO: What about references from header files? --spec greppable_string({poi_category(), any()}) -> - {ok, string()} | {error, {any(), not_supported}}. -greppable_string({function, {_M, F, _A}}) -> - io_lib:format("~p", [F]); -greppable_string({type, {_M, F, _A}}) -> - io_lib:format("~p", [F]); -greppable_string({macro, {Name, _Arity}}) -> - io_lib:format("~p", [Name]); -greppable_string({macro, Name}) -> - io_lib:format("~p", [Name]); -greppable_string({include, String}) -> - io_lib:format("~s", [String]); -%% TODO: This could be tricky -greppable_string({include_lib, String}) -> - io_lib:format("~s", [String]); -greppable_string({behaviour, Name}) -> - io_lib:format("~p", [Name]). - --spec find_candidate_uris(any()) -> [uri()]. -find_candidate_uris(Id) -> - IdString = greppable_string(Id), - Paths = els_config:get(apps_paths) ++ els_config:get(deps_paths), - PathsString = string:join(Paths, " "), - Cmd = "grep -l -r \"" ++ IdString ++ "\" " ++ PathsString, - ?LOG_DEBUG("Command: ~p", [Cmd]), - Result = string:trim(os:cmd(Cmd), trailing), - ?LOG_DEBUG("Result: ~p", [Result]), - Candidates = string:split(Result, "\n", all), - [els_uri:uri(els_utils:to_binary(Candidate)) || Candidate <- Candidates, - Candidate =/= []]. - -spec kind_to_category(poi_kind()) -> poi_category(). kind_to_category(Kind) when Kind =:= application; Kind =:= export_entry; diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl new file mode 100644 index 000000000..642bd1996 --- /dev/null +++ b/apps/els_lsp/src/els_text_search.erl @@ -0,0 +1,66 @@ +%%============================================================================== +%% Text-based search +%%============================================================================== +-module(els_text_search). + +%%============================================================================== +%% API +%%============================================================================== +-export([ find_candidate_uris/1 ]). + +%%============================================================================== +%% Includes +%%============================================================================== +-include("els_lsp.hrl"). +-include_lib("kernel/include/logger.hrl"). + +%%============================================================================== +%% API +%%============================================================================== +-spec find_candidate_uris({els_dt_references:poi_category(), any()}) -> [uri()]. +find_candidate_uris(Id) -> + Cmd = search_command(greppable_string(Id)), + ?LOG_DEBUG("Finding candidate uris via: ~p", [Cmd]), + Result = string:trim(os:cmd(Cmd), trailing), + ?LOG_DEBUG("Candidates found: ~p", [Result]), + Candidates = string:split(Result, "\n", all), + [els_uri:uri(els_utils:to_binary(Candidate)) || Candidate <- Candidates]. + +%%============================================================================== +%% Internal Functions +%%============================================================================== +-spec searchable_paths() -> [string()]. +searchable_paths() -> + els_config:get(apps_paths) ++ els_config:get(deps_paths). + +-spec search_command(string()) -> string(). +search_command(IdString) -> + PathsString = string:join(searchable_paths(), " "), + CmdFormat = els_config:get(search_command), + CmdArgs = [IdString, PathsString], + lists:flatten(io_lib:format(CmdFormat, CmdArgs)). + +-spec greppable_string({els_dt_references:poi_category(), any()}) -> + {ok, string()}. +greppable_string({function, {_M, F, _A}}) -> + atom_to_greppable_string(F); +greppable_string({type, {_M, F, _A}}) -> + atom_to_greppable_string(F); +greppable_string({macro, {Name, _Arity}}) -> + atom_to_greppable_string(Name); +greppable_string({macro, Name}) -> + atom_to_greppable_string(Name); +greppable_string({include, Id}) -> + include_id_to_greppable_string(Id); +greppable_string({include_lib, Id}) -> + include_id_to_greppable_string(Id); +greppable_string({behaviour, Name}) -> + atom_to_greppable_string(Name). + +-spec atom_to_greppable_string(atom()) -> string(). +atom_to_greppable_string(Atom) -> + lists:flatten(io_lib:format("~p", [Atom])). + +-spec include_id_to_greppable_string(string()) -> string(). +include_id_to_greppable_string(Id) -> + filename:rootname(filename:basename(Id)). From 9c73c66f9561365e836a759b9a08cd12769b9a73 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 18:04:00 +0200 Subject: [PATCH 13/38] Handle whitespaces, actually store references --- apps/els_lsp/src/els_dt_references.erl | 25 +++++++++---------------- apps/els_lsp/src/els_text_search.erl | 3 ++- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index 4bb3c45a9..d2b0ed6e3 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -102,26 +102,19 @@ find_by(#els_dt_references{id = Id} = Pattern) -> Uris = els_text_search:find_candidate_uris(Id), [begin {ok, Document} = els_utils:lookup_document(Uri), - index_references(Document) + POIs = els_dt_document:pois(Document, [ application + , behaviour + , implicit_fun + , include + , include_lib + , type_application + , import_entry + ]), + [register_reference(Uri, POI) || POI <- POIs] end || Uri <- Uris], {ok, Items} = els_db:match(name(), Pattern), {ok, [to_item(Item) || Item <- Items]}. --spec index_references(els_dt_document:id()) -> ok. -index_references(#{uri := Uri, pois := ondemand} = Document) -> - POIs = els_dt_document:pois(Document, [ application - , behaviour - , implicit_fun - , include - , include_lib - , type_application - , import_entry - ]), - [register_reference(Uri, POI) || POI <- POIs], - ok; -index_references(_) -> - ok. - -spec kind_to_category(poi_kind()) -> poi_category(). kind_to_category(Kind) when Kind =:= application; Kind =:= export_entry; diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl index 642bd1996..a1d0938f1 100644 --- a/apps/els_lsp/src/els_text_search.erl +++ b/apps/els_lsp/src/els_text_search.erl @@ -35,7 +35,8 @@ searchable_paths() -> -spec search_command(string()) -> string(). search_command(IdString) -> - PathsString = string:join(searchable_paths(), " "), + SearchablePaths = [io_lib:format("~p", [Path]) || Path <- searchable_paths()], + PathsString = string:join(SearchablePaths, " "), CmdFormat = els_config:get(search_command), CmdArgs = [IdString, PathsString], lists:flatten(io_lib:format(CmdFormat, CmdArgs)). From 1a59fd81c22613ec4fcf9211cab56e223e37c964 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Mon, 4 Apr 2022 18:21:46 +0200 Subject: [PATCH 14/38] Fixes --- apps/els_lsp/src/els_dt_signatures.erl | 16 +++++++++------- apps/els_lsp/test/els_references_SUITE.erl | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index ff95cff0c..a9894ea9b 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -74,13 +74,15 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> - case els_db:lookup(name(), MFA) of - {ok, []} -> - ok = index_signatures(M), - els_db:lookup(name(), MFA); - {ok, Items} -> - {ok, [to_item(Item) || Item <- Items]} - end. + {ok, Items} = + case els_db:lookup(name(), MFA) of + {ok, []} -> + ok = index_signatures(M), + els_db:lookup(name(), MFA); + Result -> + Result + end, + {ok, [to_item(Item) || Item <- Items]}. -spec delete_by_module(atom()) -> ok. delete_by_module(Module) -> diff --git a/apps/els_lsp/test/els_references_SUITE.erl b/apps/els_lsp/test/els_references_SUITE.erl index cc75b407d..7e7096592 100644 --- a/apps/els_lsp/test/els_references_SUITE.erl +++ b/apps/els_lsp/test/els_references_SUITE.erl @@ -551,10 +551,10 @@ refresh_after_watched_file_added(Config) -> {ok, _} = file:copy(PathC, NewPathC), els_client:did_change_watched_files([{UriC, ?FILE_CHANGE_TYPE_CREATED}]), %% After - ExpectedLocationsAfter = [ #{ uri => UriB + ExpectedLocationsAfter = [ #{ uri => NewUriC , range => #{from => {6, 3}, to => {6, 22}} } - , #{ uri => NewUriC + , #{ uri => UriB , range => #{from => {6, 3}, to => {6, 22}} } ], From afecb3124fc1b30af01eeb01c42d8e2e2e332bff Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 5 Apr 2022 09:00:40 +0200 Subject: [PATCH 15/38] Fix Dialyzer specs --- apps/els_lsp/src/els_text_search.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl index a1d0938f1..fdaa106ce 100644 --- a/apps/els_lsp/src/els_text_search.erl +++ b/apps/els_lsp/src/els_text_search.erl @@ -42,7 +42,7 @@ search_command(IdString) -> lists:flatten(io_lib:format(CmdFormat, CmdArgs)). -spec greppable_string({els_dt_references:poi_category(), any()}) -> - {ok, string()}. + string(). greppable_string({function, {_M, F, _A}}) -> atom_to_greppable_string(F); greppable_string({type, {_M, F, _A}}) -> From 056aa61d5f87189423777a5dbec84e1817e88f2f Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 5 Apr 2022 09:32:40 +0200 Subject: [PATCH 16/38] In memory grep --- apps/els_core/src/els_config.erl | 5 +-- apps/els_lsp/src/els_dt_document.erl | 12 ++++++ apps/els_lsp/src/els_text_search.erl | 64 +++++++++------------------- 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/apps/els_core/src/els_config.erl b/apps/els_core/src/els_config.erl index 7259e046c..4007f3971 100644 --- a/apps/els_core/src/els_config.erl +++ b/apps/els_core/src/els_config.erl @@ -56,8 +56,7 @@ | indexing_enabled | bsp_enabled | compiler_telemetry_enabled - | edoc_custom_tags - | search_command. + | edoc_custom_tags. -type path() :: file:filename(). -type state() :: #{ apps_dirs => [path()] @@ -136,7 +135,6 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) -> CompilerTelemetryEnabled = maps:get("compiler_telemetry_enabled", Config, false), EDocCustomTags = maps:get("edoc_custom_tags", Config, []), - SearchCommand = maps:get("search_command", Config, "grep -l -r \"~s\" ~s"), IndexingEnabled = maps:get(<<"indexingEnabled">>, InitOptions, true), @@ -161,7 +159,6 @@ do_initialize(RootUri, Capabilities, InitOptions, {ConfigPath, Config}) -> ok = set(bsp_enabled, BSPEnabled), ok = set(compiler_telemetry_enabled, CompilerTelemetryEnabled), ok = set(edoc_custom_tags, EDocCustomTags), - ok = set(search_command, SearchCommand), ok = set(incremental_sync, IncrementalSync), ok = set(indexing, maps:merge( els_config_indexing:default_config() , Indexing)), diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index c51f6ebbc..9664a008f 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -31,6 +31,7 @@ , applications_at_pos/3 , wrapping_functions/2 , wrapping_functions/3 + , find_candidates/1 ]). %%============================================================================== @@ -204,3 +205,14 @@ wrapping_functions(Document, Line, Column) -> wrapping_functions(Document, Range) -> #{start := #{character := Character, line := Line}} = Range, wrapping_functions(Document, Line, Character). + +-spec find_candidates(binary()) -> [uri()]. +find_candidates(Pattern) -> + All = ets:tab2list(name()), + Fun = fun(#els_dt_document{uri = Uri, text = Text}) -> + case binary:matches(Text, Pattern) of + [] -> false; + _ -> {true, Uri} + end + end, + lists:filtermap(Fun, All). diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl index fdaa106ce..8aa20a419 100644 --- a/apps/els_lsp/src/els_text_search.erl +++ b/apps/els_lsp/src/els_text_search.erl @@ -12,56 +12,34 @@ %% Includes %%============================================================================== -include("els_lsp.hrl"). --include_lib("kernel/include/logger.hrl"). %%============================================================================== %% API %%============================================================================== -spec find_candidate_uris({els_dt_references:poi_category(), any()}) -> [uri()]. find_candidate_uris(Id) -> - Cmd = search_command(greppable_string(Id)), - ?LOG_DEBUG("Finding candidate uris via: ~p", [Cmd]), - Result = string:trim(os:cmd(Cmd), trailing), - ?LOG_DEBUG("Candidates found: ~p", [Result]), - Candidates = string:split(Result, "\n", all), - [els_uri:uri(els_utils:to_binary(Candidate)) || Candidate <- Candidates]. + String = id_to_binary(Id), + els_dt_document:find_candidates(String). %%============================================================================== %% Internal Functions %%============================================================================== --spec searchable_paths() -> [string()]. -searchable_paths() -> - els_config:get(apps_paths) ++ els_config:get(deps_paths). - --spec search_command(string()) -> string(). -search_command(IdString) -> - SearchablePaths = [io_lib:format("~p", [Path]) || Path <- searchable_paths()], - PathsString = string:join(SearchablePaths, " "), - CmdFormat = els_config:get(search_command), - CmdArgs = [IdString, PathsString], - lists:flatten(io_lib:format(CmdFormat, CmdArgs)). - --spec greppable_string({els_dt_references:poi_category(), any()}) -> - string(). -greppable_string({function, {_M, F, _A}}) -> - atom_to_greppable_string(F); -greppable_string({type, {_M, F, _A}}) -> - atom_to_greppable_string(F); -greppable_string({macro, {Name, _Arity}}) -> - atom_to_greppable_string(Name); -greppable_string({macro, Name}) -> - atom_to_greppable_string(Name); -greppable_string({include, Id}) -> - include_id_to_greppable_string(Id); -greppable_string({include_lib, Id}) -> - include_id_to_greppable_string(Id); -greppable_string({behaviour, Name}) -> - atom_to_greppable_string(Name). - --spec atom_to_greppable_string(atom()) -> string(). -atom_to_greppable_string(Atom) -> - lists:flatten(io_lib:format("~p", [Atom])). - --spec include_id_to_greppable_string(string()) -> string(). -include_id_to_greppable_string(Id) -> - filename:rootname(filename:basename(Id)). +-spec id_to_binary({els_dt_references:poi_category(), any()}) -> binary(). +id_to_binary({function, {_M, F, _A}}) -> + atom_to_binary(F, utf8); +id_to_binary({type, {_M, F, _A}}) -> + atom_to_binary(F, utf8); +id_to_binary({macro, {Name, _Arity}}) -> + atom_to_binary(Name, utf8); +id_to_binary({macro, Name}) -> + atom_to_binary(Name, utf8); +id_to_binary({include, Id}) -> + include_id_to_binary(Id); +id_to_binary({include_lib, Id}) -> + include_id_to_binary(Id); +id_to_binary({behaviour, Name}) -> + atom_to_binary(Name, utf8). + +-spec include_id_to_binary(string()) -> binary(). +include_id_to_binary(Id) -> + els_utils:to_binary(filename:rootname(filename:basename(Id))). From 5ba84b80d5e6c44a86697aa75649e25d0d72937e Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 5 Apr 2022 09:40:41 +0200 Subject: [PATCH 17/38] Do not rely on order --- apps/els_lsp/test/els_call_hierarchy_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/els_lsp/test/els_call_hierarchy_SUITE.erl b/apps/els_lsp/test/els_call_hierarchy_SUITE.erl index cab053942..b5d5330d3 100644 --- a/apps/els_lsp/test/els_call_hierarchy_SUITE.erl +++ b/apps/els_lsp/test/els_call_hierarchy_SUITE.erl @@ -164,7 +164,8 @@ incoming_calls(Config) -> , start => #{character => 2, line => 15} }]} ], - ?assertEqual(Calls, Result). + [?assert(lists:member(Call, Result)) || Call <- Calls], + ?assertEqual(length(Calls), length(Result)). -spec outgoing_calls(config()) -> ok. outgoing_calls(Config) -> From ff720ce26e447bd837826203c2d216b4c48009f0 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Tue, 5 Apr 2022 13:25:55 +0200 Subject: [PATCH 18/38] Refactoring and fixes --- apps/els_core/src/els_utils.erl | 6 +- apps/els_lsp/src/els_dt_document.erl | 49 ++-- apps/els_lsp/src/els_dt_references.erl | 33 +-- apps/els_lsp/src/els_dt_signatures.erl | 31 +-- apps/els_lsp/src/els_index_buffer.erl | 17 +- apps/els_lsp/src/els_indexing.erl | 221 ++++++++++-------- apps/els_lsp/src/els_text_synchronization.erl | 10 +- apps/els_lsp/test/els_indexer_SUITE.erl | 12 +- apps/els_lsp/test/els_indexing_SUITE.erl | 2 +- .../els_lsp/test/els_rebar3_release_SUITE.erl | 4 +- apps/els_lsp/test/els_test_utils.erl | 3 +- 11 files changed, 193 insertions(+), 195 deletions(-) diff --git a/apps/els_core/src/els_utils.erl b/apps/els_core/src/els_utils.erl index 489645a61..56cbf53e4 100644 --- a/apps/els_core/src/els_utils.erl +++ b/apps/els_core/src/els_utils.erl @@ -110,7 +110,7 @@ find_header(Id) -> {ok, Uri}; [] -> FileName = atom_to_list(Id) ++ ".hrl", - els_indexing:find_and_index_file(FileName) + els_indexing:find_and_deeply_index_file(FileName) end. %% @doc Look for a module in the DB @@ -130,7 +130,7 @@ find_modules(Id) -> case [Uri || #{kind := module, uri := Uri} <- Candidates] of [] -> FileName = atom_to_list(Id) ++ ".erl", - case els_indexing:find_and_index_file(FileName) of + case els_indexing:find_and_deeply_index_file(FileName) of {ok, Uri} -> {ok, [Uri]}; Error -> Error end; @@ -150,7 +150,7 @@ lookup_document(Uri) -> {ok, Document}; {ok, []} -> Path = els_uri:path(Uri), - {ok, Uri} = els_indexing:index_file(Path), + {ok, Uri} = els_indexing:shallow_index(Path, app), case els_dt_document:lookup(Uri) of {ok, [Document]} -> {ok, Document}; diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index 9664a008f..cdb85d9e2 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -22,7 +22,7 @@ , delete/1 ]). --export([ new/2 +-export([ new/3 , pois/1 , pois/2 , get_element_at_pos/3 @@ -44,17 +44,20 @@ %%============================================================================== -type id() :: atom(). -type kind() :: module | header | other. +-type source() :: otp | app | dep. +-export_type([source/0]). %%============================================================================== %% Item Definition %%============================================================================== --record(els_dt_document, { uri :: uri() | '_' +-record(els_dt_document, { uri :: uri() | '_' | '$1' , id :: id() | '_' , kind :: kind() | '_' - , text :: binary() | '_' + , text :: binary() | '_' | '$3' , md5 :: binary() | '_' , pois :: [poi()] | '_' | ondemand + , source :: source() | '$2' }). -type els_dt_document() :: #els_dt_document{}. @@ -64,6 +67,7 @@ , text := binary() , md5 => binary() , pois => [poi()] | ondemand + , source => source() }. -export_type([ id/0 , item/0 @@ -92,6 +96,7 @@ from_item(#{ uri := Uri , text := Text , md5 := MD5 , pois := POIs + , source := Source }) -> #els_dt_document{ uri = Uri , id = Id @@ -99,6 +104,7 @@ from_item(#{ uri := Uri , text = Text , md5 = MD5 , pois = POIs + , source = Source }. -spec to_item(els_dt_document()) -> item(). @@ -108,6 +114,7 @@ to_item(#els_dt_document{ uri = Uri , text = Text , md5 = MD5 , pois = POIs + , source = Source }) -> #{ uri => Uri , id => Id @@ -115,6 +122,7 @@ to_item(#els_dt_document{ uri = Uri , text => Text , md5 => MD5 , pois => POIs + , source => Source }. -spec insert(item()) -> ok | {error, any()}. @@ -131,21 +139,21 @@ lookup(Uri) -> delete(Uri) -> els_db:delete(name(), Uri). --spec new(uri(), binary()) -> item(). -new(Uri, Text) -> +-spec new(uri(), binary(), source()) -> item(). +new(Uri, Text, Source) -> Extension = filename:extension(Uri), Id = binary_to_atom(filename:basename(Uri, Extension), utf8), case Extension of <<".erl">> -> - new(Uri, Text, Id, module); + new(Uri, Text, Id, module, Source); <<".hrl">> -> - new(Uri, Text, Id, header); + new(Uri, Text, Id, header, Source); _ -> - new(Uri, Text, Id, other) + new(Uri, Text, Id, other, Source) end. --spec new(uri(), binary(), atom(), kind()) -> item(). -new(Uri, Text, Id, Kind) -> +-spec new(uri(), binary(), atom(), kind(), source()) -> item(). +new(Uri, Text, Id, Kind, Source) -> MD5 = erlang:md5(Text), #{ uri => Uri , id => Id @@ -153,13 +161,14 @@ new(Uri, Text, Id, Kind) -> , text => Text , md5 => MD5 , pois => ondemand + , source => Source }. %% @doc Returns the list of POIs for the current document -spec pois(item()) -> [poi()]. -pois(#{ text := Text, pois := ondemand } = Document) -> - {ok, POIs} = els_parser:parse(Text), - ok = els_dt_document:insert(Document#{pois => POIs}), +pois(#{ uri := Uri, pois := ondemand }) -> + els_indexing:ensure_deeply_indexed(Uri), + {ok, #{pois := POIs}} = els_utils:lookup_document(Uri), POIs; pois(#{ pois := POIs }) -> POIs. @@ -208,8 +217,18 @@ wrapping_functions(Document, Range) -> -spec find_candidates(binary()) -> [uri()]. find_candidates(Pattern) -> - All = ets:tab2list(name()), - Fun = fun(#els_dt_document{uri = Uri, text = Text}) -> + %% ets:fun2ms(fun(#els_dt_document{source = Source, uri = Uri, text = Text}) + %% when Source =/= otp -> {Uri, Text} end). + MS = [{#els_dt_document{ uri = '$1' + , source = '$2' + , kind = '_' + , text = '$3' + , md5 = '_' + ,pois = '_'} + , [{'=/=', '$2', otp}] + , [{{'$1', '$3'}}]}], + All = ets:select(name(), MS), + Fun = fun({Uri, Text}) -> case binary:matches(Text, Pattern) of [] -> false; _ -> {true, Uri} diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index d2b0ed6e3..52bd98882 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -100,18 +100,7 @@ find_by_id(Kind, Id) -> -spec find_by(tuple()) -> {ok, [item()]}. find_by(#els_dt_references{id = Id} = Pattern) -> Uris = els_text_search:find_candidate_uris(Id), - [begin - {ok, Document} = els_utils:lookup_document(Uri), - POIs = els_dt_document:pois(Document, [ application - , behaviour - , implicit_fun - , include - , include_lib - , type_application - , import_entry - ]), - [register_reference(Uri, POI) || POI <- POIs] - end || Uri <- Uris], + [els_indexing:ensure_deeply_indexed(Uri) || Uri <- Uris], {ok, Items} = els_db:match(name(), Pattern), {ok, [to_item(Item) || Item <- Items]}. @@ -139,23 +128,3 @@ kind_to_category(Kind) when Kind =:= include_lib -> include_lib; kind_to_category(Kind) when Kind =:= behaviour -> behaviour. - --spec register_reference(uri(), poi()) -> ok. -register_reference(Uri, #{id := {F, A}} = POI) -> - M = els_uri:module(Uri), - register_reference(Uri, POI#{id => {M, F, A}}); -register_reference(Uri, #{kind := Kind, id := Id, range := Range}) - when %% Include - Kind =:= include; - Kind =:= include_lib; - %% Function - Kind =:= application; - Kind =:= implicit_fun; - Kind =:= import_entry; - %% Type - Kind =:= type_application; - %% Behaviour - Kind =:= behaviour -> - insert( Kind - , #{id => Id, uri => Uri, range => Range} - ). diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index a9894ea9b..cb8ce3184 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -74,37 +74,12 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> - {ok, Items} = - case els_db:lookup(name(), MFA) of - {ok, []} -> - ok = index_signatures(M), - els_db:lookup(name(), MFA); - Result -> - Result - end, + {ok, Uris} = els_utils:find_modules(M), + [els_indexing:ensure_deeply_indexed(Uri) || Uri <- Uris], + {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). - --spec index_signatures(atom()) -> ok. -index_signatures(M) -> - case els_utils:find_module(M) of - {ok, Uri} -> - {ok, #{text := Text} = Document} = els_utils:lookup_document(Uri), - POIs = els_dt_document:pois(Document, [spec]), - [index_signature(M, Text, POI) || POI <- POIs]; - {error, Error} -> - ?LOG_DEBUG("[~p] Cannot find module. [module=~p] [error=~p]", - [?MODULE, M, Error]) - end, - ok. - --spec index_signature(atom(), binary(), poi()) -> ok. -index_signature(M, Text, POI) -> - #{id := {F, A}, range := Range} = POI, - #{from := From, to := To} = Range, - Spec = els_text:range(Text, From, To), - insert(#{ mfa => {M, F, A} , spec => Spec}). diff --git a/apps/els_lsp/src/els_index_buffer.erl b/apps/els_lsp/src/els_index_buffer.erl index 7687cb12d..07db9b52b 100644 --- a/apps/els_lsp/src/els_index_buffer.erl +++ b/apps/els_lsp/src/els_index_buffer.erl @@ -96,12 +96,15 @@ loop() -> -spec do_apply_edits(uri(), [els_text:edit()]) -> ok. do_apply_edits(Uri, Edits0) -> ?LOG_DEBUG("[~p] Processing index request for ~p", [?SERVER, Uri]), - {ok, [#{text := Text0}]} = els_dt_document:lookup(Uri), + {ok, #{text := Text0}} = els_utils:lookup_document(Uri), ?LOG_DEBUG("[~p] Apply edits: ~p", [?SERVER, Edits0]), Text1 = els_text:apply_edits(Text0, Edits0), Text = receive_all(Uri, Text1), ?LOG_DEBUG("[~p] Started indexing ~p", [?SERVER, Uri]), - {Duration, ok} = timer:tc(fun() -> els_indexing:index(Uri, Text, 'deep') end), + {Duration, ok} = timer:tc(fun() -> + Document = els_dt_document:new(Uri, Text, app), + els_indexing:deep_index(Document) + end), ?LOG_DEBUG("[~p] Done indexing ~p [duration: ~pms]", [?SERVER, Uri, Duration div 1000]), ok. @@ -109,9 +112,12 @@ do_apply_edits(Uri, Edits0) -> -spec do_flush(uri()) -> ok. do_flush(Uri) -> ?LOG_DEBUG("[~p] Flushing ~p", [?SERVER, Uri]), - {ok, [#{text := Text0}]} = els_dt_document:lookup(Uri), + {ok, #{text := Text0}} = els_utils:lookup_document(Uri), Text = receive_all(Uri, Text0), - {Duration, ok} = timer:tc(fun() -> els_indexing:index(Uri, Text, 'deep') end), + {Duration, ok} = timer:tc(fun() -> + Document = els_dt_document:new(Uri, Text, app), + els_indexing:deep_index(Document) + end), ?LOG_DEBUG("[~p] Done flushing ~p [duration: ~pms]", [?SERVER, Uri, Duration div 1000]), ok. @@ -121,7 +127,8 @@ do_load(Uri, Text) -> ?LOG_DEBUG("[~p] Loading ~p", [?SERVER, Uri]), {Duration, ok} = timer:tc(fun() -> - els_indexing:index(Uri, Text, 'deep') + Document = els_dt_document:new(Uri, Text, app), + els_indexing:deep_index(Document) end), ?LOG_DEBUG("[~p] Done load ~p [duration: ~pms]", [?SERVER, Uri, Duration div 1000]), diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index d8249c565..6ccae9ada 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -3,12 +3,14 @@ -callback index(els_dt_document:item()) -> ok. %% API --export([ find_and_index_file/1 - , index_file/1 - , index/3 +-export([ find_and_deeply_index_file/1 , index_dir/2 , start/0 , maybe_start/0 + , ensure_deeply_indexed/1 + , shallow_index/2 + , deep_index/1 + , remove/1 ]). %%============================================================================== @@ -20,48 +22,30 @@ %%============================================================================== %% Types %%============================================================================== --type mode() :: 'deep' | 'shallow'. %%============================================================================== %% Exported functions %%============================================================================== --spec find_and_index_file(string()) -> +-spec find_and_deeply_index_file(string()) -> {ok, uri()} | {error, any()}. -find_and_index_file(FileName) -> +find_and_deeply_index_file(FileName) -> SearchPaths = els_config:get(search_paths), case file:path_open( SearchPaths , els_utils:to_binary(FileName) , [read] ) of - {ok, IoDevice, FullName} -> + {ok, IoDevice, Path} -> %% TODO: Avoid opening file twice file:close(IoDevice), - index_file(FullName); + Uri = els_uri:uri(Path), + ensure_deeply_indexed(Uri), + {ok, Uri}; {error, Error} -> {error, Error} end. --spec index_file(binary()) -> {ok, uri()}. -index_file(Path) -> - GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - try_index_file(Path, 'deep', false, GeneratedFilesTag), - {ok, els_uri:uri(Path)}. - --spec index_if_not_generated(uri(), binary(), mode(), boolean(), string()) -> - ok | skipped. -index_if_not_generated(Uri, Text, Mode, false, _GeneratedFilesTag) -> - index(Uri, Text, Mode); -index_if_not_generated(Uri, Text, Mode, true, GeneratedFilesTag) -> - case is_generated_file(Text, GeneratedFilesTag) of - true -> - ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), - skipped; - false -> - ok = index(Uri, Text, Mode) - end. - -spec is_generated_file(binary(), string()) -> boolean(). is_generated_file(Text, Tag) -> [Line|_] = string:split(Text, "\n", leading), @@ -72,32 +56,83 @@ is_generated_file(Text, Tag) -> false end. --spec index(uri(), binary(), mode()) -> ok. -index(Uri, Text, Mode) -> - MD5 = erlang:md5(Text), - case els_dt_document:lookup(Uri) of - {ok, [#{md5 := MD5}]} -> +-spec ensure_deeply_indexed(uri()) -> ok. +ensure_deeply_indexed(Uri) -> + {ok, #{pois := POIs} = Document} = els_utils:lookup_document(Uri), + case POIs of + ondemand -> + deep_index(Document); + _ -> + ok + end. + +-spec deep_index(els_dt_document:item()) -> ok. +deep_index(#{id := Id, uri := Uri, text := Text, source := Source} = Document) -> + {ok, POIs} = els_parser:parse(Text), + ok = els_dt_document:insert(Document#{pois => POIs}), + index_signatures(Id, Text, POIs), + case Source of + otp -> ok; - {ok, LookupResult} -> - Document = els_dt_document:new(Uri, Text), - ok = els_dt_document:insert(Document), - #{id := Id, kind := Kind} = Document, - ModuleItem = els_dt_document_index:new(Id, Uri, Kind), - ok = els_dt_document_index:insert(ModuleItem), - case Mode of - shallow -> - ok; - deep -> - case LookupResult of - [] -> - ok; - _ -> - ok = els_dt_references:delete_by_uri(Uri), - ok = els_dt_signatures:delete_by_module(els_uri:module(Uri)) - end - end + S when S =:= app orelse S =:= dep -> + index_references(Id, Uri, POIs) end. +-spec index_signatures(atom(), binary(), [poi()]) -> ok. +index_signatures(Id, Text, POIs) -> + ok = els_dt_signatures:delete_by_module(Id), + [index_signature(Id, Text, POI) || #{kind := spec} = POI <- POIs], + ok. + +-spec index_signature(atom(), binary(), poi()) -> ok. +index_signature(_M, _Text, #{id := undefined}) -> + ok; +index_signature(M, Text, #{id := {F, A}, range := Range}) -> + #{from := From, to := To} = Range, + Spec = els_text:range(Text, From, To), + els_dt_signatures:insert(#{ mfa => {M, F, A}, spec => Spec}). + +-spec index_references(atom(), uri(), [poi()]) -> ok. +index_references(Id, Uri, POIs) -> + ok = els_dt_references:delete_by_uri(Uri), + ReferenceKinds = [ %% Function + application + , implicit_fun + , import_entry + %% Include + , include + , include_lib + %% Behaviour + , behaviour + %% Type + , type_application + ], + [index_reference(Id, Uri, POI) + || #{kind := Kind} = POI <- POIs, + lists:member(Kind, ReferenceKinds)], + ok. + +-spec index_reference(atom(), uri(), poi()) -> ok. +index_reference(M, Uri, #{id := {F, A}} = POI) -> + index_reference(M, Uri, POI#{id => {M, F, A}}); +index_reference(_M, Uri, #{kind := Kind, id := Id, range := Range}) -> + els_dt_references:insert(Kind, #{id => Id, uri => Uri, range => Range}). + +-spec shallow_index(binary(), els_dt_document:source()) -> {ok, uri()}. +shallow_index(Path, Source) -> + Uri = els_uri:uri(Path), + {ok, Text} = file:read_file(Path), + shallow_index(Uri, Text, Source), + {ok, Uri}. + +-spec shallow_index(uri(), binary(), els_dt_document:source()) -> ok. +shallow_index(Uri, Text, Source) -> + Document = els_dt_document:new(Uri, Text, Source), + ok = els_dt_document:insert(Document), + #{id := Id, kind := Kind} = Document, + ModuleItem = els_dt_document_index:new(Id, Uri, Kind), + ok = els_dt_document_index:insert(ModuleItem). + -spec maybe_start() -> true | false. maybe_start() -> IndexingEnabled = els_config:get(indexing_enabled), @@ -111,17 +146,17 @@ maybe_start() -> -spec start() -> ok. start() -> - start(<<"OTP">>, entries_otp()), - start(<<"Applications">>, entries_apps()), - start(<<"Dependencies">>, entries_deps()). + start(<<"OTP">>, els_config:get(otp_paths), otp), + start(<<"Applications">>, els_config:get(apps_paths), app), + start(<<"Dependencies">>, els_config:get(deps_paths), dep). --spec start(binary(), [{string(), 'deep' | 'shallow'}]) -> ok. -start(Group, Entries) -> +-spec start(binary(), [string()], els_dt_document:source()) -> ok. +start(Group, Entries, Source) -> SkipGeneratedFiles = els_config_indexing:get_skip_generated_files(), GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - Task = fun({Dir, Mode}, {Succeeded0, Skipped0, Failed0}) -> - {Su, Sk, Fa} = index_dir(Dir, Mode, - SkipGeneratedFiles, GeneratedFilesTag), + Task = fun(Dir, {Succeeded0, Skipped0, Failed0}) -> + {Su, Sk, Fa} = + index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source), {Succeeded0 + Su, Skipped0 + Sk, Failed0 + Fa} end, Config = #{ task => Task @@ -138,45 +173,49 @@ start(Group, Entries) -> {ok, _Pid} = els_background_job:new(Config), ok. +-spec remove(uri()) -> ok. +remove(Uri) -> + 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)). + %%============================================================================== %% Internal functions %%============================================================================== - -%% @doc Try indexing a file. --spec try_index_file(binary(), mode(), boolean(), string()) -> - ok | skipped | {error, any()}. -try_index_file(FullName, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> +-spec shallow_index(binary(), boolean(), string(), els_dt_document:source()) -> + ok | skipped. +shallow_index(FullName, SkipGeneratedFiles, GeneratedFilesTag, Source) -> Uri = els_uri:uri(FullName), - try - ?LOG_DEBUG("Indexing file. [filename=~s, uri=~s]", [FullName, Uri]), - {ok, Text} = file:read_file(FullName), - index_if_not_generated(Uri, Text, Mode, - SkipGeneratedFiles, GeneratedFilesTag) - catch Type:Reason:St -> - ?LOG_ERROR("Error indexing file " - "[filename=~s, uri=~s] " - "~p:~p:~p", [FullName, Uri, Type, Reason, St]), - {error, {Type, Reason}} + ?LOG_DEBUG("Shallow indexing file. [filename=~s] [uri=~s]", + [FullName, Uri]), + {ok, Text} = file:read_file(FullName), + case SkipGeneratedFiles andalso is_generated_file(Text, GeneratedFilesTag) of + true -> + ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), + skipped; + false -> + shallow_index(Uri, Text, Source) end. --spec index_dir(string(), mode()) -> +-spec index_dir(string(), els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. -index_dir(Dir, Mode) -> +index_dir(Dir, Source) -> SkipGeneratedFiles = els_config_indexing:get_skip_generated_files(), GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag). + index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source). --spec index_dir(string(), mode(), boolean(), string()) -> +-spec index_dir(string(), boolean(), string(), els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. -index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> - ?LOG_DEBUG("Indexing directory. [dir=~s] [mode=~s]", [Dir, Mode]), +index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source) -> + ?LOG_DEBUG("Indexing directory. [dir=~s]", [Dir]), F = fun(FileName, {Succeeded, Skipped, Failed}) -> - case try_index_file(els_utils:to_binary(FileName), Mode, - SkipGeneratedFiles, GeneratedFilesTag) of + case + shallow_index(els_utils:to_binary(FileName), + SkipGeneratedFiles, GeneratedFilesTag, Source) of ok -> {Succeeded + 1, Skipped, Failed}; - skipped -> {Succeeded, Skipped + 1, Failed}; - {error, _Error} -> {Succeeded, Skipped, Failed + 1} + skipped -> {Succeeded, Skipped + 1, Failed} end end, Filter = fun(Path) -> @@ -192,19 +231,7 @@ index_dir(Dir, Mode, SkipGeneratedFiles, GeneratedFilesTag) -> , {0, 0, 0} ] ), - ?LOG_DEBUG("Finished indexing directory. [dir=~s] [mode=~s] [time=~p] " + ?LOG_DEBUG("Finished indexing directory. [dir=~s] [time=~p] " "[succeeded=~p] [skipped=~p] [failed=~p]", - [Dir, Mode, Time/1000/1000, Succeeded, Skipped, Failed]), + [Dir, Time/1000/1000, Succeeded, Skipped, Failed]), {Succeeded, Skipped, Failed}. - --spec entries_apps() -> [{string(), 'deep' | 'shallow'}]. -entries_apps() -> - [{Dir, 'shallow'} || Dir <- els_config:get(apps_paths)]. - --spec entries_deps() -> [{string(), 'deep' | 'shallow'}]. -entries_deps() -> - [{Dir, 'shallow'} || Dir <- els_config:get(deps_paths)]. - --spec entries_otp() -> [{string(), 'deep' | 'shallow'}]. -entries_otp() -> - [{Dir, 'shallow'} || Dir <- els_config:get(otp_paths)]. diff --git a/apps/els_lsp/src/els_text_synchronization.erl b/apps/els_lsp/src/els_text_synchronization.erl index abca45edf..ec8a29a60 100644 --- a/apps/els_lsp/src/els_text_synchronization.erl +++ b/apps/els_lsp/src/els_text_synchronization.erl @@ -30,7 +30,10 @@ did_change(Params) -> %% Full text sync #{<<"text">> := Text} = Change, {Duration, ok} = - timer:tc(fun() -> els_indexing:index(Uri, Text, 'deep') end), + timer:tc(fun() -> + {ok, Document} = els_utils:lookup_document(Uri), + els_indexing:deep_index(Document) + end), ?LOG_DEBUG("didChange FULLSYNC [size: ~p] [duration: ~pms]\n", [size(Text), Duration div 1000]), ok; @@ -91,7 +94,4 @@ handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_CREATED; 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)). + els_indexing:remove(Uri). diff --git a/apps/els_lsp/test/els_indexer_SUITE.erl b/apps/els_lsp/test/els_indexer_SUITE.erl index ee0adccb9..dd97c4787 100644 --- a/apps/els_lsp/test/els_indexer_SUITE.erl +++ b/apps/els_lsp/test/els_indexer_SUITE.erl @@ -92,7 +92,7 @@ index_dir_not_dir(Config) -> index_erl_file(Config) -> DataDir = ?config(data_dir, Config), Path = filename:join(els_utils:to_binary(DataDir), "test.erl"), - {ok, Uri} = els_indexing:index_file(Path), + {ok, Uri} = els_indexing:shallow_index(Path, app), {ok, [#{id := test, kind := module}]} = els_dt_document:lookup(Uri), ok. @@ -100,7 +100,7 @@ index_erl_file(Config) -> index_hrl_file(Config) -> DataDir = ?config(data_dir, Config), Path = filename:join(els_utils:to_binary(DataDir), "test.hrl"), - {ok, Uri} = els_indexing:index_file(Path), + {ok, Uri} = els_indexing:shallow_index(Path, app), {ok, [#{id := test, kind := header}]} = els_dt_document:lookup(Uri), ok. @@ -108,7 +108,7 @@ index_hrl_file(Config) -> index_unkown_extension(Config) -> DataDir = ?config(data_dir, Config), Path = filename:join(els_utils:to_binary(DataDir), "test.foo"), - {ok, Uri} = els_indexing:index_file(Path), + {ok, Uri} = els_indexing:shallow_index(Path, app), {ok, [#{kind := other}]} = els_dt_document:lookup(Uri), ok. @@ -117,7 +117,7 @@ do_not_skip_generated_file_by_tag_by_default(Config) -> DataDir = data_dir(Config), GeneratedByTagUri = uri(DataDir, "generated_file_by_tag.erl"), GeneratedByCustomTagUri = uri(DataDir, "generated_file_by_custom_tag.erl"), - ?assertEqual({4, 0, 0}, els_indexing:index_dir(DataDir, 'deep')), + ?assertEqual({4, 0, 0}, els_indexing:index_dir(DataDir, app)), {ok, [#{ id := generated_file_by_tag , kind := module } @@ -133,7 +133,7 @@ skip_generated_file_by_tag(Config) -> DataDir = data_dir(Config), GeneratedByTagUri = uri(DataDir, "generated_file_by_tag.erl"), GeneratedByCustomTagUri = uri(DataDir, "generated_file_by_custom_tag.erl"), - ?assertEqual({3, 1, 0}, els_indexing:index_dir(DataDir, 'deep')), + ?assertEqual({3, 1, 0}, els_indexing:index_dir(DataDir, app)), {ok, []} = els_dt_document:lookup(GeneratedByTagUri), {ok, [#{ id := generated_file_by_custom_tag , kind := module @@ -146,7 +146,7 @@ skip_generated_file_by_custom_tag(Config) -> DataDir = data_dir(Config), GeneratedByTagUri = uri(DataDir, "generated_file_by_tag.erl"), GeneratedByCustomTagUri = uri(DataDir, "generated_file_by_custom_tag.erl"), - ?assertEqual({3, 1, 0}, els_indexing:index_dir(DataDir, 'deep')), + ?assertEqual({3, 1, 0}, els_indexing:index_dir(DataDir, app)), {ok, [#{ id := generated_file_by_tag , kind := module } diff --git a/apps/els_lsp/test/els_indexing_SUITE.erl b/apps/els_lsp/test/els_indexing_SUITE.erl index 212024754..40673f9d9 100644 --- a/apps/els_lsp/test/els_indexing_SUITE.erl +++ b/apps/els_lsp/test/els_indexing_SUITE.erl @@ -68,7 +68,7 @@ reindex_otp(_Config) -> -spec do_index_otp() -> ok. do_index_otp() -> - [els_indexing:index_dir(Dir, 'shallow') || Dir <- els_config:get(otp_paths)], + [els_indexing:index_dir(Dir, otp) || Dir <- els_config:get(otp_paths)], ok. -spec otp_apps_exclude() -> [string()]. diff --git a/apps/els_lsp/test/els_rebar3_release_SUITE.erl b/apps/els_lsp/test/els_rebar3_release_SUITE.erl index fce638d8d..4872e2f8c 100644 --- a/apps/els_lsp/test/els_rebar3_release_SUITE.erl +++ b/apps/els_lsp/test/els_rebar3_release_SUITE.erl @@ -65,8 +65,8 @@ init_per_testcase(_TestCase, Config) -> els_client:initialize(RootUri), {ok, AppText} = file:read_file(els_uri:path(AppUri)), els_client:did_open(AppUri, erlang, 1, AppText), - els_indexing:find_and_index_file("rebar3_release_app.erl"), - els_indexing:find_and_index_file("rebar3_release_sup.erl"), + els_indexing:find_and_deeply_index_file("rebar3_release_app.erl"), + els_indexing:find_and_deeply_index_file("rebar3_release_sup.erl"), [{started, Started}|Config]. -spec end_per_testcase(atom(), config()) -> ok. diff --git a/apps/els_lsp/test/els_test_utils.erl b/apps/els_lsp/test/els_test_utils.erl index 2d96aa3a1..a2fe3d11e 100644 --- a/apps/els_lsp/test/els_test_utils.erl +++ b/apps/els_lsp/test/els_test_utils.erl @@ -130,7 +130,8 @@ includes() -> %% accessing this information from test cases. -spec index_file(binary()) -> [{atom(), any()}]. index_file(Path) -> - {ok, Uri} = els_indexing:index_file(Path), + Uri = els_uri:uri(Path), + ok = els_indexing:ensure_deeply_indexed(Uri), {ok, Text} = file:read_file(Path), ConfigId = config_id(Path), [ {atoms_append(ConfigId, '_path'), Path} From de7f47a9fad15caa359ad091145887628cda3360 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 6 Apr 2022 15:09:30 +0200 Subject: [PATCH 19/38] One buffer per document --- apps/els_lsp/src/els_buffer_server.erl | 114 ++++++++++++++ apps/els_lsp/src/els_buffer_sup.erl | 47 ++++++ apps/els_lsp/src/els_completion_provider.erl | 13 +- apps/els_lsp/src/els_dt_document.erl | 12 +- apps/els_lsp/src/els_dt_signatures.erl | 4 +- apps/els_lsp/src/els_index_buffer.erl | 147 ------------------ apps/els_lsp/src/els_indexing.erl | 3 +- apps/els_lsp/src/els_sup.erl | 10 +- apps/els_lsp/src/els_text_synchronization.erl | 24 +-- 9 files changed, 207 insertions(+), 167 deletions(-) create mode 100644 apps/els_lsp/src/els_buffer_server.erl create mode 100644 apps/els_lsp/src/els_buffer_sup.erl delete mode 100644 apps/els_lsp/src/els_index_buffer.erl diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl new file mode 100644 index 000000000..4a259aa60 --- /dev/null +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -0,0 +1,114 @@ +%%%============================================================================= +%%% @doc Buffer edits to an open buffer to avoid re-indexing too often. +%%% @end +%%%============================================================================= +-module(els_buffer_server). + +%%============================================================================== +%% API +%%============================================================================== +-export([ new/2 + , stop/1 + , apply_edits/2 + , flush/1 + ]). + +-export([ start_link/2 ]). + +%%============================================================================== +%% Callbacks for the gen_server behaviour +%%============================================================================== +-behaviour(gen_server). +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + ]). + +%%============================================================================== +%% Macro Definitions +%%============================================================================== +-define(FLUSH_DELAY, 200). %% ms + +%%============================================================================== +%% Type Definitions +%%============================================================================== +-type text() :: binary(). +-type state() :: #{ uri := uri() + , text := text() + , ref := undefined | reference() + }. +-type buffer() :: pid(). + +%%============================================================================== +%% Includes +%%============================================================================== +-include_lib("kernel/include/logger.hrl"). +-include("els_lsp.hrl"). + +%%============================================================================== +%% API +%%============================================================================== +-spec new(uri(), text()) -> {ok, pid()}. +new(Uri, Text) -> + supervisor:start_child(els_buffer_sup, [Uri, Text]). + +-spec stop(buffer()) -> ok. +stop(Buffer) -> + supervisor:terminate_child(els_buffer_sup, Buffer). + +-spec apply_edits(buffer(), [els_text:edit()]) -> ok. +apply_edits(Buffer, Edits) -> + gen_server:cast(Buffer, {apply_edits, Edits}). + +-spec flush(buffer()) -> ok. +flush(Buffer) -> + gen_server:call(Buffer, {flush}). + +-spec start_link(uri(), text()) -> {ok, buffer()}. +start_link(Uri, Text) -> + gen_server:start_link(?MODULE, {Uri, Text}, []). + +%%============================================================================== +%% Callbacks for the gen_server behaviour +%%============================================================================== +-spec init({uri(), text()}) -> {ok, state()}. +init({Uri, Text}) -> + do_flush(Uri, Text), + {ok, #{ uri => Uri, text => Text, ref => undefined }}. + +-spec handle_call(any(), {pid(), any()}, state()) -> {reply, any(), state()}. +handle_call({flush}, _From, #{uri := Uri, text := Text} = State) -> + ?LOG_DEBUG("[~p] Flushing request [uri=~p]", [?MODULE, Uri]), + do_flush(Uri, Text), + {reply, ok, State}; +handle_call(Request, _From, State) -> + {reply, {not_implemented, Request}, State}. + +-spec handle_cast(any(), state()) -> {noreply, state()}. +handle_cast({apply_edits, Edits}, #{uri := Uri} = State) -> + ?LOG_DEBUG("[~p] Applying edits [uri=~p]", [?MODULE, Uri]), + #{text := Text0, ref := Ref0} = State, + case Ref0 of + undefined -> ok; + _ -> erlang:cancel_timer(Ref0) + end, + Text = els_text:apply_edits(Text0, Edits), + Ref = erlang:send_after(?FLUSH_DELAY, self(), flush), + {noreply, State#{text => Text, ref => Ref}}. + +-spec handle_info(any(), state()) -> {noreply, state()}. +handle_info(flush, #{uri := Uri, text := Text} = State) -> + ?LOG_DEBUG("[~p] Scheduled flushing [uri=~p]", [?MODULE, Uri]), + do_flush(Uri, Text), + {noreply, State}; +handle_info(_Request, State) -> + {noreply, State}. + +%%============================================================================== +%% Internal Functions +%%============================================================================== +-spec do_flush(uri(), text()) -> ok. +do_flush(Uri, Text) -> + {ok, Document} = els_utils:lookup_document(Uri), + els_indexing:deep_index(Document#{text => Text, buffer => self()}). diff --git a/apps/els_lsp/src/els_buffer_sup.erl b/apps/els_lsp/src/els_buffer_sup.erl new file mode 100644 index 000000000..c122983ea --- /dev/null +++ b/apps/els_lsp/src/els_buffer_sup.erl @@ -0,0 +1,47 @@ +%%============================================================================== +%% Supervisor for Buffers +%%============================================================================== +-module(els_buffer_sup). + +%%============================================================================== +%% Behaviours +%%============================================================================== +-behaviour(supervisor). + +%%============================================================================== +%% Exports +%%============================================================================== + +%% API +-export([ start_link/0 ]). + +%% Supervisor Callbacks +-export([ init/1 ]). + +%%============================================================================== +%% Defines +%%============================================================================== +-define(SERVER, ?MODULE). + +%%============================================================================== +%% API +%%============================================================================== +-spec start_link() -> {ok, pid()}. +start_link() -> + supervisor:start_link({local, ?SERVER}, ?MODULE, []). + +%%============================================================================== +%% Supervisor callbacks +%%============================================================================== +-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. +init([]) -> + SupFlags = #{ strategy => simple_one_for_one + , intensity => 5 + , period => 60 + }, + ChildSpecs = [#{ id => els_buffer_sup + , start => {els_buffer_server, start_link, []} + , restart => temporary + , shutdown => 5000 + }], + {ok, {SupFlags, ChildSpecs}}. diff --git a/apps/els_lsp/src/els_completion_provider.erl b/apps/els_lsp/src/els_completion_provider.erl index a8d5c2650..01aee9eb8 100644 --- a/apps/els_lsp/src/els_completion_provider.erl +++ b/apps/els_lsp/src/els_completion_provider.erl @@ -44,8 +44,17 @@ handle_request({completion, Params}, State) -> } , <<"textDocument">> := #{<<"uri">> := Uri} } = Params, - ok = els_index_buffer:flush(Uri), - {ok, #{text := Text} = Document} = els_utils:lookup_document(Uri), + {ok, Document} = els_utils:lookup_document(Uri), + #{text := Text, buffer := Buffer} = Document, + %% Ensure there are no pending changes. This causes a heavy sync + %% point, since for a big module indexing could take a while. Maybe + %% just do a 'soft' flush and avoid indexing here? + case Buffer of + undefined -> + ok; + _ -> + ok = els_buffer_server:flush(Buffer) + end, Context = maps:get( <<"context">> , Params , #{ <<"triggerKind">> => ?COMPLETION_TRIGGER_KIND_INVOKED } diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index cdb85d9e2..34010dda4 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -45,6 +45,7 @@ -type id() :: atom(). -type kind() :: module | header | other. -type source() :: otp | app | dep. +-type buffer() :: pid(). -export_type([source/0]). %%============================================================================== @@ -58,6 +59,7 @@ , md5 :: binary() | '_' , pois :: [poi()] | '_' | ondemand , source :: source() | '$2' + , buffer :: buffer() | '_' | undefined }). -type els_dt_document() :: #els_dt_document{}. @@ -68,6 +70,7 @@ , md5 => binary() , pois => [poi()] | ondemand , source => source() + , buffer => buffer() | undefined }. -export_type([ id/0 , item/0 @@ -97,6 +100,7 @@ from_item(#{ uri := Uri , md5 := MD5 , pois := POIs , source := Source + , buffer := Buffer }) -> #els_dt_document{ uri = Uri , id = Id @@ -105,6 +109,7 @@ from_item(#{ uri := Uri , md5 = MD5 , pois = POIs , source = Source + , buffer = Buffer }. -spec to_item(els_dt_document()) -> item(). @@ -115,6 +120,7 @@ to_item(#els_dt_document{ uri = Uri , md5 = MD5 , pois = POIs , source = Source + , buffer = Buffer }) -> #{ uri => Uri , id => Id @@ -123,6 +129,7 @@ to_item(#els_dt_document{ uri = Uri , md5 => MD5 , pois => POIs , source => Source + , buffer => Buffer }. -spec insert(item()) -> ok | {error, any()}. @@ -162,6 +169,7 @@ new(Uri, Text, Id, Kind, Source) -> , md5 => MD5 , pois => ondemand , source => Source + , buffer => undefined }. %% @doc Returns the list of POIs for the current document @@ -221,10 +229,12 @@ find_candidates(Pattern) -> %% when Source =/= otp -> {Uri, Text} end). MS = [{#els_dt_document{ uri = '$1' , source = '$2' + , buffer = '_' , kind = '_' , text = '$3' , md5 = '_' - ,pois = '_'} + , pois = '_' + } , [{'=/=', '$2', otp}] , [{{'$1', '$3'}}]}], All = ets:select(name(), MS), diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index cb8ce3184..c8379fbd6 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -74,8 +74,8 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> - {ok, Uris} = els_utils:find_modules(M), - [els_indexing:ensure_deeply_indexed(Uri) || Uri <- Uris], + %% By finding a module, we also ensure the module is deeply indexed. + {ok, _Uris} = els_utils:find_modules(M), {ok, Items} = els_db:lookup(name(), MFA), {ok, [to_item(Item) || Item <- Items]}. diff --git a/apps/els_lsp/src/els_index_buffer.erl b/apps/els_lsp/src/els_index_buffer.erl deleted file mode 100644 index 07db9b52b..000000000 --- a/apps/els_lsp/src/els_index_buffer.erl +++ /dev/null @@ -1,147 +0,0 @@ -%% @doc Buffer textedits to avoid spamming indexing for every keystroke -%% -%% FIXME: Currently implemented as a simple process. -%% Might be nicer to rewrite this as a gen_server -%% FIXME: Edits are bottlenecked by this single process, so this could slow -%% down indexing when making changes in multiple files, this could be -%% mitigated by using a worker pool or spawning a process per Uri. --module(els_index_buffer). - --export([ start/0 - , stop/0 - , apply_edits_async/2 - , flush/1 - , load/2 - ]). - --include_lib("kernel/include/logger.hrl"). --include("els_lsp.hrl"). - --define(SERVER, ?MODULE). - --spec start() -> {ok, pid()} | {error, _}. -start() -> - case whereis(?SERVER) of - undefined -> - Pid = proc_lib:spawn_link(fun loop/0), - true = erlang:register(?SERVER, Pid), - ?LOG_INFO("[~p] Started.", [?SERVER]), - {ok, Pid}; - Pid -> - {error, {already_started, Pid}} - end. - --spec stop() -> ok. -stop() -> - ?SERVER ! stop, - erlang:unregister(?SERVER), - ?LOG_INFO("[~p] Stopped.", [?SERVER]), - ok. - --spec apply_edits_async(uri(), [els_text:edit()]) -> ok. -apply_edits_async(Uri, Edits) -> - ?SERVER ! {apply_edits, Uri, Edits}, - ok. - --spec load(uri(), binary()) -> ok. -load(Uri, Text) -> - Ref = make_ref(), - ?SERVER ! {load, self(), Ref, Uri, Text}, - receive - {Ref, done} -> - ok - end. - --spec flush(uri()) -> ok. -flush(Uri) -> - Ref = make_ref(), - ?SERVER ! {flush, self(), Ref, Uri}, - receive - {Ref, done} -> - ok - end. - --spec loop() -> ok. -loop() -> - receive - stop -> ok; - {apply_edits, Uri, Edits} -> - try - do_apply_edits(Uri, Edits) - catch E:R:St -> - ?LOG_ERROR("[~p] Crashed while applying edits ~p: ~p", - [?SERVER, Uri, {E, R, St}]) - end, - loop(); - {flush, Pid, Ref, Uri} -> - try - do_flush(Uri) - catch E:R:St -> - ?LOG_ERROR("[~p] Crashed while flushing ~p: ~p", - [?SERVER, Uri, {E, R, St}]) - end, - Pid ! {Ref, done}, - loop(); - {load, Pid, Ref, Uri, Text} -> - try - do_load(Uri, Text) - catch E:R:St -> - ?LOG_ERROR("[~p] Crashed while loading ~p: ~p", - [?SERVER, Uri, {E, R, St}]) - end, - Pid ! {Ref, done}, - loop() - end. - --spec do_apply_edits(uri(), [els_text:edit()]) -> ok. -do_apply_edits(Uri, Edits0) -> - ?LOG_DEBUG("[~p] Processing index request for ~p", [?SERVER, Uri]), - {ok, #{text := Text0}} = els_utils:lookup_document(Uri), - ?LOG_DEBUG("[~p] Apply edits: ~p", [?SERVER, Edits0]), - Text1 = els_text:apply_edits(Text0, Edits0), - Text = receive_all(Uri, Text1), - ?LOG_DEBUG("[~p] Started indexing ~p", [?SERVER, Uri]), - {Duration, ok} = timer:tc(fun() -> - Document = els_dt_document:new(Uri, Text, app), - els_indexing:deep_index(Document) - end), - ?LOG_DEBUG("[~p] Done indexing ~p [duration: ~pms]", - [?SERVER, Uri, Duration div 1000]), - ok. - --spec do_flush(uri()) -> ok. -do_flush(Uri) -> - ?LOG_DEBUG("[~p] Flushing ~p", [?SERVER, Uri]), - {ok, #{text := Text0}} = els_utils:lookup_document(Uri), - Text = receive_all(Uri, Text0), - {Duration, ok} = timer:tc(fun() -> - Document = els_dt_document:new(Uri, Text, app), - els_indexing:deep_index(Document) - end), - ?LOG_DEBUG("[~p] Done flushing ~p [duration: ~pms]", - [?SERVER, Uri, Duration div 1000]), - ok. - --spec do_load(uri(), binary()) -> ok. -do_load(Uri, Text) -> - ?LOG_DEBUG("[~p] Loading ~p", [?SERVER, Uri]), - {Duration, ok} = - timer:tc(fun() -> - Document = els_dt_document:new(Uri, Text, app), - els_indexing:deep_index(Document) - end), - ?LOG_DEBUG("[~p] Done load ~p [duration: ~pms]", - [?SERVER, Uri, Duration div 1000]), - ok. - --spec receive_all(uri(), binary()) -> binary(). -receive_all(Uri, Text0) -> - receive - {apply_edits, Uri, Edits} -> - ?LOG_DEBUG("[~p] Received more edits ~p.", [?SERVER, Uri]), - ?LOG_DEBUG("[~p] Apply edits: ~p", [?SERVER, Edits]), - Text = els_text:apply_edits(Text0, Edits), - receive_all(Uri, Text) - after - 0 -> Text0 - end. diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 6ccae9ada..597c91cbf 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -67,7 +67,8 @@ ensure_deeply_indexed(Uri) -> end. -spec deep_index(els_dt_document:item()) -> ok. -deep_index(#{id := Id, uri := Uri, text := Text, source := Source} = Document) -> +deep_index(Document) -> + #{id := Id, uri := Uri, text := Text, source := Source} = Document, {ok, POIs} = els_parser:parse(Text), ok = els_dt_document:insert(Document#{pois => POIs}), index_signatures(Id, Text, POIs), diff --git a/apps/els_lsp/src/els_sup.erl b/apps/els_lsp/src/els_sup.erl index 1a6d9efda..8fbe5bec2 100644 --- a/apps/els_lsp/src/els_sup.erl +++ b/apps/els_lsp/src/els_sup.erl @@ -67,14 +67,14 @@ init([]) -> , start => {els_distribution_sup, start_link, []} , type => supervisor } - , #{ id => els_snippets_server - , start => {els_snippets_server, start_link, []} + , #{ id => els_snippets_server + , start => {els_snippets_server, start_link, []} } - , #{ id => els_bsp_client + , #{ id => els_bsp_client , start => {els_bsp_client, start_link, []} } - , #{ id => els_index_buffer - , start => {els_index_buffer, start, []} + , #{ id => els_buffer_sup + , start => {els_buffer_sup, start_link, []} } , #{ id => els_server , start => {els_server, start_link, []} diff --git a/apps/els_lsp/src/els_text_synchronization.erl b/apps/els_lsp/src/els_text_synchronization.erl index ec8a29a60..0647107f2 100644 --- a/apps/els_lsp/src/els_text_synchronization.erl +++ b/apps/els_lsp/src/els_text_synchronization.erl @@ -42,7 +42,10 @@ did_change(Params) -> ?LOG_DEBUG("didChange INCREMENTAL [changes: ~p]", [ContentChanges]), Edits = [to_edit(Change) || Change <- ContentChanges], {Duration, ok} = - timer:tc(fun() -> els_index_buffer:apply_edits_async(Uri, Edits) end), + timer:tc(fun() -> + {ok, #{buffer := Buffer}} = els_utils:lookup_document(Uri), + els_buffer_server:apply_edits(Buffer, Edits) + end), ?LOG_DEBUG("didChange INCREMENTAL [duration: ~pms]\n", [Duration div 1000]), ok @@ -52,8 +55,7 @@ did_change(Params) -> did_open(Params) -> #{<<"textDocument">> := #{ <<"uri">> := Uri , <<"text">> := Text}} = Params, - ok = els_index_buffer:load(Uri, Text), - ok = els_index_buffer:flush(Uri), + {ok, _Buffer} = els_buffer_server:new(Uri, Text), Provider = els_diagnostics_provider, els_provider:handle_request(Provider, {run_diagnostics, Params}), ok. @@ -61,9 +63,7 @@ did_open(Params) -> -spec did_save(map()) -> ok. did_save(Params) -> #{<<"textDocument">> := #{<<"uri">> := Uri}} = Params, - {ok, Text} = file:read_file(els_uri:path(Uri)), - ok = els_index_buffer:load(Uri, Text), - ok = els_index_buffer:flush(Uri), + reload_from_disk(Uri), Provider = els_diagnostics_provider, els_provider:handle_request(Provider, {run_diagnostics, Params}), ok. @@ -90,8 +90,14 @@ to_edit(#{<<"text">> := Text, <<"range">> := Range}) -> -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); + reload_from_disk(Uri); handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_DELETED -> els_indexing:remove(Uri). + +-spec reload_from_disk(uri()) -> ok. +reload_from_disk(Uri) -> + {ok, Text} = file:read_file(els_uri:path(Uri)), + {ok, #{buffer := OldBuffer}} = els_utils:lookup_document(Uri), + els_buffer_server:stop(OldBuffer), + {ok, _NewBuffer} = els_buffer_server:new(Uri, Text), + ok. From 99ac92506b5355f8bc5d47d5765c02f67b9f6a93 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 6 Apr 2022 16:40:03 +0200 Subject: [PATCH 20/38] Resolve race in index buffer --- apps/els_lsp/src/els_buffer_server.erl | 45 +++++++++++++------- apps/els_lsp/src/els_completion_provider.erl | 21 ++++----- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl index 4a259aa60..509f7b8b1 100644 --- a/apps/els_lsp/src/els_buffer_server.erl +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -37,6 +37,7 @@ -type state() :: #{ uri := uri() , text := text() , ref := undefined | reference() + , pending := [{pid(), any()}] }. -type buffer() :: pid(). @@ -61,7 +62,7 @@ stop(Buffer) -> apply_edits(Buffer, Edits) -> gen_server:cast(Buffer, {apply_edits, Edits}). --spec flush(buffer()) -> ok. +-spec flush(buffer()) -> text(). flush(Buffer) -> gen_server:call(Buffer, {flush}). @@ -74,40 +75,52 @@ start_link(Uri, Text) -> %%============================================================================== -spec init({uri(), text()}) -> {ok, state()}. init({Uri, Text}) -> - do_flush(Uri, Text), - {ok, #{ uri => Uri, text => Text, ref => undefined }}. + schedule_flush(), + {ok, #{ uri => Uri, text => Text, ref => undefined, pending => [] }}. -spec handle_call(any(), {pid(), any()}, state()) -> {reply, any(), state()}. -handle_call({flush}, _From, #{uri := Uri, text := Text} = State) -> - ?LOG_DEBUG("[~p] Flushing request [uri=~p]", [?MODULE, Uri]), - do_flush(Uri, Text), - {reply, ok, State}; +handle_call({flush}, From, State) -> + #{uri := Uri, ref := Ref0, pending := Pending0} = State, + ?LOG_INFO("[~p] Flushing request [uri=~p]", [?MODULE, Uri]), + cancel_flush(Ref0), + Ref = schedule_flush(), + {noreply, State#{ref => Ref, pending => [From|Pending0]}}; handle_call(Request, _From, State) -> {reply, {not_implemented, Request}, State}. -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast({apply_edits, Edits}, #{uri := Uri} = State) -> - ?LOG_DEBUG("[~p] Applying edits [uri=~p]", [?MODULE, Uri]), + ?LOG_INFO("[~p] Applying edits [uri=~p]", [?MODULE, Uri]), #{text := Text0, ref := Ref0} = State, - case Ref0 of - undefined -> ok; - _ -> erlang:cancel_timer(Ref0) - end, + cancel_flush(Ref0), Text = els_text:apply_edits(Text0, Edits), - Ref = erlang:send_after(?FLUSH_DELAY, self(), flush), + Ref = schedule_flush(), {noreply, State#{text => Text, ref => Ref}}. -spec handle_info(any(), state()) -> {noreply, state()}. -handle_info(flush, #{uri := Uri, text := Text} = State) -> - ?LOG_DEBUG("[~p] Scheduled flushing [uri=~p]", [?MODULE, Uri]), +handle_info(flush, #{uri := Uri, text := Text, pending := Pending0} = State) -> + ?LOG_INFO("[~p] Flushing [uri=~p]", [?MODULE, Uri]), do_flush(Uri, Text), - {noreply, State}; + [gen_server:reply(From, Text) || From <- Pending0], + {noreply, State#{pending => []}}; handle_info(_Request, State) -> {noreply, State}. %%============================================================================== %% Internal Functions %%============================================================================== + +-spec schedule_flush() -> reference(). +schedule_flush() -> + erlang:send_after(?FLUSH_DELAY, self(), flush). + +-spec cancel_flush(undefined | reference()) -> ok. +cancel_flush(undefined) -> + ok; +cancel_flush(Ref) -> + erlang:cancel_timer(Ref), + ok. + -spec do_flush(uri(), text()) -> ok. do_flush(Uri, Text) -> {ok, Document} = els_utils:lookup_document(Uri), diff --git a/apps/els_lsp/src/els_completion_provider.erl b/apps/els_lsp/src/els_completion_provider.erl index 01aee9eb8..e8d8fec9b 100644 --- a/apps/els_lsp/src/els_completion_provider.erl +++ b/apps/els_lsp/src/els_completion_provider.erl @@ -44,17 +44,18 @@ handle_request({completion, Params}, State) -> } , <<"textDocument">> := #{<<"uri">> := Uri} } = Params, + %% Ensure there are no pending changes. {ok, Document} = els_utils:lookup_document(Uri), - #{text := Text, buffer := Buffer} = Document, - %% Ensure there are no pending changes. This causes a heavy sync - %% point, since for a big module indexing could take a while. Maybe - %% just do a 'soft' flush and avoid indexing here? - case Buffer of - undefined -> - ok; - _ -> - ok = els_buffer_server:flush(Buffer) - end, + #{buffer := Buffer, text := Text0} = Document, + Text = case Buffer of + undefined -> + %% This clause is only kept due to the current test suites, + %% where LSP clients can trigger a completion request + %% before a did_open + Text0; + _ -> + els_buffer_server:flush(Buffer) + end, Context = maps:get( <<"context">> , Params , #{ <<"triggerKind">> => ?COMPLETION_TRIGGER_KIND_INVOKED } From b9c83d29c6c40d6a3fbf575e81048f70e5139c01 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 6 Apr 2022 16:40:22 +0200 Subject: [PATCH 21/38] Lower verbosity of logs in case of shutdown --- apps/els_lsp/src/els_background_job.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/els_lsp/src/els_background_job.erl b/apps/els_lsp/src/els_background_job.erl index d58b71908..dae909223 100644 --- a/apps/els_lsp/src/els_background_job.erl +++ b/apps/els_lsp/src/els_background_job.erl @@ -193,7 +193,12 @@ terminate(Reason, #{ config := #{on_error := OnError} , total := Total , progress_enabled := ProgressEnabled }) -> - ?LOG_WARNING( "Background job aborted. [reason=~p]", [Reason]), + case Reason of + shutdown -> + ?LOG_DEBUG("Background job terminated.", []); + _ -> + ?LOG_WARNING( "Background job aborted. [reason=~p]", [Reason]) + end, notify_end(Token, Total, ProgressEnabled), OnError(InternalState), ok. From fc9c45627b094f89f2913c786c916fee55ad5095 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 6 Apr 2022 17:43:00 +0200 Subject: [PATCH 22/38] Set reference to undefined --- apps/els_lsp/src/els_buffer_server.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl index 509f7b8b1..8d6686a27 100644 --- a/apps/els_lsp/src/els_buffer_server.erl +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -102,7 +102,7 @@ handle_info(flush, #{uri := Uri, text := Text, pending := Pending0} = State) -> ?LOG_INFO("[~p] Flushing [uri=~p]", [?MODULE, Uri]), do_flush(Uri, Text), [gen_server:reply(From, Text) || From <- Pending0], - {noreply, State#{pending => []}}; + {noreply, State#{pending => [], ref => undefined}}; handle_info(_Request, State) -> {noreply, State}. From cec304056e5771c1af7bac6f92131892c6b8a396 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 08:28:23 +0200 Subject: [PATCH 23/38] Show edits in logs --- apps/els_lsp/src/els_buffer_server.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl index 8d6686a27..92ec3fdd2 100644 --- a/apps/els_lsp/src/els_buffer_server.erl +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -90,7 +90,7 @@ handle_call(Request, _From, State) -> -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast({apply_edits, Edits}, #{uri := Uri} = State) -> - ?LOG_INFO("[~p] Applying edits [uri=~p]", [?MODULE, Uri]), + ?LOG_INFO("[~p] Applying edits [uri=~p] [edits=~p]", [?MODULE, Uri, Edits]), #{text := Text0, ref := Ref0} = State, cancel_flush(Ref0), Text = els_text:apply_edits(Text0, Edits), From d1363705d459ffa7c23f07343463aa3898747aca Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 09:21:18 +0200 Subject: [PATCH 24/38] Make incremental indexing configurable --- apps/els_core/src/els_config_indexing.erl | 13 ++++++ apps/els_lsp/src/els_indexing.erl | 57 +++++++++++++++++------ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/apps/els_core/src/els_config_indexing.erl b/apps/els_core/src/els_config_indexing.erl index 525f1faaf..9b4c55b23 100644 --- a/apps/els_core/src/els_config_indexing.erl +++ b/apps/els_core/src/els_config_indexing.erl @@ -7,6 +7,7 @@ %% Getters -export([ get_skip_generated_files/0 , get_generated_files_tag/0 + , get_incremental/0 ]). -type config() :: #{ string() => string() }. @@ -15,6 +16,7 @@ default_config() -> #{ "skip_generated_files" => default_skip_generated_files() , "generated_files_tag" => default_generated_files_tag() + , "incremental" => default_incremental() }. -spec get_skip_generated_files() -> boolean(). @@ -30,6 +32,13 @@ get_generated_files_tag() -> els_config:get(indexing), default_generated_files_tag()). +-spec get_incremental() -> boolean(). +get_incremental() -> + Value = maps:get("incremental", + els_config:get(indexing), + default_incremental()), + normalize_boolean(Value). + -spec default_skip_generated_files() -> string(). default_skip_generated_files() -> "false". @@ -38,6 +47,10 @@ default_skip_generated_files() -> default_generated_files_tag() -> "@generated". +-spec default_incremental() -> string(). +default_incremental() -> + "false". + -spec normalize_boolean(boolean() | string()) -> boolean(). normalize_boolean("true") -> true; normalize_boolean("false") -> false; diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 597c91cbf..f0451aa00 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -153,11 +153,11 @@ start() -> -spec start(binary(), [string()], els_dt_document:source()) -> ok. start(Group, Entries, Source) -> - SkipGeneratedFiles = els_config_indexing:get_skip_generated_files(), - GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), + Skip = els_config_indexing:get_skip_generated_files(), + SkipTag = els_config_indexing:get_generated_files_tag(), + Incremental = els_config_indexing:get_incremental(), Task = fun(Dir, {Succeeded0, Skipped0, Failed0}) -> - {Su, Sk, Fa} = - index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source), + {Su, Sk, Fa} = index_dir(Dir, Skip, SkipTag, Incremental, Source), {Succeeded0 + Su, Skipped0 + Sk, Failed0 + Fa} end, Config = #{ task => Task @@ -200,23 +200,54 @@ shallow_index(FullName, SkipGeneratedFiles, GeneratedFilesTag, Source) -> shallow_index(Uri, Text, Source) end. +-spec deep_index(binary(), boolean(), string(), els_dt_document:source()) -> + ok | skipped | error. +deep_index(FullName, SkipGeneratedFiles, GeneratedFilesTag, Source) -> + Uri = els_uri:uri(FullName), + ?LOG_DEBUG("Deep indexing file. [filename=~s] [uri=~s]", + [FullName, Uri]), + {ok, Text} = file:read_file(FullName), + case SkipGeneratedFiles andalso is_generated_file(Text, GeneratedFilesTag) of + true -> + ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), + skipped; + false -> + Document = els_dt_document:new(Uri, Text, Source), + try deep_index(Document) + catch Type:Reason:St -> + ?LOG_ERROR("Error indexing file " + "[filename=~s, uri=~s] " + "~p:~p:~p", [FullName, Uri, Type, Reason, St]), + failed + end + end. + -spec index_dir(string(), els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. index_dir(Dir, Source) -> - SkipGeneratedFiles = els_config_indexing:get_skip_generated_files(), - GeneratedFilesTag = els_config_indexing:get_generated_files_tag(), - index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source). + Skip = els_config_indexing:get_skip_generated_files(), + SkipTag = els_config_indexing:get_generated_files_tag(), + Incremental = els_config_indexing:get_incremental(), + index_dir(Dir, Skip, SkipTag, Incremental, Source). --spec index_dir(string(), boolean(), string(), els_dt_document:source()) -> +-spec index_dir(string(), boolean(), string(), boolean(), + els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. -index_dir(Dir, SkipGeneratedFiles, GeneratedFilesTag, Source) -> +index_dir(Dir, Skip, SkipTag, Incremental, Source) -> ?LOG_DEBUG("Indexing directory. [dir=~s]", [Dir]), F = fun(FileName, {Succeeded, Skipped, Failed}) -> - case - shallow_index(els_utils:to_binary(FileName), - SkipGeneratedFiles, GeneratedFilesTag, Source) of + BinaryName = els_utils:to_binary(FileName), + Result = + case Incremental of + true -> + shallow_index(BinaryName, Skip, SkipTag, Source); + false -> + deep_index(BinaryName, Skip, SkipTag, Source) + end, + case Result of ok -> {Succeeded + 1, Skipped, Failed}; - skipped -> {Succeeded, Skipped + 1, Failed} + skipped -> {Succeeded, Skipped + 1, Failed}; + failed -> {Succeeded, Skipped, Failed} end end, Filter = fun(Path) -> From a003d319aa02070a9b7852cea1de4a5f814ed064 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 09:35:00 +0200 Subject: [PATCH 25/38] Clarify indexing logs --- apps/els_lsp/src/els_indexing.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index f0451aa00..37be3f064 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -147,15 +147,21 @@ maybe_start() -> -spec start() -> ok. start() -> - start(<<"OTP">>, els_config:get(otp_paths), otp), - start(<<"Applications">>, els_config:get(apps_paths), app), - start(<<"Dependencies">>, els_config:get(deps_paths), dep). - --spec start(binary(), [string()], els_dt_document:source()) -> ok. -start(Group, Entries, Source) -> Skip = els_config_indexing:get_skip_generated_files(), SkipTag = els_config_indexing:get_generated_files_tag(), Incremental = els_config_indexing:get_incremental(), + ?LOG_INFO("Start indexing. [skip=~p] [skip_tag=~p] [incremental=~p]", + [Skip, SkipTag, Incremental]), + start(<<"OTP">>, Skip, SkipTag, Incremental, + els_config:get(otp_paths), otp), + start(<<"Applications">>, Skip, SkipTag, Incremental, + els_config:get(apps_paths), app), + start(<<"Dependencies">>, Skip, SkipTag, Incremental, + els_config:get(deps_paths), dep). + +-spec start(binary(), boolean(), string(), boolean(), [string()], + els_dt_document:source()) -> ok. +start(Group, Skip, SkipTag, Incremental, Entries, Source) -> Task = fun(Dir, {Succeeded0, Skipped0, Failed0}) -> {Su, Sk, Fa} = index_dir(Dir, Skip, SkipTag, Incremental, Source), {Succeeded0 + Su, Skipped0 + Sk, Failed0 + Fa} From dc94b07b3ebf62b5a57b11c55b82fc6ed937a491 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 09:51:04 +0200 Subject: [PATCH 26/38] Show background job title on error --- apps/els_lsp/src/els_background_job.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/els_lsp/src/els_background_job.erl b/apps/els_lsp/src/els_background_job.erl index dae909223..a5687d8c3 100644 --- a/apps/els_lsp/src/els_background_job.erl +++ b/apps/els_lsp/src/els_background_job.erl @@ -187,7 +187,9 @@ terminate(normal, #{ config := #{on_complete := OnComplete} ?LOG_DEBUG("Background job completed.", []), OnComplete(InternalState), ok; -terminate(Reason, #{ config := #{on_error := OnError} +terminate(Reason, #{ config := #{ on_error := OnError + , title := Title + } , internal_state := InternalState , token := Token , total := Total @@ -197,7 +199,8 @@ terminate(Reason, #{ config := #{on_error := OnError} shutdown -> ?LOG_DEBUG("Background job terminated.", []); _ -> - ?LOG_WARNING( "Background job aborted. [reason=~p]", [Reason]) + ?LOG_ERROR( "Background job aborted. [reason=~p] [title=~p", + [Reason, Title]) end, notify_end(Token, Total, ProgressEnabled), OnError(InternalState), From 41ca00b333dcac1efe35647c3bea6deaea9f4f0f Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 10:17:22 +0200 Subject: [PATCH 27/38] Revert configuration of Incremental The deep indexing was relying on many optimizations, which have been removed as part of the incremental indexing. Supporting both feels an un-necessary effort --- apps/els_core/src/els_config_indexing.erl | 13 ----- apps/els_lsp/src/els_indexing.erl | 61 +++++------------------ 2 files changed, 12 insertions(+), 62 deletions(-) diff --git a/apps/els_core/src/els_config_indexing.erl b/apps/els_core/src/els_config_indexing.erl index 9b4c55b23..525f1faaf 100644 --- a/apps/els_core/src/els_config_indexing.erl +++ b/apps/els_core/src/els_config_indexing.erl @@ -7,7 +7,6 @@ %% Getters -export([ get_skip_generated_files/0 , get_generated_files_tag/0 - , get_incremental/0 ]). -type config() :: #{ string() => string() }. @@ -16,7 +15,6 @@ default_config() -> #{ "skip_generated_files" => default_skip_generated_files() , "generated_files_tag" => default_generated_files_tag() - , "incremental" => default_incremental() }. -spec get_skip_generated_files() -> boolean(). @@ -32,13 +30,6 @@ get_generated_files_tag() -> els_config:get(indexing), default_generated_files_tag()). --spec get_incremental() -> boolean(). -get_incremental() -> - Value = maps:get("incremental", - els_config:get(indexing), - default_incremental()), - normalize_boolean(Value). - -spec default_skip_generated_files() -> string(). default_skip_generated_files() -> "false". @@ -47,10 +38,6 @@ default_skip_generated_files() -> default_generated_files_tag() -> "@generated". --spec default_incremental() -> string(). -default_incremental() -> - "false". - -spec normalize_boolean(boolean() | string()) -> boolean(). normalize_boolean("true") -> true; normalize_boolean("false") -> false; diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 37be3f064..158a8964e 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -149,21 +149,16 @@ maybe_start() -> start() -> Skip = els_config_indexing:get_skip_generated_files(), SkipTag = els_config_indexing:get_generated_files_tag(), - Incremental = els_config_indexing:get_incremental(), - ?LOG_INFO("Start indexing. [skip=~p] [skip_tag=~p] [incremental=~p]", - [Skip, SkipTag, Incremental]), - start(<<"OTP">>, Skip, SkipTag, Incremental, - els_config:get(otp_paths), otp), - start(<<"Applications">>, Skip, SkipTag, Incremental, - els_config:get(apps_paths), app), - start(<<"Dependencies">>, Skip, SkipTag, Incremental, - els_config:get(deps_paths), dep). + ?LOG_INFO("Start indexing. [skip=~p] [skip_tag=~p]", [Skip, SkipTag]), + start(<<"OTP">>, Skip, SkipTag, els_config:get(otp_paths), otp), + start(<<"Applications">>, Skip, SkipTag, els_config:get(apps_paths), app), + start(<<"Dependencies">>, Skip, SkipTag, els_config:get(deps_paths), dep). --spec start(binary(), boolean(), string(), boolean(), [string()], +-spec start(binary(), boolean(), string(), [string()], els_dt_document:source()) -> ok. -start(Group, Skip, SkipTag, Incremental, Entries, Source) -> +start(Group, Skip, SkipTag, Entries, Source) -> Task = fun(Dir, {Succeeded0, Skipped0, Failed0}) -> - {Su, Sk, Fa} = index_dir(Dir, Skip, SkipTag, Incremental, Source), + {Su, Sk, Fa} = index_dir(Dir, Skip, SkipTag, Source), {Succeeded0 + Su, Skipped0 + Sk, Failed0 + Fa} end, Config = #{ task => Task @@ -206,54 +201,22 @@ shallow_index(FullName, SkipGeneratedFiles, GeneratedFilesTag, Source) -> shallow_index(Uri, Text, Source) end. --spec deep_index(binary(), boolean(), string(), els_dt_document:source()) -> - ok | skipped | error. -deep_index(FullName, SkipGeneratedFiles, GeneratedFilesTag, Source) -> - Uri = els_uri:uri(FullName), - ?LOG_DEBUG("Deep indexing file. [filename=~s] [uri=~s]", - [FullName, Uri]), - {ok, Text} = file:read_file(FullName), - case SkipGeneratedFiles andalso is_generated_file(Text, GeneratedFilesTag) of - true -> - ?LOG_DEBUG("Skip indexing for generated file ~p", [Uri]), - skipped; - false -> - Document = els_dt_document:new(Uri, Text, Source), - try deep_index(Document) - catch Type:Reason:St -> - ?LOG_ERROR("Error indexing file " - "[filename=~s, uri=~s] " - "~p:~p:~p", [FullName, Uri, Type, Reason, St]), - failed - end - end. - -spec index_dir(string(), els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. index_dir(Dir, Source) -> Skip = els_config_indexing:get_skip_generated_files(), SkipTag = els_config_indexing:get_generated_files_tag(), - Incremental = els_config_indexing:get_incremental(), - index_dir(Dir, Skip, SkipTag, Incremental, Source). + index_dir(Dir, Skip, SkipTag, Source). --spec index_dir(string(), boolean(), string(), boolean(), - els_dt_document:source()) -> +-spec index_dir(string(), boolean(), string(), els_dt_document:source()) -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}. -index_dir(Dir, Skip, SkipTag, Incremental, Source) -> +index_dir(Dir, Skip, SkipTag, Source) -> ?LOG_DEBUG("Indexing directory. [dir=~s]", [Dir]), F = fun(FileName, {Succeeded, Skipped, Failed}) -> BinaryName = els_utils:to_binary(FileName), - Result = - case Incremental of - true -> - shallow_index(BinaryName, Skip, SkipTag, Source); - false -> - deep_index(BinaryName, Skip, SkipTag, Source) - end, - case Result of + case shallow_index(BinaryName, Skip, SkipTag, Source) of ok -> {Succeeded + 1, Skipped, Failed}; - skipped -> {Succeeded, Skipped + 1, Failed}; - failed -> {Succeeded, Skipped, Failed} + skipped -> {Succeeded, Skipped + 1, Failed} end end, Filter = fun(Path) -> From 413b5dd415ae6bce862f70013cc866ff7195896c Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 11:04:37 +0200 Subject: [PATCH 28/38] Basic words scanner --- apps/els_lsp/src/els_dt_document.erl | 43 ++++++++++++++++++++++------ apps/els_lsp/src/els_text_search.erl | 41 +++++++++++++------------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index 34010dda4..b210ed555 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -38,6 +38,7 @@ %% Includes %%============================================================================== -include("els_lsp.hrl"). +-include_lib("kernel/include/logger.hrl"). %%============================================================================== %% Type Definitions @@ -55,11 +56,12 @@ -record(els_dt_document, { uri :: uri() | '_' | '$1' , id :: id() | '_' , kind :: kind() | '_' - , text :: binary() | '_' | '$3' + , text :: binary() | '_' , md5 :: binary() | '_' , pois :: [poi()] | '_' | ondemand , source :: source() | '$2' , buffer :: buffer() | '_' | undefined + , words :: sets:set() | '_' | '$3' }). -type els_dt_document() :: #els_dt_document{}. @@ -71,6 +73,7 @@ , pois => [poi()] | ondemand , source => source() , buffer => buffer() | undefined + , words => sets:set() }. -export_type([ id/0 , item/0 @@ -101,6 +104,7 @@ from_item(#{ uri := Uri , pois := POIs , source := Source , buffer := Buffer + , words := Words }) -> #els_dt_document{ uri = Uri , id = Id @@ -110,6 +114,7 @@ from_item(#{ uri := Uri , pois = POIs , source = Source , buffer = Buffer + , words = Words }. -spec to_item(els_dt_document()) -> item(). @@ -121,6 +126,7 @@ to_item(#els_dt_document{ uri = Uri , pois = POIs , source = Source , buffer = Buffer + , words = Words }) -> #{ uri => Uri , id => Id @@ -130,6 +136,7 @@ to_item(#els_dt_document{ uri = Uri , pois => POIs , source => Source , buffer => Buffer + , words => Words }. -spec insert(item()) -> ok | {error, any()}. @@ -170,6 +177,7 @@ new(Uri, Text, Id, Kind, Source) -> , pois => ondemand , source => Source , buffer => undefined + , words => get_words(Text) }. %% @doc Returns the list of POIs for the current document @@ -223,25 +231,42 @@ wrapping_functions(Document, Range) -> #{start := #{character := Character, line := Line}} = Range, wrapping_functions(Document, Line, Character). --spec find_candidates(binary()) -> [uri()]. +-spec find_candidates(atom() | string()) -> [uri()]. find_candidates(Pattern) -> - %% ets:fun2ms(fun(#els_dt_document{source = Source, uri = Uri, text = Text}) - %% when Source =/= otp -> {Uri, Text} end). + %% ets:fun2ms(fun(#els_dt_document{source = Source, uri = Uri, words = Words}) + %% when Source =/= otp -> {Uri, Words} end). MS = [{#els_dt_document{ uri = '$1' , source = '$2' , buffer = '_' , kind = '_' - , text = '$3' + , text = '_' , md5 = '_' , pois = '_' + , words = '$3' } , [{'=/=', '$2', otp}] , [{{'$1', '$3'}}]}], All = ets:select(name(), MS), - Fun = fun({Uri, Text}) -> - case binary:matches(Text, Pattern) of - [] -> false; - _ -> {true, Uri} + Fun = fun({Uri, Words}) -> + case sets:is_element(Pattern, Words) of + true -> {true, Uri}; + false -> false end end, lists:filtermap(Fun, All). + +-spec get_words(binary()) -> sets:set(). +get_words(Text) -> + case erl_scan:string(els_utils:to_list(Text)) of + {ok, Tokens, _EndLocation} -> + Fun = fun({atom, _Location, Atom}, Words) -> + sets:add_element(Atom, Words); + ({string, _Location, String}, Words) -> + sets:add_element(String, Words); + (_, Words) -> + Words + end, + lists:foldl(Fun, sets:new(), Tokens); + {error, ErrorInfo, _ErrorLocation} -> + ?LOG_DEBUG("Errors while get_words ~p", [ErrorInfo]) + end. diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl index 8aa20a419..c55bdd37f 100644 --- a/apps/els_lsp/src/els_text_search.erl +++ b/apps/els_lsp/src/els_text_search.erl @@ -18,28 +18,29 @@ %%============================================================================== -spec find_candidate_uris({els_dt_references:poi_category(), any()}) -> [uri()]. find_candidate_uris(Id) -> - String = id_to_binary(Id), - els_dt_document:find_candidates(String). + Pattern = extract_pattern(Id), + els_dt_document:find_candidates(Pattern). %%============================================================================== %% Internal Functions %%============================================================================== --spec id_to_binary({els_dt_references:poi_category(), any()}) -> binary(). -id_to_binary({function, {_M, F, _A}}) -> - atom_to_binary(F, utf8); -id_to_binary({type, {_M, F, _A}}) -> - atom_to_binary(F, utf8); -id_to_binary({macro, {Name, _Arity}}) -> - atom_to_binary(Name, utf8); -id_to_binary({macro, Name}) -> - atom_to_binary(Name, utf8); -id_to_binary({include, Id}) -> - include_id_to_binary(Id); -id_to_binary({include_lib, Id}) -> - include_id_to_binary(Id); -id_to_binary({behaviour, Name}) -> - atom_to_binary(Name, utf8). +-spec extract_pattern({els_dt_references:poi_category(), any()}) -> + atom() | binary(). +extract_pattern({function, {_M, F, _A}}) -> + F; +extract_pattern({type, {_M, F, _A}}) -> + F; +extract_pattern({macro, {Name, _Arity}}) -> + Name; +extract_pattern({macro, Name}) -> + Name; +extract_pattern({include, Id}) -> + include_id(Id); +extract_pattern({include_lib, Id}) -> + include_id(Id); +extract_pattern({behaviour, Name}) -> + Name. --spec include_id_to_binary(string()) -> binary(). -include_id_to_binary(Id) -> - els_utils:to_binary(filename:rootname(filename:basename(Id))). +-spec include_id(string()) -> string(). +include_id(Id) -> + filename:rootname(filename:basename(Id)). From 5416d7af5063dff65f25add36ccfd7e8d0fc3caf Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 11:36:04 +0200 Subject: [PATCH 29/38] Lower verbosity --- apps/els_lsp/src/els_buffer_server.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl index 92ec3fdd2..b21980afd 100644 --- a/apps/els_lsp/src/els_buffer_server.erl +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -81,7 +81,7 @@ init({Uri, Text}) -> -spec handle_call(any(), {pid(), any()}, state()) -> {reply, any(), state()}. handle_call({flush}, From, State) -> #{uri := Uri, ref := Ref0, pending := Pending0} = State, - ?LOG_INFO("[~p] Flushing request [uri=~p]", [?MODULE, Uri]), + ?LOG_DEBUG("[~p] Flushing request [uri=~p]", [?MODULE, Uri]), cancel_flush(Ref0), Ref = schedule_flush(), {noreply, State#{ref => Ref, pending => [From|Pending0]}}; @@ -90,7 +90,7 @@ handle_call(Request, _From, State) -> -spec handle_cast(any(), state()) -> {noreply, state()}. handle_cast({apply_edits, Edits}, #{uri := Uri} = State) -> - ?LOG_INFO("[~p] Applying edits [uri=~p] [edits=~p]", [?MODULE, Uri, Edits]), + ?LOG_DEBUG("[~p] Applying edits [uri=~p] [edits=~p]", [?MODULE, Uri, Edits]), #{text := Text0, ref := Ref0} = State, cancel_flush(Ref0), Text = els_text:apply_edits(Text0, Edits), @@ -99,7 +99,7 @@ handle_cast({apply_edits, Edits}, #{uri := Uri} = State) -> -spec handle_info(any(), state()) -> {noreply, state()}. handle_info(flush, #{uri := Uri, text := Text, pending := Pending0} = State) -> - ?LOG_INFO("[~p] Flushing [uri=~p]", [?MODULE, Uri]), + ?LOG_DEBUG("[~p] Flushing [uri=~p]", [?MODULE, Uri]), do_flush(Uri, Text), [gen_server:reply(From, Text) || From <- Pending0], {noreply, State#{pending => [], ref => undefined}}; From d98eb186c5c15fa2f312ef1a2537a44a8ad9ac2b Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 11:54:32 +0200 Subject: [PATCH 30/38] Real devs write match specs by hand. Not. --- apps/els_lsp/src/els_dt_document.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index b210ed555..44f315063 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -236,16 +236,16 @@ find_candidates(Pattern) -> %% ets:fun2ms(fun(#els_dt_document{source = Source, uri = Uri, words = Words}) %% when Source =/= otp -> {Uri, Words} end). MS = [{#els_dt_document{ uri = '$1' - , source = '$2' - , buffer = '_' + , id = '_' , kind = '_' , text = '_' , md5 = '_' , pois = '_' - , words = '$3' - } - , [{'=/=', '$2', otp}] - , [{{'$1', '$3'}}]}], + , source = '$2' + , buffer = '_' + , words = '$3'}, + [{'=/=', '$2', otp}], + [{{'$1', '$3'}}]}], All = ets:select(name(), MS), Fun = fun({Uri, Words}) -> case sets:is_element(Pattern, Words) of From d59306dd5404aaa35527e8a84e6b023f799bc6bc Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 14:28:04 +0200 Subject: [PATCH 31/38] Explicitly store buffers --- apps/els_lsp/src/els_buffer_server.erl | 3 +-- apps/els_lsp/src/els_text_synchronization.erl | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/els_lsp/src/els_buffer_server.erl b/apps/els_lsp/src/els_buffer_server.erl index b21980afd..293037323 100644 --- a/apps/els_lsp/src/els_buffer_server.erl +++ b/apps/els_lsp/src/els_buffer_server.erl @@ -75,7 +75,6 @@ start_link(Uri, Text) -> %%============================================================================== -spec init({uri(), text()}) -> {ok, state()}. init({Uri, Text}) -> - schedule_flush(), {ok, #{ uri => Uri, text => Text, ref => undefined, pending => [] }}. -spec handle_call(any(), {pid(), any()}, state()) -> {reply, any(), state()}. @@ -124,4 +123,4 @@ cancel_flush(Ref) -> -spec do_flush(uri(), text()) -> ok. do_flush(Uri, Text) -> {ok, Document} = els_utils:lookup_document(Uri), - els_indexing:deep_index(Document#{text => Text, buffer => self()}). + els_indexing:deep_index(Document#{text => Text}). diff --git a/apps/els_lsp/src/els_text_synchronization.erl b/apps/els_lsp/src/els_text_synchronization.erl index 0647107f2..342f0611a 100644 --- a/apps/els_lsp/src/els_text_synchronization.erl +++ b/apps/els_lsp/src/els_text_synchronization.erl @@ -55,7 +55,9 @@ did_change(Params) -> did_open(Params) -> #{<<"textDocument">> := #{ <<"uri">> := Uri , <<"text">> := Text}} = Params, - {ok, _Buffer} = els_buffer_server:new(Uri, Text), + {ok, Document} = els_utils:lookup_document(Uri), + {ok, Buffer} = els_buffer_server:new(Uri, Text), + els_dt_document:insert(Document#{buffer => Buffer}), Provider = els_diagnostics_provider, els_provider:handle_request(Provider, {run_diagnostics, Params}), ok. @@ -97,7 +99,8 @@ handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_DELETED -> -spec reload_from_disk(uri()) -> ok. reload_from_disk(Uri) -> {ok, Text} = file:read_file(els_uri:path(Uri)), - {ok, #{buffer := OldBuffer}} = els_utils:lookup_document(Uri), + {ok, #{buffer := OldBuffer} = Document} = els_utils:lookup_document(Uri), els_buffer_server:stop(OldBuffer), - {ok, _NewBuffer} = els_buffer_server:new(Uri, Text), + {ok, NewBuffer} = els_buffer_server:new(Uri, Text), + els_dt_document:insert(Document#{buffer => NewBuffer}), ok. From 8d34dfa4fa8e1c1350f51a9cb705f29c5e218652 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 14:58:11 +0200 Subject: [PATCH 32/38] Fixes --- apps/els_lsp/src/els_text_synchronization.erl | 11 ++++++++--- apps/els_lsp/test/els_references_SUITE.erl | 3 +-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/els_lsp/src/els_text_synchronization.erl b/apps/els_lsp/src/els_text_synchronization.erl index 342f0611a..d7a2aea86 100644 --- a/apps/els_lsp/src/els_text_synchronization.erl +++ b/apps/els_lsp/src/els_text_synchronization.erl @@ -100,7 +100,12 @@ handle_file_change(Uri, Type) when Type =:= ?FILE_CHANGE_TYPE_DELETED -> reload_from_disk(Uri) -> {ok, Text} = file:read_file(els_uri:path(Uri)), {ok, #{buffer := OldBuffer} = Document} = els_utils:lookup_document(Uri), - els_buffer_server:stop(OldBuffer), - {ok, NewBuffer} = els_buffer_server:new(Uri, Text), - els_dt_document:insert(Document#{buffer => NewBuffer}), + case OldBuffer of + undefined -> + els_indexing:deep_index(Document#{text => Text}); + _ -> + els_buffer_server:stop(OldBuffer), + {ok, B} = els_buffer_server:new(Uri, Text), + els_indexing:deep_index(Document#{text => Text, buffer => B}) + end, ok. diff --git a/apps/els_lsp/test/els_references_SUITE.erl b/apps/els_lsp/test/els_references_SUITE.erl index 7e7096592..0e5e72c09 100644 --- a/apps/els_lsp/test/els_references_SUITE.erl +++ b/apps/els_lsp/test/els_references_SUITE.erl @@ -546,10 +546,9 @@ refresh_after_watched_file_added(Config) -> DataDir = ?config(data_dir, Config), PathC = filename:join([DataDir, "watched_file_c.erl"]), NewPathC = filename:join(filename:dirname(PathB), "watched_file_c.erl"), - UriC = els_uri:uri(els_utils:to_binary(PathC)), NewUriC = els_uri:uri(NewPathC), {ok, _} = file:copy(PathC, NewPathC), - els_client:did_change_watched_files([{UriC, ?FILE_CHANGE_TYPE_CREATED}]), + els_client:did_change_watched_files([{NewUriC, ?FILE_CHANGE_TYPE_CREATED}]), %% After ExpectedLocationsAfter = [ #{ uri => NewUriC , range => #{from => {6, 3}, to => {6, 22}} From 39711145f3635a71672c1d0bcaad7d545019a043 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Thu, 7 Apr 2022 16:08:55 +0200 Subject: [PATCH 33/38] Debug failing testcase in OTP 22 --- apps/els_lsp/test/els_completion_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/els_lsp/test/els_completion_SUITE.erl b/apps/els_lsp/test/els_completion_SUITE.erl index 6754b4ecf..244c90380 100644 --- a/apps/els_lsp/test/els_completion_SUITE.erl +++ b/apps/els_lsp/test/els_completion_SUITE.erl @@ -1063,6 +1063,10 @@ resolve_application_remote_external(Config) -> -spec resolve_application_remote_otp(config()) -> ok. resolve_application_remote_otp(Config) -> + dbg:tracer(), + dbg:p(all, c), + dbg:tpl(els_utils, find_modules, x), + dbg:tpl(els_dt_signatures, x), Uri = ?config(completion_resolve_uri, Config), CompletionKind = ?COMPLETION_TRIGGER_KIND_INVOKED, #{result := CompletionItems} = From bbcb68976b47f9845cae4b80d807d1292fad5aab Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 8 Apr 2022 08:50:28 +0200 Subject: [PATCH 34/38] Do not delete specs for header files, to avoid conflict --- apps/els_core/src/els_utils.erl | 3 ++- apps/els_lsp/src/els_dt_signatures.erl | 17 +++++++++++------ apps/els_lsp/src/els_indexing.erl | 10 +++++----- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/els_core/src/els_utils.erl b/apps/els_core/src/els_utils.erl index 56cbf53e4..bc624c195 100644 --- a/apps/els_core/src/els_utils.erl +++ b/apps/els_core/src/els_utils.erl @@ -127,7 +127,8 @@ find_module(Id) -> -spec find_modules(atom()) -> {ok, [uri()]} | {error, any()}. find_modules(Id) -> {ok, Candidates} = els_dt_document_index:lookup(Id), - case [Uri || #{kind := module, uri := Uri} <- Candidates] of + case [Uri || #{kind := module, uri := Uri, pois := POIs} <- Candidates, + POIs =/= ondemand] of [] -> FileName = atom_to_list(Id) ++ ".erl", case els_indexing:find_and_deeply_index_file(FileName) of diff --git a/apps/els_lsp/src/els_dt_signatures.erl b/apps/els_lsp/src/els_dt_signatures.erl index c8379fbd6..1a5b20318 100644 --- a/apps/els_lsp/src/els_dt_signatures.erl +++ b/apps/els_lsp/src/els_dt_signatures.erl @@ -19,7 +19,7 @@ -export([ insert/1 , lookup/1 - , delete_by_module/1 + , delete_by_uri/1 ]). %%============================================================================== @@ -74,12 +74,17 @@ insert(Map) when is_map(Map) -> -spec lookup(mfa()) -> {ok, [item()]}. lookup({M, _F, _A} = MFA) -> - %% By finding a module, we also ensure the module is deeply indexed. {ok, _Uris} = els_utils:find_modules(M), {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). +-spec delete_by_uri(uri()) -> ok. +delete_by_uri(Uri) -> + case filename:extension(Uri) of + <<".erl">> -> + Module = els_uri:module(Uri), + Pattern = #els_dt_signatures{mfa = {Module, '_', '_'}, _ = '_'}, + ok = els_db:match_delete(name(), Pattern); + _ -> + ok + end. diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 158a8964e..dd300ef8a 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -71,7 +71,7 @@ deep_index(Document) -> #{id := Id, uri := Uri, text := Text, source := Source} = Document, {ok, POIs} = els_parser:parse(Text), ok = els_dt_document:insert(Document#{pois => POIs}), - index_signatures(Id, Text, POIs), + index_signatures(Id, Uri, Text, POIs), case Source of otp -> ok; @@ -79,9 +79,9 @@ deep_index(Document) -> index_references(Id, Uri, POIs) end. --spec index_signatures(atom(), binary(), [poi()]) -> ok. -index_signatures(Id, Text, POIs) -> - ok = els_dt_signatures:delete_by_module(Id), +-spec index_signatures(atom(), uri(), binary(), [poi()]) -> ok. +index_signatures(Id, Uri, Text, POIs) -> + ok = els_dt_signatures:delete_by_uri(Uri), [index_signature(Id, Text, POI) || #{kind := spec} = POI <- POIs], ok. @@ -180,7 +180,7 @@ remove(Uri) -> 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)). + ok = els_dt_signatures:delete_by_uri(Uri). %%============================================================================== %% Internal functions From f054f92b6350d5ab5163191fd0e70a84e1fccca2 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 8 Apr 2022 08:53:45 +0200 Subject: [PATCH 35/38] Remove dbg expressions --- apps/els_lsp/test/els_completion_SUITE.erl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/els_lsp/test/els_completion_SUITE.erl b/apps/els_lsp/test/els_completion_SUITE.erl index 244c90380..6754b4ecf 100644 --- a/apps/els_lsp/test/els_completion_SUITE.erl +++ b/apps/els_lsp/test/els_completion_SUITE.erl @@ -1063,10 +1063,6 @@ resolve_application_remote_external(Config) -> -spec resolve_application_remote_otp(config()) -> ok. resolve_application_remote_otp(Config) -> - dbg:tracer(), - dbg:p(all, c), - dbg:tpl(els_utils, find_modules, x), - dbg:tpl(els_dt_signatures, x), Uri = ?config(completion_resolve_uri, Config), CompletionKind = ?COMPLETION_TRIGGER_KIND_INVOKED, #{result := CompletionItems} = From 8a91a238d7369497fe530f463ba9b34df3aa0f47 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 8 Apr 2022 09:22:37 +0200 Subject: [PATCH 36/38] Fix Dialyzer issues --- apps/els_core/src/els_utils.erl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/els_core/src/els_utils.erl b/apps/els_core/src/els_utils.erl index bc624c195..80b02013b 100644 --- a/apps/els_core/src/els_utils.erl +++ b/apps/els_core/src/els_utils.erl @@ -127,16 +127,15 @@ find_module(Id) -> -spec find_modules(atom()) -> {ok, [uri()]} | {error, any()}. find_modules(Id) -> {ok, Candidates} = els_dt_document_index:lookup(Id), - case [Uri || #{kind := module, uri := Uri, pois := POIs} <- Candidates, - POIs =/= ondemand] of - [] -> - FileName = atom_to_list(Id) ++ ".erl", - case els_indexing:find_and_deeply_index_file(FileName) of - {ok, Uri} -> {ok, [Uri]}; - Error -> Error - end; - Uris -> - {ok, prioritize_uris(Uris)} + case [Uri || #{kind := module, uri := Uri} <- Candidates] of + [] -> + FileName = atom_to_list(Id) ++ ".erl", + case els_indexing:find_and_deeply_index_file(FileName) of + {ok, Uri} -> {ok, [Uri]}; + Error -> Error + end; + Uris -> + {ok, prioritize_uris(Uris)} end. %% @doc Look for a document in the DB. From 161dc2c38c1f2a2919e05b1afeb5f5cb5da0185d Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 8 Apr 2022 12:49:33 +0200 Subject: [PATCH 37/38] Remove obsolete module --- apps/els_lsp/priv/code_navigation/src/purge_references.erl | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 apps/els_lsp/priv/code_navigation/src/purge_references.erl diff --git a/apps/els_lsp/priv/code_navigation/src/purge_references.erl b/apps/els_lsp/priv/code_navigation/src/purge_references.erl deleted file mode 100644 index b354e94b5..000000000 --- a/apps/els_lsp/priv/code_navigation/src/purge_references.erl +++ /dev/null @@ -1,5 +0,0 @@ --module(purge_references). - --spec foo(any()) -> ok. -foo(_X) -> ok. -bar() -> foo(42). From c505effb020aa8a96223d62708b537acc69f45e8 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Fri, 8 Apr 2022 13:02:31 +0200 Subject: [PATCH 38/38] Fix indexing of include attributes --- apps/els_lsp/src/els_dt_document.erl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/els_lsp/src/els_dt_document.erl b/apps/els_lsp/src/els_dt_document.erl index 44f315063..1c37386ca 100644 --- a/apps/els_lsp/src/els_dt_document.erl +++ b/apps/els_lsp/src/els_dt_document.erl @@ -262,7 +262,13 @@ get_words(Text) -> Fun = fun({atom, _Location, Atom}, Words) -> sets:add_element(Atom, Words); ({string, _Location, String}, Words) -> - sets:add_element(String, Words); + case filename:extension(String) of + ".hrl" -> + Id = filename:rootname(filename:basename(String)), + sets:add_element(Id, Words); + _ -> + Words + end; (_, Words) -> Words end,