Skip to content

Commit

Permalink
Remove support for query prefixes in WorkspaceSymbolsProvider
Browse files Browse the repository at this point in the history
Closes #392
  • Loading branch information
lukaszsamson committed Nov 4, 2020
1 parent 99b6d78 commit 5e9697b
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 209 deletions.
20 changes: 1 addition & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,25 +158,7 @@ The completions include:

## Workspace Symbols

With Dialyzer integration enabled ElixirLS will build an index of symbols (modules, functions, types and callbacks). The symbols are taken from the current workspace, all dependencies and stdlib (Elixir and erlang). This feature enables quick navigation to symbol definitions. However due to sheer number of different symbols and fuzzy search utilized by the provider, ElixirLS uses query prefixes to improve search results relevance.

Use the following rules when navigating to workspace symbols:
* no prefix - search for modules
* `:erl`
* `Enu`
* `f ` prefix - search for functions
* `f inse`
* `f :ets.inse`
* `f Enum.cou`
* `f count/0`
* `t ` prefix - search for types
* `t t/0`
* `t :erlang.time_u`
* `t DateTime.`
* `c ` prefix - search for callbacks
* `c handle_info`
* `c GenServer.in`
* `c :gen_statem`
With Dialyzer integration enabled ElixirLS will build an index of symbols (modules, functions, types and callbacks). The symbols are taken from the current workspace, all dependencies and stdlib (Elixir and erlang). This feature enables quick navigation to symbol definitions.

## Troubleshooting

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,7 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbols do

@spec symbols(String.t()) :: {:ok, [symbol_information_t]}
def symbols(query, server \\ __MODULE__) do
results =
case query do
"f " <> fun_query ->
query(:functions, fun_query, server)

"t " <> type_query ->
query(:types, type_query, server)

"c " <> callback_query ->
query(:callbacks, callback_query, server)

module_query ->
query(:modules, module_query, server)
end
results = query(query, server)

{:ok, results}
end
Expand Down Expand Up @@ -109,10 +96,10 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbols do
end

@impl GenServer
def handle_call({:query, key, query}, from, state) do
def handle_call({:query, query}, from, state) do
{:ok, _pid} =
Task.start_link(fn ->
results = get_results(state, key, query)
results = get_results(state, query)
GenServer.reply(from, results)
end)

Expand Down Expand Up @@ -356,13 +343,13 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbols do
|> elem(0)
end

defp query(kind, query, server) do
defp query(query, server) do
case String.trim(query) do
"" ->
[]

trimmed ->
GenServer.call(server, {:query, kind, trimmed})
GenServer.call(server, {:query, trimmed})
end
end

Expand Down Expand Up @@ -446,14 +433,13 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbols do
:ok
end

@spec get_results(state_t, key_t, String.t()) :: [symbol_information_t]
defp get_results(state, key, query) do
@spec get_results(state_t, String.t()) :: [symbol_information_t]
defp get_results(state, query) do
query_downcase = String.downcase(query)
query_length = String.length(query)
arity_suffix = Regex.run(@arity_suffix_regex, query)

