Skip to content

Commit

Permalink
Refactor request handling (#777)
Browse files Browse the repository at this point in the history
* Delete `Provider.Env` in favor of passing server config

* Delete unused provider `:reply_and_alert` response and relevant handling

* Use consistent field name convention in provider queue

* Don't normalize request IDs to strings

* Explicitly start a task supervisor for the provider queue

* Refactor `Provider.Queue` -> `TaskQueue`

* Delete `Server.Provider.Handlers` and move only fun into `Server`
  • Loading branch information
zachallaun authored Jul 10, 2024
1 parent a5be8d1 commit 0841570
Show file tree
Hide file tree
Showing 26 changed files with 473 additions and 593 deletions.
71 changes: 55 additions & 16 deletions apps/server/lib/lexical/server.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
defmodule Lexical.Server do
alias Lexical.Proto.Convert
alias Lexical.Protocol.Notifications
alias Lexical.Protocol.Requests
alias Lexical.Protocol.Responses
alias Lexical.Server.Provider
alias Lexical.Server.Provider.Handlers
alias Lexical.Server.State
alias Lexical.Server.TaskQueue

require Logger

Expand All @@ -23,11 +24,6 @@ defmodule Lexical.Server do

@dialyzer {:nowarn_function, apply_to_state: 2}

@spec response_complete(Requests.request(), Responses.response()) :: :ok
def response_complete(request, response) do
GenServer.call(__MODULE__, {:response_complete, request, response})
end

@spec server_request(
Requests.request(),
(Requests.request(), {:ok, any()} | {:error, term()} -> term())
Expand All @@ -53,10 +49,6 @@ defmodule Lexical.Server do
{:ok, State.new()}
end

def handle_call({:response_complete, _request, _response}, _from, %State{} = state) do
{:reply, :ok, state}
end

def handle_call({:server_request, request, on_response}, _from, %State{} = state) do
new_state = State.add_request(state, request, on_response)
{:reply, :okk, new_state}
Expand Down Expand Up @@ -111,12 +103,12 @@ defmodule Lexical.Server do
end

def handle_message(%Requests.Cancel{} = cancel_request, %State{} = state) do
Provider.Queue.cancel(cancel_request)
TaskQueue.cancel(cancel_request)
{:ok, state}
end

def handle_message(%Notifications.Cancel{} = cancel_notification, %State{} = state) do
Provider.Queue.cancel(cancel_notification)
TaskQueue.cancel(cancel_notification)
{:ok, state}
end

Expand All @@ -138,13 +130,22 @@ defmodule Lexical.Server do
end

def handle_message(%_{} = request, %State{} = state) do
Provider.Queue.add(request, state.configuration)
with {:ok, handler} <- fetch_handler(request),
{:ok, req} <- Convert.to_native(request) do
TaskQueue.add(request.id, {handler, :handle, [req, state.configuration.project]})
else
{:error, {:unhandled, _}} ->
Logger.info("Unhandled request: #{request.method}")

_ ->
:ok
end

{:ok, state}
end

def handle_message(%{} = request, %State{} = state) do
new_state = State.finish_request(state, request)
def handle_message(%{} = response, %State{} = state) do
new_state = State.finish_request(state, response)

{:ok, new_state}
end
Expand All @@ -156,4 +157,42 @@ defmodule Lexical.Server do
error -> {error, state}
end
end

# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defp fetch_handler(%_{} = request) do
case request do
%Requests.FindReferences{} ->
{:ok, Handlers.FindReferences}

%Requests.Formatting{} ->
{:ok, Handlers.Formatting}

%Requests.CodeAction{} ->
{:ok, Handlers.CodeAction}

%Requests.CodeLens{} ->
{:ok, Handlers.CodeLens}

%Requests.Completion{} ->
{:ok, Handlers.Completion}

%Requests.GoToDefinition{} ->
{:ok, Handlers.GoToDefinition}

%Requests.Hover{} ->
{:ok, Handlers.Hover}

%Requests.ExecuteCommand{} ->
{:ok, Handlers.Commands}

%Requests.DocumentSymbols{} ->
{:ok, Handlers.DocumentSymbols}

%Requests.WorkspaceSymbol{} ->
{:ok, Handlers.WorkspaceSymbol}

%request_module{} ->
{:error, {:unhandled, request_module}}
end
end
end
6 changes: 3 additions & 3 deletions apps/server/lib/lexical/server/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Lexical.Server.Application do

alias Lexical.Document
alias Lexical.Server
alias Lexical.Server.Provider
alias Lexical.Server.TaskQueue
alias Lexical.Server.Transport

use Application
Expand All @@ -16,8 +16,8 @@ defmodule Lexical.Server.Application do
document_store_child_spec(),
Server,
{DynamicSupervisor, Server.Project.Supervisor.options()},
Provider.Queue.Supervisor.child_spec(),
Provider.Queue.child_spec(),
{Task.Supervisor, name: TaskQueue.task_supervisor_name()},
TaskQueue,
{Transport.StdIO, [:standard_io, &Server.protocol_message/1]}
]

Expand Down
6 changes: 3 additions & 3 deletions apps/server/lib/lexical/server/configuration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ defmodule Lexical.Server.Configuration do
|> tap(&set/1)
end

@spec new() :: t
def new do
%__MODULE__{support: Support.new()}
@spec new(keyword()) :: t
def new(attrs \\ []) do
struct!(__MODULE__, [support: Support.new()] ++ attrs)
end

defp set(%__MODULE__{} = config) do
Expand Down
28 changes: 0 additions & 28 deletions apps/server/lib/lexical/server/provider/env.ex

This file was deleted.

42 changes: 0 additions & 42 deletions apps/server/lib/lexical/server/provider/handlers.ex

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ defmodule Lexical.Server.Provider.Handlers.CodeAction do
alias Lexical.Protocol.Types.Workspace
alias Lexical.RemoteControl
alias Lexical.RemoteControl.CodeAction
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration

require Logger

def handle(%Requests.CodeAction{} = request, %Env{} = env) do
def handle(%Requests.CodeAction{} = request, %Configuration{} = config) do
diagnostics = Enum.map(request.context.diagnostics, &to_code_action_diagnostic/1)

code_actions =
RemoteControl.Api.code_actions(
env.project,
config.project,
request.document,
request.range,
diagnostics,
Expand Down
6 changes: 3 additions & 3 deletions apps/server/lib/lexical/server/provider/handlers/code_lens.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ defmodule Lexical.Server.Provider.Handlers.CodeLens do
alias Lexical.Protocol.Responses
alias Lexical.Protocol.Types.CodeLens
alias Lexical.RemoteControl
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration
alias Lexical.Server.Provider.Handlers

import Document.Line
require Logger

def handle(%Requests.CodeLens{} = request, %Env{} = env) do
def handle(%Requests.CodeLens{} = request, %Configuration{} = config) do
lenses =
case reindex_lens(env.project, request.document) do
case reindex_lens(config.project, request.document) do
nil -> []
lens -> List.wrap(lens)
end
Expand Down
8 changes: 4 additions & 4 deletions apps/server/lib/lexical/server/provider/handlers/commands.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Lexical.Server.Provider.Handlers.Commands do
alias Lexical.Protocol.Types
alias Lexical.Protocol.Types.ErrorCodes
alias Lexical.RemoteControl
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration
alias Lexical.Server.Window

require ErrorCodes
Expand All @@ -26,12 +26,12 @@ defmodule Lexical.Server.Provider.Handlers.Commands do
)
end

def handle(%Requests.ExecuteCommand{} = request, %Env{} = env) do
def handle(%Requests.ExecuteCommand{} = request, %Configuration{} = config) do
response =
case request.command do
@reindex_name ->
Logger.info("Reindex #{Project.name(env.project)}")
reindex(env.project, request.id)
Logger.info("Reindex #{Project.name(config.project)}")
reindex(config.project, request.id)

invalid ->
message = "#{invalid} is not a valid command"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ defmodule Lexical.Server.Provider.Handlers.Completion do
alias Lexical.Protocol.Responses
alias Lexical.Protocol.Types.Completion
alias Lexical.Server.CodeIntelligence
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration

require Logger

def handle(%Requests.Completion{} = request, %Env{} = env) do
def handle(%Requests.Completion{} = request, %Configuration{} = config) do
completions =
CodeIntelligence.Completion.complete(
env.project,
config.project,
document_analysis(request.document, request.position),
request.position,
request.context || Completion.Context.new(trigger_kind: :invoked)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ defmodule Lexical.Server.Provider.Handlers.DocumentSymbols do
alias Lexical.Protocol.Types.Symbol.Kind, as: SymbolKind
alias Lexical.RemoteControl.Api
alias Lexical.RemoteControl.CodeIntelligence.Symbols
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration

require SymbolKind

def handle(%DocumentSymbols{} = request, %Env{} = env) do
def handle(%DocumentSymbols{} = request, %Configuration{} = config) do
symbols =
env.project
config.project
|> Api.document_symbols(request.document)
|> Enum.map(&to_response(&1, request.document))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ defmodule Lexical.Server.Provider.Handlers.FindReferences do
alias Lexical.Protocol.Requests.FindReferences
alias Lexical.Protocol.Responses
alias Lexical.RemoteControl.Api
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration

require Logger

def handle(%FindReferences{} = request, %Env{} = env) do
def handle(%FindReferences{} = request, %Configuration{} = config) do
include_declaration? = !!request.context.include_declaration

locations =
case Document.Store.fetch(request.document.uri, :analysis) do
{:ok, _document, %Ast.Analysis{} = analysis} ->
Api.references(env.project, analysis, request.position, include_declaration?)
Api.references(config.project, analysis, request.position, include_declaration?)

_ ->
nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ defmodule Lexical.Server.Provider.Handlers.Formatting do
alias Lexical.Protocol.Requests
alias Lexical.Protocol.Responses
alias Lexical.RemoteControl
alias Lexical.Server.Provider.Env
alias Lexical.Server.Configuration

require Logger

def handle(%Requests.Formatting{} = request, %Env{} = env) do
def handle(%Requests.Formatting{} = request, %Configuration{} = config) do
document = request.document

case RemoteControl.Api.format(env.project, document) do
case RemoteControl.Api.format(config.project, document) do
{:ok, %Changes{} = document_edits} ->
response = Responses.Formatting.new(request.id, document_edits)
Logger.info("Response #{inspect(response)}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ defmodule Lexical.Server.Provider.Handlers.GoToDefinition do
alias Lexical.Protocol.Requests.GoToDefinition
alias Lexical.Protocol.Responses
alias Lexical.RemoteControl
alias Lexical.Server.Configuration

require Logger

def handle(%GoToDefinition{} = request, env) do
case RemoteControl.Api.definition(env.project, request.document, request.position) do
def handle(%GoToDefinition{} = request, %Configuration{} = config) do
case RemoteControl.Api.definition(config.project, request.document, request.position) do
{:ok, native_location} ->
{:reply, Responses.GoToDefinition.new(request.id, native_location)}

Expand Down
Loading

0 comments on commit 0841570

Please sign in to comment.