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/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 46498b49..3ce561f5 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -119,12 +119,8 @@ jobs: expert-plts-2-${{ env.DEFAULT_OTP }}-${{ env.DEFAULT_ELIXIR }}-${{ hashFiles('apps/**/mix.lock') }}- expert-plts-2-${{ env.DEFAULT_OTP }}-${{ env.DEFAULT_ELIXIR }}- - # Step: Download project dependencies. If unchanged, uses - # the cached version. - name: Install and compile dependencies - run: | - mix deps.get - mix deps.compile --skip-umbrella-children + run: make deps.compile.poncho - name: Compile run: make compile.all @@ -137,7 +133,7 @@ jobs: - name: Run dialyzer run: | - mix compile.protocols + make compile.protocols.poncho make dialyzer.all test: diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml deleted file mode 100644 index 88351c2f..00000000 --- a/.github/workflows/nix.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Nix derivation checks - -on: - pull_request: - paths: ["mix.lock"] - workflow_dispatch: - -jobs: - auto-update-nix-hash: - permissions: - contents: write - runs-on: ubuntu-latest - name: Auto update Nix hash - steps: - - uses: actions/checkout@v4 - - uses: DeterminateSystems/nix-installer-action@main - - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix run .#update-hash | tee nix/hash - - uses: stefanzweifel/git-auto-commit-action@v5 - with: - commit_message: 'Update Nix hash of Mix deps' - - run: nix build 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/mix.exs b/apps/engine/mix.exs index d63057e2..0b24e811 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, "~> 0.11"}, {: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..947b1e67 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": {: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"}, - "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": {: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"}, "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/remote_control_test.exs b/apps/engine/test/remote_control_test.exs index fcb08046..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() - |> Project.new() + |> then(&Project.new(&1)) start_project(subapp_project) diff --git a/apps/expert/.iex.exs b/apps/expert/.iex.exs index 6e4499ed..670986db 100644 --- a/apps/expert/.iex.exs +++ b/apps/expert/.iex.exs @@ -1,6 +1,5 @@ alias Forge.Project - other_project = [ File.cwd!(), diff --git a/apps/expert/lib/expert.ex b/apps/expert/lib/expert.ex index 83989e36..ab033f4a 100644 --- a/apps/expert/lib/expert.ex +++ b/apps/expert/lib/expert.ex @@ -1,155 +1,178 @@ 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 + def get_lsp, do: :persistent_term.get(:expert_lsp, nil) - @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 + :persistent_term.put(:expert_lsp, lsp) + {: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) 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, lsp) do + state = assigns(lsp).state + + if state.configuration == nil 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, assign(lsp, state: %State{state | configuration: config})} + else + {:noreply, lsp} + end 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 +208,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..40003071 100644 --- a/apps/expert/lib/expert/configuration.ex +++ b/apps/expert/lib/expert/configuration.ex @@ -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/project/diagnostics.ex b/apps/expert/lib/expert/project/diagnostics.ex index 251abfea..0a123d63 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(Expert.get_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..75cf4226 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(Expert.get_lsp(), progress.token) + write(Expert.get_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(Expert.get_lsp(), progress.token) + write(Expert.get_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(Expert.get_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(Expert.get_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(Expert.get_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(Expert.get_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..f3c67043 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(Expert.get_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(Expert.get_lsp(), %Requests.WorkspaceCodeLensRefresh{id: Id.next()}) - Window.show_info_message(message) + GenLSP.notify(Expert.get_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..875a8dcf 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(Expert.get_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..d09feca0 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 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..cfff6a19 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,10 +34,14 @@ 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 + } + ) do client_name = case event.client_info do %{name: name} -> name @@ -51,56 +52,16 @@ defmodule Expert.State do 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 +87,23 @@ defmodule Expert.State do {:ok, config} -> {:ok, %__MODULE__{state | configuration: config}} - {:ok, config, response} -> - Transport.write(response) + {:ok, config, request} -> + GenLSP.request(Expert.get_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 +123,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 +133,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 +158,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 +177,18 @@ defmodule Expert.State do {:ok, %__MODULE__{state | initialized?: true}} end - def apply(%__MODULE__{} = state, %Requests.Shutdown{} = shutdown) do - Transport.write(%Response{id: shutdown.id}) - Logger.error("Shutting down") + def apply(%__MODULE__{} = state, %GenLSP.Requests.Shutdown{}) do + Logger.info("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 +201,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 +241,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..d1595050 100644 --- a/apps/expert/mix.exs +++ b/apps/expert/mix.exs @@ -46,7 +46,7 @@ defmodule Expert.MixProject do github: "elixir-lsp/elixir_sense", ref: "73ce7e0d239342fb9527d7ba567203e77dbb9b25"}, {:engine, path: "../engine", env: Mix.env()}, {:forge, path: "../forge", env: Mix.env()}, - {:gen_lsp, "~> 0.10"}, + {:gen_lsp, "~> 0.11"}, {:jason, "~> 1.4"}, {:logger_file_backend, "~> 0.0", only: [:dev, :prod]}, {:patch, "~> 0.15", runtime: false, only: [:dev, :test]}, diff --git a/apps/expert/mix.lock b/apps/expert/mix.lock index 11140d4e..e150551a 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": {: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"}, - "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": {: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"}, "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..687a2a85 100644 --- a/apps/expert/test/expert/project/diagnostics_test.exs +++ b/apps/expert/test/expert/project/diagnostics_test.exs @@ -1,9 +1,9 @@ defmodule Expert.Project.DiagnosticsTest do alias Expert.Test.DispatchFake - alias Expert.Transport alias Forge.Document alias Forge.Plugin.V1.Diagnostic alias GenLSP.Notifications.TextDocumentPublishDiagnostics + alias GenLSP.Structures alias GenLSP.Structures.PublishDiagnosticsParams use ExUnit.Case @@ -39,7 +39,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 +73,23 @@ 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) + expected_severity = GenLSP.Enumerations.DiagnosticSeverity.error() + + assert_receive {:transport, + %TextDocumentPublishDiagnostics{ + params: %PublishDiagnosticsParams{ + diagnostics: [ + %Structures.Diagnostic{ + message: "stuff broke", + severity: ^expected_severity, + source: nil + } + ] + } + }} + + 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: [])) @@ -104,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)" ) @@ -119,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/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/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/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/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/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.lock b/apps/expert_credo/mix.lock index 817951f9..f8481ac3 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": {: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"}, "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": {: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"}, "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..03d069b4 100644 --- a/apps/forge/lib/forge/project.ex +++ b/apps/forge/lib/forge/project.ex @@ -21,16 +21,15 @@ defmodule Forge.Project do @type t :: %__MODULE__{ root_uri: Forge.uri() | nil, mix_exs_uri: Forge.uri() | nil, - entropy: non_neg_integer() - # mix_env: atom(), - # mix_target: atom(), - # env_variables: %{String.t() => String.t()} + entropy: non_neg_integer(), + mix_env: atom(), + mix_target: atom(), + env_variables: %{String.t() => String.t()} } @type error_with_message :: {:error, message} @workspace_directory_name ".expert" - # Public @spec new(Forge.uri()) :: t def new(root_uri) do entropy = :rand.uniform(65_536) 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..93e2be1c 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, "~> 0.11"}, {: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..3b6dbddd 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": {: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"}, "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": {: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"}, "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/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.exs b/mix.exs index 144e6801..22976d94 100644 --- a/mix.exs +++ b/mix.exs @@ -16,8 +16,7 @@ defmodule Expert.LanguageServer.MixProject do defp deps do [ - {:ex_doc, "~> 0.34", only: :dev, runtime: false}, - {:credo, "~> 1.7", only: [:dev, :test]} + {:ex_doc, "~> 0.34", only: :dev, runtime: false} ] end diff --git a/mix.lock b/mix.lock index 5f1588cd..8b754b59 100644 --- a/mix.lock +++ b/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": {: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": {: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"}, @@ -10,7 +10,7 @@ "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.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": {: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"}, diff --git a/nix/hash b/nix/hash index 5caeb59f..aea3a2db 100644 --- a/nix/hash +++ b/nix/hash @@ -1 +1 @@ -sha256-liscEmuTiLnkw1Jw39hOte5Jhxp0BGnSJm9r/A7fey4= +sha256-ojl0uUaODC4pJDGY/Mv8prdkorhQ2PAnWNTx9vXUkfc= 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)).