From eb0ce88e905b357a23b568c63fc68344f38d3db7 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Thu, 5 Jun 2025 17:04:14 -0400 Subject: [PATCH 01/23] refactor: integrate GenLSP --- .credo.exs | 2 + .gitignore | 1 + Makefile | 5 +- apps/engine/benchmarks/ets_bench.exs | 2 +- apps/engine/mix.exs | 2 +- apps/engine/mix.lock | 4 +- apps/engine/test/engine/build/state_test.exs | 2 +- apps/engine/test/engine/build_test.exs | 2 +- .../engine/plugin/runner/coordinator_test.exs | 2 +- apps/engine/test/remote_control_test.exs | 2 +- apps/engine/test/support/test/fixtures.ex | 2 +- apps/expert/.iex.exs | 3 +- apps/expert/lib/expert.ex | 244 +++++++++++------- apps/expert/lib/expert/application.ex | 16 +- apps/expert/lib/expert/configuration.ex | 21 +- apps/expert/lib/expert/iex/helpers.ex | 3 +- apps/expert/lib/expert/project/diagnostics.ex | 14 +- .../lib/expert/project/progress/state.ex | 34 +-- .../lib/expert/project/search_listener.ex | 17 +- apps/expert/lib/expert/project/supervisor.ex | 10 + .../expert/provider/handlers/code_action.ex | 7 +- .../lib/expert/provider/handlers/code_lens.ex | 6 +- .../lib/expert/provider/handlers/commands.ex | 31 ++- .../expert/provider/handlers/completion.ex | 8 +- .../provider/handlers/document_symbols.ex | 6 +- .../provider/handlers/find_references.ex | 6 +- .../expert/provider/handlers/formatting.ex | 9 +- .../provider/handlers/go_to_definition.ex | 7 +- .../lib/expert/provider/handlers/hover.ex | 5 +- .../provider/handlers/workspace_symbol.ex | 9 +- apps/expert/lib/expert/state.ex | 162 ++++-------- apps/expert/lib/expert/task_queue.ex | 226 ---------------- apps/expert/lib/expert/transport.ex | 12 - apps/expert/lib/expert/transport/std_io.ex | 197 -------------- apps/expert/lib/expert/window.ex | 119 --------- apps/expert/mix.exs | 5 +- apps/expert/mix.lock | 4 +- .../test/expert/project/diagnostics_test.exs | 30 ++- .../test/expert/project/progress_test.exs | 7 +- .../handlers/find_references_test.exs | 7 +- apps/expert/test/expert/task_queue_test.exs | 168 ------------ .../test/expert/transport/std_io_test.exs | 51 ---- apps/expert/test/support/transport/no_op.ex | 5 - apps/expert_credo/mix.exs | 4 +- apps/expert_credo/mix.lock | 4 +- apps/forge/lib/forge/project.ex | 13 +- .../lib/forge/protocol/error_response.ex | 21 -- apps/forge/lib/forge/protocol/json_rpc.ex | 52 ---- apps/forge/lib/forge/protocol/response.ex | 19 -- apps/forge/mix.exs | 4 +- apps/forge/mix.lock | 4 +- apps/forge/test/forge/ast/env_test.exs | 2 +- apps/forge/test/forge/project_test.exs | 2 +- flake.lock | 6 +- flake.nix | 18 +- mix_credo.exs | 3 +- pages/glossary.md | 6 - 57 files changed, 377 insertions(+), 1256 deletions(-) delete mode 100644 apps/expert/lib/expert/task_queue.ex delete mode 100644 apps/expert/lib/expert/transport.ex delete mode 100644 apps/expert/lib/expert/transport/std_io.ex delete mode 100644 apps/expert/lib/expert/window.ex delete mode 100644 apps/expert/test/expert/task_queue_test.exs delete mode 100644 apps/expert/test/expert/transport/std_io_test.exs delete mode 100644 apps/expert/test/support/transport/no_op.ex delete mode 100644 apps/forge/lib/forge/protocol/error_response.ex delete mode 100644 apps/forge/lib/forge/protocol/json_rpc.ex delete mode 100644 apps/forge/lib/forge/protocol/response.ex diff --git a/.credo.exs b/.credo.exs index db7fda8a..adfea688 100644 --- a/.credo.exs +++ b/.credo.exs @@ -18,6 +18,8 @@ {Credo.Check.Readability.PreferImplicitTry, false}, {Credo.Check.Refactor.CyclomaticComplexity, max_complexity: 10}, {Credo.Check.Refactor.Nesting, max_nesting: 3}, + {Credo.Check.Design.TagTODO, false}, + {Credo.Check.Design.TagFIXME, false}, {Credo.Check.Refactor.PipeChainStart, []} ] } diff --git a/.gitignore b/.gitignore index 48992149..ee91cbb4 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ expert_debug priv/plts apps/forge/src/future_elixir_parser.erl +.notes/ diff --git a/Makefile b/Makefile index ea5cfcf6..89c980da 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,10 @@ env.test: export MIX_ENV=test deps.poncho: - $(foreach dir, $(poncho_dirs), cd apps/$(dir) && mix deps.get && cd ../..;) + $(foreach dir, $(poncho_dirs), cd apps/$(dir) && echo $(dir) && mix deps.get && cd ../..;) + +clean.poncho: + $(foreach dir, $(poncho_dirs), cd apps/$(dir) && echo $(dir) && mix clean && cd ../..;) deps.compile.poncho: deps.poncho $(foreach dir, $(poncho_dirs), cd apps/$(dir) && mix deps.compile && cd ../..;) diff --git a/apps/engine/benchmarks/ets_bench.exs b/apps/engine/benchmarks/ets_bench.exs index 6c90b520..0387437b 100644 --- a/apps/engine/benchmarks/ets_bench.exs +++ b/apps/engine/benchmarks/ets_bench.exs @@ -33,7 +33,7 @@ defmodule BenchHelper do end cwd = __DIR__ -project = Project.new("file://#{cwd}") +project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{cwd}") Engine.set_project(project) Project.ensure_workspace(project) diff --git a/apps/engine/mix.exs b/apps/engine/mix.exs index d63057e2..0e622d55 100644 --- a/apps/engine/mix.exs +++ b/apps/engine/mix.exs @@ -46,7 +46,7 @@ defmodule Engine.MixProject do Mix.Dialyzer.dependency(), {:elixir_sense, github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, - {:gen_lsp, "~> 0.10"}, + {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:patch, "~> 0.15", only: [:dev, :test], optional: true, runtime: false}, {:path_glob, "~> 0.2", optional: true}, {:phoenix_live_view, "~> 1.0", only: [:test], optional: true, runtime: false}, diff --git a/apps/engine/mix.lock b/apps/engine/mix.lock index 74bd1a20..63b2885f 100644 --- a/apps/engine/mix.lock +++ b/apps/engine/mix.lock @@ -2,13 +2,13 @@ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, - "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, + "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:hex, :gen_lsp, "0.10.0", "f6da076b5ccedf937d17aa9743635a2c3d0f31265c853e58b02ab84d71852270", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "768f8f7b5c5e218fb36dcebd30dcd6275b61ca77052c98c3c4c0375158392c4a"}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, diff --git a/apps/engine/test/engine/build/state_test.exs b/apps/engine/test/engine/build/state_test.exs index 8041e8bb..a3eaeb2e 100644 --- a/apps/engine/test/engine/build/state_test.exs +++ b/apps/engine/test/engine/build/state_test.exs @@ -40,7 +40,7 @@ defmodule Engine.Build.StateTest do project_name = to_string(project_name) fixture_dir = Path.join(fixtures_path(), project_name) - project = Project.new("file://#{fixture_dir}") + project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{fixture_dir}") state = State.new(project) Engine.set_project(project) diff --git a/apps/engine/test/engine/build_test.exs b/apps/engine/test/engine/build_test.exs index ffda124a..54a16738 100644 --- a/apps/engine/test/engine/build_test.exs +++ b/apps/engine/test/engine/build_test.exs @@ -35,7 +35,7 @@ defmodule Engine.BuildTest do def with_project(project_name) do project_name = to_string(project_name) fixture_dir = Path.join(fixtures_path(), project_name) - project = Project.new("file://#{fixture_dir}") + project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{fixture_dir}") project |> Project.workspace_path() diff --git a/apps/engine/test/engine/plugin/runner/coordinator_test.exs b/apps/engine/test/engine/plugin/runner/coordinator_test.exs index c7adb3fc..7df053a2 100644 --- a/apps/engine/test/engine/plugin/runner/coordinator_test.exs +++ b/apps/engine/test/engine/plugin/runner/coordinator_test.exs @@ -77,7 +77,7 @@ defmodule Engine.Plugin.Runner.CoordinatorTest do end test "works with projects" do - project = Project.new("file://" <> __DIR__) + project = Project.new(%GenLSP.LSP{mod: :foo}, "file://" <> __DIR__) Runner.diagnose(project, notifier()) diff --git a/apps/engine/test/remote_control_test.exs b/apps/engine/test/remote_control_test.exs index fcb08046..662b65f2 100644 --- a/apps/engine/test/remote_control_test.exs +++ b/apps/engine/test/remote_control_test.exs @@ -24,7 +24,7 @@ defmodule EngineTest do [fixtures_path(), "umbrella", "apps", "first"] |> Path.join() |> Document.Path.to_uri() - |> Project.new() + |> then(&Project.new(%GenLSP.LSP{mod: :foo}, &1)) start_project(subapp_project) diff --git a/apps/engine/test/support/test/fixtures.ex b/apps/engine/test/support/test/fixtures.ex index e16e284a..d6964a9c 100644 --- a/apps/engine/test/support/test/fixtures.ex +++ b/apps/engine/test/support/test/fixtures.ex @@ -15,7 +15,7 @@ defmodule Engine.Test.Fixtures do |> Path.join() |> Path.expand() |> Forge.Document.Path.to_uri() - |> Project.new() + |> then(&Project.new(%GenLSP.LSP{mod: :foo}, &1)) end def project do diff --git a/apps/expert/.iex.exs b/apps/expert/.iex.exs index 6e4499ed..96140830 100644 --- a/apps/expert/.iex.exs +++ b/apps/expert/.iex.exs @@ -1,6 +1,5 @@ alias Forge.Project - other_project = [ File.cwd!(), @@ -12,4 +11,4 @@ other_project = |> Path.join() |> Path.expand() -project = Forge.Project.new("file://#{other_project}") +project = Forge.Project.new(%GenLSP.LSP{mod: :foo}, "file://#{other_project}") diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index 83989e36..9c834a80 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -1,155 +1,173 @@ defmodule Expert do alias Expert.Provider.Handlers alias Expert.State - alias Expert.TaskQueue alias Forge.Protocol.Convert - alias GenLSP.Notifications + alias Forge.Protocol.Id alias GenLSP.Requests + alias GenLSP.Structures require Logger - use GenServer + use GenLSP @server_specific_messages [ - Notifications.TextDocumentDidChange, - Notifications.WorkspaceDidChangeConfiguration, - Notifications.WorkspaceDidChangeWatchedFiles, - Notifications.TextDocumentDidClose, - Notifications.TextDocumentDidOpen, - Notifications.TextDocumentDidSave, - Notifications.Exit, - Notifications.Initialized, - Requests.Shutdown + GenLSP.Notifications.TextDocumentDidChange, + GenLSP.Notifications.WorkspaceDidChangeConfiguration, + GenLSP.Notifications.WorkspaceDidChangeWatchedFiles, + GenLSP.Notifications.TextDocumentDidClose, + GenLSP.Notifications.TextDocumentDidOpen, + GenLSP.Notifications.TextDocumentDidSave, + GenLSP.Notifications.Exit, + GenLSP.Notifications.Initialized, + GenLSP.Requests.Shutdown ] @dialyzer {:nowarn_function, apply_to_state: 2} - @spec server_request( - term(), - (term(), {:ok, any()} | {:error, term()} -> term()) - ) :: :ok - def server_request(request, on_response) when is_function(on_response, 2) do - GenServer.call(__MODULE__, {:server_request, request, on_response}) - end - - @spec server_request(term()) :: :ok - def server_request(request) do - server_request(request, fn _, _ -> :ok end) - end + def start_link(args) do + Logger.debug(inspect(args)) - def start_link(_) do - GenServer.start_link(__MODULE__, [], name: __MODULE__) + GenLSP.start_link( + __MODULE__, + [], + Keyword.take(args, [:buffer, :assigns, :task_supervisor, :name]) + ) end - def protocol_message(message) do - GenServer.cast(__MODULE__, {:protocol_message, message}) + def init(lsp, _args) do + {:ok, assign(lsp, state: State.new())} end - def init(_) do - {:ok, State.new()} - end + def handle_request(%GenLSP.Requests.Initialize{} = request, lsp) do + state = assigns(lsp).state + Process.send_after(self(), :default_config, :timer.seconds(5)) - def handle_call({:server_request, request, on_response}, _from, %State{} = state) do - new_state = State.add_request(state, request, on_response) - {:reply, :ok, new_state} - end + case State.initialize(state, request, lsp) do + {:ok, response, state} -> + # TODO: this should be gated behind the dynamic registration in the initialization params + registrations = registrations() - def handle_cast({:protocol_message, message}, %State{} = state) do - new_state = - case handle_message(message, state) do - {:ok, new_state} -> - new_state + if nil != GenLSP.request(lsp, registrations) do + Logger.error("Failed to register capability") + end - error -> - Logger.error( - "Could not handle message #{inspect(message.__struct__)} #{inspect(error)}" - ) + lsp = assign(lsp, state: state) + {:ok, response} = Forge.Protocol.Convert.to_lsp(response) - state - end + {:reply, response, lsp} - {:noreply, new_state} - end + {:error, error} -> + response = %GenLSP.ErrorResponse{ + code: GenLSP.Enumerations.ErrorCodes.invalid_request(), + message: to_string(error) + } - def handle_cast(other, %State{} = state) do - Logger.info("got other: #{inspect(other)}") - {:noreply, state} + {:reply, response, lsp} + end end - def handle_info(:default_config, %State{configuration: nil} = state) do - Logger.warning( - "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <> - "Using default settings." - ) + def handle_request(%mod{} = request, lsp) when mod in @server_specific_messages do + GenLSP.error(lsp, "handling server specific request #{Macro.to_string(mod)}") - {:ok, config} = State.default_configuration(state) - {:noreply, %State{state | configuration: config}} + with {:ok, request} <- Forge.Protocol.Convert.to_native(request), + {:ok, response, state} <- apply_to_state(assigns(lsp).state, request), + {:ok, response} <- Forge.Protocol.Convert.to_lsp(response) do + {:reply, Forge.Protocol.Convert.to_lsp(response), assign(lsp, state: state)} + else + error -> + message = "Failed to handle #{mod}, #{inspect(error)}" + Logger.error(message) + + {:reply, + %GenLSP.ErrorResponse{ + code: GenLSP.Enumerations.ErrorCodes.internal_error(), + message: message + }, lsp} + end end - def handle_info(:default_config, %State{} = state) do - {:noreply, state} - end + def handle_request(request, lsp) do + state = assigns(lsp).state - def handle_message(%Requests.Initialize{} = initialize, %State{} = state) do - Process.send_after(self(), :default_config, :timer.seconds(5)) + with {:ok, handler} <- fetch_handler(request), + {:ok, request} <- Convert.to_native(request), + {:ok, response} <- handler.handle(request, state.configuration), + {:ok, response} <- Forge.Protocol.Convert.to_lsp(response) do + {:reply, response, lsp} + else + {:error, {:unhandled, _}} -> + Logger.info("Unhandled request: #{request.method}") - case State.initialize(state, initialize) do - {:ok, _state} = success -> - success + {:reply, + %GenLSP.ErrorResponse{ + code: GenLSP.Enumerations.ErrorCodes.method_not_found(), + message: "Method not found" + }, lsp} error -> - {error, state} + message = "Failed to handle #{request.method}, #{inspect(error)}" + Logger.error(message) + + {:reply, + %GenLSP.ErrorResponse{ + code: GenLSP.Enumerations.ErrorCodes.internal_error(), + message: message + }, lsp} end end - def handle_message(%Notifications.DollarCancelRequest{} = cancel_notification, %State{} = state) do - TaskQueue.cancel(cancel_notification) - {:ok, state} - end - - def handle_message(%message_module{} = message, %State{} = state) - when message_module in @server_specific_messages do - case apply_to_state(state, message) do - {:ok, _} = success -> - success - + def handle_notification(%mod{} = notification, lsp) when mod in @server_specific_messages do + with {:ok, notification} <- Convert.to_native(notification), + {:ok, state} <- apply_to_state(assigns(lsp).state, notification) do + {:noreply, assign(lsp, state: state)} + else error -> - Logger.error("Failed to handle #{message.__struct__}, #{inspect(error)}") - end - end + message = "Failed to handle #{notification.method}, #{inspect(error)}" + Logger.error(message) - def handle_message(nil, %State{} = state) do - # NOTE: This deals with the response after a request is requested by the server, - # such as the response of `CreateWorkDoneProgress`. - {:ok, state} + {:noreply, lsp} + end end - def handle_message(%_{} = request, %State{} = state) do - with {:ok, handler} <- fetch_handler(request), - {:ok, request} <- Convert.to_native(request) do - # Logger.info("Handling request: #{inspect(request, pretty: true)}") + def handle_notification(notification, lsp) do + state = assigns(lsp).state - TaskQueue.add(request.id, {handler, :handle, [request, state.configuration]}) + with {:ok, handler} <- fetch_handler(notification), + {:ok, notification} <- Convert.to_native(notification), + {:ok, _response} <- handler.handle(notification, state.configuration) do + {:noreply, lsp} else {:error, {:unhandled, _}} -> - Logger.info("Unhandled request: #{request.method}") + Logger.info("Unhandled notification: #{notification.method}") - _ -> - :ok - end + {:noreply, lsp} + + error -> + message = "Failed to handle #{notification.method}, #{inspect(error)}" + Logger.error(message) - {:ok, state} + {:noreply, lsp} + end end - def handle_message(%{} = response, %State{} = state) do - new_state = State.finish_request(state, response) + def handle_info(:default_config, %State{configuration: nil} = state) do + Logger.warning( + "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <> + "Using default settings." + ) - {:ok, new_state} + {:ok, config} = State.default_configuration(state) + {:noreply, %State{state | configuration: config}} + end + + def handle_info(:default_config, %State{} = state) do + {:noreply, state} end defp apply_to_state(%State{} = state, %{} = request_or_notification) do case State.apply(state, request_or_notification) do - {:ok, new_state} -> {:ok, new_state} + {:ok, response, new_state} -> {:ok, response, new_state} + {:ok, state} -> {:ok, state} :ok -> {:ok, state} error -> {error, state} end @@ -185,11 +203,37 @@ defmodule Expert do %Requests.TextDocumentDocumentSymbol{} -> {:ok, Handlers.DocumentSymbols} - %Requests.WorkspaceSymbol{} -> + %GenLSP.Requests.WorkspaceSymbol{} -> {:ok, Handlers.WorkspaceSymbol} %request_module{} -> {:error, {:unhandled, request_module}} end end + + defp registrations do + %Requests.ClientRegisterCapability{ + id: Id.next(), + params: %GenLSP.Structures.RegistrationParams{ + registrations: [file_watcher_registration()] + } + } + end + + @did_changed_watched_files_id "-42" + @watched_extensions ~w(ex exs) + defp file_watcher_registration do + extension_glob = "{" <> Enum.join(@watched_extensions, ",") <> "}" + + watchers = [ + %Structures.FileSystemWatcher{glob_pattern: "**/mix.lock"}, + %Structures.FileSystemWatcher{glob_pattern: "**/*.#{extension_glob}"} + ] + + %Structures.Registration{ + id: @did_changed_watched_files_id, + method: "workspace/didChangeWatchedFiles", + register_options: %Structures.DidChangeWatchedFilesRegistrationOptions{watchers: watchers} + } + end end diff --git a/apps/expert/lib/expert/application.ex b/apps/expert/lib/expert/application.ex index f57086b7..f3537eff 100644 --- a/apps/expert/lib/expert/application.ex +++ b/apps/expert/lib/expert/application.ex @@ -5,20 +5,22 @@ defmodule Expert.Application do alias Forge.Document - alias Expert.TaskQueue - alias Expert.Transport - use Application @impl true def start(_type, _args) do children = [ document_store_child_spec(), - Expert, {DynamicSupervisor, Expert.Project.Supervisor.options()}, - {Task.Supervisor, name: TaskQueue.task_supervisor_name()}, - TaskQueue, - {Transport.StdIO, [:standard_io, &Expert.protocol_message/1]} + {DynamicSupervisor, name: Expert.DynamicSupervisor}, + {GenLSP.Assigns, [name: Expert.Assigns]}, + {Task.Supervisor, name: :expert_task_queue}, + {GenLSP.Buffer, name: Expert.Buffer}, + {Expert, + buffer: Expert.Buffer, + task_supervisor: :expert_task_queue, + dynamic_supervisor: Expert.DynamicSupervisor, + assigns: Expert.Assigns} ] opts = [strategy: :one_for_one, name: Expert.Supervisor] diff --git a/apps/expert/lib/expert/configuration.ex b/apps/expert/lib/expert/configuration.ex index 38961642..e952854e 100644 --- a/apps/expert/lib/expert/configuration.ex +++ b/apps/expert/lib/expert/configuration.ex @@ -29,10 +29,10 @@ defmodule Expert.Configuration do @dialyzer {:nowarn_function, set_dialyzer_enabled: 2} - @spec new(Forge.uri(), map(), String.t() | nil) :: t - def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name) do + @spec new(Forge.uri(), map(), String.t() | nil, GenLSP.Lsp.t()) :: t + def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name, lsp) do support = Support.new(client_capabilities) - project = Project.new(root_uri) + project = Project.new(lsp, root_uri) %__MODULE__{support: support, project: project, client_name: client_name} |> tap(&set/1) @@ -44,6 +44,7 @@ defmodule Expert.Configuration do end defp set(%__MODULE__{} = config) do + # FIXME(mhanberg): I don't think this will work once we have workspace support :persistent_term.put(__MODULE__, config) end @@ -107,15 +108,17 @@ defmodule Expert.Configuration do %__MODULE__{old_config | dialyzer_enabled?: enabled?} end - defp maybe_add_watched_extensions(%__MODULE__{} = old_config, %{ - "additionalWatchedExtensions" => [] - }) do + defp maybe_add_watched_extensions( + %__MODULE__{} = old_config, + %{"additionalWatchedExtensions" => []} + ) do {:ok, old_config} end - defp maybe_add_watched_extensions(%__MODULE__{} = old_config, %{ - "additionalWatchedExtensions" => extensions - }) + defp maybe_add_watched_extensions( + %__MODULE__{} = old_config, + %{"additionalWatchedExtensions" => extensions} + ) when is_list(extensions) do register_id = Id.next() request_id = Id.next() diff --git a/apps/expert/lib/expert/iex/helpers.ex b/apps/expert/lib/expert/iex/helpers.ex index 92dfe17b..e7824747 100644 --- a/apps/expert/lib/expert/iex/helpers.ex +++ b/apps/expert/lib/expert/iex/helpers.ex @@ -177,7 +177,8 @@ defmodule Expert.IEx.Helpers do |> Path.expand() project_uri = "file://#{project_path}" - Forge.Project.new(project_uri) + # FIXME(mhanberg): unclear how we will get LSP struct here + Forge.Project.new(%GenLSP.LSP{mod: :foo}, project_uri) end) end diff --git a/apps/expert/lib/expert/project/diagnostics.ex b/apps/expert/lib/expert/project/diagnostics.ex index 251abfea..bdab0362 100644 --- a/apps/expert/lib/expert/project/diagnostics.ex +++ b/apps/expert/lib/expert/project/diagnostics.ex @@ -1,7 +1,6 @@ defmodule Expert.Project.Diagnostics do alias Engine.Api.Messages alias Expert.Project.Diagnostics.State - alias Expert.Transport alias Forge.Formats alias Forge.Project alias GenLSP.Notifications.TextDocumentPublishDiagnostics @@ -94,13 +93,12 @@ defmodule Expert.Project.Diagnostics do defp publish_diagnostics(%State{} = state) do Enum.each(state.entries_by_uri, fn {uri, %State.Entry{} = entry} -> - diagnostics_list = State.Entry.diagnostics(entry) - - notification = %TextDocumentPublishDiagnostics{ - params: %Structures.PublishDiagnosticsParams{uri: uri, diagnostics: diagnostics_list} - } - - Transport.write(notification) + with {:ok, diagnostics} <- + entry |> State.Entry.diagnostics() |> Forge.Protocol.Convert.to_lsp() do + GenLSP.notify(state.project.lsp, %TextDocumentPublishDiagnostics{ + params: %Structures.PublishDiagnosticsParams{uri: uri, diagnostics: diagnostics} + }) + end end) end diff --git a/apps/expert/lib/expert/project/progress/state.ex b/apps/expert/lib/expert/project/progress/state.ex index da450326..43c2ee0c 100644 --- a/apps/expert/lib/expert/project/progress/state.ex +++ b/apps/expert/lib/expert/project/progress/state.ex @@ -2,7 +2,6 @@ defmodule Expert.Project.Progress.State do alias Expert.Configuration alias Expert.Project.Progress.Percentage alias Expert.Project.Progress.Value - alias Expert.Transport alias Forge.Project alias Forge.Protocol.Id alias GenLSP.Requests @@ -20,8 +19,8 @@ defmodule Expert.Project.Progress.State do progress = Value.begin(label) progress_by_label = Map.put(state.progress_by_label, label, progress) - write_work_done(progress.token) - write(progress) + write_work_done(state.project.lsp, progress.token) + write(state.project.lsp, progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -29,8 +28,8 @@ defmodule Expert.Project.Progress.State do def begin(%__MODULE__{} = state, percent_progress(label: label, max: max)) do progress = Percentage.begin(label, max) progress_by_label = Map.put(state.progress_by_label, label, progress) - write_work_done(progress.token) - write(progress) + write_work_done(state.project.lsp, progress.token) + write(state.project.lsp, progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -42,7 +41,7 @@ defmodule Expert.Project.Progress.State do {new_value, new_value} end) - write(progress) + write(state.project.lsp, progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -56,7 +55,7 @@ defmodule Expert.Project.Progress.State do {new_percentage, new_percentage} end) - write(progress) + write(state.project.lsp, progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -66,7 +65,7 @@ defmodule Expert.Project.Progress.State do case progress do %Value{} = progress -> - progress |> Value.complete(message) |> write + write(state.project.lsp, Value.complete(progress, message)) _ -> :ok @@ -81,7 +80,7 @@ defmodule Expert.Project.Progress.State do case progress do %Percentage{} = progress -> - progress |> Percentage.complete(message) |> write() + write(state.project.lsp, Percentage.complete(progress, message)) nil -> :ok @@ -90,22 +89,23 @@ defmodule Expert.Project.Progress.State do %__MODULE__{state | progress_by_label: progress_by_label} end - defp write_work_done(token) do + defp write_work_done(lsp, token) do if Configuration.client_supports?(:work_done_progress) do - progress = %Requests.WindowWorkDoneProgressCreate{ + GenLSP.request(lsp, %Requests.WindowWorkDoneProgressCreate{ id: Id.next(), params: %Structures.WorkDoneProgressCreateParams{token: token} - } - - Transport.write(progress) + }) end end - defp write(%progress_module{token: token} = progress) when not is_nil(token) do + defp write(lsp, %progress_module{token: token} = progress) when not is_nil(token) do if Configuration.client_supports?(:work_done_progress) do - progress |> progress_module.to_protocol() |> Transport.write() + GenLSP.notify( + lsp, + progress_module.to_protocol(progress) + ) end end - defp write(_), do: :ok + defp write(_, _), do: :ok end diff --git a/apps/expert/lib/expert/project/search_listener.ex b/apps/expert/lib/expert/project/search_listener.ex index afa347db..1dbf4a62 100644 --- a/apps/expert/lib/expert/project/search_listener.ex +++ b/apps/expert/lib/expert/project/search_listener.ex @@ -1,6 +1,5 @@ defmodule Expert.Project.SearchListener do alias Engine.Api - alias Expert.Window alias Forge.Formats alias Forge.Project alias Forge.Protocol.Id @@ -32,7 +31,7 @@ defmodule Expert.Project.SearchListener do @impl GenServer def handle_info(project_reindex_requested(), %Project{} = project) do Logger.info("project reindex requested") - send_code_lens_refresh() + GenLSP.request(project.lsp, %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) {:noreply, project} end @@ -40,15 +39,15 @@ defmodule Expert.Project.SearchListener do def handle_info(project_reindexed(elapsed_ms: elapsed), %Project{} = project) do message = "Reindexed #{Project.name(project)} in #{Formats.time(elapsed, unit: :millisecond)}" Logger.info(message) - send_code_lens_refresh() + GenLSP.request(project.lsp, %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) - Window.show_info_message(message) + GenLSP.notify(project.lsp, %GenLSP.Notifications.WindowShowMessage{ + params: %GenLSP.Structures.ShowMessageParams{ + type: GenLSP.Enumerations.MessageType.info(), + message: message + } + }) {:noreply, project} end - - defp send_code_lens_refresh do - request = %Requests.WorkspaceCodeLensRefresh{id: Id.next()} - Expert.server_request(request) - end end diff --git a/apps/expert/lib/expert/project/supervisor.ex b/apps/expert/lib/expert/project/supervisor.ex index b2c2980f..27b39500 100644 --- a/apps/expert/lib/expert/project/supervisor.ex +++ b/apps/expert/lib/expert/project/supervisor.ex @@ -7,6 +7,16 @@ defmodule Expert.Project.Supervisor do alias Expert.Project.SearchListener alias Forge.Project + # TODO: this module is slightly weird + # it is a module based supervisor, but has lots of dynamic supervisor functions + # what I learned is that in Expert.Application, it is starting an ad hoc + # dynamic supervisor, calling a function from this module + # Later, when the server is initializing, it calls the start function in + # this module, which starts a normal supervisor, which the start_link and + # init callbacks will be called + # my suggestion is to separate the dynamic supervisor functionalities from + # this module into its own module + use Supervisor def dynamic_supervisor_name do diff --git a/apps/expert/lib/expert/provider/handlers/code_action.ex b/apps/expert/lib/expert/provider/handlers/code_action.ex index be0c637b..5be304f9 100644 --- a/apps/expert/lib/expert/provider/handlers/code_action.ex +++ b/apps/expert/lib/expert/provider/handlers/code_action.ex @@ -1,13 +1,11 @@ defmodule Expert.Provider.Handlers.CodeAction do alias Engine.CodeAction alias Expert.Configuration - alias Forge.Protocol.Response alias GenLSP.Requests alias GenLSP.Structures def handle( - %Requests.TextDocumentCodeAction{params: %Structures.CodeActionParams{} = params} = - request, + %Requests.TextDocumentCodeAction{params: %Structures.CodeActionParams{} = params}, %Configuration{} = config ) do document = Forge.Document.Container.context_document(params, nil) @@ -24,9 +22,8 @@ defmodule Expert.Provider.Handlers.CodeAction do ) results = Enum.map(code_actions, &to_result/1) - reply = %Response{id: request.id, result: results} - {:reply, reply} + {:ok, results} end defp to_code_action_diagnostic(%Structures.Diagnostic{} = diagnostic) do diff --git a/apps/expert/lib/expert/provider/handlers/code_lens.ex b/apps/expert/lib/expert/provider/handlers/code_lens.ex index 38650e8f..3218addb 100644 --- a/apps/expert/lib/expert/provider/handlers/code_lens.ex +++ b/apps/expert/lib/expert/provider/handlers/code_lens.ex @@ -5,7 +5,6 @@ defmodule Expert.Provider.Handlers.CodeLens do alias Forge.Document.Position alias Forge.Document.Range alias Forge.Project - alias Forge.Protocol.Response alias GenLSP.Requests alias GenLSP.Structures @@ -13,7 +12,7 @@ defmodule Expert.Provider.Handlers.CodeLens do require Logger def handle( - %Requests.TextDocumentCodeLens{params: %Structures.CodeLensParams{} = params} = request, + %Requests.TextDocumentCodeLens{params: %Structures.CodeLensParams{} = params}, %Configuration{} = config ) do document = Document.Container.context_document(params, nil) @@ -24,8 +23,7 @@ defmodule Expert.Provider.Handlers.CodeLens do lens -> List.wrap(lens) end - response = %Response{id: request.id, result: lenses} - {:reply, response} + {:ok, lenses} end defp reindex_lens(%Project{} = project, %Document{} = document) do diff --git a/apps/expert/lib/expert/provider/handlers/commands.ex b/apps/expert/lib/expert/provider/handlers/commands.ex index 8e37c3ba..64eb45eb 100644 --- a/apps/expert/lib/expert/provider/handlers/commands.ex +++ b/apps/expert/lib/expert/provider/handlers/commands.ex @@ -1,9 +1,6 @@ defmodule Expert.Provider.Handlers.Commands do alias Expert.Configuration - alias Expert.Window alias Forge.Project - alias Forge.Protocol.ErrorResponse - alias Forge.Protocol.Response alias GenLSP.Enumerations.ErrorCodes alias GenLSP.Requests alias GenLSP.Structures @@ -26,41 +23,43 @@ defmodule Expert.Provider.Handlers.Commands do end def handle( - %Requests.WorkspaceExecuteCommand{params: %Structures.ExecuteCommandParams{} = params} = - request, + %Requests.WorkspaceExecuteCommand{params: %Structures.ExecuteCommandParams{} = params}, %Configuration{} = config ) do response = case params.command do @reindex_name -> Logger.info("Reindex #{Project.name(config.project)}") - reindex(config.project, request.id) + reindex(config.project) invalid -> message = "#{invalid} is not a valid command" - internal_error(request.id, message) + internal_error(message) end {:reply, response} end - defp reindex(%Project{} = project, request_id) do + defp reindex(%Project{} = project) do case Engine.Api.reindex(project) do :ok -> - %Response{id: request_id, result: "ok"} + {:ok, "ok"} error -> - Window.show_error_message("Indexing #{Project.name(project)} failed") + GenLSP.notify(project.lsp, %GenLSP.Notifications.WindowShowMessage{ + params: %GenLSP.Structures.ShowMessageParams{ + type: GenLSP.Enumerations.MessageType.error(), + message: "Indexing #{Project.name(project)} failed" + } + }) + Logger.error("Indexing command failed due to #{inspect(error)}") - internal_error(request_id, "Could not reindex: #{error}") + {:ok, internal_error("Could not reindex: #{error}")} end end - defp internal_error(request_id, message) do - %ErrorResponse{ - id: request_id, - error: %GenLSP.ErrorResponse{code: ErrorCodes.internal_error(), message: message} - } + defp internal_error(message) do + %GenLSP.ErrorResponse{code: ErrorCodes.internal_error(), message: message} end end diff --git a/apps/expert/lib/expert/provider/handlers/completion.ex b/apps/expert/lib/expert/provider/handlers/completion.ex index b7d6402a..39f19618 100644 --- a/apps/expert/lib/expert/provider/handlers/completion.ex +++ b/apps/expert/lib/expert/provider/handlers/completion.ex @@ -4,18 +4,15 @@ defmodule Expert.Provider.Handlers.Completion do alias Forge.Ast alias Forge.Document alias Forge.Document.Position - alias Forge.Protocol.Response alias GenLSP.Enumerations.CompletionTriggerKind alias GenLSP.Requests alias GenLSP.Structures alias GenLSP.Structures.CompletionContext - require Logger - def handle( %Requests.TextDocumentCompletion{ params: %Structures.CompletionParams{} = params - } = request, + }, %Configuration{} = config ) do document = Document.Container.context_document(params, nil) @@ -28,8 +25,7 @@ defmodule Expert.Provider.Handlers.Completion do params.context || %CompletionContext{trigger_kind: CompletionTriggerKind.invoked()} ) - response = %Response{id: request.id, result: completions} - {:reply, response} + {:ok, completions} end defp document_analysis(%Document{} = document, %Position{} = position) do diff --git a/apps/expert/lib/expert/provider/handlers/document_symbols.ex b/apps/expert/lib/expert/provider/handlers/document_symbols.ex index ed2edd4f..7ff635db 100644 --- a/apps/expert/lib/expert/provider/handlers/document_symbols.ex +++ b/apps/expert/lib/expert/provider/handlers/document_symbols.ex @@ -3,7 +3,6 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do alias Engine.CodeIntelligence.Symbols alias Expert.Configuration alias Forge.Document - alias Forge.Protocol.Response alias GenLSP.Enumerations.SymbolKind alias GenLSP.Requests alias GenLSP.Structures @@ -16,9 +15,7 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do |> Api.document_symbols(document) |> Enum.map(&to_response(&1, document)) - response = %Response{id: request.id, result: symbols} - - {:reply, response} + {:ok, symbols} end def to_response(%Symbols.Document{} = root, %Document{} = document) do @@ -31,6 +28,7 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do nil end + # FIXME: the ranges need to be converted to lsp structures %Structures.DocumentSymbol{ children: children, detail: root.detail, diff --git a/apps/expert/lib/expert/provider/handlers/find_references.ex b/apps/expert/lib/expert/provider/handlers/find_references.ex index 4f740247..38c06a9b 100644 --- a/apps/expert/lib/expert/provider/handlers/find_references.ex +++ b/apps/expert/lib/expert/provider/handlers/find_references.ex @@ -3,14 +3,13 @@ defmodule Expert.Provider.Handlers.FindReferences do alias Expert.Configuration alias Forge.Ast alias Forge.Document - alias Forge.Protocol.Response alias GenLSP.Requests.TextDocumentReferences alias GenLSP.Structures require Logger def handle( - %TextDocumentReferences{params: %Structures.ReferenceParams{} = params} = request, + %TextDocumentReferences{params: %Structures.ReferenceParams{} = params}, %Configuration{} = config ) do document = Forge.Document.Container.context_document(params, nil) @@ -25,7 +24,6 @@ defmodule Expert.Provider.Handlers.FindReferences do nil end - response = %Response{id: request.id, result: locations} - {:reply, response} + {:ok, locations} end end diff --git a/apps/expert/lib/expert/provider/handlers/formatting.ex b/apps/expert/lib/expert/provider/handlers/formatting.ex index 1ce544de..0c4f86b6 100644 --- a/apps/expert/lib/expert/provider/handlers/formatting.ex +++ b/apps/expert/lib/expert/provider/handlers/formatting.ex @@ -1,27 +1,24 @@ defmodule Expert.Provider.Handlers.Formatting do alias Expert.Configuration alias Forge.Document.Changes - alias Forge.Protocol.Response alias GenLSP.Requests alias GenLSP.Structures require Logger def handle( - %Requests.TextDocumentFormatting{params: %Structures.DocumentFormattingParams{} = params} = - request, + %Requests.TextDocumentFormatting{params: %Structures.DocumentFormattingParams{} = params}, %Configuration{} = config ) do document = Forge.Document.Container.context_document(params, nil) case Engine.Api.format(config.project, document) do {:ok, %Changes{} = document_edits} -> - response = %Response{id: request.id, result: document_edits} - {:reply, response} + {:ok, document_edits} {:error, reason} -> Logger.error("Formatter failed #{inspect(reason)}") - {:reply, %Response{id: request.id, result: nil}} + {:ok, nil} end end end diff --git a/apps/expert/lib/expert/provider/handlers/go_to_definition.ex b/apps/expert/lib/expert/provider/handlers/go_to_definition.ex index 963f9d51..3115131e 100644 --- a/apps/expert/lib/expert/provider/handlers/go_to_definition.ex +++ b/apps/expert/lib/expert/provider/handlers/go_to_definition.ex @@ -1,6 +1,5 @@ defmodule Expert.Provider.Handlers.GoToDefinition do alias Expert.Configuration - alias Forge.Protocol.Response alias GenLSP.Requests alias GenLSP.Structures @@ -9,18 +8,18 @@ defmodule Expert.Provider.Handlers.GoToDefinition do def handle( %Requests.TextDocumentDefinition{ params: %Structures.DefinitionParams{} = params - } = request, + }, %Configuration{} = config ) do document = Forge.Document.Container.context_document(params, nil) case Engine.Api.definition(config.project, document, params.position) do {:ok, native_location} -> - {:reply, %Response{id: request.id, result: native_location}} + {:ok, native_location} {:error, reason} -> Logger.error("GoToDefinition failed: #{inspect(reason)}") - {:reply, %Response{id: request.id, result: nil}} + {:ok, nil} end end end diff --git a/apps/expert/lib/expert/provider/handlers/hover.ex b/apps/expert/lib/expert/provider/handlers/hover.ex index 0d040ee6..6697eba0 100644 --- a/apps/expert/lib/expert/provider/handlers/hover.ex +++ b/apps/expert/lib/expert/provider/handlers/hover.ex @@ -7,7 +7,6 @@ defmodule Expert.Provider.Handlers.Hover do alias Forge.Document alias Forge.Document.Position alias Forge.Project - alias Forge.Protocol.Response alias GenLSP.Requests alias GenLSP.Structures @@ -16,7 +15,7 @@ defmodule Expert.Provider.Handlers.Hover do def handle( %Requests.TextDocumentHover{ params: %Structures.HoverParams{} = params - } = request, + }, %Configuration{} = config ) do document = Document.Container.context_document(params, nil) @@ -34,7 +33,7 @@ defmodule Expert.Provider.Handlers.Hover do nil end - {:reply, %Response{id: request.id, result: maybe_hover}} + {:ok, maybe_hover} end defp resolve_entity(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do diff --git a/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex b/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex index d2fb4a3e..1b5211b0 100644 --- a/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex +++ b/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex @@ -2,7 +2,6 @@ defmodule Expert.Provider.Handlers.WorkspaceSymbol do alias Engine.Api alias Engine.CodeIntelligence.Symbols alias Expert.Configuration - alias Forge.Protocol.Response alias GenLSP.Enumerations.SymbolKind alias GenLSP.Requests alias GenLSP.Structures @@ -10,7 +9,7 @@ defmodule Expert.Provider.Handlers.WorkspaceSymbol do require Logger def handle( - %Requests.WorkspaceSymbol{params: %Structures.WorkspaceSymbolParams{} = params} = request, + %Requests.WorkspaceSymbol{params: %Structures.WorkspaceSymbolParams{} = params}, %Configuration{} = config ) do symbols = @@ -23,11 +22,9 @@ defmodule Expert.Provider.Handlers.WorkspaceSymbol do [] end - response = %Response{id: request.id, result: symbols} + Logger.info("WorkspaceSymbol results: #{inspect(symbols, pretty: true)}") - Logger.info("WorkspaceSymbol results: #{inspect(response, pretty: true)}") - - {:reply, response} + {:ok, symbols} end def to_response(%Symbols.Workspace{} = root) do diff --git a/apps/expert/lib/expert/state.ex b/apps/expert/lib/expert/state.ex index ca3926f6..9ae5e19f 100644 --- a/apps/expert/lib/expert/state.ex +++ b/apps/expert/lib/expert/state.ex @@ -4,10 +4,7 @@ defmodule Expert.State do alias Expert.Configuration alias Expert.Project alias Expert.Provider.Handlers - alias Expert.Transport alias Forge.Document - alias Forge.Protocol.Id - alias Forge.Protocol.Response alias GenLSP.Enumerations alias GenLSP.Notifications alias GenLSP.Requests @@ -37,70 +34,35 @@ defmodule Expert.State do %__MODULE__{} end - def initialize(%__MODULE__{initialized?: false} = state, %Requests.Initialize{ - id: event_id, - params: %Structures.InitializeParams{} = event - }) do + # TODO: this function has a side effect (starting the project supervisor) + # that i think might be better off in the calling function + def initialize( + %__MODULE__{initialized?: false} = state, + %Requests.Initialize{ + params: %Structures.InitializeParams{} = event + }, + lsp + ) do client_name = case event.client_info do %{name: name} -> name _ -> nil end - config = Configuration.new(event.root_uri, event.capabilities, client_name) + config = Configuration.new(event.root_uri, event.capabilities, client_name, lsp) new_state = %__MODULE__{state | configuration: config, initialized?: true} Logger.info("Starting project at uri #{config.project.root_uri}") - event_id - |> initialize_result() - |> tap(fn result -> - Logger.info("Sending initialize result: #{inspect(result)}") - end) - |> Transport.write() - - Transport.write(registrations()) + response = initialize_result() Project.Supervisor.start(config.project) - {:ok, new_state} + {:ok, response, new_state} end def initialize(%__MODULE__{initialized?: true}, %Requests.Initialize{}) do {:error, :already_initialized} end - def in_flight?(%__MODULE__{} = state, request_id) do - Map.has_key?(state.in_flight_requests, request_id) - end - - def add_request(%__MODULE__{} = state, request, callback) do - Transport.write(request) - - in_flight_requests = Map.put(state.in_flight_requests, request.id, {request, callback}) - - %__MODULE__{state | in_flight_requests: in_flight_requests} - end - - def finish_request(%__MODULE__{} = state, response) do - %{"id" => response_id} = response - - case Map.pop(state.in_flight_requests, response_id) do - {{%request_module{} = request, callback}, in_flight_requests} -> - case request_module.parse_response(response) do - {:ok, response} -> - callback.(request, {:ok, response.result}) - - error -> - Logger.info("failed to parse response for #{request_module}, #{inspect(error)}") - callback.(request, error) - end - - %__MODULE__{state | in_flight_requests: in_flight_requests} - - _ -> - state - end - end - def default_configuration(%__MODULE__{configuration: config}) do Configuration.default(config) end @@ -126,22 +88,23 @@ defmodule Expert.State do {:ok, config} -> {:ok, %__MODULE__{state | configuration: config}} - {:ok, config, response} -> - Transport.write(response) + {:ok, config, request} -> + GenLSP.request(state.configuration.project.lsp, request) {:ok, %__MODULE__{state | configuration: config}} end {:ok, state} end - def apply(%__MODULE__{} = state, %Notifications.TextDocumentDidChange{params: event}) do - uri = event.text_document.uri - version = event.text_document.version + def apply(%__MODULE__{} = state, %GenLSP.Notifications.TextDocumentDidChange{params: params}) do + uri = params.text_document.uri + version = params.text_document.version project = state.configuration.project case Document.Store.get_and_update( uri, - &Document.apply_content_changes(&1, version, event.content_changes) + # TODO: this function needs to accept the GenLSP data structure + &Document.apply_content_changes(&1, version, params.content_changes) ) do {:ok, updated_source} -> updated_message = @@ -161,8 +124,8 @@ defmodule Expert.State do end end - def apply(%__MODULE__{} = state, %Notifications.TextDocumentDidOpen{} = did_open) do - %Structures.TextDocumentItem{ + def apply(%__MODULE__{} = state, %GenLSP.Notifications.TextDocumentDidOpen{} = did_open) do + %GenLSP.Structures.TextDocumentItem{ text: text, uri: uri, version: version, @@ -171,17 +134,17 @@ defmodule Expert.State do case Document.Store.open(uri, text, version, language_id) do :ok -> - Logger.info("opened #{uri}") + Logger.info("################### opened #{uri}") {:ok, state} error -> - Logger.error("Could not open #{uri} #{inspect(error)}") + Logger.error("################## Could not open #{uri} #{inspect(error)}") error end end - def apply(%__MODULE__{} = state, %Notifications.TextDocumentDidClose{params: event}) do - uri = event.text_document.uri + def apply(%__MODULE__{} = state, %GenLSP.Notifications.TextDocumentDidClose{params: params}) do + uri = params.text_document.uri case Document.Store.close(uri) do :ok -> @@ -196,8 +159,8 @@ defmodule Expert.State do end end - def apply(%__MODULE__{} = state, %Notifications.TextDocumentDidSave{params: event}) do - uri = event.text_document.uri + def apply(%__MODULE__{} = state, %GenLSP.Notifications.TextDocumentDidSave{params: params}) do + uri = params.text_document.uri case Document.Store.save(uri) do :ok -> @@ -215,17 +178,19 @@ defmodule Expert.State do {:ok, %__MODULE__{state | initialized?: true}} end - def apply(%__MODULE__{} = state, %Requests.Shutdown{} = shutdown) do - Transport.write(%Response{id: shutdown.id}) + def apply(%__MODULE__{} = state, %GenLSP.Requests.Shutdown{}) do + # TODO: why is this an error log? Logger.error("Shutting down") - {:ok, %__MODULE__{state | shutdown_received?: true}} + {:ok, nil, %__MODULE__{state | shutdown_received?: true}} end - def apply(%__MODULE__{} = state, %Notifications.WorkspaceDidChangeWatchedFiles{params: event}) do + def apply(%__MODULE__{} = state, %GenLSP.Notifications.WorkspaceDidChangeWatchedFiles{ + params: params + }) do project = state.configuration.project - Enum.each(event.changes, fn %Structures.FileEvent{} = change -> + Enum.each(params.changes, fn %GenLSP.Structures.FileEvent{} = change -> event = filesystem_event(project: Project, uri: change.uri, event_type: change.type) Engine.Api.broadcast(project, event) end) @@ -238,54 +203,28 @@ defmodule Expert.State do {:ok, state} end - defp registrations do - %Requests.ClientRegisterCapability{ - id: Id.next(), - params: %Structures.RegistrationParams{ - registrations: [file_watcher_registration()] - } - } - end - - @did_changed_watched_files_id "-42" - @watched_extensions ~w(ex exs) - defp file_watcher_registration do - extension_glob = "{" <> Enum.join(@watched_extensions, ",") <> "}" - - watchers = [ - %Structures.FileSystemWatcher{glob_pattern: "**/mix.lock"}, - %Structures.FileSystemWatcher{glob_pattern: "**/*.#{extension_glob}"} - ] - - %Structures.Registration{ - id: @did_changed_watched_files_id, - method: "workspace/didChangeWatchedFiles", - register_options: %Structures.DidChangeWatchedFilesRegistrationOptions{watchers: watchers} - } - end - - def initialize_result(event_id) do + def initialize_result do sync_options = - %Structures.TextDocumentSyncOptions{ + %GenLSP.Structures.TextDocumentSyncOptions{ open_close: true, - change: Enumerations.TextDocumentSyncKind.incremental(), + change: GenLSP.Enumerations.TextDocumentSyncKind.incremental(), save: true } code_action_options = - %Structures.CodeActionOptions{ + %GenLSP.Structures.CodeActionOptions{ code_action_kinds: @supported_code_actions, resolve_provider: false } - code_lens_options = %Structures.CodeLensOptions{resolve_provider: false} + code_lens_options = + %GenLSP.Structures.CodeLensOptions{resolve_provider: false} - command_options = %Structures.ExecuteCommandOptions{ - commands: Handlers.Commands.names() - } + command_options = + %GenLSP.Structures.ExecuteCommandOptions{commands: Handlers.Commands.names()} completion_options = - %Structures.CompletionOptions{ + %GenLSP.Structures.CompletionOptions{ trigger_characters: CodeIntelligence.Completion.trigger_characters() } @@ -304,15 +243,12 @@ defmodule Expert.State do workspace_symbol_provider: true } - result = - %Structures.InitializeResult{ - capabilities: server_capabilities, - server_info: %{ - name: "Expert", - version: "0.0.1" - } + %GenLSP.Structures.InitializeResult{ + capabilities: server_capabilities, + server_info: %{ + name: "Expert", + version: "0.0.1" } - - %Response{id: event_id, result: result} + } end end diff --git a/apps/expert/lib/expert/task_queue.ex b/apps/expert/lib/expert/task_queue.ex deleted file mode 100644 index 643d376c..00000000 --- a/apps/expert/lib/expert/task_queue.ex +++ /dev/null @@ -1,226 +0,0 @@ -defmodule Expert.TaskQueue do - defmodule State do - alias Expert.Transport - alias Forge.Protocol.Convert - alias GenLSP.Enumerations.ErrorCodes - import Forge.Logging - require Logger - - defstruct ids_to_tasks: %{}, pids_to_ids: %{} - - @type t :: %__MODULE__{} - - def new do - %__MODULE__{} - end - - def task_supervisor_name do - __MODULE__.TaskSupervisor - end - - @spec add(t, request_id :: term(), mfa :: {module(), atom(), [term()]}) :: t - def add(%__MODULE__{} = state, request_id, {_, _, _} = mfa) do - task = %Task{} = as_task(request_id, mfa) - - %__MODULE__{ - state - | ids_to_tasks: Map.put(state.ids_to_tasks, request_id, task), - pids_to_ids: Map.put(state.pids_to_ids, task.pid, request_id) - } - end - - @spec cancel(t, request_id :: term()) :: t - def cancel(%__MODULE__{} = state, request_id) do - with {:ok, %Task{} = task} <- Map.fetch(state.ids_to_tasks, request_id), - :ok <- cancel_task(task) do - write_error(request_id, "Request cancelled", :request_cancelled) - - %__MODULE__{ - state - | ids_to_tasks: Map.delete(state.ids_to_tasks, request_id), - pids_to_ids: Map.delete(state.pids_to_ids, task.pid) - } - else - _ -> - state - end - end - - def size(%__MODULE__{} = state) do - map_size(state.ids_to_tasks) - end - - def task_finished(%__MODULE__{} = state, pid, reason) do - case Map.pop(state.pids_to_ids, pid) do - {nil, _} -> - state - - {request_id, new_pids_to_ids} -> - log_task_run_time(state.ids_to_tasks[request_id], :success) - maybe_log_task(reason, request_id) - - %__MODULE__{ - state - | pids_to_ids: new_pids_to_ids, - ids_to_tasks: Map.delete(state.ids_to_tasks, request_id) - } - end - end - - defp maybe_log_task(:normal, _), - do: :ok - - defp maybe_log_task(reason, request_id), - do: Logger.warning("Request id #{request_id} failed with reason #{inspect(reason)}") - - defp as_task(request_id, {m, f, a}) do - handler = fn -> - try do - case apply(m, f, a) do - :noreply -> - {:request_complete, request_id} - - {:reply, reply} -> - write_reply(reply) - - {:request_complete, request_id} - end - rescue - e -> - exception_string = Exception.format(:error, e, __STACKTRACE__) - Logger.error(exception_string) - write_error(request_id, exception_string) - - {:request_complete, request_id} - end - end - - run_task(handler, {m, f, a}) - end - - defp write_reply(response) do - case timed_log("convert", fn -> Convert.to_lsp(response) end) do - {:ok, lsp_response} -> - Transport.write(lsp_response) - - error -> - error_message = """ - Failed to convert #{response.__struct__}: - - #{inspect(error, pretty: true)}\ - """ - - Logger.critical(""" - #{error_message} - - #{inspect(response, pretty: true)}\ - """) - - write_error(response.id, error_message) - end - end - - defp write_error(id, message, code \\ ErrorCodes.internal_error()) do - error = %GenLSP.ErrorResponse{code: map_code(code), message: message} - - Transport.write(%{id: id, error: error}) - end - - @dialyzer {:nowarn_function, map_code: 1} - - defp map_code(:parse_error), do: ErrorCodes.parse_error() - defp map_code(:invalid_request), do: ErrorCodes.invalid_request() - defp map_code(:method_not_found), do: ErrorCodes.method_not_found() - defp map_code(:invalid_params), do: ErrorCodes.invalid_params() - defp map_code(:internal_error), do: ErrorCodes.internal_error() - defp map_code(:server_not_initialized), do: ErrorCodes.server_not_initialized() - defp map_code(:unknown_error_code), do: ErrorCodes.unknown_error_code() - defp map_code(:request_cancelled), do: -32_800 - defp map_code(code) when is_integer(code), do: code - - defp run_task(fun, mfa) when is_function(fun) do - task_supervisor_name() - |> Task.Supervisor.async_nolink(fun) - |> Map.merge(%{started_at: System.system_time(:microsecond), mfa: mfa}) - end - - defp cancel_task(%Task{} = task) do - log_task_run_time(task, :canceled) - Process.exit(task.pid, :canceled) - :ok - end - - defp log_task_run_time(%{started_at: ts, mfa: {m, f, a}}, result) do - elapsed = System.system_time(:microsecond) - ts - - Logger.warning( - "Task #{m}.#{f}/#{length(a)} ran for #{Forge.Formats.time(elapsed)}. Result #{inspect(result)}" - ) - end - - defp log_task_run_time(_, _), do: :ok - end - - use GenServer - - def task_supervisor_name do - State.task_supervisor_name() - end - - @spec add(request_id :: term(), mfa :: {module(), atom(), [term()]}) :: :ok - def add(request_id, {_, _, _} = mfa) do - GenServer.call(__MODULE__, {:add, request_id, mfa}) - end - - @spec size() :: non_neg_integer() - def size do - GenServer.call(__MODULE__, :size) - end - - def cancel(%{params: %{id: id}}) do - cancel(id) - end - - def cancel(%{id: request_id}) do - cancel(request_id) - end - - def cancel(request_id) do - GenServer.call(__MODULE__, {:cancel, request_id}) - end - - # genserver callbacks - - def start_link(_) do - GenServer.start_link(__MODULE__, [], name: __MODULE__) - end - - def init(_) do - {:ok, State.new()} - end - - def handle_call({:add, request_id, mfa}, _from, %State{} = state) do - new_state = State.add(state, request_id, mfa) - {:reply, :ok, new_state} - end - - def handle_call({:cancel, request_id}, _from, %State{} = state) do - new_state = State.cancel(state, request_id) - {:reply, :ok, new_state} - end - - def handle_call(:size, _from, %State{} = state) do - {:reply, State.size(state), state} - end - - def handle_info({:DOWN, _ref, :process, pid, reason}, state) do - new_state = State.task_finished(state, pid, reason) - {:noreply, new_state} - end - - def handle_info({ref, {:request_complete, _request_id}}, %State{} = state) - when is_reference(ref) do - # This head handles the replies from the tasks, which we don't really care about. - {:noreply, state} - end -end diff --git a/apps/expert/lib/expert/transport.ex b/apps/expert/lib/expert/transport.ex deleted file mode 100644 index 4110bd1a..00000000 --- a/apps/expert/lib/expert/transport.ex +++ /dev/null @@ -1,12 +0,0 @@ -defmodule Expert.Transport do - @moduledoc """ - A behaviour for a LSP transport - """ - @callback write(Jason.Encoder.t()) :: Jason.Encoder.t() - - alias Expert.Transport.StdIO - - @implementation Application.compile_env(:expert, :transport, StdIO) - - defdelegate write(message), to: @implementation -end diff --git a/apps/expert/lib/expert/transport/std_io.ex b/apps/expert/lib/expert/transport/std_io.ex deleted file mode 100644 index d2de6480..00000000 --- a/apps/expert/lib/expert/transport/std_io.ex +++ /dev/null @@ -1,197 +0,0 @@ -defmodule Expert.Transport.StdIO do - alias Forge.Protocol.Convert - alias Forge.Protocol.JsonRpc - - require Logger - - @behaviour Expert.Transport - - def start_link(device, callback) do - pid = :proc_lib.spawn_link(__MODULE__, :init, [{callback, device}]) - {:ok, pid} - end - - def child_spec([device, callback]) do - %{id: __MODULE__, start: {__MODULE__, :start_link, [device, callback]}} - end - - def init({callback, device}) do - :io.setopts(binary: true, encoding: :latin1) - loop([], device, callback) - end - - def write(io_device \\ :stdio, payload) - - def write(io_device, %_{} = payload) do - with {:ok, lsp} <- encode(payload), - {:ok, json} <- Jason.encode(lsp) do - write(io_device, json) - end - end - - def write(io_device, %{} = payload) do - with {:ok, encoded} <- Jason.encode(payload) do - write(io_device, encoded) - end - end - - def write(io_device, payload) when is_binary(payload) do - message = - case io_device do - device when device in [:stdio, :standard_io] or is_pid(device) -> - {:ok, json_rpc} = JsonRpc.encode(payload) - json_rpc - - _ -> - payload - end - - IO.binwrite(io_device, message) - end - - def write(_, nil) do - end - - def write(_, []) do - end - - defp encode(%{id: id, result: result}) do - with {:ok, result} <- dump_lsp(result) do - {:ok, - %{ - "jsonrpc" => "2.0", - "id" => id, - "result" => result - }} - end - end - - defp encode(%{id: id, error: error}) do - with {:ok, error} <- dump_lsp(error) do - {:ok, - %{ - "jsonrpc" => "2.0", - "id" => id, - "error" => error - }} - end - end - - defp encode(%_{} = request) do - dump_lsp(request) - end - - # Dialyzer complains about Schematic.dump not existing, but it does - # This will be removed by #20 anyways - @dialyzer {:nowarn_function, dump_lsp: 1} - - defp dump_lsp(%module{} = item) do - with {:ok, item} <- Convert.to_lsp(item) do - Schematic.dump(module.schematic(), item) - end - end - - defp dump_lsp(list) when is_list(list) do - dump = - Enum.map(list, fn item -> - case dump_lsp(item) do - {:ok, dumped} -> dumped - {:error, reason} -> throw({:error, reason}) - end - end) - - {:ok, dump} - catch - {:error, reason} -> - {:error, reason} - end - - defp dump_lsp(other), do: {:ok, other} - - defp loop(buffer, device, callback) do - case IO.binread(device, :line) do - "\n" -> - headers = parse_headers(buffer) - - with {:ok, content_length} <- content_length(headers), - {:ok, data} <- read_body(device, content_length), - {:ok, message} <- JsonRpc.decode(data) do - callback.(message) - else - {:error, :empty_response} -> - :noop - - {:error, reason} -> - Logger.critical("read protocol message: #{inspect(reason)}") - end - - loop([], device, callback) - - :eof -> - Logger.critical("stdio received :eof, server will stop.") - maybe_stop() - - line -> - loop([line | buffer], device, callback) - end - end - - defp content_length(headers) do - with {:ok, len_str} <- find_header(headers, "content-length") do - parse_length(len_str) - end - end - - defp find_header(headers, name) do - case List.keyfind(headers, name, 0) do - {_, len_str} -> {:ok, len_str} - nil -> {:error, {:header_not_found, name}} - end - end - - defp parse_length(len_str) when is_binary(len_str) do - case Integer.parse(len_str) do - {int, ""} -> {:ok, int} - :error -> {:error, {:cant_parse_length, len_str}} - end - end - - defp read_body(device, byte_count) do - case IO.binread(device, byte_count) do - data when is_binary(data) -> - {:ok, data} - - :eof -> - Logger.critical("stdio received :eof, server will stop.") - maybe_stop() - - {:error, reason} -> - {:error, reason} - end - end - - defp parse_headers(headers) do - Enum.map(headers, &parse_header/1) - end - - defp parse_header(line) do - [name, value] = String.split(line, ":") - - header_name = - name - |> String.downcase() - |> String.trim() - - {header_name, String.trim(value)} - end - - if Mix.env() == :test do - defp maybe_stop do - :ok - end - else - defp maybe_stop do - System.stop() - end - end -end diff --git a/apps/expert/lib/expert/window.ex b/apps/expert/lib/expert/window.ex deleted file mode 100644 index f78af130..00000000 --- a/apps/expert/lib/expert/window.ex +++ /dev/null @@ -1,119 +0,0 @@ -defmodule Expert.Window do - alias Expert.Transport - alias Forge.Protocol.Id - alias GenLSP.Enumerations - alias GenLSP.Notifications - alias GenLSP.Requests - alias GenLSP.Structures - - @type level :: :error | :warning | :info | :log - @type message_result :: {:errory, term()} | {:ok, nil} | {:ok, Structures.MessageActionItem.t()} - @type on_response_callback :: (message_result() -> any()) - @type message :: String.t() - @type action :: String.t() - - @levels [:error, :warning, :info, :log] - - @spec log(level, message()) :: :ok - def log(level, message) when level in @levels and is_binary(message) do - log_message = log_message(level, message) - Transport.write(log_message) - :ok - end - - for level <- [:error, :warning, :info] do - def unquote(level)(message) do - log(unquote(level), message) - end - end - - # There is a warning introduced somehow in #19 but this file will get removed - # in #20 so we can ignore it for now. - @dialyzer {:nowarn_function, show: 2} - - @spec show(level(), message()) :: :ok - def show(level, message) when level in @levels and is_binary(message) do - show_message = show_message(level, message) - Transport.write(show_message) - :ok - end - - for type <- @levels do - def log_message(unquote(type), message) when is_binary(message) do - %Notifications.WindowLogMessage{ - params: %Structures.ShowMessageParams{ - message: message, - type: Enumerations.MessageType.unquote(type) - } - } - end - - def show_message(unquote(type), message) when is_binary(message) do - %Notifications.WindowShowMessage{ - params: %Structures.ShowMessageParams{ - message: message, - type: Enumerations.MessageType.unquote(type) - } - } - end - end - - @spec show_message(level(), message()) :: :ok - def show_message(level, message) do - request = %Requests.WindowShowMessageRequest{ - id: Id.next(), - params: %Structures.ShowMessageRequestParams{message: message, type: level} - } - - Expert.server_request(request) - end - - for level <- @levels, - fn_name = :"show_#{level}_message" do - def unquote(fn_name)(message) do - show_message(unquote(level), message) - end - end - - for level <- @levels, - fn_name = :"show_#{level}_message" do - @doc """ - Shows a message at the #{level} level. Delegates to `show_message/4` - """ - def unquote(fn_name)(message, actions, on_response) when is_function(on_response, 1) do - show_message(unquote(level), message, actions, on_response) - end - end - - @doc """ - Shows a message request and handles the response - - Displays a message to the user in the UI and waits for a response. - The result type handed to the callback function is a - `GenLSP.Structures.MessageActionItem` or nil if there was no response - from the user. - - The strings passed in as the `actions` command are displayed to the user, and when - they select one, the `Types.Message.ActionItem` is passed to the callback function. - """ - @spec show_message(level(), message(), [action()], on_response_callback) :: :ok - def show_message(level, message, actions, on_response) - when is_function(on_response, 1) do - action_items = - Enum.map(actions, fn action_string -> - %Structures.MessageActionItem{title: action_string} - end) - - request = - %Requests.WindowShowMessageRequest{ - id: Id.next(), - params: %Structures.ShowMessageRequestParams{ - message: message, - actions: action_items, - type: level - } - } - - Expert.server_request(request, fn _request, response -> on_response.(response) end) - end -end diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index 73fe08d2..2a56dbbc 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -44,14 +44,13 @@ defmodule Expert.MixProject do Mix.Dialyzer.dependency(), {:elixir_sense, github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, - {:engine, path: "../engine", env: Mix.env()}, - {:forge, path: "../forge", env: Mix.env()}, - {:gen_lsp, "~> 0.10"}, {:jason, "~> 1.4"}, {:logger_file_backend, "~> 0.0", only: [:dev, :prod]}, {:patch, "~> 0.15", runtime: false, only: [:dev, :test]}, {:path_glob, "~> 0.2"}, {:schematic, "~> 0.2"}, + {:engine, path: "../engine", env: Mix.env()}, + {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:sourceror, "~> 1.9"} ] end diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index 11140d4e..eb5e1052 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -1,13 +1,13 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, + "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:hex, :gen_lsp, "0.10.0", "f6da076b5ccedf937d17aa9743635a2c3d0f31265c853e58b02ab84d71852270", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "768f8f7b5c5e218fb36dcebd30dcd6275b61ca77052c98c3c4c0375158392c4a"}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, diff --git a/apps/expert/test/expert/project/diagnostics_test.exs b/apps/expert/test/expert/project/diagnostics_test.exs index 2f3106c4..0dbbae6e 100644 --- a/apps/expert/test/expert/project/diagnostics_test.exs +++ b/apps/expert/test/expert/project/diagnostics_test.exs @@ -1,6 +1,5 @@ defmodule Expert.Project.DiagnosticsTest do alias Expert.Test.DispatchFake - alias Expert.Transport alias Forge.Document alias Forge.Plugin.V1.Diagnostic alias GenLSP.Notifications.TextDocumentPublishDiagnostics @@ -39,7 +38,15 @@ defmodule Expert.Project.DiagnosticsTest do def with_patched_tranport(_) do test = self() - patch(Transport, :write, fn message -> + patch(GenLSP, :notify_server, fn _, message -> + send(test, {:transport, message}) + end) + + patch(GenLSP, :notify, fn _, message -> + send(test, {:transport, message}) + end) + + patch(GenLSP, :request, fn _, message -> send(test, {:transport, message}) end) @@ -65,9 +72,24 @@ defmodule Expert.Project.DiagnosticsTest do file_diagnostics(diagnostics: [diagnostic(document.uri)], uri: document.uri) Engine.Api.broadcast(project, file_diagnostics_message) - assert_receive {:transport, %TextDocumentPublishDiagnostics{}} - Document.Store.get_and_update(document.uri, &Document.mark_clean/1) + assert_receive {:transport, + %TextDocumentPublishDiagnostics{ + params: %PublishDiagnosticsParams{ + diagnostics: [ + %Forge.Plugin.V1.Diagnostic.Result{ + details: nil, + message: "stuff broke", + position: 1, + severity: :error, + source: nil, + uri: "file:///" <> _ + } + ] + } + }} + + Document.Store.get_and_update(document.uri, &{:ok, Document.mark_clean(&1)}) Engine.Api.broadcast(project, project_compile_requested()) Engine.Api.broadcast(project, project_diagnostics(diagnostics: [])) diff --git a/apps/expert/test/expert/project/progress_test.exs b/apps/expert/test/expert/project/progress_test.exs index 0f4e91a8..3c370044 100644 --- a/apps/expert/test/expert/project/progress_test.exs +++ b/apps/expert/test/expert/project/progress_test.exs @@ -2,7 +2,6 @@ defmodule Expert.Project.ProgressTest do alias Expert.Configuration alias Expert.Project alias Expert.Test.DispatchFake - alias Expert.Transport alias GenLSP.Notifications alias GenLSP.Requests alias GenLSP.Structures @@ -47,7 +46,11 @@ defmodule Expert.Project.ProgressTest do def with_patched_transport(_) do test = self() - patch(Transport, :write, fn message -> + patch(GenLSP, :notify, fn _, message -> + send(test, {:transport, message}) + end) + + patch(GenLSP, :request, fn _, message -> send(test, {:transport, message}) end) diff --git a/apps/expert/test/expert/provider/handlers/find_references_test.exs b/apps/expert/test/expert/provider/handlers/find_references_test.exs index aace5b6a..cd31e3ad 100644 --- a/apps/expert/test/expert/provider/handlers/find_references_test.exs +++ b/apps/expert/test/expert/provider/handlers/find_references_test.exs @@ -4,7 +4,6 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do alias Forge.Document alias Forge.Document.Location alias Forge.Protocol.Convert - alias Forge.Protocol.Response alias GenLSP.Requests.TextDocumentReferences alias GenLSP.Structures @@ -67,8 +66,7 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do {:ok, request} = build_request(uri, 5, 6) - assert {:reply, %Response{} = response} = handle(request, project) - assert [%Location{} = location] = response.result + assert {:ok, [%Location{} = location]} = handle(request, project) assert location.uri =~ "file.ex" end @@ -77,8 +75,7 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do {:ok, request} = build_request(uri, 1, 5) - assert {:reply, %Response{} = response} = handle(request, project) - assert response.result == nil + assert {:ok, nil} == handle(request, project) end end end diff --git a/apps/expert/test/expert/task_queue_test.exs b/apps/expert/test/expert/task_queue_test.exs deleted file mode 100644 index 89e0e566..00000000 --- a/apps/expert/test/expert/task_queue_test.exs +++ /dev/null @@ -1,168 +0,0 @@ -defmodule Expert.TaskQueueTest do - alias Engine.Test.Fixtures - alias Expert.Configuration - alias Expert.Provider.Handlers - alias Expert.TaskQueue - alias Expert.Transport - alias GenLSP.Enumerations.ErrorCodes - alias GenLSP.Notifications - alias GenLSP.Requests - alias GenLSP.Structures - - use ExUnit.Case - use Patch - use Forge.Test.EventualAssertions - - setup_all do - {:ok, config: Configuration.new(project: Fixtures.project())} - end - - setup do - {:ok, _} = start_supervised({Task.Supervisor, name: TaskQueue.task_supervisor_name()}) - {:ok, _} = start_supervised(TaskQueue) - unit_test = self() - patch(Transport, :write, &send(unit_test, &1)) - - :ok - end - - def request(config, func) do - id = System.unique_integer([:positive]) - - patch(Expert, :handler_for, fn _ -> {:ok, Handlers.Completion} end) - - patch(Handlers.Completion, :handle, fn request, %Configuration{} = ^config -> - func.(request, config) - end) - - patch(Requests.TextDocumentCompletion, :to_elixir, fn req -> {:ok, req} end) - - request = %Requests.TextDocumentCompletion{ - id: id, - params: %Structures.CompletionParams{text_document: nil, position: nil, context: nil} - } - - {id, {Handlers.Completion, :handle, [request, config]}} - end - - describe "size/0" do - test "an empty queue has size 0" do - assert 0 == TaskQueue.size() - end - - test "adding a request makes the queue grow", %{config: config} do - {id, mfa} = request(config, fn _, _ -> Process.sleep(500) end) - assert :ok = TaskQueue.add(id, mfa) - assert 1 == TaskQueue.size() - end - end - - describe "cancel/1" do - test "canceling a request stops it", %{config: config} do - {id, mfa} = request(config, fn _, _ -> Process.sleep(500) end) - - assert :ok = TaskQueue.add(id, mfa) - assert :ok = TaskQueue.cancel(id) - - assert_receive %{id: ^id, error: error} - - assert TaskQueue.size() == 0 - # ErrorCodes.request_cancelled() - assert error.code == -32_800 - assert error.message == "Request cancelled" - end - - test "passing in a request for cancellation", %{config: config} do - {id, mfa} = request(config, fn _, _ -> Process.sleep(500) end) - - assert :ok = TaskQueue.add(id, mfa) - assert :ok = TaskQueue.cancel(id) - - assert_receive %{id: ^id, error: error} - assert TaskQueue.size() == 0 - # ErrorCodes.request_cancelled() - assert error.code == -32_800 - assert error.message == "Request cancelled" - end - - test "canceling a non-existing request is a no-op" do - assert :ok = TaskQueue.cancel("5") - refute_receive %{id: _} - end - - test "Adding a cancel notification cancels the request", %{config: config} do - {id, mfa} = request(config, fn _, _ -> Process.sleep(500) end) - assert :ok = TaskQueue.add(id, mfa) - - notif = - %Notifications.DollarCancelRequest{ - params: %{ - id: id - } - } - - assert :ok = TaskQueue.cancel(notif) - assert_receive %{id: ^id, error: error} - assert TaskQueue.size() == 0 - # ErrorCodes.request_cancelled() - assert error.code == -32_800 - assert error.message == "Request cancelled" - end - - test "Adding a cancel request cancels the request", %{config: config} do - {id, mfa} = request(config, fn _, _ -> Process.sleep(500) end) - assert :ok = TaskQueue.add(id, mfa) - - req = - %Notifications.DollarCancelRequest{ - params: %{ - id: id - } - } - - assert :ok = TaskQueue.cancel(req) - assert_receive %{id: ^id, error: error} - assert TaskQueue.size() == 0 - # ErrorCodes.request_cancelled() - assert error.code == -32_800 - assert error.message == "Request cancelled" - end - - test "canceling a request that has finished is a no-op", %{config: config} do - me = self() - {id, mfa} = request(config, fn _, _ -> send(me, :finished) end) - - assert :ok = TaskQueue.add(id, mfa) - assert_receive :finished - - assert :ok = TaskQueue.cancel(id) - assert TaskQueue.size() == 0 - end - end - - describe "task return values" do - test "tasks can reply", %{config: config} do - {id, mfa} = request(config, fn _, _ -> {:reply, "great"} end) - assert :ok = TaskQueue.add(id, mfa) - - assert_receive "great" - end - - test "replies are optional", %{config: config} do - {id, mfa} = request(config, fn _, _ -> :noreply end) - assert :ok = TaskQueue.add(id, mfa) - - assert_eventually TaskQueue.size() == 0 - refute_receive _ - end - - test "exceptions are handled", %{config: config} do - {id, mfa} = request(config, fn _, _ -> raise "Boom!" end) - assert :ok = TaskQueue.add(id, mfa) - - assert_receive %{id: ^id, error: error} - assert error.code == ErrorCodes.internal_error() - assert error.message =~ "Boom!" - end - end -end diff --git a/apps/expert/test/expert/transport/std_io_test.exs b/apps/expert/test/expert/transport/std_io_test.exs deleted file mode 100644 index 975f7cb6..00000000 --- a/apps/expert/test/expert/transport/std_io_test.exs +++ /dev/null @@ -1,51 +0,0 @@ -defmodule Expert.Transport.StdIoTest do - alias Expert.Transport.StdIO - alias Forge.Protocol.JsonRpc - - use ExUnit.Case - - defp request(requests) do - {:ok, requests} = - requests - |> List.wrap() - |> Enum.map_join(fn req -> - {:ok, req} = - req - |> Map.put("jsonrpc", "2.0") - |> Jason.encode!() - |> JsonRpc.encode() - - req - end) - |> StringIO.open(encoding: :latin1) - - test = self() - StdIO.start_link(requests, &send(test, {:request, &1})) - end - - defp receive_request do - assert_receive {:request, request} - request - end - - test "works with unicode characters" do - # This tests a bug that occurred when we were using `IO.read`. - # Due to `IO.read` reading characters, a prior request with unicode - # in the body, can make the body length in characters longer than the content-length. - # This would cause the prior request to consume some of the next request if they happen - # quickly enough. If the prior request consumes the subsequent request's headers, then - # the read for the next request will read the JSON body as headers, and will fail the - # pattern match in the call to `parse_header`. This would cause the dreaded - # "no match on right hand side value [...JSON content]". - # The fix is to switch to binread, which takes bytes as an argument. - # This series of requests is specially crafted to cause the original failure. Removing - # a single « from the string will break the setup. - request([ - %{method: "workspace/symbol", id: 1, params: %{query: "««««««««««««««««««««««"}}, - %{method: "$/cancelRequest", id: 2}, - %{method: "$/cancelRequest", id: 3} - ]) - - _ = receive_request() - end -end diff --git a/apps/expert/test/support/transport/no_op.ex b/apps/expert/test/support/transport/no_op.ex deleted file mode 100644 index e9515833..00000000 --- a/apps/expert/test/support/transport/no_op.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule Expert.Test.Transport.NoOp do - @behaviour Expert.Transport - - def write(_message), do: :ok -end diff --git a/apps/expert_credo/mix.exs b/apps/expert_credo/mix.exs index 8df35506..43f6e5c1 100644 --- a/apps/expert_credo/mix.exs +++ b/apps/expert_credo/mix.exs @@ -1,6 +1,7 @@ defmodule ExpertCredo.MixProject do use Mix.Project Code.require_file("../../mix_dialyzer.exs") + Code.require_file("../../mix_includes.exs") @repo_url "https://github.com/elixir-lang/expert/" @version "0.5.0" @@ -28,7 +29,8 @@ defmodule ExpertCredo.MixProject do defp deps do [ {:forge, path: "../forge", env: Mix.env()}, - {:credo, "> 0.0.0", only: [:dev, :test]}, + # {:credo, "> 0.0.0", only: [:dev, :test]}, + Mix.Credo.dependency(), Mix.Dialyzer.dependency(), {:jason, "> 0.0.0", optional: true}, {:ex_doc, "~> 0.34", optional: true, only: [:dev, :hex]} diff --git a/apps/expert_credo/mix.lock b/apps/expert_credo/mix.lock index 817951f9..d7f03fa0 100644 --- a/apps/expert_credo/mix.lock +++ b/apps/expert_credo/mix.lock @@ -1,14 +1,14 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, + "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.37.2", "2a3aa7014094f0e4e286a82aa5194a34dd17057160988b8509b15aa6c292720c", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "4dfa56075ce4887e4e8b1dcc121cd5fcb0f02b00391fd367ff5336d98fa49049"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:hex, :gen_lsp, "0.10.0", "f6da076b5ccedf937d17aa9743635a2c3d0f31265c853e58b02ab84d71852270", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "768f8f7b5c5e218fb36dcebd30dcd6275b61ca77052c98c3c4c0375158392c4a"}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, diff --git a/apps/forge/lib/forge/project.ex b/apps/forge/lib/forge/project.ex index ad612ab5..abb2566d 100644 --- a/apps/forge/lib/forge/project.ex +++ b/apps/forge/lib/forge/project.ex @@ -14,14 +14,16 @@ defmodule Forge.Project do mix_target: nil, env_variables: %{}, project_module: nil, - entropy: 1 + entropy: 1, + lsp: nil @type message :: String.t() @type restart_notification :: {:restart, Logger.level(), String.t()} @type t :: %__MODULE__{ root_uri: Forge.uri() | nil, mix_exs_uri: Forge.uri() | nil, - entropy: non_neg_integer() + entropy: non_neg_integer(), + lsp: GenLSP.LSP.t() # mix_env: atom(), # mix_target: atom(), # env_variables: %{String.t() => String.t()} @@ -30,12 +32,11 @@ defmodule Forge.Project do @workspace_directory_name ".expert" - # Public - @spec new(Forge.uri()) :: t - def new(root_uri) do + @spec new(GenLSP.LSP.t(), Forge.uri()) :: t + def new(lsp, root_uri) do entropy = :rand.uniform(65_536) - %__MODULE__{entropy: entropy} + %__MODULE__{entropy: entropy, lsp: lsp} |> maybe_set_root_uri(root_uri) |> maybe_set_mix_exs_uri() end diff --git a/apps/forge/lib/forge/protocol/error_response.ex b/apps/forge/lib/forge/protocol/error_response.ex deleted file mode 100644 index 0c4d6fb5..00000000 --- a/apps/forge/lib/forge/protocol/error_response.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule Forge.Protocol.ErrorResponse do - import Schematic - - alias GenLSP.ErrorResponse - - @type t :: %__MODULE__{ - id: integer(), - error: ErrorResponse.t(), - jsonrpc: String.t() - } - - defstruct [:id, :error, jsonrpc: "2.0"] - - def schematic do - schema(__MODULE__, %{ - id: int(), - error: ErrorResponse.schematic(), - jsonrpc: "2.0" - }) - end -end diff --git a/apps/forge/lib/forge/protocol/json_rpc.ex b/apps/forge/lib/forge/protocol/json_rpc.ex deleted file mode 100644 index b8f499c0..00000000 --- a/apps/forge/lib/forge/protocol/json_rpc.ex +++ /dev/null @@ -1,52 +0,0 @@ -defmodule Forge.Protocol.JsonRpc do - require Logger - - @crlf "\r\n" - - def decode(message_string) do - with {:ok, json_map} <- Jason.decode(message_string) do - do_decode(json_map) - end - end - - def encode(%_proto_module{} = proto_struct) do - with {:ok, encoded} <- Jason.encode(proto_struct) do - encode(encoded) - end - end - - def encode(payload) when is_binary(payload) or is_list(payload) do - content_length = IO.iodata_length(payload) - - json_rpc = [ - "Content-Length: ", - to_string(content_length), - @crlf, - @crlf, - payload - ] - - {:ok, json_rpc} - end - - # These messages appear to be empty Responses (per LSP spec) sent to - # aknowledge Requests sent from the language server to the client. - defp do_decode(%{"id" => _id, "result" => nil}) do - {:error, :empty_response} - end - - defp do_decode(%{"id" => _id, "result" => _result} = response) do - # this is due to a client -> server message, but we can't decode it properly yet. - # since we can't match up the response type to the message. - - {:ok, response} - end - - defp do_decode(%{"method" => _, "id" => _id} = request) do - GenLSP.Requests.new(request) - end - - defp do_decode(%{"method" => _} = notification) do - GenLSP.Notifications.new(notification) - end -end diff --git a/apps/forge/lib/forge/protocol/response.ex b/apps/forge/lib/forge/protocol/response.ex deleted file mode 100644 index 9cc9eda3..00000000 --- a/apps/forge/lib/forge/protocol/response.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Forge.Protocol.Response do - import Schematic - - defstruct [:id, :result, jsonrpc: "2.0"] - - @type t() :: %__MODULE__{ - id: integer(), - result: any(), - jsonrpc: String.t() - } - - def schematic do - schema(__MODULE__, %{ - id: int(), - result: any(), - jsonrpc: "2.0" - }) - end -end diff --git a/apps/forge/mix.exs b/apps/forge/mix.exs index f90c65aa..5107a5e7 100644 --- a/apps/forge/mix.exs +++ b/apps/forge/mix.exs @@ -34,9 +34,7 @@ defmodule Forge.MixProject do {:benchee, "~> 1.3", only: :test}, Mix.Credo.dependency(), Mix.Dialyzer.dependency(), - {:gen_lsp, "~> 0.10"}, - {:jason, "~> 1.4"}, - {:schematic, "~> 0.2"}, + {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:snowflake, "~> 1.0"}, {:sourceror, "~> 1.9"}, {:stream_data, "~> 1.1", only: [:test], runtime: false}, diff --git a/apps/forge/mix.lock b/apps/forge/mix.lock index b0c0efb1..4f79c948 100644 --- a/apps/forge/mix.lock +++ b/apps/forge/mix.lock @@ -1,12 +1,12 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, + "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:hex, :gen_lsp, "0.10.0", "f6da076b5ccedf937d17aa9743635a2c3d0f31265c853e58b02ab84d71852270", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "768f8f7b5c5e218fb36dcebd30dcd6275b61ca77052c98c3c4c0375158392c4a"}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, diff --git a/apps/forge/test/forge/ast/env_test.exs b/apps/forge/test/forge/ast/env_test.exs index 071cab7b..0352139d 100644 --- a/apps/forge/test/forge/ast/env_test.exs +++ b/apps/forge/test/forge/ast/env_test.exs @@ -9,7 +9,7 @@ defmodule Forge.Ast.EnvTest do def new_env(text, opts \\ []) do opts = Keyword.merge([as: :document], opts) - project = Project.new("file://#{File.cwd!()}") + project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{File.cwd!()}") {position, document} = pop_cursor(text, opts) analysis = Ast.analyze(document) {:ok, env} = new(project, analysis, position) diff --git a/apps/forge/test/forge/project_test.exs b/apps/forge/test/forge/project_test.exs index 74f3df8e..b6a2ce07 100644 --- a/apps/forge/test/forge/project_test.exs +++ b/apps/forge/test/forge/project_test.exs @@ -7,7 +7,7 @@ defmodule Forge.ProjectTest do def project do root = Forge.Document.Path.to_uri(__DIR__) - Project.new(root) + Project.new(%GenLSP.LSP{mod: :foo}, root) end describe "name/1" do diff --git a/flake.lock b/flake.lock index aa24ddf9..81d1605c 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719931832, - "narHash": "sha256-0LD+KePCKKEb4CcPsTBOwf019wDtZJanjoKm1S8q3Do=", + "lastModified": 1749903597, + "narHash": "sha256-jp0D4vzBcRKwNZwfY4BcWHemLGUs4JrS3X9w5k/JYDA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0aeab749216e4c073cece5d34bc01b79e717c3e0", + "rev": "41da1e3ea8e23e094e5e3eeb1e6b830468a7399e", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index ad0d7d8a..ef1e4d81 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,6 @@ outputs = { self, - nixpkgs, systems, ... } @ inputs: @@ -20,12 +19,8 @@ systems = import systems; - perSystem = { - self', - pkgs, - ... - }: let - erlang = pkgs.beam.packages.erlang; + perSystem = {pkgs, ...}: let + erlang = pkgs.beam.packages.erlang_25; expert = self.lib.mkExpert {inherit erlang;}; in { formatter = pkgs.alejandra; @@ -34,7 +29,7 @@ script = pkgs.writeShellApplication { name = "update-hash"; - runtimeInputs = [ pkgs.nixFlakes pkgs.gawk ]; + runtimeInputs = [pkgs.nixFlakes pkgs.gawk]; text = '' nix --extra-experimental-features 'nix-command flakes' \ @@ -56,9 +51,12 @@ }; devShells.default = pkgs.mkShell { - packages = + packages = let + beamPackages = pkgs.beam.packages; + in [ - erlang.elixir + beamPackages.erlang_27.erlang + beamPackages.erlang_27.elixir_1_17 ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ pkgs.darwin.apple_sdk.frameworks.CoreFoundation diff --git a/mix_credo.exs b/mix_credo.exs index fcca0657..914cd5d2 100644 --- a/mix_credo.exs +++ b/mix_credo.exs @@ -1,6 +1,7 @@ defmodule Mix.Credo do def dependency do - {:credo, "~> 1.7", only: [:dev, :test]} + # {:credo, "~> 1.7", only: [:dev, :test]} + {:credo, github: "rrrene/credo", only: [:dev, :test]} end def config(opts \\ []) do diff --git a/pages/glossary.md b/pages/glossary.md index 3cf46c93..6aef3e5b 100644 --- a/pages/glossary.md +++ b/pages/glossary.md @@ -63,12 +63,6 @@ Some LSP data structures cannot be trivially converted to Elixir terms. The `Lexical.Convertible` protocol helps centralize the necessary conversion logic where this is the case. -### The Transport Behaviour - -A behaviour responsible for reading, writing, serializing, and deserializing messages between the LSP client and Lexical language server. - -The behaviour is defined in `Expert.Transport`, with the implementation for stdio in `Expert.Transport.StdIO`. - ### The Translatable protocol and Translation modules The `Lexical.Completion.Translatable` protocol specifies how Elixir language constructs (such as behaviour callbacks) are converted into LSP constructs (such as [completion items](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem)). From 0be65947b2751d691b2627114ad793327c04bf58 Mon Sep 17 00:00:00 2001 From: doorgan Date: Fri, 20 Jun 2025 20:54:26 -0300 Subject: [PATCH 02/23] fixup!: fix tests --- apps/engine/mix.lock | 2 +- apps/expert/mix.lock | 2 +- .../test/expert/project/diagnostics_test.exs | 22 ++++++---- .../provider/handlers/code_lens_test.exs | 6 +-- .../handlers/go_to_definition_test.exs | 8 ++-- .../expert/provider/handlers/hover_test.exs | 44 +++++++++---------- apps/expert_credo/mix.lock | 2 +- apps/forge/mix.lock | 2 +- 8 files changed, 46 insertions(+), 42 deletions(-) diff --git a/apps/engine/mix.lock b/apps/engine/mix.lock index 63b2885f..f02a666b 100644 --- a/apps/engine/mix.lock +++ b/apps/engine/mix.lock @@ -8,7 +8,7 @@ "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "4d553a6ed09045ea28b3e10907a14caf633cf567", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index eb5e1052..a1a44494 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -7,7 +7,7 @@ "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "4d553a6ed09045ea28b3e10907a14caf633cf567", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, diff --git a/apps/expert/test/expert/project/diagnostics_test.exs b/apps/expert/test/expert/project/diagnostics_test.exs index 0dbbae6e..687a2a85 100644 --- a/apps/expert/test/expert/project/diagnostics_test.exs +++ b/apps/expert/test/expert/project/diagnostics_test.exs @@ -3,6 +3,7 @@ defmodule Expert.Project.DiagnosticsTest do alias Forge.Document alias Forge.Plugin.V1.Diagnostic alias GenLSP.Notifications.TextDocumentPublishDiagnostics + alias GenLSP.Structures alias GenLSP.Structures.PublishDiagnosticsParams use ExUnit.Case @@ -73,17 +74,16 @@ defmodule Expert.Project.DiagnosticsTest do Engine.Api.broadcast(project, file_diagnostics_message) + expected_severity = GenLSP.Enumerations.DiagnosticSeverity.error() + assert_receive {:transport, %TextDocumentPublishDiagnostics{ params: %PublishDiagnosticsParams{ diagnostics: [ - %Forge.Plugin.V1.Diagnostic.Result{ - details: nil, + %Structures.Diagnostic{ message: "stuff broke", - position: 1, - severity: :error, - source: nil, - uri: "file:///" <> _ + severity: ^expected_severity, + source: nil } ] } @@ -126,7 +126,7 @@ defmodule Expert.Project.DiagnosticsTest do document = open_file(project, "defmodule Dummy do\n .\nend\n") # only 3 lines in the file, but elixir compiler gives us a line number of 4 diagnostic = - diagnostic("lib/project.ex", + diagnostic(document.uri, position: {4, 1}, message: "missing terminator: end (for \"do\" starting at line 1)" ) @@ -141,8 +141,12 @@ defmodule Expert.Project.DiagnosticsTest do }}, 500 - assert %Diagnostic.Result{} = diagnostic - assert diagnostic.position == {4, 1} + assert %Structures.Diagnostic{} = diagnostic + + assert diagnostic.range == %GenLSP.Structures.Range{ + end: %Structures.Position{character: 0, line: 3}, + start: %Structures.Position{character: 0, line: 3} + } end end end diff --git a/apps/expert/test/expert/provider/handlers/code_lens_test.exs b/apps/expert/test/expert/provider/handlers/code_lens_test.exs index df300b6c..8d40cc34 100644 --- a/apps/expert/test/expert/provider/handlers/code_lens_test.exs +++ b/apps/expert/test/expert/provider/handlers/code_lens_test.exs @@ -68,7 +68,7 @@ defmodule Expert.Provider.Handlers.CodeLensTest do mix_exs = File.read!(mix_exs_path) {:ok, request} = build_request(mix_exs_path) - {:reply, %{result: lenses}} = handle(request, project) + {:ok, lenses} = handle(request, project) assert [%Structures.CodeLens{} = code_lens] = lenses @@ -83,7 +83,7 @@ defmodule Expert.Provider.Handlers.CodeLensTest do |> Path.join("apps/first/lib/umbrella/first.ex") |> build_request() - assert {:reply, %{result: []}} = handle(request, project) + assert {:ok, []} = handle(request, project) end test "does not emite a code lens for an umbrella app's mix.exs", %{project: project} do @@ -93,7 +93,7 @@ defmodule Expert.Provider.Handlers.CodeLensTest do |> Path.join("apps/first/mix.exs") |> build_request() - assert {:reply, %{result: []}} = handle(request, project) + assert {:ok, []} = handle(request, project) end end end diff --git a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs index 407b1fca..3d29bf04 100644 --- a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs +++ b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs @@ -63,7 +63,7 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do uses_file_path = file_path(project, Path.join("lib", "uses.ex")) {:ok, request} = build_request(uses_file_path, 4, 17) - {:reply, %{result: %Location{} = location}} = handle(request, project) + {:ok, %Location{} = location} = handle(request, project) assert Location.uri(location) == referenced_uri end @@ -71,7 +71,7 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do uses_file_path = file_path(project, Path.join("lib", "uses.ex")) {:ok, request} = build_request(uses_file_path, 4, 4) - {:reply, %{result: %Location{} = location}} = handle(request, project) + {:ok, %Location{} = location} = handle(request, project) assert Location.uri(location) == referenced_uri end @@ -79,14 +79,14 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do uses_file_path = file_path(project, Path.join("lib", "uses.ex")) {:ok, request} = build_request(uses_file_path, 8, 7) - {:reply, %{result: nil}} = handle(request, project) + {:ok, nil} = handle(request, project) end test "does not find built-in modules", %{project: project} do uses_file_path = file_path(project, Path.join("lib", "uses.ex")) {:ok, request} = build_request(uses_file_path, 8, 4) - {:reply, %{result: nil}} = handle(request, project) + {:ok, nil} = handle(request, project) end end end diff --git a/apps/expert/test/expert/provider/handlers/hover_test.exs b/apps/expert/test/expert/provider/handlers/hover_test.exs index 03c22256..a25919f2 100644 --- a/apps/expert/test/expert/provider/handlers/hover_test.exs +++ b/apps/expert/test/expert/provider/handlers/hover_test.exs @@ -97,7 +97,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«HoverWithDoc»" = hovered |> strip_cursor() |> decorate(result.range) @@ -114,7 +114,7 @@ defmodule Expert.Provider.Handlers.HoverTest do hovered = "|HoverPrivate" with_compiled_in(project, code, fn -> - assert {:reply, %{result: nil}} = hover(project, hovered) + assert {:ok, nil} = hover(project, hovered) end) end @@ -127,7 +127,7 @@ defmodule Expert.Provider.Handlers.HoverTest do hovered = "|HoverNoDocs" with_compiled_in(project, code, fn -> - assert {:reply, %{result: nil}} = hover(project, hovered) + assert {:ok, nil} = hover(project, hovered) end) end @@ -164,7 +164,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«HoverBehaviour»" = hovered |> strip_cursor() |> decorate(result.range) @@ -221,7 +221,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«HoverBehaviour»" = hovered |> strip_cursor() |> decorate(result.range) @@ -261,7 +261,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "%«StructWithDoc»{}" = hovered |> strip_cursor() |> decorate(result.range) @@ -299,7 +299,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "%«StructWithDoc»{}" = hovered |> strip_cursor() |> decorate(result.range) @@ -328,7 +328,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "%«StructWithDoc»{}" = hovered |> strip_cursor() |> decorate(result.range) @@ -361,7 +361,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«CallHover.my_fun»(1, 2)" = hovered |> strip_cursor() |> decorate(result.range) @@ -389,7 +389,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«CallHover.my_fun»(1, 2)" = hovered |> strip_cursor() |> decorate(result.range) @@ -429,7 +429,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«CallHover.my_fun»(1, 2)" = hovered |> strip_cursor() |> decorate(result.range) @@ -455,7 +455,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«CallHover.my_fun»" = hovered |> strip_cursor() |> decorate(result.range) @@ -475,7 +475,7 @@ defmodule Expert.Provider.Handlers.HoverTest do hovered = "CallHover.|my_fun(1)" with_compiled_in(project, code, fn -> - assert {:reply, %{result: nil}} = hover(project, hovered) + assert {:ok, nil} = hover(project, hovered) end) end @@ -498,7 +498,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«CallHover.my_fun»(1)" = hovered |> strip_cursor() |> decorate(result.range) @@ -526,7 +526,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected assert "«MacroHover.my_macro»(:foo)" = hovered |> strip_cursor() |> decorate(result.range) @@ -554,7 +554,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected end) @@ -585,7 +585,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected @@ -612,7 +612,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected @@ -639,7 +639,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected @@ -666,7 +666,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected @@ -686,7 +686,7 @@ defmodule Expert.Provider.Handlers.HoverTest do hovered = "@type foo :: TypeHover.|my_type()" with_compiled_in(project, code, fn -> - assert {:reply, %{result: nil}} = hover(project, hovered) + assert {:ok, nil} = hover(project, hovered) end) end @@ -710,7 +710,7 @@ defmodule Expert.Provider.Handlers.HoverTest do """ with_compiled_in(project, code, fn -> - assert {:reply, %{result: %Structures.Hover{} = result}} = hover(project, hovered) + assert {:ok, %Structures.Hover{} = result} = hover(project, hovered) assert result.contents.kind == "markdown" assert result.contents.value == expected end) diff --git a/apps/expert_credo/mix.lock b/apps/expert_credo/mix.lock index d7f03fa0..33452e16 100644 --- a/apps/expert_credo/mix.lock +++ b/apps/expert_credo/mix.lock @@ -8,7 +8,7 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.37.2", "2a3aa7014094f0e4e286a82aa5194a34dd17057160988b8509b15aa6c292720c", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "4dfa56075ce4887e4e8b1dcc121cd5fcb0f02b00391fd367ff5336d98fa49049"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "4d553a6ed09045ea28b3e10907a14caf633cf567", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, diff --git a/apps/forge/mix.lock b/apps/forge/mix.lock index 4f79c948..9c6c682f 100644 --- a/apps/forge/mix.lock +++ b/apps/forge/mix.lock @@ -6,7 +6,7 @@ "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "7b8d9fabba34b88fff36b62c316c538e6acdea5f", [branch: "async"]}, + "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "4d553a6ed09045ea28b3e10907a14caf633cf567", [branch: "async"]}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, From b4dd20155c1889aa34be20b7b37f87dd9f072080 Mon Sep 17 00:00:00 2001 From: doorgan Date: Fri, 20 Jun 2025 22:01:06 -0300 Subject: [PATCH 03/23] fixup!: use persistent_term instead of adding .lsp to Project --- apps/engine/benchmarks/ets_bench.exs | 2 +- apps/engine/test/engine/build/state_test.exs | 2 +- apps/engine/test/engine/build_test.exs | 2 +- .../engine/plugin/runner/coordinator_test.exs | 2 +- apps/engine/test/remote_control_test.exs | 2 +- apps/engine/test/support/test/fixtures.ex | 2 +- apps/expert/.iex.exs | 2 +- apps/expert/lib/expert.ex | 3 +++ apps/expert/lib/expert/configuration.ex | 4 ++-- apps/expert/lib/expert/iex/helpers.ex | 2 +- apps/expert/lib/expert/project/diagnostics.ex | 2 +- apps/expert/lib/expert/project/progress/state.ex | 16 ++++++++-------- .../expert/lib/expert/project/search_listener.ex | 6 +++--- .../lib/expert/provider/handlers/commands.ex | 2 +- apps/expert/lib/expert/state.ex | 2 +- apps/forge/lib/forge/project.ex | 16 +++++++--------- apps/forge/test/forge/ast/env_test.exs | 2 +- apps/forge/test/forge/project_test.exs | 2 +- 18 files changed, 36 insertions(+), 35 deletions(-) diff --git a/apps/engine/benchmarks/ets_bench.exs b/apps/engine/benchmarks/ets_bench.exs index 0387437b..6c90b520 100644 --- a/apps/engine/benchmarks/ets_bench.exs +++ b/apps/engine/benchmarks/ets_bench.exs @@ -33,7 +33,7 @@ defmodule BenchHelper do end cwd = __DIR__ -project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{cwd}") +project = Project.new("file://#{cwd}") Engine.set_project(project) Project.ensure_workspace(project) diff --git a/apps/engine/test/engine/build/state_test.exs b/apps/engine/test/engine/build/state_test.exs index a3eaeb2e..8041e8bb 100644 --- a/apps/engine/test/engine/build/state_test.exs +++ b/apps/engine/test/engine/build/state_test.exs @@ -40,7 +40,7 @@ defmodule Engine.Build.StateTest do project_name = to_string(project_name) fixture_dir = Path.join(fixtures_path(), project_name) - project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{fixture_dir}") + project = Project.new("file://#{fixture_dir}") state = State.new(project) Engine.set_project(project) diff --git a/apps/engine/test/engine/build_test.exs b/apps/engine/test/engine/build_test.exs index 54a16738..ffda124a 100644 --- a/apps/engine/test/engine/build_test.exs +++ b/apps/engine/test/engine/build_test.exs @@ -35,7 +35,7 @@ defmodule Engine.BuildTest do def with_project(project_name) do project_name = to_string(project_name) fixture_dir = Path.join(fixtures_path(), project_name) - project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{fixture_dir}") + project = Project.new("file://#{fixture_dir}") project |> Project.workspace_path() diff --git a/apps/engine/test/engine/plugin/runner/coordinator_test.exs b/apps/engine/test/engine/plugin/runner/coordinator_test.exs index 7df053a2..c7adb3fc 100644 --- a/apps/engine/test/engine/plugin/runner/coordinator_test.exs +++ b/apps/engine/test/engine/plugin/runner/coordinator_test.exs @@ -77,7 +77,7 @@ defmodule Engine.Plugin.Runner.CoordinatorTest do end test "works with projects" do - project = Project.new(%GenLSP.LSP{mod: :foo}, "file://" <> __DIR__) + project = Project.new("file://" <> __DIR__) Runner.diagnose(project, notifier()) diff --git a/apps/engine/test/remote_control_test.exs b/apps/engine/test/remote_control_test.exs index 662b65f2..6ac1e131 100644 --- a/apps/engine/test/remote_control_test.exs +++ b/apps/engine/test/remote_control_test.exs @@ -24,7 +24,7 @@ defmodule EngineTest do [fixtures_path(), "umbrella", "apps", "first"] |> Path.join() |> Document.Path.to_uri() - |> then(&Project.new(%GenLSP.LSP{mod: :foo}, &1)) + |> then(&Project.new(&1)) start_project(subapp_project) diff --git a/apps/engine/test/support/test/fixtures.ex b/apps/engine/test/support/test/fixtures.ex index d6964a9c..1a6a5b5c 100644 --- a/apps/engine/test/support/test/fixtures.ex +++ b/apps/engine/test/support/test/fixtures.ex @@ -15,7 +15,7 @@ defmodule Engine.Test.Fixtures do |> Path.join() |> Path.expand() |> Forge.Document.Path.to_uri() - |> then(&Project.new(%GenLSP.LSP{mod: :foo}, &1)) + |> then(&Project.new(&1)) end def project do diff --git a/apps/expert/.iex.exs b/apps/expert/.iex.exs index 96140830..670986db 100644 --- a/apps/expert/.iex.exs +++ b/apps/expert/.iex.exs @@ -11,4 +11,4 @@ other_project = |> Path.join() |> Path.expand() -project = Forge.Project.new(%GenLSP.LSP{mod: :foo}, "file://#{other_project}") +project = Forge.Project.new("file://#{other_project}") diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index 9c834a80..b0b24372 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -24,6 +24,8 @@ defmodule Expert do @dialyzer {:nowarn_function, apply_to_state: 2} + def get_lsp(), do: :persistent_term.get(:expert_lsp, nil) + def start_link(args) do Logger.debug(inspect(args)) @@ -35,6 +37,7 @@ defmodule Expert do end def init(lsp, _args) do + :persistent_term.put(:expert_lsp, lsp) {:ok, assign(lsp, state: State.new())} end diff --git a/apps/expert/lib/expert/configuration.ex b/apps/expert/lib/expert/configuration.ex index e952854e..0d8d3a3f 100644 --- a/apps/expert/lib/expert/configuration.ex +++ b/apps/expert/lib/expert/configuration.ex @@ -30,9 +30,9 @@ defmodule Expert.Configuration do @dialyzer {:nowarn_function, set_dialyzer_enabled: 2} @spec new(Forge.uri(), map(), String.t() | nil, GenLSP.Lsp.t()) :: t - def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name, lsp) do + def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name, _lsp) do support = Support.new(client_capabilities) - project = Project.new(lsp, root_uri) + project = Project.new(root_uri) %__MODULE__{support: support, project: project, client_name: client_name} |> tap(&set/1) diff --git a/apps/expert/lib/expert/iex/helpers.ex b/apps/expert/lib/expert/iex/helpers.ex index e7824747..087ab143 100644 --- a/apps/expert/lib/expert/iex/helpers.ex +++ b/apps/expert/lib/expert/iex/helpers.ex @@ -178,7 +178,7 @@ defmodule Expert.IEx.Helpers do project_uri = "file://#{project_path}" # FIXME(mhanberg): unclear how we will get LSP struct here - Forge.Project.new(%GenLSP.LSP{mod: :foo}, project_uri) + Forge.Project.new(project_uri) end) end diff --git a/apps/expert/lib/expert/project/diagnostics.ex b/apps/expert/lib/expert/project/diagnostics.ex index bdab0362..0a123d63 100644 --- a/apps/expert/lib/expert/project/diagnostics.ex +++ b/apps/expert/lib/expert/project/diagnostics.ex @@ -95,7 +95,7 @@ defmodule Expert.Project.Diagnostics do Enum.each(state.entries_by_uri, fn {uri, %State.Entry{} = entry} -> with {:ok, diagnostics} <- entry |> State.Entry.diagnostics() |> Forge.Protocol.Convert.to_lsp() do - GenLSP.notify(state.project.lsp, %TextDocumentPublishDiagnostics{ + GenLSP.notify(Expert.get_lsp(), %TextDocumentPublishDiagnostics{ params: %Structures.PublishDiagnosticsParams{uri: uri, diagnostics: diagnostics} }) end diff --git a/apps/expert/lib/expert/project/progress/state.ex b/apps/expert/lib/expert/project/progress/state.ex index 43c2ee0c..75cf4226 100644 --- a/apps/expert/lib/expert/project/progress/state.ex +++ b/apps/expert/lib/expert/project/progress/state.ex @@ -19,8 +19,8 @@ defmodule Expert.Project.Progress.State do progress = Value.begin(label) progress_by_label = Map.put(state.progress_by_label, label, progress) - write_work_done(state.project.lsp, progress.token) - write(state.project.lsp, progress) + write_work_done(Expert.get_lsp(), progress.token) + write(Expert.get_lsp(), progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -28,8 +28,8 @@ defmodule Expert.Project.Progress.State do def begin(%__MODULE__{} = state, percent_progress(label: label, max: max)) do progress = Percentage.begin(label, max) progress_by_label = Map.put(state.progress_by_label, label, progress) - write_work_done(state.project.lsp, progress.token) - write(state.project.lsp, progress) + write_work_done(Expert.get_lsp(), progress.token) + write(Expert.get_lsp(), progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -41,7 +41,7 @@ defmodule Expert.Project.Progress.State do {new_value, new_value} end) - write(state.project.lsp, progress) + write(Expert.get_lsp(), progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -55,7 +55,7 @@ defmodule Expert.Project.Progress.State do {new_percentage, new_percentage} end) - write(state.project.lsp, progress) + write(Expert.get_lsp(), progress) %__MODULE__{state | progress_by_label: progress_by_label} end @@ -65,7 +65,7 @@ defmodule Expert.Project.Progress.State do case progress do %Value{} = progress -> - write(state.project.lsp, Value.complete(progress, message)) + write(Expert.get_lsp(), Value.complete(progress, message)) _ -> :ok @@ -80,7 +80,7 @@ defmodule Expert.Project.Progress.State do case progress do %Percentage{} = progress -> - write(state.project.lsp, Percentage.complete(progress, message)) + write(Expert.get_lsp(), Percentage.complete(progress, message)) nil -> :ok diff --git a/apps/expert/lib/expert/project/search_listener.ex b/apps/expert/lib/expert/project/search_listener.ex index 1dbf4a62..f3c67043 100644 --- a/apps/expert/lib/expert/project/search_listener.ex +++ b/apps/expert/lib/expert/project/search_listener.ex @@ -31,7 +31,7 @@ defmodule Expert.Project.SearchListener do @impl GenServer def handle_info(project_reindex_requested(), %Project{} = project) do Logger.info("project reindex requested") - GenLSP.request(project.lsp, %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) + GenLSP.request(Expert.get_lsp(), %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) {:noreply, project} end @@ -39,9 +39,9 @@ defmodule Expert.Project.SearchListener do def handle_info(project_reindexed(elapsed_ms: elapsed), %Project{} = project) do message = "Reindexed #{Project.name(project)} in #{Formats.time(elapsed, unit: :millisecond)}" Logger.info(message) - GenLSP.request(project.lsp, %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) + GenLSP.request(Expert.get_lsp(), %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) - GenLSP.notify(project.lsp, %GenLSP.Notifications.WindowShowMessage{ + GenLSP.notify(Expert.get_lsp(), %GenLSP.Notifications.WindowShowMessage{ params: %GenLSP.Structures.ShowMessageParams{ type: GenLSP.Enumerations.MessageType.info(), message: message diff --git a/apps/expert/lib/expert/provider/handlers/commands.ex b/apps/expert/lib/expert/provider/handlers/commands.ex index 64eb45eb..875a8dcf 100644 --- a/apps/expert/lib/expert/provider/handlers/commands.ex +++ b/apps/expert/lib/expert/provider/handlers/commands.ex @@ -46,7 +46,7 @@ defmodule Expert.Provider.Handlers.Commands do {:ok, "ok"} error -> - GenLSP.notify(project.lsp, %GenLSP.Notifications.WindowShowMessage{ + GenLSP.notify(Expert.get_lsp(), %GenLSP.Notifications.WindowShowMessage{ params: %GenLSP.Structures.ShowMessageParams{ type: GenLSP.Enumerations.MessageType.error(), message: "Indexing #{Project.name(project)} failed" diff --git a/apps/expert/lib/expert/state.ex b/apps/expert/lib/expert/state.ex index 9ae5e19f..0a51f946 100644 --- a/apps/expert/lib/expert/state.ex +++ b/apps/expert/lib/expert/state.ex @@ -89,7 +89,7 @@ defmodule Expert.State do {:ok, %__MODULE__{state | configuration: config}} {:ok, config, request} -> - GenLSP.request(state.configuration.project.lsp, request) + GenLSP.request(Expert.get_lsp(), request) {:ok, %__MODULE__{state | configuration: config}} end diff --git a/apps/forge/lib/forge/project.ex b/apps/forge/lib/forge/project.ex index abb2566d..03d069b4 100644 --- a/apps/forge/lib/forge/project.ex +++ b/apps/forge/lib/forge/project.ex @@ -14,8 +14,7 @@ defmodule Forge.Project do mix_target: nil, env_variables: %{}, project_module: nil, - entropy: 1, - lsp: nil + entropy: 1 @type message :: String.t() @type restart_notification :: {:restart, Logger.level(), String.t()} @@ -23,20 +22,19 @@ defmodule Forge.Project do root_uri: Forge.uri() | nil, mix_exs_uri: Forge.uri() | nil, entropy: non_neg_integer(), - lsp: GenLSP.LSP.t() - # mix_env: atom(), - # mix_target: atom(), - # env_variables: %{String.t() => String.t()} + mix_env: atom(), + mix_target: atom(), + env_variables: %{String.t() => String.t()} } @type error_with_message :: {:error, message} @workspace_directory_name ".expert" - @spec new(GenLSP.LSP.t(), Forge.uri()) :: t - def new(lsp, root_uri) do + @spec new(Forge.uri()) :: t + def new(root_uri) do entropy = :rand.uniform(65_536) - %__MODULE__{entropy: entropy, lsp: lsp} + %__MODULE__{entropy: entropy} |> maybe_set_root_uri(root_uri) |> maybe_set_mix_exs_uri() end diff --git a/apps/forge/test/forge/ast/env_test.exs b/apps/forge/test/forge/ast/env_test.exs index 0352139d..071cab7b 100644 --- a/apps/forge/test/forge/ast/env_test.exs +++ b/apps/forge/test/forge/ast/env_test.exs @@ -9,7 +9,7 @@ defmodule Forge.Ast.EnvTest do def new_env(text, opts \\ []) do opts = Keyword.merge([as: :document], opts) - project = Project.new(%GenLSP.LSP{mod: :foo}, "file://#{File.cwd!()}") + project = Project.new("file://#{File.cwd!()}") {position, document} = pop_cursor(text, opts) analysis = Ast.analyze(document) {:ok, env} = new(project, analysis, position) diff --git a/apps/forge/test/forge/project_test.exs b/apps/forge/test/forge/project_test.exs index b6a2ce07..74f3df8e 100644 --- a/apps/forge/test/forge/project_test.exs +++ b/apps/forge/test/forge/project_test.exs @@ -7,7 +7,7 @@ defmodule Forge.ProjectTest do def project do root = Forge.Document.Path.to_uri(__DIR__) - Project.new(%GenLSP.LSP{mod: :foo}, root) + Project.new(root) end describe "name/1" do From d200ed572d3450b1c7b11a98e5192a643851f700 Mon Sep 17 00:00:00 2001 From: doorgan Date: Fri, 20 Jun 2025 22:29:18 -0300 Subject: [PATCH 04/23] fixup!: fix dialyzer issues --- apps/expert/lib/expert.ex | 24 +++++++++++++----------- apps/expert/lib/expert/configuration.ex | 4 ++-- apps/expert/lib/expert/state.ex | 5 ++--- apps/expert/mix.exs | 5 +++-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index b0b24372..be4f605c 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -45,7 +45,7 @@ defmodule Expert do state = assigns(lsp).state Process.send_after(self(), :default_config, :timer.seconds(5)) - case State.initialize(state, request, lsp) do + case State.initialize(state, request) do {:ok, response, state} -> # TODO: this should be gated behind the dynamic registration in the initialization params registrations = registrations() @@ -153,18 +153,20 @@ defmodule Expert do end end - def handle_info(:default_config, %State{configuration: nil} = state) do - Logger.warning( - "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <> - "Using default settings." - ) + def handle_info(:default_config, lsp) do + state = assigns(lsp).state - {:ok, config} = State.default_configuration(state) - {:noreply, %State{state | configuration: config}} - end + if state.configuration == nil do + Logger.warning( + "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <> + "Using default settings." + ) - def handle_info(:default_config, %State{} = state) do - {:noreply, state} + {:ok, config} = State.default_configuration(state) + {:noreply, assign(lsp, state: %State{state | configuration: config})} + else + {:noreply, lsp} + end end defp apply_to_state(%State{} = state, %{} = request_or_notification) do diff --git a/apps/expert/lib/expert/configuration.ex b/apps/expert/lib/expert/configuration.ex index 0d8d3a3f..40003071 100644 --- a/apps/expert/lib/expert/configuration.ex +++ b/apps/expert/lib/expert/configuration.ex @@ -29,8 +29,8 @@ defmodule Expert.Configuration do @dialyzer {:nowarn_function, set_dialyzer_enabled: 2} - @spec new(Forge.uri(), map(), String.t() | nil, GenLSP.Lsp.t()) :: t - def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name, _lsp) do + @spec new(Forge.uri(), map(), String.t() | nil) :: t + def new(root_uri, %Structures.ClientCapabilities{} = client_capabilities, client_name) do support = Support.new(client_capabilities) project = Project.new(root_uri) diff --git a/apps/expert/lib/expert/state.ex b/apps/expert/lib/expert/state.ex index 0a51f946..b02ca5c6 100644 --- a/apps/expert/lib/expert/state.ex +++ b/apps/expert/lib/expert/state.ex @@ -40,8 +40,7 @@ defmodule Expert.State do %__MODULE__{initialized?: false} = state, %Requests.Initialize{ params: %Structures.InitializeParams{} = event - }, - lsp + } ) do client_name = case event.client_info do @@ -49,7 +48,7 @@ defmodule Expert.State do _ -> nil end - config = Configuration.new(event.root_uri, event.capabilities, client_name, lsp) + config = Configuration.new(event.root_uri, event.capabilities, client_name) new_state = %__MODULE__{state | configuration: config, initialized?: true} Logger.info("Starting project at uri #{config.project.root_uri}") diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index 2a56dbbc..976c9af2 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -44,13 +44,14 @@ defmodule Expert.MixProject do Mix.Dialyzer.dependency(), {:elixir_sense, github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, + {:engine, path: "../engine", env: Mix.env()}, + {:forge, path: "../forge", env: Mix.env()}, + {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:jason, "~> 1.4"}, {:logger_file_backend, "~> 0.0", only: [:dev, :prod]}, {:patch, "~> 0.15", runtime: false, only: [:dev, :test]}, {:path_glob, "~> 0.2"}, {:schematic, "~> 0.2"}, - {:engine, path: "../engine", env: Mix.env()}, - {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:sourceror, "~> 1.9"} ] end From 809375cc1312c0aabebea5d02db9ec62d24e0280 Mon Sep 17 00:00:00 2001 From: doorgan Date: Mon, 23 Jun 2025 16:01:56 -0300 Subject: [PATCH 05/23] fixup!: Don't use parenthesis on 0-arity function --- apps/expert/lib/expert.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index be4f605c..ab033f4a 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -24,7 +24,7 @@ defmodule Expert do @dialyzer {:nowarn_function, apply_to_state: 2} - def get_lsp(), do: :persistent_term.get(:expert_lsp, nil) + def get_lsp, do: :persistent_term.get(:expert_lsp, nil) def start_link(args) do Logger.debug(inspect(args)) From 6638c1359eb53bf92aaa30a8cd60205c32692f29 Mon Sep 17 00:00:00 2001 From: doorgan Date: Mon, 23 Jun 2025 16:09:15 -0300 Subject: [PATCH 06/23] fixup!: cleanup credo changes --- apps/engine/mix.lock | 2 +- apps/engine/test/support/test/fixtures.ex | 2 +- apps/expert/mix.lock | 2 +- apps/expert_credo/mix.lock | 2 +- apps/forge/mix.lock | 2 +- mix.exs | 2 +- mix_credo.exs | 3 +-- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/engine/mix.lock b/apps/engine/mix.lock index f02a666b..a9c56175 100644 --- a/apps/engine/mix.lock +++ b/apps/engine/mix.lock @@ -2,7 +2,7 @@ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, - "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, diff --git a/apps/engine/test/support/test/fixtures.ex b/apps/engine/test/support/test/fixtures.ex index 1a6a5b5c..e16e284a 100644 --- a/apps/engine/test/support/test/fixtures.ex +++ b/apps/engine/test/support/test/fixtures.ex @@ -15,7 +15,7 @@ defmodule Engine.Test.Fixtures do |> Path.join() |> Path.expand() |> Forge.Document.Path.to_uri() - |> then(&Project.new(&1)) + |> Project.new() end def project do diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index a1a44494..37728201 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -1,7 +1,7 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, diff --git a/apps/expert_credo/mix.lock b/apps/expert_credo/mix.lock index 33452e16..1c2be922 100644 --- a/apps/expert_credo/mix.lock +++ b/apps/expert_credo/mix.lock @@ -1,7 +1,7 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"}, diff --git a/apps/forge/mix.lock b/apps/forge/mix.lock index 9c6c682f..e9e56b58 100644 --- a/apps/forge/mix.lock +++ b/apps/forge/mix.lock @@ -1,7 +1,7 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "credo": {:git, "https://github.com/rrrene/credo.git", "f731459d4fb5c3359303e99fde9fa1e51d6fbea9", []}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, diff --git a/mix.exs b/mix.exs index 144e6801..d7ffce32 100644 --- a/mix.exs +++ b/mix.exs @@ -17,7 +17,7 @@ defmodule Expert.LanguageServer.MixProject do defp deps do [ {:ex_doc, "~> 0.34", only: :dev, runtime: false}, - {:credo, "~> 1.7", only: [:dev, :test]} + {:credo, github: "rrrene/credo", only: [:dev, :test]} ] end diff --git a/mix_credo.exs b/mix_credo.exs index 914cd5d2..fcca0657 100644 --- a/mix_credo.exs +++ b/mix_credo.exs @@ -1,7 +1,6 @@ defmodule Mix.Credo do def dependency do - # {:credo, "~> 1.7", only: [:dev, :test]} - {:credo, github: "rrrene/credo", only: [:dev, :test]} + {:credo, "~> 1.7", only: [:dev, :test]} end def config(opts \\ []) do From 8119e4942339d2c6adb279a90669b9c91a5b2293 Mon Sep 17 00:00:00 2001 From: doorgan Date: Mon, 23 Jun 2025 16:10:36 -0300 Subject: [PATCH 07/23] fixup!: Remove outdated comments --- apps/expert/lib/expert/iex/helpers.ex | 1 - apps/expert/lib/expert/provider/handlers/document_symbols.ex | 1 - 2 files changed, 2 deletions(-) diff --git a/apps/expert/lib/expert/iex/helpers.ex b/apps/expert/lib/expert/iex/helpers.ex index 087ab143..92dfe17b 100644 --- a/apps/expert/lib/expert/iex/helpers.ex +++ b/apps/expert/lib/expert/iex/helpers.ex @@ -177,7 +177,6 @@ defmodule Expert.IEx.Helpers do |> Path.expand() project_uri = "file://#{project_path}" - # FIXME(mhanberg): unclear how we will get LSP struct here Forge.Project.new(project_uri) end) end diff --git a/apps/expert/lib/expert/provider/handlers/document_symbols.ex b/apps/expert/lib/expert/provider/handlers/document_symbols.ex index 7ff635db..d09feca0 100644 --- a/apps/expert/lib/expert/provider/handlers/document_symbols.ex +++ b/apps/expert/lib/expert/provider/handlers/document_symbols.ex @@ -28,7 +28,6 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do nil end - # FIXME: the ranges need to be converted to lsp structures %Structures.DocumentSymbol{ children: children, detail: root.detail, From da45be7d2f06dba2c05bfe377fb52f93b6c48aa5 Mon Sep 17 00:00:00 2001 From: doorgan Date: Mon, 23 Jun 2025 16:14:40 -0300 Subject: [PATCH 08/23] fixup!: use info severity for normal shutdown log --- apps/expert/lib/expert/state.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/expert/lib/expert/state.ex b/apps/expert/lib/expert/state.ex index b02ca5c6..cfff6a19 100644 --- a/apps/expert/lib/expert/state.ex +++ b/apps/expert/lib/expert/state.ex @@ -178,8 +178,7 @@ defmodule Expert.State do end def apply(%__MODULE__{} = state, %GenLSP.Requests.Shutdown{}) do - # TODO: why is this an error log? - Logger.error("Shutting down") + Logger.info("Shutting down") {:ok, nil, %__MODULE__{state | shutdown_received?: true}} end From 9d230dbe0050ee3667e1bc534ac2f32d11d9516a Mon Sep 17 00:00:00 2001 From: doorgan Date: Mon, 23 Jun 2025 16:17:16 -0300 Subject: [PATCH 09/23] fixup!: cleanup credo changes --- apps/expert_credo/mix.exs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/expert_credo/mix.exs b/apps/expert_credo/mix.exs index 43f6e5c1..8df35506 100644 --- a/apps/expert_credo/mix.exs +++ b/apps/expert_credo/mix.exs @@ -1,7 +1,6 @@ defmodule ExpertCredo.MixProject do use Mix.Project Code.require_file("../../mix_dialyzer.exs") - Code.require_file("../../mix_includes.exs") @repo_url "https://github.com/elixir-lang/expert/" @version "0.5.0" @@ -29,8 +28,7 @@ defmodule ExpertCredo.MixProject do defp deps do [ {:forge, path: "../forge", env: Mix.env()}, - # {:credo, "> 0.0.0", only: [:dev, :test]}, - Mix.Credo.dependency(), + {:credo, "> 0.0.0", only: [:dev, :test]}, Mix.Dialyzer.dependency(), {:jason, "> 0.0.0", optional: true}, {:ex_doc, "~> 0.34", optional: true, only: [:dev, :hex]} From 1a8ea20d31c00c31aa5ca96685c550d8f6ca8c59 Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 17:35:19 -0300 Subject: [PATCH 10/23] refactor: isolate Engine from Expert --- .gitignore | 2 + Makefile | 2 + apps/engine/.formatter.exs | 21 +- apps/engine/lib/engine/engine.ex | 159 +-------- apps/engine/lib/engine/engine/api.ex | 141 -------- apps/engine/lib/engine/engine/api/proxy.ex | 2 +- .../engine/api/proxy/buffering_state.ex | 5 +- apps/engine/lib/engine/engine/build.ex | 10 - apps/engine/lib/engine/engine/build/state.ex | 4 +- apps/engine/lib/engine/engine/code_action.ex | 29 +- .../lib/engine/engine/code_action/handler.ex | 4 +- .../engine/code_action/handlers/add_alias.ex | 7 +- .../code_action/handlers/organize_aliases.ex | 2 +- .../engine/code_action/handlers/refactorex.ex | 2 +- .../handlers/remove_unused_alias.ex | 4 +- .../handlers/replace_remote_function.ex | 6 +- .../handlers/replace_with_underscore.ex | 4 +- .../engine/code_intelligence/definition.ex | 2 +- .../engine/engine/code_intelligence/docs.ex | 21 +- .../engine/code_intelligence/references.ex | 2 +- .../engine/code_intelligence/structs.ex | 2 +- .../engine/code_intelligence/symbols.ex | 4 +- .../engine/code_intelligence/variable.ex | 2 +- .../lib/engine/engine/commands/reindex.ex | 4 +- .../lib/engine/engine/compilation/tracer.ex | 2 +- apps/engine/lib/engine/engine/completion.ex | 2 +- apps/engine/lib/engine/engine/dispatch.ex | 5 +- .../lib/engine/engine/dispatch/handler.ex | 2 +- .../engine/dispatch/handlers/indexing.ex | 2 +- apps/engine/lib/engine/engine/mix.ex | 2 +- .../lib/engine/engine/module_mappings.ex | 2 +- apps/engine/lib/engine/engine/plugin.ex | 5 +- .../lib/engine/engine/plugin/discovery.ex | 5 +- apps/engine/lib/engine/engine/progress.ex | 2 +- apps/engine/lib/engine/engine/search/fuzzy.ex | 2 +- .../lib/engine/engine/search/indexer.ex | 2 +- .../search/indexer/extractors/ecto_schema.ex | 2 +- .../search/indexer/extractors/ex_unit.ex | 2 +- .../indexer/extractors/function_definition.ex | 2 +- .../indexer/extractors/function_reference.ex | 2 +- .../search/indexer/extractors/module.ex | 4 +- .../indexer/extractors/module_attribute.ex | 2 +- .../indexer/extractors/struct_definition.ex | 2 +- .../indexer/extractors/struct_reference.ex | 2 +- .../search/indexer/extractors/variable.ex | 2 +- .../engine/search/indexer/source/reducer.ex | 4 +- apps/engine/lib/engine/engine/search/store.ex | 10 +- .../lib/engine/engine/search/store/backend.ex | 2 +- .../engine/search/store/backends/ets.ex | 2 +- .../search/store/backends/ets/schema.ex | 2 +- .../search/store/backends/ets/schemas/v1.ex | 2 +- .../search/store/backends/ets/schemas/v2.ex | 2 +- .../search/store/backends/ets/schemas/v3.ex | 2 +- .../engine/search/store/backends/ets/state.ex | 2 +- .../lib/engine/engine/search/store/state.ex | 4 +- apps/engine/lib/mix/tasks/build.ex | 22 ++ apps/engine/lib/mix/tasks/namespace/path.ex | 29 -- apps/engine/mix.exs | 4 +- apps/engine/mix.lock | 4 +- apps/engine/priv/.gitkeep | 0 .../engine/api/proxy/buffering_state_test.exs | 7 +- apps/engine/test/engine/api/proxy_test.exs | 13 +- apps/engine/test/engine/build/state_test.exs | 2 +- .../code_action/handlers/add_alias_test.exs | 2 +- .../handlers/organize_aliases_test.exs | 2 +- .../code_action/handlers/refactorex_test.exs | 2 +- .../handlers/remove_unused_alias_test.exs | 4 +- .../handlers/replace_remote_function_test.exs | 4 +- .../handlers/replace_with_underscore_test.exs | 4 +- .../engine/code_intelligence/entity_test.exs | 2 +- .../code_intelligence/references_test.exs | 2 +- .../engine/code_intelligence/symbols_test.exs | 8 +- .../test/engine/code_mod/aliases_test.exs | 2 +- .../engine/test/engine/code_mod/diff_test.exs | 2 +- .../test/engine/code_mod/format_test.exs | 27 +- .../test/engine/commands/reindex_test.exs | 2 +- .../candidate/argument_names_test.exs | 4 +- apps/engine/test/engine/completion_test.exs | 2 +- .../test/engine/dispatch/handler_test.exs | 2 +- .../engine/dispatch/handlers/indexer_test.exs | 5 +- apps/engine/test/engine/dispatch_test.exs | 2 +- .../test/engine/module_mappings_test.exs | 2 +- apps/engine/test/engine/progress_test.exs | 2 +- .../extractors/function_definition_test.exs | 2 +- .../test/engine/search/indexer_test.exs | 4 +- .../search/store/backends/ets/schema_test.exs | 2 +- .../search/store/backends/ets/wal_test.exs | 2 +- .../engine/search/store/backends/ets_test.exs | 2 +- apps/engine/test/engine/search/store_test.exs | 11 +- .../test/fixtures/project/.expert/project.log | 316 ++++++++++++++++++ .../test/support/test/entry/entry_builder.ex | 2 +- apps/expert/config/runtime.exs | 2 + apps/expert/lib/expert.ex | 1 + apps/expert/lib/expert/application.ex | 2 + .../expert/code_intelligence/completion.ex | 18 +- .../translations/bitstring_option.ex | 2 +- .../completion/translations/callable.ex | 2 +- .../completion/translations/callback.ex | 2 +- .../completion/translations/function.ex | 2 +- .../completion/translations/macro.ex | 2 +- .../completion/translations/map_field.ex | 2 +- .../translations/module_attribute.ex | 2 +- .../translations/module_or_behaviour.ex | 2 +- .../completion/translations/struct_field.ex | 2 +- .../completion/translations/typespec.ex | 2 +- .../completion/translations/variable.ex | 2 +- apps/expert/lib/expert/engine_api.ex | 285 ++++++++++++++++ apps/expert/lib/expert/iex/helpers.ex | 33 +- .../engine => expert/lib/expert}/port.ex | 10 +- apps/expert/lib/expert/project/diagnostics.ex | 5 +- .../expert/lib/expert/project/intelligence.ex | 8 +- apps/expert/lib/expert/project/node.ex | 11 +- apps/expert/lib/expert/project/progress.ex | 2 +- .../lib/expert/project/progress/state.ex | 2 +- .../lib/expert/project/progress/support.ex | 2 +- .../lib/expert/project/search_listener.ex | 6 +- apps/expert/lib/expert/project/supervisor.ex | 2 +- .../lib/expert}/project_node.ex | 6 +- .../lib/expert}/project_node_supervisor.ex | 4 +- .../expert/provider/handlers/code_action.ex | 5 +- .../lib/expert/provider/handlers/code_lens.ex | 3 +- .../lib/expert/provider/handlers/commands.ex | 3 +- .../provider/handlers/document_symbols.ex | 6 +- .../provider/handlers/find_references.ex | 4 +- .../expert/provider/handlers/formatting.ex | 3 +- .../provider/handlers/go_to_definition.ex | 3 +- .../lib/expert/provider/handlers/hover.ex | 11 +- .../provider/handlers/workspace_symbol.ex | 6 +- apps/expert/lib/expert/release.ex | 21 ++ apps/expert/lib/expert/state.ex | 12 +- apps/expert/lib/mix/tasks/package.ex | 2 +- apps/expert/mix.exs | 2 - apps/expert/mix.lock | 4 +- apps/{engine => expert}/priv/port_wrapper.sh | 0 apps/expert/test/engine/README.md | 1 + .../test/engine/build_test.exs | 32 +- .../code_intelligence/definition_test.exs | 19 +- .../test/engine/engine_test.exs} | 9 +- .../completion/builder_test.exs | 2 +- .../translations/module_or_behaviour_test.exs | 3 +- .../code_intelligence/completion_test.exs | 27 +- .../expert/project/diagnostics/state_test.exs | 4 +- .../test/expert/project/diagnostics_test.exs | 19 +- .../test/expert/project/intelligence_test.exs | 7 +- apps/expert/test/expert/project/node_test.exs | 16 +- .../expert/project/progress/state_test.exs | 4 +- .../expert/project/progress/support_test.exs | 4 +- .../test/expert/project/progress_test.exs | 17 +- .../test/expert}/project_node_test.exs | 13 +- .../provider/handlers/code_lens_test.exs | 11 +- .../handlers/find_references_test.exs | 7 +- .../provider/handlers/formatting_test.exs | 41 +++ .../handlers/go_to_definition_test.exs | 9 +- .../expert/provider/handlers/hover_test.exs | 13 +- .../test/support/test/completion_case.ex | 9 +- .../expert/test/support/test/dispatch_fake.ex | 4 +- apps/expert/test/test_helper.exs | 15 +- apps/forge/.credo.exs | 4 +- apps/forge/.formatter.exs | 38 ++- apps/forge/lib/forge/code_action.ex | 23 ++ .../lib/forge}/code_action/diagnostic.ex | 2 +- .../forge/lib/forge/code_intelligence/docs.ex | 13 + .../forge}/code_intelligence/docs/entry.ex | 2 +- .../code_intelligence/symbols/document.ex | 4 +- .../code_intelligence/symbols/workspace.ex | 4 +- .../lib/forge}/completion/candidate.ex | 4 +- .../completion/candidate/argument_names.ex | 2 +- .../lib/forge/engine_api}/messages.ex | 2 +- .../lib/forge}/namespace/abstract.ex | 8 +- .../lib/forge}/namespace/code.ex | 2 +- .../lib/forge/namespace}/erlang.ex | 2 +- .../lib/forge}/namespace/module.ex | 8 +- apps/forge/lib/forge/namespace/path.ex | 38 +++ .../namespace/transform/app_directories.ex | 19 +- .../lib/forge}/namespace/transform/apps.ex | 24 +- .../lib/forge}/namespace/transform/beams.ex | 9 +- .../lib/forge}/namespace/transform/boots.ex | 2 +- .../lib/forge}/namespace/transform/configs.ex | 10 +- .../lib/forge}/namespace/transform/scripts.ex | 28 +- apps/forge/lib/forge/project.ex | 25 ++ .../lib/forge}/search/indexer/entry.ex | 4 +- .../lib/forge}/search/indexer/source/block.ex | 2 +- .../lib/mix/tasks/namespace.ex | 7 +- apps/forge/mix.exs | 2 +- .../.formatter.exs | 0 .../compilation_callback_errors/.gitignore | 0 .../lib/compile_callback_error.ex | 0 .../compilation_callback_errors/mix.exs | 0 .../compilation_errors/.formatter.exs | 0 .../fixtures/compilation_errors/.gitignore | 0 .../fixtures/compilation_errors/README.md | 0 .../lib/compilation_errors.ex | 0 .../test/fixtures/compilation_errors/mix.exs | 0 .../compilation_warnings/.formatter.exs | 0 .../fixtures/compilation_warnings/.gitignore | 0 .../fixtures/compilation_warnings/README.md | 0 .../lib/unused_variable.ex | 0 .../fixtures/compilation_warnings/mix.exs | 0 .../dependency/lib/dependency/structs.ex | 0 .../fixtures/navigations/lib/macro_struct.ex | 0 .../fixtures/navigations/lib/multi_arity.ex | 0 .../fixtures/navigations/lib/my_definition.ex | 0 .../test/fixtures/navigations/lib/struct.ex | 0 .../test/fixtures/navigations/lib/uses.ex | 0 .../test/fixtures/navigations/mix.exs | 0 .../fixtures/parse_errors/lib/parse_errors.ex | 0 .../test/fixtures/parse_errors/mix.exs | 0 .../test/fixtures/project/.formatter.exs | 0 .../test/fixtures/project/.gitignore | 0 .../test/fixtures/project/README.md | 0 .../test/fixtures/project/lib/behaviours.ex | 0 .../test/fixtures/project/lib/default_args.ex | 0 .../test/fixtures/project/lib/functions.ex | 0 .../test/fixtures/project/lib/macros.ex | 0 .../fixtures/project/lib/other_modules.ex | 0 .../test/fixtures/project/lib/project.ex | 0 .../test/fixtures/project/lib/structs.ex | 0 .../test/fixtures/project/mix.exs | 0 .../fixtures/project_metadata/.formatter.exs | 0 .../test/fixtures/project_metadata/.gitignore | 0 .../test/fixtures/project_metadata/README.md | 0 .../project_metadata/config/config.exs | 0 .../project_metadata/lib/project_metadata.ex | 0 .../test/fixtures/project_metadata/mix.exs | 0 .../test/fixtures/umbrella/.formatter.exs | 0 .../test/fixtures/umbrella/.gitignore | 0 .../test/fixtures/umbrella/README.md | 0 .../umbrella/apps/first/.formatter.exs | 0 .../fixtures/umbrella/apps/first/.gitignore | 0 .../fixtures/umbrella/apps/first/README.md | 0 .../umbrella/apps/first/lib/umbrella/first.ex | 0 .../test/fixtures/umbrella/apps/first/mix.exs | 0 .../umbrella/apps/second/.formatter.exs | 0 .../fixtures/umbrella/apps/second/.gitignore | 0 .../fixtures/umbrella/apps/second/README.md | 0 .../apps/second/lib/umbrella/second.ex | 0 .../fixtures/umbrella/apps/second/mix.exs | 0 .../test/fixtures/umbrella/config/config.exs | 0 .../test/fixtures/umbrella/mix.exs | 0 .../test/support/test/code_mod_case.ex | 4 +- .../test/support/test/fixtures.ex | 2 +- 241 files changed, 1270 insertions(+), 846 deletions(-) delete mode 100644 apps/engine/lib/engine/engine/api.ex create mode 100644 apps/engine/lib/mix/tasks/build.ex delete mode 100644 apps/engine/lib/mix/tasks/namespace/path.ex create mode 100644 apps/engine/priv/.gitkeep create mode 100644 apps/engine/test/fixtures/project/.expert/project.log create mode 100644 apps/expert/lib/expert/engine_api.ex rename apps/{engine/lib/engine/engine => expert/lib/expert}/port.ex (89%) rename apps/{engine/lib/engine/engine => expert/lib/expert}/project_node.ex (97%) rename apps/{engine/lib/engine/engine => expert/lib/expert}/project_node_supervisor.ex (90%) create mode 100644 apps/expert/lib/expert/release.ex rename apps/{engine => expert}/priv/port_wrapper.sh (100%) create mode 100644 apps/expert/test/engine/README.md rename apps/{engine => expert}/test/engine/build_test.exs (96%) rename apps/{engine => expert}/test/engine/code_intelligence/definition_test.exs (96%) rename apps/{engine/test/remote_control_test.exs => expert/test/engine/engine_test.exs} (82%) rename apps/{engine/test/engine => expert/test/expert}/project_node_test.exs (78%) create mode 100644 apps/expert/test/expert/provider/handlers/formatting_test.exs create mode 100644 apps/forge/lib/forge/code_action.ex rename apps/{engine/lib/engine/engine => forge/lib/forge}/code_action/diagnostic.ex (91%) create mode 100644 apps/forge/lib/forge/code_intelligence/docs.ex rename apps/{engine/lib/engine/engine => forge/lib/forge}/code_intelligence/docs/entry.ex (96%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/code_intelligence/symbols/document.ex (96%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/code_intelligence/symbols/workspace.ex (92%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/completion/candidate.ex (98%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/completion/candidate/argument_names.ex (98%) rename apps/{engine/lib/engine/engine/api => forge/lib/forge/engine_api}/messages.ex (99%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/abstract.ex (98%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/code.ex (70%) rename apps/{engine/lib/mix/tasks/namespace/transform => forge/lib/forge/namespace}/erlang.ex (93%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/module.ex (91%) create mode 100644 apps/forge/lib/forge/namespace/path.ex rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/app_directories.ex (50%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/apps.ex (66%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/beams.ex (93%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/boots.ex (92%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/configs.ex (80%) rename apps/{engine/lib/mix/tasks => forge/lib/forge}/namespace/transform/scripts.ex (67%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/search/indexer/entry.ex (98%) rename apps/{engine/lib/engine/engine => forge/lib/forge}/search/indexer/source/block.ex (87%) rename apps/{engine => forge}/lib/mix/tasks/namespace.ex (96%) rename apps/{engine => forge}/test/fixtures/compilation_callback_errors/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/compilation_callback_errors/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/compilation_callback_errors/lib/compile_callback_error.ex (100%) rename apps/{engine => forge}/test/fixtures/compilation_callback_errors/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/compilation_errors/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/compilation_errors/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/compilation_errors/README.md (100%) rename apps/{engine => forge}/test/fixtures/compilation_errors/lib/compilation_errors.ex (100%) rename apps/{engine => forge}/test/fixtures/compilation_errors/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/compilation_warnings/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/compilation_warnings/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/compilation_warnings/README.md (100%) rename apps/{engine => forge}/test/fixtures/compilation_warnings/lib/unused_variable.ex (100%) rename apps/{engine => forge}/test/fixtures/compilation_warnings/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/dependency/lib/dependency/structs.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/lib/macro_struct.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/lib/multi_arity.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/lib/my_definition.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/lib/struct.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/lib/uses.ex (100%) rename apps/{engine => forge}/test/fixtures/navigations/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/parse_errors/lib/parse_errors.ex (100%) rename apps/{engine => forge}/test/fixtures/parse_errors/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/project/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/project/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/project/README.md (100%) rename apps/{engine => forge}/test/fixtures/project/lib/behaviours.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/default_args.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/functions.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/macros.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/other_modules.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/project.ex (100%) rename apps/{engine => forge}/test/fixtures/project/lib/structs.ex (100%) rename apps/{engine => forge}/test/fixtures/project/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/README.md (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/config/config.exs (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/lib/project_metadata.ex (100%) rename apps/{engine => forge}/test/fixtures/project_metadata/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/umbrella/README.md (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/first/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/first/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/first/README.md (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/first/lib/umbrella/first.ex (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/first/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/second/.formatter.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/second/.gitignore (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/second/README.md (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/second/lib/umbrella/second.ex (100%) rename apps/{engine => forge}/test/fixtures/umbrella/apps/second/mix.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/config/config.exs (100%) rename apps/{engine => forge}/test/fixtures/umbrella/mix.exs (100%) rename apps/{engine => forge}/test/support/test/code_mod_case.ex (95%) rename apps/{engine => forge}/test/support/test/fixtures.ex (95%) diff --git a/.gitignore b/.gitignore index ee91cbb4..71a27ae0 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,6 @@ expert_debug priv/plts apps/forge/src/future_elixir_parser.erl + +.DS_Store .notes/ diff --git a/Makefile b/Makefile index 89c980da..d276731b 100644 --- a/Makefile +++ b/Makefile @@ -41,5 +41,7 @@ dialyzer.plt.poncho: dialyzer.poncho: compile.poncho compile.protocols.poncho $(foreach dir, $(poncho_dirs), cd apps/$(dir) && mix dialyzer && cd ../..;) +build.engine: + cd apps/engine && mix deps.get && MIX_ENV=dev mix build package: cd apps/expert && mix package diff --git a/apps/engine/.formatter.exs b/apps/engine/.formatter.exs index b5eb9eee..4e725b36 100644 --- a/apps/engine/.formatter.exs +++ b/apps/engine/.formatter.exs @@ -3,30 +3,11 @@ current_directory = Path.dirname(__ENV__.file) import_deps = [:forge] -impossible_to_format = [ - Path.join([ - current_directory, - "test", - "fixtures", - "compilation_errors", - "lib", - "compilation_errors.ex" - ]), - Path.join([current_directory, "test", "fixtures", "parse_errors", "lib", "parse_errors.ex"]) -] - locals_without_parens = [with_progress: 2, with_progress: 3, defkey: 2, defkey: 3, with_wal: 2] [ locals_without_parens: locals_without_parens, export: [locals_without_parens: locals_without_parens], import_deps: import_deps, - inputs: - Enum.flat_map( - [ - Path.join(current_directory, "*.exs"), - Path.join(current_directory, "{lib,test}/**/*.{ex,exs}") - ], - &Path.wildcard(&1, match_dot: true) - ) -- impossible_to_format + inputs: ["*.exs", "{lib,test}/**/*.{ex,exs}"] ] diff --git a/apps/engine/lib/engine/engine.ex b/apps/engine/lib/engine/engine.ex index da25d7af..afe3c4e8 100644 --- a/apps/engine/lib/engine/engine.ex +++ b/apps/engine/lib/engine/engine.ex @@ -8,11 +8,8 @@ defmodule Engine do alias Engine.Api.Proxy alias Engine.CodeAction alias Engine.CodeIntelligence - alias Engine.ProjectNode alias Forge.Project - alias Mix.Tasks.Namespace - require Logger @excluded_apps [:patch, :nimble_parsec] @@ -67,21 +64,19 @@ defmodule Engine do def list_apps do for {app, _, _} <- :application.loaded_applications(), - not Namespace.Module.prefixed?(app), + not Forge.Namespace.Module.prefixed?(app), do: app end - def start_link(%Project{} = project) do - :ok = ensure_epmd_started() - start_net_kernel(project) - - apps_to_start = [:elixir | @allowed_apps] ++ [:runtime_tools] - node = Project.node_name(project) + def ensure_apps_started do + apps_to_start = [:elixir, :runtime_tools | @allowed_apps] - with {:ok, node_pid} <- ProjectNode.start(project, glob_paths()), - :ok <- ensure_apps_started(node, apps_to_start) do - {:ok, node, node_pid} - end + Enum.reduce_while(apps_to_start, :ok, fn app_name, _ -> + case :application.ensure_all_started(app_name) do + {:ok, _} -> {:cont, :ok} + error -> {:halt, error} + end + end) end def deps_paths do @@ -116,140 +111,4 @@ defmodule Engine do def set_project(%Project{} = project) do :persistent_term.put({__MODULE__, :project}, project) end - - defdelegate stop(project), to: ProjectNode - - def call(%Project{} = project, m, f, a \\ []) do - project - |> Project.node_name() - |> :erpc.call(m, f, a) - end - - def manager_node_name(%Project{} = project) do - :"manager-#{Project.name(project)}-#{Project.entropy(project)}@127.0.0.1" - end - - defp start_net_kernel(%Project{} = project) do - manager = manager_node_name(project) - :net_kernel.start(manager, %{name_domain: :longnames}) - end - - defp ensure_apps_started(node, app_names) do - Enum.reduce_while(app_names, :ok, fn app_name, _ -> - case :rpc.call(node, :application, :ensure_all_started, [app_name]) do - {:ok, _} -> {:cont, :ok} - error -> {:halt, error} - end - end) - end - - defp glob_paths do - for entry <- :code.get_path(), - entry_string = List.to_string(entry), - entry_string != ".", - Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do - entry - end - end - - def elixir_executable(%Project{} = project) do - root_path = Project.root_path(project) - - {path_result, env} = - with nil <- version_manager_path_and_env("asdf", root_path), - nil <- version_manager_path_and_env("mise", root_path), - nil <- version_manager_path_and_env("rtx", root_path) do - {File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()} - end - - case path_result do - nil -> - {:error, :no_elixir} - - executable when is_binary(executable) -> - {:ok, executable, env} - end - end - - defp app_globs do - app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) - ["/**/priv" | app_globs] - end - - defp ensure_epmd_started do - case System.cmd("epmd", ~w(-daemon)) do - {"", 0} -> - :ok - - _ -> - {:error, :epmd_failed} - end - end - - defp version_manager_path_and_env(manager, root_path) do - with true <- is_binary(System.find_executable(manager)), - env = reset_env(manager, root_path), - {path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do - {String.trim(path), env} - else - _ -> - nil - end - end - - # We launch expert by asking the version managers to provide an environment, - # which contains path munging. This initial environment is present in the running - # VM, and needs to be undone so we can find the correct elixir executable in the project. - defp reset_env("asdf", _root_path) do - orig_path = System.get_env("PATH_SAVE", System.get_env("PATH")) - - Enum.map(System.get_env(), fn - {"ASDF_ELIXIR_VERSION", _} -> {"ASDF_ELIXIR_VERSION", nil} - {"ASDF_ERLANG_VERSION", _} -> {"ASDF_ERLANG_VERSION", nil} - {"PATH", _} -> {"PATH", orig_path} - other -> other - end) - end - - defp reset_env("rtx", root_path) do - {env, _} = System.cmd("rtx", ~w(env -s bash), cd: root_path) - - env - |> String.trim() - |> String.split("\n") - |> Enum.map(fn - "export " <> key_and_value -> - [key, value] = - key_and_value - |> String.split("=", parts: 2) - |> Enum.map(&String.trim/1) - - {key, value} - - _ -> - nil - end) - |> Enum.reject(&is_nil/1) - end - - defp reset_env("mise", root_path) do - {env, _} = System.cmd("mise", ~w(env -s bash), cd: root_path) - - env - |> String.trim() - |> String.split("\n") - |> Enum.map(fn - "export " <> key_and_value -> - [key, value] = - key_and_value - |> String.split("=", parts: 2) - |> Enum.map(&String.trim/1) - - {key, value} - - _ -> - nil - end) - |> Enum.reject(&is_nil/1) - end end diff --git a/apps/engine/lib/engine/engine/api.ex b/apps/engine/lib/engine/engine/api.ex deleted file mode 100644 index 93d3e55a..00000000 --- a/apps/engine/lib/engine/engine/api.ex +++ /dev/null @@ -1,141 +0,0 @@ -defmodule Engine.Api do - alias Engine.CodeIntelligence - alias Forge.Ast.Analysis - alias Forge.Ast.Env - alias Forge.Document - alias Forge.Document.Position - alias Forge.Document.Range - alias Forge.Project - - require Logger - - def schedule_compile(%Project{} = project, force?) do - Engine.call(project, Engine, :schedule_compile, [force?]) - end - - def compile_document(%Project{} = project, %Document{} = document) do - Engine.call(project, Engine, :compile_document, [document]) - end - - def expand_alias( - %Project{} = project, - segments_or_module, - %Analysis{} = analysis, - %Position{} = position - ) do - Engine.call(project, Engine, :expand_alias, [ - segments_or_module, - analysis, - position - ]) - end - - def list_modules(%Project{} = project) do - Engine.call(project, Engine, :list_modules) - end - - def project_apps(%Project{} = project) do - Engine.call(project, Engine, :list_apps) - end - - def format(%Project{} = project, %Document{} = document) do - Engine.call(project, Engine, :format, [document]) - end - - def code_actions( - %Project{} = project, - %Document{} = document, - %Range{} = range, - diagnostics, - kinds, - trigger_kind - ) do - Engine.call(project, Engine, :code_actions, [ - document, - range, - diagnostics, - kinds, - trigger_kind - ]) - end - - def complete(%Project{} = project, %Env{} = env) do - Logger.info("Completion for #{inspect(env.position)}") - Engine.call(project, Engine, :complete, [env]) - end - - def complete_struct_fields(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do - Engine.call(project, Engine, :complete_struct_fields, [ - analysis, - position - ]) - end - - def definition(%Project{} = project, %Document{} = document, %Position{} = position) do - Engine.call(project, Engine, :definition, [document, position]) - end - - def references( - %Project{} = project, - %Analysis{} = analysis, - %Position{} = position, - include_definitions? - ) do - Engine.call(project, Engine, :references, [ - analysis, - position, - include_definitions? - ]) - end - - def modules_with_prefix(%Project{} = project, prefix) - when is_binary(prefix) or is_atom(prefix) do - Engine.call(project, Engine, :modules_with_prefix, [prefix]) - end - - def modules_with_prefix(%Project{} = project, prefix, predicate) - when is_binary(prefix) or is_atom(prefix) do - Engine.call(project, Engine, :modules_with_prefix, [prefix, predicate]) - end - - @spec docs(Project.t(), module()) :: {:ok, CodeIntelligence.Docs.t()} | {:error, any()} - def docs(%Project{} = project, module, opts \\ []) when is_atom(module) do - Engine.call(project, Engine, :docs, [module, opts]) - end - - def register_listener(%Project{} = project, listener_pid, message_types) - when is_pid(listener_pid) and is_list(message_types) do - Engine.call(project, Engine, :register_listener, [ - listener_pid, - message_types - ]) - end - - def broadcast(%Project{} = project, message) do - Engine.call(project, Engine, :broadcast, [message]) - end - - def reindex(%Project{} = project) do - Engine.call(project, Engine, :reindex, []) - end - - def index_running?(%Project{} = project) do - Engine.call(project, Engine, :index_running?, []) - end - - def resolve_entity(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do - Engine.call(project, Engine, :resolve_entity, [analysis, position]) - end - - def struct_definitions(%Project{} = project) do - Engine.call(project, Engine, :struct_definitions, []) - end - - def document_symbols(%Project{} = project, %Document{} = document) do - Engine.call(project, Engine, :document_symbols, [document]) - end - - def workspace_symbols(%Project{} = project, query) do - Engine.call(project, Engine, :workspace_symbols, [query]) - end -end diff --git a/apps/engine/lib/engine/engine/api/proxy.ex b/apps/engine/lib/engine/engine/api/proxy.ex index 9b669d24..ebd6c791 100644 --- a/apps/engine/lib/engine/engine/api/proxy.ex +++ b/apps/engine/lib/engine/engine/api/proxy.ex @@ -33,13 +33,13 @@ defmodule Engine.Api.Proxy do alias Forge.Document alias Forge.Document.Changes - alias Engine.Api.Messages alias Engine.Api.Proxy.BufferingState alias Engine.Api.Proxy.DrainingState alias Engine.Api.Proxy.ProxyingState alias Engine.Api.Proxy.Records alias Engine.CodeMod alias Engine.Commands + alias Forge.EngineApi.Messages import Messages import Record diff --git a/apps/engine/lib/engine/engine/api/proxy/buffering_state.ex b/apps/engine/lib/engine/engine/api/proxy/buffering_state.ex index 6cd23c40..bee50d47 100644 --- a/apps/engine/lib/engine/engine/api/proxy/buffering_state.ex +++ b/apps/engine/lib/engine/engine/api/proxy/buffering_state.ex @@ -1,12 +1,11 @@ defmodule Engine.Api.Proxy.BufferingState do alias Forge.Document - alias Engine.Api alias Engine.Build alias Engine.Commands - import Api.Messages - import Api.Proxy.Records + import Forge.EngineApi.Messages + import Engine.Api.Proxy.Records defstruct initiator_pid: nil, buffer: [] diff --git a/apps/engine/lib/engine/engine/build.ex b/apps/engine/lib/engine/engine/build.ex index 32ab3e70..b9819a5a 100644 --- a/apps/engine/lib/engine/engine/build.ex +++ b/apps/engine/lib/engine/engine/build.ex @@ -4,7 +4,6 @@ defmodule Engine.Build do alias Engine.Build.Document.Compilers.HEEx alias Engine.Build.State - alias Forge.VM.Versions require Logger use GenServer @@ -13,15 +12,6 @@ defmodule Engine.Build do # Public interface - def path(%Project{} = project) do - %{elixir: elixir, erlang: erlang} = Versions.current() - erlang_major = erlang |> String.split(".") |> List.first() - elixir_version = Version.parse!(elixir) - elixir_major = "#{elixir_version.major}.#{elixir_version.minor}" - build_root = Project.build_path(project) - Path.join([build_root, "erl-#{erlang_major}", "elixir-#{elixir_major}"]) - end - def schedule_compile(%Project{} = _project, force? \\ false) do GenServer.cast(__MODULE__, {:compile, force?}) end diff --git a/apps/engine/lib/engine/engine/build/state.ex b/apps/engine/lib/engine/engine/build/state.ex index 93a50d4f..45969ec6 100644 --- a/apps/engine/lib/engine/engine/build/state.ex +++ b/apps/engine/lib/engine/engine/build/state.ex @@ -1,9 +1,9 @@ defmodule Engine.Build.State do alias Elixir.Features - alias Engine.Api.Messages alias Engine.Build alias Engine.Plugin alias Forge.Document + alias Forge.EngineApi.Messages alias Forge.Project alias Forge.VM.Versions @@ -60,7 +60,7 @@ defmodule Engine.Build.State do # If the project directory isn't there, for some reason the main build fails, so we create it here # to ensure that the build will succeed. project = state.project - build_path = Engine.Build.path(project) + build_path = Project.versioned_build_path(project) unless Versions.compatible?(build_path) do Logger.info("Build path #{build_path} was compiled on a previous erlang version. Deleting") diff --git a/apps/engine/lib/engine/engine/code_action.ex b/apps/engine/lib/engine/engine/code_action.ex index 08dc4dba..518bc61a 100644 --- a/apps/engine/lib/engine/engine/code_action.ex +++ b/apps/engine/lib/engine/engine/code_action.ex @@ -1,25 +1,9 @@ defmodule Engine.CodeAction do - alias Engine.CodeAction.Diagnostic alias Engine.CodeAction.Handlers + alias Forge.CodeAction.Diagnostic alias Forge.Document - alias Forge.Document.Changes alias Forge.Document.Range - require Logger - - defstruct [:title, :kind, :changes, :uri] - - @type code_action_kind :: GenLSP.Enumerations.CodeActionKind.t() - - @type trigger_kind :: GenLSP.Enumerations.CodeActionTriggerKind.t() - - @type t :: %__MODULE__{ - title: String.t(), - kind: code_action_kind, - changes: Changes.t(), - uri: Forge.uri() - } - @handlers [ Handlers.ReplaceRemoteFunction, Handlers.ReplaceWithUnderscore, @@ -29,18 +13,13 @@ defmodule Engine.CodeAction do Handlers.Refactorex ] - @spec new(Forge.uri(), String.t(), code_action_kind(), Changes.t()) :: t() - def new(uri, title, kind, changes) do - %__MODULE__{uri: uri, title: title, changes: changes, kind: kind} - end - @spec for_range( Document.t(), Range.t(), [Diagnostic.t()], - [code_action_kind] | :all, - trigger_kind - ) :: [t()] + [Forge.CodeAction.code_action_kind()] | :all, + Forge.CodeAction.trigger_kind() + ) :: [Forge.CodeAction.t()] def for_range(%Document{} = doc, %Range{} = range, diagnostics, kinds, trigger_kind) do Enum.flat_map(@handlers, fn handler -> if handle_kinds?(handler, kinds) and handle_trigger_kind?(handler, trigger_kind) do diff --git a/apps/engine/lib/engine/engine/code_action/handler.ex b/apps/engine/lib/engine/engine/code_action/handler.ex index bc352766..630b52e1 100644 --- a/apps/engine/lib/engine/engine/code_action/handler.ex +++ b/apps/engine/lib/engine/engine/code_action/handler.ex @@ -1,6 +1,6 @@ defmodule Engine.CodeAction.Handler do - alias Engine.CodeAction - alias Engine.CodeAction.Diagnostic + alias Forge.CodeAction + alias Forge.CodeAction.Diagnostic alias Forge.Document alias Forge.Document.Range diff --git a/apps/engine/lib/engine/engine/code_action/handlers/add_alias.ex b/apps/engine/lib/engine/engine/code_action/handlers/add_alias.ex index 1176c68b..48593468 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/add_alias.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/add_alias.ex @@ -5,7 +5,6 @@ defmodule Engine.CodeAction.Handlers.AddAlias do alias Engine.CodeMod alias Engine.Modules alias Engine.Search.Fuzzy - alias Engine.Search.Indexer.Entry alias Forge.Ast alias Forge.Ast.Analysis alias Forge.Ast.Analysis.Alias @@ -14,8 +13,8 @@ defmodule Engine.CodeAction.Handlers.AddAlias do alias Forge.Document.Position alias Forge.Document.Range alias Forge.Formats + alias Forge.Search.Indexer.Entry alias GenLSP.Enumerations.CodeActionKind - alias Mix.Tasks.Namespace alias Sourceror.Zipper @behaviour CodeAction.Handler @@ -66,7 +65,7 @@ defmodule Engine.CodeAction.Handlers.AddAlias do changes = Changes.new(analysis.document, replace_current_alias ++ alias_edits) - CodeAction.new( + Forge.CodeAction.new( analysis.document.uri, "alias #{Formats.module(potential_alias_module)}", CodeActionKind.quick_fix(), @@ -189,7 +188,7 @@ defmodule Engine.CodeAction.Handlers.AddAlias do for {mod, _, _} <- all_modules(), elixir_module?(mod), - not Namespace.Module.prefixed?(mod) do + not Forge.Namespace.Module.prefixed?(mod) do module_name = List.to_atom(mod) %Entry{ diff --git a/apps/engine/lib/engine/engine/code_action/handlers/organize_aliases.ex b/apps/engine/lib/engine/engine/code_action/handlers/organize_aliases.ex index bc1be535..9b79c34a 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/organize_aliases.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/organize_aliases.ex @@ -26,7 +26,7 @@ defmodule Engine.CodeAction.Handlers.OrganizeAliases do changes = Changes.new(doc, edits) [ - CodeAction.new( + Forge.CodeAction.new( doc.uri, "Organize aliases", CodeActionKind.source_organize_imports(), diff --git a/apps/engine/lib/engine/engine/code_action/handlers/refactorex.ex b/apps/engine/lib/engine/engine/code_action/handlers/refactorex.ex index 0b5921d4..0a67edb9 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/refactorex.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/refactorex.ex @@ -17,7 +17,7 @@ defmodule Engine.CodeAction.Handlers.Refactorex do |> Sourceror.Zipper.zip() |> Refactor.available_refactorings(target, true) |> Enum.map(fn refactoring -> - CodeAction.new( + Forge.CodeAction.new( doc.uri, refactoring.title, refactoring.kind, diff --git a/apps/engine/lib/engine/engine/code_action/handlers/remove_unused_alias.ex b/apps/engine/lib/engine/engine/code_action/handlers/remove_unused_alias.ex index 02d495e5..b30f81c5 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/remove_unused_alias.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/remove_unused_alias.ex @@ -23,9 +23,9 @@ defmodule Engine.CodeAction.Handlers.RemoveUnusedAlias do alias Engine.Analyzer alias Engine.CodeAction - alias Engine.CodeAction.Diagnostic alias Forge.Ast alias Forge.Ast.Analysis + alias Forge.CodeAction.Diagnostic alias Forge.Document alias Forge.Document.Changes alias Forge.Document.Edit @@ -54,7 +54,7 @@ defmodule Engine.CodeAction.Handlers.RemoveUnusedAlias do changes = Changes.new(document, [edit]) action = - CodeAction.new( + Forge.CodeAction.new( document.uri, "Remove alias #{module_name}", Enumerations.CodeActionKind.source(), diff --git a/apps/engine/lib/engine/engine/code_action/handlers/replace_remote_function.ex b/apps/engine/lib/engine/engine/code_action/handlers/replace_remote_function.ex index 7c6ac41d..e2c873a9 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/replace_remote_function.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/replace_remote_function.ex @@ -1,8 +1,8 @@ defmodule Engine.CodeAction.Handlers.ReplaceRemoteFunction do alias Engine.CodeAction - alias Engine.CodeAction.Diagnostic alias Engine.Modules alias Forge.Ast + alias Forge.CodeAction.Diagnostic alias Forge.Document alias Forge.Document.Changes alias Forge.Document.Edit @@ -34,7 +34,7 @@ defmodule Engine.CodeAction.Handlers.ReplaceRemoteFunction do def trigger_kind, do: :all @spec to_code_actions(Document.t(), non_neg_integer(), module(), String.t(), [atom()]) :: - [CodeAction.t()] + [Forge.CodeAction.t()] defp to_code_actions(%Document{} = doc, line_number, module, function, suggestions) do suggestions |> Enum.reduce([], fn suggestion, acc -> @@ -43,7 +43,7 @@ defmodule Engine.CodeAction.Handlers.ReplaceRemoteFunction do changes = Changes.new(doc, edits) code_action = - CodeAction.new( + Forge.CodeAction.new( doc.uri, "Rename to #{suggestion}", CodeActionKind.quick_fix(), diff --git a/apps/engine/lib/engine/engine/code_action/handlers/replace_with_underscore.ex b/apps/engine/lib/engine/engine/code_action/handlers/replace_with_underscore.ex index cf000e62..83762926 100644 --- a/apps/engine/lib/engine/engine/code_action/handlers/replace_with_underscore.ex +++ b/apps/engine/lib/engine/engine/code_action/handlers/replace_with_underscore.ex @@ -1,7 +1,7 @@ defmodule Engine.CodeAction.Handlers.ReplaceWithUnderscore do alias Engine.CodeAction - alias Engine.CodeAction.Diagnostic alias Forge.Ast + alias Forge.CodeAction.Diagnostic alias Forge.Document alias Forge.Document.Changes alias Forge.Document.Range @@ -16,7 +16,7 @@ defmodule Engine.CodeAction.Handlers.ReplaceWithUnderscore do with {:ok, variable_name, line_number} <- extract_variable_and_line(diagnostic), {:ok, changes} <- to_changes(doc, line_number, variable_name) do action = - CodeAction.new( + Forge.CodeAction.new( doc.uri, "Rename to _#{variable_name}", CodeActionKind.quick_fix(), diff --git a/apps/engine/lib/engine/engine/code_intelligence/definition.ex b/apps/engine/lib/engine/engine/code_intelligence/definition.ex index e4590def..d8f21302 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/definition.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/definition.ex @@ -1,7 +1,6 @@ defmodule Engine.CodeIntelligence.Definition do alias ElixirSense.Providers.Location, as: ElixirSenseLocation alias Engine.CodeIntelligence.Entity - alias Engine.Search.Indexer.Entry alias Engine.Search.Store alias Forge.Ast alias Forge.Ast.Analysis @@ -9,6 +8,7 @@ defmodule Engine.CodeIntelligence.Definition do alias Forge.Document.Location alias Forge.Document.Position alias Forge.Formats + alias Forge.Search.Indexer.Entry alias Forge.Text alias Future.Code diff --git a/apps/engine/lib/engine/engine/code_intelligence/docs.ex b/apps/engine/lib/engine/engine/code_intelligence/docs.ex index 6fb9bad2..39335f03 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/docs.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/docs.ex @@ -1,21 +1,12 @@ defmodule Engine.CodeIntelligence.Docs do + alias Engine.Modules + alias Forge.CodeIntelligence.Docs + alias Forge.CodeIntelligence.Docs.Entry + @moduledoc """ Utilities for fetching documentation for a compiled module. """ - alias Engine.CodeIntelligence.Docs.Entry - alias Engine.Modules - - defstruct [:module, :doc, functions_and_macros: [], callbacks: [], types: []] - - @type t :: %__MODULE__{ - module: module(), - doc: Entry.content(), - functions_and_macros: %{optional(atom()) => [Entry.t(:function | :macro)]}, - callbacks: %{optional(atom()) => [Entry.t(:callback)]}, - types: %{optional(atom()) => [Entry.t(:type)]} - } - @doc """ Fetches known documentation for the given module. @@ -26,7 +17,7 @@ defmodule Engine.CodeIntelligence.Docs do Defaults to `false`. """ - @spec for_module(module(), [opt]) :: {:ok, t} | {:error, any()} + @spec for_module(module(), [opt]) :: {:ok, Docs.t()} | {:error, any()} when opt: {:exclude_hidden, boolean()} def for_module(module, opts) when is_atom(module) do exclude_hidden? = Keyword.get(opts, :exclude_hidden, false) @@ -54,7 +45,7 @@ defmodule Engine.CodeIntelligence.Docs do callback_defs = beam |> Modules.fetch_callbacks() |> ok_or([]) type_defs = beam |> Modules.fetch_types() |> ok_or([]) - result = %__MODULE__{ + result = %Docs{ module: module, doc: Entry.parse_doc(module_doc), functions_and_macros: diff --git a/apps/engine/lib/engine/engine/code_intelligence/references.ex b/apps/engine/lib/engine/engine/code_intelligence/references.ex index 05727860..264b9a5d 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/references.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/references.ex @@ -2,13 +2,13 @@ defmodule Engine.CodeIntelligence.References do alias Engine.Analyzer alias Engine.CodeIntelligence.Entity alias Engine.CodeIntelligence.Variable - alias Engine.Search.Indexer.Entry alias Engine.Search.Store alias Engine.Search.Subject alias Forge.Ast.Analysis alias Forge.Document alias Forge.Document.Location alias Forge.Document.Position + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/code_intelligence/structs.ex b/apps/engine/lib/engine/engine/code_intelligence/structs.ex index bc2cd664..c9c2d96f 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/structs.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/structs.ex @@ -1,7 +1,7 @@ defmodule Engine.CodeIntelligence.Structs do alias Engine.Module.Loader - alias Engine.Search.Indexer.Entry alias Engine.Search.Store + alias Forge.Search.Indexer.Entry def for_project do if Mix.Project.get() do diff --git a/apps/engine/lib/engine/engine/code_intelligence/symbols.ex b/apps/engine/lib/engine/engine/code_intelligence/symbols.ex index 1b110e53..6e614f6e 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/symbols.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/symbols.ex @@ -1,11 +1,11 @@ defmodule Engine.CodeIntelligence.Symbols do - alias Engine.CodeIntelligence.Symbols alias Engine.Search alias Engine.Search.Indexer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Extractors + alias Forge.CodeIntelligence.Symbols alias Forge.Document alias Forge.Document.Range + alias Forge.Search.Indexer.Entry @block_types [ :ex_unit_describe, diff --git a/apps/engine/lib/engine/engine/code_intelligence/variable.ex b/apps/engine/lib/engine/engine/code_intelligence/variable.ex index 4f59c7c8..ad18c711 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/variable.ex +++ b/apps/engine/lib/engine/engine/code_intelligence/variable.ex @@ -1,9 +1,9 @@ defmodule Engine.CodeIntelligence.Variable do alias Engine.Search.Indexer - alias Engine.Search.Indexer.Entry alias Forge.Ast.Analysis alias Forge.Document.Position alias Forge.Document.Range + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/commands/reindex.ex b/apps/engine/lib/engine/engine/commands/reindex.ex index 3927e87f..1a9d8a9a 100644 --- a/apps/engine/lib/engine/engine/commands/reindex.ex +++ b/apps/engine/lib/engine/engine/commands/reindex.ex @@ -71,13 +71,13 @@ defmodule Engine.Commands.Reindex do """ alias Forge.Document + alias Forge.EngineApi alias Forge.Project - alias Engine.Api alias Engine.Search use GenServer - import Api.Messages + import EngineApi.Messages def start_link(opts) do [reindex_fun: fun] = Keyword.validate!(opts, reindex_fun: &do_reindex/1) diff --git a/apps/engine/lib/engine/engine/compilation/tracer.ex b/apps/engine/lib/engine/engine/compilation/tracer.ex index d4b3a59d..6d687984 100644 --- a/apps/engine/lib/engine/engine/compilation/tracer.ex +++ b/apps/engine/lib/engine/engine/compilation/tracer.ex @@ -2,7 +2,7 @@ defmodule Engine.Compilation.Tracer do alias Engine.Build alias Engine.Module.Loader - import Engine.Api.Messages + import Forge.EngineApi.Messages def trace({:on_module, module_binary, _filename}, %Macro.Env{} = env) do message = extract_module_updated(env.module, module_binary, env.file) diff --git a/apps/engine/lib/engine/engine/completion.ex b/apps/engine/lib/engine/engine/completion.ex index 26092c3b..30895ab5 100644 --- a/apps/engine/lib/engine/engine/completion.ex +++ b/apps/engine/lib/engine/engine/completion.ex @@ -5,7 +5,7 @@ defmodule Engine.Completion do alias Forge.Document.Position alias Engine.CodeMod.Format - alias Engine.Completion.Candidate + alias Forge.Completion.Candidate import Document.Line import Forge.Logging diff --git a/apps/engine/lib/engine/engine/dispatch.ex b/apps/engine/lib/engine/engine/dispatch.ex index be264d74..752136d3 100644 --- a/apps/engine/lib/engine/engine/dispatch.ex +++ b/apps/engine/lib/engine/engine/dispatch.ex @@ -9,7 +9,8 @@ defmodule Engine.Dispatch do alias Engine.Dispatch.Handlers alias Engine.Dispatch.PubSub - import Engine.Api.Messages + alias Forge.Project + import Forge.EngineApi.Messages @handlers [PubSub, Handlers.Indexing] @@ -78,7 +79,7 @@ defmodule Engine.Dispatch do defp progress_pid do project = Engine.get_project() - manager_node_name = Engine.manager_node_name(project) + manager_node_name = Project.manager_node_name(project) :rpc.call(manager_node_name, Expert.Project.Progress, :whereis, [project]) end end diff --git a/apps/engine/lib/engine/engine/dispatch/handler.ex b/apps/engine/lib/engine/engine/dispatch/handler.ex index 47d4fcbc..a9f56f3e 100644 --- a/apps/engine/lib/engine/engine/dispatch/handler.ex +++ b/apps/engine/lib/engine/engine/dispatch/handler.ex @@ -7,7 +7,7 @@ defmodule Engine.Dispatch.Handler do Define a handler, specifying the events to be handled and implementing `on_event/2`: defmodule MyHandler do - alias Engine.Api.Messages + alias Forge.EngineApi.Messages alias Engine.Dispatch.Handler import Messages diff --git a/apps/engine/lib/engine/engine/dispatch/handlers/indexing.ex b/apps/engine/lib/engine/engine/dispatch/handlers/indexing.ex index a6e6b74e..b877f7bc 100644 --- a/apps/engine/lib/engine/engine/dispatch/handlers/indexing.ex +++ b/apps/engine/lib/engine/engine/dispatch/handlers/indexing.ex @@ -1,9 +1,9 @@ defmodule Engine.Dispatch.Handlers.Indexing do - alias Engine.Api.Messages alias Engine.Commands alias Engine.Dispatch alias Engine.Search alias Forge.Document + alias Forge.EngineApi.Messages require Logger import Messages diff --git a/apps/engine/lib/engine/engine/mix.ex b/apps/engine/lib/engine/engine/mix.ex index b8757ed7..7b7d5c65 100644 --- a/apps/engine/lib/engine/engine/mix.ex +++ b/apps/engine/lib/engine/engine/mix.ex @@ -19,7 +19,7 @@ defmodule Engine.Mix do try do Mix.ProjectStack.post_config(prune_code_paths: false) - build_path = Engine.Build.path(project) + build_path = Project.versioned_build_path(project) project_root = Project.root_path(project) project diff --git a/apps/engine/lib/engine/engine/module_mappings.ex b/apps/engine/lib/engine/engine/module_mappings.ex index c2090dee..ed351c5d 100644 --- a/apps/engine/lib/engine/engine/module_mappings.ex +++ b/apps/engine/lib/engine/engine/module_mappings.ex @@ -39,7 +39,7 @@ defmodule Engine.ModuleMappings do end end - alias Engine.Api.Messages + alias Forge.EngineApi.Messages use GenServer diff --git a/apps/engine/lib/engine/engine/plugin.ex b/apps/engine/lib/engine/engine/plugin.ex index 2f590c99..e32d6bb2 100644 --- a/apps/engine/lib/engine/engine/plugin.ex +++ b/apps/engine/lib/engine/engine/plugin.ex @@ -1,10 +1,9 @@ defmodule Engine.Plugin do + alias Engine.Plugin.Runner alias Forge.Document + alias Forge.EngineApi.Messages alias Forge.Project - alias Engine.Api.Messages - alias Engine.Plugin.Runner - import Messages def diagnose(%Project{} = project, build_number) do diff --git a/apps/engine/lib/engine/engine/plugin/discovery.ex b/apps/engine/lib/engine/engine/plugin/discovery.ex index 02d3e317..d0d1d60f 100644 --- a/apps/engine/lib/engine/engine/plugin/discovery.ex +++ b/apps/engine/lib/engine/engine/plugin/discovery.ex @@ -12,13 +12,12 @@ defmodule Engine.Plugin.Discovery do alias Engine.Module.Loader alias Engine.Plugin.Runner - alias Mix.Tasks.Namespace require Logger @namespaced_document_module [:Forge, :Document] |> Module.concat() - |> Namespace.Module.apply() + |> Forge.Namespace.Module.apply() def run do for {app_name, _, _} <- :application.loaded_applications(), @@ -53,7 +52,7 @@ defmodule Engine.Plugin.Discovery do module |> :code.which() |> List.to_string() - |> Namespace.Transform.Beams.apply() + |> Forge.Namespace.Transform.Beams.apply() end defp unload_module(module) do diff --git a/apps/engine/lib/engine/engine/progress.ex b/apps/engine/lib/engine/engine/progress.ex index ab24e5e8..2c724b62 100644 --- a/apps/engine/lib/engine/engine/progress.ex +++ b/apps/engine/lib/engine/engine/progress.ex @@ -1,5 +1,5 @@ defmodule Engine.Progress do - import Engine.Api.Messages + import Forge.EngineApi.Messages @type label :: String.t() @type message :: String.t() diff --git a/apps/engine/lib/engine/engine/search/fuzzy.ex b/apps/engine/lib/engine/engine/search/fuzzy.ex index f6a071d2..1c1a9855 100644 --- a/apps/engine/lib/engine/engine/search/fuzzy.ex +++ b/apps/engine/lib/engine/engine/search/fuzzy.ex @@ -12,8 +12,8 @@ defmodule Engine.Search.Fuzzy do """ alias Engine.Search.Fuzzy.Scorer - alias Engine.Search.Indexer.Entry alias Forge.Project + alias Forge.Search.Indexer.Entry import Record defstruct subject_to_values: %{}, diff --git a/apps/engine/lib/engine/engine/search/indexer.ex b/apps/engine/lib/engine/engine/search/indexer.ex index 7d5994bc..783220ce 100644 --- a/apps/engine/lib/engine/engine/search/indexer.ex +++ b/apps/engine/lib/engine/engine/search/indexer.ex @@ -1,10 +1,10 @@ defmodule Engine.Search.Indexer do alias Engine.Progress alias Engine.Search.Indexer - alias Engine.Search.Indexer.Entry alias Forge.Identifier alias Forge.ProcessCache alias Forge.Project + alias Forge.Search.Indexer.Entry require ProcessCache diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/ecto_schema.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/ecto_schema.ex index 40102425..94e7a178 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/ecto_schema.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/ecto_schema.ex @@ -1,10 +1,10 @@ defmodule Engine.Search.Indexer.Extractors.EctoSchema do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Metadata alias Engine.Search.Indexer.Source.Reducer alias Forge.Ast alias Forge.Document.Position + alias Forge.Search.Indexer.Entry def extract( {:schema, meta, [{:__block__, _, [_source]} | _]} = schema_block, diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/ex_unit.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/ex_unit.ex index 4e1d4e29..f0d1c302 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/ex_unit.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/ex_unit.ex @@ -1,6 +1,5 @@ defmodule Engine.Search.Indexer.Extractors.ExUnit do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Metadata alias Engine.Search.Indexer.Source.Reducer alias Forge.Ast @@ -8,6 +7,7 @@ defmodule Engine.Search.Indexer.Extractors.ExUnit do alias Forge.Document.Position alias Forge.Document.Range alias Forge.Formats + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/function_definition.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/function_definition.ex index cafb61cc..e1972a65 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/function_definition.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/function_definition.ex @@ -1,10 +1,10 @@ defmodule Engine.Search.Indexer.Extractors.FunctionDefinition do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Source.Reducer alias Engine.Search.Subject alias Forge.Ast alias Forge.Ast.Analysis + alias Forge.Search.Indexer.Entry @function_definitions [:def, :defp] diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/function_reference.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/function_reference.ex index 801b3362..de8ac680 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/function_reference.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/function_reference.ex @@ -1,5 +1,4 @@ defmodule Engine.Search.Indexer.Extractors.FunctionReference do - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Extractors.FunctionDefinition alias Engine.Search.Indexer.Metadata alias Engine.Search.Indexer.Source.Reducer @@ -7,6 +6,7 @@ defmodule Engine.Search.Indexer.Extractors.FunctionReference do alias Forge.Ast alias Forge.Document.Position alias Forge.Document.Range + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/module.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/module.ex index 4883a059..32878336 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/module.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/module.ex @@ -3,15 +3,15 @@ defmodule Engine.Search.Indexer.Extractors.Module do Extracts module references and definitions from AST """ - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Metadata - alias Engine.Search.Indexer.Source.Block alias Engine.Search.Indexer.Source.Reducer alias Engine.Search.Subject alias Forge.Ast alias Forge.Document.Position alias Forge.Document.Range alias Forge.ProcessCache + alias Forge.Search.Indexer.Entry + alias Forge.Search.Indexer.Source.Block require Logger diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/module_attribute.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/module_attribute.ex index af6ab2f9..aa025bf9 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/module_attribute.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/module_attribute.ex @@ -4,11 +4,11 @@ defmodule Engine.Search.Indexer.Extractors.ModuleAttribute do """ alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Source.Reducer alias Engine.Search.Subject alias Forge.Document.Position alias Forge.Document.Range + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/struct_definition.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/struct_definition.ex index c9b2fec0..d10446e5 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/struct_definition.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/struct_definition.ex @@ -1,8 +1,8 @@ defmodule Engine.Search.Indexer.Extractors.StructDefinition do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Source.Reducer alias Forge.Ast + alias Forge.Search.Indexer.Entry def extract({:defstruct, _, [_fields]} = definition, %Reducer{} = reducer) do document = reducer.analysis.document diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/struct_reference.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/struct_reference.ex index 5621cd6c..1733684f 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/struct_reference.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/struct_reference.ex @@ -1,9 +1,9 @@ defmodule Engine.Search.Indexer.Extractors.StructReference do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Source.Reducer alias Engine.Search.Subject alias Forge.Ast + alias Forge.Search.Indexer.Entry require Logger diff --git a/apps/engine/lib/engine/engine/search/indexer/extractors/variable.ex b/apps/engine/lib/engine/engine/search/indexer/extractors/variable.ex index 41c32fe4..8679ba32 100644 --- a/apps/engine/lib/engine/engine/search/indexer/extractors/variable.ex +++ b/apps/engine/lib/engine/engine/search/indexer/extractors/variable.ex @@ -1,8 +1,8 @@ defmodule Engine.Search.Indexer.Extractors.Variable do alias Engine.Analyzer - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Source.Reducer alias Forge.Ast + alias Forge.Search.Indexer.Entry @defs [:def, :defmacro, :defp, :defmacrop] diff --git a/apps/engine/lib/engine/engine/search/indexer/source/reducer.ex b/apps/engine/lib/engine/engine/search/indexer/source/reducer.ex index 6c4580a8..6f94ebcb 100644 --- a/apps/engine/lib/engine/engine/search/indexer/source/reducer.ex +++ b/apps/engine/lib/engine/engine/search/indexer/source/reducer.ex @@ -6,12 +6,12 @@ defmodule Engine.Search.Indexer.Source.Reducer do with the AST's overall structure, and can focus on extracting content from it. """ - alias Engine.Search.Indexer.Entry alias Engine.Search.Indexer.Extractors alias Engine.Search.Indexer.Metadata - alias Engine.Search.Indexer.Source.Block alias Forge.Ast.Analysis alias Forge.Document.Position + alias Forge.Search.Indexer.Entry + alias Forge.Search.Indexer.Source.Block defstruct [:analysis, :entries, :position, :blocks, :block_hierarchy, extractors: []] diff --git a/apps/engine/lib/engine/engine/search/store.ex b/apps/engine/lib/engine/engine/search/store.ex index 190ce2a2..b6f160df 100644 --- a/apps/engine/lib/engine/engine/search/store.ex +++ b/apps/engine/lib/engine/engine/search/store.ex @@ -3,13 +3,13 @@ defmodule Engine.Search.Store do A persistent store for search entries """ - alias Forge.Project - - alias Engine.Api - alias Engine.Search.Indexer.Entry alias Engine.Search.Store alias Engine.Search.Store.State + alias Forge.EngineApi + alias Forge.Project + alias Forge.Search.Indexer.Entry + @type index_state :: :empty | :stale @type existing_entries :: [Entry.t()] @type new_entries :: [Entry.t()] @@ -36,7 +36,7 @@ defmodule Engine.Search.Store do 2500 ) - import Api.Messages + import EngineApi.Messages use GenServer require Logger diff --git a/apps/engine/lib/engine/engine/search/store/backend.ex b/apps/engine/lib/engine/engine/search/store/backend.ex index 02ca0652..d9f02b2e 100644 --- a/apps/engine/lib/engine/engine/search/store/backend.ex +++ b/apps/engine/lib/engine/engine/search/store/backend.ex @@ -2,8 +2,8 @@ defmodule Engine.Search.Store.Backend do @moduledoc """ A behaviour for search store backends """ - alias Engine.Search.Indexer.Entry alias Forge.Project + alias Forge.Search.Indexer.Entry @type version :: pos_integer() diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets.ex b/apps/engine/lib/engine/engine/search/store/backends/ets.ex index bafd854f..f5952ac3 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets.ex @@ -1,8 +1,8 @@ defmodule Engine.Search.Store.Backends.Ets do - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backend alias Engine.Search.Store.Backends.Ets.State alias Forge.Project + alias Forge.Search.Indexer.Entry use GenServer diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets/schema.ex b/apps/engine/lib/engine/engine/search/store/backends/ets/schema.ex index d7517a5b..e2b6b265 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets/schema.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets/schema.ex @@ -43,9 +43,9 @@ defmodule Engine.Search.Store.Backends.Ets.Schema do end end - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backends.Ets.Wal alias Forge.Project + alias Forge.Search.Indexer.Entry import Wal, only: :macros diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v1.ex b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v1.ex index 502f81d9..7bcc3df8 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v1.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v1.ex @@ -9,8 +9,8 @@ defmodule Engine.Search.Store.Backends.Ets.Schemas.V1 do """ - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backends.Ets.Schema + alias Forge.Search.Indexer.Entry use Schema, version: 1 diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v2.ex b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v2.ex index 13fec328..f582bc53 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v2.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v2.ex @@ -1,6 +1,6 @@ defmodule Engine.Search.Store.Backends.Ets.Schemas.V2 do - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backends.Ets.Schema + alias Forge.Search.Indexer.Entry require Entry use Schema, version: 2 diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v3.ex b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v3.ex index 19713ca2..d7579cde 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v3.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets/schemas/v3.ex @@ -1,6 +1,6 @@ defmodule Engine.Search.Store.Backends.Ets.Schemas.V3 do - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backends.Ets.Schema + alias Forge.Search.Indexer.Entry require Entry use Schema, version: 3 diff --git a/apps/engine/lib/engine/engine/search/store/backends/ets/state.ex b/apps/engine/lib/engine/engine/search/store/backends/ets/state.ex index 59ebb1ae..06c6ebdf 100644 --- a/apps/engine/lib/engine/engine/search/store/backends/ets/state.ex +++ b/apps/engine/lib/engine/engine/search/store/backends/ets/state.ex @@ -5,11 +5,11 @@ defmodule Engine.Search.Store.Backends.Ets.State do This backend uses an ETS table to store its data using a schema defined in the schemas submodule. """ - alias Engine.Search.Indexer.Entry alias Engine.Search.Store.Backends.Ets.Schema alias Engine.Search.Store.Backends.Ets.Schemas alias Engine.Search.Store.Backends.Ets.Wal alias Forge.Project + alias Forge.Search.Indexer.Entry @schema_order [ Schemas.LegacyV0, diff --git a/apps/engine/lib/engine/engine/search/store/state.ex b/apps/engine/lib/engine/engine/search/store/state.ex index 5179d328..7d2b6f90 100644 --- a/apps/engine/lib/engine/engine/search/store/state.ex +++ b/apps/engine/lib/engine/engine/search/store/state.ex @@ -1,9 +1,9 @@ defmodule Engine.Search.Store.State do - alias Engine.Api.Messages alias Engine.Dispatch alias Engine.Search.Fuzzy - alias Engine.Search.Indexer.Entry + alias Forge.EngineApi.Messages alias Forge.Project + alias Forge.Search.Indexer.Entry require Logger import Messages diff --git a/apps/engine/lib/mix/tasks/build.ex b/apps/engine/lib/mix/tasks/build.ex new file mode 100644 index 00000000..34727750 --- /dev/null +++ b/apps/engine/lib/mix/tasks/build.ex @@ -0,0 +1,22 @@ +defmodule Mix.Tasks.Build do + use Mix.Task + + def run(_args) do + Mix.Task.run("compile", []) + + namespaced_dir = "_build/#{Mix.env()}_ns" + + # Remove the existing namespaced dir + File.rm_rf(namespaced_dir) + # Create our namespaced area + File.mkdir_p(namespaced_dir) + + # Move our build artifacts from safekeeping to the build area + File.cp_r!("_build/#{Mix.env()}", namespaced_dir) + + # Namespace the new code + Mix.Task.run(:namespace, [ + namespaced_dir + ]) + end +end diff --git a/apps/engine/lib/mix/tasks/namespace/path.ex b/apps/engine/lib/mix/tasks/namespace/path.ex deleted file mode 100644 index aaf1778f..00000000 --- a/apps/engine/lib/mix/tasks/namespace/path.ex +++ /dev/null @@ -1,29 +0,0 @@ -defmodule Mix.Tasks.Namespace.Path do - alias Mix.Tasks.Namespace - - def apply(path) when is_list(path) do - path - |> List.to_string() - |> apply() - |> String.to_charlist() - end - - def apply(path) when is_binary(path) do - path - |> Path.split() - |> Enum.map(&replace_namespaced_apps/1) - |> Path.join() - end - - defp replace_namespaced_apps(path_component) do - Enum.reduce(Namespace.app_names(), path_component, fn app_name, path -> - if path == Atom.to_string(app_name) do - app_name - |> Namespace.Module.apply() - |> Atom.to_string() - else - path - end - end) - end -end diff --git a/apps/engine/mix.exs b/apps/engine/mix.exs index 0e622d55..c1083314 100644 --- a/apps/engine/mix.exs +++ b/apps/engine/mix.exs @@ -41,18 +41,18 @@ defmodule Engine.MixProject do defp deps do [ {:benchee, "~> 1.3", only: :test}, - {:forge, path: "../forge", env: Mix.env()}, Mix.Credo.dependency(), Mix.Dialyzer.dependency(), {:elixir_sense, github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, + {:forge, path: "../forge", env: Mix.env()}, {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:patch, "~> 0.15", only: [:dev, :test], optional: true, runtime: false}, {:path_glob, "~> 0.2", optional: true}, {:phoenix_live_view, "~> 1.0", only: [:test], optional: true, runtime: false}, {:sourceror, "~> 1.9"}, {:stream_data, "~> 1.1", only: [:test], runtime: false}, - {:refactorex, "~> 0.1.51"} + {:refactorex, "~> 0.1.52"} ] end diff --git a/apps/engine/mix.lock b/apps/engine/mix.lock index a9c56175..1c47e6ce 100644 --- a/apps/engine/mix.lock +++ b/apps/engine/mix.lock @@ -22,10 +22,10 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.17.0", "a0832e7af4ae0f4819e0c08dd2e7482364937aea6a8a997a679f2cbb7e026b2e", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6692046652a69a00a5a21d0b7e11fcf401064839d59d6b8787f23af55b1e6bc"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "refactorex": {:hex, :refactorex, "0.1.51", "74fc4603b31b600d78539ffea9fe170038aa8d471eec5aed261354c9734b4b27", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "aefa150ab2c0d62aa8c01c4d04b932806118790f09c4106e20883281932fba03"}, + "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, - "sourceror": {:hex, :sourceror, "1.9.0", "3bf5fe2d017aaabe3866d8a6da097dd7c331e0d2d54e59e21c2b066d47f1e08e", [:mix], [], "hexpm", "d20a9dd5efe162f0d75a307146faa2e17b823ea4f134f662358d70f0332fed82"}, + "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, diff --git a/apps/engine/priv/.gitkeep b/apps/engine/priv/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/apps/engine/test/engine/api/proxy/buffering_state_test.exs b/apps/engine/test/engine/api/proxy/buffering_state_test.exs index 49adc6cf..4c66f730 100644 --- a/apps/engine/test/engine/api/proxy/buffering_state_test.exs +++ b/apps/engine/test/engine/api/proxy/buffering_state_test.exs @@ -1,13 +1,12 @@ defmodule Engine.Api.Proxy.BufferingStateStateTest do - alias Forge.Document - - alias Engine.Api.Messages alias Engine.Api.Proxy alias Engine.Api.Proxy.BufferingState alias Engine.Build alias Engine.Commands + alias Forge.Document + alias Forge.EngineApi.Messages - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Messages import Proxy.Records diff --git a/apps/engine/test/engine/api/proxy_test.exs b/apps/engine/test/engine/api/proxy_test.exs index 69661b9c..a1e9b548 100644 --- a/apps/engine/test/engine/api/proxy_test.exs +++ b/apps/engine/test/engine/api/proxy_test.exs @@ -1,8 +1,4 @@ defmodule Engine.Api.ProxyTest do - alias Forge.Document - alias Forge.Document.Changes - - alias Engine.Api alias Engine.Api.Proxy alias Engine.Api.Proxy.BufferingState alias Engine.Api.Proxy.DrainingState @@ -11,14 +7,17 @@ defmodule Engine.Api.ProxyTest do alias Engine.Commands alias Engine.Dispatch + alias Forge.Document + alias Forge.Document.Changes + use ExUnit.Case use Patch - import Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures setup do - start_supervised!(Api.Proxy) + start_supervised!(Proxy) project = project() Engine.set_project(project) diff --git a/apps/engine/test/engine/build/state_test.exs b/apps/engine/test/engine/build/state_test.exs index 8041e8bb..b9e834fa 100644 --- a/apps/engine/test/engine/build/state_test.exs +++ b/apps/engine/test/engine/build/state_test.exs @@ -5,7 +5,7 @@ defmodule Engine.Build.StateTest do alias Forge.Document alias Forge.Project - import Engine.Test.Fixtures + import Forge.Test.Fixtures use ExUnit.Case, async: false use Patch diff --git a/apps/engine/test/engine/code_action/handlers/add_alias_test.exs b/apps/engine/test/engine/code_action/handlers/add_alias_test.exs index ed104f14..da5b601a 100644 --- a/apps/engine/test/engine/code_action/handlers/add_alias_test.exs +++ b/apps/engine/test/engine/code_action/handlers/add_alias_test.exs @@ -11,7 +11,7 @@ defmodule Engine.CodeAction.Handlers.AddAliasTest do import Forge.Test.CursorSupport import Forge.Test.CodeSigil - use Engine.Test.CodeMod.Case, enable_ast_conversion: false + use Forge.Test.CodeMod.Case, enable_ast_conversion: false use Patch setup do diff --git a/apps/engine/test/engine/code_action/handlers/organize_aliases_test.exs b/apps/engine/test/engine/code_action/handlers/organize_aliases_test.exs index 499eff0d..3ad96f7c 100644 --- a/apps/engine/test/engine/code_action/handlers/organize_aliases_test.exs +++ b/apps/engine/test/engine/code_action/handlers/organize_aliases_test.exs @@ -7,7 +7,7 @@ defmodule Engine.CodeAction.Handlers.OrganizeAliasesTest do import Forge.Test.CursorSupport import Forge.Test.CodeSigil - use Engine.Test.CodeMod.Case, enable_ast_conversion: false + use Forge.Test.CodeMod.Case, enable_ast_conversion: false use Patch setup do diff --git a/apps/engine/test/engine/code_action/handlers/refactorex_test.exs b/apps/engine/test/engine/code_action/handlers/refactorex_test.exs index a52388f9..c571bbab 100644 --- a/apps/engine/test/engine/code_action/handlers/refactorex_test.exs +++ b/apps/engine/test/engine/code_action/handlers/refactorex_test.exs @@ -1,5 +1,5 @@ defmodule Engine.CodeAction.Handlers.RefactorexTest do - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case alias Engine.CodeAction.Handlers.Refactorex alias Forge.Document diff --git a/apps/engine/test/engine/code_action/handlers/remove_unused_alias_test.exs b/apps/engine/test/engine/code_action/handlers/remove_unused_alias_test.exs index 1ce0adbc..d67c3679 100644 --- a/apps/engine/test/engine/code_action/handlers/remove_unused_alias_test.exs +++ b/apps/engine/test/engine/code_action/handlers/remove_unused_alias_test.exs @@ -1,14 +1,14 @@ defmodule Engine.CodeAction.Handlers.RemoveUnusedAliasTest do - alias Engine.CodeAction.Diagnostic alias Engine.CodeAction.Handlers.RemoveUnusedAlias alias Forge.Ast + alias Forge.CodeAction.Diagnostic alias Forge.Document alias Forge.Document.Range import Forge.Test.CursorSupport import Forge.Test.CodeSigil - use Engine.Test.CodeMod.Case, enable_ast_conversion: false + use Forge.Test.CodeMod.Case, enable_ast_conversion: false def apply_code_mod(original_text, _ast, options) do Document.Store.open("file:///file.ex", original_text, 1) diff --git a/apps/engine/test/engine/code_action/handlers/replace_remote_function_test.exs b/apps/engine/test/engine/code_action/handlers/replace_remote_function_test.exs index baed29e9..330d52a6 100644 --- a/apps/engine/test/engine/code_action/handlers/replace_remote_function_test.exs +++ b/apps/engine/test/engine/code_action/handlers/replace_remote_function_test.exs @@ -1,9 +1,9 @@ defmodule Engine.CodeAction.Handlers.ReplaceRemoteFunctionTest do - alias Engine.CodeAction.Diagnostic alias Engine.CodeAction.Handlers.ReplaceRemoteFunction + alias Forge.CodeAction.Diagnostic alias Forge.Document - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case setup do start_supervised!({Document.Store, derive: [analysis: &Forge.Ast.analyze/1]}) diff --git a/apps/engine/test/engine/code_action/handlers/replace_with_underscore_test.exs b/apps/engine/test/engine/code_action/handlers/replace_with_underscore_test.exs index e37e37b5..fa070222 100644 --- a/apps/engine/test/engine/code_action/handlers/replace_with_underscore_test.exs +++ b/apps/engine/test/engine/code_action/handlers/replace_with_underscore_test.exs @@ -1,9 +1,9 @@ defmodule Engine.CodeAction.Handlers.ReplaceWithUnderscoreTest do - alias Engine.CodeAction.Diagnostic alias Engine.CodeAction.Handlers.ReplaceWithUnderscore + alias Forge.CodeAction.Diagnostic alias Forge.Document - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case def apply_code_mod(original_text, _ast, options) do variable = Keyword.get(options, :variable, :unused) diff --git a/apps/engine/test/engine/code_intelligence/entity_test.exs b/apps/engine/test/engine/code_intelligence/entity_test.exs index 1c80b538..ea216b66 100644 --- a/apps/engine/test/engine/code_intelligence/entity_test.exs +++ b/apps/engine/test/engine/code_intelligence/entity_test.exs @@ -5,7 +5,7 @@ defmodule Engine.CodeIntelligence.EntityTest do import ExUnit.CaptureIO import Forge.Test.CodeSigil import Forge.Test.CursorSupport - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Forge.Test.RangeSupport use ExUnit.Case diff --git a/apps/engine/test/engine/code_intelligence/references_test.exs b/apps/engine/test/engine/code_intelligence/references_test.exs index 4ae8ab04..9e9ba968 100644 --- a/apps/engine/test/engine/code_intelligence/references_test.exs +++ b/apps/engine/test/engine/code_intelligence/references_test.exs @@ -10,7 +10,7 @@ defmodule Engine.CodeIntelligence.ReferencesTest do import Forge.Test.CodeSigil import Forge.Test.CursorSupport - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Forge.Test.RangeSupport import Forge.Test.EventualAssertions diff --git a/apps/engine/test/engine/code_intelligence/symbols_test.exs b/apps/engine/test/engine/code_intelligence/symbols_test.exs index afd1a656..1f1e9778 100644 --- a/apps/engine/test/engine/code_intelligence/symbols_test.exs +++ b/apps/engine/test/engine/code_intelligence/symbols_test.exs @@ -2,7 +2,7 @@ defmodule Engine.CodeIntelligence.SymbolsTest do alias Engine.CodeIntelligence.Symbols alias Engine.Search.Indexer.Extractors alias Engine.Search.Indexer.Source - alias Forge.Document + alias Forge.CodeIntelligence.Symbols.Document use ExUnit.Case use Patch @@ -11,13 +11,13 @@ defmodule Engine.CodeIntelligence.SymbolsTest do import Forge.Test.RangeSupport def document_symbols(code) do - doc = Document.new("file:///file.ex", code, 1) + doc = Forge.Document.new("file:///file.ex", code, 1) symbols = Symbols.for_document(doc) {symbols, doc} end def workspace_symbols(code) do - doc = Document.new("file:///file.ex", code, 1) + doc = Forge.Document.new("file:///file.ex", code, 1) {:ok, entries} = Source.index_document(doc, [ @@ -45,7 +45,7 @@ defmodule Engine.CodeIntelligence.SymbolsTest do describe "document symbols" do test "a top level module is found" do - {[%Symbols.Document{} = module], doc} = + {[%Document{} = module], doc} = ~q[ defmodule MyModule do end diff --git a/apps/engine/test/engine/code_mod/aliases_test.exs b/apps/engine/test/engine/code_mod/aliases_test.exs index 1d9a8db2..24c36cd3 100644 --- a/apps/engine/test/engine/code_mod/aliases_test.exs +++ b/apps/engine/test/engine/code_mod/aliases_test.exs @@ -4,7 +4,7 @@ defmodule Engine.CodeMod.AliasesTest do alias Engine.CodeMod.Aliases import Forge.Test.CursorSupport - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case use Patch setup do diff --git a/apps/engine/test/engine/code_mod/diff_test.exs b/apps/engine/test/engine/code_mod/diff_test.exs index 5f3094e8..ab3b7605 100644 --- a/apps/engine/test/engine/code_mod/diff_test.exs +++ b/apps/engine/test/engine/code_mod/diff_test.exs @@ -4,7 +4,7 @@ defmodule Engine.CodeMod.DiffTest do alias Forge.Document.Edit alias Forge.Document.Range - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case use Forge.Test.PositionSupport def edit(start_line, start_code_unit, end_line, end_code_unit, replacement) do diff --git a/apps/engine/test/engine/code_mod/format_test.exs b/apps/engine/test/engine/code_mod/format_test.exs index 6d60cc81..35870d25 100644 --- a/apps/engine/test/engine/code_mod/format_test.exs +++ b/apps/engine/test/engine/code_mod/format_test.exs @@ -1,14 +1,12 @@ # credo:disable-for-this-file Credo.Check.Readability.RedundantBlankLines defmodule Engine.CodeMod.FormatTest do - alias Engine.Api.Messages alias Engine.Build alias Engine.CodeMod.Format alias Forge.Document alias Forge.Project - use Engine.Test.CodeMod.Case, enable_ast_conversion: false + use Forge.Test.CodeMod.Case, enable_ast_conversion: false use Patch - import Messages def apply_code_mod(text, _ast, opts) do project = Keyword.get(opts, :project) @@ -52,13 +50,6 @@ defmodule Engine.CodeMod.FormatTest do ]t end - def with_real_project(%{project: project}) do - {:ok, _} = start_supervised({Engine.ProjectNodeSupervisor, project}) - {:ok, _, _} = Engine.start_link(project) - Engine.Api.register_listener(project, self(), [:all]) - :ok - end - def with_patched_build(_) do patch(Build, :compile_document, fn _, _ -> :ok end) :ok @@ -124,20 +115,4 @@ defmodule Engine.CodeMod.FormatTest do assert result == formatted() end end - - describe "emitting diagnostics" do - setup [:with_real_project] - - test "it should emit diagnostics when a syntax error occurs", %{project: project} do - text = ~q[ - def foo(a, ) do - end - ] - document = document("file:///file.ex", text) - Engine.Api.format(project, document) - - assert_receive file_diagnostics(diagnostics: [diagnostic]), 500 - assert diagnostic.message =~ "syntax error" - end - end end diff --git a/apps/engine/test/engine/commands/reindex_test.exs b/apps/engine/test/engine/commands/reindex_test.exs index eeddff64..d49e3ef8 100644 --- a/apps/engine/test/engine/commands/reindex_test.exs +++ b/apps/engine/test/engine/commands/reindex_test.exs @@ -4,7 +4,7 @@ defmodule Engine.Commands.ReindexTest do alias Forge.Document import Forge.Test.EventualAssertions - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Engine.Test.Entry.Builder use ExUnit.Case diff --git a/apps/engine/test/engine/completion/candidate/argument_names_test.exs b/apps/engine/test/engine/completion/candidate/argument_names_test.exs index 3a871f10..58aa4198 100644 --- a/apps/engine/test/engine/completion/candidate/argument_names_test.exs +++ b/apps/engine/test/engine/completion/candidate/argument_names_test.exs @@ -1,5 +1,5 @@ -defmodule Engine.Completion.Candidate.ArgumentNamesTest do - alias Engine.Completion.Candidate.ArgumentNames +defmodule Forge.Completion.Candidate.ArgumentNamesTest do + alias Forge.Completion.Candidate.ArgumentNames use ExUnit.Case import ArgumentNames diff --git a/apps/engine/test/engine/completion_test.exs b/apps/engine/test/engine/completion_test.exs index 2c5b232a..ddcad527 100644 --- a/apps/engine/test/engine/completion_test.exs +++ b/apps/engine/test/engine/completion_test.exs @@ -6,7 +6,7 @@ defmodule Engine.CompletionTest do import Forge.Test.CodeSigil import Forge.Test.CursorSupport - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Forge.Test.Quiet use ExUnit.Case, async: true diff --git a/apps/engine/test/engine/dispatch/handler_test.exs b/apps/engine/test/engine/dispatch/handler_test.exs index 927407fc..59c65995 100644 --- a/apps/engine/test/engine/dispatch/handler_test.exs +++ b/apps/engine/test/engine/dispatch/handler_test.exs @@ -1,6 +1,6 @@ defmodule Engine.Dispatch.HandlerTest do - alias Engine.Api.Messages alias Engine.Dispatch + alias Forge.EngineApi.Messages import Messages use ExUnit.Case diff --git a/apps/engine/test/engine/dispatch/handlers/indexer_test.exs b/apps/engine/test/engine/dispatch/handlers/indexer_test.exs index be21eb82..ca4776a3 100644 --- a/apps/engine/test/engine/dispatch/handlers/indexer_test.exs +++ b/apps/engine/test/engine/dispatch/handlers/indexer_test.exs @@ -1,15 +1,14 @@ defmodule Engine.Dispatch.Handlers.IndexingTest do alias Forge.Document - alias Engine.Api alias Engine.Commands alias Engine.Dispatch.Handlers.Indexing alias Engine.Search - import Api.Messages + import Forge.EngineApi.Messages import Forge.Test.CodeSigil import Forge.Test.EventualAssertions - import Engine.Test.Fixtures + import Forge.Test.Fixtures use ExUnit.Case use Patch diff --git a/apps/engine/test/engine/dispatch_test.exs b/apps/engine/test/engine/dispatch_test.exs index 0ddb2688..d261c489 100644 --- a/apps/engine/test/engine/dispatch_test.exs +++ b/apps/engine/test/engine/dispatch_test.exs @@ -1,6 +1,6 @@ defmodule Engine.DispatchTest do - alias Engine.Api.Messages alias Engine.Dispatch + alias Forge.EngineApi.Messages import Messages use ExUnit.Case diff --git a/apps/engine/test/engine/module_mappings_test.exs b/apps/engine/test/engine/module_mappings_test.exs index 3a4def9b..7ea19950 100644 --- a/apps/engine/test/engine/module_mappings_test.exs +++ b/apps/engine/test/engine/module_mappings_test.exs @@ -5,7 +5,7 @@ defmodule Engine.ModuleMappingsTest do use ExUnit.Case use Forge.Test.EventualAssertions - import Engine.Api.Messages + import Forge.EngineApi.Messages setup do start_supervised!(Dispatch) diff --git a/apps/engine/test/engine/progress_test.exs b/apps/engine/test/engine/progress_test.exs index 6daa4ad6..19b12a04 100644 --- a/apps/engine/test/engine/progress_test.exs +++ b/apps/engine/test/engine/progress_test.exs @@ -1,7 +1,7 @@ defmodule Engine.ProgressTest do alias Engine.Progress - import Engine.Api.Messages + import Forge.EngineApi.Messages use ExUnit.Case use Patch diff --git a/apps/engine/test/engine/search/indexer/extractors/function_definition_test.exs b/apps/engine/test/engine/search/indexer/extractors/function_definition_test.exs index 3d7941c1..2902face 100644 --- a/apps/engine/test/engine/search/indexer/extractors/function_definition_test.exs +++ b/apps/engine/test/engine/search/indexer/extractors/function_definition_test.exs @@ -1,5 +1,5 @@ defmodule Engine.Search.Indexer.Extractors.FunctionDefinitionTest do - alias Engine.Search.Indexer.Entry + alias Forge.Search.Indexer.Entry use Engine.Test.ExtractorCase def index(source) do diff --git a/apps/engine/test/engine/search/indexer_test.exs b/apps/engine/test/engine/search/indexer_test.exs index 29379b6c..25965518 100644 --- a/apps/engine/test/engine/search/indexer_test.exs +++ b/apps/engine/test/engine/search/indexer_test.exs @@ -1,12 +1,12 @@ defmodule Engine.Search.IndexerTest do alias Engine.Dispatch alias Engine.Search.Indexer - alias Engine.Search.Indexer.Entry alias Forge.Project + alias Forge.Search.Indexer.Entry use ExUnit.Case use Patch - import Engine.Test.Fixtures + import Forge.Test.Fixtures defmodule FakeBackend do def set_entries(entries) when is_list(entries) do diff --git a/apps/engine/test/engine/search/store/backends/ets/schema_test.exs b/apps/engine/test/engine/search/store/backends/ets/schema_test.exs index 67f1c82e..f52a03de 100644 --- a/apps/engine/test/engine/search/store/backends/ets/schema_test.exs +++ b/apps/engine/test/engine/search/store/backends/ets/schema_test.exs @@ -3,7 +3,7 @@ defmodule Engine.Search.Store.Backends.Ets.SchemaTest do alias Engine.Search.Store.Backends.Ets.Wal alias Forge.Project - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Wal, only: :macros use ExUnit.Case diff --git a/apps/engine/test/engine/search/store/backends/ets/wal_test.exs b/apps/engine/test/engine/search/store/backends/ets/wal_test.exs index ea812cec..8cff9050 100644 --- a/apps/engine/test/engine/search/store/backends/ets/wal_test.exs +++ b/apps/engine/test/engine/search/store/backends/ets/wal_test.exs @@ -1,7 +1,7 @@ defmodule Engine.Search.Store.Backends.Ets.WalTest do alias Engine.Search.Store.Backends.Ets.Wal - import Engine.Test.Fixtures + import Forge.Test.Fixtures use ExUnit.Case use Patch diff --git a/apps/engine/test/engine/search/store/backends/ets_test.exs b/apps/engine/test/engine/search/store/backends/ets_test.exs index 786c3a88..b6efd3e0 100644 --- a/apps/engine/test/engine/search/store/backends/ets_test.exs +++ b/apps/engine/test/engine/search/store/backends/ets_test.exs @@ -3,9 +3,9 @@ defmodule Engine.Search.Store.Backend.EtsTest do alias Engine.Search.Store alias Engine.Search.Store.Backends alias Engine.Test.Entry - alias Engine.Test.Fixtures alias Forge.Project alias Forge.Test.EventualAssertions + alias Forge.Test.Fixtures use ExUnit.Case, async: false diff --git a/apps/engine/test/engine/search/store_test.exs b/apps/engine/test/engine/search/store_test.exs index 7628e096..0fbd2a71 100644 --- a/apps/engine/test/engine/search/store_test.exs +++ b/apps/engine/test/engine/search/store_test.exs @@ -1,16 +1,15 @@ defmodule Engine.Search.StoreTest do alias Engine.Dispatch alias Engine.Search.Indexer - alias Engine.Search.Indexer.Entry alias Engine.Search.Store alias Engine.Search.Store.Backends.Ets - alias Engine.Test.Entry - alias Engine.Test.Fixtures + alias Forge.Search.Indexer.Entry alias Forge.Test.EventualAssertions + alias Forge.Test.Fixtures use ExUnit.Case, async: false - import Entry.Builder + import Engine.Test.Entry.Builder import EventualAssertions import Fixtures import Forge.Test.CodeSigil @@ -246,7 +245,7 @@ defmodule Engine.Search.StoreTest do end test "findidng siblings of a non-existent entry" do - assert :error = Store.siblings(%Indexer.Entry{}) + assert :error = Store.siblings(%Entry{}) end test "finding a parent in a function" do @@ -324,7 +323,7 @@ defmodule Engine.Search.StoreTest do end test "finding a non-existent entry" do - assert Store.parent(%Indexer.Entry{}) == :error + assert Store.parent(%Entry{}) == :error end end end diff --git a/apps/engine/test/fixtures/project/.expert/project.log b/apps/engine/test/fixtures/project/.expert/project.log new file mode 100644 index 00000000..629181fb --- /dev/null +++ b/apps/engine/test/fixtures/project/.expert/project.log @@ -0,0 +1,316 @@ +2025-06-27T17:55:07.711895-03:00 error: ** Connection attempt from node 'project-project-26527@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:12.896484-03:00 error: ** Connection attempt from node 'project-project-32261@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:15.909631-03:00 error: ** Connection attempt from node 'project-project-35017@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:18.838230-03:00 error: ** Connection attempt from node 'project-project-46677@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:21.590215-03:00 error: ** Connection attempt from node 'project-project-29311@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:24.319625-03:00 error: ** Connection attempt from node 'project-project-59859@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:26.971925-03:00 error: ** Connection attempt from node 'project-project-36574@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:33.457528-03:00 error: ** Connection attempt from node 'project-project-22049@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:34.006338-03:00 error: ** Connection attempt from node 'project-project-52198@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:36.647764-03:00 error: ** Connection attempt from node 'project-project-65331@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:39.641319-03:00 error: ** Connection attempt from node 'project-project-10217@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:40.121935-03:00 error: ** Connection attempt from node 'project-project-11812@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:40.746678-03:00 error: ** Connection attempt from node 'project-project-23124@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:41.243479-03:00 error: ** Connection attempt from node 'project-project-51954@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:41.849064-03:00 error: ** Connection attempt from node 'project-project-3854@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:42.334043-03:00 error: ** Connection attempt from node 'project-project-38967@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:45.296391-03:00 error: ** Connection attempt from node 'project-project-48366@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:48.038275-03:00 error: ** Connection attempt from node 'project-project-10642@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T17:55:52.843072-03:00 error: ** Connection attempt from node 'project-project-55893@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:15.134189-03:00 error: ** Connection attempt from node 'project-project-24330@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:15.627743-03:00 error: ** Connection attempt from node 'project-project-5224@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:16.187446-03:00 error: ** Connection attempt from node 'project-project-53048@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:17.007827-03:00 error: ** Connection attempt from node 'project-project-17072@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:17.488831-03:00 error: ** Connection attempt from node 'project-project-14105@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:20.440570-03:00 error: ** Connection attempt from node 'project-project-46561@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:24.535754-03:00 error: ** Connection attempt from node 'project-project-20685@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:27.230938-03:00 error: ** Connection attempt from node 'project-project-64865@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:30.361143-03:00 error: ** Connection attempt from node 'project-project-10067@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:33.285299-03:00 error: ** Connection attempt from node 'project-project-57225@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:37.175945-03:00 error: ** Connection attempt from node 'project-project-24942@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:39.459734-03:00 error: ** Connection attempt from node 'project-project-3249@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:40.137355-03:00 error: ** Connection attempt from node 'project-project-52514@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:42.753815-03:00 error: ** Connection attempt from node 'project-project-33241@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:46.212046-03:00 error: ** Connection attempt from node 'project-project-26834@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:51.119782-03:00 error: ** Connection attempt from node 'project-project-14382@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:53.689614-03:00 error: ** Connection attempt from node 'project-project-26411@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:54.154988-03:00 error: ** Connection attempt from node 'project-project-29186@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:54.800252-03:00 error: ** Connection attempt from node 'project-project-39039@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:55.300953-03:00 error: ** Connection attempt from node 'project-project-29695@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:29:57.951961-03:00 error: ** Connection attempt from node 'project-project-56633@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:00.565596-03:00 error: ** Connection attempt from node 'project-project-49960@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:03.360042-03:00 error: ** Connection attempt from node 'project-project-29500@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:04.146295-03:00 error: ** Connection attempt from node 'project-project-25247@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:06.675842-03:00 error: ** Connection attempt from node 'project-project-31600@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:09.364095-03:00 error: ** Connection attempt from node 'project-project-43851@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:13.531055-03:00 error: ** Connection attempt from node 'project-project-25112@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:16.630546-03:00 error: ** Connection attempt from node 'project-project-47248@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:30:20.480177-03:00 error: ** Connection attempt from node 'project-project-27289@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:31:34.130765-03:00 error: ** Connection attempt from node 'project-project-4005@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:50.308512-03:00 error: ** Connection attempt from node 'project-project-31982@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:53.223099-03:00 error: ** Connection attempt from node 'project-project-36773@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:53.771141-03:00 error: ** Connection attempt from node 'project-project-18482@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:54.356758-03:00 error: ** Connection attempt from node 'project-project-33716@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:56.240617-03:00 error: ** Connection attempt from node 'project-project-16735@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:56.752989-03:00 error: ** Connection attempt from node 'project-project-60903@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:33:57.496397-03:00 error: ** Connection attempt from node 'project-project-43645@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:00.463373-03:00 error: ** Connection attempt from node 'project-project-867@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:03.363222-03:00 error: ** Connection attempt from node 'project-project-20894@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:06.987201-03:00 error: ** Connection attempt from node 'project-project-27924@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:09.837150-03:00 error: ** Connection attempt from node 'project-project-6851@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:12.537434-03:00 error: ** Connection attempt from node 'project-project-9643@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:21.597379-03:00 error: ** Connection attempt from node 'project-project-57549@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:24.329164-03:00 error: ** Connection attempt from node 'project-project-50478@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:27.187234-03:00 error: ** Connection attempt from node 'project-project-41856@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:30.317583-03:00 error: ** Connection attempt from node 'project-project-57905@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:33.705079-03:00 error: ** Connection attempt from node 'project-project-21018@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:36.490311-03:00 error: ** Connection attempt from node 'project-project-48028@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:37.084429-03:00 error: ** Connection attempt from node 'project-project-6561@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:37.594688-03:00 error: ** Connection attempt from node 'project-project-12023@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:38.262035-03:00 error: ** Connection attempt from node 'project-project-50692@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:38.741909-03:00 error: ** Connection attempt from node 'project-project-12898@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:42.765296-03:00 error: ** Connection attempt from node 'project-project-37322@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:45.725226-03:00 error: ** Connection attempt from node 'project-project-15104@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:48.662948-03:00 error: ** Connection attempt from node 'project-project-40533@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:53.402771-03:00 error: ** Connection attempt from node 'project-project-39335@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:56.468789-03:00 error: ** Connection attempt from node 'project-project-30678@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:34:59.352488-03:00 error: ** Connection attempt from node 'project-project-49892@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:02.396299-03:00 error: ** Connection attempt from node 'project-project-30012@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:05.134660-03:00 error: ** Connection attempt from node 'project-project-12591@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:08.017303-03:00 error: ** Connection attempt from node 'project-project-12880@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:12.113083-03:00 error: ** Connection attempt from node 'project-project-51847@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:15.859083-03:00 error: ** Connection attempt from node 'project-project-47422@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:16.781267-03:00 error: ** Connection attempt from node 'project-project-47541@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.381229-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.446313-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.478048-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.507177-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.516331-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.523616-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.549741-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.577522-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.603364-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.631534-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.659170-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.685438-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.711350-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.735556-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.762651-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.787368-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.814386-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.839745-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:39.864827-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.098541-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.110966-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.155192-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.192695-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.226304-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.247009-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.268402-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.276396-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.283776-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.292766-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.302045-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.335563-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.367376-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.374464-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.394682-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:45.403954-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:46.788279-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:46.872530-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:46.913108-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:46.921004-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:46.961931-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.755741-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.775795-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.789960-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.809047-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.826120-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.837240-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.848126-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.862722-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.971696-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:35:49.983605-03:00 error: ** Connection attempt from node 'testing-188@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:13.686999-03:00 error: ** Connection attempt from node 'project-project-40852@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:14.207684-03:00 error: ** Connection attempt from node 'project-project-62620@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:17.665038-03:00 error: ** Connection attempt from node 'project-project-27863@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:20.436590-03:00 error: ** Connection attempt from node 'project-project-15945@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:23.283528-03:00 error: ** Connection attempt from node 'project-project-17558@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:26.438036-03:00 error: ** Connection attempt from node 'project-project-53510@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:30.272303-03:00 error: ** Connection attempt from node 'project-project-3161@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:33.337858-03:00 error: ** Connection attempt from node 'project-project-43987@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:35.019659-03:00 error: ** Connection attempt from node 'project-project-5544@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:35.531024-03:00 error: ** Connection attempt from node 'project-project-56021@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:38.762228-03:00 error: ** Connection attempt from node 'project-project-58000@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:43.318530-03:00 error: ** Connection attempt from node 'project-project-11257@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:46.336622-03:00 error: ** Connection attempt from node 'project-project-54803@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:47.175450-03:00 error: ** Connection attempt from node 'project-project-61448@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:47.729386-03:00 error: ** Connection attempt from node 'project-project-47432@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:48.219851-03:00 error: ** Connection attempt from node 'project-project-27261@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:50.728239-03:00 error: ** Connection attempt from node 'project-project-26702@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:52.481478-03:00 error: ** Connection attempt from node 'project-project-46170@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:40:55.981156-03:00 error: ** Connection attempt from node 'project-project-11133@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:41:01.015731-03:00 error: ** Connection attempt from node 'project-project-35100@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:52.119740-03:00 error: ** Connection attempt from node 'project-project-52402@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:55.100009-03:00 error: ** Connection attempt from node 'project-project-2022@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:55.670557-03:00 error: ** Connection attempt from node 'project-project-56306@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:56.221305-03:00 error: ** Connection attempt from node 'project-project-34104@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:58.066639-03:00 error: ** Connection attempt from node 'project-project-12014@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:45:58.804467-03:00 error: ** Connection attempt from node 'project-project-41604@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:01.621791-03:00 error: ** Connection attempt from node 'project-project-57621@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:05.540622-03:00 error: ** Connection attempt from node 'project-project-5322@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:11.043053-03:00 error: ** Connection attempt from node 'project-project-64764@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:11.656228-03:00 error: ** Connection attempt from node 'project-project-12144@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:14.645900-03:00 error: ** Connection attempt from node 'project-project-25658@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:15.180148-03:00 error: ** Connection attempt from node 'project-project-2594@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:16.766352-03:00 error: ** Connection attempt from node 'project-project-27426@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:19.811064-03:00 error: ** Connection attempt from node 'project-project-60754@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:23.851477-03:00 error: ** Connection attempt from node 'project-project-28315@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:26.787005-03:00 error: ** Connection attempt from node 'project-project-57114@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:29.640918-03:00 error: ** Connection attempt from node 'project-project-37214@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:34.302339-03:00 error: ** Connection attempt from node 'project-project-10910@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:35.244006-03:00 error: ** Connection attempt from node 'project-project-9017@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:46:38.979533-03:00 error: ** Connection attempt from node 'project-project-56988@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:54:57.407300-03:00 error: ** Connection attempt from node 'project-project-11669@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.109815-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.181070-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.261077-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.300282-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.361251-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.391227-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.419457-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.447593-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.476726-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.503668-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.532139-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.558767-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.584428-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.612185-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.619565-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.626333-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.652431-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.677827-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.703832-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.728907-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.753909-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.781057-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.807711-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:31.834111-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.166517-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.275756-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.288543-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.296429-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.310599-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.326903-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.338208-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.349134-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.360958-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:33.377521-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.012155-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.024513-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.030869-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.038431-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.045974-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.074996-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.094059-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.132378-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.151854-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.158968-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.186761-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.213030-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.240819-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.260644-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.266864-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:56:39.274255-03:00 error: ** Connection attempt from node 'testing-469@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.556823-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.580976-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.594243-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.604988-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.712712-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.722279-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.733069-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.745224-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.757407-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:52.768445-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.072851-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.082735-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.107996-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.127987-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.155091-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.162020-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.169550-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.198022-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.225717-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.232322-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.239110-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.266270-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.285387-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.291652-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.309548-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:54.316188-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.595049-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.630982-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.674514-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.768613-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.825430-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.887142-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.949858-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:58:59.988691-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.155788-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.166185-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.200003-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.237849-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.268567-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.304545-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.334006-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.362049-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.391110-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.398686-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:00.427200-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:01.147568-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:01.220109-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:01.267201-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:01.276730-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:01.342179-03:00 error: ** Connection attempt from node 'testing-5@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:30.189967-03:00 error: ** Connection attempt from node 'project-project-816@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:33.350491-03:00 error: ** Connection attempt from node 'project-project-45514@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:33.820271-03:00 error: ** Connection attempt from node 'project-project-10082@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:35.354557-03:00 error: ** Connection attempt from node 'project-project-2125@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:38.153451-03:00 error: ** Connection attempt from node 'project-project-56808@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:40.834289-03:00 error: ** Connection attempt from node 'project-project-24775@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:41.382041-03:00 error: ** Connection attempt from node 'project-project-9405@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:43.093771-03:00 error: ** Connection attempt from node 'project-project-54644@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:43.608900-03:00 error: ** Connection attempt from node 'project-project-42776@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:44.550367-03:00 error: ** Connection attempt from node 'project-project-35491@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:50.028881-03:00 error: ** Connection attempt from node 'project-project-27394@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:52.703629-03:00 error: ** Connection attempt from node 'project-project-21778@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:56.742971-03:00 error: ** Connection attempt from node 'project-project-49850@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T19:59:59.856205-03:00 error: ** Connection attempt from node 'project-project-25657@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:02.728770-03:00 error: ** Connection attempt from node 'project-project-20179@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:06.644223-03:00 error: ** Connection attempt from node 'project-project-25334@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:09.702967-03:00 error: ** Connection attempt from node 'project-project-18836@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:12.469070-03:00 error: ** Connection attempt from node 'project-project-33598@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:16.220664-03:00 error: ** Connection attempt from node 'project-project-3278@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:22.316115-03:00 error: ** Connection attempt from node 'project-project-49741@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-27T20:00:36.768520-03:00 error: ** Connection attempt from node 'project-project-49128@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:17.852394-03:00 error: ** Connection attempt from node 'project-project-40911@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:20.984698-03:00 error: ** Connection attempt from node 'project-project-8880@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:21.530195-03:00 error: ** Connection attempt from node 'project-project-25006@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:23.241672-03:00 error: ** Connection attempt from node 'project-project-17904@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:23.827564-03:00 error: ** Connection attempt from node 'project-project-50291@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:25.355420-03:00 error: ** Connection attempt from node 'project-project-377@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:28.181544-03:00 error: ** Connection attempt from node 'project-project-15700@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:31.095768-03:00 error: ** Connection attempt from node 'project-project-41368@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:31.643356-03:00 error: ** Connection attempt from node 'project-project-15273@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:34.540522-03:00 error: ** Connection attempt from node 'project-project-30994@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:37.729694-03:00 error: ** Connection attempt from node 'project-project-22263@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:40.799733-03:00 error: ** Connection attempt from node 'project-project-57330@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:43.660094-03:00 error: ** Connection attempt from node 'project-project-41724@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:46.763882-03:00 error: ** Connection attempt from node 'project-project-32890@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:50.251812-03:00 error: ** Connection attempt from node 'project-project-50223@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:54.255895-03:00 error: ** Connection attempt from node 'project-project-20535@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T17:59:57.246481-03:00 error: ** Connection attempt from node 'project-project-56987@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T18:00:02.101814-03:00 error: ** Connection attempt from node 'project-project-42438@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T18:00:06.041655-03:00 error: ** Connection attempt from node 'project-project-64281@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T18:00:07.556026-03:00 error: ** Connection attempt from node 'project-project-50094@127.0.0.1' rejected. Invalid challenge reply. ** +2025-06-29T18:00:24.856010-03:00 error: ** Connection attempt from node 'project-project-62223@127.0.0.1' rejected. Invalid challenge reply. ** diff --git a/apps/engine/test/support/test/entry/entry_builder.ex b/apps/engine/test/support/test/entry/entry_builder.ex index 82759c6e..e086795a 100644 --- a/apps/engine/test/support/test/entry/entry_builder.ex +++ b/apps/engine/test/support/test/entry/entry_builder.ex @@ -1,7 +1,7 @@ defmodule Engine.Test.Entry.Builder do - alias Engine.Search.Indexer.Entry alias Forge.Document.Range alias Forge.Identifier + alias Forge.Search.Indexer.Entry import Forge.Test.PositionSupport diff --git a/apps/expert/config/runtime.exs b/apps/expert/config/runtime.exs index f888c3b9..71f77d68 100644 --- a/apps/expert/config/runtime.exs +++ b/apps/expert/config/runtime.exs @@ -20,3 +20,5 @@ if Code.ensure_loaded?(LoggerFileBackend) do else :ok end + +require Logger diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index ab033f4a..e42aa25a 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -96,6 +96,7 @@ defmodule Expert do {:ok, request} <- Convert.to_native(request), {:ok, response} <- handler.handle(request, state.configuration), {:ok, response} <- Forge.Protocol.Convert.to_lsp(response) do + Logger.info("Handling request: #{request.method}") {:reply, response, lsp} else {:error, {:unhandled, _}} -> diff --git a/apps/expert/lib/expert/application.ex b/apps/expert/lib/expert/application.ex index f3537eff..4455bc61 100644 --- a/apps/expert/lib/expert/application.ex +++ b/apps/expert/lib/expert/application.ex @@ -7,6 +7,8 @@ defmodule Expert.Application do use Application + require Logger + @impl true def start(_type, _args) do children = [ diff --git a/apps/expert/lib/expert/code_intelligence/completion.ex b/apps/expert/lib/expert/code_intelligence/completion.ex index 868eb70c..f10a37fd 100644 --- a/apps/expert/lib/expert/code_intelligence/completion.ex +++ b/apps/expert/lib/expert/code_intelligence/completion.ex @@ -1,11 +1,12 @@ defmodule Expert.CodeIntelligence.Completion do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.Builder alias Expert.CodeIntelligence.Completion.Translatable alias Expert.Configuration + alias Expert.EngineApi alias Expert.Project.Intelligence alias Forge.Ast.Analysis alias Forge.Ast.Env + alias Forge.Completion.Candidate alias Forge.Document.Position alias Forge.Project alias Future.Code, as: Code @@ -13,7 +14,6 @@ defmodule Expert.CodeIntelligence.Completion do alias GenLSP.Structures.CompletionContext alias GenLSP.Structures.CompletionItem alias GenLSP.Structures.CompletionList - alias Mix.Tasks.Namespace require Logger @@ -77,12 +77,12 @@ defmodule Expert.CodeIntelligence.Completion do Env.in_context?(env, :struct_field_key) -> project - |> Engine.Api.complete_struct_fields(env.analysis, env.position) + |> EngineApi.complete_struct_fields(env.analysis, env.position) |> Enum.map(&Translatable.translate(&1, Builder, env)) true -> project - |> Engine.Api.complete(env) + |> EngineApi.complete(env) |> to_completion_items(project, env, context) end end @@ -165,7 +165,7 @@ defmodule Expert.CodeIntelligence.Completion do %CompletionContext{} = context ) do debug_local_completions(local_completions) - project_apps = Engine.Api.project_apps(project) + project_apps = EngineApi.project_apps(project) for result <- local_completions, displayable?(project, project_apps, result), @@ -215,7 +215,7 @@ defmodule Expert.CodeIntelligence.Completion do end cond do - Namespace.Module.prefixed?(suggested_module) -> + Forge.Namespace.Module.prefixed?(suggested_module) -> false # If we're working on the dependency, we should include it! @@ -320,7 +320,7 @@ defmodule Expert.CodeIntelligence.Completion do case completion do %{full_name: full_name} -> with_prefix = - Engine.Api.modules_with_prefix( + EngineApi.modules_with_prefix( env.project, full_name, {Kernel, :macro_exported?, [:__using__, 1]} @@ -340,7 +340,7 @@ defmodule Expert.CodeIntelligence.Completion do case completion do %{full_name: full_name} -> with_prefix = - Engine.Api.modules_with_prefix( + EngineApi.modules_with_prefix( env.project, full_name, {Kernel, :function_exported?, [:behaviour_info, 1]} @@ -375,7 +375,7 @@ defmodule Expert.CodeIntelligence.Completion do end defp typespec_or_type_candidate?(%Candidate.Function{} = function, %Env{} = env) do - case Engine.Api.expand_alias(env.project, [:__MODULE__], env.analysis, env.position) do + case EngineApi.expand_alias(env.project, [:__MODULE__], env.analysis, env.position) do {:ok, expanded} -> expanded == function.origin diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/bitstring_option.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/bitstring_option.ex index e8b1d162..01299aad 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/bitstring_option.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/bitstring_option.ex @@ -1,9 +1,9 @@ defmodule Expert.CodeIntelligence.Completion.Translations.BitstringOption do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations alias Forge.Ast.Env + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind require Logger diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/callable.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/callable.ex index 0eb2e190..b64b9190 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/callable.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/callable.ex @@ -1,8 +1,8 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Callable do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.Builder alias Expert.CodeIntelligence.Completion.SortScope alias Forge.Ast.Env + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind @callables [Candidate.Function, Candidate.Macro, Candidate.Callback, Candidate.Typespec] diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/callback.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/callback.ex index 384cfc07..5b9ad7ee 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/callback.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/callback.ex @@ -1,9 +1,9 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Callback do - alias Engine.Completion.Candidate.Callback alias Expert.CodeIntelligence.Completion.Builder alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Forge.Ast.Env + alias Forge.Completion.Candidate.Callback alias Forge.Document alias GenLSP.Enumerations.CompletionItemKind diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/function.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/function.ex index 332b0719..ae7b64fa 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/function.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/function.ex @@ -1,8 +1,8 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Function do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations alias Forge.Ast.Env + alias Forge.Completion.Candidate defimpl Translatable, for: Candidate.Function do def translate(function, _builder, %Env{} = env) do diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/macro.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/macro.ex index 2723ead4..baaed649 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/macro.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/macro.ex @@ -1,5 +1,4 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Macro do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations @@ -7,6 +6,7 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Macro do alias Expert.CodeIntelligence.Completion.Translations.Struct alias Forge.Ast alias Forge.Ast.Env + alias Forge.Completion.Candidate alias Forge.Document alias Forge.Document.Position alias GenLSP.Enumerations.CompletionItemKind diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/map_field.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/map_field.ex index b00ada08..bf37f51d 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/map_field.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/map_field.ex @@ -1,7 +1,7 @@ defmodule Expert.CodeIntelligence.Completion.Translations.MapField do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.Translatable alias Forge.Ast.Env + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind defimpl Translatable, for: Candidate.MapField do diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/module_attribute.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/module_attribute.ex index e80a052d..230e4eb4 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/module_attribute.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/module_attribute.ex @@ -1,10 +1,10 @@ defmodule Expert.CodeIntelligence.Completion.Translations.ModuleAttribute do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations alias Forge.Ast alias Forge.Ast.Env + alias Forge.Completion.Candidate alias Forge.Document.Position alias GenLSP.Enumerations.CompletionItemKind diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/module_or_behaviour.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/module_or_behaviour.ex index 0d25eda9..b8a4b8e4 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/module_or_behaviour.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/module_or_behaviour.ex @@ -1,10 +1,10 @@ defmodule Expert.CodeIntelligence.Completion.Translations.ModuleOrBehaviour do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations alias Expert.Project.Intelligence alias Forge.Ast.Env + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind defimpl Translatable, for: Candidate.Module do diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/struct_field.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/struct_field.ex index f8063bb9..1317e53a 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/struct_field.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/struct_field.ex @@ -1,9 +1,9 @@ defmodule Expert.CodeIntelligence.Completion.Translations.StructField do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations alias Forge.Ast.Env + alias Forge.Completion.Candidate alias Future.Code, as: Code alias GenLSP.Enumerations.CompletionItemKind diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/typespec.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/typespec.ex index 44b0f9c0..8530c0a7 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/typespec.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/typespec.ex @@ -1,8 +1,8 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Typespec do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.Translatable alias Expert.CodeIntelligence.Completion.Translations.Callable alias Forge.Ast.Env + alias Forge.Completion.Candidate defimpl Translatable, for: Candidate.Typespec do def translate(typespec, _builder, %Env{} = env) do diff --git a/apps/expert/lib/expert/code_intelligence/completion/translations/variable.ex b/apps/expert/lib/expert/code_intelligence/completion/translations/variable.ex index 5c5f7f3a..caac50d8 100644 --- a/apps/expert/lib/expert/code_intelligence/completion/translations/variable.ex +++ b/apps/expert/lib/expert/code_intelligence/completion/translations/variable.ex @@ -1,8 +1,8 @@ defmodule Expert.CodeIntelligence.Completion.Translations.Variable do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope alias Expert.CodeIntelligence.Completion.Translatable alias Forge.Ast.Env + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind defimpl Translatable, for: Candidate.Variable do diff --git a/apps/expert/lib/expert/engine_api.ex b/apps/expert/lib/expert/engine_api.ex new file mode 100644 index 00000000..83b04255 --- /dev/null +++ b/apps/expert/lib/expert/engine_api.ex @@ -0,0 +1,285 @@ +defmodule Expert.EngineApi do + alias Expert.ProjectNode + alias Forge.Ast.Analysis + alias Forge.Ast.Env + alias Forge.Document + alias Forge.Document.Position + alias Forge.Document.Range + alias Forge.Project + + alias Forge.CodeIntelligence + + require Logger + + def start_link(%Project{} = project) do + :ok = ensure_epmd_started() + start_net_kernel(project) + + node = Project.node_name(project) + + with {:ok, node_pid} <- ProjectNode.start(project, glob_paths()), + :ok <- ensure_apps_started(node) do + {:ok, node, node_pid} + end + end + + defp start_net_kernel(%Project{} = project) do + manager = Project.manager_node_name(project) + :net_kernel.start(manager, %{name_domain: :longnames}) + end + + defp ensure_apps_started(node) do + :rpc.call(node, Engine, :ensure_apps_started, []) + end + + defp ensure_epmd_started do + case System.cmd("epmd", ~w(-daemon)) do + {"", 0} -> + :ok + + _ -> + {:error, :epmd_failed} + end + end + + def call(%Project{} = project, m, f, a \\ []) do + project + |> Project.node_name() + |> :erpc.call(m, f, a) + end + + def elixir_executable(%Project{} = project) do + root_path = Project.root_path(project) + + {path_result, env} = + with nil <- version_manager_path_and_env("asdf", root_path), + nil <- version_manager_path_and_env("mise", root_path), + nil <- version_manager_path_and_env("rtx", root_path) do + {File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()} + end + + case path_result do + nil -> + {:error, :no_elixir} + + executable when is_binary(executable) -> + {:ok, executable, env} + end + end + + @excluded_apps [:patch, :nimble_parsec] + @allowed_apps [:engine | Mix.Project.deps_apps()] -- @excluded_apps + + defp app_globs do + app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) + ["/**/priv" | app_globs] + end + + def glob_paths do + for entry <- :code.get_path(), + entry_string = List.to_string(entry), + entry_string != ".", + Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do + entry + end + end + + defp version_manager_path_and_env(manager, root_path) do + with true <- is_binary(System.find_executable(manager)), + env = reset_env(manager, root_path), + {path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do + {String.trim(path), env} + else + _ -> + nil + end + end + + # We launch expert by asking the version managers to provide an environment, + # which contains path munging. This initial environment is present in the running + # VM, and needs to be undone so we can find the correct elixir executable in the project. + defp reset_env("asdf", _root_path) do + orig_path = System.get_env("PATH_SAVE", System.get_env("PATH")) + + Enum.map(System.get_env(), fn + {"ASDF_ELIXIR_VERSION", _} -> {"ASDF_ELIXIR_VERSION", nil} + {"ASDF_ERLANG_VERSION", _} -> {"ASDF_ERLANG_VERSION", nil} + {"PATH", _} -> {"PATH", orig_path} + other -> other + end) + end + + defp reset_env("rtx", root_path) do + {env, _} = System.cmd("rtx", ~w(env -s bash), cd: root_path) + + env + |> String.trim() + |> String.split("\n") + |> Enum.map(fn + "export " <> key_and_value -> + [key, value] = + key_and_value + |> String.split("=", parts: 2) + |> Enum.map(&String.trim/1) + + {key, value} + + _ -> + nil + end) + |> Enum.reject(&is_nil/1) + end + + defp reset_env("mise", root_path) do + {env, _} = System.cmd("mise", ~w(env -s bash), cd: root_path) + + env + |> String.trim() + |> String.split("\n") + |> Enum.map(fn + "export " <> key_and_value -> + [key, value] = + key_and_value + |> String.split("=", parts: 2) + |> Enum.map(&String.trim/1) + + {key, value} + + _ -> + nil + end) + |> Enum.reject(&is_nil/1) + end + + def schedule_compile(%Project{} = project, force?) do + call(project, Engine, :schedule_compile, [force?]) + end + + def compile_document(%Project{} = project, %Document{} = document) do + call(project, Engine, :compile_document, [document]) + end + + def expand_alias( + %Project{} = project, + segments_or_module, + %Analysis{} = analysis, + %Position{} = position + ) do + call(project, Engine, :expand_alias, [ + segments_or_module, + analysis, + position + ]) + end + + def list_modules(%Project{} = project) do + call(project, Engine, :list_modules) + end + + def project_apps(%Project{} = project) do + call(project, Engine, :list_apps) + end + + def format(%Project{} = project, %Document{} = document) do + call(project, Engine, :format, [document]) + end + + def code_actions( + %Project{} = project, + %Document{} = document, + %Range{} = range, + diagnostics, + kinds, + trigger_kind + ) do + call(project, Engine, :code_actions, [ + document, + range, + diagnostics, + kinds, + trigger_kind + ]) + end + + def complete(%Project{} = project, %Env{} = env) do + Logger.info("Completion for #{inspect(env.position)}") + call(project, Engine, :complete, [env]) + end + + def complete_struct_fields(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do + call(project, Engine, :complete_struct_fields, [ + analysis, + position + ]) + end + + def definition(%Project{} = project, %Document{} = document, %Position{} = position) do + call(project, Engine, :definition, [document, position]) + end + + def references( + %Project{} = project, + %Analysis{} = analysis, + %Position{} = position, + include_definitions? + ) do + call(project, Engine, :references, [ + analysis, + position, + include_definitions? + ]) + end + + def modules_with_prefix(%Project{} = project, prefix) + when is_binary(prefix) or is_atom(prefix) do + call(project, Engine, :modules_with_prefix, [prefix]) + end + + def modules_with_prefix(%Project{} = project, prefix, predicate) + when is_binary(prefix) or is_atom(prefix) do + call(project, Engine, :modules_with_prefix, [prefix, predicate]) + end + + @spec docs(Project.t(), module()) :: {:ok, CodeIntelligence.Docs.t()} | {:error, any()} + def docs(%Project{} = project, module, opts \\ []) when is_atom(module) do + call(project, Engine, :docs, [module, opts]) + end + + def register_listener(%Project{} = project, listener_pid, message_types) + when is_pid(listener_pid) and is_list(message_types) do + call(project, Engine, :register_listener, [ + listener_pid, + message_types + ]) + end + + def broadcast(%Project{} = project, message) do + call(project, Engine, :broadcast, [message]) + end + + def reindex(%Project{} = project) do + call(project, Engine, :reindex, []) + end + + def index_running?(%Project{} = project) do + call(project, Engine, :index_running?, []) + end + + def resolve_entity(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do + call(project, Engine, :resolve_entity, [analysis, position]) + end + + def struct_definitions(%Project{} = project) do + call(project, Engine, :struct_definitions, []) + end + + def document_symbols(%Project{} = project, %Document{} = document) do + call(project, Engine, :document_symbols, [document]) + end + + def workspace_symbols(%Project{} = project, query) do + call(project, Engine, :workspace_symbols, [query]) + end + + defdelegate stop(project), to: ProjectNode +end diff --git a/apps/expert/lib/expert/iex/helpers.ex b/apps/expert/lib/expert/iex/helpers.ex index 92dfe17b..270d0b6c 100644 --- a/apps/expert/lib/expert/iex/helpers.ex +++ b/apps/expert/lib/expert/iex/helpers.ex @@ -1,6 +1,6 @@ defmodule Expert.IEx.Helpers do - alias Engine.Search alias Expert.CodeIntelligence + alias Expert.EngineApi alias Forge.Ast alias Forge.Document alias Forge.Document.Position @@ -13,11 +13,7 @@ defmodule Expert.IEx.Helpers do alias Forge.Document alias Forge.Document.Position - alias Engine.Search import unquote(__MODULE__) - - Engine.Module.Loader.start_link(nil) - Engine.Dispatch.start_link([]) end end @@ -29,7 +25,7 @@ defmodule Expert.IEx.Helpers do def observer(project) do project |> ensure_project() - |> Engine.call(:observer, :start) + |> EngineApi.call(:observer, :start) end def doc(text) do @@ -54,27 +50,6 @@ defmodule Expert.IEx.Helpers do |> Document.new(text, 0) end - def search_store(project) do - project = ensure_project(project) - Engine.set_project(project) - - Search.Store.start_link( - project, - &Search.Indexer.create_index/1, - &Search.Indexer.update_index/2, - Search.Store.Backends.Ets - ) - end - - def search_entries(project) do - {:ok, entries} = - project - |> ensure_project() - |> Search.Indexer.create_index() - - entries - end - def pos(doc, line, character) do Position.new(doc, line, character) end @@ -82,7 +57,7 @@ defmodule Expert.IEx.Helpers do def compile_project(project) do project |> ensure_project() - |> Engine.Api.schedule_compile(true) + |> EngineApi.schedule_compile(true) end def compile_file(project, source) when is_binary(source) do @@ -94,7 +69,7 @@ defmodule Expert.IEx.Helpers do def compile_file(project, %Document{} = document) do project |> ensure_project() - |> Engine.Api.compile_document(document) + |> EngineApi.compile_document(document) end def complete(project, source, context \\ nil) diff --git a/apps/engine/lib/engine/engine/port.ex b/apps/expert/lib/expert/port.ex similarity index 89% rename from apps/engine/lib/engine/engine/port.ex rename to apps/expert/lib/expert/port.ex index c6815a9c..4b601387 100644 --- a/apps/engine/lib/engine/engine/port.ex +++ b/apps/expert/lib/expert/port.ex @@ -1,4 +1,4 @@ -defmodule Engine.Port do +defmodule Expert.Port do @moduledoc """ Utilities for launching ports in the context of a project """ @@ -17,11 +17,11 @@ defmodule Engine.Port do Launches elixir in a port. This function takes the project's context into account and looks for the executable via calling - `Engine.elixir_executable(project)`. Environment variables are also retrieved with that call. + `Expert.EngineApi.elixir_executable(project)`. Environment variables are also retrieved with that call. """ @spec open_elixir(Project.t(), open_opts()) :: port() def open_elixir(%Project{} = project, opts) do - {:ok, elixir_executable, environment_variables} = Engine.elixir_executable(project) + {:ok, elixir_executable, environment_variables} = Expert.EngineApi.elixir_executable(project) opts = opts @@ -62,8 +62,10 @@ defmodule Engine.Port do end def path({:unix, _}) do + require Logger + with :non_existing <- :code.where_is_file(~c"port_wrapper.sh") do - :engine + :expert |> :code.priv_dir() |> Path.join("port_wrapper.sh") |> Path.expand() diff --git a/apps/expert/lib/expert/project/diagnostics.ex b/apps/expert/lib/expert/project/diagnostics.ex index 0a123d63..babee274 100644 --- a/apps/expert/lib/expert/project/diagnostics.ex +++ b/apps/expert/lib/expert/project/diagnostics.ex @@ -1,6 +1,7 @@ defmodule Expert.Project.Diagnostics do - alias Engine.Api.Messages + alias Expert.EngineApi alias Expert.Project.Diagnostics.State + alias Forge.EngineApi.Messages alias Forge.Formats alias Forge.Project alias GenLSP.Notifications.TextDocumentPublishDiagnostics @@ -25,7 +26,7 @@ defmodule Expert.Project.Diagnostics do @impl GenServer def init([%Project{} = project]) do - Engine.Api.register_listener(project, self(), [ + EngineApi.register_listener(project, self(), [ file_diagnostics(), project_compile_requested(), project_compiled(), diff --git a/apps/expert/lib/expert/project/intelligence.ex b/apps/expert/lib/expert/project/intelligence.ex index 3bc82fd6..c5c16a64 100644 --- a/apps/expert/lib/expert/project/intelligence.ex +++ b/apps/expert/lib/expert/project/intelligence.ex @@ -58,11 +58,11 @@ defmodule Expert.Project.Intelligence do end end - alias Engine.Api + alias Expert.EngineApi alias Forge.Project use GenServer - import Api.Messages + import Forge.EngineApi.Messages @generations [ :self, @@ -167,7 +167,7 @@ defmodule Expert.Project.Intelligence do @impl GenServer def init([%Project{} = project]) do - Api.register_listener(project, self(), [ + EngineApi.register_listener(project, self(), [ project_index_ready(), module_updated(), struct_discovered() @@ -213,7 +213,7 @@ defmodule Expert.Project.Intelligence do @impl GenServer def handle_info(project_index_ready(), %State{} = state) do - {:ok, struct_definitions} = Api.struct_definitions(state.project) + {:ok, struct_definitions} = EngineApi.struct_definitions(state.project) state = Enum.reduce(struct_definitions, State.new(state.project), fn module, state -> diff --git a/apps/expert/lib/expert/project/node.ex b/apps/expert/lib/expert/project/node.ex index 69121482..b605f79d 100644 --- a/apps/expert/lib/expert/project/node.ex +++ b/apps/expert/lib/expert/project/node.ex @@ -13,6 +13,7 @@ defmodule Expert.Project.Node do alias Forge.Project + alias Expert.EngineApi alias Expert.Project.Progress require Logger @@ -60,7 +61,7 @@ defmodule Expert.Project.Node do @impl GenServer def handle_continue(:trigger_build, %State{} = state) do - Engine.Api.schedule_compile(state.project, true) + EngineApi.schedule_compile(state.project, true) {:noreply, state} end @@ -71,7 +72,7 @@ defmodule Expert.Project.Node do @impl GenServer def handle_cast(:trigger_build, %State{} = state) do - Engine.Api.schedule_compile(state.project, true) + EngineApi.schedule_compile(state.project, true) {:noreply, state} end @@ -90,15 +91,15 @@ defmodule Expert.Project.Node do # private api - def start_node(%Project{} = project) do - with {:ok, node, node_pid} <- Engine.start_link(project) do + defp start_node(%Project{} = project) do + with {:ok, node, node_pid} <- EngineApi.start_link(project) do Node.monitor(node, true) {:ok, State.new(project, node, node_pid)} end end defp delete_build_artifacts(%Project{} = project) do - build_path = Engine.Build.path(project) + build_path = Project.versioned_build_path(project) case File.rm_rf(build_path) do {:ok, _deleted} -> :ok diff --git a/apps/expert/lib/expert/project/progress.ex b/apps/expert/lib/expert/project/progress.ex index bfa9f151..73561c28 100644 --- a/apps/expert/lib/expert/project/progress.ex +++ b/apps/expert/lib/expert/project/progress.ex @@ -2,7 +2,7 @@ defmodule Expert.Project.Progress do alias Expert.Project.Progress.State alias Forge.Project - import Engine.Api.Messages + import Forge.EngineApi.Messages use GenServer diff --git a/apps/expert/lib/expert/project/progress/state.ex b/apps/expert/lib/expert/project/progress/state.ex index 75cf4226..f55b97a2 100644 --- a/apps/expert/lib/expert/project/progress/state.ex +++ b/apps/expert/lib/expert/project/progress/state.ex @@ -7,7 +7,7 @@ defmodule Expert.Project.Progress.State do alias GenLSP.Requests alias GenLSP.Structures - import Engine.Api.Messages + import Forge.EngineApi.Messages defstruct project: nil, progress_by_label: %{} diff --git a/apps/expert/lib/expert/project/progress/support.ex b/apps/expert/lib/expert/project/progress/support.ex index bcd590ba..148f5f27 100644 --- a/apps/expert/lib/expert/project/progress/support.ex +++ b/apps/expert/lib/expert/project/progress/support.ex @@ -2,7 +2,7 @@ defmodule Expert.Project.Progress.Support do alias Expert.Project.Progress alias Forge.Project - import Engine.Api.Messages + import Forge.EngineApi.Messages defmacro __using__(_) do quote do diff --git a/apps/expert/lib/expert/project/search_listener.ex b/apps/expert/lib/expert/project/search_listener.ex index f3c67043..e09b05e9 100644 --- a/apps/expert/lib/expert/project/search_listener.ex +++ b/apps/expert/lib/expert/project/search_listener.ex @@ -1,11 +1,11 @@ defmodule Expert.Project.SearchListener do - alias Engine.Api + alias Expert.EngineApi alias Forge.Formats alias Forge.Project alias Forge.Protocol.Id alias GenLSP.Requests - import Api.Messages + import Forge.EngineApi.Messages use GenServer require Logger @@ -20,7 +20,7 @@ defmodule Expert.Project.SearchListener do @impl GenServer def init([%Project{} = project]) do - Api.register_listener(project, self(), [ + EngineApi.register_listener(project, self(), [ project_reindex_requested(), project_reindexed() ]) diff --git a/apps/expert/lib/expert/project/supervisor.ex b/apps/expert/lib/expert/project/supervisor.ex index 27b39500..5e280987 100644 --- a/apps/expert/lib/expert/project/supervisor.ex +++ b/apps/expert/lib/expert/project/supervisor.ex @@ -1,10 +1,10 @@ defmodule Expert.Project.Supervisor do - alias Engine.ProjectNodeSupervisor alias Expert.Project.Diagnostics alias Expert.Project.Intelligence alias Expert.Project.Node alias Expert.Project.Progress alias Expert.Project.SearchListener + alias Expert.ProjectNodeSupervisor alias Forge.Project # TODO: this module is slightly weird diff --git a/apps/engine/lib/engine/engine/project_node.ex b/apps/expert/lib/expert/project_node.ex similarity index 97% rename from apps/engine/lib/engine/engine/project_node.ex rename to apps/expert/lib/expert/project_node.ex index 2d9e6c24..9fa63a9d 100644 --- a/apps/engine/lib/engine/engine/project_node.ex +++ b/apps/expert/lib/expert/project_node.ex @@ -1,4 +1,4 @@ -defmodule Engine.ProjectNode do +defmodule Expert.ProjectNode do alias Forge.Project require Logger @@ -39,7 +39,7 @@ defmodule Engine.ProjectNode do | path_append_arguments(paths) ] - port = Engine.Port.open_elixir(state.project, args: args) + port = Expert.Port.open_elixir(state.project, args: args) %{state | port: port, started_by: from} end @@ -103,7 +103,7 @@ defmodule Engine.ProjectNode do end end - alias Engine.ProjectNodeSupervisor + alias Expert.ProjectNodeSupervisor alias Forge.Document use GenServer diff --git a/apps/engine/lib/engine/engine/project_node_supervisor.ex b/apps/expert/lib/expert/project_node_supervisor.ex similarity index 90% rename from apps/engine/lib/engine/engine/project_node_supervisor.ex rename to apps/expert/lib/expert/project_node_supervisor.ex index cd7d3d13..529b2390 100644 --- a/apps/engine/lib/engine/engine/project_node_supervisor.ex +++ b/apps/expert/lib/expert/project_node_supervisor.ex @@ -1,7 +1,7 @@ -defmodule Engine.ProjectNodeSupervisor do +defmodule Expert.ProjectNodeSupervisor do use DynamicSupervisor - alias Engine.ProjectNode + alias Expert.ProjectNode alias Forge.Project @dialyzer {:no_return, start_link: 1} diff --git a/apps/expert/lib/expert/provider/handlers/code_action.ex b/apps/expert/lib/expert/provider/handlers/code_action.ex index 5be304f9..d116b4b9 100644 --- a/apps/expert/lib/expert/provider/handlers/code_action.ex +++ b/apps/expert/lib/expert/provider/handlers/code_action.ex @@ -1,6 +1,7 @@ defmodule Expert.Provider.Handlers.CodeAction do - alias Engine.CodeAction alias Expert.Configuration + alias Expert.EngineApi + alias Forge.CodeAction alias GenLSP.Requests alias GenLSP.Structures @@ -12,7 +13,7 @@ defmodule Expert.Provider.Handlers.CodeAction do diagnostics = Enum.map(params.context.diagnostics, &to_code_action_diagnostic/1) code_actions = - Engine.Api.code_actions( + EngineApi.code_actions( config.project, document, params.range, diff --git a/apps/expert/lib/expert/provider/handlers/code_lens.ex b/apps/expert/lib/expert/provider/handlers/code_lens.ex index 3218addb..62ccdd6e 100644 --- a/apps/expert/lib/expert/provider/handlers/code_lens.ex +++ b/apps/expert/lib/expert/provider/handlers/code_lens.ex @@ -1,5 +1,6 @@ defmodule Expert.Provider.Handlers.CodeLens do alias Expert.Configuration + alias Expert.EngineApi alias Expert.Provider.Handlers alias Forge.Document alias Forge.Document.Position @@ -55,6 +56,6 @@ defmodule Expert.Provider.Handlers.CodeLens do document_path = Path.expand(document.path) document_path == Project.mix_exs_path(project) and - not Engine.Api.index_running?(project) + not EngineApi.index_running?(project) end end diff --git a/apps/expert/lib/expert/provider/handlers/commands.ex b/apps/expert/lib/expert/provider/handlers/commands.ex index 875a8dcf..d18836f2 100644 --- a/apps/expert/lib/expert/provider/handlers/commands.ex +++ b/apps/expert/lib/expert/provider/handlers/commands.ex @@ -1,5 +1,6 @@ defmodule Expert.Provider.Handlers.Commands do alias Expert.Configuration + alias Expert.EngineApi alias Forge.Project alias GenLSP.Enumerations.ErrorCodes alias GenLSP.Requests @@ -41,7 +42,7 @@ defmodule Expert.Provider.Handlers.Commands do end defp reindex(%Project{} = project) do - case Engine.Api.reindex(project) do + case EngineApi.reindex(project) do :ok -> {:ok, "ok"} diff --git a/apps/expert/lib/expert/provider/handlers/document_symbols.ex b/apps/expert/lib/expert/provider/handlers/document_symbols.ex index d09feca0..a026a97a 100644 --- a/apps/expert/lib/expert/provider/handlers/document_symbols.ex +++ b/apps/expert/lib/expert/provider/handlers/document_symbols.ex @@ -1,7 +1,7 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do - alias Engine.Api - alias Engine.CodeIntelligence.Symbols alias Expert.Configuration + alias Expert.EngineApi + alias Forge.CodeIntelligence.Symbols alias Forge.Document alias GenLSP.Enumerations.SymbolKind alias GenLSP.Requests @@ -12,7 +12,7 @@ defmodule Expert.Provider.Handlers.DocumentSymbols do symbols = config.project - |> Api.document_symbols(document) + |> EngineApi.document_symbols(document) |> Enum.map(&to_response(&1, document)) {:ok, symbols} diff --git a/apps/expert/lib/expert/provider/handlers/find_references.ex b/apps/expert/lib/expert/provider/handlers/find_references.ex index 38c06a9b..d7318934 100644 --- a/apps/expert/lib/expert/provider/handlers/find_references.ex +++ b/apps/expert/lib/expert/provider/handlers/find_references.ex @@ -1,6 +1,6 @@ defmodule Expert.Provider.Handlers.FindReferences do - alias Engine.Api alias Expert.Configuration + alias Expert.EngineApi alias Forge.Ast alias Forge.Document alias GenLSP.Requests.TextDocumentReferences @@ -18,7 +18,7 @@ defmodule Expert.Provider.Handlers.FindReferences do locations = case Document.Store.fetch(document.uri, :analysis) do {:ok, _document, %Ast.Analysis{} = analysis} -> - Api.references(config.project, analysis, params.position, include_declaration?) + EngineApi.references(config.project, analysis, params.position, include_declaration?) _ -> nil diff --git a/apps/expert/lib/expert/provider/handlers/formatting.ex b/apps/expert/lib/expert/provider/handlers/formatting.ex index 0c4f86b6..9d9f043a 100644 --- a/apps/expert/lib/expert/provider/handlers/formatting.ex +++ b/apps/expert/lib/expert/provider/handlers/formatting.ex @@ -1,5 +1,6 @@ defmodule Expert.Provider.Handlers.Formatting do alias Expert.Configuration + alias Expert.EngineApi alias Forge.Document.Changes alias GenLSP.Requests alias GenLSP.Structures @@ -12,7 +13,7 @@ defmodule Expert.Provider.Handlers.Formatting do ) do document = Forge.Document.Container.context_document(params, nil) - case Engine.Api.format(config.project, document) do + case EngineApi.format(config.project, document) do {:ok, %Changes{} = document_edits} -> {:ok, document_edits} diff --git a/apps/expert/lib/expert/provider/handlers/go_to_definition.ex b/apps/expert/lib/expert/provider/handlers/go_to_definition.ex index 3115131e..b18e744d 100644 --- a/apps/expert/lib/expert/provider/handlers/go_to_definition.ex +++ b/apps/expert/lib/expert/provider/handlers/go_to_definition.ex @@ -1,5 +1,6 @@ defmodule Expert.Provider.Handlers.GoToDefinition do alias Expert.Configuration + alias Expert.EngineApi alias GenLSP.Requests alias GenLSP.Structures @@ -13,7 +14,7 @@ defmodule Expert.Provider.Handlers.GoToDefinition do ) do document = Forge.Document.Container.context_document(params, nil) - case Engine.Api.definition(config.project, document, params.position) do + case EngineApi.definition(config.project, document, params.position) do {:ok, native_location} -> {:ok, native_location} diff --git a/apps/expert/lib/expert/provider/handlers/hover.ex b/apps/expert/lib/expert/provider/handlers/hover.ex index 6697eba0..ce43e9c7 100644 --- a/apps/expert/lib/expert/provider/handlers/hover.ex +++ b/apps/expert/lib/expert/provider/handlers/hover.ex @@ -1,9 +1,10 @@ defmodule Expert.Provider.Handlers.Hover do - alias Engine.CodeIntelligence.Docs alias Expert.Configuration + alias Expert.EngineApi alias Expert.Provider.Markdown alias Forge.Ast alias Forge.Ast.Analysis + alias Forge.CodeIntelligence.Docs alias Forge.Document alias Forge.Document.Position alias Forge.Project @@ -37,11 +38,11 @@ defmodule Expert.Provider.Handlers.Hover do end defp resolve_entity(%Project{} = project, %Analysis{} = analysis, %Position{} = position) do - Engine.Api.resolve_entity(project, analysis, position) + EngineApi.resolve_entity(project, analysis, position) end defp hover_content({kind, module}, %Project{} = project) when kind in [:module, :struct] do - case Engine.Api.docs(project, module, exclude_hidden: false) do + case EngineApi.docs(project, module, exclude_hidden: false) do {:ok, %Docs{} = module_docs} -> header = module_header(kind, module_docs) types = module_header_types(kind, module_docs) @@ -64,7 +65,7 @@ defmodule Expert.Provider.Handlers.Hover do end defp hover_content({:call, module, fun, arity}, %Project{} = project) do - with {:ok, %Docs{} = module_docs} <- Engine.Api.docs(project, module), + with {:ok, %Docs{} = module_docs} <- EngineApi.docs(project, module), {:ok, entries} <- Map.fetch(module_docs.functions_and_macros, fun) do sections = entries @@ -77,7 +78,7 @@ defmodule Expert.Provider.Handlers.Hover do end defp hover_content({:type, module, type, arity}, %Project{} = project) do - with {:ok, %Docs{} = module_docs} <- Engine.Api.docs(project, module), + with {:ok, %Docs{} = module_docs} <- EngineApi.docs(project, module), {:ok, entries} <- Map.fetch(module_docs.types, type) do case Enum.find(entries, &(&1.arity == arity)) do %Docs.Entry{} = entry -> diff --git a/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex b/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex index 1b5211b0..e3154f23 100644 --- a/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex +++ b/apps/expert/lib/expert/provider/handlers/workspace_symbol.ex @@ -1,7 +1,7 @@ defmodule Expert.Provider.Handlers.WorkspaceSymbol do - alias Engine.Api - alias Engine.CodeIntelligence.Symbols alias Expert.Configuration + alias Expert.EngineApi + alias Forge.CodeIntelligence.Symbols alias GenLSP.Enumerations.SymbolKind alias GenLSP.Requests alias GenLSP.Structures @@ -15,7 +15,7 @@ defmodule Expert.Provider.Handlers.WorkspaceSymbol do symbols = if String.length(params.query) > 1 do config.project - |> Api.workspace_symbols(params.query) + |> EngineApi.workspace_symbols(params.query) |> tap(fn symbols -> Logger.info("syms #{inspect(Enum.take(symbols, 5))}") end) |> Enum.map(&to_response/1) else diff --git a/apps/expert/lib/expert/release.ex b/apps/expert/lib/expert/release.ex new file mode 100644 index 00000000..676f20a3 --- /dev/null +++ b/apps/expert/lib/expert/release.ex @@ -0,0 +1,21 @@ +defmodule Expert.Release do + def assemble(release) do + Mix.Task.run(:namespace, [release.path]) + + engine_path = Path.expand("../../../engine", __DIR__) + + source = Path.join([engine_path, "_build/dev_ns"]) + + dest = + Path.join([ + release.path, + "lib", + "xp_expert-#{release.version}", + "priv" + ]) + + File.cp_r!(source, dest) + + release + end +end diff --git a/apps/expert/lib/expert/state.ex b/apps/expert/lib/expert/state.ex index cfff6a19..c17a54b8 100644 --- a/apps/expert/lib/expert/state.ex +++ b/apps/expert/lib/expert/state.ex @@ -1,7 +1,7 @@ defmodule Expert.State do - alias Engine.Api alias Expert.CodeIntelligence alias Expert.Configuration + alias Expert.EngineApi alias Expert.Project alias Expert.Provider.Handlers alias Forge.Document @@ -12,7 +12,7 @@ defmodule Expert.State do require Logger - import Api.Messages + import Forge.EngineApi.Messages defstruct configuration: nil, initialized?: false, @@ -114,8 +114,8 @@ defmodule Expert.State do to_version: updated_source.version ) - Api.broadcast(project, updated_message) - Api.compile_document(state.configuration.project, updated_source) + EngineApi.broadcast(project, updated_message) + EngineApi.compile_document(state.configuration.project, updated_source) {:ok, state} error -> @@ -163,7 +163,7 @@ defmodule Expert.State do case Document.Store.save(uri) do :ok -> - Api.schedule_compile(state.configuration.project, false) + EngineApi.schedule_compile(state.configuration.project, false) {:ok, state} error -> @@ -190,7 +190,7 @@ defmodule Expert.State do Enum.each(params.changes, fn %GenLSP.Structures.FileEvent{} = change -> event = filesystem_event(project: Project, uri: change.uri, event_type: change.type) - Engine.Api.broadcast(project, event) + EngineApi.broadcast(project, event) end) {:ok, state} diff --git a/apps/expert/lib/mix/tasks/package.ex b/apps/expert/lib/mix/tasks/package.ex index f8fbf6e9..b8ad8bb3 100644 --- a/apps/expert/lib/mix/tasks/package.ex +++ b/apps/expert/lib/mix/tasks/package.ex @@ -67,7 +67,7 @@ defmodule Mix.Tasks.Package do """ alias Forge.VM.Versions - alias Mix.Tasks.Namespace + alias Forge.Namespace @options [ strict: [ diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index 976c9af2..e962553c 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -42,8 +42,6 @@ defmodule Expert.MixProject do [ Mix.Credo.dependency(), Mix.Dialyzer.dependency(), - {:elixir_sense, - github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, {:engine, path: "../engine", env: Mix.env()}, {:forge, path: "../forge", env: Mix.env()}, {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index 37728201..8dbf4c09 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -14,10 +14,10 @@ "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, "path_glob": {:hex, :path_glob, "0.2.0", "b9e34b5045cac5ecb76ef1aa55281a52bf603bf7009002085de40958064ca312", [:mix], [{:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "be2594cb4553169a1a189f95193d910115f64f15f0d689454bb4e8cfae2e7ebc"}, - "refactorex": {:hex, :refactorex, "0.1.51", "74fc4603b31b600d78539ffea9fe170038aa8d471eec5aed261354c9734b4b27", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "aefa150ab2c0d62aa8c01c4d04b932806118790f09c4106e20883281932fba03"}, + "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, - "sourceror": {:hex, :sourceror, "1.9.0", "3bf5fe2d017aaabe3866d8a6da097dd7c331e0d2d54e59e21c2b066d47f1e08e", [:mix], [], "hexpm", "d20a9dd5efe162f0d75a307146faa2e17b823ea4f134f662358d70f0332fed82"}, + "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, diff --git a/apps/engine/priv/port_wrapper.sh b/apps/expert/priv/port_wrapper.sh similarity index 100% rename from apps/engine/priv/port_wrapper.sh rename to apps/expert/priv/port_wrapper.sh diff --git a/apps/expert/test/engine/README.md b/apps/expert/test/engine/README.md new file mode 100644 index 00000000..21aee008 --- /dev/null +++ b/apps/expert/test/engine/README.md @@ -0,0 +1 @@ +While the Expert application does not depend on Engine as a dependency, and logically Engine should not depend on Expert, we still need to call the Engine from a client like the Expert application in order to test Engine specific functionality. Due to this, this folder with files that don't map to Expert exclusive modules exists. Also for this same reason, Expert does depend on Engine as a test dependency, so we can call Engine functions in tests. diff --git a/apps/engine/test/engine/build_test.exs b/apps/expert/test/engine/build_test.exs similarity index 96% rename from apps/engine/test/engine/build_test.exs rename to apps/expert/test/engine/build_test.exs index ffda124a..eaf6d057 100644 --- a/apps/engine/test/engine/build_test.exs +++ b/apps/expert/test/engine/build_test.exs @@ -1,15 +1,15 @@ defmodule Engine.BuildTest do alias Elixir.Features - - alias Engine.Api.Messages alias Engine.Build - alias Engine.ProjectNodeSupervisor + alias Expert.EngineApi + alias Expert.ProjectNodeSupervisor alias Forge.Document + alias Forge.EngineApi.Messages alias Forge.Plugin.V1.Diagnostic alias Forge.Project import Messages - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Forge.Test.DiagnosticSupport use ExUnit.Case use Patch @@ -29,7 +29,7 @@ defmodule Engine.BuildTest do end source = Document.new(uri, source_code, 0) - Engine.call(project, Build, :force_compile_document, [source]) + EngineApi.call(project, Build, :force_compile_document, [source]) end def with_project(project_name) do @@ -42,8 +42,8 @@ defmodule Engine.BuildTest do |> File.rm_rf() {:ok, _} = start_supervised({ProjectNodeSupervisor, project}) - {:ok, _, _} = Engine.start_link(project) - Engine.Api.register_listener(project, self(), [:all]) + {:ok, _, _} = EngineApi.start_link(project) + EngineApi.register_listener(project, self(), [:all]) {:ok, project} end @@ -72,7 +72,7 @@ defmodule Engine.BuildTest do describe "compiling a project" do test "sends a message when complete " do {:ok, project} = with_project(:project_metadata) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :success) assert_receive project_progress(label: "Building " <> project_name) @@ -82,7 +82,7 @@ defmodule Engine.BuildTest do test "receives metadata about the defined modules" do {:ok, project} = with_project(:project_metadata) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive module_updated(name: ProjectMetadata, functions: functions) assert {:zero_arity, 0} in functions @@ -94,7 +94,7 @@ defmodule Engine.BuildTest do describe "compiling an umbrella project" do test "it sends a message when compilation is complete" do {:ok, project} = with_project(:umbrella) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :success) assert_receive project_diagnostics(diagnostics: []) @@ -119,7 +119,7 @@ defmodule Engine.BuildTest do describe "compiling a project that has errors" do test "it reports the errors" do {:ok, project} = with_project(:compilation_errors) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :error) assert_receive project_diagnostics(diagnostics: [%Diagnostic.Result{}]) @@ -132,7 +132,7 @@ defmodule Engine.BuildTest do @feature_condition span_in_diagnostic?: false @tag execute_if(@feature_condition) test "stuff", %{project: project} do - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :error) assert_receive project_diagnostics(diagnostics: [%Diagnostic.Result{} = diagnostic]) @@ -144,7 +144,7 @@ defmodule Engine.BuildTest do @feature_condition span_in_diagnostic?: true @tag execute_if(@feature_condition) test "stuff when #{inspect(@feature_condition)}", %{project: project} do - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :error) assert_receive project_diagnostics(diagnostics: [%Diagnostic.Result{} = diagnostic]) @@ -160,7 +160,7 @@ defmodule Engine.BuildTest do describe "when compiling a project that has warnings" do test "it reports them" do {:ok, project} = with_project(:compilation_warnings) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(status: :success) assert_receive project_diagnostics(diagnostics: diagnostics) @@ -565,7 +565,7 @@ defmodule Engine.BuildTest do end def loaded?(project, module) do - Engine.call(project, Code, :ensure_loaded?, [module]) + EngineApi.call(project, Code, :ensure_loaded?, [module]) end describe "module sanitization" do @@ -675,7 +675,7 @@ defmodule Engine.BuildTest do describe "exceptions during compilation" do test "compiling a project with callback errors" do {:ok, project} = with_project(:compilation_callback_errors) - Engine.Api.schedule_compile(project, false) + EngineApi.schedule_compile(project, false) assert_receive project_compiled(status: :error) assert_receive project_diagnostics(diagnostics: [diagnostic]) diff --git a/apps/engine/test/engine/code_intelligence/definition_test.exs b/apps/expert/test/engine/code_intelligence/definition_test.exs similarity index 96% rename from apps/engine/test/engine/code_intelligence/definition_test.exs rename to apps/expert/test/engine/code_intelligence/definition_test.exs index e4ba6399..b2b849e6 100644 --- a/apps/engine/test/engine/code_intelligence/definition_test.exs +++ b/apps/expert/test/engine/code_intelligence/definition_test.exs @@ -1,12 +1,13 @@ defmodule Engine.CodeIntelligence.DefinitionTest do - alias Engine.ProjectNodeSupervisor alias Engine.Search + alias Expert.EngineApi + alias Expert.ProjectNodeSupervisor alias Forge.Document - import Engine.Api.Messages + import Forge.EngineApi.Messages import Forge.Test.CodeSigil import Forge.Test.CursorSupport - import Engine.Test.Fixtures + import Forge.Test.Fixtures import Forge.Test.RangeSupport use ExUnit.Case, async: false @@ -44,10 +45,10 @@ defmodule Engine.CodeIntelligence.DefinitionTest do project = project(:navigations) start_supervised!({Document.Store, derive: [analysis: &Forge.Ast.analyze/1]}) {:ok, _} = start_supervised({ProjectNodeSupervisor, project}) - {:ok, _, _} = Engine.start_link(project) + {:ok, _, _} = EngineApi.start_link(project) - Engine.Api.register_listener(project, self(), [:all]) - Engine.Api.schedule_compile(project, true) + EngineApi.register_listener(project, self(), [:all]) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(), 5000 assert_receive project_index_ready(), 5000 @@ -452,7 +453,7 @@ defmodule Engine.CodeIntelligence.DefinitionTest do {:ok, document} <- subject_module(project, code), :ok <- index(project, referenced_uri), {:ok, location} <- - Engine.Api.definition(project, document, position) do + EngineApi.definition(project, document, position) do if is_list(location) do {:ok, Enum.map(location, &{&1.document.uri, decorate(&1.document, &1.range)})} else @@ -463,12 +464,12 @@ defmodule Engine.CodeIntelligence.DefinitionTest do defp index(project, referenced_uris) when is_list(referenced_uris) do entries = Enum.flat_map(referenced_uris, &do_index/1) - Engine.call(project, Search.Store, :replace, [entries]) + EngineApi.call(project, Search.Store, :replace, [entries]) end defp index(project, referenced_uri) do entries = do_index(referenced_uri) - Engine.call(project, Search.Store, :replace, [entries]) + EngineApi.call(project, Search.Store, :replace, [entries]) end defp do_index(referenced_uri) do diff --git a/apps/engine/test/remote_control_test.exs b/apps/expert/test/engine/engine_test.exs similarity index 82% rename from apps/engine/test/remote_control_test.exs rename to apps/expert/test/engine/engine_test.exs index 6ac1e131..4c512c23 100644 --- a/apps/engine/test/remote_control_test.exs +++ b/apps/expert/test/engine/engine_test.exs @@ -1,19 +1,20 @@ defmodule EngineTest do + alias Expert.EngineApi alias Forge.Document alias Forge.Project use ExUnit.Case use Forge.Test.EventualAssertions - import Engine.Test.Fixtures + import Forge.Test.Fixtures def start_project(%Project{} = project) do - start_supervised!({Engine.ProjectNodeSupervisor, project}) - assert {:ok, _, _} = Engine.start_link(project) + start_supervised!({Expert.ProjectNodeSupervisor, project}) + assert {:ok, _, _} = EngineApi.start_link(project) :ok end def engine_cwd(project) do - Engine.call(project, File, :cwd!, []) + EngineApi.call(project, File, :cwd!, []) end describe "detecting an umbrella app" do diff --git a/apps/expert/test/expert/code_intelligence/completion/builder_test.exs b/apps/expert/test/expert/code_intelligence/completion/builder_test.exs index 01c8dca0..66552733 100644 --- a/apps/expert/test/expert/code_intelligence/completion/builder_test.exs +++ b/apps/expert/test/expert/code_intelligence/completion/builder_test.exs @@ -8,7 +8,7 @@ defmodule Expert.CodeIntelligence.Completion.BuilderTest do import Expert.CodeIntelligence.Completion.Builder import Forge.Test.CursorSupport - import Engine.Test.Fixtures + import Forge.Test.Fixtures def new_env(text) do project = project() diff --git a/apps/expert/test/expert/code_intelligence/completion/translations/module_or_behaviour_test.exs b/apps/expert/test/expert/code_intelligence/completion/translations/module_or_behaviour_test.exs index ee03fcdc..4f795f33 100644 --- a/apps/expert/test/expert/code_intelligence/completion/translations/module_or_behaviour_test.exs +++ b/apps/expert/test/expert/code_intelligence/completion/translations/module_or_behaviour_test.exs @@ -1,4 +1,5 @@ defmodule Expert.CodeIntelligence.Completion.Translations.ModuleOrBehaviourTest do + alias Expert.EngineApi alias GenLSP.Enumerations.CompletionItemKind alias GenLSP.Enumerations.InsertTextFormat @@ -263,7 +264,7 @@ defmodule Expert.CodeIntelligence.Completion.Translations.ModuleOrBehaviourTest %{ project: project } do - patch(Engine.Api, :project_apps, [:project, :ex_unit, :stream_data]) + patch(EngineApi, :project_apps, [:project, :ex_unit, :stream_data]) source = ~q[ use En| diff --git a/apps/expert/test/expert/code_intelligence/completion_test.exs b/apps/expert/test/expert/code_intelligence/completion_test.exs index 9125a737..320cfb34 100644 --- a/apps/expert/test/expert/code_intelligence/completion_test.exs +++ b/apps/expert/test/expert/code_intelligence/completion_test.exs @@ -1,6 +1,7 @@ defmodule Expert.CodeIntelligence.CompletionTest do - alias Engine.Completion.Candidate alias Expert.CodeIntelligence.Completion.SortScope + alias Expert.EngineApi + alias Forge.Completion.Candidate alias GenLSP.Enumerations.CompletionItemKind alias GenLSP.Structures.CompletionItem @@ -16,49 +17,49 @@ defmodule Expert.CodeIntelligence.CompletionTest do describe "excluding modules from expert dependencies" do test "expert modules are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Expert.CodeIntelligence|") end test "Expert submodules are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Engin|e") assert [] = complete(project, "Forg|e") end test "Expert functions are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Engine.|") end test "Dependency modules are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "ElixirSense|") end test "Dependency functions are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Jason.encod|") end test "Dependency protocols are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Jason.Encode|") end test "Dependency structs are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Jason.Fragment|") end test "Dependency exceptions are removed", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [] = complete(project, "Jason.DecodeErro|") end end test "includes modules from dependencies shared by the project and Expert", %{project: project} do - patch(Engine.Api, :project_apps, [:project, :sourceror]) + patch(EngineApi, :project_apps, [:project, :sourceror]) assert [sourceror_module] = complete(project, "Sourcer|") assert sourceror_module.kind == CompletionItemKind.module() @@ -173,7 +174,7 @@ defmodule Expert.CodeIntelligence.CompletionTest do origin: nil } - patch(Engine.Api, :complete, [candidate]) + patch(EngineApi, :complete, [candidate]) [completion] = complete(project, " @type a|") assert completion.label == "any()" @@ -181,7 +182,7 @@ defmodule Expert.CodeIntelligence.CompletionTest do test "typespecs with no full_name are completed", %{project: project} do candidate = %Candidate.Struct{full_name: nil, metadata: %{}, name: "Struct"} - patch(Engine.Api, :complete, [candidate]) + patch(EngineApi, :complete, [candidate]) [completion] = complete(project, " %Stru|") assert completion.label == "Struct" @@ -234,7 +235,7 @@ defmodule Expert.CodeIntelligence.CompletionTest do %Candidate.Variable{name: "#{name}-variable"} ] - patch(Engine.Api, :complete, all_completions) + patch(EngineApi, :complete, all_completions) :ok end diff --git a/apps/expert/test/expert/project/diagnostics/state_test.exs b/apps/expert/test/expert/project/diagnostics/state_test.exs index 234066ae..25fd4315 100644 --- a/apps/expert/test/expert/project/diagnostics/state_test.exs +++ b/apps/expert/test/expert/project/diagnostics/state_test.exs @@ -5,9 +5,9 @@ defmodule Forge.Project.Diagnostics.StateTest do alias Forge.Plugin.V1.Diagnostic alias Forge.Project - import Engine.Test.Fixtures + import Forge.Test.Fixtures - use Engine.Test.CodeMod.Case + use Forge.Test.CodeMod.Case setup do {:ok, _} = start_supervised(Forge.Document.Store) diff --git a/apps/expert/test/expert/project/diagnostics_test.exs b/apps/expert/test/expert/project/diagnostics_test.exs index 687a2a85..ee0213a8 100644 --- a/apps/expert/test/expert/project/diagnostics_test.exs +++ b/apps/expert/test/expert/project/diagnostics_test.exs @@ -1,4 +1,5 @@ defmodule Expert.Project.DiagnosticsTest do + alias Expert.EngineApi alias Expert.Test.DispatchFake alias Forge.Document alias Forge.Plugin.V1.Diagnostic @@ -10,8 +11,8 @@ defmodule Expert.Project.DiagnosticsTest do use Patch use DispatchFake - import Engine.Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures setup do project = project() @@ -72,7 +73,7 @@ defmodule Expert.Project.DiagnosticsTest do file_diagnostics_message = file_diagnostics(diagnostics: [diagnostic(document.uri)], uri: document.uri) - Engine.Api.broadcast(project, file_diagnostics_message) + EngineApi.broadcast(project, file_diagnostics_message) expected_severity = GenLSP.Enumerations.DiagnosticSeverity.error() @@ -91,8 +92,8 @@ defmodule Expert.Project.DiagnosticsTest do Document.Store.get_and_update(document.uri, &{:ok, Document.mark_clean(&1)}) - Engine.Api.broadcast(project, project_compile_requested()) - Engine.Api.broadcast(project, project_diagnostics(diagnostics: [])) + EngineApi.broadcast(project, project_compile_requested()) + EngineApi.broadcast(project, project_diagnostics(diagnostics: [])) assert_receive {:transport, %TextDocumentPublishDiagnostics{ @@ -108,13 +109,13 @@ defmodule Expert.Project.DiagnosticsTest do file_diagnostics_message = file_diagnostics(diagnostics: [diagnostic(document.uri)], uri: document.uri) - Engine.Api.broadcast(project, file_diagnostics_message) + EngineApi.broadcast(project, file_diagnostics_message) assert_receive {:transport, %TextDocumentPublishDiagnostics{}}, 500 Document.Store.close(document.uri) - Engine.Api.broadcast(project, project_compile_requested()) - Engine.Api.broadcast(project, project_diagnostics(diagnostics: [])) + EngineApi.broadcast(project, project_compile_requested()) + EngineApi.broadcast(project, project_diagnostics(diagnostics: [])) assert_receive {:transport, %TextDocumentPublishDiagnostics{ @@ -133,7 +134,7 @@ defmodule Expert.Project.DiagnosticsTest do file_diagnostics_message = file_diagnostics(diagnostics: [diagnostic], uri: document.uri) - Engine.Api.broadcast(project, file_diagnostics_message) + EngineApi.broadcast(project, file_diagnostics_message) assert_receive {:transport, %TextDocumentPublishDiagnostics{ diff --git a/apps/expert/test/expert/project/intelligence_test.exs b/apps/expert/test/expert/project/intelligence_test.exs index 17c661d2..1484e32e 100644 --- a/apps/expert/test/expert/project/intelligence_test.exs +++ b/apps/expert/test/expert/project/intelligence_test.exs @@ -1,8 +1,9 @@ defmodule Expert.Project.IntelligenceTest do - alias Engine.Api.Messages - alias Engine.Test.Fixtures + alias Expert.EngineApi alias Expert.Project.Intelligence alias Expert.Test.DispatchFake + alias Forge.EngineApi.Messages + alias Forge.Test.Fixtures use ExUnit.Case use Patch @@ -38,7 +39,7 @@ defmodule Expert.Project.IntelligenceTest do struct: [name: nil] ) ] - |> Enum.each(&Engine.Api.broadcast(project, &1)) + |> Enum.each(&EngineApi.broadcast(project, &1)) Process.sleep(50) :ok diff --git a/apps/expert/test/expert/project/node_test.exs b/apps/expert/test/expert/project/node_test.exs index 0a4db7d4..492e9e51 100644 --- a/apps/expert/test/expert/project/node_test.exs +++ b/apps/expert/test/expert/project/node_test.exs @@ -1,8 +1,10 @@ defmodule Expert.Project.NodeTest do - import Engine.Test.Fixtures - import Engine.Api.Messages + alias Expert.EngineApi alias Expert.Project.Node, as: ProjectNode + import Forge.Test.Fixtures + import Forge.EngineApi.Messages + use ExUnit.Case use Forge.Test.EventualAssertions @@ -12,7 +14,7 @@ defmodule Expert.Project.NodeTest do {:ok, _} = start_supervised({DynamicSupervisor, Expert.Project.Supervisor.options()}) {:ok, _} = start_supervised({Expert.Project.Supervisor, project}) - :ok = Engine.Api.register_listener(project, self(), [project_compiled()]) + :ok = EngineApi.register_listener(project, self(), [project_compiled()]) {:ok, project: project} end @@ -22,7 +24,7 @@ defmodule Expert.Project.NodeTest do end test "remote control is started when the node starts", %{project: project} do - apps = Engine.call(project, Application, :started_applications) + apps = EngineApi.call(project, Application, :started_applications) app_names = Enum.map(apps, &elem(&1, 0)) assert :engine in app_names end @@ -31,7 +33,7 @@ defmodule Expert.Project.NodeTest do node_name = ProjectNode.node_name(project) old_pid = node_pid(project) - :ok = Engine.stop(project) + :ok = EngineApi.stop(project) assert_eventually Node.ping(node_name) == :pong, 1000 new_pid = node_pid(project) @@ -41,7 +43,7 @@ defmodule Expert.Project.NodeTest do test "the node restarts when the supervisor pid is killed", %{project: project} do node_name = ProjectNode.node_name(project) - supervisor_pid = Engine.call(project, Process, :whereis, [Engine.Supervisor]) + supervisor_pid = EngineApi.call(project, Process, :whereis, [Engine.Supervisor]) assert is_pid(supervisor_pid) Process.exit(supervisor_pid, :kill) @@ -50,7 +52,7 @@ defmodule Expert.Project.NodeTest do defp node_pid(project) do project - |> Engine.ProjectNode.name() + |> Expert.ProjectNode.name() |> Process.whereis() end end diff --git a/apps/expert/test/expert/project/progress/state_test.exs b/apps/expert/test/expert/project/progress/state_test.exs index 484a96a0..4c87d4cf 100644 --- a/apps/expert/test/expert/project/progress/state_test.exs +++ b/apps/expert/test/expert/project/progress/state_test.exs @@ -2,8 +2,8 @@ defmodule Expert.Project.Progress.StateTest do alias Expert.Project.Progress.State alias Expert.Project.Progress.Value - import Engine.Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures use ExUnit.Case, async: true diff --git a/apps/expert/test/expert/project/progress/support_test.exs b/apps/expert/test/expert/project/progress/support_test.exs index 37bc0e55..ef1eabd0 100644 --- a/apps/expert/test/expert/project/progress/support_test.exs +++ b/apps/expert/test/expert/project/progress/support_test.exs @@ -1,8 +1,8 @@ defmodule Expert.Project.Progress.SupportTest do alias Expert.Project.Progress - import Engine.Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures use ExUnit.Case use Patch diff --git a/apps/expert/test/expert/project/progress_test.exs b/apps/expert/test/expert/project/progress_test.exs index 3c370044..45725893 100644 --- a/apps/expert/test/expert/project/progress_test.exs +++ b/apps/expert/test/expert/project/progress_test.exs @@ -1,13 +1,14 @@ defmodule Expert.Project.ProgressTest do alias Expert.Configuration + alias Expert.EngineApi alias Expert.Project alias Expert.Test.DispatchFake alias GenLSP.Notifications alias GenLSP.Requests alias GenLSP.Structures - import Engine.Test.Fixtures - import Engine.Api.Messages + import Forge.Test.Fixtures + import Forge.EngineApi.Messages use ExUnit.Case use Patch @@ -26,17 +27,17 @@ defmodule Expert.Project.ProgressTest do def percent_begin(project, label, max) do message = percent_progress(stage: :begin, label: label, max: max) - Engine.Api.broadcast(project, message) + EngineApi.broadcast(project, message) end defp percent_report(project, label, delta, message \\ nil) do message = percent_progress(stage: :report, label: label, message: message, delta: delta) - Engine.Api.broadcast(project, message) + EngineApi.broadcast(project, message) end defp percent_complete(project, label, message) do message = percent_progress(stage: :complete, label: label, message: message) - Engine.Api.broadcast(project, message) + EngineApi.broadcast(project, message) end def progress(stage, label, message \\ "") do @@ -69,7 +70,7 @@ defmodule Expert.Project.ProgressTest do patch(Configuration, :client_supports?, fn :work_done_progress -> true end) begin_message = progress(:begin, "mix compile") - Engine.Api.broadcast(project, begin_message) + EngineApi.broadcast(project, begin_message) assert_receive {:transport, %Requests.WindowWorkDoneProgressCreate{ @@ -79,7 +80,7 @@ defmodule Expert.Project.ProgressTest do assert_receive {:transport, %Notifications.DollarProgress{}} report_message = progress(:report, "mix compile", "lib/file.ex") - Engine.Api.broadcast(project, report_message) + EngineApi.broadcast(project, report_message) assert_receive {:transport, %Notifications.DollarProgress{ @@ -96,7 +97,7 @@ defmodule Expert.Project.ProgressTest do patch(Configuration, :client_supports?, fn :work_done_progress -> false end) begin_message = progress(:begin, "mix compile") - Engine.Api.broadcast(project, begin_message) + EngineApi.broadcast(project, begin_message) refute_receive {:transport, %Requests.WindowWorkDoneProgressCreate{params: %{}}} end diff --git a/apps/engine/test/engine/project_node_test.exs b/apps/expert/test/expert/project_node_test.exs similarity index 78% rename from apps/engine/test/engine/project_node_test.exs rename to apps/expert/test/expert/project_node_test.exs index 4a7fee02..600fa34b 100644 --- a/apps/engine/test/engine/project_node_test.exs +++ b/apps/expert/test/expert/project_node_test.exs @@ -1,9 +1,10 @@ -defmodule Engine.ProjectNodeTest do - alias Engine.ProjectNode - alias Engine.ProjectNodeSupervisor +defmodule Expert.ProjectNodeTest do + alias Expert.EngineApi + alias Expert.ProjectNode + alias Expert.ProjectNodeSupervisor import Forge.Test.EventualAssertions - import Engine.Test.Fixtures + import Forge.Test.Fixtures use ExUnit.Case, async: false @@ -14,7 +15,7 @@ defmodule Engine.ProjectNodeTest do end test "it should be able to stop a project node and won't restart", %{project: project} do - {:ok, _node_name, _} = Engine.start_link(project) + {:ok, _node_name, _} = EngineApi.start_link(project) project_alive? = project |> ProjectNode.name() |> Process.whereis() |> Process.alive?() @@ -28,7 +29,7 @@ defmodule Engine.ProjectNodeTest do linked_node_process = spawn(fn -> - {:ok, _node_name, _} = Engine.start_link(project) + {:ok, _node_name, _} = EngineApi.start_link(project) send(test_pid, :started) end) diff --git a/apps/expert/test/expert/provider/handlers/code_lens_test.exs b/apps/expert/test/expert/provider/handlers/code_lens_test.exs index 8d40cc34..f83b11e7 100644 --- a/apps/expert/test/expert/provider/handlers/code_lens_test.exs +++ b/apps/expert/test/expert/provider/handlers/code_lens_test.exs @@ -1,4 +1,5 @@ defmodule Expert.Provider.Handlers.CodeLensTest do + alias Expert.EngineApi alias Expert.Provider.Handlers alias Forge.Document alias Forge.Project @@ -7,8 +8,8 @@ defmodule Expert.Provider.Handlers.CodeLensTest do alias GenLSP.Requests.TextDocumentCodeLens alias GenLSP.Structures - import Engine.Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures import Forge.Test.RangeSupport use ExUnit.Case, async: false @@ -21,8 +22,8 @@ defmodule Expert.Provider.Handlers.CodeLensTest do start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) - Engine.Api.register_listener(project, self(), [project_compiled()]) - Engine.Api.schedule_compile(project, true) + EngineApi.register_listener(project, self(), [project_compiled()]) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(), 5000 @@ -30,7 +31,7 @@ defmodule Expert.Provider.Handlers.CodeLensTest do end defp with_indexing_enabled(_) do - patch(Engine.Api, :index_running?, false) + patch(EngineApi, :index_running?, false) :ok end diff --git a/apps/expert/test/expert/provider/handlers/find_references_test.exs b/apps/expert/test/expert/provider/handlers/find_references_test.exs index cd31e3ad..254471ea 100644 --- a/apps/expert/test/expert/provider/handlers/find_references_test.exs +++ b/apps/expert/test/expert/provider/handlers/find_references_test.exs @@ -1,4 +1,5 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do + alias Expert.EngineApi alias Expert.Provider.Handlers alias Forge.Ast.Analysis alias Forge.Document @@ -7,7 +8,7 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do alias GenLSP.Requests.TextDocumentReferences alias GenLSP.Structures - import Engine.Test.Fixtures + import Forge.Test.Fixtures use ExUnit.Case, async: false use Patch @@ -50,7 +51,7 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do describe "find references" do test "returns locations that the entity returns", %{project: project, uri: uri} do - patch(Engine.Api, :references, fn ^project, %Analysis{document: document}, _position, _ -> + patch(EngineApi, :references, fn ^project, %Analysis{document: document}, _position, _ -> locations = [ Location.new( Document.Range.new( @@ -71,7 +72,7 @@ defmodule Expert.Provider.Handlers.FindReferencesTest do end test "returns nothing if the entity can't resolve it", %{project: project, uri: uri} do - patch(Engine.Api, :references, nil) + patch(EngineApi, :references, nil) {:ok, request} = build_request(uri, 1, 5) diff --git a/apps/expert/test/expert/provider/handlers/formatting_test.exs b/apps/expert/test/expert/provider/handlers/formatting_test.exs new file mode 100644 index 00000000..7324ba91 --- /dev/null +++ b/apps/expert/test/expert/provider/handlers/formatting_test.exs @@ -0,0 +1,41 @@ +defmodule Expert.Provider.Handlers.FormattingTest do + alias Expert.EngineApi + alias Forge.Document + alias Forge.EngineApi.Messages + + use Forge.Test.CodeMod.Case, enable_ast_conversion: false + import Messages + + def document(file_uri, text) do + Document.new(file_uri, text, 1) + end + + def with_real_project(%{project: project}) do + {:ok, _} = start_supervised({Expert.ProjectNodeSupervisor, project}) + {:ok, _, _} = EngineApi.start_link(project) + EngineApi.register_listener(project, self(), [:all]) + :ok + end + + setup do + project = project() + Engine.set_project(project) + {:ok, project: project} + end + + describe "emitting diagnostics" do + setup [:with_real_project] + + test "it should emit diagnostics when a syntax error occurs", %{project: project} do + text = ~q[ + def foo(a, ) do + end + ] + document = document("file:///file.ex", text) + EngineApi.format(project, document) + + assert_receive file_diagnostics(diagnostics: [diagnostic]), 500 + assert diagnostic.message =~ "syntax error" + end + end +end diff --git a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs index 3d29bf04..21576285 100644 --- a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs +++ b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs @@ -1,4 +1,5 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do + alias Expert.EngineApi alias Expert.Provider.Handlers alias Forge.Document alias Forge.Document.Location @@ -6,8 +7,8 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do alias GenLSP.Requests.TextDocumentDefinition alias GenLSP.Structures - import Engine.Api.Messages - import Engine.Test.Fixtures + import Forge.EngineApi.Messages + import Forge.Test.Fixtures use ExUnit.Case, async: false @@ -18,12 +19,12 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) - Engine.Api.register_listener(project, self(), [ + EngineApi.register_listener(project, self(), [ project_compiled(), project_index_ready() ]) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(), 5000 assert_receive project_index_ready(), 5000 diff --git a/apps/expert/test/expert/provider/handlers/hover_test.exs b/apps/expert/test/expert/provider/handlers/hover_test.exs index a25919f2..2c58d7ee 100644 --- a/apps/expert/test/expert/provider/handlers/hover_test.exs +++ b/apps/expert/test/expert/provider/handlers/hover_test.exs @@ -1,10 +1,11 @@ defmodule Expert.Provider.Handlers.HoverTest do - alias Engine.Api.Messages - alias Engine.Test.Fixtures + alias Expert.EngineApi alias Expert.Provider.Handlers alias Forge.Document alias Forge.Document.Position + alias Forge.EngineApi.Messages alias Forge.Protocol.Convert + alias Forge.Test.Fixtures alias GenLSP.Requests alias GenLSP.Structures @@ -23,7 +24,7 @@ defmodule Expert.Provider.Handlers.HoverTest do start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) - :ok = Engine.Api.register_listener(project, self(), [Messages.project_compiled()]) + :ok = EngineApi.register_listener(project, self(), [Messages.project_compiled()]) assert_receive Messages.project_compiled(), 5000 {:ok, project: project} @@ -46,7 +47,7 @@ defmodule Expert.Provider.Handlers.HoverTest do end) {:ok, modules, _} = - Engine.call(project, Kernel.ParallelCompiler, :compile_to_path, [ + EngineApi.call(project, Kernel.ParallelCompiler, :compile_to_path, [ [tmp_path], compile_path ]) @@ -55,8 +56,8 @@ defmodule Expert.Provider.Handlers.HoverTest do fun.() after for module <- modules do - path = Engine.call(project, :code, :which, [module]) - Engine.call(project, :code, :delete, [module]) + path = EngineApi.call(project, :code, :which, [module]) + EngineApi.call(project, :code, :delete, [module]) File.rm!(path) end end diff --git a/apps/expert/test/support/test/completion_case.ex b/apps/expert/test/support/test/completion_case.ex index 5ad8f316..dc965231 100644 --- a/apps/expert/test/support/test/completion_case.ex +++ b/apps/expert/test/support/test/completion_case.ex @@ -1,5 +1,6 @@ defmodule Expert.Test.Expert.CompletionCase do alias Expert.CodeIntelligence.Completion + alias Expert.EngineApi alias Forge.Ast alias Forge.Document alias Forge.Project @@ -11,8 +12,8 @@ defmodule Expert.Test.Expert.CompletionCase do use ExUnit.CaseTemplate import Forge.Test.CursorSupport - import Engine.Test.Fixtures - import Engine.Api.Messages + import Forge.Test.Fixtures + import Forge.EngineApi.Messages setup_all do project = project() @@ -20,12 +21,12 @@ defmodule Expert.Test.Expert.CompletionCase do start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) - Engine.Api.register_listener(project, self(), [ + EngineApi.register_listener(project, self(), [ project_compiled(), project_index_ready() ]) - Engine.Api.schedule_compile(project, true) + EngineApi.schedule_compile(project, true) assert_receive project_compiled(), 5000 assert_receive project_index_ready(), 5000 {:ok, project: project} diff --git a/apps/expert/test/support/test/dispatch_fake.ex b/apps/expert/test/support/test/dispatch_fake.ex index e70aeb76..d77e40f8 100644 --- a/apps/expert/test/support/test/dispatch_fake.ex +++ b/apps/expert/test/support/test/dispatch_fake.ex @@ -12,11 +12,11 @@ defmodule Expert.Test.DispatchFake do # does that for us. defmacro start do quote do - patch(Engine.Api, :register_listener, fn _project, listener_pid, message_types -> + patch(Expert.EngineApi, :register_listener, fn _project, listener_pid, message_types -> Dispatch.register_listener(listener_pid, message_types) end) - patch(Engine.Api, :broadcast, fn _project, message -> + patch(Expert.EngineApi, :broadcast, fn _project, message -> Dispatch.broadcast(message) end) diff --git a/apps/expert/test/test_helper.exs b/apps/expert/test/test_helper.exs index 8e6e63c4..15d06e94 100644 --- a/apps/expert/test/test_helper.exs +++ b/apps/expert/test/test_helper.exs @@ -1,6 +1,17 @@ Application.ensure_all_started(:snowflake) -ExUnit.configure(timeout: :infinity) -ExUnit.start() +Application.ensure_all_started(:refactorex) +{"", 0} = System.cmd("epmd", ~w(-daemon)) +random_number = :rand.uniform(500) + +with :nonode@nohost <- Node.self() do + {:ok, _pid} = + :net_kernel.start(:"testing-#{random_number}@127.0.0.1", %{name_domain: :longnames}) +end + +Engine.Module.Loader.start_link(nil) +ExUnit.configure(timeout: :infinity, assert_receive_timeout: 1000) + +ExUnit.start(exclude: [:skip]) if Version.match?(System.version(), ">= 1.15.0") do Logger.configure(level: :none) diff --git a/apps/forge/.credo.exs b/apps/forge/.credo.exs index b65c86be..d489cea3 100644 --- a/apps/forge/.credo.exs +++ b/apps/forge/.credo.exs @@ -1,3 +1,5 @@ Code.require_file("../../mix_credo.exs") -Mix.Credo.config(excluded: ["lib/future/**/*.ex"]) +Mix.Credo.config( + excluded: ["lib/future/**/*.ex", "test/fixtures/**/*.ex", "test/fixtures/**/*.exs"] +) diff --git a/apps/forge/.formatter.exs b/apps/forge/.formatter.exs index a2d1d585..d7a78d80 100644 --- a/apps/forge/.formatter.exs +++ b/apps/forge/.formatter.exs @@ -15,13 +15,39 @@ detected_assertions = [ assertions = eventual_assertions ++ detected_assertions +current_directory = Path.dirname(__ENV__.file) + +impossible_to_format = [ + Path.join([ + current_directory, + "test", + "fixtures", + "compilation_errors", + "lib", + "compilation_errors.ex" + ]), + Path.join([current_directory, "test", "fixtures", "parse_errors", "lib", "parse_errors.ex"]) +] + +inputs = + Enum.flat_map( + [ + "{mix,.formatter}.exs", + "{config,test}/**/*.{ex,exs}", + "lib/forge/**/*.{ex,ex}", + "lib/mix/**/*.{ex,exs}" + ], + fn path -> + current_directory + |> Path.join(path) + |> Path.wildcard() + end + ) + +inputs = inputs -- impossible_to_format + [ - inputs: [ - "{mix,.formatter}.exs", - "{config,test}/**/*.{ex,exs}", - "lib/forge/**/*.{ex,ex}", - "lib/mix/**/*.{ex,exs}" - ], + inputs: inputs, locals_without_parens: assertions, export: [locals_without_parens: assertions] ] diff --git a/apps/forge/lib/forge/code_action.ex b/apps/forge/lib/forge/code_action.ex new file mode 100644 index 00000000..3a6fd50b --- /dev/null +++ b/apps/forge/lib/forge/code_action.ex @@ -0,0 +1,23 @@ +defmodule Forge.CodeAction do + alias Forge.Document.Changes + + require Logger + + defstruct [:title, :kind, :changes, :uri] + + @type code_action_kind :: GenLSP.Enumerations.CodeActionKind.t() + + @type trigger_kind :: GenLSP.Enumerations.CodeActionTriggerKind.t() + + @type t :: %__MODULE__{ + title: String.t(), + kind: code_action_kind, + changes: Changes.t(), + uri: Forge.uri() + } + + @spec new(Forge.uri(), String.t(), code_action_kind(), Changes.t()) :: t() + def new(uri, title, kind, changes) do + %__MODULE__{uri: uri, title: title, changes: changes, kind: kind} + end +end diff --git a/apps/engine/lib/engine/engine/code_action/diagnostic.ex b/apps/forge/lib/forge/code_action/diagnostic.ex similarity index 91% rename from apps/engine/lib/engine/engine/code_action/diagnostic.ex rename to apps/forge/lib/forge/code_action/diagnostic.ex index 8d4c3086..3a6a3d81 100644 --- a/apps/engine/lib/engine/engine/code_action/diagnostic.ex +++ b/apps/forge/lib/forge/code_action/diagnostic.ex @@ -1,4 +1,4 @@ -defmodule Engine.CodeAction.Diagnostic do +defmodule Forge.CodeAction.Diagnostic do alias Forge.Document.Range defstruct [:range, :message, :source] diff --git a/apps/forge/lib/forge/code_intelligence/docs.ex b/apps/forge/lib/forge/code_intelligence/docs.ex new file mode 100644 index 00000000..9d342593 --- /dev/null +++ b/apps/forge/lib/forge/code_intelligence/docs.ex @@ -0,0 +1,13 @@ +defmodule Forge.CodeIntelligence.Docs do + alias Forge.CodeIntelligence.Docs.Entry + + defstruct [:module, :doc, functions_and_macros: [], callbacks: [], types: []] + + @type t :: %__MODULE__{ + module: module(), + doc: Entry.content(), + functions_and_macros: %{optional(atom()) => [Entry.t(:function | :macro)]}, + callbacks: %{optional(atom()) => [Entry.t(:callback)]}, + types: %{optional(atom()) => [Entry.t(:type)]} + } +end diff --git a/apps/engine/lib/engine/engine/code_intelligence/docs/entry.ex b/apps/forge/lib/forge/code_intelligence/docs/entry.ex similarity index 96% rename from apps/engine/lib/engine/engine/code_intelligence/docs/entry.ex rename to apps/forge/lib/forge/code_intelligence/docs/entry.ex index 359ae0f2..420788dc 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/docs/entry.ex +++ b/apps/forge/lib/forge/code_intelligence/docs/entry.ex @@ -1,4 +1,4 @@ -defmodule Engine.CodeIntelligence.Docs.Entry do +defmodule Forge.CodeIntelligence.Docs.Entry do @moduledoc """ A documentation entry for a named entity within a module. """ diff --git a/apps/engine/lib/engine/engine/code_intelligence/symbols/document.ex b/apps/forge/lib/forge/code_intelligence/symbols/document.ex similarity index 96% rename from apps/engine/lib/engine/engine/code_intelligence/symbols/document.ex rename to apps/forge/lib/forge/code_intelligence/symbols/document.ex index 6aa680d8..5bf9b53f 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/symbols/document.ex +++ b/apps/forge/lib/forge/code_intelligence/symbols/document.ex @@ -1,7 +1,7 @@ -defmodule Engine.CodeIntelligence.Symbols.Document do - alias Engine.Search.Indexer.Entry +defmodule Forge.CodeIntelligence.Symbols.Document do alias Forge.Document alias Forge.Formats + alias Forge.Search.Indexer.Entry defstruct [:name, :type, :range, :detail_range, :detail, :original_type, :subject, children: []] diff --git a/apps/engine/lib/engine/engine/code_intelligence/symbols/workspace.ex b/apps/forge/lib/forge/code_intelligence/symbols/workspace.ex similarity index 92% rename from apps/engine/lib/engine/engine/code_intelligence/symbols/workspace.ex rename to apps/forge/lib/forge/code_intelligence/symbols/workspace.ex index 64dc0e94..ec86fdf5 100644 --- a/apps/engine/lib/engine/engine/code_intelligence/symbols/workspace.ex +++ b/apps/forge/lib/forge/code_intelligence/symbols/workspace.ex @@ -1,4 +1,4 @@ -defmodule Engine.CodeIntelligence.Symbols.Workspace do +defmodule Forge.CodeIntelligence.Symbols.Workspace do defmodule Link do defstruct [:uri, :range, :detail_range] @@ -13,9 +13,9 @@ defmodule Engine.CodeIntelligence.Symbols.Workspace do end end - alias Engine.Search.Indexer.Entry alias Forge.Document alias Forge.Formats + alias Forge.Search.Indexer.Entry defstruct [:name, :type, :link, container_name: nil] diff --git a/apps/engine/lib/engine/engine/completion/candidate.ex b/apps/forge/lib/forge/completion/candidate.ex similarity index 98% rename from apps/engine/lib/engine/engine/completion/candidate.ex rename to apps/forge/lib/forge/completion/candidate.ex index 00a5199a..d7cabc8b 100644 --- a/apps/engine/lib/engine/engine/completion/candidate.ex +++ b/apps/forge/lib/forge/completion/candidate.ex @@ -1,5 +1,5 @@ -defmodule Engine.Completion.Candidate do - alias Engine.Completion.Candidate.ArgumentNames +defmodule Forge.Completion.Candidate do + alias Forge.Completion.Candidate.ArgumentNames require Logger defmodule Function do diff --git a/apps/engine/lib/engine/engine/completion/candidate/argument_names.ex b/apps/forge/lib/forge/completion/candidate/argument_names.ex similarity index 98% rename from apps/engine/lib/engine/engine/completion/candidate/argument_names.ex rename to apps/forge/lib/forge/completion/candidate/argument_names.ex index d8d2e1b8..1fccfed5 100644 --- a/apps/engine/lib/engine/engine/completion/candidate/argument_names.ex +++ b/apps/forge/lib/forge/completion/candidate/argument_names.ex @@ -1,4 +1,4 @@ -defmodule Engine.Completion.Candidate.ArgumentNames do +defmodule Forge.Completion.Candidate.ArgumentNames do @moduledoc """ Elixir sense, for whatever reason returns all the argument names when asked to do a completion on a function. This means that the arity of the function might differ from the argument names returned. Furthermore, the diff --git a/apps/engine/lib/engine/engine/api/messages.ex b/apps/forge/lib/forge/engine_api/messages.ex similarity index 99% rename from apps/engine/lib/engine/engine/api/messages.ex rename to apps/forge/lib/forge/engine_api/messages.ex index 49afdada..f1d83b2d 100644 --- a/apps/engine/lib/engine/engine/api/messages.ex +++ b/apps/forge/lib/forge/engine_api/messages.ex @@ -1,4 +1,4 @@ -defmodule Engine.Api.Messages do +defmodule Forge.EngineApi.Messages do alias Forge.Project import Record diff --git a/apps/engine/lib/mix/tasks/namespace/abstract.ex b/apps/forge/lib/forge/namespace/abstract.ex similarity index 98% rename from apps/engine/lib/mix/tasks/namespace/abstract.ex rename to apps/forge/lib/forge/namespace/abstract.ex index b6139971..8a2fa074 100644 --- a/apps/engine/lib/mix/tasks/namespace/abstract.ex +++ b/apps/forge/lib/forge/namespace/abstract.ex @@ -1,4 +1,4 @@ -defmodule Mix.Tasks.Namespace.Abstract do +defmodule Forge.Namespace.Abstract do @moduledoc """ Transformations from erlang abstract syntax @@ -6,8 +6,6 @@ defmodule Mix.Tasks.Namespace.Abstract do https://www.erlang.org/doc/apps/erts/absform.html """ - alias Mix.Tasks.Namespace - def rewrite(abstract_format) when is_list(abstract_format) do Enum.map(abstract_format, &rewrite/1) end @@ -67,7 +65,7 @@ defmodule Mix.Tasks.Namespace.Abstract do {:for, rewrite(target)} end - defp do_rewrite({:protocol, protocol}) do + defp do_rewrite({:protocol, protocol}) when is_atom(protocol) do {:protocol, rewrite_module(protocol)} end @@ -296,6 +294,6 @@ defmodule Mix.Tasks.Namespace.Abstract do end defp rewrite_module(module) do - Namespace.Module.apply(module) + Forge.Namespace.Module.apply(module) end end diff --git a/apps/engine/lib/mix/tasks/namespace/code.ex b/apps/forge/lib/forge/namespace/code.ex similarity index 70% rename from apps/engine/lib/mix/tasks/namespace/code.ex rename to apps/forge/lib/forge/namespace/code.ex index 60523a3d..618c007f 100644 --- a/apps/engine/lib/mix/tasks/namespace/code.ex +++ b/apps/forge/lib/forge/namespace/code.ex @@ -1,4 +1,4 @@ -defmodule Mix.Tasks.Namespace.Code do +defmodule Forge.Namespace.Code do def compile(forms) do :compile.forms(forms, [:return_errors, :debug_info]) end diff --git a/apps/engine/lib/mix/tasks/namespace/transform/erlang.ex b/apps/forge/lib/forge/namespace/erlang.ex similarity index 93% rename from apps/engine/lib/mix/tasks/namespace/transform/erlang.ex rename to apps/forge/lib/forge/namespace/erlang.ex index 77fa59c5..f4b225bf 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/erlang.ex +++ b/apps/forge/lib/forge/namespace/erlang.ex @@ -1,4 +1,4 @@ -defmodule Mix.Tasks.Namespace.Transform.Erlang do +defmodule Forge.Namespace.Erlang do @moduledoc """ Utilities for reading and writing erlang terms from and to text """ diff --git a/apps/engine/lib/mix/tasks/namespace/module.ex b/apps/forge/lib/forge/namespace/module.ex similarity index 91% rename from apps/engine/lib/mix/tasks/namespace/module.ex rename to apps/forge/lib/forge/namespace/module.ex index 214bddf7..64ba6634 100644 --- a/apps/engine/lib/mix/tasks/namespace/module.ex +++ b/apps/forge/lib/forge/namespace/module.ex @@ -1,6 +1,4 @@ -defmodule Mix.Tasks.Namespace.Module do - alias Mix.Tasks.Namespace - +defmodule Forge.Namespace.Module do @namespace_prefix "XP" def apply(module_name) do @@ -8,7 +6,7 @@ defmodule Mix.Tasks.Namespace.Module do prefixed?(module_name) -> module_name - module_name in Namespace.app_names() -> + module_name in Mix.Tasks.Namespace.app_names() -> :"xp_#{module_name}" true -> @@ -41,7 +39,7 @@ defmodule Mix.Tasks.Namespace.Module do do: false defp apply_namespace("Elixir." <> rest) do - Namespace.root_modules() + Mix.Tasks.Namespace.root_modules() |> Enum.map(fn module -> module |> Module.split() |> List.first() end) |> Enum.reduce_while(rest, fn root_module, module -> if has_root_module?(root_module, module) do diff --git a/apps/forge/lib/forge/namespace/path.ex b/apps/forge/lib/forge/namespace/path.ex new file mode 100644 index 00000000..060feea8 --- /dev/null +++ b/apps/forge/lib/forge/namespace/path.ex @@ -0,0 +1,38 @@ +defmodule Forge.Namespace.Path do + def apply(path) when is_list(path) do + path + |> List.to_string() + |> apply() + |> String.to_charlist() + end + + def apply(path) when is_binary(path) do + path + |> Path.split() + |> Enum.map(&replace_namespaced_apps/1) + |> Path.join() + end + + defp replace_namespaced_apps(path_component) do + Enum.reduce(Mix.Tasks.Namespace.app_names(), path_component, fn app_name, path -> + [path | vsn] = String.split(path, "-") + + if path == Atom.to_string(app_name) do + new_path = + app_name + |> Forge.Namespace.Module.apply() + |> Atom.to_string() + + rebuild_path(new_path, vsn) + else + rebuild_path(path, vsn) + end + end) + end + + defp rebuild_path(path, []), do: path + + defp rebuild_path(path, rest) do + "#{path}-#{Enum.join(rest, "-")}" + end +end diff --git a/apps/engine/lib/mix/tasks/namespace/transform/app_directories.ex b/apps/forge/lib/forge/namespace/transform/app_directories.ex similarity index 50% rename from apps/engine/lib/mix/tasks/namespace/transform/app_directories.ex rename to apps/forge/lib/forge/namespace/transform/app_directories.ex index 0d26725a..fa477581 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/app_directories.ex +++ b/apps/forge/lib/forge/namespace/transform/app_directories.ex @@ -1,27 +1,30 @@ -defmodule Mix.Tasks.Namespace.Transform.AppDirectories do - alias Mix.Tasks.Namespace - +defmodule Forge.Namespace.Transform.AppDirectories do def apply_to_all(base_directory) do base_directory |> find_app_directories() |> Enum.each(&apply_transform(base_directory, &1)) end - def apply_transform(base_dirctory, app_path) do + def apply_transform(base_directory, app_path) do namespaced_relative_path = app_path - |> Path.relative_to(base_dirctory) - |> Namespace.Path.apply() + |> Path.relative_to(base_directory) + |> Forge.Namespace.Path.apply() - namespaced_app_path = Path.join(base_dirctory, namespaced_relative_path) + namespaced_app_path = Path.join(base_directory, namespaced_relative_path) with {:ok, _} <- File.rm_rf(namespaced_app_path) do File.rename!(app_path, namespaced_app_path) end + catch + e -> + Mix.Shell.IO.error("Failed to rename app directory") + reraise e, __STACKTRACE__ end defp find_app_directories(base_directory) do - app_globs = Enum.join(Namespace.app_names(), "*,") + app_names = Mix.Tasks.Namespace.app_names() + app_globs = Enum.join(app_names, "*,") [base_directory, "lib", "{" <> app_globs <> "*}"] |> Path.join() diff --git a/apps/engine/lib/mix/tasks/namespace/transform/apps.ex b/apps/forge/lib/forge/namespace/transform/apps.ex similarity index 66% rename from apps/engine/lib/mix/tasks/namespace/transform/apps.ex rename to apps/forge/lib/forge/namespace/transform/apps.ex index a3c1e938..28630a32 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/apps.ex +++ b/apps/forge/lib/forge/namespace/transform/apps.ex @@ -1,9 +1,7 @@ -defmodule Mix.Tasks.Namespace.Transform.Apps do +defmodule Forge.Namespace.Transform.Apps do @moduledoc """ Applies namespacing to all modules defined in .app files """ - alias Mix.Tasks.Namespace - alias Mix.Tasks.Namespace.Transform def apply_to_all(base_directory) do base_directory @@ -15,7 +13,7 @@ defmodule Mix.Tasks.Namespace.Transform.Apps do end def apply(file_path) do - with {:ok, app_definition} <- Transform.Erlang.path_to_term(file_path), + with {:ok, app_definition} <- Forge.Namespace.Erlang.path_to_term(file_path), {:ok, converted} <- convert(app_definition), :ok <- File.write(file_path, converted) do app_name = @@ -24,7 +22,7 @@ defmodule Mix.Tasks.Namespace.Transform.Apps do |> Path.rootname() |> String.to_atom() - namespaced_app_name = Namespace.Module.apply(app_name) + namespaced_app_name = Forge.Namespace.Module.apply(app_name) new_filename = "#{namespaced_app_name}.app" new_file_path = @@ -34,10 +32,14 @@ defmodule Mix.Tasks.Namespace.Transform.Apps do File.rename!(file_path, new_file_path) end + catch + e -> + Mix.Shell.IO.error("Failed to rename app file") + reraise e, __STACKTRACE__ end defp find_app_files(base_directory) do - app_files_glob = Enum.join(Namespace.app_names(), ",") + app_files_glob = Enum.join(Mix.Tasks.Namespace.app_names(), ",") [base_directory, "**", "{#{app_files_glob}}.app"] |> Path.join() @@ -48,21 +50,21 @@ defmodule Mix.Tasks.Namespace.Transform.Apps do erlang_terms = app_definition |> visit() - |> Transform.Erlang.term_to_string() + |> Forge.Namespace.Erlang.term_to_string() {:ok, erlang_terms} end defp visit({:application, app_name, keys}) do - {:application, Namespace.Module.apply(app_name), Enum.map(keys, &visit/1)} + {:application, Forge.Namespace.Module.apply(app_name), Enum.map(keys, &visit/1)} end defp visit({:applications, app_list}) do - {:applications, Enum.map(app_list, &Namespace.Module.apply/1)} + {:applications, Enum.map(app_list, &Forge.Namespace.Module.apply/1)} end defp visit({:modules, module_list}) do - {:modules, Enum.map(module_list, &Namespace.Module.apply/1)} + {:modules, Enum.map(module_list, &Forge.Namespace.Module.apply/1)} end defp visit({:description, desc}) do @@ -70,7 +72,7 @@ defmodule Mix.Tasks.Namespace.Transform.Apps do end defp visit({:mod, {module_name, args}}) do - {:mod, {Namespace.Module.apply(module_name), args}} + {:mod, {Forge.Namespace.Module.apply(module_name), args}} end defp visit(key_value) do diff --git a/apps/engine/lib/mix/tasks/namespace/transform/beams.ex b/apps/forge/lib/forge/namespace/transform/beams.ex similarity index 93% rename from apps/engine/lib/mix/tasks/namespace/transform/beams.ex rename to apps/forge/lib/forge/namespace/transform/beams.ex index 0dfe6452..ca44c363 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/beams.ex +++ b/apps/forge/lib/forge/namespace/transform/beams.ex @@ -1,11 +1,10 @@ -defmodule Mix.Tasks.Namespace.Transform.Beams do +defmodule Forge.Namespace.Transform.Beams do @moduledoc """ A transformer that finds and replaces any instance of a module in a .beam file """ - alias Mix.Tasks.Namespace - alias Mix.Tasks.Namespace.Abstract - alias Mix.Tasks.Namespace.Code + alias Forge.Namespace.Abstract + alias Forge.Namespace.Code def apply_to_all(base_directory) do Mix.Shell.IO.info("Rewriting .beam files") @@ -76,7 +75,7 @@ defmodule Mix.Tasks.Namespace.Transform.Beams do end defp find_app_beams(base_directory) do - namespaced_apps = Enum.join(Namespace.app_names(), ",") + namespaced_apps = Enum.join(Mix.Tasks.Namespace.app_names(), ",") apps_glob = "{#{namespaced_apps}}*" [base_directory, "lib", apps_glob, "**", "*.beam"] diff --git a/apps/engine/lib/mix/tasks/namespace/transform/boots.ex b/apps/forge/lib/forge/namespace/transform/boots.ex similarity index 92% rename from apps/engine/lib/mix/tasks/namespace/transform/boots.ex rename to apps/forge/lib/forge/namespace/transform/boots.ex index 0fcc0c6e..c66f5897 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/boots.ex +++ b/apps/forge/lib/forge/namespace/transform/boots.ex @@ -1,4 +1,4 @@ -defmodule Mix.Tasks.Namespace.Transform.Boots do +defmodule Forge.Namespace.Transform.Boots do @moduledoc """ A transformer that re-builds .boot files by converting a .script file """ diff --git a/apps/engine/lib/mix/tasks/namespace/transform/configs.ex b/apps/forge/lib/forge/namespace/transform/configs.ex similarity index 80% rename from apps/engine/lib/mix/tasks/namespace/transform/configs.ex rename to apps/forge/lib/forge/namespace/transform/configs.ex index 2c455a1f..aed519d2 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/configs.ex +++ b/apps/forge/lib/forge/namespace/transform/configs.ex @@ -1,9 +1,7 @@ -defmodule Mix.Tasks.Namespace.Transform.Configs do - alias Mix.Tasks.Namespace - +defmodule Forge.Namespace.Transform.Configs do def apply_to_all(base_directory) do base_directory - |> Path.join("**") + |> Path.join("**/releases/**/runtime.exs") |> Path.wildcard() |> Enum.map(&Path.absname/1) |> tap(fn paths -> @@ -22,14 +20,14 @@ defmodule Mix.Tasks.Namespace.Transform.Configs do namespaced_alias = alias |> Module.concat() - |> Namespace.Module.apply() + |> Forge.Namespace.Module.apply() |> Module.split() |> Enum.map(&String.to_atom/1) {:__aliases__, meta, namespaced_alias} atom when is_atom(atom) -> - Namespace.Module.apply(atom) + Forge.Namespace.Module.apply(atom) ast -> ast diff --git a/apps/engine/lib/mix/tasks/namespace/transform/scripts.ex b/apps/forge/lib/forge/namespace/transform/scripts.ex similarity index 67% rename from apps/engine/lib/mix/tasks/namespace/transform/scripts.ex rename to apps/forge/lib/forge/namespace/transform/scripts.ex index 37ea5271..95df751c 100644 --- a/apps/engine/lib/mix/tasks/namespace/transform/scripts.ex +++ b/apps/forge/lib/forge/namespace/transform/scripts.ex @@ -1,11 +1,8 @@ -defmodule Mix.Tasks.Namespace.Transform.Scripts do +defmodule Forge.Namespace.Transform.Scripts do @moduledoc """ A transform that updates any module in .script and .rel files with namespaced versions """ - alias Mix.Tasks.Namespace - alias Mix.Tasks.Namespace.Transform - def apply_to_all(base_directory) do base_directory |> find_scripts() @@ -16,7 +13,7 @@ defmodule Mix.Tasks.Namespace.Transform.Scripts do end def apply(file_path) do - with {:ok, app_definition} <- Transform.Erlang.path_to_term(file_path), + with {:ok, app_definition} <- Forge.Namespace.Erlang.path_to_term(file_path), {:ok, converted} <- convert(app_definition) do File.write(file_path, converted) end @@ -33,7 +30,7 @@ defmodule Mix.Tasks.Namespace.Transform.Scripts do defp convert(app_definition) do converted = visit(app_definition) - erlang_terms = Transform.Erlang.term_to_string(converted) + erlang_terms = Forge.Namespace.Erlang.term_to_string(converted) script = """ %% coding: utf-8 @@ -47,7 +44,7 @@ defmodule Mix.Tasks.Namespace.Transform.Scripts do defp visit({:release, release_vsn, erts_vsn, app_versions}) do fixed_apps = Enum.map(app_versions, fn {app_name, version, start_type} -> - {Namespace.Module.apply(app_name), version, start_type} + {Forge.Namespace.Module.apply(app_name), version, start_type} end) {:release, release_vsn, erts_vsn, fixed_apps} @@ -58,11 +55,11 @@ defmodule Mix.Tasks.Namespace.Transform.Scripts do end defp visit({:primLoad, app_list}) do - {:primLoad, Enum.map(app_list, &Namespace.Module.apply/1)} + {:primLoad, Enum.map(app_list, &Forge.Namespace.Module.apply/1)} end defp visit({:path, paths}) do - {:path, Enum.map(paths, &Namespace.Path.apply/1)} + {:path, Enum.map(paths, &Forge.Namespace.Path.apply/1)} end defp visit({:apply, {:application, :load, load_apps}}) do @@ -70,27 +67,28 @@ defmodule Mix.Tasks.Namespace.Transform.Scripts do end defp visit({:apply, {:application, :start_boot, apps_to_start}}) do - {:apply, {:application, :start_boot, Enum.map(apps_to_start, &Namespace.Module.apply/1)}} + {:apply, + {:application, :start_boot, Enum.map(apps_to_start, &Forge.Namespace.Module.apply/1)}} end defp visit({:application, app_name, app_keys}) do - {:application, Namespace.Module.apply(app_name), Enum.map(app_keys, &visit/1)} + {:application, Forge.Namespace.Module.apply(app_name), Enum.map(app_keys, &visit/1)} end defp visit({:application, app_name}) do - {:application, Namespace.Module.apply(app_name)} + {:application, Forge.Namespace.Module.apply(app_name)} end defp visit({:mod, {module_name, args}}) do - {:mod, {Namespace.Module.apply(module_name), Enum.map(args, &visit/1)}} + {:mod, {Forge.Namespace.Module.apply(module_name), Enum.map(args, &visit/1)}} end defp visit({:modules, module_list}) do - {:modules, Enum.map(module_list, &Namespace.Module.apply/1)} + {:modules, Enum.map(module_list, &Forge.Namespace.Module.apply/1)} end defp visit({:applications, app_names}) do - {:applications, Enum.map(app_names, &Namespace.Module.apply/1)} + {:applications, Enum.map(app_names, &Forge.Namespace.Module.apply/1)} end defp visit(key_value) do diff --git a/apps/forge/lib/forge/project.ex b/apps/forge/lib/forge/project.ex index 03d069b4..536bee8a 100644 --- a/apps/forge/lib/forge/project.ex +++ b/apps/forge/lib/forge/project.ex @@ -163,6 +163,10 @@ defmodule Forge.Project do set_env_vars(project, environment_variables) end + def manager_node_name(%__MODULE__{} = project) do + :"manager-#{name(project)}-#{entropy(project)}@127.0.0.1" + end + @doc """ Returns the full path to the project's expert workspace directory @@ -197,6 +201,27 @@ defmodule Forge.Project do |> Path.join("build") end + @doc """ + Returns the full path to the directory where expert puts versioned build artifacts + """ + def versioned_build_path(%__MODULE__{} = project) do + %{elixir: elixir, erlang: erlang} = Forge.VM.Versions.current() + erlang_major = erlang |> String.split(".") |> List.first() + elixir_version = Version.parse!(elixir) + elixir_major = "#{elixir_version.major}.#{elixir_version.minor}" + build_root = build_path(project) + Path.join([build_root, "erl-#{erlang_major}", "elixir-#{elixir_major}"]) + end + + @doc """ + Returns the full path to the directory where expert puts engine archives + """ + def engine_path(%__MODULE__{} = project) do + project + |> workspace_path() + |> Path.join("engine") + end + @doc """ Creates and initializes expert's workspace directory if it doesn't already exist """ diff --git a/apps/engine/lib/engine/engine/search/indexer/entry.ex b/apps/forge/lib/forge/search/indexer/entry.ex similarity index 98% rename from apps/engine/lib/engine/engine/search/indexer/entry.ex rename to apps/forge/lib/forge/search/indexer/entry.ex index 928c894c..2e4ff895 100644 --- a/apps/engine/lib/engine/engine/search/indexer/entry.ex +++ b/apps/forge/lib/forge/search/indexer/entry.ex @@ -1,4 +1,4 @@ -defmodule Engine.Search.Indexer.Entry do +defmodule Forge.Search.Indexer.Entry do @type function_type :: :public | :private | :delegated | :usage @type protocol_type :: :implementation | :definition @@ -50,8 +50,8 @@ defmodule Engine.Search.Indexer.Entry do @type datetime_format :: :erl | :unix | :datetime @type date_type :: :calendar.datetime() | integer() | DateTime.t() - alias Engine.Search.Indexer.Source.Block alias Forge.Identifier + alias Forge.Search.Indexer.Source.Block alias Forge.StructAccess use StructAccess diff --git a/apps/engine/lib/engine/engine/search/indexer/source/block.ex b/apps/forge/lib/forge/search/indexer/source/block.ex similarity index 87% rename from apps/engine/lib/engine/engine/search/indexer/source/block.ex rename to apps/forge/lib/forge/search/indexer/source/block.ex index b91f4a2d..e2ff9b13 100644 --- a/apps/engine/lib/engine/engine/search/indexer/source/block.ex +++ b/apps/forge/lib/forge/search/indexer/source/block.ex @@ -1,4 +1,4 @@ -defmodule Engine.Search.Indexer.Source.Block do +defmodule Forge.Search.Indexer.Source.Block do @moduledoc """ A struct that represents a block of source code """ diff --git a/apps/engine/lib/mix/tasks/namespace.ex b/apps/forge/lib/mix/tasks/namespace.ex similarity index 96% rename from apps/engine/lib/mix/tasks/namespace.ex rename to apps/forge/lib/mix/tasks/namespace.ex index c6fa611d..aeb5fa62 100644 --- a/apps/engine/lib/mix/tasks/namespace.ex +++ b/apps/forge/lib/mix/tasks/namespace.ex @@ -11,10 +11,10 @@ defmodule Mix.Tasks.Namespace do This task takes a single argument, which is the full path to the release. """ alias Forge.Ast - alias Mix.Tasks.Namespace.Transform + alias Forge.Namespace.Transform use Mix.Task - @dev_deps [:patch] + @dev_deps [:patch, :burrito] # Unless explicitly added, nimble_parsec won't show up as a loaded app # and will therefore not be namespaced. @no_app_deps [:nimble_parsec] @@ -23,8 +23,6 @@ defmodule Mix.Tasks.Namespace do # by this task. Plugin discovery uses this task, which happens after # namespacing. @extra_apps %{ - "proto" => "Expert", - "protocol" => "Expert", "engine" => "Engine", "expert" => "Expert", "forge" => "Forge" @@ -43,6 +41,7 @@ defmodule Mix.Tasks.Namespace do # The boot file transform just turns script files into boot files # so it must come after the script file transform Transform.Boots.apply_to_all(base_directory) + Transform.Configs.apply_to_all(base_directory) Transform.AppDirectories.apply_to_all(base_directory) end diff --git a/apps/forge/mix.exs b/apps/forge/mix.exs index 5107a5e7..0077e1b6 100644 --- a/apps/forge/mix.exs +++ b/apps/forge/mix.exs @@ -17,7 +17,7 @@ defmodule Forge.MixProject do def application do [ - extra_applications: [:logger] + extra_applications: [:logger, :sasl, :eex] ] end diff --git a/apps/engine/test/fixtures/compilation_callback_errors/.formatter.exs b/apps/forge/test/fixtures/compilation_callback_errors/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_callback_errors/.formatter.exs rename to apps/forge/test/fixtures/compilation_callback_errors/.formatter.exs diff --git a/apps/engine/test/fixtures/compilation_callback_errors/.gitignore b/apps/forge/test/fixtures/compilation_callback_errors/.gitignore similarity index 100% rename from apps/engine/test/fixtures/compilation_callback_errors/.gitignore rename to apps/forge/test/fixtures/compilation_callback_errors/.gitignore diff --git a/apps/engine/test/fixtures/compilation_callback_errors/lib/compile_callback_error.ex b/apps/forge/test/fixtures/compilation_callback_errors/lib/compile_callback_error.ex similarity index 100% rename from apps/engine/test/fixtures/compilation_callback_errors/lib/compile_callback_error.ex rename to apps/forge/test/fixtures/compilation_callback_errors/lib/compile_callback_error.ex diff --git a/apps/engine/test/fixtures/compilation_callback_errors/mix.exs b/apps/forge/test/fixtures/compilation_callback_errors/mix.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_callback_errors/mix.exs rename to apps/forge/test/fixtures/compilation_callback_errors/mix.exs diff --git a/apps/engine/test/fixtures/compilation_errors/.formatter.exs b/apps/forge/test/fixtures/compilation_errors/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_errors/.formatter.exs rename to apps/forge/test/fixtures/compilation_errors/.formatter.exs diff --git a/apps/engine/test/fixtures/compilation_errors/.gitignore b/apps/forge/test/fixtures/compilation_errors/.gitignore similarity index 100% rename from apps/engine/test/fixtures/compilation_errors/.gitignore rename to apps/forge/test/fixtures/compilation_errors/.gitignore diff --git a/apps/engine/test/fixtures/compilation_errors/README.md b/apps/forge/test/fixtures/compilation_errors/README.md similarity index 100% rename from apps/engine/test/fixtures/compilation_errors/README.md rename to apps/forge/test/fixtures/compilation_errors/README.md diff --git a/apps/engine/test/fixtures/compilation_errors/lib/compilation_errors.ex b/apps/forge/test/fixtures/compilation_errors/lib/compilation_errors.ex similarity index 100% rename from apps/engine/test/fixtures/compilation_errors/lib/compilation_errors.ex rename to apps/forge/test/fixtures/compilation_errors/lib/compilation_errors.ex diff --git a/apps/engine/test/fixtures/compilation_errors/mix.exs b/apps/forge/test/fixtures/compilation_errors/mix.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_errors/mix.exs rename to apps/forge/test/fixtures/compilation_errors/mix.exs diff --git a/apps/engine/test/fixtures/compilation_warnings/.formatter.exs b/apps/forge/test/fixtures/compilation_warnings/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_warnings/.formatter.exs rename to apps/forge/test/fixtures/compilation_warnings/.formatter.exs diff --git a/apps/engine/test/fixtures/compilation_warnings/.gitignore b/apps/forge/test/fixtures/compilation_warnings/.gitignore similarity index 100% rename from apps/engine/test/fixtures/compilation_warnings/.gitignore rename to apps/forge/test/fixtures/compilation_warnings/.gitignore diff --git a/apps/engine/test/fixtures/compilation_warnings/README.md b/apps/forge/test/fixtures/compilation_warnings/README.md similarity index 100% rename from apps/engine/test/fixtures/compilation_warnings/README.md rename to apps/forge/test/fixtures/compilation_warnings/README.md diff --git a/apps/engine/test/fixtures/compilation_warnings/lib/unused_variable.ex b/apps/forge/test/fixtures/compilation_warnings/lib/unused_variable.ex similarity index 100% rename from apps/engine/test/fixtures/compilation_warnings/lib/unused_variable.ex rename to apps/forge/test/fixtures/compilation_warnings/lib/unused_variable.ex diff --git a/apps/engine/test/fixtures/compilation_warnings/mix.exs b/apps/forge/test/fixtures/compilation_warnings/mix.exs similarity index 100% rename from apps/engine/test/fixtures/compilation_warnings/mix.exs rename to apps/forge/test/fixtures/compilation_warnings/mix.exs diff --git a/apps/engine/test/fixtures/dependency/lib/dependency/structs.ex b/apps/forge/test/fixtures/dependency/lib/dependency/structs.ex similarity index 100% rename from apps/engine/test/fixtures/dependency/lib/dependency/structs.ex rename to apps/forge/test/fixtures/dependency/lib/dependency/structs.ex diff --git a/apps/engine/test/fixtures/navigations/lib/macro_struct.ex b/apps/forge/test/fixtures/navigations/lib/macro_struct.ex similarity index 100% rename from apps/engine/test/fixtures/navigations/lib/macro_struct.ex rename to apps/forge/test/fixtures/navigations/lib/macro_struct.ex diff --git a/apps/engine/test/fixtures/navigations/lib/multi_arity.ex b/apps/forge/test/fixtures/navigations/lib/multi_arity.ex similarity index 100% rename from apps/engine/test/fixtures/navigations/lib/multi_arity.ex rename to apps/forge/test/fixtures/navigations/lib/multi_arity.ex diff --git a/apps/engine/test/fixtures/navigations/lib/my_definition.ex b/apps/forge/test/fixtures/navigations/lib/my_definition.ex similarity index 100% rename from apps/engine/test/fixtures/navigations/lib/my_definition.ex rename to apps/forge/test/fixtures/navigations/lib/my_definition.ex diff --git a/apps/engine/test/fixtures/navigations/lib/struct.ex b/apps/forge/test/fixtures/navigations/lib/struct.ex similarity index 100% rename from apps/engine/test/fixtures/navigations/lib/struct.ex rename to apps/forge/test/fixtures/navigations/lib/struct.ex diff --git a/apps/engine/test/fixtures/navigations/lib/uses.ex b/apps/forge/test/fixtures/navigations/lib/uses.ex similarity index 100% rename from apps/engine/test/fixtures/navigations/lib/uses.ex rename to apps/forge/test/fixtures/navigations/lib/uses.ex diff --git a/apps/engine/test/fixtures/navigations/mix.exs b/apps/forge/test/fixtures/navigations/mix.exs similarity index 100% rename from apps/engine/test/fixtures/navigations/mix.exs rename to apps/forge/test/fixtures/navigations/mix.exs diff --git a/apps/engine/test/fixtures/parse_errors/lib/parse_errors.ex b/apps/forge/test/fixtures/parse_errors/lib/parse_errors.ex similarity index 100% rename from apps/engine/test/fixtures/parse_errors/lib/parse_errors.ex rename to apps/forge/test/fixtures/parse_errors/lib/parse_errors.ex diff --git a/apps/engine/test/fixtures/parse_errors/mix.exs b/apps/forge/test/fixtures/parse_errors/mix.exs similarity index 100% rename from apps/engine/test/fixtures/parse_errors/mix.exs rename to apps/forge/test/fixtures/parse_errors/mix.exs diff --git a/apps/engine/test/fixtures/project/.formatter.exs b/apps/forge/test/fixtures/project/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/project/.formatter.exs rename to apps/forge/test/fixtures/project/.formatter.exs diff --git a/apps/engine/test/fixtures/project/.gitignore b/apps/forge/test/fixtures/project/.gitignore similarity index 100% rename from apps/engine/test/fixtures/project/.gitignore rename to apps/forge/test/fixtures/project/.gitignore diff --git a/apps/engine/test/fixtures/project/README.md b/apps/forge/test/fixtures/project/README.md similarity index 100% rename from apps/engine/test/fixtures/project/README.md rename to apps/forge/test/fixtures/project/README.md diff --git a/apps/engine/test/fixtures/project/lib/behaviours.ex b/apps/forge/test/fixtures/project/lib/behaviours.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/behaviours.ex rename to apps/forge/test/fixtures/project/lib/behaviours.ex diff --git a/apps/engine/test/fixtures/project/lib/default_args.ex b/apps/forge/test/fixtures/project/lib/default_args.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/default_args.ex rename to apps/forge/test/fixtures/project/lib/default_args.ex diff --git a/apps/engine/test/fixtures/project/lib/functions.ex b/apps/forge/test/fixtures/project/lib/functions.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/functions.ex rename to apps/forge/test/fixtures/project/lib/functions.ex diff --git a/apps/engine/test/fixtures/project/lib/macros.ex b/apps/forge/test/fixtures/project/lib/macros.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/macros.ex rename to apps/forge/test/fixtures/project/lib/macros.ex diff --git a/apps/engine/test/fixtures/project/lib/other_modules.ex b/apps/forge/test/fixtures/project/lib/other_modules.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/other_modules.ex rename to apps/forge/test/fixtures/project/lib/other_modules.ex diff --git a/apps/engine/test/fixtures/project/lib/project.ex b/apps/forge/test/fixtures/project/lib/project.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/project.ex rename to apps/forge/test/fixtures/project/lib/project.ex diff --git a/apps/engine/test/fixtures/project/lib/structs.ex b/apps/forge/test/fixtures/project/lib/structs.ex similarity index 100% rename from apps/engine/test/fixtures/project/lib/structs.ex rename to apps/forge/test/fixtures/project/lib/structs.ex diff --git a/apps/engine/test/fixtures/project/mix.exs b/apps/forge/test/fixtures/project/mix.exs similarity index 100% rename from apps/engine/test/fixtures/project/mix.exs rename to apps/forge/test/fixtures/project/mix.exs diff --git a/apps/engine/test/fixtures/project_metadata/.formatter.exs b/apps/forge/test/fixtures/project_metadata/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/project_metadata/.formatter.exs rename to apps/forge/test/fixtures/project_metadata/.formatter.exs diff --git a/apps/engine/test/fixtures/project_metadata/.gitignore b/apps/forge/test/fixtures/project_metadata/.gitignore similarity index 100% rename from apps/engine/test/fixtures/project_metadata/.gitignore rename to apps/forge/test/fixtures/project_metadata/.gitignore diff --git a/apps/engine/test/fixtures/project_metadata/README.md b/apps/forge/test/fixtures/project_metadata/README.md similarity index 100% rename from apps/engine/test/fixtures/project_metadata/README.md rename to apps/forge/test/fixtures/project_metadata/README.md diff --git a/apps/engine/test/fixtures/project_metadata/config/config.exs b/apps/forge/test/fixtures/project_metadata/config/config.exs similarity index 100% rename from apps/engine/test/fixtures/project_metadata/config/config.exs rename to apps/forge/test/fixtures/project_metadata/config/config.exs diff --git a/apps/engine/test/fixtures/project_metadata/lib/project_metadata.ex b/apps/forge/test/fixtures/project_metadata/lib/project_metadata.ex similarity index 100% rename from apps/engine/test/fixtures/project_metadata/lib/project_metadata.ex rename to apps/forge/test/fixtures/project_metadata/lib/project_metadata.ex diff --git a/apps/engine/test/fixtures/project_metadata/mix.exs b/apps/forge/test/fixtures/project_metadata/mix.exs similarity index 100% rename from apps/engine/test/fixtures/project_metadata/mix.exs rename to apps/forge/test/fixtures/project_metadata/mix.exs diff --git a/apps/engine/test/fixtures/umbrella/.formatter.exs b/apps/forge/test/fixtures/umbrella/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/.formatter.exs rename to apps/forge/test/fixtures/umbrella/.formatter.exs diff --git a/apps/engine/test/fixtures/umbrella/.gitignore b/apps/forge/test/fixtures/umbrella/.gitignore similarity index 100% rename from apps/engine/test/fixtures/umbrella/.gitignore rename to apps/forge/test/fixtures/umbrella/.gitignore diff --git a/apps/engine/test/fixtures/umbrella/README.md b/apps/forge/test/fixtures/umbrella/README.md similarity index 100% rename from apps/engine/test/fixtures/umbrella/README.md rename to apps/forge/test/fixtures/umbrella/README.md diff --git a/apps/engine/test/fixtures/umbrella/apps/first/.formatter.exs b/apps/forge/test/fixtures/umbrella/apps/first/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/first/.formatter.exs rename to apps/forge/test/fixtures/umbrella/apps/first/.formatter.exs diff --git a/apps/engine/test/fixtures/umbrella/apps/first/.gitignore b/apps/forge/test/fixtures/umbrella/apps/first/.gitignore similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/first/.gitignore rename to apps/forge/test/fixtures/umbrella/apps/first/.gitignore diff --git a/apps/engine/test/fixtures/umbrella/apps/first/README.md b/apps/forge/test/fixtures/umbrella/apps/first/README.md similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/first/README.md rename to apps/forge/test/fixtures/umbrella/apps/first/README.md diff --git a/apps/engine/test/fixtures/umbrella/apps/first/lib/umbrella/first.ex b/apps/forge/test/fixtures/umbrella/apps/first/lib/umbrella/first.ex similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/first/lib/umbrella/first.ex rename to apps/forge/test/fixtures/umbrella/apps/first/lib/umbrella/first.ex diff --git a/apps/engine/test/fixtures/umbrella/apps/first/mix.exs b/apps/forge/test/fixtures/umbrella/apps/first/mix.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/first/mix.exs rename to apps/forge/test/fixtures/umbrella/apps/first/mix.exs diff --git a/apps/engine/test/fixtures/umbrella/apps/second/.formatter.exs b/apps/forge/test/fixtures/umbrella/apps/second/.formatter.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/second/.formatter.exs rename to apps/forge/test/fixtures/umbrella/apps/second/.formatter.exs diff --git a/apps/engine/test/fixtures/umbrella/apps/second/.gitignore b/apps/forge/test/fixtures/umbrella/apps/second/.gitignore similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/second/.gitignore rename to apps/forge/test/fixtures/umbrella/apps/second/.gitignore diff --git a/apps/engine/test/fixtures/umbrella/apps/second/README.md b/apps/forge/test/fixtures/umbrella/apps/second/README.md similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/second/README.md rename to apps/forge/test/fixtures/umbrella/apps/second/README.md diff --git a/apps/engine/test/fixtures/umbrella/apps/second/lib/umbrella/second.ex b/apps/forge/test/fixtures/umbrella/apps/second/lib/umbrella/second.ex similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/second/lib/umbrella/second.ex rename to apps/forge/test/fixtures/umbrella/apps/second/lib/umbrella/second.ex diff --git a/apps/engine/test/fixtures/umbrella/apps/second/mix.exs b/apps/forge/test/fixtures/umbrella/apps/second/mix.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/apps/second/mix.exs rename to apps/forge/test/fixtures/umbrella/apps/second/mix.exs diff --git a/apps/engine/test/fixtures/umbrella/config/config.exs b/apps/forge/test/fixtures/umbrella/config/config.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/config/config.exs rename to apps/forge/test/fixtures/umbrella/config/config.exs diff --git a/apps/engine/test/fixtures/umbrella/mix.exs b/apps/forge/test/fixtures/umbrella/mix.exs similarity index 100% rename from apps/engine/test/fixtures/umbrella/mix.exs rename to apps/forge/test/fixtures/umbrella/mix.exs diff --git a/apps/engine/test/support/test/code_mod_case.ex b/apps/forge/test/support/test/code_mod_case.ex similarity index 95% rename from apps/engine/test/support/test/code_mod_case.ex rename to apps/forge/test/support/test/code_mod_case.ex index 9e2f49ff..1b5587bd 100644 --- a/apps/engine/test/support/test/code_mod_case.ex +++ b/apps/forge/test/support/test/code_mod_case.ex @@ -1,4 +1,4 @@ -defmodule Engine.Test.CodeMod.Case do +defmodule Forge.Test.CodeMod.Case do alias Forge.Document alias Forge.Test.CodeSigil @@ -8,7 +8,7 @@ defmodule Engine.Test.CodeMod.Case do convert_to_ast? = Keyword.get(opts, :enable_ast_conversion, true) quote do - import Engine.Test.Fixtures + import Forge.Test.Fixtures import unquote(CodeSigil), only: [sigil_q: 2] def apply_code_mod(_, _, _) do diff --git a/apps/engine/test/support/test/fixtures.ex b/apps/forge/test/support/test/fixtures.ex similarity index 95% rename from apps/engine/test/support/test/fixtures.ex rename to apps/forge/test/support/test/fixtures.ex index e16e284a..edc98e5c 100644 --- a/apps/engine/test/support/test/fixtures.ex +++ b/apps/forge/test/support/test/fixtures.ex @@ -1,4 +1,4 @@ -defmodule Engine.Test.Fixtures do +defmodule Forge.Test.Fixtures do alias Forge.Document alias Forge.Project From c3ca2e502fe21f4c541ed468cd18c5ebcbb74abc Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 20:28:12 -0300 Subject: [PATCH 11/23] refactor: rename project node modules and simplify EngineApi/EngineNode code --- apps/expert/lib/expert/application.ex | 2 +- apps/expert/lib/expert/engine_api.ex | 138 +----------------- .../{project_node.ex => engine_node.ex} | 54 ++++++- ...ode_supervisor.ex => engine_supervisor.ex} | 6 +- apps/expert/lib/expert/port.ex | 90 +++++++++++- .../lib/expert/project/dynamic_supervisor.ex | 9 ++ apps/expert/lib/expert/project/node.ex | 3 +- apps/expert/lib/expert/project/supervisor.ex | 32 +--- apps/expert/test/engine/build_test.exs | 7 +- .../code_intelligence/definition_test.exs | 7 +- apps/expert/test/engine/engine_test.exs | 5 +- ...ect_node_test.exs => engine_node_test.exs} | 21 ++- apps/expert/test/expert/project/node_test.exs | 10 +- .../provider/handlers/code_lens_test.exs | 2 +- .../provider/handlers/formatting_test.exs | 5 +- .../handlers/go_to_definition_test.exs | 2 +- .../expert/provider/handlers/hover_test.exs | 2 +- .../test/support/test/completion_case.ex | 2 +- 18 files changed, 192 insertions(+), 205 deletions(-) rename apps/expert/lib/expert/{project_node.ex => engine_node.ex} (81%) rename apps/expert/lib/expert/{project_node_supervisor.ex => engine_supervisor.ex} (78%) create mode 100644 apps/expert/lib/expert/project/dynamic_supervisor.ex rename apps/expert/test/expert/{project_node_test.exs => engine_node_test.exs} (57%) diff --git a/apps/expert/lib/expert/application.ex b/apps/expert/lib/expert/application.ex index 4455bc61..de275596 100644 --- a/apps/expert/lib/expert/application.ex +++ b/apps/expert/lib/expert/application.ex @@ -13,7 +13,7 @@ defmodule Expert.Application do def start(_type, _args) do children = [ document_store_child_spec(), - {DynamicSupervisor, Expert.Project.Supervisor.options()}, + {DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}, {DynamicSupervisor, name: Expert.DynamicSupervisor}, {GenLSP.Assigns, [name: Expert.Assigns]}, {Task.Supervisor, name: :expert_task_queue}, diff --git a/apps/expert/lib/expert/engine_api.ex b/apps/expert/lib/expert/engine_api.ex index 83b04255..46aa28e3 100644 --- a/apps/expert/lib/expert/engine_api.ex +++ b/apps/expert/lib/expert/engine_api.ex @@ -1,5 +1,5 @@ defmodule Expert.EngineApi do - alias Expert.ProjectNode + alias Expert.EngineNode alias Forge.Ast.Analysis alias Forge.Ast.Env alias Forge.Document @@ -11,146 +11,12 @@ defmodule Expert.EngineApi do require Logger - def start_link(%Project{} = project) do - :ok = ensure_epmd_started() - start_net_kernel(project) - - node = Project.node_name(project) - - with {:ok, node_pid} <- ProjectNode.start(project, glob_paths()), - :ok <- ensure_apps_started(node) do - {:ok, node, node_pid} - end - end - - defp start_net_kernel(%Project{} = project) do - manager = Project.manager_node_name(project) - :net_kernel.start(manager, %{name_domain: :longnames}) - end - - defp ensure_apps_started(node) do - :rpc.call(node, Engine, :ensure_apps_started, []) - end - - defp ensure_epmd_started do - case System.cmd("epmd", ~w(-daemon)) do - {"", 0} -> - :ok - - _ -> - {:error, :epmd_failed} - end - end - def call(%Project{} = project, m, f, a \\ []) do project |> Project.node_name() |> :erpc.call(m, f, a) end - def elixir_executable(%Project{} = project) do - root_path = Project.root_path(project) - - {path_result, env} = - with nil <- version_manager_path_and_env("asdf", root_path), - nil <- version_manager_path_and_env("mise", root_path), - nil <- version_manager_path_and_env("rtx", root_path) do - {File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()} - end - - case path_result do - nil -> - {:error, :no_elixir} - - executable when is_binary(executable) -> - {:ok, executable, env} - end - end - - @excluded_apps [:patch, :nimble_parsec] - @allowed_apps [:engine | Mix.Project.deps_apps()] -- @excluded_apps - - defp app_globs do - app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) - ["/**/priv" | app_globs] - end - - def glob_paths do - for entry <- :code.get_path(), - entry_string = List.to_string(entry), - entry_string != ".", - Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do - entry - end - end - - defp version_manager_path_and_env(manager, root_path) do - with true <- is_binary(System.find_executable(manager)), - env = reset_env(manager, root_path), - {path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do - {String.trim(path), env} - else - _ -> - nil - end - end - - # We launch expert by asking the version managers to provide an environment, - # which contains path munging. This initial environment is present in the running - # VM, and needs to be undone so we can find the correct elixir executable in the project. - defp reset_env("asdf", _root_path) do - orig_path = System.get_env("PATH_SAVE", System.get_env("PATH")) - - Enum.map(System.get_env(), fn - {"ASDF_ELIXIR_VERSION", _} -> {"ASDF_ELIXIR_VERSION", nil} - {"ASDF_ERLANG_VERSION", _} -> {"ASDF_ERLANG_VERSION", nil} - {"PATH", _} -> {"PATH", orig_path} - other -> other - end) - end - - defp reset_env("rtx", root_path) do - {env, _} = System.cmd("rtx", ~w(env -s bash), cd: root_path) - - env - |> String.trim() - |> String.split("\n") - |> Enum.map(fn - "export " <> key_and_value -> - [key, value] = - key_and_value - |> String.split("=", parts: 2) - |> Enum.map(&String.trim/1) - - {key, value} - - _ -> - nil - end) - |> Enum.reject(&is_nil/1) - end - - defp reset_env("mise", root_path) do - {env, _} = System.cmd("mise", ~w(env -s bash), cd: root_path) - - env - |> String.trim() - |> String.split("\n") - |> Enum.map(fn - "export " <> key_and_value -> - [key, value] = - key_and_value - |> String.split("=", parts: 2) - |> Enum.map(&String.trim/1) - - {key, value} - - _ -> - nil - end) - |> Enum.reject(&is_nil/1) - end - def schedule_compile(%Project{} = project, force?) do call(project, Engine, :schedule_compile, [force?]) end @@ -281,5 +147,5 @@ defmodule Expert.EngineApi do call(project, Engine, :workspace_symbols, [query]) end - defdelegate stop(project), to: ProjectNode + defdelegate stop(project), to: EngineNode end diff --git a/apps/expert/lib/expert/project_node.ex b/apps/expert/lib/expert/engine_node.ex similarity index 81% rename from apps/expert/lib/expert/project_node.ex rename to apps/expert/lib/expert/engine_node.ex index 9fa63a9d..0722491d 100644 --- a/apps/expert/lib/expert/project_node.ex +++ b/apps/expert/lib/expert/engine_node.ex @@ -1,4 +1,4 @@ -defmodule Expert.ProjectNode do +defmodule Expert.EngineNode do alias Forge.Project require Logger @@ -103,18 +103,58 @@ defmodule Expert.ProjectNode do end end - alias Expert.ProjectNodeSupervisor + alias Expert.EngineSupervisor alias Forge.Document use GenServer - def start(project, paths) do + def start(project) do + :ok = ensure_epmd_started() + start_net_kernel(project) + node_name = Project.node_name(project) bootstrap_args = [project, Document.Store.entropy(), all_app_configs()] - with {:ok, node_pid} <- ProjectNodeSupervisor.start_project_node(project), - :ok <- start_node(project, paths), - :ok <- :rpc.call(node_name, Engine.Bootstrap, :init, bootstrap_args) do - {:ok, node_pid} + with {:ok, node_pid} <- EngineSupervisor.start_project_node(project), + :ok <- start_node(project, glob_paths()), + :ok <- :rpc.call(node_name, Engine.Bootstrap, :init, bootstrap_args), + :ok <- ensure_apps_started(node_name) do + {:ok, node_name, node_pid} + end + end + + defp start_net_kernel(%Project{} = project) do + manager = Project.manager_node_name(project) + :net_kernel.start(manager, %{name_domain: :longnames}) + end + + defp ensure_apps_started(node) do + :rpc.call(node, Engine, :ensure_apps_started, []) + end + + defp ensure_epmd_started do + case System.cmd("epmd", ~w(-daemon)) do + {"", 0} -> + :ok + + _ -> + {:error, :epmd_failed} + end + end + + @excluded_apps [:patch, :nimble_parsec] + @allowed_apps [:engine | Mix.Project.deps_apps()] -- @excluded_apps + + defp app_globs do + app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) + ["/**/priv" | app_globs] + end + + def glob_paths do + for entry <- :code.get_path(), + entry_string = List.to_string(entry), + entry_string != ".", + Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do + entry end end diff --git a/apps/expert/lib/expert/project_node_supervisor.ex b/apps/expert/lib/expert/engine_supervisor.ex similarity index 78% rename from apps/expert/lib/expert/project_node_supervisor.ex rename to apps/expert/lib/expert/engine_supervisor.ex index 529b2390..680b40d5 100644 --- a/apps/expert/lib/expert/project_node_supervisor.ex +++ b/apps/expert/lib/expert/engine_supervisor.ex @@ -1,7 +1,7 @@ -defmodule Expert.ProjectNodeSupervisor do +defmodule Expert.EngineSupervisor do use DynamicSupervisor - alias Expert.ProjectNode + alias Expert.EngineNode alias Forge.Project @dialyzer {:no_return, start_link: 1} @@ -18,7 +18,7 @@ defmodule Expert.ProjectNodeSupervisor do end def start_project_node(%Project{} = project) do - DynamicSupervisor.start_child(__MODULE__, ProjectNode.child_spec(project)) + DynamicSupervisor.start_child(__MODULE__, EngineNode.child_spec(project)) end @impl true diff --git a/apps/expert/lib/expert/port.ex b/apps/expert/lib/expert/port.ex index 4b601387..d85096a1 100644 --- a/apps/expert/lib/expert/port.ex +++ b/apps/expert/lib/expert/port.ex @@ -17,11 +17,11 @@ defmodule Expert.Port do Launches elixir in a port. This function takes the project's context into account and looks for the executable via calling - `Expert.EngineApi.elixir_executable(project)`. Environment variables are also retrieved with that call. + `elixir_executable(project)`. Environment variables are also retrieved with that call. """ @spec open_elixir(Project.t(), open_opts()) :: port() def open_elixir(%Project{} = project, opts) do - {:ok, elixir_executable, environment_variables} = Expert.EngineApi.elixir_executable(project) + {:ok, elixir_executable, environment_variables} = elixir_executable(project) opts = opts @@ -31,6 +31,92 @@ defmodule Expert.Port do open(project, elixir_executable, opts) end + def elixir_executable(%Project{} = project) do + root_path = Project.root_path(project) + + {path_result, env} = + with nil <- version_manager_path_and_env("asdf", root_path), + nil <- version_manager_path_and_env("mise", root_path), + nil <- version_manager_path_and_env("rtx", root_path) do + {File.cd!(root_path, fn -> System.find_executable("elixir") end), System.get_env()} + end + + case path_result do + nil -> + {:error, :no_elixir} + + executable when is_binary(executable) -> + {:ok, executable, env} + end + end + + defp version_manager_path_and_env(manager, root_path) do + with true <- is_binary(System.find_executable(manager)), + env = reset_env(manager, root_path), + {path, 0} <- System.cmd(manager, ~w(which elixir), cd: root_path, env: env) do + {String.trim(path), env} + else + _ -> + nil + end + end + + # We launch expert by asking the version managers to provide an environment, + # which contains path munging. This initial environment is present in the running + # VM, and needs to be undone so we can find the correct elixir executable in the project. + defp reset_env("asdf", _root_path) do + orig_path = System.get_env("PATH_SAVE", System.get_env("PATH")) + + Enum.map(System.get_env(), fn + {"ASDF_ELIXIR_VERSION", _} -> {"ASDF_ELIXIR_VERSION", nil} + {"ASDF_ERLANG_VERSION", _} -> {"ASDF_ERLANG_VERSION", nil} + {"PATH", _} -> {"PATH", orig_path} + other -> other + end) + end + + defp reset_env("rtx", root_path) do + {env, _} = System.cmd("rtx", ~w(env -s bash), cd: root_path) + + env + |> String.trim() + |> String.split("\n") + |> Enum.map(fn + "export " <> key_and_value -> + [key, value] = + key_and_value + |> String.split("=", parts: 2) + |> Enum.map(&String.trim/1) + + {key, value} + + _ -> + nil + end) + |> Enum.reject(&is_nil/1) + end + + defp reset_env("mise", root_path) do + {env, _} = System.cmd("mise", ~w(env -s bash), cd: root_path) + + env + |> String.trim() + |> String.split("\n") + |> Enum.map(fn + "export " <> key_and_value -> + [key, value] = + key_and_value + |> String.split("=", parts: 2) + |> Enum.map(&String.trim/1) + + {key, value} + + _ -> + nil + end) + |> Enum.reject(&is_nil/1) + end + @doc """ Launches an executable in the project context via a port. """ diff --git a/apps/expert/lib/expert/project/dynamic_supervisor.ex b/apps/expert/lib/expert/project/dynamic_supervisor.ex new file mode 100644 index 00000000..93557863 --- /dev/null +++ b/apps/expert/lib/expert/project/dynamic_supervisor.ex @@ -0,0 +1,9 @@ +defmodule Expert.Project.DynamicSupervisor do + def name do + Expert.ProjectSupervisor + end + + def options do + [name: name(), strategy: :one_for_one] + end +end diff --git a/apps/expert/lib/expert/project/node.ex b/apps/expert/lib/expert/project/node.ex index b605f79d..5f8b6b3b 100644 --- a/apps/expert/lib/expert/project/node.ex +++ b/apps/expert/lib/expert/project/node.ex @@ -14,6 +14,7 @@ defmodule Expert.Project.Node do alias Forge.Project alias Expert.EngineApi + alias Expert.EngineNode alias Expert.Project.Progress require Logger @@ -92,7 +93,7 @@ defmodule Expert.Project.Node do # private api defp start_node(%Project{} = project) do - with {:ok, node, node_pid} <- EngineApi.start_link(project) do + with {:ok, node, node_pid} <- EngineNode.start(project) do Node.monitor(node, true) {:ok, State.new(project, node, node_pid)} end diff --git a/apps/expert/lib/expert/project/supervisor.ex b/apps/expert/lib/expert/project/supervisor.ex index 5e280987..9d9de84c 100644 --- a/apps/expert/lib/expert/project/supervisor.ex +++ b/apps/expert/lib/expert/project/supervisor.ex @@ -4,37 +4,19 @@ defmodule Expert.Project.Supervisor do alias Expert.Project.Node alias Expert.Project.Progress alias Expert.Project.SearchListener - alias Expert.ProjectNodeSupervisor + alias Expert.EngineSupervisor alias Forge.Project - # TODO: this module is slightly weird - # it is a module based supervisor, but has lots of dynamic supervisor functions - # what I learned is that in Expert.Application, it is starting an ad hoc - # dynamic supervisor, calling a function from this module - # Later, when the server is initializing, it calls the start function in - # this module, which starts a normal supervisor, which the start_link and - # init callbacks will be called - # my suggestion is to separate the dynamic supervisor functionalities from - # this module into its own module - use Supervisor - def dynamic_supervisor_name do - Expert.ProjectSupervisor - end - - def options do - [name: dynamic_supervisor_name(), strategy: :one_for_one] - end - def start_link(%Project{} = project) do - Supervisor.start_link(__MODULE__, project, name: supervisor_name(project)) + Supervisor.start_link(__MODULE__, project, name: name(project)) end def init(%Project{} = project) do children = [ {Progress, project}, - {ProjectNodeSupervisor, project}, + {EngineSupervisor, project}, {Node, project}, {Diagnostics, project}, {Intelligence, project}, @@ -45,19 +27,19 @@ defmodule Expert.Project.Supervisor do end def start(%Project{} = project) do - DynamicSupervisor.start_child(dynamic_supervisor_name(), {__MODULE__, project}) + DynamicSupervisor.start_child(Expert.Project.DynamicSupervisor.name(), {__MODULE__, project}) end def stop(%Project{} = project) do pid = project - |> supervisor_name() + |> name() |> Process.whereis() - DynamicSupervisor.terminate_child(dynamic_supervisor_name(), pid) + DynamicSupervisor.terminate_child(Expert.Project.DynamicSupervisor.name(), pid) end - defp supervisor_name(%Project{} = project) do + defp name(%Project{} = project) do :"#{Project.name(project)}::supervisor" end end diff --git a/apps/expert/test/engine/build_test.exs b/apps/expert/test/engine/build_test.exs index eaf6d057..36f3a5e8 100644 --- a/apps/expert/test/engine/build_test.exs +++ b/apps/expert/test/engine/build_test.exs @@ -2,7 +2,8 @@ defmodule Engine.BuildTest do alias Elixir.Features alias Engine.Build alias Expert.EngineApi - alias Expert.ProjectNodeSupervisor + alias Expert.EngineNode + alias Expert.EngineSupervisor alias Forge.Document alias Forge.EngineApi.Messages alias Forge.Plugin.V1.Diagnostic @@ -41,8 +42,8 @@ defmodule Engine.BuildTest do |> Project.workspace_path() |> File.rm_rf() - {:ok, _} = start_supervised({ProjectNodeSupervisor, project}) - {:ok, _, _} = EngineApi.start_link(project) + {:ok, _} = start_supervised({EngineSupervisor, project}) + {:ok, _, _} = EngineNode.start(project) EngineApi.register_listener(project, self(), [:all]) {:ok, project} diff --git a/apps/expert/test/engine/code_intelligence/definition_test.exs b/apps/expert/test/engine/code_intelligence/definition_test.exs index b2b849e6..f2893f85 100644 --- a/apps/expert/test/engine/code_intelligence/definition_test.exs +++ b/apps/expert/test/engine/code_intelligence/definition_test.exs @@ -1,7 +1,8 @@ defmodule Engine.CodeIntelligence.DefinitionTest do alias Engine.Search alias Expert.EngineApi - alias Expert.ProjectNodeSupervisor + alias Expert.EngineNode + alias Expert.EngineSupervisor alias Forge.Document import Forge.EngineApi.Messages @@ -44,8 +45,8 @@ defmodule Engine.CodeIntelligence.DefinitionTest do setup_all do project = project(:navigations) start_supervised!({Document.Store, derive: [analysis: &Forge.Ast.analyze/1]}) - {:ok, _} = start_supervised({ProjectNodeSupervisor, project}) - {:ok, _, _} = EngineApi.start_link(project) + {:ok, _} = start_supervised({EngineSupervisor, project}) + {:ok, _, _} = EngineNode.start(project) EngineApi.register_listener(project, self(), [:all]) EngineApi.schedule_compile(project, true) diff --git a/apps/expert/test/engine/engine_test.exs b/apps/expert/test/engine/engine_test.exs index 4c512c23..f4b0c5b3 100644 --- a/apps/expert/test/engine/engine_test.exs +++ b/apps/expert/test/engine/engine_test.exs @@ -1,5 +1,6 @@ defmodule EngineTest do alias Expert.EngineApi + alias Expert.EngineNode alias Forge.Document alias Forge.Project @@ -8,8 +9,8 @@ defmodule EngineTest do import Forge.Test.Fixtures def start_project(%Project{} = project) do - start_supervised!({Expert.ProjectNodeSupervisor, project}) - assert {:ok, _, _} = EngineApi.start_link(project) + start_supervised!({Expert.EngineSupervisor, project}) + assert {:ok, _, _} = EngineNode.start(project) :ok end diff --git a/apps/expert/test/expert/project_node_test.exs b/apps/expert/test/expert/engine_node_test.exs similarity index 57% rename from apps/expert/test/expert/project_node_test.exs rename to apps/expert/test/expert/engine_node_test.exs index 600fa34b..e45d5d81 100644 --- a/apps/expert/test/expert/project_node_test.exs +++ b/apps/expert/test/expert/engine_node_test.exs @@ -1,7 +1,6 @@ -defmodule Expert.ProjectNodeTest do - alias Expert.EngineApi - alias Expert.ProjectNode - alias Expert.ProjectNodeSupervisor +defmodule Expert.EngineNodeTest do + alias Expert.EngineNode + alias Expert.EngineSupervisor import Forge.Test.EventualAssertions import Forge.Test.Fixtures @@ -10,18 +9,18 @@ defmodule Expert.ProjectNodeTest do setup do project = project() - start_supervised!({ProjectNodeSupervisor, project}) + start_supervised!({EngineSupervisor, project}) {:ok, %{project: project}} end test "it should be able to stop a project node and won't restart", %{project: project} do - {:ok, _node_name, _} = EngineApi.start_link(project) + {:ok, _node_name, _} = EngineNode.start(project) - project_alive? = project |> ProjectNode.name() |> Process.whereis() |> Process.alive?() + project_alive? = project |> EngineNode.name() |> Process.whereis() |> Process.alive?() assert project_alive? - assert :ok = ProjectNode.stop(project, 1500) - assert Process.whereis(ProjectNode.name(project)) == nil + assert :ok = EngineNode.stop(project, 1500) + assert Process.whereis(EngineNode.name(project)) == nil end test "it should be stopped atomically when the startup process is dead", %{project: project} do @@ -29,13 +28,13 @@ defmodule Expert.ProjectNodeTest do linked_node_process = spawn(fn -> - {:ok, _node_name, _} = EngineApi.start_link(project) + {:ok, _node_name, _} = EngineNode.start(project) send(test_pid, :started) end) assert_receive :started, 1500 - node_process_name = ProjectNode.name(project) + node_process_name = EngineNode.name(project) assert node_process_name |> Process.whereis() |> Process.alive?() Process.exit(linked_node_process, :kill) diff --git a/apps/expert/test/expert/project/node_test.exs b/apps/expert/test/expert/project/node_test.exs index 492e9e51..58f5d917 100644 --- a/apps/expert/test/expert/project/node_test.exs +++ b/apps/expert/test/expert/project/node_test.exs @@ -1,6 +1,6 @@ defmodule Expert.Project.NodeTest do alias Expert.EngineApi - alias Expert.Project.Node, as: ProjectNode + alias Expert.Project.Node, as: EngineNode import Forge.Test.Fixtures import Forge.EngineApi.Messages @@ -11,7 +11,7 @@ defmodule Expert.Project.NodeTest do setup do project = project() - {:ok, _} = start_supervised({DynamicSupervisor, Expert.Project.Supervisor.options()}) + {:ok, _} = start_supervised({DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}) {:ok, _} = start_supervised({Expert.Project.Supervisor, project}) :ok = EngineApi.register_listener(project, self(), [project_compiled()]) @@ -30,7 +30,7 @@ defmodule Expert.Project.NodeTest do end test "the node is restarted when it goes down", %{project: project} do - node_name = ProjectNode.node_name(project) + node_name = EngineNode.node_name(project) old_pid = node_pid(project) :ok = EngineApi.stop(project) @@ -42,7 +42,7 @@ defmodule Expert.Project.NodeTest do end test "the node restarts when the supervisor pid is killed", %{project: project} do - node_name = ProjectNode.node_name(project) + node_name = EngineNode.node_name(project) supervisor_pid = EngineApi.call(project, Process, :whereis, [Engine.Supervisor]) assert is_pid(supervisor_pid) @@ -52,7 +52,7 @@ defmodule Expert.Project.NodeTest do defp node_pid(project) do project - |> Expert.ProjectNode.name() + |> Expert.EngineNode.name() |> Process.whereis() end end diff --git a/apps/expert/test/expert/provider/handlers/code_lens_test.exs b/apps/expert/test/expert/provider/handlers/code_lens_test.exs index f83b11e7..3c71541a 100644 --- a/apps/expert/test/expert/provider/handlers/code_lens_test.exs +++ b/apps/expert/test/expert/provider/handlers/code_lens_test.exs @@ -19,7 +19,7 @@ defmodule Expert.Provider.Handlers.CodeLensTest do start_supervised(Document.Store) project = project(:umbrella) - start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) + start_supervised!({DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) EngineApi.register_listener(project, self(), [project_compiled()]) diff --git a/apps/expert/test/expert/provider/handlers/formatting_test.exs b/apps/expert/test/expert/provider/handlers/formatting_test.exs index 7324ba91..9471c839 100644 --- a/apps/expert/test/expert/provider/handlers/formatting_test.exs +++ b/apps/expert/test/expert/provider/handlers/formatting_test.exs @@ -1,5 +1,6 @@ defmodule Expert.Provider.Handlers.FormattingTest do alias Expert.EngineApi + alias Expert.EngineNode alias Forge.Document alias Forge.EngineApi.Messages @@ -11,8 +12,8 @@ defmodule Expert.Provider.Handlers.FormattingTest do end def with_real_project(%{project: project}) do - {:ok, _} = start_supervised({Expert.ProjectNodeSupervisor, project}) - {:ok, _, _} = EngineApi.start_link(project) + {:ok, _} = start_supervised({Expert.EngineSupervisor, project}) + {:ok, _, _} = EngineNode.start(project) EngineApi.register_listener(project, self(), [:all]) :ok end diff --git a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs index 21576285..499296b9 100644 --- a/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs +++ b/apps/expert/test/expert/provider/handlers/go_to_definition_test.exs @@ -16,7 +16,7 @@ defmodule Expert.Provider.Handlers.GoToDefinitionTest do project = project(:navigations) start_supervised!(Expert.Application.document_store_child_spec()) - start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) + start_supervised!({DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) EngineApi.register_listener(project, self(), [ diff --git a/apps/expert/test/expert/provider/handlers/hover_test.exs b/apps/expert/test/expert/provider/handlers/hover_test.exs index 2c58d7ee..2142413b 100644 --- a/apps/expert/test/expert/provider/handlers/hover_test.exs +++ b/apps/expert/test/expert/provider/handlers/hover_test.exs @@ -21,7 +21,7 @@ defmodule Expert.Provider.Handlers.HoverTest do project = Fixtures.project() start_supervised!(Expert.Application.document_store_child_spec()) - start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) + start_supervised!({DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) :ok = EngineApi.register_listener(project, self(), [Messages.project_compiled()]) diff --git a/apps/expert/test/support/test/completion_case.ex b/apps/expert/test/support/test/completion_case.ex index dc965231..2b313f1f 100644 --- a/apps/expert/test/support/test/completion_case.ex +++ b/apps/expert/test/support/test/completion_case.ex @@ -18,7 +18,7 @@ defmodule Expert.Test.Expert.CompletionCase do setup_all do project = project() - start_supervised!({DynamicSupervisor, Expert.Project.Supervisor.options()}) + start_supervised!({DynamicSupervisor, Expert.Project.DynamicSupervisor.options()}) start_supervised!({Expert.Project.Supervisor, project}) EngineApi.register_listener(project, self(), [ From 78481081ff9c17b1f5c83cab1d121b08fd0691c0 Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 17:35:19 -0300 Subject: [PATCH 12/23] refactor: use Burrito for packaging --- .github/workflows/release.yml | 14 +- .gitignore | 3 + Makefile | 42 ++- apps/engine/priv/.gitkeep | 0 apps/expert/bin/activate_version_manager.sh | 100 ------ apps/expert/bin/boot.exs | 29 -- apps/expert/bin/debug_shell.sh | 1 - apps/expert/bin/start_expert.sh | 23 -- apps/expert/config/config.exs | 8 + apps/expert/lib/expert/boot.ex | 148 --------- apps/expert/lib/expert/engine_node.ex | 41 ++- apps/expert/lib/expert/iex/helpers.ex | 242 -------------- apps/expert/lib/mix/tasks/package.ex | 339 -------------------- apps/expert/mix.exs | 40 ++- apps/expert/mix.lock | 9 + apps/expert/test/expert/boot_test.exs | 58 ---- mix.lock | 10 +- 17 files changed, 134 insertions(+), 973 deletions(-) delete mode 100644 apps/engine/priv/.gitkeep delete mode 100755 apps/expert/bin/activate_version_manager.sh delete mode 100644 apps/expert/bin/boot.exs delete mode 100755 apps/expert/bin/start_expert.sh delete mode 100644 apps/expert/lib/expert/boot.ex delete mode 100644 apps/expert/lib/expert/iex/helpers.ex delete mode 100644 apps/expert/lib/mix/tasks/package.ex delete mode 100644 apps/expert/test/expert/boot_test.exs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e714ab11..462c1ecd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,23 +32,13 @@ jobs: restore-keys: | ${{ runner.os }}-mix-${{ env.cache-name }}- - - name: Install dependencies - run: mix deps.get - - - name: Compile - run: mix compile - - name: Build release - run: mix package --zip - - - name: Archive release - run: | - cp expert.zip expert-${{ github.ref_name }}.zip + run: make release - name: Publish release uses: ncipollo/release-action@v1 with: - artifacts: expert*.zip + artifacts: apps/expert/burrito_out/* makeLatest: true generateReleaseNotes: false allowUpdates: true diff --git a/.gitignore b/.gitignore index 71a27ae0..6c1a6e45 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,7 @@ priv/plts apps/forge/src/future_elixir_parser.erl .DS_Store + +# Ignore release artifacts +**/burrito_out .notes/ diff --git a/Makefile b/Makefile index d276731b..2ed9cfb6 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,34 @@ poncho_dirs = forge expert_credo engine expert +local_target := +ifeq ($(OS),Windows_NT) + local_target := $(local_target)windows + ifeq ($(PROCESSOR_ARCHITECTURE),AMD64) + local_target := $(local_target)_amd64 + endif + ifeq ($(PROCESSOR_ARCHITECTURE),x86) + local_target := $(local_target)_amd64 + endif +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + local_target := $(local_target)linux + endif + ifeq ($(UNAME_S),Darwin) + local_target := $(local_target)darwin + endif + UNAME_P := $(shell uname -p) + ifeq ($(UNAME_P),x86_64) + local_target := $(local_target)_amd64 + endif + ifneq ($(filter %86,$(UNAME_P)),) + local_target := $(local_target)_amd64 + endif + ifneq ($(filter arm%,$(UNAME_P)),) + local_target := $(local_target)_arm64 + endif +endif + compile.all: compile.poncho dialyzer.all: compile.poncho dialyzer.poncho @@ -43,5 +72,14 @@ dialyzer.poncho: compile.poncho compile.protocols.poncho build.engine: cd apps/engine && mix deps.get && MIX_ENV=dev mix build -package: - cd apps/expert && mix package + +release: build.engine + cd apps/expert &&\ + mix deps.get &&\ + EXPERT_RELEASE_MODE=burrito MIX_ENV=prod mix release --force --overwrite + +release.local: build.engine + cd apps/expert &&\ + mix deps.get &&\ + EXPERT_RELEASE_MODE=burrito BURRITO_TARGET=$(local_target) MIX_ENV=prod mix release --force --overwrite + diff --git a/apps/engine/priv/.gitkeep b/apps/engine/priv/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/expert/bin/activate_version_manager.sh b/apps/expert/bin/activate_version_manager.sh deleted file mode 100755 index 754ae450..00000000 --- a/apps/expert/bin/activate_version_manager.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash - -# The purpose of these functions is to detect and activate the correct -# installed version manager in the current shell session. Currently, we -# try to detect asdf, rtx, and mise (new name for rtx). -# -# The general approach involves the following steps: -# -# 1. Try to detect an already activated version manager that provides -# Elixir. If one is present, there's nothing more to do. -# 2. Try to find and activate an asdf installation. If it provides -# Elixir, we're all set. -# 3. Try to find and activate an rtx installation. If it provides -# Elixir, we're all set. -# 4. Try to find and activate a mise installation. If it provides -# Elixir, we're all set. -# - -activate_version_manager() { - if (_detect_asdf || _detect_rtx || _detect_mise); then - return 0 - fi - - echo >&2 "No activated version manager detected. Searching for version manager..." - - { _try_activating_asdf && _detect_asdf; } || - { _try_activating_rtx && _detect_rtx; } || - { _try_activating_mise && _detect_mise; } - return $? -} - -_detect_asdf() { - if command -v asdf >/dev/null && asdf which elixir >/dev/null 2>&1 && _ensure_which_elixir asdf; then - echo >&2 "Detected Elixir through asdf: $(asdf which elixir)" - return 0 - else - return 1 - fi -} - -_detect_rtx() { - if command -v rtx >/dev/null && rtx which elixir >/dev/null 2>&1 && _ensure_which_elixir rtx; then - echo >&2 "Detected Elixir through rtx: $(rtx which elixir)" - return 0 - else - return 1 - fi -} - -_detect_mise() { - if command -v mise >/dev/null && mise which elixir >/dev/null 2>&1 && _ensure_which_elixir mise; then - echo >&2 "Detected Elixir through mise: $(mise which elixir)" - return 0 - else - return 1 - fi -} - -_ensure_which_elixir() { - [[ $(which elixir) == *"$1"* ]] - return $? -} - -_try_activating_asdf() { - local asdf_dir="${ASDF_DIR:-"$HOME/.asdf"}" - local asdf_vm="$asdf_dir/asdf.sh" - - if test -f "$asdf_vm"; then - echo >&2 "Found asdf. Activating..." - # shellcheck disable=SC1090 - . "$asdf_vm" - return $? - else - return 1 - fi -} - -_try_activating_rtx() { - if which rtx >/dev/null; then - echo >&2 "Found rtx. Activating..." - eval "$(rtx activate bash)" - eval "$(rtx env)" - return $? - else - return 1 - fi -} - -_try_activating_mise() { - if which mise >/dev/null; then - echo >&2 "Found mise. Activating..." - eval "$(mise activate bash)" - eval "$(mise env)" - return $? - else - return 1 - fi -} - -activate_version_manager diff --git a/apps/expert/bin/boot.exs b/apps/expert/bin/boot.exs deleted file mode 100644 index 551a64db..00000000 --- a/apps/expert/bin/boot.exs +++ /dev/null @@ -1,29 +0,0 @@ -script_dir = __DIR__ - -Enum.each(["consolidated", "config", "priv"], fn dir -> - [script_dir, "..", dir] - |> Path.join() - |> Code.append_path() -end) - -[script_dir, "..", "lib", "*.ez"] -|> Path.join() -|> Path.wildcard() -|> Enum.each(fn archive_path -> - lib = - archive_path - |> Path.basename() - |> String.replace_suffix(".ez", "") - - [archive_path, lib, "ebin"] - |> Path.join() - |> Code.append_path() -end) - -XPExpert.Boot.start() - -if System.get_env("XP_HALT_AFTER_BOOT") do - require Logger - Logger.warning("Shutting down (XP_HALT_AFTER_BOOT)") - System.halt() -end diff --git a/apps/expert/bin/debug_shell.sh b/apps/expert/bin/debug_shell.sh index 4d4a80c8..cbd73f28 100755 --- a/apps/expert/bin/debug_shell.sh +++ b/apps/expert/bin/debug_shell.sh @@ -5,5 +5,4 @@ node_name=$(epmd -names | grep manager-"$project_name" | awk '{print $2}') iex --name "shell@127.0.0.1" \ --remsh "${node_name}" \ - --dot-iex .iex.namespaced.exs \ --cookie expert diff --git a/apps/expert/bin/start_expert.sh b/apps/expert/bin/start_expert.sh deleted file mode 100755 index ee813daf..00000000 --- a/apps/expert/bin/start_expert.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -set -o pipefail - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" - -# shellcheck disable=SC1091 -if ! . "$script_dir"/activate_version_manager.sh; then - echo >&2 "Could not activate a version manager. Trying system installation." -fi - -case $1 in - iex) - elixir_command=iex - ;; - *) - elixir_command=elixir - ;; -esac - -$elixir_command \ - --cookie "expert" \ - --no-halt \ - "$script_dir/boot.exs" diff --git a/apps/expert/config/config.exs b/apps/expert/config/config.exs index 45a44d26..f94d6393 100644 --- a/apps/expert/config/config.exs +++ b/apps/expert/config/config.exs @@ -5,4 +5,12 @@ config :snowflake, # First second of 2024 epoch: 1_704_070_800_000 +case System.get_env("EXPERT_RELEASE_MODE", "plain") do + "burrito" -> + config :expert, arg_parser: {Burrito.Util.Args, :get_arguments, []} + + "plain" -> + config :expert, arg_parser: {System, :argv, []} +end + import_config("#{config_env()}.exs") diff --git a/apps/expert/lib/expert/boot.ex b/apps/expert/lib/expert/boot.ex deleted file mode 100644 index 9d5e6073..00000000 --- a/apps/expert/lib/expert/boot.ex +++ /dev/null @@ -1,148 +0,0 @@ -defmodule Expert.Boot do - @moduledoc """ - This module is called when the server starts by the start script. - - Packaging will ensure that config.exs and runtime.exs will be visible to the `:code` module - """ - alias Forge.VM.Versions - alias Future.Code - require Logger - - # halt/1 will generate a "no local return" error, which is exactly right, but that's it's _job_ - @dialyzer {:nowarn_function, halt: 1} - - @env Mix.env() - @target Mix.target() - @dep_apps Enum.map(Mix.Dep.cached(), & &1.app) - - def start do - {:ok, _} = Application.ensure_all_started(:mix) - - Application.stop(:logger) - load_config() - Application.ensure_all_started(:logger) - - Enum.each(@dep_apps, &load_app_modules/1) - - case detect_errors() do - [] -> - :ok - - errors -> - errors - |> Enum.join("\n\n") - |> halt() - end - - Application.ensure_all_started(:expert) - end - - @doc false - def detect_errors do - versioning_errors() - end - - defp load_config do - config = read_config("config.exs") - runtime = read_config("runtime.exs") - merged_config = Config.Reader.merge(config, runtime) - apply_config(merged_config) - end - - defp apply_config(configs) do - for {app_name, keywords} <- configs, - {config_key, config_value} <- keywords do - Application.put_env(app_name, config_key, config_value) - end - end - - defp read_config(file_name) do - case where_is_file(String.to_charlist(file_name)) do - {:ok, path} -> - Config.Reader.read!(path, env: @env, target: @target) - - _ -> - [] - end - end - - defp where_is_file(file_name) do - case :code.where_is_file(file_name) do - :non_existing -> - :error - - path -> - {:ok, List.to_string(path)} - end - end - - defp load_app_modules(app_name) do - Application.ensure_loaded(app_name) - modules = Application.spec(app_name, :modules) - Code.ensure_all_loaded!(modules) - end - - @allowed_elixir %{ - "1.15.0" => ">= 1.15.8", - "1.16.0" => ">= 1.16.0", - "1.17.0-rc" => ">= 1.17.0-rc", - "1.17.0" => ">= 1.17.0", - "1.18.0" => ">= 1.18.1" - } - @allowed_erlang %{ - "24" => ">= 24.3.4", - "25" => ">= 25.0.0", - "26" => ">= 26.0.2", - "27" => ">= 27.0.0" - } - - defp versioning_errors do - versions = Versions.to_versions(Versions.current()) - - elixir_base = to_string(%Version{versions.elixir | patch: 0}) - erlang_base = to_string(versions.erlang.major) - - detected_elixir_range = Map.get(@allowed_elixir, elixir_base, false) - detected_erlang_range = Map.get(@allowed_erlang, erlang_base, false) - - elixir_ok? = detected_elixir_range && Version.match?(versions.elixir, detected_elixir_range) - erlang_ok? = detected_erlang_range && Version.match?(versions.erlang, detected_erlang_range) - - errors = [ - unless elixir_ok? do - """ - FATAL: Expert is not compatible with Elixir #{versions.elixir} - - Expert is compatible with the following versions of Elixir: - - #{format_allowed_versions(@allowed_elixir)} - """ - end, - unless erlang_ok? do - """ - FATAL: Expert is not compatible with Erlang/OTP #{versions.erlang} - - Expert is compatible with the following versions of Erlang/OTP: - - #{format_allowed_versions(@allowed_erlang)} - """ - end - ] - - Enum.filter(errors, &Function.identity/1) - end - - defp format_allowed_versions(%{} = versions) do - versions - |> Map.values() - |> Enum.sort() - |> Enum.map_join("\n", fn range -> " #{range}" end) - end - - defp halt(message) do - Mix.Shell.IO.error(message) - Logger.emergency(message) - Logger.flush() - System.halt() - end -end diff --git a/apps/expert/lib/expert/engine_node.ex b/apps/expert/lib/expert/engine_node.ex index 0722491d..be2ab967 100644 --- a/apps/expert/lib/expert/engine_node.ex +++ b/apps/expert/lib/expert/engine_node.ex @@ -141,20 +141,35 @@ defmodule Expert.EngineNode do end end - @excluded_apps [:patch, :nimble_parsec] - @allowed_apps [:engine | Mix.Project.deps_apps()] -- @excluded_apps - - defp app_globs do - app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) - ["/**/priv" | app_globs] - end + if Mix.env() == :test do + # In test environment, Expert depends on the Engine app, so we look for it + # in the expert build path. + @excluded_apps [:patch, :nimble_parsec] + @allowed_apps [:engine | Mix.Project.deps_apps()] -- @excluded_apps + + defp app_globs do + app_globs = Enum.map(@allowed_apps, fn app_name -> "/**/#{app_name}*/ebin" end) + ["/**/priv" | app_globs] + end - def glob_paths do - for entry <- :code.get_path(), - entry_string = List.to_string(entry), - entry_string != ".", - Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do - entry + def glob_paths do + for entry <- :code.get_path(), + entry_string = List.to_string(entry), + entry_string != ".", + Enum.any?(app_globs(), &PathGlob.match?(entry_string, &1, match_dot: true)) do + entry + end + end + else + # In dev and prod environments, a default build of Engine is built + # separately and copied to expert's priv directory. + # When Engine is built in CI for a version matrix, we'll need to check if + # we have the right version downloaded, and if not, we should download it. + defp glob_paths do + :expert + |> :code.priv_dir() + |> Path.join("lib/**/ebin") + |> Path.wildcard() end end diff --git a/apps/expert/lib/expert/iex/helpers.ex b/apps/expert/lib/expert/iex/helpers.ex deleted file mode 100644 index 270d0b6c..00000000 --- a/apps/expert/lib/expert/iex/helpers.ex +++ /dev/null @@ -1,242 +0,0 @@ -defmodule Expert.IEx.Helpers do - alias Expert.CodeIntelligence - alias Expert.EngineApi - alias Forge.Ast - alias Forge.Document - alias Forge.Document.Position - alias Forge.Project - alias GenLSP.Enumerations.CompletionTriggerKind - alias GenLSP.Structures - - defmacro __using__(_) do - quote do - alias Forge.Document - alias Forge.Document.Position - - import unquote(__MODULE__) - end - end - - def observer do - # credo:disable-for-next-line - apply(:observer, :start, []) - end - - def observer(project) do - project - |> ensure_project() - |> EngineApi.call(:observer, :start) - end - - def doc(text) do - doc(:expert, text) - end - - def project_node(name) do - name - |> project() - |> Project.node_name() - end - - def doc(project, text) do - root_path = - project - |> project() - |> Project.root_path() - - [root_path, "lib", "file.ex"] - |> Path.join() - |> Document.Path.to_uri() - |> Document.new(text, 0) - end - - def pos(doc, line, character) do - Position.new(doc, line, character) - end - - def compile_project(project) do - project - |> ensure_project() - |> EngineApi.schedule_compile(true) - end - - def compile_file(project, source) when is_binary(source) do - project - |> ensure_project() - |> compile_file(doc(source)) - end - - def compile_file(project, %Document{} = document) do - project - |> ensure_project() - |> EngineApi.compile_document(document) - end - - def complete(project, source, context \\ nil) - - def complete(project, source, context) when is_binary(source) do - case completion_position(source) do - {:found, line, character} -> - analysis = source |> doc() |> Ast.analyze() - complete(project, analysis, line, character, context) - - other -> - other - end - end - - def complete(project, %Ast.Analysis{} = analysis, line, character, context) do - context = - if is_nil(context) do - %Structures.CompletionContext{trigger_kind: CompletionTriggerKind.trigger_character()} - else - context - end - - position = pos(analysis.document, line, character) - - project - |> ensure_project() - |> CodeIntelligence.Completion.complete(analysis, position, context) - end - - def connect do - manager_name = manager_name() - Node.start(:"r@127.0.0.1") - Node.set_cookie(:expert) - Node.connect(:"#{manager_name}@127.0.0.1") - end - - @doc """ - Create an Expert Project for an application in the same directory as - Expert. - - Alternatively, a project for one of our test fixtures can be created - using the `fixture: true` option. - - ## Examples - - iex> project() - %Forge.Project{ - root_uri: "file:///.../expert - ... - } - - iex> project(:my_project) - %Forge.Project{ - root_uri: "file:///.../my_project" - ... - } - - iex> project(:navigations, fixture: true) - %Forge.Project{ - root_uri: "file:///.../expert/apps/engine/test/fixtures/navigations" - ... - } - - """ - def project(project \\ :expert, opts \\ []) do - project = - if opts[:fixture] do - "expert/apps/engine/test/fixtures/#{project}" - else - project - end - - # We're using a cache here because we need project's - # entropy to be the same after every call. - trans(project, fn -> - project_path = - [File.cwd!(), "..", to_string(project)] - |> Path.join() - |> Path.expand() - - project_uri = "file://#{project_path}" - Forge.Project.new(project_uri) - end) - end - - def current_project do - [prefix, _] = Node.self() |> to_string() |> String.split("@") - [_, project_name, entropy] = String.split(prefix, "-") - %{ensure_project(project_name) | entropy: entropy} - end - - def stop_project(project) do - project - |> ensure_project() - |> Expert.Project.Supervisor.stop() - end - - def start_project(project) do - project - |> ensure_project() - |> Expert.Project.Supervisor.start() - end - - def time(fun) when is_function(fun, 0) do - {elapsed_us, result} = :timer.tc(fun) - - IO.puts([ - IO.ANSI.format([:cyan, :bright, "Time: "]), - Forge.Formats.time(elapsed_us) - ]) - - result - end - - defp manager_name do - {:ok, names} = :erl_epmd.names() - - names - |> Enum.map(fn {name, _port} -> List.to_string(name) end) - |> Enum.find(&String.starts_with?(&1, "manager")) - end - - defp completion_position(source_string) do - source_string - |> String.split(["\r\n", "\r", "\n"]) - |> Enum.with_index() - |> Enum.reduce_while(:not_found, fn {line, line_number}, _ -> - if String.contains?(line, "|") do - index = - line - |> String.graphemes() - |> Enum.find_index(&(&1 == "|")) - - {:halt, {:found, line_number + 1, index + 1}} - else - {:cont, :not_found} - end - end) - end - - defp ensure_project(%Project{} = project) do - project - end - - defp ensure_project(project) when is_binary(project) do - project - |> String.to_atom() - |> project() - end - - defp ensure_project(project) when is_atom(project) do - project(project) - end - - defp trans(name, function) do - name = {__MODULE__, name} - - case :persistent_term.get(name, :undefined) do - :undefined -> - value = function.() - :persistent_term.put(name, value) - - _ -> - :ok - end - - :persistent_term.get(name) - end -end diff --git a/apps/expert/lib/mix/tasks/package.ex b/apps/expert/lib/mix/tasks/package.ex deleted file mode 100644 index b8ad8bb3..00000000 --- a/apps/expert/lib/mix/tasks/package.ex +++ /dev/null @@ -1,339 +0,0 @@ -defmodule Mix.Tasks.Package do - @moduledoc """ - Creates the Expert application's artifacts - - Expert does some strange things to its own code via namespacing, but because it does so, we can't - use standard build tooling. The app names of its modules and dependencies are changed, so `mix install` - won't realize that the correct apps are installed. It uses two different VMs, so making an `escript` - will fail, as the second VM needs to find Expert's modules _somewhere. Releases seem ideal, and we - used them for a while, but they need to match the _exact_ Elixir and Erlang versions they were compiled on, - right down to the patch level. This is much, much too strict for a project that needs to be able to run - on a variety of elixir / erlang versions. - - An ideal packaging system will have the following properties: - - * It creates a self contained artifact directory - * It will run under a variety of versions of Elixir and Erlang - * It allows namespaced apps to run - * It allows multiple VMs to find elixir's modules - * It allows us to package non-elixir resources (mainly launcher scripts) and access them during runtime - - This packaging system meets all of the above parameters. - It works by examining the server app's dependencies, namespacing what's required and then packaging them - in to [Erlang archive files](https://www.erlang.org/doc/man/code.html#loading-of-code-from-archive-files) - in a `lib` directory. Similarly, the configuration is copied to a `config` directory, and the launcher - scripts are copied to a `bin` directory. - - The end result is a release-like filesystem, but without a lot of the erlang booting stuff. Bootstrapping - accomplished by simple scripts that reside in the project's `/bin` directory, and the `Expert.Boot` - module, which loads applications and their modules. - - ## Command line options - - * `--path` - The package will be written to the path given. Defaults to `./build/dev/expert`. If the - `--zip` option is specified, The name of the zip file is determined by the last entry of the path. - For example, if the `--path` option is `_build/dev/output`, then the name of the zip file will be - `output.zip`. - * `--zip` - The resulting package will be zipped. The zip file will be placed in the current directory, - and the package directory will be deleted - - - ## Directory structure - ```text - bin/ - start_expert.sh - debug_shell.sh - lib/ - xp_forge.sh - xp_engine.sh - xp_expert.ez - ... - config/ - config.exs - dev.exs - prod.exs - test.exs - runtime.exs - priv/ - port_wrapper.sh - ... - consolidated/ - Elixir.(consolidated protocol module).beam - ``` - - On boot, the `ERL_LIBS` environment variable is set to the `lib` directory so all of the `.ez` files are - picked up by the code server. Similarly, the `config`, `consolidated` and `priv` directories are added - to the code search path with the `-pa` argument. - """ - - alias Forge.VM.Versions - alias Forge.Namespace - - @options [ - strict: [ - path: :string, - zip: :boolean - ] - ] - - @execute_permisson 0o755 - - def run(args) do - {opts, _, _} = OptionParser.parse(args, @options) - default_path = Path.join([Mix.Project.build_path(), "package", "expert"]) - package_root = Keyword.get(opts, :path, default_path) - - rebuild_on_version_change(package_root) - - Mix.Task.run(:compile) - Mix.Shell.IO.info("Assembling build in #{package_root}") - File.mkdir_p!(package_root) - - {:ok, scratch_directory} = prepare(package_root) - - build_archives(package_root, scratch_directory) - copy_consolidated_beams(package_root) - copy_launchers(package_root) - copy_priv_files(package_root) - copy_config(package_root) - write_vm_versions(package_root) - - File.rm_rf!(scratch_directory) - - if Keyword.get(opts, :zip, false) do - zip(package_root) - File.rm_rf(package_root) - end - end - - defp rebuild_on_version_change(package_root) do - %{elixir: elixir_current, erlang: erlang_current} = Versions.current() - - with {:ok, %{elixir: elixir_compiled, erlang: erlang_compiled}} <- - Versions.read(priv_path(package_root)) do - if elixir_compiled != elixir_current or erlang_compiled != erlang_current do - Code.put_compiler_option(:ignore_module_conflict, true) - Mix.Shell.IO.error("The version of elixir or erlang has changed. Forcing recompilation.") - File.rm_rf!(package_root) - Mix.Task.clear() - Mix.Task.run(:clean, ~w(--deps)) - end - end - end - - defp prepare(package_root) do - scratch_directory = Path.join(package_root, "scratch") - File.mkdir(scratch_directory) - - [Mix.Project.build_path(), "lib"] - |> Path.join() - |> File.cp_r!(Path.join(scratch_directory, "lib")) - - Mix.Task.run(:namespace, [scratch_directory]) - {:ok, scratch_directory} - end - - defp build_archives(package_root, scratch_directory) do - scratch_directory - |> target_path() - |> File.mkdir_p!() - - app_dirs = app_dirs(scratch_directory) - - Enum.each(app_dirs, fn {app_name, path} -> - create_archive(package_root, app_name, path) - end) - end - - defp app_dirs(scratch_directory) do - lib_directory = Path.join(scratch_directory, "lib") - server_deps = server_deps() - - lib_directory - |> File.ls!() - |> Enum.filter(&(&1 in server_deps)) - |> Map.new(fn dir -> - app_name = Path.basename(dir) - {app_name, Path.join([scratch_directory, "lib", dir])} - end) - end - - defp create_archive(package_root, app_name, app_path) do - file_list = file_list(app_name, app_path) - zip_path = Path.join([target_path(package_root), "#{app_name}.ez"]) - - {:ok, _} = - zip_path - |> String.to_charlist() - |> :zip.create(file_list, uncompress: [~c".beam"]) - - :ok - end - - defp file_list(app_name, app_path) do - File.cd!(app_path, fn -> - beams = Path.wildcard("ebin/*.{app,beam}") - priv = Path.wildcard("priv/**/*", match_dot: true) - - Enum.reduce(beams ++ priv, [], fn relative_path, acc -> - case File.read(relative_path) do - {:ok, contents} -> - zip_relative_path = - app_name - |> Path.join(relative_path) - |> String.to_charlist() - - [{zip_relative_path, contents} | acc] - - {:error, _} -> - acc - end - end) - end) - end - - defp copy_consolidated_beams(package_root) do - beams_dest_dir = Path.join(package_root, "consolidated") - - File.mkdir_p!(beams_dest_dir) - - File.cp_r!(Mix.Project.consolidation_path(), beams_dest_dir) - - beams_dest_dir - |> File.ls!() - |> Enum.each(fn relative_path -> - absolute_path = Path.join(beams_dest_dir, relative_path) - Namespace.Transform.Beams.apply(absolute_path) - end) - end - - defp copy_launchers(package_root) do - launcher_source_dir = - Mix.Project.project_file() - |> Path.dirname() - |> Path.join("bin") - - launcher_dest_dir = Path.join(package_root, "bin") - - File.mkdir_p!(launcher_dest_dir) - File.cp_r!(launcher_source_dir, launcher_dest_dir) - - launcher_dest_dir - |> Path.join("*") - |> Path.wildcard() - |> Enum.each(fn path -> - File.chmod!(path, @execute_permisson) - end) - end - - defp target_path(scratch_directory) do - Path.join([scratch_directory, "lib"]) - end - - defp server_deps do - deps_apps = - if Mix.Project.get() == Expert.MixProject do - Mix.Project.deps_apps() - else - server_path = Mix.Project.deps_paths()[:expert] - - Mix.Project.in_project(:expert, server_path, fn _ -> - Mix.Project.deps_apps() - end) - end - - deps = - Enum.map(deps_apps, fn app_module -> - app_module - |> Namespace.Module.apply() - |> to_string() - end) - - server_dep = - :expert - |> Namespace.Module.apply() - |> to_string() - - [server_dep | deps] - end - - defp copy_config(package_root) do - config_source = - Mix.Project.config()[:config_path] - |> Path.absname() - |> Path.dirname() - - config_dest = Path.join(package_root, "config") - File.mkdir_p!(config_dest) - File.cp_r!(config_source, config_dest) - - Namespace.Transform.Configs.apply_to_all(config_dest) - end - - @priv_apps [:engine] - - defp copy_priv_files(package_root) do - priv_dest_dir = priv_path(package_root) - - Enum.each(@priv_apps, fn app_name -> - case priv_dir(app_name) do - {:ok, priv_source_dir} -> - File.cp_r!(priv_source_dir, priv_dest_dir) - - _ -> - :ok - end - end) - end - - defp write_vm_versions(package_root) do - package_root - |> priv_path() - |> Versions.write() - end - - defp zip(package_root) do - package_name = Path.basename(package_root) - - zip_output = Path.join(File.cwd!(), "#{package_name}.zip") - - package_root - |> Path.dirname() - |> File.cd!(fn -> - System.cmd("zip", ["-r", zip_output, package_name]) - end) - end - - defp priv_dir(app) do - case :code.priv_dir(app) do - {:error, _} -> - :error - - path -> - normalized = - path - |> List.to_string() - |> normalize_path() - - {:ok, normalized} - end - end - - defp normalize_path(path) do - case File.read_link(path) do - {:ok, orig} -> - path - |> Path.dirname() - |> Path.join(orig) - |> Path.expand() - |> Path.absname() - - _ -> - path - end - end - - defp priv_path(package_root) do - Path.join(package_root, "priv") - end -end diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index e962553c..52a1a8d7 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -6,18 +6,19 @@ defmodule Expert.MixProject do [ app: :expert, version: "0.7.2", - elixir: "~> 1.15", + elixir: "~> 1.17", start_permanent: Mix.env() == :prod, deps: deps(), dialyzer: Mix.Dialyzer.config(add_apps: [:jason]), aliases: aliases(), - elixirc_paths: elixirc_paths(Mix.env()) + elixirc_paths: elixirc_paths(Mix.env()), + releases: releases() ] end def application do [ - extra_applications: [:logger, :runtime_tools, :kernel, :erts], + extra_applications: [:logger, :runtime_tools, :kernel, :erts, :observer], mod: {Expert.Application, []} ] end @@ -38,11 +39,42 @@ defmodule Expert.MixProject do ["lib"] end + defp releases() do + [ + expert: [ + strip_beams: false, + cookie: "expert", + steps: release_steps(), + burrito: [ + targets: [ + darwin_arm64: [os: :darwin, cpu: :aarch64], + darwin_amd64: [os: :darwin, cpu: :x86_64], + linux_arm64: [os: :linux, cpu: :aarch64], + linux_amd64: [os: :linux, cpu: :x86_64], + windows_amd64: [os: :windows, cpu: :x86_64] + ] + ] + ] + ] + end + + defp release_steps() do + [ + :assemble, + &Expert.Release.assemble/1, + &Burrito.wrap/1 + ] + end + defp deps do [ + {:burrito, "~> 1.3", only: [:dev, :prod]}, Mix.Credo.dependency(), Mix.Dialyzer.dependency(), - {:engine, path: "../engine", env: Mix.env()}, + # In practice Expert does not hardly depend on Engine, only on its compiled + # artifacts, but we need it as a test dependency to set up tests that + # assume a roundtrip to a project node is made. + {:engine, path: "../engine", env: Mix.env(), only: [:test]}, {:forge, path: "../forge", env: Mix.env()}, {:gen_lsp, github: "elixir-tools/gen_lsp", branch: "async"}, {:jason, "~> 1.4"}, diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index 8dbf4c09..32ccaeeb 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -1,20 +1,29 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "burrito": {:hex, :burrito, "1.3.0", "4be8504185250756ff4a8770d0c0d91dbfe518d2faa5f1888f13b00540028c59", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:req, ">= 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.2.0 or ~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "a53f6bc0644bfd998164d68714c9af04291c220f5f7d0c90cb9616780cc60165"}, "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "gen_lsp": {:git, "https://github.com/elixir-tools/gen_lsp.git", "4d553a6ed09045ea28b3e10907a14caf633cf567", [branch: "async"]}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, + "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "observer_cli": {:hex, :observer_cli, "1.8.3", "866ee083eb3482d5f40d301c2ac7e1df0b6061d02ae771e164d71931d3c687c4", [:mix, :rebar3], [{:recon, "~> 2.5.6", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "041c638d54cc8265e6e0472aec7c17a83bb2c4a02628ddedd9138747d9d0b8bf"}, "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, "path_glob": {:hex, :path_glob, "0.2.0", "b9e34b5045cac5ecb76ef1aa55281a52bf603bf7009002085de40958064ca312", [:mix], [{:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "be2594cb4553169a1a189f95193d910115f64f15f0d689454bb4e8cfae2e7ebc"}, + "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, diff --git a/apps/expert/test/expert/boot_test.exs b/apps/expert/test/expert/boot_test.exs deleted file mode 100644 index 61765003..00000000 --- a/apps/expert/test/expert/boot_test.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule Expert.BootTest do - alias Expert.Boot - alias Forge.VM.Versions - - use ExUnit.Case - use Patch - - describe "detect_errors/0" do - test "returns empty list when all checks succeed" do - patch_runtime_versions("1.15.8", "25.0") - patch_compiled_versions("1.15.8", "25.0") - - assert [] = Boot.detect_errors() - end - - test "includes error when runtime elixir is incompatible" do - patch_runtime_versions("1.12.0", "24.3.4") - patch_compiled_versions("1.13.4", "24.3.4") - - assert [error] = Boot.detect_errors() - assert error =~ "FATAL: Expert is not compatible with Elixir 1.12.0" - end - - test "includes error when runtime erlang is incompatible" do - patch_runtime_versions("1.15.8", "23.0") - patch_compiled_versions("1.15.8", "23.0") - - assert [error] = Boot.detect_errors() - assert error =~ "FATAL: Expert is not compatible with Erlang/OTP 23.0.0" - end - - test "includes multiple errors when runtime elixir and erlang are incompatible" do - patch_runtime_versions("1.15.2", "26.0.0") - patch_compiled_versions("1.15.8", "26.1") - - assert [elixir_error, erlang_error] = Boot.detect_errors() - assert elixir_error =~ "FATAL: Expert is not compatible with Elixir 1.15.2" - assert erlang_error =~ "FATAL: Expert is not compatible with Erlang/OTP 26.0.0" - end - end - - defp patch_runtime_versions(elixir, erlang) do - patch(Versions, :elixir_version, elixir) - patch(Versions, :erlang_version, erlang) - end - - defp patch_compiled_versions(elixir, erlang) do - patch(Versions, :code_find_file, fn file -> {:ok, file} end) - - patch(Versions, :read_file, fn file -> - if String.ends_with?(file, ".elixir") do - {:ok, elixir} - else - {:ok, erlang} - end - end) - end -end diff --git a/mix.lock b/mix.lock index 5f1588cd..f941de77 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,7 @@ %{ "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "burrito": {:hex, :burrito, "1.3.0", "4be8504185250756ff4a8770d0c0d91dbfe518d2faa5f1888f13b00540028c59", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:req, ">= 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.2.0 or ~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "a53f6bc0644bfd998164d68714c9af04291c220f5f7d0c90cb9616780cc60165"}, "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, @@ -10,15 +11,19 @@ "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "gen_lsp": {:hex, :gen_lsp, "0.10.0", "f6da076b5ccedf937d17aa9743635a2c3d0f31265c853e58b02ab84d71852270", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "768f8f7b5c5e218fb36dcebd30dcd6275b61ca77052c98c3c4c0375158392c4a"}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, "path_glob": {:hex, :path_glob, "0.2.0", "b9e34b5045cac5ecb76ef1aa55281a52bf603bf7009002085de40958064ca312", [:mix], [{:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "be2594cb4553169a1a189f95193d910115f64f15f0d689454bb4e8cfae2e7ebc"}, "phoenix": {:hex, :phoenix, "1.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"}, @@ -28,10 +33,11 @@ "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "plug": {:hex, :plug, "1.17.0", "a0832e7af4ae0f4819e0c08dd2e7482364937aea6a8a997a679f2cbb7e026b2e", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6692046652a69a00a5a21d0b7e11fcf401064839d59d6b8787f23af55b1e6bc"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "refactorex": {:hex, :refactorex, "0.1.51", "74fc4603b31b600d78539ffea9fe170038aa8d471eec5aed261354c9734b4b27", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "aefa150ab2c0d62aa8c01c4d04b932806118790f09c4106e20883281932fba03"}, + "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, - "sourceror": {:hex, :sourceror, "1.9.0", "3bf5fe2d017aaabe3866d8a6da097dd7c331e0d2d54e59e21c2b066d47f1e08e", [:mix], [], "hexpm", "d20a9dd5efe162f0d75a307146faa2e17b823ea4f134f662358d70f0332fed82"}, + "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, From 0047f4d3f1b7af231f977a4820511406ab4d1610 Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 18:29:48 -0300 Subject: [PATCH 13/23] chore: update github release workflow --- .github/workflows/release.yml | 91 ++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 462c1ecd..db3748f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,44 +2,69 @@ name: Release Expert on: push: - tags: - - v* + branches: + - main + +permissions: + contents: write + pull-requests: write + jobs: release: + name: release runs-on: ubuntu-latest - name: Build and release Expert - permissions: - contents: write + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} steps: - - name: Set up Elixir - uses: erlef/setup-beam@v1 - with: - otp-version: "25.0" - elixir-version: "1.15.8-otp-25" - version-type: strict + - uses: googleapis/release-please-action@v4 + id: release - - name: Checkout code - uses: actions/checkout@v3 + draft: + name: draft + needs: release + env: + GH_TOKEN: ${{ github.token }} + runs-on: ubuntu-latest + if: ${{ needs.release.outputs.release_created }} + steps: + - run: gh release edit ${{ needs.release.outputs.tag_name }} --draft=true --repo='elixir-lang/expert' - - name: Cache deps - id: cache-deps - uses: actions/cache@v3 - env: - cache-name: cache-elixir-deps + build: + needs: [release, draft] + runs-on: macos-14 + if: ${{ needs.release.outputs.release_created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: cachix/cachix-action@v15 with: - path: deps - key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} - restore-keys: | - ${{ runner.os }}-mix-${{ env.cache-name }}- - - - name: Build release - run: make release + name: elixir-tools + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - run: nix develop --command bash -c 'mix local.hex --force' + - run: nix develop --command bash -c 'mix local.rebar --force' + - run: chmod +x priv/cmd + - run: nix develop --command bash -c 'make release' + env: + MIX_ENV: prod + - name: Create Checksum + run: | + cd ./apps/expert/burrito_out + chmod +x ./* + shasum -a 256 ./* > expert_checksums.txt + cd .. + - name: Upload to release + env: + GITHUB_TOKEN: ${{ secrets.token }} + run: gh release upload ${{ needs.release.outputs.tag_name }} ./apps/expert/burrito_out/* - - name: Publish release - uses: ncipollo/release-action@v1 - with: - artifacts: apps/expert/burrito_out/* - makeLatest: true - generateReleaseNotes: false - allowUpdates: true - artifactErrorsFailBuild: true + publish: + name: publish + needs: [release, draft, build] + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - run: gh release edit ${{ needs.release.outputs.tag_name }} --draft=false --repo='elixir-lang/expert' From fdc77e985efb0918bf0152369c64932e00a2b0d3 Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 21:15:44 -0300 Subject: [PATCH 14/23] feat: add plain(non burrito) releases --- Makefile | 9 +++++++-- apps/expert/mix.exs | 10 +++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 2ed9cfb6..3eecc414 100644 --- a/Makefile +++ b/Makefile @@ -76,10 +76,15 @@ build.engine: release: build.engine cd apps/expert &&\ mix deps.get &&\ - EXPERT_RELEASE_MODE=burrito MIX_ENV=prod mix release --force --overwrite + EXPERT_RELEASE_MODE=burrito MIX_ENV=prod mix release expert --overwrite release.local: build.engine cd apps/expert &&\ mix deps.get &&\ - EXPERT_RELEASE_MODE=burrito BURRITO_TARGET=$(local_target) MIX_ENV=prod mix release --force --overwrite + EXPERT_RELEASE_MODE=burrito BURRITO_TARGET=$(local_target) MIX_ENV=prod mix release expert --overwrite + +release.plain: build.engine + cd apps/expert &&\ + mix deps.get &&\ + EXPERT_RELEASE_MODE=plain MIX_ENV=prod mix release plain --overwrite diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index 52a1a8d7..217b23d6 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -44,7 +44,7 @@ defmodule Expert.MixProject do expert: [ strip_beams: false, cookie: "expert", - steps: release_steps(), + steps: release_steps() ++ [&Burrito.wrap/1], burrito: [ targets: [ darwin_arm64: [os: :darwin, cpu: :aarch64], @@ -54,6 +54,11 @@ defmodule Expert.MixProject do windows_amd64: [os: :windows, cpu: :x86_64] ] ] + ], + plain: [ + strip_beams: false, + cookie: "expert", + steps: release_steps() ] ] end @@ -61,8 +66,7 @@ defmodule Expert.MixProject do defp release_steps() do [ :assemble, - &Expert.Release.assemble/1, - &Burrito.wrap/1 + &Expert.Release.assemble/1 ] end From 8580b28ec1972f8d8d704a98282a759ecbeebdd1 Mon Sep 17 00:00:00 2001 From: doorgan Date: Sun, 29 Jun 2025 21:28:35 -0300 Subject: [PATCH 15/23] chore: Remove release-please-action --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db3748f6..f9ad0666 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,6 @@ jobs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: - - uses: googleapis/release-please-action@v4 id: release draft: From c320c319a2bf5b789a63a007dfb22702985d885e Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 01:34:41 -0300 Subject: [PATCH 16/23] chore: Remove build/release tasks --- Makefile | 2 -- apps/engine/lib/mix/tasks/build.ex | 22 ---------------------- apps/expert/lib/expert/release.ex | 21 --------------------- 3 files changed, 45 deletions(-) delete mode 100644 apps/engine/lib/mix/tasks/build.ex delete mode 100644 apps/expert/lib/expert/release.ex diff --git a/Makefile b/Makefile index d276731b..89c980da 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,5 @@ dialyzer.plt.poncho: dialyzer.poncho: compile.poncho compile.protocols.poncho $(foreach dir, $(poncho_dirs), cd apps/$(dir) && mix dialyzer && cd ../..;) -build.engine: - cd apps/engine && mix deps.get && MIX_ENV=dev mix build package: cd apps/expert && mix package diff --git a/apps/engine/lib/mix/tasks/build.ex b/apps/engine/lib/mix/tasks/build.ex deleted file mode 100644 index 34727750..00000000 --- a/apps/engine/lib/mix/tasks/build.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule Mix.Tasks.Build do - use Mix.Task - - def run(_args) do - Mix.Task.run("compile", []) - - namespaced_dir = "_build/#{Mix.env()}_ns" - - # Remove the existing namespaced dir - File.rm_rf(namespaced_dir) - # Create our namespaced area - File.mkdir_p(namespaced_dir) - - # Move our build artifacts from safekeeping to the build area - File.cp_r!("_build/#{Mix.env()}", namespaced_dir) - - # Namespace the new code - Mix.Task.run(:namespace, [ - namespaced_dir - ]) - end -end diff --git a/apps/expert/lib/expert/release.ex b/apps/expert/lib/expert/release.ex deleted file mode 100644 index 676f20a3..00000000 --- a/apps/expert/lib/expert/release.ex +++ /dev/null @@ -1,21 +0,0 @@ -defmodule Expert.Release do - def assemble(release) do - Mix.Task.run(:namespace, [release.path]) - - engine_path = Path.expand("../../../engine", __DIR__) - - source = Path.join([engine_path, "_build/dev_ns"]) - - dest = - Path.join([ - release.path, - "lib", - "xp_expert-#{release.version}", - "priv" - ]) - - File.cp_r!(source, dest) - - release - end -end From 2e165558836ef908f80006d29d6f4a004eabdacb Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 01:38:57 -0300 Subject: [PATCH 17/23] chore: fix unordered aliases --- apps/expert/lib/expert/project/supervisor.ex | 2 +- apps/expert/lib/mix/tasks/package.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/expert/lib/expert/project/supervisor.ex b/apps/expert/lib/expert/project/supervisor.ex index c80752a1..d23d6d65 100644 --- a/apps/expert/lib/expert/project/supervisor.ex +++ b/apps/expert/lib/expert/project/supervisor.ex @@ -1,10 +1,10 @@ defmodule Expert.Project.Supervisor do + alias Expert.EngineSupervisor alias Expert.Project.Diagnostics alias Expert.Project.Intelligence alias Expert.Project.Node alias Expert.Project.Progress alias Expert.Project.SearchListener - alias Expert.EngineSupervisor alias Forge.Project # TODO: this module is slightly weird diff --git a/apps/expert/lib/mix/tasks/package.ex b/apps/expert/lib/mix/tasks/package.ex index b8ad8bb3..3fb7945e 100644 --- a/apps/expert/lib/mix/tasks/package.ex +++ b/apps/expert/lib/mix/tasks/package.ex @@ -66,8 +66,8 @@ defmodule Mix.Tasks.Package do to the code search path with the `-pa` argument. """ - alias Forge.VM.Versions alias Forge.Namespace + alias Forge.VM.Versions @options [ strict: [ From 0d20aaa1328e3dfa34bc09e6816877d7018e0f57 Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 01:52:39 -0300 Subject: [PATCH 18/23] chore: update lockfile --- apps/expert/mix.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index 68f50854..a1b90536 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -8,7 +8,9 @@ "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, "gen_lsp": {:hex, :gen_lsp, "0.11.0", "9eda4d2fcaff94d9b3062e322fcf524c176db1502f584a3cff6135088b46084b", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "d67c20650a5290a02f7bac53083ac4487d3c6b461f35a8b14c5d2d7638c20d26"}, + "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, From b5a63e299e82b6c99dc354c2dff879a48c94b256 Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 01:59:20 -0300 Subject: [PATCH 19/23] fix: rollback change to elixir version --- apps/expert/mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index 34f1cc60..f0d504c9 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -6,7 +6,7 @@ defmodule Expert.MixProject do [ app: :expert, version: "0.7.2", - elixir: "~> 1.17", + elixir: "~> 1.15", start_permanent: Mix.env() == :prod, deps: deps(), dialyzer: Mix.Dialyzer.config(add_apps: [:jason]), From 38574cedd31a284e142cc55ab0d20bed4bb04627 Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 02:06:48 -0300 Subject: [PATCH 20/23] chore: remove ci integration tests Using burrito we're no longer trying to activate the user's elixir executable on boot, making those tests stale --- .github/workflows/elixir.yml | 26 ------ integration/Dockerfile | 43 --------- integration/README.md | 38 -------- integration/boot/set_up_asdf.sh | 19 ---- integration/boot/set_up_mise.sh | 38 -------- integration/build.sh | 4 - integration/test.sh | 159 -------------------------------- integration/test_utils.sh | 93 ------------------- 8 files changed, 420 deletions(-) delete mode 100644 integration/Dockerfile delete mode 100644 integration/README.md delete mode 100755 integration/boot/set_up_asdf.sh delete mode 100755 integration/boot/set_up_mise.sh delete mode 100755 integration/build.sh delete mode 100755 integration/test.sh delete mode 100755 integration/test_utils.sh diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 219c6d1a..e5e0fcad 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -195,29 +195,3 @@ jobs: # Step: Execute the tests. - name: Run tests run: make test.all - - integration_test: - runs-on: ubuntu-latest - name: Integration tests - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build integration runner - uses: docker/build-push-action@v6 - with: - context: . - file: ./integration/Dockerfile - tags: xp - # GitHub Actions cache - # https://docs.docker.com/build/ci/github-actions/cache/ - cache-from: type=gha - cache-to: type=gha,mode=max - # Required to make the image available through docker - load: true - - - name: Run integration tests - run: NO_BUILD=1 ./integration/test.sh diff --git a/integration/Dockerfile b/integration/Dockerfile deleted file mode 100644 index e9e91258..00000000 --- a/integration/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# Used to build a clean container for testing the Expert boot sequence. -# -# Build: docker build -t xp -f integration/Dockerfile . -# Run: docker run -it xp - -ARG SYS_ELIXIR_VERSION=1.15.7 -ARG SYS_ERLANG_VERSION=26.2.1 - -FROM hexpm/elixir:${SYS_ELIXIR_VERSION}-erlang-${SYS_ERLANG_VERSION}-ubuntu-jammy-20231128 - -ENV ELIXIR_VERSION=1.15.7-otp-26 -ENV ERLANG_VERSION=26.2.1 - -RUN apt-get update -RUN apt-get install -y \ - git \ - curl \ - unzip \ - # used to compile erlang - build-essential \ - autoconf \ - openssl \ - libssl-dev - -WORKDIR /expert - -COPY integration/boot/set_up_mise.sh integration/boot/set_up_mise.sh -RUN integration/boot/set_up_mise.sh - -COPY integration/boot/set_up_asdf.sh integration/boot/set_up_asdf.sh -RUN integration/boot/set_up_asdf.sh - -COPY mix_*.exs . -COPY apps apps - -WORKDIR /expert/apps/expert - -RUN mix local.hex --force -RUN mix deps.get -RUN mix compile -RUN mix package - -CMD bash diff --git a/integration/README.md b/integration/README.md deleted file mode 100644 index 812a1d1b..00000000 --- a/integration/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Integration tests - -These integration tests confirm that Lexical can start using the correct version manager (asdf/rtx) and Elixir/Erlang versions in a variety of environments. -They work using a Docker image with both asdf and rtx installed, but with neither activated. -An individual test will then run some setup and manipulate the environment, then ensure that Lexical can properly start. - -## Running the tests - -Tests are run using `test.sh`: - -```sh -$ ./integration/test.sh -``` - -By default, this will first build the Docker image and then run the tests. -The first run will take quite some time to build the image, but subsequent runs will be much faster, as most setup will be cached and only Lexical will need to be rebuilt. - -If needed, you can separate building and running using `build.sh` and the `NO_BUILD=1` flag: - -```sh -$ ./integration/build.sh -$ NO_BUILD=1 ./integration/test.sh -``` - -### Debugging - -Run the tests with `XP_DEBUG=1` to see the output from the underlying commands: - -```sh -$ XP_DEBUG=1 ./integration/test.sh -... -test_find_asdf_directory... -> No version manager detected -> Found asdf. Activating... -> Detected Elixir through asdf: /root/.asdf/installs/elixir/1.15.8-otp-26/bin/elixir -Pass -... -``` diff --git a/integration/boot/set_up_asdf.sh b/integration/boot/set_up_asdf.sh deleted file mode 100755 index ae75c810..00000000 --- a/integration/boot/set_up_asdf.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -asdf_dir=/version_managers/asdf_vm -mkdir -p $asdf_dir && cd $asdf_dir - -git clone https://github.com/asdf-vm/asdf.git . - -# shellcheck disable=SC1091 -ASDF_DIR=$asdf_dir . asdf.sh - -export KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-termcap --without-wx" -asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git -asdf install erlang "$ERLANG_VERSION" -asdf global erlang "$ERLANG_VERSION" - -asdf plugin add elixir https://github.com/asdf-vm/asdf-elixir.git -asdf install elixir "$ELIXIR_VERSION" -asdf global elixir "$ELIXIR_VERSION" diff --git a/integration/boot/set_up_mise.sh b/integration/boot/set_up_mise.sh deleted file mode 100755 index 52a9c8bc..00000000 --- a/integration/boot/set_up_mise.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -mise_dir=/version_managers/mise_vm -mkdir -p $mise_dir && cd $mise_dir - -# Download the mise binary for the correct architecture -arch=$(uname -m) -architecture="" - -case $arch in - "x86_64") - architecture="linux-x64" - ;; - "aarch64") - architecture="linux-arm64" - ;; - "arm64") - architecture="macos-arm64" - ;; - *) - echo "Unsupported architecture: $arch" - exit 1 - ;; -esac - -curl -L "https://github.com/jdx/mise/releases/download/v2025.2.6/mise-v2025.2.6-${architecture}.tar.gz" -o mise.tar.gz -tar xfvz mise.tar.gz -mv mise mise_download -mv mise_download/bin/mise . -chmod +x ./mise - -eval "$(./mise activate bash)" -export MISE_VERBOSE=1 -export KERL_CONFIGURE_OPTIONS="--disable-debug --without-javac --without-termcap --without-wx" - -./mise use --global "erlang@$ERLANG_VERSION" -./mise use --global "elixir@$ELIXIR_VERSION" diff --git a/integration/build.sh b/integration/build.sh deleted file mode 100755 index 6954dfed..00000000 --- a/integration/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail - -docker build -t xp -f integration/Dockerfile . diff --git a/integration/test.sh b/integration/test.sh deleted file mode 100755 index 9e11b4ba..00000000 --- a/integration/test.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash - -# Disable warning for interpolations in single quotes: -# shellcheck disable=2016 - -set -eo pipefail - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" - -# shellcheck disable=SC1091 -. "$script_dir/test_utils.sh" - -# Ensure the Docker image is up-to-date unless NO_BUILD=1 -if [ -z "$NO_BUILD" ]; then - "$script_dir"/build.sh -fi - -start_expert() { - local command='XP_HALT_AFTER_BOOT=1 _build/dev/package/expert/bin/start_expert.sh; exit $?' - - if [[ $1 != "" ]]; then - command="$1 && $command" - fi - - docker run -i xp bash -c "$command" 2>&1 - return $? -} - -run_test() { - local setup=$1 - local expected=("${@:2}") - - # $FUNCNAME is a special array containing the stack of function calls, - # with the current function at the head. - local test="${FUNCNAME[1]}" - log "$test... " - - local output - output=$(start_expert "$setup") - local exit_code=$? - - if [[ -n $XP_DEBUG ]]; then - log_info "\n$(prefix_lines "> " "$output")" - fi - - assert_contains "$output" "${expected[@]}" - local assert_exit_code=$? - - if [ $exit_code -ne 0 ]; then - log_error "Fail (exit_code=$exit_code)" - return 1 - elif [ $assert_exit_code -ne 0 ]; then - return 1 - else - log_success "Pass" - fi -} - -test_system_installation() { - local expect=( - "No activated version manager detected" - "Could not activate a version manager" - ) - - run_test "" "${expect[@]}" - return $? -} - -test_asdf_already_activated() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'ASDF_DIR=/version_managers/asdf_vm . /version_managers/asdf_vm/asdf.sh' - ) - local expect=( - "Detected Elixir through asdf" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -test_asdf_dir_found() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'export ASDF_DIR=/version_managers/asdf_vm' - ) - local expect=( - "No activated version manager detected" - "Found asdf. Activating" - "Detected Elixir through asdf" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -test_asdf_used_when_activated_mise_missing_elixir() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'eval "$(/version_managers/mise_vm/mise activate bash)" && ' - 'mise uninstall "elixir@$ELIXIR_VERSION" && ' - 'export ASDF_DIR=/version_managers/asdf_vm' - ) - local expect=( - "No activated version manager detected" - "Found asdf. Activating" - "Detected Elixir through asdf" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -test_mise_already_activated() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'eval "$(/version_managers/mise_vm/mise activate bash)"' - ) - local expect=( - "Detected Elixir through mise" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -test_mise_binary_found_and_activated() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'export PATH="/version_managers/mise_vm:$PATH"' - ) - local expect=( - "No activated version manager detected" - "Found mise" - "Detected Elixir through mise" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -test_mise_used_when_activated_asdf_missing_elixir() { - local setup=( - 'mv "$(which elixir)" "$(which elixir).hidden" && ' - 'ASDF_DIR=/version_managers/asdf_vm . /version_managers/asdf_vm/asdf.sh && ' - 'asdf uninstall elixir "$ELIXIR_VERSION" && ' - 'export PATH="/version_managers/mise_vm:$PATH"' - ) - local expect=( - "No activated version manager detected" - "Found mise. Activating" - "Detected Elixir through mise" - ) - - run_test "${setup[*]}" "${expect[@]}" - return $? -} - -run_tests_and_exit diff --git a/integration/test_utils.sh b/integration/test_utils.sh deleted file mode 100755 index 6f38c4dc..00000000 --- a/integration/test_utils.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env bash - -reset="\033[0m" -faint="$reset\033[0;2m" -red="$reset\033[0;31m" -green="$reset\033[0;32m" -cyan="$reset\033[0;36m" - -# Asserts that the given string contains all given substrings. -# -# assert_contains STRING SUBSTRING* -# -# Example: -# -# assert_contains "foobar" "foo" "bar" -# -assert_contains() { - local output=$1 - local expectations=("${@:2}") - local not_found=() - - for expected in "${expectations[@]}"; do - if [[ $output != *"$expected"* ]]; then - not_found+=("$expected") - fi - done - - if [ ${#not_found[@]} -ne 0 ]; then - log_error "Assertion failed!" - log_section "Expected" "${not_found[@]}" - log_section "To be in" "$output" - log "\n\n" - return 1 - fi -} - -# Runs every function that starts with "test_" in the current script, -# exits 1 if any tests exit non-zero -# -# Example: -# -# test_foo() { ... } -# test_bar() { ... } -# -# # automatically runs test_foo and test_bar -# run_tests_and_exit -# -run_tests_and_exit() { - local tests - tests=$(declare -F | awk '/test_/ {print $3}') - - local exit_code=0 - - for test in $tests; do - if ! "$test"; then - exit_code=1 - fi - done - - exit $exit_code -} - -log() { - echo -ne "$1" -} - -log_error() { - log "${red}$1${reset}\n" -} - -log_success() { - log "${green}$1${reset}\n" -} - -log_info() { - log "${faint}$1${reset}\n" -} - -log_section() { - local title="$1" - local content=("${@:2}") - - log "\n ${cyan}${title}:${reset}\n\n" - - for item in "${content[@]}"; do - prefix_lines " " "$item" - done -} - -prefix_lines() { - local prefix_with=$1 - echo "$2" | sed "s/^/$prefix_with/" | cat -} From 7a114bc89ae2ad091ea07c981bb3231b818387db Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 14:31:57 -0300 Subject: [PATCH 21/23] Revert changes to the release workflow It will be done in a follow-up PR --- .github/workflows/release.yml | 99 +++++++++++++++-------------------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db3748f6..e1b62a1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,69 +2,54 @@ name: Release Expert on: push: - branches: - - main - -permissions: - contents: write - pull-requests: write - + tags: + - v* jobs: release: - name: release runs-on: ubuntu-latest - outputs: - release_created: ${{ steps.release.outputs.release_created }} - tag_name: ${{ steps.release.outputs.tag_name }} + name: Build and release Expert + permissions: + contents: write steps: - - uses: googleapis/release-please-action@v4 - id: release + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: "25.0" + elixir-version: "1.15.8-otp-25" + version-type: strict - draft: - name: draft - needs: release - env: - GH_TOKEN: ${{ github.token }} - runs-on: ubuntu-latest - if: ${{ needs.release.outputs.release_created }} - steps: - - run: gh release edit ${{ needs.release.outputs.tag_name }} --draft=true --repo='elixir-lang/expert' + - name: Checkout code + uses: actions/checkout@v4 - build: - needs: [release, draft] - runs-on: macos-14 - if: ${{ needs.release.outputs.release_created }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - uses: DeterminateSystems/nix-installer-action@main - - uses: cachix/cachix-action@v15 - with: - name: elixir-tools - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - run: nix develop --command bash -c 'mix local.hex --force' - - run: nix develop --command bash -c 'mix local.rebar --force' - - run: chmod +x priv/cmd - - run: nix develop --command bash -c 'make release' + - name: Cache deps + id: cache-deps + uses: actions/cache@v4 env: - MIX_ENV: prod - - name: Create Checksum + cache-name: cache-elixir-deps + with: + path: deps + key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix-${{ env.cache-name }}- + + - name: Install dependencies + run: mix deps.get + + - name: Compile + run: mix compile + + - name: Build release + run: mix package --zip + + - name: Archive release run: | - cd ./apps/expert/burrito_out - chmod +x ./* - shasum -a 256 ./* > expert_checksums.txt - cd .. - - name: Upload to release - env: - GITHUB_TOKEN: ${{ secrets.token }} - run: gh release upload ${{ needs.release.outputs.tag_name }} ./apps/expert/burrito_out/* + cp expert.zip expert-${{ github.ref_name }}.zip - publish: - name: publish - needs: [release, draft, build] - runs-on: ubuntu-latest - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - run: gh release edit ${{ needs.release.outputs.tag_name }} --draft=false --repo='elixir-lang/expert' + - name: Publish release + uses: ncipollo/release-action@v1 + with: + artifacts: expert*.zip + makeLatest: true + generateReleaseNotes: false + allowUpdates: true + artifactErrorsFailBuild: true From 2ff126482ffffefec410b659921acc030eea3f17 Mon Sep 17 00:00:00 2001 From: doorgan Date: Thu, 3 Jul 2025 15:11:50 -0300 Subject: [PATCH 22/23] chore: add ci test for release builds --- .github/workflows/elixir.yml | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index e5e0fcad..de1ad022 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -136,6 +136,55 @@ jobs: make compile.protocols.poncho make dialyzer.all + release-test: + runs-on: ${{matrix.os.name}} + name: Release test (${{matrix.os.name}}) + strategy: + matrix: + os: + - name: ubuntu-latest + target: linux_amd64 + - name: macos-14 + target: darwin_arm64 + - name: macos-13 + target: darwin_amd64 + + include: + - elixir: "1.15.8" + otp: "27" + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp}} + elixir-version: ${{matrix.elixir}} + + - uses: mlugg/setup-zig@v2 + with: + version: "0.14.0" + + - name: Cache deps + id: cache-deps + uses: actions/cache@v4 + with: + path: | + apps/**/deps + apps/**/_build + + key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('apps/**/mix.lock') }} + restore-keys: | + ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}- + + - name: Build and release + env: + MIX_ENV: prod + BURRITO_TARGET: ${{ matrix.os.target }} + run: make build.engine && cd apps/expert && mix deps.get && mix release expert + test: runs-on: ubuntu-latest name: Test on OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} From 43f3793d8e09055c77bc20229e51158b8141184b Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Tue, 8 Jul 2025 02:19:57 -0300 Subject: [PATCH 23/23] wip --- apps/expert/mix.exs | 3 +- apps/expert/mix.lock | 4 +- flake.lock | 36 ++++++++++++++- flake.nix | 31 ++++++++----- justfile | 102 +++++++++++++++++++++++++++++++++++++++++++ mix.exs | 58 ------------------------ mix.lock | 45 ------------------- 7 files changed, 160 insertions(+), 119 deletions(-) create mode 100644 justfile delete mode 100644 mix.exs delete mode 100644 mix.lock diff --git a/apps/expert/mix.exs b/apps/expert/mix.exs index f0d504c9..63a28cfe 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -12,7 +12,8 @@ defmodule Expert.MixProject do dialyzer: Mix.Dialyzer.config(add_apps: [:jason]), aliases: aliases(), elixirc_paths: elixirc_paths(Mix.env()), - releases: releases() + releases: releases(), + default_release: :expert ] end diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index a1b90536..9829ccf2 100644 --- a/apps/expert/mix.lock +++ b/apps/expert/mix.lock @@ -8,7 +8,7 @@ "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, + "finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"}, "gen_lsp": {:hex, :gen_lsp, "0.11.0", "9eda4d2fcaff94d9b3062e322fcf524c176db1502f584a3cff6135088b46084b", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "d67c20650a5290a02f7bac53083ac4487d3c6b461f35a8b14c5d2d7638c20d26"}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, @@ -23,7 +23,7 @@ "path_glob": {:hex, :path_glob, "0.2.0", "b9e34b5045cac5ecb76ef1aa55281a52bf603bf7009002085de40958064ca312", [:mix], [{:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "be2594cb4553169a1a189f95193d910115f64f15f0d689454bb4e8cfae2e7ebc"}, "recon": {:hex, :recon, "2.5.6", "9052588e83bfedfd9b72e1034532aee2a5369d9d9343b61aeb7fbce761010741", [:mix, :rebar3], [], "hexpm", "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0"}, "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, - "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, + "req": {:hex, :req, "0.5.14", "521b449fa0bf275e6d034c05f29bec21789a0d6cd6f7a1c326c7bee642bf6e07", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b7b15692071d556c73432c7797aa7e96b51d1a2db76f746b976edef95c930021"}, "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, diff --git a/flake.lock b/flake.lock index 81d1605c..66aafe4f 100644 --- a/flake.lock +++ b/flake.lock @@ -48,7 +48,9 @@ "inputs": { "flake-parts": "flake-parts", "nixpkgs": "nixpkgs", - "systems": "systems" + "systems": "systems", + "xzpkgs": "xzpkgs", + "zigpkgs": "zigpkgs" } }, "systems": { @@ -65,6 +67,38 @@ "repo": "default", "type": "github" } + }, + "xzpkgs": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + } + }, + "zigpkgs": { + "locked": { + "lastModified": 1747426788, + "narHash": "sha256-N4cp0asTsJCnRMFZ/k19V9akkxb7J/opG+K+jU57JGc=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "12a55407652e04dcf2309436eb06fef0d3713ef3", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "12a55407652e04dcf2309436eb06fef0d3713ef3", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index ef1e4d81..4d36b230 100644 --- a/flake.nix +++ b/flake.nix @@ -2,12 +2,16 @@ description = "Reimagined language server for Elixir"; inputs.nixpkgs.url = "flake:nixpkgs"; + inputs.zigpkgs.url = "github:nixos/nixpkgs/12a55407652e04dcf2309436eb06fef0d3713ef3"; + inputs.xzpkgs.url = "github:nixos/nixpkgs/18dd725c29603f582cf1900e0d25f9f1063dbf11"; inputs.flake-parts.url = "github:hercules-ci/flake-parts"; inputs.systems.url = "github:nix-systems/default"; outputs = { self, systems, + zigpkgs, + xzpkgs, ... } @ inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} { @@ -19,8 +23,14 @@ systems = import systems; - perSystem = {pkgs, ...}: let + perSystem = { + pkgs, + system, + ... + }: let erlang = pkgs.beam.packages.erlang_25; + zpkgs = zigpkgs.legacyPackages.${system}; + xzpkgs' = xzpkgs.legacyPackages.${system}; expert = self.lib.mkExpert {inherit erlang;}; in { formatter = pkgs.alejandra; @@ -51,17 +61,14 @@ }; devShells.default = pkgs.mkShell { - packages = let - beamPackages = pkgs.beam.packages; - in - [ - beamPackages.erlang_27.erlang - beamPackages.erlang_27.elixir_1_17 - ] - ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ - pkgs.darwin.apple_sdk.frameworks.CoreFoundation - pkgs.darwin.apple_sdk.frameworks.CoreServices - ]; + packages = with pkgs; [ + beam.packages.erlang_27.erlang + beam.packages.erlang_27.elixir_1_17 + zpkgs.zig_0_14 + xzpkgs'.xz + just + _7zz + ]; }; }; }; diff --git a/justfile b/justfile new file mode 100644 index 00000000..2b8c48b7 --- /dev/null +++ b/justfile @@ -0,0 +1,102 @@ +os := if os() == "macos" { "darwin" } else { os() } +arch := if arch() =~ "(arm|aarch64)" { "arm64" } else { if arch() =~ "(x86|x86_64)" { "amd64" } else { "unsupported" } } +local_target := if os =~ "(darwin|linux|windows)" { os + "_" + arch } else { "unsupported" } +apps := "expert engine forge expert_credo" + +[doc('Run mix deps.get for the given project')] +deps project: + #!/usr/bin/env bash + cd apps/{{ project }} + mix deps.get + +[doc('Run an arbitrary command inside the given project directory')] +run project +ARGS: + #!/usr/bin/env bash + set -euo pipefail + cd apps/{{ project }} + eval "{{ ARGS }}" + +[doc('Compile the given project.')] +compile project: (deps project) + cd apps/{{ project }} && mix compile + +[doc('Run tests in the given project')] +test project="all" *args="": + #!/usr/bin/env bash + set -euo pipefail + + case {{ project }} in + all) + for proj in {{ apps }}; do + (cd "apps/$proj" && mix test {{args}}) + done + ;; + *) + (cd "apps/{{ project }}" && mix test {{args}}) + ;; + esac + +[doc('Run a mix command in one or all projects. Use `just test` to run tests.')] +mix cmd *project: + #!/usr/bin/env bash + + if [ -n "{{ project }}" ]; then + cd apps/{{ project }} + mix {{ cmd }} + else + for project in {{ apps }}; do + ( + cd apps/"$project" + + mix {{ cmd }} + ) + done + fi + +[doc('Lint all projects or just a single project')] +lint *project: + #!/usr/bin/env bash + set -euxo pipefail + + just mix "format --check-formatted" {{ project }} + just mix credo {{ project }} + just mix dialyzer {{ project }} + +build-engine: + #!/usr/bin/env bash + + cd apps/engine + mix build + + +[doc('Build a release for the local system')] +[unix] +release-local: (deps "expert") (compile "engine") build-engine + #!/usr/bin/env bash + cd apps/expert + + if [ "{{ local_target }}" == "unsupported" ]; then + echo "unsupported OS/Arch combination: {{ local_target }}" + exit 1 + fi + MIX_ENV=prod EXPERT_RELEASE_MODE=burrito BURRITO_TARGET="{{ local_target }}" mix release --overwrite + +[windows] +release-local: (deps "expert") (compile "engine") build-engine + # idk actually how to set env vars like this on windows, might crash + EXPERT_RELEASE_MODE=burrito BURRITO_TARGET="windows_amd64" MIX_ENV=prod mix release --no-compile + +[doc('Build releases for all target platforms')] +release-all: (compile "engine") + #!/usr/bin/env bash + cd apps/expert + EXPERT_RELEASE_MODE=burrito MIX_ENV=prod mix release --no-compile + +[doc('Build a plain release without burrito')] +release-plain: (compile "engine") + #!/usr/bin/env bash + cd apps/expert + MIX_ENV=prod mix release plain --overwrite + +default: release-local + diff --git a/mix.exs b/mix.exs deleted file mode 100644 index 22976d94..00000000 --- a/mix.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule Expert.LanguageServer.MixProject do - use Mix.Project - - def project do - [ - apps_path: "apps", - version: "0.7.2", - start_permanent: Mix.env() == :prod, - deps: deps(), - aliases: aliases(), - docs: docs(), - name: "Expert", - consolidate_protocols: Mix.env() != :test - ] - end - - defp deps do - [ - {:ex_doc, "~> 0.34", only: :dev, runtime: false} - ] - end - - defp docs do - [ - main: "readme", - deps: [jason: "https://hexdocs.pm/jason/Jason.html"], - extras: ~w( - README.md - pages/installation.md - pages/architecture.md - pages/glossary.md - ), - groups_for_modules: [ - Core: ~r/(Expert|Engine)/, - Engine: ~r/Engine/, - Server: ~r/Expert/ - ] - ] - end - - defp aliases do - [ - compile: "compile --docs --debug-info", - docs: "docs --html", - test: "test --no-start", - "nix.hash": &nix_hash/1 - ] - end - - defp nix_hash(_args) do - docker = System.get_env("DOCKER_CMD", "docker") - - Mix.shell().cmd( - "#{docker} run --rm -v '#{File.cwd!()}:/data' nixos/nix nix --extra-experimental-features 'nix-command flakes' run ./data#update-hash", - stderr_to_stdout: false - ) - end -end diff --git a/mix.lock b/mix.lock deleted file mode 100644 index 66f9badb..00000000 --- a/mix.lock +++ /dev/null @@ -1,45 +0,0 @@ -%{ - "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, - "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "burrito": {:hex, :burrito, "1.3.0", "4be8504185250756ff4a8770d0c0d91dbfe518d2faa5f1888f13b00540028c59", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:req, ">= 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.2.0 or ~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "a53f6bc0644bfd998164d68714c9af04291c220f5f7d0c90cb9616780cc60165"}, - "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, - "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, - "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, - "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "73ce7e0d239342fb9527d7ba567203e77dbb9b25", [ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"]}, - "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, - "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "gen_lsp": {:hex, :gen_lsp, "0.11.0", "9eda4d2fcaff94d9b3062e322fcf524c176db1502f584a3cff6135088b46084b", [:mix], [{:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:schematic, "~> 0.2.1", [hex: :schematic, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "d67c20650a5290a02f7bac53083ac4487d3c6b461f35a8b14c5d2d7638c20d26"}, - "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "logger_file_backend": {:hex, :logger_file_backend, "0.0.14", "774bb661f1c3fed51b624d2859180c01e386eb1273dc22de4f4a155ef749a602", [:mix], [], "hexpm", "071354a18196468f3904ef09413af20971d55164267427f6257b52cfba03f9e6"}, - "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, - "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, - "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, - "patch": {:hex, :patch, "0.15.0", "947dd6a8b24a2d2d1137721f20bb96a8feb4f83248e7b4ad88b4871d52807af5", [:mix], [], "hexpm", "e8dadf9b57b30e92f6b2b1ce2f7f57700d14c66d4ed56ee27777eb73fb77e58d"}, - "path_glob": {:hex, :path_glob, "0.2.0", "b9e34b5045cac5ecb76ef1aa55281a52bf603bf7009002085de40958064ca312", [:mix], [{:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "be2594cb4553169a1a189f95193d910115f64f15f0d689454bb4e8cfae2e7ebc"}, - "phoenix": {:hex, :phoenix, "1.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"}, - "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.9", "4dc5e535832733df68df22f9de168b11c0c74bca65b27b088a10ac36dfb75d04", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1dccb04ec8544340e01608e108f32724458d0ac4b07e551406b3b920c40ba2e5"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, - "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, - "plug": {:hex, :plug, "1.17.0", "a0832e7af4ae0f4819e0c08dd2e7482364937aea6a8a997a679f2cbb7e026b2e", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6692046652a69a00a5a21d0b7e11fcf401064839d59d6b8787f23af55b1e6bc"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "refactorex": {:hex, :refactorex, "0.1.52", "22a69062c84e0f20a752d3d6580269c09c242645ee4f722f03d4270dd8cbf218", [:mix], [{:sourceror, "~> 1.7", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "4927fe6c3acd1f4695d6d3e443380167d61d004d507b1279c6084433900c94d0"}, - "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, - "schematic": {:hex, :schematic, "0.2.1", "0b091df94146fd15a0a343d1bd179a6c5a58562527746dadd09477311698dbb1", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0b255d65921e38006138201cd4263fd8bb807d9dfc511074615cd264a571b3b1"}, - "snowflake": {:hex, :snowflake, "1.0.4", "8433b4e04fbed19272c55e1b7de0f7a1ee1230b3ae31a813b616fd6ef279e87a", [:mix], [], "hexpm", "badb07ebb089a5cff737738297513db3962760b10fe2b158ae3bebf0b4d5be13"}, - "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, - "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, - "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, - "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, - "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, - "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, -}