Skip to content

Commit

Permalink
First take on documentation
Browse files Browse the repository at this point in the history
Wired up mix.exs so documentation can be generated
  • Loading branch information
scohen committed Feb 28, 2023
1 parent 31071dc commit d3e20e0
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Lexical.RemoteControl.Completion.Result do
defmodule Function do
@moduledoc false
defstruct [:argument_names, :arity, :name, :origin, :type, :visibility, :spec, :metadata]

def new(%{} = elixir_sense_map) do
Expand All @@ -10,6 +11,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Callback do
@moduledoc false
defstruct [:argument_names, :arity, :metadata, :name, :origin, :spec, :summary, :type]

def new(%{} = elixir_sense_map) do
Expand All @@ -20,6 +22,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Macro do
@moduledoc false
defstruct [:argument_names, :arity, :name, :origin, :type, :visibility, :spec, :metadata]

def new(%{} = elixir_sense_map) do
Expand All @@ -30,6 +33,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Module do
@moduledoc false
defstruct [:full_name, :metadata, :name, :summary]

def new(%{} = elixir_sense_map) do
Expand All @@ -38,6 +42,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Exception do
@moduledoc false
defstruct [:full_name, :metadata, :name, :summary]

def new(%{} = elixir_sense_map) do
Expand All @@ -46,6 +51,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Behaviour do
@moduledoc false
defstruct [:full_name, :metadata, :name, :summary]

def new(%{} = elixir_sense_map) do
Expand All @@ -54,6 +60,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Protocol do
@moduledoc false
defstruct [:full_name, :metadata, :name, :summary]

def new(%{} = elixir_sense_map) do
Expand All @@ -62,6 +69,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Struct do
@moduledoc false
defstruct [:full_name, :metadata, :name, :summary]

def new(%{} = elixir_sense_map) do
Expand All @@ -70,6 +78,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule StructField do
@moduledoc false
defstruct [:call?, :name, :origin]

def new(%{} = elixir_sense_map) do
Expand All @@ -78,6 +87,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule ModuleAttribute do
@moduledoc false
defstruct [:name]

def new(%{} = elixir_sense_map) do
Expand All @@ -86,6 +96,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Typespec do
@moduledoc false
defstruct [:args_list, :airty, :doc, :metadata, :name, :signature, :spec]

def new(%{} = elixir_sense_map) do
Expand All @@ -94,6 +105,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Variable do
@moduledoc false
defstruct [:name]

def new(%{} = elixir_sense_map) do
Expand All @@ -102,6 +114,7 @@ defmodule Lexical.RemoteControl.Completion.Result do
end

defmodule Snippet do
@moduledoc false
defstruct [:detail, :documentation, :filter_text, :kind, :label, :priority, :snippet]

