Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into signature-help
Browse files Browse the repository at this point in the history
  • Loading branch information
lucacervello committed May 16, 2024
2 parents ad3957c + e99b7a0 commit 92dfabc
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 81 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## [0.22.3](https://github.com/elixir-tools/next-ls/compare/v0.22.2...v0.22.3) (2024-05-15)


### Bug Fixes

* ensure some elixir internals are ready ([#478](https://github.com/elixir-tools/next-ls/issues/478)) ([f4685d0](https://github.com/elixir-tools/next-ls/commit/f4685d01266b4afb7f557d9a361fc7770aa22ec6)), closes [#467](https://github.com/elixir-tools/next-ls/issues/467)

## [0.22.2](https://github.com/elixir-tools/next-ls/compare/v0.22.1...v0.22.2) (2024-05-14)


### Bug Fixes

* **completions:** work in guards ([#475](https://github.com/elixir-tools/next-ls/issues/475)) ([e0573ab](https://github.com/elixir-tools/next-ls/commit/e0573ab23c439313ed2546015f12a21dfe573d1d))

## [0.22.1](https://github.com/elixir-tools/next-ls/compare/v0.22.0...v0.22.1) (2024-05-13)


Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}: let
inherit (nixpkgs) lib;

version = "0.22.1"; # x-release-please-version
version = "0.22.3"; # x-release-please-version

# Helper to provide system-specific attributes
forAllSystems = f:
Expand Down
18 changes: 0 additions & 18 deletions lib/next_ls/autocomplete.ex
Original file line number Diff line number Diff line change
Expand Up @@ -636,24 +636,6 @@ defmodule NextLS.Autocomplete do
for {fun, arity} <- funs,
name = Atom.to_string(fun),
if(exact?, do: name == hint, else: String.starts_with?(name, hint)) do
# doc =
# Enum.find(fdocs, fn {{type, fname, _a}, _, _, _doc, _} ->
# type in [:function, :macro] and to_string(fname) == name
# end)

# doc =
# case doc do
# {_, _, _, %{"en" => fdoc}, _} ->
# """
# ## #{Macro.to_string(mod)}.#{name}/#{arity}

# #{NextLS.DocsHelpers.to_markdown(content_type, fdoc)}
# """

# _ ->
# nil
# end

%{
kind: :function,
data: {mod, name, arity},
Expand Down
56 changes: 40 additions & 16 deletions lib/next_ls/runtime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -231,22 +231,32 @@ defmodule NextLS.Runtime do
true <- connect(node, port, 120) do
NextLS.Logger.info(logger, "Connected to node #{node}")

:next_ls
|> :code.priv_dir()
|> Path.join("monkey/_next_ls_private_compiler.ex")
|> then(&:rpc.call(node, Code, :compile_file, [&1]))
|> tap(fn
{:badrpc, error} ->
NextLS.Logger.error(logger, "Bad RPC call to node #{node}: #{inspect(error)}")
send(me, {:cancel, error})

_ ->
:ok
end)

{:ok, _} = :rpc.call(node, :_next_ls_private_compiler, :start, [])

send(me, {:node, node})
result =
:next_ls
|> :code.priv_dir()
|> Path.join("monkey/_next_ls_private_compiler.ex")
|> then(fn path ->
if await_config_table(node, 5) do
:rpc.call(node, Code, :compile_file, [path])
else
{:badrpc, "internal ets table not found"}
end
end)
|> then(fn
{:badrpc, error} ->
NextLS.Logger.error(logger, "Bad RPC call to node #{node}: #{inspect(error)}")
send(me, {:cancel, error})
:error

_ ->
:ok
end)

if result == :ok do
{:ok, _} = :rpc.call(node, :_next_ls_private_compiler, :start, [])

send(me, {:node, node})
end
else
error ->
send(me, {:cancel, error})
Expand Down Expand Up @@ -275,6 +285,20 @@ defmodule NextLS.Runtime do
end
end

defp await_config_table(_node, 0) do
false
end

defp await_config_table(node, attempts) do
# this is an Elixir implementation detail, handle with care
if :undefined == :rpc.call(node, :ets, :whereis, [:elixir_config]) do
Process.sleep(100)
await_config_table(node, attempts - 1)
else
true
end
end

@impl GenServer
def handle_call(:ready?, _from, state) when is_ready(state) do
{:reply, true, state}
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule NextLS.MixProject do
use Mix.Project

@version "0.22.1" # x-release-please-version
@version "0.22.3" # x-release-please-version

def project do
[
Expand Down
80 changes: 35 additions & 45 deletions priv/monkey/_next_ls_private_compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1032,14 +1032,13 @@ defmodule :_next_ls_private_compiler do
@moduledoc false

def start do
Code.put_compiler_option(:parser_options, columns: true, token_metadata: true)

children = [
:_next_ls_private_compiler_worker
]

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: :_next_ls_private_application_supervisor]
{:ok, pid} = Supervisor.start_link(children, opts)
{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one, name: :_next_ls_private_application_supervisor)
Process.unlink(pid)
{:ok, pid}
end
Expand All @@ -1049,7 +1048,6 @@ defmodule :_next_ls_private_compiler do
def compile do
# keep stdout on this node
Process.group_leader(self(), Process.whereis(:user))
Code.put_compiler_option(:parser_options, columns: true, token_metadata: true)

Code.put_compiler_option(:tracers, [NextLSPrivate.DepTracer | @tracers])

Expand Down Expand Up @@ -1425,39 +1423,36 @@ if Version.match?(System.version(), ">= 1.17.0-dev") do
end
end

defp expand_macro(_meta, Kernel, type, [{name, _, params}, [{_, block}]], _callback, state, env)
when type in [:def, :defp] and is_tuple(block) and is_atom(name) and is_list(params) do
{_, state, penv} =
for p <- params, reduce: {nil, state, env} do
{_, state, penv} ->
expand_pattern(p, state, penv)
end

{res, state, _env} = expand(block, state, penv)
defp expand_macro(_meta, Kernel, type, args, _callback, state, env)
when type in [:def, :defmacro, :defp, :defmacrop] do
# extract the name, params, guards, and blocks
{name, params, guards, blocks} =
case args do
[{:when, _, [{name, _, params} | guards]} | maybe_blocks] ->
{name, params, guards, maybe_blocks}

arity = length(List.wrap(params))
functions = Map.update(state.functions, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
{res, put_in(state.functions, functions), env}
end
[{name, _, params} | maybe_blocks] ->
{name, params, [], maybe_blocks}
end

defp expand_macro(_meta, Kernel, type, [{name, _, params}, block], _callback, state, env)
when type in [:defmacro, :defmacrop] do
{_res, state, penv} = expand(params, state, env)
{res, state, _env} = expand(block, state, penv)
blocks = List.first(blocks, [])

arity = length(List.wrap(params))
macros = Map.update(state.macros, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
{res, put_in(state.macros, macros), env}
end

defp expand_macro(_meta, Kernel, type, [{name, _, params}, blocks], _callback, state, env)
when type in [:def, :defp] and is_atom(name) and is_list(params) and is_list(blocks) do
# collect the environment from the parameters
# parameters are always patterns
{_, state, penv} =
for p <- params, reduce: {nil, state, env} do
{_, state, penv} ->
expand_pattern(p, state, penv)
end

# expand guards, which includes the env from params
{_, state, _} =
for guard <- guards, reduce: {nil, state, penv} do
{_, state, env} ->
expand(guard, state, env)
end

# expand the blocks, there could be `:do`, `:after`, `:catch`, etc
{blocks, state} =
for {type, block} <- blocks, reduce: {[], state} do
{acc, state} ->
Expand All @@ -1467,26 +1462,21 @@ if Version.match?(System.version(), ">= 1.17.0-dev") do

arity = length(List.wrap(params))

functions = Map.update(state.functions, env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
{Enum.reverse(blocks), put_in(state.functions, functions), env}
end

defp expand_macro(_meta, Kernel, type, [{_name, _, params}, blocks], _callback, state, env)
when type in [:def, :defp] and is_list(params) and is_list(blocks) do
{_, state, penv} =
for p <- params, reduce: {nil, state, env} do
{_, state, penv} ->
expand_pattern(p, state, penv)
# determine which key to save this function in state
state_key =
case type do
type when type in [:def, :defp] -> :functions
type when type in [:defmacro, :defmacrop] -> :macros
end

{blocks, state} =
for {type, block} <- blocks, reduce: {[], state} do
{acc, state} ->
{res, state, _env} = expand(block, state, penv)
{[{type, res} | acc], state}
funcs =
if is_atom(name) do
Map.update(state[state_key], env.module, [{name, arity}], &Keyword.put_new(&1, name, arity))
else
state[state_key]
end

{Enum.reverse(blocks), state, env}
{Enum.reverse(blocks), put_in(state[state_key], funcs), env}
end

defp expand_macro(meta, Kernel, :@, [{name, _, p}] = args, callback, state, env) when is_list(p) do
Expand Down
29 changes: 29 additions & 0 deletions test/next_ls/completions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -809,4 +809,33 @@ defmodule NextLS.CompletionsTest do
assert_match %{"kind" => 6, "label" => "items"} in results
assert_match %{"kind" => 6, "label" => "item"} not in results
end

test "parameters are available inside guards", %{client: client, foo: foo} do
uri = uri(foo)

did_open(client, foo, """
defmodule Foo do
def run(items) when is_list(i
end
""")

request client, %{
method: "textDocument/completion",
id: 2,
jsonrpc: "2.0",
params: %{
textDocument: %{
uri: uri
},
position: %{
line: 1,
character: 31
}
}
}

assert_result 2, results

assert_match %{"kind" => 6, "label" => "items"} in results
end
end

0 comments on commit 92dfabc

Please sign in to comment.