Skip to content

Commit 1102cff

Browse files
authored
Fix suggest contracts windows regression (#531)
* Dialyzer.suggest_contracts expects absnames on windows we need to convert from "c:\\some\\other.ex" to "c:/some/other.ex" Fixes #528 * fix test on windows * add suggest contracts tests * apply code review suggestion * revert non windows compatible change
1 parent cfef8a7 commit 1102cff

File tree

5 files changed

+163
-8
lines changed

5 files changed

+163
-8
lines changed

apps/language_server/lib/language_server/providers/formatting.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ defmodule ElixirLS.LanguageServer.Providers.Formatting do
3737
# If in an umbrella project, the cwd might be set to a sub-app if it's being compiled. This is
3838
# fine if the file we're trying to format is in that app. Otherwise, we return an error.
3939
defp can_format?(file_uri = "file:" <> _, project_dir) do
40-
file_path = file_uri |> SourceFile.path_from_uri() |> Path.absname()
40+
file_path = file_uri |> SourceFile.abs_path_from_uri()
4141

4242
String.starts_with?(file_path, Path.absname(project_dir)) or
4343
String.starts_with?(file_path, File.cwd!())
@@ -46,7 +46,7 @@ defmodule ElixirLS.LanguageServer.Providers.Formatting do
4646
defp can_format?(_uri, _project_dir), do: false
4747

4848
def should_format?(file_uri, project_dir, inputs) when is_list(inputs) do
49-
file_path = file_uri |> SourceFile.path_from_uri() |> Path.absname()
49+
file_path = file_uri |> SourceFile.abs_path_from_uri()
5050

5151
inputs
5252
|> Stream.flat_map(fn glob ->

apps/language_server/lib/language_server/server.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ defmodule ElixirLS.LanguageServer.Server do
7575
## Client API
7676

7777
def start_link(name \\ nil) do
78-
GenServer.start_link(__MODULE__, :ok, name: name)
78+
GenServer.start_link(__MODULE__, :ok, name: name || __MODULE__)
7979
end
8080

8181
def receive_packet(server \\ __MODULE__, packet) do
@@ -122,7 +122,11 @@ defmodule ElixirLS.LanguageServer.Server do
122122
def handle_call({:suggest_contracts, uri = "file:" <> _}, from, state) do
123123
case state do
124124
%{analysis_ready?: true, source_files: %{^uri => %{dirty?: false}}} ->
125-
{:reply, Dialyzer.suggest_contracts([SourceFile.path_from_uri(uri)]), state}
125+
abs_path =
126+
uri
127+
|> SourceFile.abs_path_from_uri()
128+
129+
{:reply, Dialyzer.suggest_contracts([abs_path]), state}
126130

127131
_ ->
128132
awaiting_contracts = reject_awaiting_contracts(state.awaiting_contracts, uri)
@@ -504,7 +508,7 @@ defmodule ElixirLS.LanguageServer.Server do
504508
state =
505509
case root_uri do
506510
"file://" <> _ ->
507-
root_path = SourceFile.path_from_uri(root_uri)
511+
root_path = SourceFile.abs_path_from_uri(root_uri)
508512
File.cd!(root_path)
509513
%{state | root_uri: root_uri}
510514

@@ -1040,7 +1044,7 @@ defmodule ElixirLS.LanguageServer.Server do
10401044

10411045
defp set_project_dir(%{project_dir: prev_project_dir, root_uri: root_uri} = state, project_dir)
10421046
when is_binary(root_uri) do
1043-
root_dir = root_uri |> SourceFile.path_from_uri() |> Path.absname()
1047+
root_dir = root_uri |> SourceFile.abs_path_from_uri()
10441048

10451049
project_dir =
10461050
if is_binary(project_dir) do

apps/language_server/lib/language_server/source_file.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ defmodule ElixirLS.LanguageServer.SourceFile do
157157
defp downcase(char) when char >= ?A and char <= ?Z, do: char + 32
158158
defp downcase(char), do: char
159159

160+
def abs_path_from_uri(uri) do
161+
uri |> path_from_uri |> Path.absname()
162+
end
163+
160164
def full_range(source_file) do
161165
lines = lines(source_file)
162166

apps/language_server/test/dialyzer_test.exs

Lines changed: 143 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule ElixirLS.LanguageServer.DialyzerTest do
22
# TODO: Test loading and saving manifest
33

4-
alias ElixirLS.LanguageServer.{Dialyzer, Server, Protocol, SourceFile}
4+
alias ElixirLS.LanguageServer.{Dialyzer, Server, Protocol, SourceFile, JsonRpc}
55
import ExUnit.CaptureLog
66
use ElixirLS.Utils.MixTest.Case, async: false
77
use Protocol
@@ -442,4 +442,146 @@ defmodule ElixirLS.LanguageServer.DialyzerTest do
442442
refute_receive notification("textDocument/publishDiagnostics", _), 3000
443443
end)
444444
end
445+
446+
@tag slow: true, fixture: true
447+
test "do not suggests contracts if not enabled", %{server: server} do
448+
in_fixture(__DIR__, "dialyzer", fn ->
449+
file_c = SourceFile.path_to_uri(Path.absname("lib/c.ex"))
450+
451+
capture_log(fn ->
452+
root_uri = SourceFile.path_to_uri(File.cwd!())
453+
Server.receive_packet(server, initialize_req(1, root_uri, %{}))
454+
455+
Server.receive_packet(
456+
server,
457+
did_change_configuration(%{
458+
"elixirLS" => %{"dialyzerEnabled" => true, "dialyzerFormat" => "dialyxir_long"}
459+
})
460+
)
461+
462+
message = assert_receive %{"method" => "textDocument/publishDiagnostics"}, 20000
463+
464+
assert publish_diagnostics_notif(_, _) = message
465+
466+
Server.receive_packet(
467+
server,
468+
did_open(file_c, "elixir", 2, File.read!(Path.absname("lib/c.ex")))
469+
)
470+
471+
Server.receive_packet(
472+
server,
473+
code_lens_req(3, file_c)
474+
)
475+
476+
resp = assert_receive(%{"id" => 3}, 5000)
477+
478+
assert response(3, []) == resp
479+
end)
480+
end)
481+
end
482+
483+
@tag slow: true, fixture: true
484+
test "suggests contracts if enabled and applies suggestion", %{server: server} do
485+
in_fixture(__DIR__, "dialyzer", fn ->
486+
file_c = SourceFile.path_to_uri(Path.absname("lib/c.ex"))
487+
488+
capture_log(fn ->
489+
root_uri = SourceFile.path_to_uri(File.cwd!())
490+
Server.receive_packet(server, initialize_req(1, root_uri, %{}))
491+
492+
Server.receive_packet(
493+
server,
494+
did_change_configuration(%{
495+
"elixirLS" => %{
496+
"dialyzerEnabled" => true,
497+
"dialyzerFormat" => "dialyxir_long",
498+
"suggestSpecs" => true
499+
}
500+
})
501+
)
502+
503+
message = assert_receive %{"method" => "textDocument/publishDiagnostics"}, 20000
504+
505+
assert publish_diagnostics_notif(_, _) = message
506+
507+
Server.receive_packet(
508+
server,
509+
did_open(file_c, "elixir", 2, File.read!(Path.absname("lib/c.ex")))
510+
)
511+
512+
Server.receive_packet(
513+
server,
514+
code_lens_req(3, file_c)
515+
)
516+
517+
resp = assert_receive(%{"id" => 3}, 5000)
518+
519+
assert response(3, [
520+
%{
521+
"command" => %{
522+
"arguments" =>
523+
args = [
524+
%{
525+
"arity" => 0,
526+
"fun" => "myfun",
527+
"line" => 2,
528+
"mod" => "Elixir.C",
529+
"spec" => "myfun :: 1",
530+
"uri" => ^file_c
531+
}
532+
],
533+
"command" => command = "spec:" <> _,
534+
"title" => "@spec myfun :: 1"
535+
},
536+
"range" => %{
537+
"end" => %{"character" => 0, "line" => 1},
538+
"start" => %{"character" => 0, "line" => 1}
539+
}
540+
}
541+
]) = resp
542+
543+
Server.receive_packet(
544+
server,
545+
execute_command_req(4, command, args)
546+
)
547+
548+
assert_receive(%{
549+
"id" => 1,
550+
"method" => "workspace/applyEdit",
551+
"params" => %{
552+
"edit" => %{
553+
"changes" => %{
554+
^file_c => [
555+
%{
556+
"newText" => " @spec myfun :: 1\n",
557+
"range" => %{
558+
"end" => %{"character" => 0, "line" => 1},
559+
"start" => %{"character" => 0, "line" => 1}
560+
}
561+
}
562+
]
563+
}
564+
},
565+
"label" => "Add @spec to Elixir.C.myfun/0"
566+
}
567+
})
568+
569+
# TODO something is broken in packet capture
570+
# using JsonRpc.receive_packet causes the packet to be delivered to LanguageServer
571+
# which crashes with no match error
572+
# JsonRpc.receive_packet(
573+
# server,
574+
# response(1, %{"applied" => true})
575+
# )
576+
# instead we fake a callback in JsonRpc server that forwards the response as needed
577+
JsonRpc.handle_call(
578+
{:packet, response(1, %{"applied" => true})},
579+
nil,
580+
:sys.get_state(JsonRpc)
581+
)
582+
583+
assert_receive(%{"id" => 4, "result" => nil}, 5000)
584+
end)
585+
end)
586+
end
445587
end

apps/language_server/test/server_test.exs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,9 +1189,14 @@ defmodule ElixirLS.LanguageServer.ServerTest do
11891189
in_fixture(__DIR__, "test_code_lens", fn ->
11901190
file_path = "test/fixture_test.exs"
11911191
file_uri = SourceFile.path_to_uri(file_path)
1192+
# this is not an abs path as returned by Path.absname
1193+
# on Windows it's c:\asdf instead of c:/asdf
11921194
file_absolute_path = SourceFile.path_from_uri(file_uri)
11931195
text = File.read!(file_path)
1194-
project_dir = SourceFile.path_from_uri(root_uri())
1196+
1197+
project_dir =
1198+
root_uri()
1199+
|> SourceFile.abs_path_from_uri()
11951200

11961201
initialize(server)
11971202

0 commit comments

Comments
 (0)