Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions lib/gen_lsp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ defmodule GenLSP do
type: {:or, [:pid, :atom]},
doc: "The `t:pid/0` or name of the `GenLSP.Buffer` process."
],
assigns: [
type: {:or, [:pid, :atom]},
doc: "The `t:pid/0` or name of the `GenLSP.Assigns` process."
],
name: [
type: :atom,
doc:
Expand All @@ -188,15 +192,16 @@ defmodule GenLSP do
opts = NimbleOptions.validate!(opts, @options_schema)

:proc_lib.start_link(__MODULE__, :init, [
{module, init_args, Keyword.take(opts, [:name, :buffer]), self()}
{module, init_args, Keyword.take(opts, [:name, :buffer, :assigns]), self()}
])
end

@doc false
def init({module, init_args, opts, parent}) do
me = self()
buffer = opts[:buffer]
lsp = %LSP{mod: module, pid: me, buffer: buffer}
assigns = opts[:assigns]
lsp = %LSP{mod: module, pid: me, buffer: buffer, assigns: assigns}

case module.init(lsp, init_args) do
{:ok, %LSP{} = lsp} ->
Expand Down
22 changes: 22 additions & 0 deletions lib/gen_lsp/assigns.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule GenLSP.Assigns do
use Agent

def start_link(opts \\ []) do
Agent.start_link(fn -> Map.new() end, Keyword.take(opts, [:name]))
end

def get(agent) do
Agent.get(agent, & &1)
end

def merge(agent, new_assigns) do
Agent.update(agent, &Map.merge(&1, Map.new(new_assigns)))
end

def update(agent, callback) do
Agent.update(agent, fn assigns ->
new_assigns = callback.(assigns)
Map.merge(assigns, Map.new(new_assigns))
end)
end
end
19 changes: 16 additions & 3 deletions lib/gen_lsp/lsp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,26 @@ defmodule GenLSP.LSP do
"""
typedstruct do
field :mod, atom(), enforce: true
field :assigns, map(), default: Map.new()
field :buffer, atom() | pid()
field :assigns, atom() | pid()
field :pid, pid()
end

@spec assign(t(), Keyword.t()) :: t()
@spec assign(t(), Keyword.t() | (map() -> keyword())) :: t()
def assign(%__MODULE__{assigns: assigns} = lsp, new_assigns) when is_list(new_assigns) do
%{lsp | assigns: Map.merge(assigns, Map.new(new_assigns))}
GenLSP.Assigns.merge(assigns, new_assigns)

lsp
end

def assign(%__MODULE__{assigns: assigns} = lsp, callback) when is_function(callback, 1) do
GenLSP.Assigns.update(assigns, callback)

lsp
end

@spec assigns(t()) :: map()
def assigns(%__MODULE__{assigns: assigns}) do
GenLSP.Assigns.get(assigns)
end
end
18 changes: 16 additions & 2 deletions lib/gen_lsp/test.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,32 @@ defmodule GenLSP.Test do
@spec server(mod :: atom(), opts :: Keyword.t()) :: server()
def server(mod, opts \\ []) do
buffer_id = Keyword.get(opts, :buffer_id, :buffer)
assigns_id = Keyword.get(opts, :assigns_id, :assigns)
lsp_id = Keyword.get(opts, :lsp_id, :lsp)

buffer =
start_supervised!({GenLSP.Buffer, communication: {GenLSP.Communication.TCP, [port: 0]}},
id: buffer_id
)

assigns =
start_supervised!(GenLSP.Assigns, id: assigns_id)

{:ok, port} = :inet.port(GenLSP.Buffer.comm_state(buffer).lsocket)

lsp = start_supervised!({mod, Keyword.merge([buffer: buffer], opts)}, id: lsp_id)
lsp =
start_supervised!({mod, Keyword.merge([buffer: buffer, assigns: assigns], opts)},
id: lsp_id
)

%{lsp: lsp, buffer: buffer, port: port, buffer_id: buffer_id, lsp_id: lsp_id}
%{
lsp: lsp,
buffer: buffer,
assigns: assigns,
port: port,
buffer_id: buffer_id,
lsp_id: lsp_id
}
end

@doc """
Expand Down
9 changes: 4 additions & 5 deletions test/gen_lsp/communication/tcp_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,19 @@
@string ~s|{"a":"‘","b":"#{String.duplicate("hello world! ", 5000)}"}|
@length byte_size(@string)

@port 9000
@port 5000

@connect_opts [:binary, packet: :raw, active: false]

test "works" do

Check failure on line 33 in test/gen_lsp/communication/tcp_test.exs

View workflow job for this annotation

GitHub Actions / Test (1.17.x/26.x)

test works (GenLSP.Communication.TCPTest)

Check failure on line 33 in test/gen_lsp/communication/tcp_test.exs

View workflow job for this annotation

GitHub Actions / Test (1.18.x/28.x)

test works (GenLSP.Communication.TCPTest)
me = self()
# the following match ensures that the script completes and does
# not raise after stdin is closed.
Task.start_link(fn ->
{:ok, args} = GenLSP.Communication.TCP.init(port: @port)
{:ok, args} = GenLSP.Communication.TCP.listen(args)
send(me, {:done, args})
{:ok, tcp} = GenLSP.Communication.TCP.init(port: @port)
{:ok, tcp} = GenLSP.Communication.TCP.listen(tcp)

assert :eof = GenLSP.Support.Buffer.loop(args, me, "")
assert :eof = GenLSP.Support.Buffer.loop(tcp, me, "")
end)

assert {:ok, socket} = connect()
Expand Down
8 changes: 1 addition & 7 deletions test/gen_lsp_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@ defmodule GenLSPTest do
test "stores the user state and internal state", %{server: server} do
assert alive?(server)

assert %GenLSP.LSP{
assigns: %{foo: :bar, test_pid: self()},
buffer: server.buffer,
pid: server.lsp,
mod: GenLSPTest.ExampleServer
} ==
:sys.get_state(server.lsp)
assert %{foo: :bar, test_pid: self()} == :sys.get_state(server.assigns)
end

test "can receive and reply to a request", %{client: client} do
Expand Down
8 changes: 4 additions & 4 deletions test/support/example_server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ defmodule GenLSPTest.ExampleServer do
}
})

send(lsp.assigns.test_pid, result)
send(assigns(lsp).test_pid, result)

GenLSP.log(lsp, "done initializing")

Expand All @@ -87,7 +87,7 @@ defmodule GenLSPTest.ExampleServer do

@impl true
def handle_notification(%Notifications.TextDocumentDidOpen{} = notification, lsp) do
send(lsp.assigns.test_pid, {:callback, notification})
send(assigns(lsp).test_pid, {:callback, notification})

{:noreply, lsp}
end
Expand All @@ -98,7 +98,7 @@ defmodule GenLSPTest.ExampleServer do
} = notification,
lsp
) do
send(lsp.assigns.test_pid, {:callback, notification})
send(assigns(lsp).test_pid, {:callback, notification})

GenLSP.notify(lsp, %Notifications.TextDocumentPublishDiagnostics{
params: %Structures.PublishDiagnosticsParams{
Expand Down Expand Up @@ -126,7 +126,7 @@ defmodule GenLSPTest.ExampleServer do
end

def handle_info(_message, lsp) do
send(lsp.assigns.test_pid, {:info, :ack})
send(assigns(lsp).test_pid, {:info, :ack})
{:noreply, lsp}
end
end
Loading