From 0047288487adc3283939a5b05bf7b0562075c649 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Fri, 12 May 2023 22:23:52 +0200 Subject: [PATCH 1/9] fix map stable sort issue on OTP 26 --- apps/elixir_ls_debugger/lib/debugger/variables.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/elixir_ls_debugger/lib/debugger/variables.ex b/apps/elixir_ls_debugger/lib/debugger/variables.ex index f86a4309a..8aeec8931 100644 --- a/apps/elixir_ls_debugger/lib/debugger/variables.ex +++ b/apps/elixir_ls_debugger/lib/debugger/variables.ex @@ -66,6 +66,7 @@ defmodule ElixirLS.Debugger.Variables do children = var |> Map.to_list() + |> Enum.sort() |> Enum.slice(start || 0, count || map_size(var)) for {key, value} <- children do From 7696c5c4458e731c06d8803a0e7042fc157bcccb Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Fri, 12 May 2023 22:24:31 +0200 Subject: [PATCH 2/9] make dialyzer work with OTP 26 --- .../lib/language_server/dialyzer/analyzer.ex | 29 ++++++++++++++++++- .../lib/language_server/dialyzer/manifest.ex | 18 +++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/apps/language_server/lib/language_server/dialyzer/analyzer.ex b/apps/language_server/lib/language_server/dialyzer/analyzer.ex index 0d8e5dc79..ecee353f4 100644 --- a/apps/language_server/lib/language_server/dialyzer/analyzer.ex +++ b/apps/language_server/lib/language_server/dialyzer/analyzer.ex @@ -96,6 +96,26 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Analyzer do solvers: :undefined ) + Record.defrecordp( + :analysis_26, + :analysis, + analysis_pid: :undefined, + type: :succ_typings, + defines: [], + doc_plt: :undefined, + files: [], + include_dirs: [], + start_from: :byte_code, + plt: :undefined, + use_contracts: true, + behaviours_chk: false, + timing: false, + timing_server: :none, + callgraph_file: [], + mod_deps_file: [], + solvers: :undefined + ) + def analyze(active_plt, []) do {active_plt, %{}, []} end @@ -110,12 +130,19 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Analyzer do solvers: [] ) - _ -> + 25 -> analysis_25( plt: active_plt, files: files, solvers: [] ) + + _ -> + analysis_26( + plt: active_plt, + files: files, + solvers: [] + ) end parent = self() diff --git a/apps/language_server/lib/language_server/dialyzer/manifest.ex b/apps/language_server/lib/language_server/dialyzer/manifest.ex index d86041c47..7d1677d34 100644 --- a/apps/language_server/lib/language_server/dialyzer/manifest.ex +++ b/apps/language_server/lib/language_server/dialyzer/manifest.ex @@ -106,14 +106,15 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do exported_types_list } = File.read!(manifest_path) |> :erlang.binary_to_term() - # FIXME: matching against opaque type + active_plt = :dialyzer_plt.new() + plt( info: info, types: types, contracts: contracts, callbacks: callbacks, exported_types: exported_types - ) = active_plt = apply(:dialyzer_plt, :new, []) + ) = active_plt for item <- info_list, do: :ets.insert(info, item) for item <- types_list, do: :ets.insert(types, item) @@ -127,7 +128,11 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do end def load_elixir_plt() do - apply(:dialyzer_plt, :from_file, [to_charlist(elixir_plt_path())]) + if String.to_integer(System.otp_release()) < 26 do + :dialyzer_plt.from_file(to_charlist(elixir_plt_path())) + else + :dialyzer_cplt.from_file(to_charlist(elixir_plt_path())) + end rescue _ -> build_elixir_plt() catch @@ -175,7 +180,12 @@ defmodule ElixirLS.LanguageServer.Dialyzer.Manifest do ) JsonRpc.show_message(:info, "Saved Elixir PLT to #{elixir_plt_path()}") - :dialyzer_plt.from_file(to_charlist(elixir_plt_path())) + + if String.to_integer(System.otp_release()) < 26 do + :dialyzer_plt.from_file(to_charlist(elixir_plt_path())) + else + :dialyzer_cplt.from_file(to_charlist(elixir_plt_path())) + end end defp otp_vsn() do From 9ef993eb37ddca0d6ad4232d5508fdfd3ae124fc Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 13 May 2023 14:13:42 +0200 Subject: [PATCH 3/9] trap exits in output device make sure that io_request is handled before exiting --- apps/elixir_ls_utils/lib/output_device.ex | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/elixir_ls_utils/lib/output_device.ex b/apps/elixir_ls_utils/lib/output_device.ex index 2594bdddd..dfb34e1e0 100644 --- a/apps/elixir_ls_utils/lib/output_device.ex +++ b/apps/elixir_ls_utils/lib/output_device.ex @@ -9,7 +9,11 @@ defmodule ElixirLS.Utils.OutputDevice do ## Client API def start_link(device, output_fn) do - Task.start_link(fn -> loop({device, output_fn}) end) + Task.start_link(fn -> + # Trap exit to make sure the process completes :io_request handling before exiting + Process.flag(:trap_exit, true) + loop({device, output_fn}) + end) end def child_spec(arguments) do @@ -28,6 +32,8 @@ defmodule ElixirLS.Utils.OutputDevice do defp loop(state) do receive do + {:EXIT, _from, reason} -> + exit(reason) {:io_request, from, reply_as, request} -> result = io_request(request, state, reply_as) send(from, {:io_reply, reply_as, result}) @@ -82,6 +88,8 @@ defmodule ElixirLS.Utils.OutputDevice do end defp io_request({:setopts, new_opts}, _state, _reply_as) do + # we do not support changing opts + # only validate that the passed ones match defaults validate_otps(new_opts, {:ok, 0}) end From e9f45b6d85295a59e35b0d156fab777cf07c947c Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 13 May 2023 14:51:35 +0200 Subject: [PATCH 4/9] make sure that we log to stderr if user device dies --- apps/elixir_ls_utils/lib/output_device.ex | 2 - apps/elixir_ls_utils/lib/packet_stream.ex | 36 +++++++++++++- apps/elixir_ls_utils/lib/wire_protocol.ex | 60 +++++++++++++++++++---- 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/apps/elixir_ls_utils/lib/output_device.ex b/apps/elixir_ls_utils/lib/output_device.ex index dfb34e1e0..0b2df2439 100644 --- a/apps/elixir_ls_utils/lib/output_device.ex +++ b/apps/elixir_ls_utils/lib/output_device.ex @@ -26,8 +26,6 @@ defmodule ElixirLS.Utils.OutputDevice do } end - def get_opts, do: @opts - ## Implementation defp loop(state) do diff --git a/apps/elixir_ls_utils/lib/packet_stream.ex b/apps/elixir_ls_utils/lib/packet_stream.ex index 1235321fb..54d5d4f2f 100644 --- a/apps/elixir_ls_utils/lib/packet_stream.ex +++ b/apps/elixir_ls_utils/lib/packet_stream.ex @@ -3,7 +3,16 @@ defmodule ElixirLS.Utils.PacketStream do Reads from an IO device and provides a stream of incoming packets """ - def stream(pid \\ Process.group_leader()) do + def stream(pid \\ Process.group_leader(), halt_on_error? \\ false) do + stream_pid = self() + Task.start_link(fn -> + ref = Process.monitor(pid) + receive do + {:DOWN, ^ref, :process, _pid, reason} -> + send(stream_pid, {:exit_reason, reason}) + end + end) + if is_pid(pid) do :ok = :io.setopts(pid, binary: true, encoding: :latin1) end @@ -31,7 +40,30 @@ defmodule ElixirLS.Utils.PacketStream do :ok {:error, reason} -> - raise "Unable to read from device: #{inspect(reason)}" + "Unable to read from input device: #{inspect(reason)}" + + error_message = unless Process.alive?(pid) do + receive do + {:exit_reason, exit_reason} -> + "Input device terminated: #{inspect(exit_reason)}" + after + 500 -> "Input device terminated" + end + else + "Unable to read from device: #{inspect(reason)}" + end + + if halt_on_error? do + if ElixirLS.Utils.WireProtocol.io_intercepted? do + ElixirLS.Utils.WireProtocol.undo_intercept_output + end + + IO.puts(:stderr, error_message) + + System.halt(1) + else + raise error_message + end end ) end diff --git a/apps/elixir_ls_utils/lib/wire_protocol.ex b/apps/elixir_ls_utils/lib/wire_protocol.ex index 8e0450319..97b6895de 100644 --- a/apps/elixir_ls_utils/lib/wire_protocol.ex +++ b/apps/elixir_ls_utils/lib/wire_protocol.ex @@ -23,33 +23,75 @@ defmodule ElixirLS.Utils.WireProtocol do end def io_intercepted? do - !!Process.whereis(:raw_user) + !!Process.whereis(:raw_standard_error) end def intercept_output(print_fn, print_err_fn) do raw_user = Process.whereis(:user) raw_standard_error = Process.whereis(:standard_error) - :ok = :io.setopts(raw_user, OutputDevice.get_opts()) + :ok = :io.setopts(raw_user, binary: true, encoding: :latin1) - {:ok, user} = OutputDevice.start_link(raw_user, print_fn) - {:ok, standard_error} = OutputDevice.start_link(raw_user, print_err_fn) + {:ok, intercepted_user} = OutputDevice.start_link(raw_user, print_fn) + {:ok, intercepted_standard_error} = OutputDevice.start_link(raw_user, print_err_fn) Process.unregister(:user) Process.register(raw_user, :raw_user) - Process.register(user, :user) + Process.register(intercepted_user, :user) Process.unregister(:standard_error) Process.register(raw_standard_error, :raw_standard_error) - Process.register(standard_error, :standard_error) + Process.register(intercepted_standard_error, :standard_error) - for process <- :erlang.processes(), process not in [raw_user, raw_standard_error] do - Process.group_leader(process, user) + for process <- :erlang.processes(), process not in [raw_user, raw_standard_error, intercepted_user, intercepted_standard_error] do + Process.group_leader(process, intercepted_user) end end + def undo_intercept_output() do + intercepted_user = Process.whereis(:user) + intercepted_standard_error = Process.whereis(:standard_error) + + Process.unregister(:user) + raw_user = try do + raw_user = Process.whereis(:raw_user) + Process.unregister(:raw_user) + Process.register(raw_user, :user) + raw_user + rescue + ArgumentError -> nil + end + + Process.unregister(:standard_error) + raw_standard_error = try do + raw_standard_error = Process.whereis(:raw_standard_error) + Process.unregister(:raw_standard_error) + Process.register(raw_standard_error, :standard_error) + raw_user + rescue + ArgumentError -> nil + end + + if raw_user do + for process <- :erlang.processes(), process not in [raw_user, raw_standard_error, intercepted_user, intercepted_standard_error] do + Process.group_leader(process, raw_user) + end + else + init = :erlang.processes() |> hd + for process <- :erlang.processes(), process not in [raw_standard_error, intercepted_user, intercepted_standard_error] do + Process.group_leader(process, init) + end + end + + Process.unlink(intercepted_user) + Process.unlink(intercepted_standard_error) + + Process.exit(intercepted_user, :kill) + Process.exit(intercepted_standard_error, :kill) + end + def stream_packets(receive_packets_fn) do - PacketStream.stream(Process.whereis(:raw_user)) + PacketStream.stream(Process.whereis(:raw_user), true) |> Stream.each(fn packet -> receive_packets_fn.(packet) end) |> Stream.run() end From 5ed4c7acb9768fd0099af2e28b1f3a2f1009aa4c Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 13 May 2023 14:55:02 +0200 Subject: [PATCH 5/9] do not call setopt on OTP 26 somehow this results in setopt request being sent to OutputDevice with not supported options causing it to crash --- apps/elixir_ls_utils/lib/packet_stream.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/elixir_ls_utils/lib/packet_stream.ex b/apps/elixir_ls_utils/lib/packet_stream.ex index 54d5d4f2f..10b9f0ecf 100644 --- a/apps/elixir_ls_utils/lib/packet_stream.ex +++ b/apps/elixir_ls_utils/lib/packet_stream.ex @@ -3,7 +3,7 @@ defmodule ElixirLS.Utils.PacketStream do Reads from an IO device and provides a stream of incoming packets """ - def stream(pid \\ Process.group_leader(), halt_on_error? \\ false) do + def stream(pid, halt_on_error? \\ false) when is_pid(pid) do stream_pid = self() Task.start_link(fn -> ref = Process.monitor(pid) @@ -13,10 +13,6 @@ defmodule ElixirLS.Utils.PacketStream do end end) - if is_pid(pid) do - :ok = :io.setopts(pid, binary: true, encoding: :latin1) - end - Stream.resource( fn -> :ok end, fn _acc -> From 1eb960600d510b18bd217ddd488ae0985b001306 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 13 May 2023 14:55:50 +0200 Subject: [PATCH 6/9] run formatter --- apps/elixir_ls_utils/lib/output_device.ex | 1 + apps/elixir_ls_utils/lib/packet_stream.ex | 27 ++++++----- apps/elixir_ls_utils/lib/wire_protocol.ex | 58 +++++++++++++++-------- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/apps/elixir_ls_utils/lib/output_device.ex b/apps/elixir_ls_utils/lib/output_device.ex index 0b2df2439..344319b7b 100644 --- a/apps/elixir_ls_utils/lib/output_device.ex +++ b/apps/elixir_ls_utils/lib/output_device.ex @@ -32,6 +32,7 @@ defmodule ElixirLS.Utils.OutputDevice do receive do {:EXIT, _from, reason} -> exit(reason) + {:io_request, from, reply_as, request} -> result = io_request(request, state, reply_as) send(from, {:io_reply, reply_as, result}) diff --git a/apps/elixir_ls_utils/lib/packet_stream.ex b/apps/elixir_ls_utils/lib/packet_stream.ex index 10b9f0ecf..4a9c1accd 100644 --- a/apps/elixir_ls_utils/lib/packet_stream.ex +++ b/apps/elixir_ls_utils/lib/packet_stream.ex @@ -5,8 +5,10 @@ defmodule ElixirLS.Utils.PacketStream do def stream(pid, halt_on_error? \\ false) when is_pid(pid) do stream_pid = self() + Task.start_link(fn -> ref = Process.monitor(pid) + receive do {:DOWN, ^ref, :process, _pid, reason} -> send(stream_pid, {:exit_reason, reason}) @@ -38,24 +40,25 @@ defmodule ElixirLS.Utils.PacketStream do {:error, reason} -> "Unable to read from input device: #{inspect(reason)}" - error_message = unless Process.alive?(pid) do - receive do - {:exit_reason, exit_reason} -> - "Input device terminated: #{inspect(exit_reason)}" - after - 500 -> "Input device terminated" + error_message = + unless Process.alive?(pid) do + receive do + {:exit_reason, exit_reason} -> + "Input device terminated: #{inspect(exit_reason)}" + after + 500 -> "Input device terminated" + end + else + "Unable to read from device: #{inspect(reason)}" end - else - "Unable to read from device: #{inspect(reason)}" - end if halt_on_error? do - if ElixirLS.Utils.WireProtocol.io_intercepted? do - ElixirLS.Utils.WireProtocol.undo_intercept_output + if ElixirLS.Utils.WireProtocol.io_intercepted?() do + ElixirLS.Utils.WireProtocol.undo_intercept_output() end IO.puts(:stderr, error_message) - + System.halt(1) else raise error_message diff --git a/apps/elixir_ls_utils/lib/wire_protocol.ex b/apps/elixir_ls_utils/lib/wire_protocol.ex index 97b6895de..502d1e7b8 100644 --- a/apps/elixir_ls_utils/lib/wire_protocol.ex +++ b/apps/elixir_ls_utils/lib/wire_protocol.ex @@ -43,7 +43,13 @@ defmodule ElixirLS.Utils.WireProtocol do Process.register(raw_standard_error, :raw_standard_error) Process.register(intercepted_standard_error, :standard_error) - for process <- :erlang.processes(), process not in [raw_user, raw_standard_error, intercepted_user, intercepted_standard_error] do + for process <- :erlang.processes(), + process not in [ + raw_user, + raw_standard_error, + intercepted_user, + intercepted_standard_error + ] do Process.group_leader(process, intercepted_user) end end @@ -53,32 +59,44 @@ defmodule ElixirLS.Utils.WireProtocol do intercepted_standard_error = Process.whereis(:standard_error) Process.unregister(:user) - raw_user = try do - raw_user = Process.whereis(:raw_user) - Process.unregister(:raw_user) - Process.register(raw_user, :user) - raw_user - rescue - ArgumentError -> nil - end - + + raw_user = + try do + raw_user = Process.whereis(:raw_user) + Process.unregister(:raw_user) + Process.register(raw_user, :user) + raw_user + rescue + ArgumentError -> nil + end + Process.unregister(:standard_error) - raw_standard_error = try do - raw_standard_error = Process.whereis(:raw_standard_error) - Process.unregister(:raw_standard_error) - Process.register(raw_standard_error, :standard_error) - raw_user - rescue - ArgumentError -> nil - end + + raw_standard_error = + try do + raw_standard_error = Process.whereis(:raw_standard_error) + Process.unregister(:raw_standard_error) + Process.register(raw_standard_error, :standard_error) + raw_user + rescue + ArgumentError -> nil + end if raw_user do - for process <- :erlang.processes(), process not in [raw_user, raw_standard_error, intercepted_user, intercepted_standard_error] do + for process <- :erlang.processes(), + process not in [ + raw_user, + raw_standard_error, + intercepted_user, + intercepted_standard_error + ] do Process.group_leader(process, raw_user) end else init = :erlang.processes() |> hd - for process <- :erlang.processes(), process not in [raw_standard_error, intercepted_user, intercepted_standard_error] do + + for process <- :erlang.processes(), + process not in [raw_standard_error, intercepted_user, intercepted_standard_error] do Process.group_leader(process, init) end end From ce88f3cac188991184a1976928cc257a7090b23b Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sat, 13 May 2023 23:30:20 +0200 Subject: [PATCH 7/9] failing case --- apps/elixir_ls_utils/fail.sh | 3 +++ .../test/fixtures/protocol_messages/output | 3 +++ apps/elixir_ls_utils/test/packet_stream_test.exs | 12 ++++++++++++ 3 files changed, 18 insertions(+) create mode 100755 apps/elixir_ls_utils/fail.sh create mode 100644 apps/elixir_ls_utils/test/fixtures/protocol_messages/output diff --git a/apps/elixir_ls_utils/fail.sh b/apps/elixir_ls_utils/fail.sh new file mode 100755 index 000000000..bcd8dbb7d --- /dev/null +++ b/apps/elixir_ls_utils/fail.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +cat test/fixtures/protocol_messages/output | elixir --erl "-noshell" -e ":io.setopts(:standard_io, encoding: :latin1, binary: true); IO.binread(:standard_io, :line) |> IO.inspect; IO.binread(:standard_io, :line) |> IO.inspect; IO.binread(:standard_io, 5832) |> IO.inspect" diff --git a/apps/elixir_ls_utils/test/fixtures/protocol_messages/output b/apps/elixir_ls_utils/test/fixtures/protocol_messages/output new file mode 100644 index 000000000..e113339b1 --- /dev/null +++ b/apps/elixir_ls_utils/test/fixtures/protocol_messages/output @@ -0,0 +1,3 @@ +Content-Length: 5832 + +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":69121,"clientInfo":{"name":"Visual Studio Code","version":"1.78.2"},"locale":"en","rootPath":"/Users/lukaszsamson/vscode-elixir-ls/duπa","rootUri":"file:///Users/lukaszsamson/vscode-elixir-ls/du%CF%80a","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":true}},"configuration":true,"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]},"resolveSupport":{"properties":["location.range"]}},"codeLens":{"refreshSupport":true},"executeCommand":{"dynamicRegistration":true},"didChangeConfiguration":{"dynamicRegistration":true},"semanticTokens":{"refreshSupport":true},"fileOperations":{"dynamicRegistration":true,"didCreate":true,"didRename":true,"didDelete":true,"willCreate":true,"willRename":true,"willDelete":true},"inlineValue":{"refreshSupport":true},"inlayHint":{"refreshSupport":true},"diagnostics":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"insertTextModeSupport":{"valueSet":[1,2]},"labelDetailsSupport":true},"insertTextMode":2,"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},"completionList":{"itemDefaults":["commitCharacters","editRange","insertTextFormat","insertTextMode"]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"honorsChangeAnnotations":false},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":1,"honorsChangeAnnotations":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true,"foldingRangeKind":{"valueSet":["comment","imports","region"]},"foldingRange":{"collapsedText":false}},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}},"multilineTokenSupport":false,"overlappingTokenSupport":false,"serverCancelSupport":true,"augmentsSyntaxTokens":true},"linkedEditingRange":{"dynamicRegistration":true},"typeHierarchy":{"dynamicRegistration":true},"inlineValue":{"dynamicRegistration":true},"inlayHint":{"dynamicRegistration":true,"resolveSupport":{"properties":["tooltip","textEdits","label.tooltip","label.location","label.command"]}},"diagnostic":{"dynamicRegistration":true,"relatedDocumentSupport":false}},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"showDocument":{"support":true},"workDoneProgress":true},"general":{"staleRequestSupport":{"cancel":true,"retryOnContentModified":["textDocument/semanticTokens/full","textDocument/semanticTokens/range","textDocument/semanticTokens/full/delta"]},"regularExpressions":{"engine":"ECMAScript","version":"ES2020"},"markdown":{"parser":"marked","version":"1.1.0"},"positionEncodings":["utf-16"]},"notebookDocument":{"synchronization":{"dynamicRegistration":true,"executionSummarySupport":true}}},"trace":"verbose","workspaceFolders":[{"uri":"file:///Users/lukaszsamson/vscode-elixir-ls/du%CF%80a","name":"duπa"}]}} \ No newline at end of file diff --git a/apps/elixir_ls_utils/test/packet_stream_test.exs b/apps/elixir_ls_utils/test/packet_stream_test.exs index 27d24ff07..7b1d10fde 100644 --- a/apps/elixir_ls_utils/test/packet_stream_test.exs +++ b/apps/elixir_ls_utils/test/packet_stream_test.exs @@ -80,6 +80,18 @@ defmodule ElixirLS.Utils.PacketStreamTest do File.close(pid) end + test "valid utf failing on otp 26" do + {:ok, pid} = File.open("test/fixtures/protocol_messages/output", [:read, :binary]) + + [message] = + PacketStream.stream(pid) + |> Enum.to_list() + + assert message == %{"some" => "value"} + + File.close(pid) + end + test "valid utf" do {:ok, pid} = File.open("test/fixtures/protocol_messages/valid_message_utf", [:read, :binary]) From fe8f8b8687554b5676d92e7d76c31f7b9141cbb6 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 25 Jun 2023 20:55:46 +0200 Subject: [PATCH 8/9] start erl with latin1 stdin --- apps/elixir_ls_utils/priv/debugger.bat | 2 +- apps/elixir_ls_utils/priv/language_server.bat | 2 +- apps/elixir_ls_utils/priv/launch.sh | 2 +- scripts/debugger.bat | 2 +- scripts/language_server.bat | 2 +- scripts/launch.sh | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/elixir_ls_utils/priv/debugger.bat b/apps/elixir_ls_utils/priv/debugger.bat index 04c00bf8a..57089cb3d 100755 --- a/apps/elixir_ls_utils/priv/debugger.bat +++ b/apps/elixir_ls_utils/priv/debugger.bat @@ -6,4 +6,4 @@ IF EXIST "%APPDATA%\elixir_ls\setup.bat" ( ) SET ERL_LIBS=%~dp0;%ERL_LIBS% -elixir %ELS_ELIXIR_OPTS% --erl "+sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" -e "ElixirLS.Debugger.CLI.main()" +elixir %ELS_ELIXIR_OPTS% --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" -e "ElixirLS.Debugger.CLI.main()" diff --git a/apps/elixir_ls_utils/priv/language_server.bat b/apps/elixir_ls_utils/priv/language_server.bat index c14e3cf09..0de4b6349 100755 --- a/apps/elixir_ls_utils/priv/language_server.bat +++ b/apps/elixir_ls_utils/priv/language_server.bat @@ -6,4 +6,4 @@ IF EXIST "%APPDATA%\elixir_ls\setup.bat" ( ) SET ERL_LIBS=%~dp0;%ERL_LIBS% -elixir %ELS_ELIXIR_OPTS% --erl "+sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" -e "ElixirLS.LanguageServer.CLI.main()" +elixir %ELS_ELIXIR_OPTS% --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" -e "ElixirLS.LanguageServer.CLI.main()" diff --git a/apps/elixir_ls_utils/priv/launch.sh b/apps/elixir_ls_utils/priv/launch.sh index 2364526d6..c434c5ccd 100755 --- a/apps/elixir_ls_utils/priv/launch.sh +++ b/apps/elixir_ls_utils/priv/launch.sh @@ -79,4 +79,4 @@ fi export ERL_LIBS="$SCRIPTPATH:$ERL_LIBS" -exec elixir $ELS_ELIXIR_OPTS --erl "+sbwt none +sbwtdcpu none +sbwtdio none $ELS_ERL_OPTS" -e "$ELS_SCRIPT" +exec elixir $ELS_ELIXIR_OPTS --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none $ELS_ERL_OPTS" -e "$ELS_SCRIPT" diff --git a/scripts/debugger.bat b/scripts/debugger.bat index 4c87e1905..0fecba623 100755 --- a/scripts/debugger.bat +++ b/scripts/debugger.bat @@ -13,4 +13,4 @@ SET MIX_ENV=prod @REM elixir is a batch script and needs to be called ECHO "" | CALL elixir "%~dp0quiet_install.exs" > nul IF %ERRORLEVEL% NEQ 0 EXIT 1 -elixir %ELS_ELIXIR_OPTS% --erl "+sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" "%~dp0launch.exs" +elixir %ELS_ELIXIR_OPTS% --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" "%~dp0launch.exs" diff --git a/scripts/language_server.bat b/scripts/language_server.bat index e035ff412..6bfae24d9 100755 --- a/scripts/language_server.bat +++ b/scripts/language_server.bat @@ -13,4 +13,4 @@ SET MIX_ENV=prod @REM elixir is a batch script and needs to be called ECHO "" | CALL elixir "%~dp0quiet_install.exs" >nul IF %ERRORLEVEL% NEQ 0 EXIT 1 -elixir %ELS_ELIXIR_OPTS% --erl "+sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" "%~dp0launch.exs" +elixir %ELS_ELIXIR_OPTS% --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none %ELS_ERL_OPTS%" "%~dp0launch.exs" diff --git a/scripts/launch.sh b/scripts/launch.sh index aa0acd5f4..f31d4bebd 100755 --- a/scripts/launch.sh +++ b/scripts/launch.sh @@ -82,4 +82,4 @@ export MIX_ENV=prod # we need to make sure it doesn't interfere with LSP/DAP echo "" | elixir "$SCRIPTPATH/quiet_install.exs" >/dev/null || exit 1 -exec elixir $ELS_ELIXIR_OPTS --erl "+sbwt none +sbwtdcpu none +sbwtdio none $ELS_ERL_OPTS" "$SCRIPTPATH/launch.exs" +exec elixir $ELS_ELIXIR_OPTS --erl "-kernel standard_io_encoding latin1 +sbwt none +sbwtdcpu none +sbwtdio none $ELS_ERL_OPTS" "$SCRIPTPATH/launch.exs" From b6cb0670ade00f22de69cd634b8fe04721623ad0 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Sun, 25 Jun 2023 22:35:26 +0200 Subject: [PATCH 9/9] cleanup --- apps/elixir_ls_utils/fail.sh | 3 --- .../test/fixtures/protocol_messages/output | 3 --- apps/elixir_ls_utils/test/packet_stream_test.exs | 12 ------------ 3 files changed, 18 deletions(-) delete mode 100755 apps/elixir_ls_utils/fail.sh delete mode 100644 apps/elixir_ls_utils/test/fixtures/protocol_messages/output diff --git a/apps/elixir_ls_utils/fail.sh b/apps/elixir_ls_utils/fail.sh deleted file mode 100755 index bcd8dbb7d..000000000 --- a/apps/elixir_ls_utils/fail.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -cat test/fixtures/protocol_messages/output | elixir --erl "-noshell" -e ":io.setopts(:standard_io, encoding: :latin1, binary: true); IO.binread(:standard_io, :line) |> IO.inspect; IO.binread(:standard_io, :line) |> IO.inspect; IO.binread(:standard_io, 5832) |> IO.inspect" diff --git a/apps/elixir_ls_utils/test/fixtures/protocol_messages/output b/apps/elixir_ls_utils/test/fixtures/protocol_messages/output deleted file mode 100644 index e113339b1..000000000 --- a/apps/elixir_ls_utils/test/fixtures/protocol_messages/output +++ /dev/null @@ -1,3 +0,0 @@ -Content-Length: 5832 - -{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":69121,"clientInfo":{"name":"Visual Studio Code","version":"1.78.2"},"locale":"en","rootPath":"/Users/lukaszsamson/vscode-elixir-ls/duπa","rootUri":"file:///Users/lukaszsamson/vscode-elixir-ls/du%CF%80a","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":true}},"configuration":true,"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]},"resolveSupport":{"properties":["location.range"]}},"codeLens":{"refreshSupport":true},"executeCommand":{"dynamicRegistration":true},"didChangeConfiguration":{"dynamicRegistration":true},"semanticTokens":{"refreshSupport":true},"fileOperations":{"dynamicRegistration":true,"didCreate":true,"didRename":true,"didDelete":true,"willCreate":true,"willRename":true,"willDelete":true},"inlineValue":{"refreshSupport":true},"inlayHint":{"refreshSupport":true},"diagnostics":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"insertTextModeSupport":{"valueSet":[1,2]},"labelDetailsSupport":true},"insertTextMode":2,"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},"completionList":{"itemDefaults":["commitCharacters","editRange","insertTextFormat","insertTextMode"]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"honorsChangeAnnotations":false},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":1,"honorsChangeAnnotations":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true,"foldingRangeKind":{"valueSet":["comment","imports","region"]},"foldingRange":{"collapsedText":false}},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}},"multilineTokenSupport":false,"overlappingTokenSupport":false,"serverCancelSupport":true,"augmentsSyntaxTokens":true},"linkedEditingRange":{"dynamicRegistration":true},"typeHierarchy":{"dynamicRegistration":true},"inlineValue":{"dynamicRegistration":true},"inlayHint":{"dynamicRegistration":true,"resolveSupport":{"properties":["tooltip","textEdits","label.tooltip","label.location","label.command"]}},"diagnostic":{"dynamicRegistration":true,"relatedDocumentSupport":false}},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"showDocument":{"support":true},"workDoneProgress":true},"general":{"staleRequestSupport":{"cancel":true,"retryOnContentModified":["textDocument/semanticTokens/full","textDocument/semanticTokens/range","textDocument/semanticTokens/full/delta"]},"regularExpressions":{"engine":"ECMAScript","version":"ES2020"},"markdown":{"parser":"marked","version":"1.1.0"},"positionEncodings":["utf-16"]},"notebookDocument":{"synchronization":{"dynamicRegistration":true,"executionSummarySupport":true}}},"trace":"verbose","workspaceFolders":[{"uri":"file:///Users/lukaszsamson/vscode-elixir-ls/du%CF%80a","name":"duπa"}]}} \ No newline at end of file diff --git a/apps/elixir_ls_utils/test/packet_stream_test.exs b/apps/elixir_ls_utils/test/packet_stream_test.exs index 7b1d10fde..27d24ff07 100644 --- a/apps/elixir_ls_utils/test/packet_stream_test.exs +++ b/apps/elixir_ls_utils/test/packet_stream_test.exs @@ -80,18 +80,6 @@ defmodule ElixirLS.Utils.PacketStreamTest do File.close(pid) end - test "valid utf failing on otp 26" do - {:ok, pid} = File.open("test/fixtures/protocol_messages/output", [:read, :binary]) - - [message] = - PacketStream.stream(pid) - |> Enum.to_list() - - assert message == %{"some" => "value"} - - File.close(pid) - end - test "valid utf" do {:ok, pid} = File.open("test/fixtures/protocol_messages/valid_message_utf", [:read, :binary])