Skip to content

Commit fb641e4

Browse files
authored
DocumentSymbols provider should handle modules without a name (#179)
* DocumentSymbols provider should handle modules without a name The DocumentSymbols provider should be able to handle a variety of compilation errors so that it can be useful while debugging the compilation errors. The main problem was that the `extract_symbol/2` clause that handled `defmodule` and `defprotocol` was overly restrictive in the pattern match that it did. So I've changed it to match all the possible AST variations (although some may still raise an exception within the clause, but this prevents bad data from propagating). `MISSING_MODULE_NAME` was returned since a non-empty name is required * Update test to use == on an assert that is not making use of pattern matching * Update changelog
1 parent 24ddf88 commit fb641e4

File tree

3 files changed

+87
-3
lines changed

3 files changed

+87
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Meta:
66
Bug Fixes:
77
- Fix `textDocument/documentSymbol` on a non-fully initialized server (thanks [Jason Axelson](https://github.com/axelson)) [#173](https://github.com/elixir-lsp/elixir-ls/pull/173)
88
- Don't return snippets to clients that don't declare `snippetSupport` for completions (thanks [Jason Axelson](https://github.com/axelson)) [#173](https://github.com/elixir-lsp/elixir-ls/pull/177)
9+
- Handle an exception that was raised in the DocumentSymbols provider (thanks [Jason Axelson](https://github.com/axelson)) [#173](https://github.com/elixir-lsp/elixir-ls/pull/179)
910

1011
### v0.3.2: 28 Mar 2020
1112

apps/language_server/lib/language_server/providers/document_symbols.ex

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,28 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbols do
5353
defp extract_modules(_ast), do: []
5454

5555
# Modules, protocols
56-
defp extract_symbol(_module_name, {defname, location, [module_expression, [do: module_body]]})
56+
defp extract_symbol(_module_name, {defname, location, arguments})
5757
when defname in [:defmodule, :defprotocol] do
58+
{module_name, module_body} =
59+
case arguments do
60+
# Handles `defmodule do\nend` type compile errors
61+
[[do: module_body]] ->
62+
# The LSP requires us to return a non-empty name
63+
case defname do
64+
:defmodule -> {"MISSING_MODULE_NAME", module_body}
65+
:defprotocol -> {"MISSING_PROTOCOL_NAME", module_body}
66+
end
67+
68+
[module_expression, [do: module_body]] ->
69+
{extract_module_name(module_expression), module_body}
70+
end
71+
5872
mod_defns =
5973
case module_body do
6074
{:__block__, [], mod_defns} -> mod_defns
6175
stmt -> [stmt]
6276
end
6377

64-
module_name = extract_module_name(module_expression)
65-
6678
module_symbols =
6779
mod_defns
6880
|> Enum.map(&extract_symbol(module_name, &1))

apps/language_server/test/providers/document_symbols_test.exs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,4 +2324,75 @@ defmodule ElixirLS.LanguageServer.Providers.DocumentSymbolsTest do
23242324
}
23252325
]} = DocumentSymbols.symbols(uri, text, false)
23262326
end
2327+
2328+
test "[nested] handles a file with a top-level module without a name" do
2329+
uri = "file://project/test.exs"
2330+
2331+
text = """
2332+
defmodule do
2333+
def foo, do: :bar
2334+
end
2335+
"""
2336+
2337+
assert {:ok, document_symbols} = DocumentSymbols.symbols(uri, text, true)
2338+
2339+
assert [
2340+
%Protocol.DocumentSymbol{
2341+
children: children,
2342+
kind: 2,
2343+
name: "MISSING_MODULE_NAME",
2344+
range: %{
2345+
start: %{line: 0, character: 0},
2346+
end: %{line: 0, character: 0}
2347+
},
2348+
selectionRange: %{
2349+
start: %{line: 0, character: 0},
2350+
end: %{line: 0, character: 0}
2351+
}
2352+
}
2353+
] = document_symbols
2354+
2355+
assert children == [
2356+
%Protocol.DocumentSymbol{
2357+
children: [],
2358+
kind: 12,
2359+
name: "foo",
2360+
range: %{
2361+
start: %{character: 4, line: 1},
2362+
end: %{character: 4, line: 1}
2363+
},
2364+
selectionRange: %{
2365+
start: %{character: 4, line: 1},
2366+
end: %{character: 4, line: 1}
2367+
}
2368+
}
2369+
]
2370+
end
2371+
2372+
test "[nested] handles a file with a top-level protocol module without a name" do
2373+
uri = "file://project/test.exs"
2374+
2375+
text = """
2376+
defprotocol do
2377+
end
2378+
"""
2379+
2380+
assert {:ok, document_symbols} = DocumentSymbols.symbols(uri, text, true)
2381+
2382+
assert document_symbols == [
2383+
%Protocol.DocumentSymbol{
2384+
children: [],
2385+
kind: 11,
2386+
name: "MISSING_PROTOCOL_NAME",
2387+
range: %{
2388+
start: %{line: 0, character: 0},
2389+
end: %{line: 0, character: 0}
2390+
},
2391+
selectionRange: %{
2392+
start: %{line: 0, character: 0},
2393+
end: %{line: 0, character: 0}
2394+
}
2395+
}
2396+
]
2397+
end
23272398
end

0 commit comments

Comments
 (0)