From 0dfc854b00692af011f8fbedd82ea01f08143bdf Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Sun, 13 Aug 2023 13:42:11 -0400 Subject: [PATCH 1/3] 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 c492af31..ab087fa9 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: ""}} From 3d1608c1f8c161a1eb9e9f75e62c09d67bd2d0a1 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Sun, 13 Aug 2023 15:22:23 -0400 Subject: [PATCH 2/3] fixup! feat: progress messages for workspace indexing --- config/config.exs | 5 +++++ config/dev.exs | 1 + config/prod.exs | 1 + config/test.exs | 1 + lib/next_ls/db/activity.ex | 5 +++-- lib/next_ls/runtime/supervisor.ex | 2 +- test/test_helper.exs | 1 + 7 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 config/config.exs create mode 100644 config/dev.exs create mode 100644 config/prod.exs create mode 100644 config/test.exs diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 00000000..66b093dd --- /dev/null +++ b/config/config.exs @@ -0,0 +1,5 @@ +import Config + +config :next_ls, :indexing_timeout, 100 + +import_config "#{config_env()}.exs" diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 00000000..becde769 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1 @@ +import Config diff --git a/config/prod.exs b/config/prod.exs new file mode 100644 index 00000000..becde769 --- /dev/null +++ b/config/prod.exs @@ -0,0 +1 @@ +import Config diff --git a/config/test.exs b/config/test.exs new file mode 100644 index 00000000..becde769 --- /dev/null +++ b/config/test.exs @@ -0,0 +1 @@ +import Config diff --git a/lib/next_ls/db/activity.ex b/lib/next_ls/db/activity.ex index 46e12e07..26d34099 100644 --- a/lib/next_ls/db/activity.ex +++ b/lib/next_ls/db/activity.ex @@ -22,12 +22,13 @@ defmodule NextLS.DB.Activity do def init(args) do logger = Keyword.fetch!(args, :logger) lsp = Keyword.fetch!(args, :lsp) + timeout = Keyword.fetch!(args, :timeout) - {:ok, :waiting, %{count: 0, logger: logger, lsp: lsp, token: nil}} + {:ok, :waiting, %{count: 0, logger: logger, lsp: lsp, timeout: timeout, token: nil}} end def active(:cast, 0, data) do - {:keep_state, %{data | count: 0}, [{:state_timeout, 100, :waiting}]} + {:keep_state, %{data | count: 0}, [{:state_timeout, data.timeout, :waiting}]} end def active(:cast, mailbox_count, %{count: 0} = data) do diff --git a/lib/next_ls/runtime/supervisor.ex b/lib/next_ls/runtime/supervisor.ex index 50121bac..af9d7204 100644 --- a/lib/next_ls/runtime/supervisor.ex +++ b/lib/next_ls/runtime/supervisor.ex @@ -25,7 +25,7 @@ defmodule NextLS.Runtime.Supervisor do children = [ {NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name}, - {NextLS.DB.Activity, logger: logger, name: db_activity, lsp: lsp}, + {NextLS.DB.Activity, logger: logger, name: db_activity, lsp: lsp, timeout: Application.get_env(:next_ls, :indexing_timeout)}, {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/test_helper.exs b/test/test_helper.exs index b69eddb8..8d2affbc 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,6 +2,7 @@ Logger.configure(level: :warning) timeout = if System.get_env("CI", "false") == "true" do + Application.put_env(:next_ls, :indexing_timeout, 500) 60_000 else 30_000 From df00178e0bcf0d6d6793921c5ef7299a08fa4e16 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Sun, 13 Aug 2023 15:23:22 -0400 Subject: [PATCH 3/3] fixup! feat: progress messages for workspace indexing --- lib/next_ls/runtime/supervisor.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/next_ls/runtime/supervisor.ex b/lib/next_ls/runtime/supervisor.ex index af9d7204..29e8b621 100644 --- a/lib/next_ls/runtime/supervisor.ex +++ b/lib/next_ls/runtime/supervisor.ex @@ -25,7 +25,8 @@ defmodule NextLS.Runtime.Supervisor do children = [ {NextLS.Runtime.Sidecar, name: sidecar_name, db: db_name}, - {NextLS.DB.Activity, logger: logger, name: db_activity, lsp: lsp, timeout: Application.get_env(:next_ls, :indexing_timeout)}, + {NextLS.DB.Activity, + logger: logger, name: db_activity, lsp: lsp, timeout: Application.get_env(:next_ls, :indexing_timeout)}, {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]}