diff --git a/lib/next_ls.ex b/lib/next_ls.ex index 499a41bd..5df083b7 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -215,6 +215,20 @@ defmodule NextLS do [module, "alias"] ) + {:attribute, module, attribute} -> + DB.query( + database, + ~Q""" + SELECT file, start_line, end_line, start_column, end_column + FROM "references" as refs + WHERE refs.identifier = ? + AND refs.type = ? + AND refs.module = ? + AND refs.source = 'user' + """, + [attribute, "attribute", module] + ) + :unknown -> [] end @@ -266,7 +280,7 @@ defmodule NextLS do filtered_symbols = for {pid, _} <- entries, symbol <- symbols.(pid), score = fuzzy_match(symbol.name, query, case_sensitive?) do name = - if symbol.type != "defstruct" do + if symbol.type not in ["defstruct", "attribute"] do "#{symbol.type} #{symbol.name}" else "#{symbol.name}" @@ -679,6 +693,7 @@ defmodule NextLS do defp elixir_kind_to_lsp_kind("defmodule"), do: GenLSP.Enumerations.SymbolKind.module() defp elixir_kind_to_lsp_kind("defstruct"), do: GenLSP.Enumerations.SymbolKind.struct() + defp elixir_kind_to_lsp_kind("attribute"), do: GenLSP.Enumerations.SymbolKind.property() defp elixir_kind_to_lsp_kind(kind) when kind in ["def", "defp", "defmacro", "defmacrop"], do: GenLSP.Enumerations.SymbolKind.function() @@ -737,11 +752,17 @@ defmodule NextLS do [[module, "defmacro", function]] -> {:function, module, function} + [[module, "attribute", attribute]] -> + {:attribute, module, attribute} + _unknown_definition -> case DB.query(database, reference_query, [file, line, col]) do [[function, "function", module]] -> {:function, module, function} + [[attribute, "attribute", module]] -> + {:attribute, module, attribute} + [[_alias, "alias", module]] -> {:module, module} diff --git a/lib/next_ls/runtime/sidecar.ex b/lib/next_ls/runtime/sidecar.ex index 17968679..9c98c349 100644 --- a/lib/next_ls/runtime/sidecar.ex +++ b/lib/next_ls/runtime/sidecar.ex @@ -15,7 +15,7 @@ defmodule NextLS.Runtime.Sidecar do end def handle_info({:tracer, payload}, state) do - "Elixir." <> module_name = to_string(payload.module) + module_name = payload.module |> to_string() |> String.replace("Elixir.", "") all_symbols = parse_symbols(payload.file, module_name) attributes = filter_attributes(all_symbols) diff --git a/test/next_ls/references_test.exs b/test/next_ls/references_test.exs index be8ef570..e840cb02 100644 --- a/test/next_ls/references_test.exs +++ b/test/next_ls/references_test.exs @@ -33,6 +33,18 @@ defmodule NextLS.ReferencesTest do Peace.and_love() end end + + defmodule Foo do + @foo_attr 123 + + def foo_foo(a) do + {:ok, a + @foo_attr} + end + + def foo2 do + {:error, @foo_attr} + end + end """) [bar: bar, peace: peace] @@ -105,4 +117,45 @@ defmodule NextLS.ReferencesTest do } ] end + + test "list attribute references", %{client: client, bar: bar} = context do + assert :ok == notify(client, %{method: "initialized", jsonrpc: "2.0", params: %{}}) + assert_request(client, "client/registerCapability", fn _params -> nil end) + assert_is_ready(context, "my_proj") + assert_compiled(context, "my_proj") + assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}} + + request(client, %{ + method: "textDocument/references", + id: 4, + jsonrpc: "2.0", + params: %{ + position: %{line: 8, character: 4}, + textDocument: %{uri: uri(bar)}, + context: %{includeDeclaration: true} + } + }) + + uri = uri(bar) + + assert_result2( + 4, + [ + %{ + "uri" => uri, + "range" => %{ + "start" => %{"line" => 11, "character" => 14}, + "end" => %{"line" => 11, "character" => 23} + } + }, + %{ + "uri" => uri, + "range" => %{ + "start" => %{"line" => 15, "character" => 13}, + "end" => %{"line" => 15, "character" => 22} + } + } + ] + ) + end end