From 9c7ff4df880582eb20f22226bb5c442c0274143c Mon Sep 17 00:00:00 2001 From: Mitchell Hanberg Date: Mon, 1 Apr 2024 11:35:38 -0400 Subject: [PATCH] fix(completions): log source code when env fails to build (#404) Closes #403 --- lib/next_ls.ex | 20 ++++- lib/next_ls/helpers/ast_helpers.ex | 15 ++++ lib/next_ls/helpers/ast_helpers/env.ex | 15 ++-- test/next_ls/helpers/ast_helpers/env_test.exs | 76 +++++-------------- 4 files changed, 60 insertions(+), 66 deletions(-) diff --git a/lib/next_ls.ex b/lib/next_ls.ex index d3fc541f..3334aea7 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -577,18 +577,36 @@ defmodule NextLS do def handle_request(%TextDocumentCompletion{params: %{text_document: %{uri: uri}, position: position}}, lsp) do document = lsp.assigns.documents[uri] - env = + spliced = document |> List.update_at(position.line, fn row -> {front, back} = String.split_at(row, position.character) String.slice(front, -1..1) <> "__cursor__()" <> back end) |> Enum.join("\n") + + env = + spliced |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) |> then(fn {:ok, ast} -> ast {:error, ast, _} -> ast end) + |> NextLS.ASTHelpers.find_cursor() + |> then(fn + {:ok, cursor} -> + cursor + + {:error, :not_found} -> + NextLS.Logger.warning(lsp.assigns.logger, "Could not locate cursor when building environment") + + NextLS.Logger.warning( + lsp.assigns.logger, + "Source code that produced the above warning: #{spliced}" + ) + + nil + end) |> NextLS.ASTHelpers.Env.build() document_slice = diff --git a/lib/next_ls/helpers/ast_helpers.ex b/lib/next_ls/helpers/ast_helpers.ex index 109cff13..1277c6b5 100644 --- a/lib/next_ls/helpers/ast_helpers.ex +++ b/lib/next_ls/helpers/ast_helpers.ex @@ -1,5 +1,6 @@ defmodule NextLS.ASTHelpers do @moduledoc false + alias Sourceror.Zipper defmodule Attributes do @moduledoc false @@ -152,4 +153,18 @@ defmodule NextLS.ASTHelpers do end end) end + + def find_cursor(ast) do + with nil <- + ast + |> Zipper.zip() + |> Zipper.find(fn + {:__cursor__, _, []} -> true + _ -> false + end) do + {:error, :not_found} + else + zipper -> {:ok, zipper} + end + end end diff --git a/lib/next_ls/helpers/ast_helpers/env.ex b/lib/next_ls/helpers/ast_helpers/env.ex index e2808d85..c816afe1 100644 --- a/lib/next_ls/helpers/ast_helpers/env.ex +++ b/lib/next_ls/helpers/ast_helpers/env.ex @@ -6,15 +6,11 @@ defmodule NextLS.ASTHelpers.Env do Sourceror.compare_positions(range.start, position) == :lt && Sourceror.compare_positions(range.end, position) == :gt end - def build(ast) do - cursor = - ast - |> Zipper.zip() - |> Zipper.find(fn - {:__cursor__, _, _} -> true - _ -> false - end) + def build(nil) do + %{variables: []} + end + def build(cursor) do position = cursor |> Zipper.node() |> Sourceror.get_range() |> Map.get(:start) zipper = Zipper.prev(cursor) @@ -75,7 +71,8 @@ defmodule NextLS.ASTHelpers.Env do acc end - {def, _, [{_, _, args} | _]} when def in [:def, :defp, :defmacro, :defmacrop] and args != [] and is_inside -> + {def, _, [{_, _, args} | _]} + when def in [:def, :defp, :defmacro, :defmacrop] and args != [] and is_inside -> {_, vars} = Macro.prewalk(args, [], fn node, acc -> case node do diff --git a/test/next_ls/helpers/ast_helpers/env_test.exs b/test/next_ls/helpers/ast_helpers/env_test.exs index 473f8e07..bf68ee20 100644 --- a/test/next_ls/helpers/ast_helpers/env_test.exs +++ b/test/next_ls/helpers/ast_helpers/env_test.exs @@ -19,14 +19,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["foo", "bar"] end @@ -46,14 +39,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["two", "one"] end @@ -76,14 +62,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["baz", "bar", "foo"] end @@ -106,14 +85,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["three", "two", "one"] end @@ -133,14 +105,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["foo", "bar"] end @@ -161,14 +126,7 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["baz", "bar", "big_bar"] end @@ -194,16 +152,22 @@ defmodule NextLS.ASTHelpers.EnvTest do end """ - actual = - code - |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) - |> then(fn - {:ok, ast} -> ast - {:error, ast, _} -> ast - end) - |> NextLS.ASTHelpers.Env.build() + actual = run(code) assert actual.variables == ["entries"] end end + + defp run(code) do + {:ok, zip} = + code + |> Spitfire.parse(literal_encoder: &{:ok, {:__literal__, &2, [&1]}}) + |> then(fn + {:ok, ast} -> ast + {:error, ast, _} -> ast + end) + |> NextLS.ASTHelpers.find_cursor() + + NextLS.ASTHelpers.Env.build(zip) + end end