Skip to content

Commit 1754831

Browse files
committed
Return diagnostics from config files
1 parent 8e1a068 commit 1754831

File tree

2 files changed

+98
-26
lines changed

2 files changed

+98
-26
lines changed

apps/language_server/lib/language_server/build.ex

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ defmodule ElixirLS.LanguageServer.Build do
158158
Code.put_compiler_option(:no_warn_undefined, :all)
159159

160160
# We can get diagnostics if Mixfile fails to load
161-
{status, diagnostics} =
161+
{mixfile_status, mixfile_diagnostics} =
162162
case Kernel.ParallelCompiler.compile([mixfile]) do
163163
{:ok, _, warnings} ->
164164
{:ok, Enum.map(warnings, &Diagnostics.mixfile_diagnostic(&1, :warning))}
@@ -174,39 +174,65 @@ defmodule ElixirLS.LanguageServer.Build do
174174
# restore warnings
175175
Code.put_compiler_option(:no_warn_undefined, old_undefined)
176176

177-
if status == :ok do
177+
if mixfile_status == :ok do
178178
# The project may override our logger config, so we reset it after loading their config
179179
# store log config
180180
logger_config = Application.get_all_env(:logger)
181181

182-
try do
183-
Mix.Task.run("loadconfig")
184-
after
185-
# reset log config
186-
Application.put_all_env(logger: logger_config)
182+
{config_result, config_raw_diagnostics} =
183+
with_diagnostics([log: true], fn ->
184+
try do
185+
Mix.Task.run("loadconfig")
186+
:ok
187+
catch
188+
kind, err ->
189+
{payload, stacktrace} = Exception.blame(kind, err, __STACKTRACE__)
190+
{:error, kind, payload, stacktrace}
191+
after
192+
# reset log config
193+
Application.put_all_env(logger: logger_config)
194+
195+
if Version.match?(System.version(), ">= 1.15.0") do
196+
# remove all log handlers and restore our
197+
for handler_id <- :logger.get_handler_ids(),
198+
handler_id != Logger.Backends.JsonRpc do
199+
:ok = :logger.remove_handler(handler_id)
200+
end
201+
202+
if Logger.Backends.JsonRpc not in :logger.get_handler_ids() do
203+
:ok =
204+
:logger.add_handler(
205+
Logger.Backends.JsonRpc,
206+
Logger.Backends.JsonRpc,
207+
Logger.Backends.JsonRpc.handler_config()
208+
)
209+
end
210+
end
187211

188-
if Version.match?(System.version(), ">= 1.15.0") do
189-
# remove all log handlers and restore our
190-
for handler_id <- :logger.get_handler_ids(), handler_id != Logger.Backends.JsonRpc do
191-
:ok = :logger.remove_handler(handler_id)
212+
# make sure ANSI is disabled
213+
Application.put_env(:elixir, :ansi_enabled, false)
192214
end
215+
end)
193216

194-
if Logger.Backends.JsonRpc not in :logger.get_handler_ids() do
195-
:ok =
196-
:logger.add_handler(
197-
Logger.Backends.JsonRpc,
198-
Logger.Backends.JsonRpc,
199-
Logger.Backends.JsonRpc.handler_config()
200-
)
201-
end
202-
end
217+
config_diagnostics =
218+
config_raw_diagnostics
219+
|> Enum.map(&Diagnostics.code_diagnostic/1)
220+
221+
case config_result do
222+
{:error, kind, err, stacktrace} ->
223+
config_path = Mix.Project.config()[:config_path]
224+
225+
{:error,
226+
mixfile_diagnostics ++
227+
config_diagnostics ++
228+
[Diagnostics.error_to_diagnostic(kind, err, stacktrace, config_path)]}
203229

204-
# make sure ANSI is disabled
205-
Application.put_env(:elixir, :ansi_enabled, false)
230+
:ok ->
231+
{:ok, mixfile_diagnostics ++ config_diagnostics}
206232
end
233+
else
234+
{mixfile_status, mixfile_diagnostics}
207235
end
208-
209-
{status, diagnostics}
210236
else
211237
msg =
212238
"No mixfile found in project. " <>
@@ -450,4 +476,13 @@ defmodule ElixirLS.LanguageServer.Build do
450476
end
451477
end
452478
end
479+
480+
def with_diagnostics(opts \\ [], fun) do
481+
# Code.with_diagnostics is broken on elixir < 1.15.3
482+
if Version.match?(System.version(), ">= 1.15.3") do
483+
Code.with_diagnostics(opts, fun)
484+
else
485+
{fun.(), []}
486+
end
487+
end
453488
end

apps/language_server/lib/language_server/diagnostics.ex

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,44 @@ defmodule ElixirLS.LanguageServer.Diagnostics do
228228
}
229229
end
230230

231-
def exception_to_diagnostic(error) do
231+
def code_diagnostic(%{
232+
file: file,
233+
severity: severity,
234+
message: message,
235+
position: position
236+
}) do
237+
%Mix.Task.Compiler.Diagnostic{
238+
compiler_name: "ElixirLS",
239+
file: file,
240+
position: position,
241+
message: message,
242+
severity: severity
243+
}
244+
end
245+
246+
def error_to_diagnostic(kind, payload, stacktrace, path) do
247+
path = Path.absname(path)
248+
message = Exception.format(kind, payload, stacktrace)
249+
250+
line =
251+
stacktrace
252+
|> Enum.find_value(fn {_m, _f, _a, opts} ->
253+
if opts |> Keyword.get(:file) |> Path.absname() == path do
254+
opts |> Keyword.get(:line)
255+
end
256+
end)
257+
258+
%Mix.Task.Compiler.Diagnostic{
259+
compiler_name: "ElixirLS",
260+
file: Path.absname(path),
261+
position: line || 0,
262+
message: message,
263+
severity: :error,
264+
details: payload
265+
}
266+
end
267+
268+
def exception_to_diagnostic(error, path \\ MixfileHelpers.mix_exs()) do
232269
msg =
233270
case error do
234271
{:shutdown, 1} ->
@@ -240,7 +277,7 @@ defmodule ElixirLS.LanguageServer.Diagnostics do
240277

241278
%Mix.Task.Compiler.Diagnostic{
242279
compiler_name: "ElixirLS",
243-
file: Path.absname(MixfileHelpers.mix_exs()),
280+
file: Path.absname(path),
244281
# 0 means unknown
245282
position: 0,
246283
message: msg,

0 commit comments

Comments
 (0)