From 6b378da5b7b1d43a0af1c8761b1a035e3929efdd Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Tue, 2 Jan 2024 19:44:23 +0100 Subject: [PATCH] cache module completion results avoid reading docs chunks --- lib/elixir_sense/core/introspection.ex | 4 +- .../providers/suggestion/complete.ex | 78 +++++++++++++------ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/lib/elixir_sense/core/introspection.ex b/lib/elixir_sense/core/introspection.ex index 705cb256..df51493e 100644 --- a/lib/elixir_sense/core/introspection.ex +++ b/lib/elixir_sense/core/introspection.ex @@ -602,8 +602,8 @@ defmodule ElixirSense.Core.Introspection do defp to_var(atom, _) when is_atom(atom), do: {:atom, [], nil} defp to_var(_, position), do: {:"arg#{position}", [], nil} - def get_module_docs_summary(module) do - case NormalizedCode.get_docs(module, :moduledoc) do + def get_module_docs_summary(module, docs \\ nil) do + case docs || NormalizedCode.get_docs(module, :moduledoc) do {_, doc, metadata} -> {extract_summary_from_docs(doc), metadata} diff --git a/lib/elixir_sense/providers/suggestion/complete.ex b/lib/elixir_sense/providers/suggestion/complete.ex index 6493172a..9aca8cf8 100644 --- a/lib/elixir_sense/providers/suggestion/complete.ex +++ b/lib/elixir_sense/providers/suggestion/complete.ex @@ -55,6 +55,9 @@ defmodule ElixirSense.Providers.Suggestion.Complete do alias ElixirSense.Providers.Suggestion.Matcher alias ElixirSense.Providers.Suggestion.Reducers + require Logger + + @module_results_cache_key :"#{__MODULE__}_module_results_cache" @erlang_module_builtin_functions [{:module_info, 0}, {:module_info, 1}] @elixir_module_builtin_functions [{:__info__, 1}] @@ -510,22 +513,37 @@ defmodule ElixirSense.Providers.Suggestion.Complete do for mod <- match_modules(hint, true, env, metadata), usable_as_unquoted_module?(mod) do mod_as_atom = String.to_atom(mod) - subtype = Introspection.get_module_subtype(mod_as_atom) - - desc = - if hint != "" do - Introspection.get_module_docs_summary(mod_as_atom) - else - # performance optimization - # TODO is it still needed on OTP 23+? - {"", %{}} - end - name = ":" <> mod - %{kind: :module, name: name, full_name: name, type: :erlang, desc: desc, subtype: subtype} + case :persistent_term.get({@module_results_cache_key, mod_as_atom}, nil) do + nil -> get_erlang_module_result(mod_as_atom) + result -> result + end end end + def fill_erlang_module_cache(module, docs) do + get_erlang_module_result(module, docs) + end + + defp get_erlang_module_result(module, docs \\ nil) do + subtype = Introspection.get_module_subtype(module) + desc = Introspection.get_module_docs_summary(module, docs) + + name = inspect(module) + + result = %{ + kind: :module, + name: name, + full_name: name, + type: :erlang, + desc: desc, + subtype: subtype + } + + :persistent_term.put({@module_results_cache_key, module}, result) + result + end + defp struct_module_filter(true, %State.Env{} = _env, %Metadata{} = metadata) do fn module -> Struct.is_struct(module, metadata.structs) end end @@ -715,17 +733,13 @@ defmodule ElixirSense.Providers.Suggestion.Complete do end) |> Enum.uniq_by(&elem(&1, 1)) |> Enum.map(fn {name, module, required_alias?} -> - {desc, meta} = Introspection.get_module_docs_summary(module) - subtype = Introspection.get_module_subtype(module) + result = + case :persistent_term.get({@module_results_cache_key, module}, nil) do + nil -> get_elixir_module_result(module) + result -> result + end - result = %{ - kind: :module, - type: :elixir, - name: name, - full_name: inspect(module), - desc: {desc, meta}, - subtype: subtype - } + result = Map.put(result, :name, name) if required_alias? do Map.put(result, :required_alias, module) @@ -735,6 +749,26 @@ defmodule ElixirSense.Providers.Suggestion.Complete do end) end + def fill_elixir_module_cache(module, docs) do + get_elixir_module_result(module, docs) + end + + defp get_elixir_module_result(module, docs \\ nil) do + {desc, meta} = Introspection.get_module_docs_summary(module, docs) + subtype = Introspection.get_module_subtype(module) + + result = %{ + kind: :module, + type: :elixir, + full_name: inspect(module), + desc: {desc, meta}, + subtype: subtype + } + + :persistent_term.put({@module_results_cache_key, module}, result) + result + end + defp valid_alias_piece?(<>) when char in ?A..?Z, do: valid_alias_rest?(rest)