state
|> Map.fetch!(key)
(state.modules ++ state.functions ++ state.types ++ state.callbacks)
|> process_chunked(fn chunk ->
chunk
|> Enum.map(&{&1, get_score(&1.name, query, query_downcase, query_length, arity_suffix)})
Expand Down Expand Up @@ -509,15 +495,15 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbols do
end

defp symbol_name(:functions, {module, function, arity}) do
"f #{inspect(module)}.#{function}/#{arity}"
"#{inspect(module)}.#{function}/#{arity}"
end

defp symbol_name(:types, {module, type, arity}) do
"t #{inspect(module)}.#{type}/#{arity}"
"#{inspect(module)}.#{type}/#{arity}"
end

defp symbol_name(:callbacks, {module, callback, arity}) do
"c #{inspect(module)}.#{callback}/#{arity}"
"#{inspect(module)}.#{callback}/#{arity}"
end

@spec build_range(nil | non_neg_integer) :: range_t
Expand Down
260 changes: 95 additions & 165 deletions apps/language_server/test/providers/workspace_symbols_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,180 +44,110 @@ defmodule ElixirLS.LanguageServer.Providers.WorkspaceSymbolsTest do
end

test "returns modules", %{server: server} do
assert {:ok,
[
%{
kind: 2,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}},
uri: uri
},
name: "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols"
}
]} = WorkspaceSymbols.symbols("ElixirLS.LanguageServer.Fixtures.", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert {:ok,
[
%{
kind: 2,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}},
uri: uri
},
name: "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols"
}
]} = WorkspaceSymbols.symbols("work", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")
assert {:ok, list} = WorkspaceSymbols.symbols("ElixirLS.LanguageServer.Fixtures.", server)

assert module =
Enum.find(list, &(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols"))

assert module.kind == 2
assert module.location.uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert module.location.range == %{
end: %{character: 0, line: 1},
start: %{character: 0, line: 0}
}

assert WorkspaceSymbols.symbols("work", server)
|> elem(1)
|> Enum.any?(&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols"))
end

test "returns functions", %{server: server} do
assert {
:ok,
[
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}},
uri: uri
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.module_info/1"
},
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}}
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.module_info/0"
},
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}}
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.behaviour_info/1"
},
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 3}, start: %{character: 0, line: 2}}
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_macro/1"
},
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 2}, start: %{character: 0, line: 1}}
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_function/1"
},
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 1}, start: %{character: 0, line: 0}}
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.__info__/1"
}
]
} = WorkspaceSymbols.symbols("f ElixirLS.LanguageServer.Fixtures.", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert {:ok,
[
%{
kind: 12,
location: %{
range: %{end: %{character: 0, line: 2}, start: %{character: 0, line: 1}},
uri: uri
},
name: "f ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_function/1"
}
]} = WorkspaceSymbols.symbols("f fun", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")
assert {:ok, list} = WorkspaceSymbols.symbols("ElixirLS.LanguageServer.Fixtures.", server)

assert some_function =
Enum.find(
list,
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_function/1")
)

assert some_function.kind == 12

assert some_function.location.uri
|> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert some_function.location.range == %{
end: %{character: 0, line: 2},
start: %{character: 0, line: 1}
}

assert WorkspaceSymbols.symbols("fun", server)
|> elem(1)
|> Enum.any?(
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_function/1")
)
end

test "returns types", %{server: server} do
assert {
:ok,
[
%{
kind: 5,
location: %{
range: %{end: %{character: 0, line: 8}, start: %{character: 0, line: 7}},
uri: uri
},
name: "t ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_type/0"
},
%{
kind: 5,
location: %{
range: %{end: %{character: 0, line: 9}, start: %{character: 0, line: 8}}
},
name: "t ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_opaque_type/0"
}
]
} = WorkspaceSymbols.symbols("t ElixirLS.LanguageServer.Fixtures.", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert {
:ok,
[
%{
kind: 5,
location: %{
range: %{end: %{character: 0, line: 9}, start: %{character: 0, line: 8}},
uri: uri
},
name: "t ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_opaque_type/0"
}
]
} = WorkspaceSymbols.symbols("t opa", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")
assert {:ok, list} = WorkspaceSymbols.symbols("ElixirLS.LanguageServer.Fixtures.", server)

assert some_type =
Enum.find(
list,
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_type/0")
)

assert some_type.kind == 5

assert some_type.location.uri
|> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert some_type.location.range == %{
end: %{character: 0, line: 8},
start: %{character: 0, line: 7}
}

assert Enum.any?(
list,
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_opaque_type/0")
)

assert WorkspaceSymbols.symbols("opa", server)
|> elem(1)
|> Enum.any?(
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_opaque_type/0")
)
end

test "returns callbacks", %{server: server} do
assert {
:ok,
[
%{
kind: 24,
location: %{
range: %{end: %{character: 0, line: 5}, start: %{character: 0, line: 4}},
uri: uri
},
name: "c ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_callback/1"
},
%{
kind: 24,
location: %{
range: %{end: %{character: 0, line: 6}, start: %{character: 0, line: 5}}
},
name: "c ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_macrocallback/1"
}
]
} = WorkspaceSymbols.symbols("c ElixirLS.LanguageServer.Fixtures.", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert {:ok,
[
%{
kind: 24,
location: %{
range: %{end: %{character: 0, line: 6}, start: %{character: 0, line: 5}},
uri: uri
},
name: "c ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_macrocallback/1"
}
]} = WorkspaceSymbols.symbols("c macr", server)

assert uri |> String.ends_with?("test/support/fixtures/workspace_symbols.ex")
assert {:ok, list} = WorkspaceSymbols.symbols("ElixirLS.LanguageServer.Fixtures.", server)

assert some_callback =
Enum.find(
list,
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_callback/1")
)

assert some_callback.kind == 24

assert some_callback.location.uri
|> String.ends_with?("test/support/fixtures/workspace_symbols.ex")

assert some_callback.location.range == %{
end: %{character: 0, line: 5},
start: %{character: 0, line: 4}
}

assert Enum.any?(
list,
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_macrocallback/1")
)

assert WorkspaceSymbols.symbols("macr", server)
|> elem(1)
|> Enum.any?(
&(&1.name == "ElixirLS.LanguageServer.Fixtures.WorkspaceSymbols.some_macrocallback/1")
)
end

defp wait_until_indexed(pid) do
Expand Down

0 comments on commit 5e9697b

Please sign in to comment.