def new(%{} = elixir_sense_map) do
Expand Down
1 change: 0 additions & 1 deletion apps/remote_control/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ defmodule Lexical.RemoteControl.MixProject do
defp deps do
[
{:common, in_umbrella: true},
{:jason, "~> 1.4", optional: true},
{:path_glob, "~> 0.2", optional: true},
{:elixir_sense, git: "https://github.com/elixir-lsp/elixir_sense.git", runtime: false},
{:patch, "~> 0.12", only: [:dev, :test], optional: true, runtime: false}
Expand Down
9 changes: 9 additions & 0 deletions apps/server/lib/lexical/server/transport.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
defmodule Lexical.Server.Transport do
@moduledoc """
A behaviour for a LSP transport
"""

@type level :: :error | :warning | :info | :log

@callback log(level(), Jason.Encoder.t()) :: Jason.Encoder.t()
@callback write(Jason.Encoder.t()) :: Jason.Encoder.t()

alias Lexical.Server.Transport.StdIO

defdelegate log(level, message), to: StdIO
Expand Down
3 changes: 3 additions & 0 deletions apps/server/lib/lexical/server/transport/std_io.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
defmodule Lexical.Server.Transport.StdIO do
alias Lexical.Protocol.Notifications.LogMessage

alias Lexical.Protocol.JsonRpc

@behaviour Lexical.Server.Transport

def start_link(device, callback) do
pid = :proc_lib.spawn_link(__MODULE__, :init, [{callback, device}])
{:ok, pid}
Expand Down
20 changes: 17 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,33 @@ defmodule Lexical.MixProject do
end

defp deps do
[{:ex_doc, "~> 0.29.1", only: :dev, runtime: false}]
[
{:ex_doc, "~> 0.29.1", only: :dev, runtime: false}
]
end

defp docs do
[
main: "Lexical",
extras: ["README.md"],
deps: [jason: "https://hexdocs.pm/jason/Jason.html"],
extras: ~w(README.md pages/architecture.md),
filter_modules: fn mod_name, _ ->
case Module.split(mod_name) do
["Lexical", "Protocol", "Requests" | _] -> true
["Lexical", "Protocol", "Notifications" | _] -> true
["Lexical", "Protocol", "Responses" | _] -> true
["Lexical", "Protocol" | _] -> false
_ -> true
end
end
end,
groups_for_modules: [
Core: ~r/Lexical.^(RemoteControl|Protocol|Server)/,
"Remote Control": ~r/Lexical.RemoteControl/,
"Protocol Requests": ~r/Lexical.Protocol.Requests/,
"Protocol Notifications": ~r/Lexical.Protocol.Notifications/,
"Protocol Responses": ~r/Lexical.Protocol.Responses/,
Server: ~r/Lexical.Server/
]
]
end

Expand Down
25 changes: 25 additions & 0 deletions pages/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Architecture

## Project Structure
Lexical is designed to keep your application isolated from lexical's code. Because of this, lexical is structured as an umbrella app, with the following sub-apps:

* `core`: Contains all code common to the other applications.
* `protocol`: Code related to speaking the language server protocol.
* `remote_control`: The application that's injected into a project's code, which
gives lexical an API to do things in the context of your app.
* `server` The language server itself.

Lexical is an umbrella app so we can control how many dependencies the remote control app has. By separating lexical into sub-applications, each is built as a separate archive, and we can pick and choose which of these applications (and their dependencies) are injected into the project's VM, thus reducing how much contamination the project sees. If lexical was a standard application, adding dependencies to lexical would cause those dependencies to appear in the project's VM, which might cause build issues, version conflicts in mix or other inconsistiencies.

Since the `remote_control` app only depends on `common`, `path_glob` and `elixir_sense`, only those applications pollute the project's vm. Keeping `remote_control`'s dependencies to a minumum is a design goal of this architecutre.


## Language Server
The language server (the `server` app) is the entry point to Lexical. When started by the `start_lexical.sh` command, it sets up a [transport](`Lexical.Server.Transport`) that [reads JsonRPC from standard input and writes responses to standard output](`Lexical.Server.Transport.StdIO`).

When a message is received, it is parsed into either a [LSP Request](`Lexical.Protocol.Requests`) or a [LSP Notification](`Lexical.Protocol.Notifications`) and and then it's handed to the [language server](`Lexical.Server`) to process.

The only mesages the [lexical server process](`Lexical.Server`) handles directly are those related to the lifecycle of the language server itself. All other messages are delegated to a _Provider Handler_. This delegation is accomplished by the server process adding the request to the [provider queue](`Lexical.Server.Provider.Queue`). The provider queue asks the `Lexical.Server.Provider.Handlers.for_request/1` function which handler is configured to handle the request, creates a task for the handler and starts it.

A _Provider Handler_ is just a module that defines a function of arity 2 that takes the request to handle and a `Lexical.Server.Provider.Env`. These functions can reply to the request, ignore it, or do some other action.

0 comments on commit d3e20e0

Please sign in to comment.