From f5a627ce4bb1f2c700770c6e815c41f4dae606b0 Mon Sep 17 00:00:00 2001 From: Jason Axelson Date: Wed, 8 Apr 2020 07:28:57 -1000 Subject: [PATCH] DocumentSymbols: Return the macro used (#189) e.g. `def`, or `defmacro`. This makes the document outline much more useful because you can see if the "function" (in LSP) is a macro, private function, guard, etc. --- .../providers/document_symbols.ex | 38 +++--- .../test/providers/document_symbols_test.exs | 110 +++++++++++------- 2 files changed, 87 insertions(+), 61 deletions(-) diff --git a/apps/language_server/lib/language_server/providers/document_symbols.ex b/apps/language_server/lib/language_server/providers/document_symbols.ex index 7d241dda8..7f7570eda 100644 --- a/apps/language_server/lib/language_server/providers/document_symbols.ex +++ b/apps/language_server/lib/language_server/providers/document_symbols.ex @@ -8,6 +8,10 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do alias ElixirLS.LanguageServer.Providers.SymbolUtils alias ElixirLS.LanguageServer.Protocol + defmodule Info do + defstruct [:type, :name, :location, :children] + end + @defs [:def, :defp, :defmacro, :defmacrop, :defguard, :defguardp, :defdelegate] @docs [ @@ -95,7 +99,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do :defprotocol -> :interface end - %{type: type, name: module_name, location: location, children: module_symbols} + %Info{type: type, name: module_name, location: location, children: module_symbols} end # Protocol implementations @@ -128,7 +132,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do [] end - %{type: :struct, name: name, location: location, children: children} + %Info{type: :struct, name: name, location: location, children: children} end # Docs @@ -148,7 +152,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do type = if type_kind in [:type, :typep, :opaque], do: :class, else: :event - %{ + %Info{ type: type, name: type_name, location: location, @@ -160,32 +164,34 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do defp extract_symbol(_current_module, {:@, _, [{:behaviour, location, [behaviour_expression]}]}) do module_name = extract_module_name(behaviour_expression) - %{type: :constant, name: "@behaviour #{module_name}", location: location, children: []} + %Info{type: :constant, name: "@behaviour #{module_name}", location: location, children: []} end # @impl true defp extract_symbol(_current_module, {:@, _, [{:impl, location, [true]}]}) do - %{type: :constant, name: "@impl true", location: location, children: []} + %Info{type: :constant, name: "@impl true", location: location, children: []} end # @impl BehaviourModule defp extract_symbol(_current_module, {:@, _, [{:impl, location, [impl_expression]}]}) do module_name = extract_module_name(impl_expression) - %{type: :constant, name: "@impl #{module_name}", location: location, children: []} + %Info{type: :constant, name: "@impl #{module_name}", location: location, children: []} end # Other attributes defp extract_symbol(_current_module, {:@, _, [{name, location, _}]}) do - %{type: :constant, name: "@#{name}", location: location, children: []} + %Info{type: :constant, name: "@#{name}", location: location, children: []} end # Function, macro, guard, delegate defp extract_symbol(_current_module, {defname, _, [{_, location, _} = fn_head | _]}) when defname in @defs do - %{ + name = Macro.to_string(fn_head) + + %Info{ type: :function, - name: Macro.to_string(fn_head), + name: "#{defname} #{name}", location: location, children: [] } @@ -193,7 +199,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do # ExUnit test defp extract_symbol(_current_module, {:test, location, [name | _]}) do - %{ + %Info{ type: :function, name: ~s(test "#{name}"), location: location, @@ -204,7 +210,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do # ExUnit setup and setup_all callbacks defp extract_symbol(_current_module, {name, location, [_name | _]}) when name in [:setup, :setup_all] do - %{ + %Info{ type: :function, name: "#{name}", location: location, @@ -227,7 +233,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do |> Enum.map(&extract_symbol(current_module, &1)) |> Enum.reject(&is_nil/1) - %{ + %Info{ type: :function, name: ~s(describe "#{name}"), location: location, @@ -249,7 +255,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do end for key <- keys do - %{ + %Info{ type: :key, name: "config :#{app} #{key}", location: location, @@ -263,7 +269,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do defp build_symbol_information_hierarchical(uri, info) when is_list(info), do: Enum.map(info, &build_symbol_information_hierarchical(uri, &1)) - defp build_symbol_information_hierarchical(uri, info) do + defp build_symbol_information_hierarchical(uri, %Info{} = info) do %Protocol.DocumentSymbol{ name: info.name, kind: SymbolUtils.symbol_kind_to_code(info.type), @@ -278,7 +284,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do defp build_symbol_information_flat(uri, info, parent_name) when is_list(info), do: Enum.map(info, &build_symbol_information_flat(uri, &1, parent_name)) - defp build_symbol_information_flat(uri, info, parent_name) do + defp build_symbol_information_flat(uri, %Info{} = info, parent_name) do case info.children do [_ | _] -> [ @@ -346,7 +352,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do defp extract_module_name(_), do: "# unknown" defp extract_property(property_name, location) when is_atom(property_name) do - %{ + %Info{ type: :property, name: "#{property_name}", location: location, diff --git a/apps/language_server/test/providers/document_symbols_test.exs b/apps/language_server/test/providers/document_symbols_test.exs index e6cbce018..32dde95fe 100644 --- a/apps/language_server/test/providers/document_symbols_test.exs +++ b/apps/language_server/test/providers/document_symbols_test.exs @@ -50,7 +50,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn(arg)", + name: "def my_fn(arg)", range: %{end: %{character: 12, line: 3}, start: %{character: 12, line: 3}}, selectionRange: %{ end: %{character: 12, line: 3}, @@ -60,7 +60,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_private_fn(arg)", + name: "defp my_private_fn(arg)", range: %{end: %{character: 13, line: 4}, start: %{character: 13, line: 4}}, selectionRange: %{ end: %{character: 13, line: 4}, @@ -70,7 +70,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_macro()", + name: "defmacro my_macro()", range: %{end: %{character: 17, line: 5}, start: %{character: 17, line: 5}}, selectionRange: %{ end: %{character: 17, line: 5}, @@ -80,7 +80,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_private_macro()", + name: "defmacrop my_private_macro()", range: %{end: %{character: 18, line: 6}, start: %{character: 18, line: 6}}, selectionRange: %{ end: %{character: 18, line: 6}, @@ -90,7 +90,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_guard(a) when is_integer(a)", + name: "defguard my_guard(a) when is_integer(a)", range: %{end: %{character: 29, line: 7}, start: %{character: 29, line: 7}}, selectionRange: %{ end: %{character: 29, line: 7}, @@ -100,7 +100,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_private_guard(a) when is_integer(a)", + name: "defguardp my_private_guard(a) when is_integer(a)", range: %{end: %{character: 38, line: 8}, start: %{character: 38, line: 8}}, selectionRange: %{ end: %{character: 38, line: 8}, @@ -110,7 +110,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_delegate(list)", + name: "defdelegate my_delegate(list)", range: %{end: %{character: 20, line: 9}, start: %{character: 20, line: 9}}, selectionRange: %{ end: %{character: 20, line: 9}, @@ -120,7 +120,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_guard when 1 == 1", + name: "defguard my_guard when 1 == 1", range: %{end: %{character: 26, line: 10}, start: %{character: 26, line: 10}}, selectionRange: %{ end: %{character: 26, line: 10}, @@ -130,7 +130,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn_no_arg", + name: "def my_fn_no_arg", range: %{end: %{character: 12, line: 11}, start: %{character: 12, line: 11}}, selectionRange: %{ end: %{character: 12, line: 11}, @@ -140,7 +140,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn_with_guard(arg) when is_integer(arg)", + name: "def my_fn_with_guard(arg) when is_integer(arg)", range: %{end: %{character: 34, line: 12}, start: %{character: 34, line: 12}}, selectionRange: %{ end: %{character: 34, line: 12}, @@ -150,7 +150,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn_with_more_blocks(arg)", + name: "def my_fn_with_more_blocks(arg)", range: %{end: %{character: 12, line: 13}, start: %{character: 12, line: 13}}, selectionRange: %{ end: %{character: 12, line: 13}, @@ -213,7 +213,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_fn(arg)", + name: "def my_fn(arg)", kind: 12, location: %{ range: %{end: %{character: 12, line: 3}, start: %{character: 12, line: 3}} @@ -221,7 +221,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_private_fn(arg)", + name: "defp my_private_fn(arg)", kind: 12, location: %{ range: %{end: %{character: 13, line: 4}, start: %{character: 13, line: 4}} @@ -229,7 +229,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_macro()", + name: "defmacro my_macro()", kind: 12, location: %{ range: %{end: %{character: 17, line: 5}, start: %{character: 17, line: 5}} @@ -237,7 +237,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_private_macro()", + name: "defmacrop my_private_macro()", kind: 12, location: %{ range: %{end: %{character: 18, line: 6}, start: %{character: 18, line: 6}} @@ -245,7 +245,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_guard(a) when is_integer(a)", + name: "defguard my_guard(a) when is_integer(a)", kind: 12, location: %{ range: %{end: %{character: 29, line: 7}, start: %{character: 29, line: 7}} @@ -253,7 +253,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_private_guard(a) when is_integer(a)", + name: "defguardp my_private_guard(a) when is_integer(a)", kind: 12, location: %{ range: %{end: %{character: 38, line: 8}, start: %{character: 38, line: 8}} @@ -261,7 +261,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_delegate(list)", + name: "defdelegate my_delegate(list)", kind: 12, location: %{ range: %{end: %{character: 20, line: 9}, start: %{character: 20, line: 9}} @@ -269,7 +269,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_guard when 1 == 1", + name: "defguard my_guard when 1 == 1", kind: 12, location: %{ range: %{end: %{character: 26, line: 10}, start: %{character: 26, line: 10}} @@ -277,7 +277,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_fn_no_arg", + name: "def my_fn_no_arg", kind: 12, location: %{ range: %{end: %{character: 12, line: 11}, start: %{character: 12, line: 11}} @@ -285,7 +285,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_fn_with_guard(arg) when is_integer(arg)", + name: "def my_fn_with_guard(arg) when is_integer(arg)", kind: 12, location: %{ range: %{end: %{character: 34, line: 12}, start: %{character: 34, line: 12}} @@ -293,7 +293,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_fn_with_more_blocks(arg)", + name: "def my_fn_with_more_blocks(arg)", kind: 12, location: %{ range: %{end: %{character: 12, line: 13}, start: %{character: 12, line: 13}} @@ -322,7 +322,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn()", + name: "def my_fn()", range: %{ end: %{character: 14, line: 3}, start: %{character: 14, line: 3} @@ -394,7 +394,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "my_fn()", + name: "def my_fn()", location: %{ range: %{ end: %{character: 14, line: 3}, @@ -424,7 +424,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "some_function()", + name: "def some_function()", range: %{ end: %{character: 12, line: 2}, start: %{character: 12, line: 2} @@ -451,7 +451,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "some_other_function()", + name: "def some_other_function()", range: %{ end: %{character: 12, line: 5}, start: %{character: 12, line: 5} @@ -500,7 +500,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do } }, %Protocol.SymbolInformation{ - name: "some_function()", + name: "def some_function()", kind: 12, location: %{ range: %{ @@ -522,7 +522,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "some_other_function()", + name: "def some_other_function()", location: %{ range: %{ end: %{character: 12, line: 5}, @@ -549,7 +549,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn()", + name: "def my_fn()", range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}}, selectionRange: %{ end: %{character: 12, line: 2}, @@ -583,7 +583,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do } }, %Protocol.SymbolInformation{ - name: "my_fn()", + name: "def my_fn()", kind: 12, location: %{ range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}} @@ -608,7 +608,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn()", + name: "def my_fn()", range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}}, selectionRange: %{ end: %{character: 12, line: 2}, @@ -643,7 +643,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "my_fn()", + name: "def my_fn()", location: %{ range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}} }, @@ -667,7 +667,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn()", + name: "def my_fn()", range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}}, selectionRange: %{ end: %{character: 12, line: 2}, @@ -701,7 +701,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do } }, %Protocol.SymbolInformation{ - name: "my_fn()", + name: "def my_fn()", kind: 12, location: %{ range: %{end: %{character: 12, line: 2}, start: %{character: 12, line: 2}} @@ -730,7 +730,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn()", + name: "def my_fn()", range: %{end: %{character: 14, line: 3}, start: %{character: 14, line: 3}}, selectionRange: %{ end: %{character: 14, line: 3}, @@ -783,7 +783,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "__MODULE__" }, %Protocol.SymbolInformation{ - name: "my_fn()", + name: "def my_fn()", kind: 12, location: %{ range: %{end: %{character: 14, line: 3}, start: %{character: 14, line: 3}} @@ -818,7 +818,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "size(data)", + name: "def size(data)", range: %{end: %{character: 6, line: 2}, start: %{character: 6, line: 2}}, selectionRange: %{ end: %{character: 6, line: 2}, @@ -836,7 +836,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "size(binary)", + name: "def size(binary)", range: %{end: %{character: 6, line: 6}, start: %{character: 6, line: 6}}, selectionRange: %{ end: %{character: 6, line: 6}, @@ -854,7 +854,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "size(param)", + name: "def size(param)", range: %{end: %{character: 6, line: 10}, start: %{character: 6, line: 10}}, selectionRange: %{ end: %{character: 6, line: 10}, @@ -899,7 +899,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "size(data)", + name: "def size(data)", location: %{ range: %{end: %{character: 6, line: 2}, start: %{character: 6, line: 2}} }, @@ -914,7 +914,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "size(binary)", + name: "def size(binary)", location: %{ range: %{end: %{character: 6, line: 6}, start: %{character: 6, line: 6}} }, @@ -929,7 +929,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do }, %Protocol.SymbolInformation{ kind: 12, - name: "size(param)", + name: "def size(param)", location: %{ range: %{end: %{character: 6, line: 10}, start: %{character: 6, line: 10}} }, @@ -1468,7 +1468,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "my_fn(a)", + name: "def my_fn(a)", range: %{end: %{character: 12, line: 3}, start: %{character: 12, line: 3}}, selectionRange: %{ end: %{character: 12, line: 3}, @@ -1511,7 +1511,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do containerName: "MyModule" }, %Protocol.SymbolInformation{ - name: "my_fn(a)", + name: "def my_fn(a)", kind: 12, location: %{ range: %{end: %{character: 12, line: 3}, start: %{character: 12, line: 3}} @@ -2356,7 +2356,7 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do %Protocol.DocumentSymbol{ children: [], kind: 12, - name: "foo", + name: "def foo", range: %{ start: %{character: 4, line: 1}, end: %{character: 4, line: 1} @@ -2410,4 +2410,24 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do assert {:error, :server_error, message} = DocumentSymbols.symbols(uri, text, true) assert String.contains?(message, "Compilation error") end + + test "returns def and defp as a prefix" do + uri = "file://project/test.exs" + + text = """ + defmodule A do + def hello do + greetings() + end + + defp greetings do + IO.puts("Hello, world") + end + end + """ + + assert {:ok, document_symbols} = DocumentSymbols.symbols(uri, text, true) + # IO.inspect(document_symbols, label: "document_symbols") + # assert String.contains?(message, "Compilation error") + end end