From 5620afbe2ee2f5ee53c664bfcd03218b487e6d8d Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Sun, 13 Aug 2023 13:42:11 -0400 Subject: [PATCH] feat: progress messages for workspace indexing --- lib/next_ls.ex | 1 + lib/next_ls/db.ex | 14 ++++++-- lib/next_ls/db/activity.ex | 55 +++++++++++++++++++++++++++++++ lib/next_ls/runtime/supervisor.ex | 9 +++-- test/next_ls/definition_test.exs | 14 ++++---- test/next_ls/references_test.exs | 2 ++ test/next_ls_test.exs | 6 ++-- 7 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 lib/next_ls/db/activity.ex diff --git a/lib/next_ls.ex b/lib/next_ls.ex index 7353c60c..b036e4b8 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -375,6 +375,7 @@ defmodule NextLS do {NextLS.Runtime.Supervisor, path: Path.join(working_dir, ".elixir-tools"), name: name, + lsp: lsp, registry: lsp.assigns.registry, logger: lsp.assigns.logger, runtime: [ diff --git a/lib/next_ls/db.ex b/lib/next_ls/db.ex index 30a15617..0c5adf34 100644 --- a/lib/next_ls/db.ex +++ b/lib/next_ls/db.ex @@ -1,5 +1,5 @@ defmodule NextLS.DB do - @moduledoc false + @moduledoc nil use GenServer import __MODULE__.Query @@ -26,6 +26,7 @@ defmodule NextLS.DB do file = Keyword.fetch!(args, :file) registry = Keyword.fetch!(args, :registry) logger = Keyword.fetch!(args, :logger) + activity = Keyword.fetch!(args, :activity) Registry.register(registry, :databases, %{}) {:ok, conn} = Exqlite.Basic.open(file) @@ -35,11 +36,14 @@ defmodule NextLS.DB do %{ conn: conn, file: file, - logger: logger + logger: logger, + activity: activity }} end def handle_call({:query, query, args}, _from, %{conn: conn} = s) do + {:message_queue_len, count} = Process.info(self(), :message_queue_len) + NextLS.DB.Activity.update(s.activity, count) rows = __query__({conn, s.logger}, query, args) {:reply, rows, s} @@ -74,6 +78,9 @@ defmodule NextLS.DB do end def handle_cast({:insert_symbol, symbol}, %{conn: conn} = s) do + {:message_queue_len, count} = Process.info(self(), :message_queue_len) + NextLS.DB.Activity.update(s.activity, count) + %{ module: mod, module_line: module_line, @@ -128,6 +135,9 @@ defmodule NextLS.DB do end def handle_cast({:insert_reference, reference}, %{conn: conn} = s) do + {:message_queue_len, count} = Process.info(self(), :message_queue_len) + NextLS.DB.Activity.update(s.activity, count) + %{ meta: meta, identifier: identifier, diff --git a/lib/next_ls/db/activity.ex b/lib/next_ls/db/activity.ex new file mode 100644 index 00000000..46e12e07 --- /dev/null +++ b/lib/next_ls/db/activity.ex @@ -0,0 +1,55 @@ +defmodule NextLS.DB.Activity do + @moduledoc false + @behaviour :gen_statem + + def child_spec(opts) do + %{ + id: opts[:name] || opts[:id], + start: {__MODULE__, :start_link, [opts]} + } + end + + def start_link(args) do + :gen_statem.start_link({:local, Keyword.get(args, :name)}, __MODULE__, Keyword.drop(args, [:name]), []) + end + + def update(statem, count), do: :gen_statem.cast(statem, count) + + @impl :gen_statem + def callback_mode, do: :state_functions + + @impl :gen_statem + def init(args) do + logger = Keyword.fetch!(args, :logger) + lsp = Keyword.fetch!(args, :lsp) + + {:ok, :waiting, %{count: 0, logger: logger, lsp: lsp, token: nil}} + end + + def active(:cast, 0, data) do + {:keep_state, %{data | count: 0}, [{:state_timeout, 100, :waiting}]} + end + + def active(:cast, mailbox_count, %{count: 0} = data) do + {:keep_state, %{data | count: mailbox_count}, [{:state_timeout, :cancel}]} + end + + def active(:cast, mailbox_count, data) do + {:keep_state, %{data | count: mailbox_count}, []} + end + + def active(:state_timeout, :waiting, data) do + NextLS.Progress.stop(data.lsp, data.token, "Finished indexing!") + {:next_state, :waiting, %{data | token: nil}} + end + + def waiting(:cast, 0, _data) do + :keep_state_and_data + end + + def waiting(:cast, mailbox_count, data) do + token = NextLS.Progress.token() + NextLS.Progress.start(data.lsp, token, "Indexing!") + {:next_state, :active, %{data | count: mailbox_count, token: token}} + end +end diff --git a/lib/next_ls/runtime/supervisor.ex b/lib/next_ls/runtime/supervisor.ex index a20de339..50121bac 100644 --- a/lib/next_ls/runtime/supervisor.ex +++ b/lib/next_ls/runtime/supervisor.ex @@ -10,21 +10,24 @@ defmodule NextLS.Runtime.Supervisor do @impl true def init(init_arg) do name = init_arg[:name] + lsp = init_arg[:lsp] registry = init_arg[:registry] logger = init_arg[:logger] hidden_folder = init_arg[:path] File.mkdir_p!(hidden_folder) File.write!(Path.join(hidden_folder, ".gitignore"), "*\n") - symbol_table_name = :"symbol-table-#{name}" db_name = :"db-#{name}" sidecar_name = :"sidecar-#{name}" + db_activity = :"db-activity-#{name}" Registry.register(registry, :runtime_supervisors, %{name: name}) children = [ - {NextLS.DB, logger: logger, file: "#{hidden_folder}/nextls.db", registry: registry, name: db_name}, - {NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name, symbol_table: symbol_table_name}, + {NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name}, + {NextLS.DB.Activity, logger: logger, name: db_activity, lsp: lsp}, + {NextLS.DB, + logger: logger, file: "#{hidden_folder}/nextls.db", registry: registry, name: db_name, activity: db_activity}, {NextLS.Runtime, init_arg[:runtime] ++ [name: name, registry: registry, parent: sidecar_name, db: db_name]} ] diff --git a/test/next_ls/definition_test.exs b/test/next_ls/definition_test.exs index f78f3584..546a3b47 100644 --- a/test/next_ls/definition_test.exs +++ b/test/next_ls/definition_test.exs @@ -62,7 +62,7 @@ defmodule NextLS.DefinitionTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -96,7 +96,7 @@ defmodule NextLS.DefinitionTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -132,7 +132,7 @@ defmodule NextLS.DefinitionTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -228,7 +228,7 @@ defmodule NextLS.DefinitionTest do assert :ok == notify(client, %{method: "initialized", jsonrpc: "2.0", params: %{}}) assert_request(client, "client/registerCapability", fn _params -> nil end) - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -262,7 +262,7 @@ defmodule NextLS.DefinitionTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -298,7 +298,7 @@ defmodule NextLS.DefinitionTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) @@ -369,7 +369,7 @@ defmodule NextLS.DefinitionTest do assert :ok == notify(client, %{method: "initialized", jsonrpc: "2.0", params: %{}}) assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} uri = uri(bar) diff --git a/test/next_ls/references_test.exs b/test/next_ls/references_test.exs index e8e3e0bc..cf58cf08 100644 --- a/test/next_ls/references_test.exs +++ b/test/next_ls/references_test.exs @@ -45,6 +45,7 @@ defmodule NextLS.ReferencesTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} request(client, %{ method: "textDocument/references", @@ -78,6 +79,7 @@ defmodule NextLS.ReferencesTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} request(client, %{ method: "textDocument/references", diff --git a/test/next_ls_test.exs b/test/next_ls_test.exs index 3a6e2674..2e6101e3 100644 --- a/test/next_ls_test.exs +++ b/test/next_ls_test.exs @@ -240,7 +240,7 @@ defmodule NextLSTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} request client, %{ method: "workspace/symbol", @@ -331,7 +331,7 @@ defmodule NextLSTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} request client, %{ method: "workspace/symbol", @@ -387,7 +387,7 @@ defmodule NextLSTest do assert_request(client, "client/registerCapability", fn _params -> nil end) assert_is_ready(context, "my_proj") - assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"} + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} request client, %{method: "workspace/symbol", id: 2, jsonrpc: "2.0", params: %{query: ""}}