-
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(elixir): compiler diagnostics (#8)
Introduce the "extension" concept and creates the ElixirExtension, which provides Elixir compiler diagnostics. TODO: Correct the column information on the diagnostics. I think there are some commits on main that fix some of this. But there are Tokenizer diagnostics that need some massaging as well. The interface for extensions also needs to to be finalized.
- Loading branch information
Showing
10 changed files
with
412 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
defmodule NextLS.DiagnosticCache do | ||
# TODO: this should be an ETS table | ||
@moduledoc """ | ||
Cache for diagnostics. | ||
""" | ||
use Agent | ||
|
||
def start_link(opts) do | ||
Agent.start_link(fn -> Map.new() end, Keyword.take(opts, [:name])) | ||
end | ||
|
||
def get(cache) do | ||
Agent.get(cache, & &1) | ||
end | ||
|
||
def put(cache, namespace, filename, diagnostic) do | ||
Agent.update(cache, fn cache -> | ||
Map.update(cache, namespace, %{filename => [diagnostic]}, fn cache -> | ||
Map.update(cache, filename, [diagnostic], fn v -> | ||
[diagnostic | v] | ||
end) | ||
end) | ||
end) | ||
end | ||
|
||
def clear(cache, namespace) do | ||
Agent.update(cache, fn cache -> | ||
Map.update(cache, namespace, %{}, fn cache -> | ||
for {k, _} <- cache, into: Map.new() do | ||
{k, []} | ||
end | ||
end) | ||
end) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
defmodule NextLS.ElixirExtension do | ||
use GenServer | ||
|
||
alias NextLS.DiagnosticCache | ||
|
||
def start_link(args) do | ||
GenServer.start_link( | ||
__MODULE__, | ||
Keyword.take(args, [:cache, :registry, :publisher]), | ||
Keyword.take(args, [:name]) | ||
) | ||
end | ||
|
||
@impl GenServer | ||
def init(args) do | ||
cache = Keyword.fetch!(args, :cache) | ||
registry = Keyword.fetch!(args, :registry) | ||
publisher = Keyword.fetch!(args, :publisher) | ||
|
||
Registry.register(registry, :extension, :elixir) | ||
|
||
{:ok, %{cache: cache, registry: registry, publisher: publisher}} | ||
end | ||
|
||
@impl GenServer | ||
def handle_info({:compiler, diagnostics}, state) do | ||
DiagnosticCache.clear(state.cache, :elixir) | ||
|
||
for d <- diagnostics do | ||
# TODO: some compiler diagnostics only have the line number | ||
# but we want to only highlight the source code, so we | ||
# need to read the text of the file (either from the lsp cache | ||
# if the source code is "open", or read from disk) and calculate the | ||
# column of the first non-whitespace character. | ||
# | ||
# it is not clear to me whether the LSP process or the extension should | ||
# be responsible for this. The open documents live in the LSP process | ||
DiagnosticCache.put(state.cache, :elixir, d.file, %GenLSP.Structures.Diagnostic{ | ||
severity: severity(d.severity), | ||
message: d.message, | ||
source: d.compiler_name, | ||
range: range(d.position) | ||
}) | ||
end | ||
|
||
send(state.publisher, :publish) | ||
|
||
{:noreply, state} | ||
end | ||
|
||
defp severity(:error), do: GenLSP.Enumerations.DiagnosticSeverity.error() | ||
defp severity(:warning), do: GenLSP.Enumerations.DiagnosticSeverity.warning() | ||
defp severity(:info), do: GenLSP.Enumerations.DiagnosticSeverity.information() | ||
defp severity(:hint), do: GenLSP.Enumerations.DiagnosticSeverity.hint() | ||
|
||
defp range({start_line, start_col, end_line, end_col}) do | ||
%GenLSP.Structures.Range{ | ||
start: %GenLSP.Structures.Position{ | ||
line: start_line - 1, | ||
character: start_col | ||
}, | ||
end: %GenLSP.Structures.Position{ | ||
line: end_line - 1, | ||
character: end_col | ||
} | ||
} | ||
end | ||
|
||
defp range({line, col}) do | ||
%GenLSP.Structures.Range{ | ||
start: %GenLSP.Structures.Position{ | ||
line: line - 1, | ||
character: col | ||
}, | ||
end: %GenLSP.Structures.Position{ | ||
line: line - 1, | ||
character: 999 | ||
} | ||
} | ||
end | ||
|
||
defp range(line) do | ||
%GenLSP.Structures.Range{ | ||
start: %GenLSP.Structures.Position{ | ||
line: line - 1, | ||
character: 0 | ||
}, | ||
end: %GenLSP.Structures.Position{ | ||
line: line - 1, | ||
character: 999 | ||
} | ||
} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.