From e0573ab23c439313ed2546015f12a21dfe573d1d Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Tue, 14 May 2024 09:52:18 -0400 Subject: [PATCH 1/5] fix(completions): work in guards (#475) --- priv/monkey/_next_ls_private_compiler.ex | 72 +++++++++++------------- test/next_ls/completions_test.exs | 29 ++++++++++ 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/priv/monkey/_next_ls_private_compiler.ex b/priv/monkey/_next_ls_private_compiler.ex index 1ef1b1dc..3e34a6b1 100644 --- a/priv/monkey/_next_ls_private_compiler.ex +++ b/priv/monkey/_next_ls_private_compiler.ex @@ -1425,39 +1425,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) + 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} + + [{name, _, params} | maybe_blocks] -> + {name, params, [], maybe_blocks} end - {res, state, _env} = expand(block, state, penv) - - 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 - - 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} -> @@ -1467,26 +1464,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 diff --git a/test/next_ls/completions_test.exs b/test/next_ls/completions_test.exs index e5422cc8..543fa377 100644 --- a/test/next_ls/completions_test.exs +++ b/test/next_ls/completions_test.exs @@ -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 From 4559d1ae2f58acfae71925458c7f594e80c247ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 10:00:27 -0400 Subject: [PATCH 2/5] chore(main): release 0.22.2 (#476) --- CHANGELOG.md | 7 +++++++ flake.nix | 2 +- mix.exs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a654725..e5f02e04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [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) diff --git a/flake.nix b/flake.nix index 10830b1d..ff2f0890 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ }: let inherit (nixpkgs) lib; - version = "0.22.1"; # x-release-please-version + version = "0.22.2"; # x-release-please-version # Helper to provide system-specific attributes forAllSystems = f: diff --git a/mix.exs b/mix.exs index 73f7a0ae..c4de621f 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule NextLS.MixProject do use Mix.Project - @version "0.22.1" # x-release-please-version + @version "0.22.2" # x-release-please-version def project do [ From f4685d01266b4afb7f557d9a361fc7770aa22ec6 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Wed, 15 May 2024 10:06:15 -0400 Subject: [PATCH 3/5] fix: ensure some elixir internals are ready (#478) Frequently we'd see the following error in tests, and some users were seeing in production: ``` [error] Bad RPC call to node nextls-runtime-1715431446385794000@MacBook -XXX: {:EXIT, {:badarg, [{:ets, :lookup, [:elixir_config, :parser_options], [error_info: %{cause: :id, module: :erl_stdlib_errors}] }, {:elixir_config, :get, 1, [file: ~c"src/elixir_config.erl", line: 21]} , {:elixir_compiler, :string, 3, [file: ~c"src/elixir_compiler.erl", line : 7]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel _checker.ex", line: 112]}, {Code, :compile_file, 1, []}]}} ``` I believe that this is caused by an internal ETS table not being booted yet, so we not await on it being present on the project node. Closes #467 --- .github/workflows/release.yaml | 24 +++++----- lib/next_ls/runtime.ex | 56 +++++++++++++++++------- priv/monkey/_next_ls_private_compiler.ex | 8 ++-- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 49102ae6..3aa8f98e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,22 +9,22 @@ permissions: pull-requests: write jobs: - commitlint: - runs-on: ubuntu-latest - name: commitlint + # commitlint: + # runs-on: ubuntu-latest + # name: commitlint - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install Deps - run: yarn install - - name: Lint PR Title - run: yarn commitlint --from "${{ github.event.before }}" + # steps: + # - uses: actions/checkout@v4 + # with: + # fetch-depth: 0 + # - name: Install Deps + # run: yarn install + # - name: Lint PR Title + # run: yarn commitlint --from "${{ github.event.before }}" release: name: release - needs: commitlint + # needs: commitlint runs-on: ubuntu-latest outputs: release_created: ${{ steps.release.outputs.release_created }} diff --git a/lib/next_ls/runtime.ex b/lib/next_ls/runtime.ex index 4ca68420..749122d6 100644 --- a/lib/next_ls/runtime.ex +++ b/lib/next_ls/runtime.ex @@ -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}) @@ -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} diff --git a/priv/monkey/_next_ls_private_compiler.ex b/priv/monkey/_next_ls_private_compiler.ex index 3e34a6b1..d673b3ea 100644 --- a/priv/monkey/_next_ls_private_compiler.ex +++ b/priv/monkey/_next_ls_private_compiler.ex @@ -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 @@ -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]) From 43a7e623905abff01458b17f1afae67811ffce33 Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Wed, 15 May 2024 10:15:23 -0400 Subject: [PATCH 4/5] chore(ci): replace commitlint --- .github/workflows/release.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3aa8f98e..49102ae6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,22 +9,22 @@ permissions: pull-requests: write jobs: - # commitlint: - # runs-on: ubuntu-latest - # name: commitlint + commitlint: + runs-on: ubuntu-latest + name: commitlint - # steps: - # - uses: actions/checkout@v4 - # with: - # fetch-depth: 0 - # - name: Install Deps - # run: yarn install - # - name: Lint PR Title - # run: yarn commitlint --from "${{ github.event.before }}" + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install Deps + run: yarn install + - name: Lint PR Title + run: yarn commitlint --from "${{ github.event.before }}" release: name: release - # needs: commitlint + needs: commitlint runs-on: ubuntu-latest outputs: release_created: ${{ steps.release.outputs.release_created }} From e99b7a0ba74382d931ab1347ffe7c32632a45bd2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 May 2024 10:15:56 -0400 Subject: [PATCH 5/5] chore(main): release 0.22.3 (#479) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ flake.nix | 2 +- mix.exs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f02e04..5c10755b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # 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) diff --git a/flake.nix b/flake.nix index ff2f0890..a5ae57ab 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ }: let inherit (nixpkgs) lib; - version = "0.22.2"; # x-release-please-version + version = "0.22.3"; # x-release-please-version # Helper to provide system-specific attributes forAllSystems = f: diff --git a/mix.exs b/mix.exs index c4de621f..7dd22abb 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule NextLS.MixProject do use Mix.Project - @version "0.22.2" # x-release-please-version + @version "0.22.3" # x-release-please-version def project do [