diff --git a/apps/els_core/src/els_utils.erl b/apps/els_core/src/els_utils.erl index a1fe0ea5e..27c99e7b4 100644 --- a/apps/els_core/src/els_utils.erl +++ b/apps/els_core/src/els_utils.erl @@ -25,7 +25,8 @@ camel_case/1, jaro_distance/2, is_windows/0, - system_tmp_dir/0 + system_tmp_dir/0, + race/1 ]). %%============================================================================== @@ -272,9 +273,33 @@ system_tmp_dir() -> "/tmp" end. +%% @doc Run functions in parallel and return the result of the first function +%% that terminates +-spec race(fun(() -> Result)) -> Result. +race(Funs) -> + Parent = self(), + Ref = make_ref(), + Pids = [spawn(fun() -> Parent ! {Ref, Fun()} end) || Fun <- Funs], + receive + {Ref, Result} -> + %% Ensure no lingering processes + [exit(Pid, kill) || Pid <- Pids], + %% Ensure no lingering messages + ok = flush(Ref), + Result + end. + %%============================================================================== %% Internal functions %%============================================================================== +-spec flush(reference()) -> ok. +flush(Ref) -> + receive + {Ref, _} -> + flush(Ref) + after 0 -> + ok + end. %% Folding over files diff --git a/apps/els_lsp/src/els_dt_references.erl b/apps/els_lsp/src/els_dt_references.erl index e66112e6d..d4492b8ec 100644 --- a/apps/els_lsp/src/els_dt_references.erl +++ b/apps/els_lsp/src/els_dt_references.erl @@ -185,7 +185,7 @@ kind_to_category(Kind) when Kind =:= record_field; Kind =:= record_def_field -> - record; + record_field; kind_to_category(Kind) when Kind =:= include -> include; kind_to_category(Kind) when Kind =:= include_lib -> diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 279c5d1fa..ecec2c347 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -146,7 +146,8 @@ index_references(Id, Uri, POIs, Version) -> %% Macro macro, %% Record - record_expr + record_expr, + record_field ], [ index_reference(Id, Uri, POI, Version) @@ -156,16 +157,9 @@ index_references(Id, Uri, POIs, Version) -> ok. -spec index_reference(atom(), uri(), els_poi:poi(), version()) -> ok. -index_reference(_M, Uri, #{kind := Kind, id := Id, range := Range}, Version) when - Kind =:= macro --> - els_dt_references:versioned_insert(Kind, #{ - id => Id, - uri => Uri, - range => Range, - version => Version - }); -index_reference(M, Uri, #{kind := _Kind, id := {F, A}} = POI, Version) -> +index_reference(M, Uri, #{kind := Kind, id := {F, A}} = POI, Version) when + Kind =/= macro, + Kind =/= record_field -> index_reference(M, Uri, POI#{id => {M, F, A}}, Version); index_reference(_M, Uri, #{kind := Kind, id := Id, range := Range}, Version) -> els_dt_references:versioned_insert(Kind, #{ diff --git a/apps/els_lsp/src/els_references_provider.erl b/apps/els_lsp/src/els_references_provider.erl index 3134ca23b..8f887a794 100644 --- a/apps/els_lsp/src/els_references_provider.erl +++ b/apps/els_lsp/src/els_references_provider.erl @@ -156,29 +156,52 @@ local_refs(Uri, Kind, Id) -> LocalRefs. -spec find_scoped_references_for_def(uri(), els_poi:poi()) -> [location()]. -find_scoped_references_for_def(Uri, #{kind := Kind, id := Id}) when - Kind =:= record_def_field; +find_scoped_references_for_def(Uri, POI = #{kind := Kind, id := Id}) when Kind =:= type_definition -> %% TODO: This is a hack, ideally we shouldn't have any special handling for - %% these kinds + %% these kinds. + find_scoped_references_naive(Uri, POI); +find_scoped_references_for_def(Uri, POI) -> + %% Finding scoped references can be done in two ways: + %% * Naive, find all POIs that can reach our POI and matches the id. + %% * Indexed, use the index to find all matching POIs, then check if + %% they actually reference our POI by using goto_definition. + %% It varies from case to case which is the fastest, so we race both + %% functions to get the quickest answer. + Naive = fun() -> find_scoped_references_naive(Uri, POI) end, + Index = fun() -> find_scoped_references_with_index(Uri, POI) end, + els_utils:race([Naive, Index]). + +-spec find_scoped_references_naive(uri(), els_poi:poi()) -> [location()]. +find_scoped_references_naive(Uri, #{id := Id, kind := Kind}) -> RefKind = kind_to_ref_kind(Kind), Refs = els_scope:local_and_includer_pois(Uri, [RefKind]), - [ + MatchingRefs = [ location(U, R) || {U, Pois} <- Refs, #{id := N, range := R} <- Pois, N =:= Id - ]; -find_scoped_references_for_def(Uri, POI = #{kind := Kind, id := Id}) -> + ], + ?LOG_DEBUG("Found scoped references (naive) for ~p: ~p", + [Id, length(MatchingRefs)]), + MatchingRefs. + +-spec find_scoped_references_with_index(uri(), els_poi:poi()) -> [location()]. +find_scoped_references_with_index(Uri, POI = #{kind := Kind, id := Id}) -> + RefPOI = POI#{kind := kind_to_ref_kind(Kind)}, - F = fun(#{uri := RefUri}) -> - case els_code_navigation:goto_definition(RefUri, RefPOI) of - {ok, Uri, _} -> true; - _ -> false - end - end, - [Ref || Ref <- find_references_for_id(Kind, Id), F(Ref)]. + Match = fun(#{uri := RefUri}) -> + case els_code_navigation:goto_definition(RefUri, RefPOI) of + {ok, [{Uri, _}]} -> true; + _Else -> + false + end + end, + Refs = [Ref || Ref <- find_references_for_id(Kind, Id), Match(Ref)], + ?LOG_DEBUG("Found scoped references (with index) for ~p: ~p", + [Id, length(Refs)]), + Refs. -spec kind_to_ref_kind(els_poi:poi_kind()) -> els_poi:poi_kind(). kind_to_ref_kind(define) -> diff --git a/apps/els_lsp/src/els_text_search.erl b/apps/els_lsp/src/els_text_search.erl index 5b603c940..f7bae1171 100644 --- a/apps/els_lsp/src/els_text_search.erl +++ b/apps/els_lsp/src/els_text_search.erl @@ -44,8 +44,7 @@ extract_pattern({behaviour, Name}) -> Name; extract_pattern({atom, Name}) -> Name; -extract_pattern({record, {_Record, Name}}) -> - %% Record fields +extract_pattern({record_field, {_Record, Name}}) -> Name; extract_pattern({record, Name}) -> Name.