elixir-lsp / elixir-ls

A frontend-independent IDE "smartness" server for Elixir. Implements the "Language Server Protocol" standard and provides debugger support via the "Debug Adapter Protocol"
https://elixir-lsp.github.io/elixir-ls/
Apache License 2.0
1.48k stars 194 forks source link

Protocol.UndefinedError #790

Closed bigardone closed 1 year ago

bigardone commented 1 year ago

Environment

Current behavior

An error saying that Elixir LS has crashed rises every time I open an Elixir file.

[Error - 09:23:07.740] GenServer ElixirLS.LanguageServer.Experimental.SourceFile.Store terminating
** (Protocol.UndefinedError) protocol Enumerable not implemented for %ElixirLS.LanguageServer.Experimental.SourceFile.Document{lines: {{:line, "defmodule MyApp.MixProject do", "\n", 1, true}, {:line, "  use Mix.Project", "\n", 2, true}, {:line, "", "\n", 3, true}, {:line, "  def project do", "\n", 4, true}, {:line, "    [", "\n", 5, true}, {:line, "      app: :my_app,", "\n", 6, true}, {:line, "      version: \"0.1.0\",", "\n", 7, true}, {:line, "      elixir: \"~> 1.14\",", "\n", 8, true}, {:line, "      elixirc_paths: elixirc_paths(Mix.env()),", "\n", 9, true}, {:line, "      start_permanent: Mix.env() == :prod,", "\n", 10, true}, {:line, "      aliases: aliases(),", "\n", 11, true}, {:line, "      deps: deps()", "\n", 12, true}, {:line, "    ]", "\n", 13, true}, {:line, "  end", "\n", 14, true}, {:line, "", "\n", 15, true}, {:line, "  # Configuration for the OTP application.", "\n", 16, true}, {:line, "  #", "\n", 17, true}, {:line, "  # Type `mix help compile.app` for more information.", "\n", 18, true}, {:line, "  def application do", "\n", 19, true}, {:line, "    [", "\n", 20, true}, {:line, "      mod: {MyApp.Application, []},", "\n", 21, true}, {:line, "      extra_applications: [:logger, :runtime_tools]", "\n", 22, true}, {:line, "    ]", "\n", 23, true}, {:line, "  end", "\n", 24, true}, {:line, "", "\n", 25, true}, {:line, "  # Specifies which paths to compile per environment.", "\n", 26, true}, {:line, "  defp elixirc_paths(:test), do: [\"lib\", \"test/support\"]", "\n", 27, true}, {:line, "  defp elixirc_paths(_), do: [\"lib\"]", "\n", 28, true}, {:line, "", "\n", 29, true}, {:line, "  # Specifies your project dependencies.", "\n", 30, true}, {:line, "  #", "\n", 31, true}, {:line, "  # Type `mix help deps` for examples and options.", "\n", 32, true}, {:line, "  defp deps do", "\n", 33, true}, {:line, "    [", "\n", 34, true}, {:line, "      {:phoenix, \"~> 1.7.0-rc.0\", override: true},", "\n", 35, true}, {:line, "      {:phoenix_html, \"~> 3.0\"},", "\n", 36, true}, {:line, "      {:phoenix_live_reload, \"~> 1.2\", only: :dev},", "\n", 37, true}, {:line, "      {:phoenix_live_view, \"~> 0.18.3\"},", "\n", 38, true}, {:line, "      {:heroicons, \"~> 0.5\"},", "\n", 39, true}, {:line, "      {:floki, \">= 0.30.0\", only: :test},", "\n", 40, true}, {:line, "      {:esbuild, \"~> 0.5\", runtime: Mix.env() == :dev},", "\n", 41, true}, {:line, "      {:tailwind, \"~> 0.1.8\", runtime: Mix.env() == :dev},", "\n", 42, true}, {:line, "      {:telemetry_metrics, \"~> 0.6\"},", "\n", 43, true}, {:line, "      {:telemetry_poller, \"~> 1.0\"},", "\n", 44, true}, {:line, "      {:jason, \"~> 1.2\"},", "\n", 45, ...}, {:line, "      {:plug_cowboy, \"~> 2.5\"},", "\n", ...}, {:line, "      {:faker, \"~> 0.17.0\"},", ...}, {:line, ...}, {...}, ...}, starting_index: 1} of type ElixirLS.LanguageServer.Experimental.SourceFile.Document (a struct). This protocol is implemented for the following type(s): Date.Range, File.Stream, Floki.HTMLTree, Function, GenEvent.Stream, HashDict, HashSet, IO.Stream, Jason.OrderedObject, List, Map, MapSet, Range, Scrivener.Page, Stream
    (elixir 1.14.2) lib/enum.ex:1: Enumerable.impl_for!/1
    (elixir 1.14.2) lib/enum.ex:230: Enumerable.slice/1
    (elixir 1.14.2) lib/enum.ex:4379: Enum.slice_forward/4
    (elixir 1.14.2) lib/enum.ex:468: Enum.at/3
    (language_server 0.12.0) lib/language_server/experimental/source_file/document.ex:36: ElixirLS.LanguageServer.Experimental.SourceFile.Document.fetch_line/2
    (language_server 0.12.0) lib/language_server/experimental/source_file/conversions.ex:73: ElixirLS.LanguageServer.Experimental.SourceFile.Conversions.to_elixir/2
    (language_server 0.12.0) lib/language_server/experimental/source_file/conversions.ex:28: ElixirLS.LanguageServer.Experimental.SourceFile.Conversions.to_elixir/2
    (language_server 0.12.0) lib/language_server/experimental/source_file.ex:138: ElixirLS.LanguageServer.Experimental.SourceFile.apply_change/2
Last message (from ElixirLS.LanguageServer.Experimental.Server): {:update, "file:///Users/myuser/projects//my_app/mix.exs", #Function<1.80531545/1 in ElixirLS.LanguageServer.Experimental.Server.State.apply/2>}
State: %ElixirLS.LanguageServer.Experimental.SourceFile.Store.State{source_files: %{"file:///Users/myuser/projects//my_app/mix.exs" => %ElixirLS.LanguageServer.Experimental.SourceFile{uri: "file:///Users/myuser/projects//my_app/mix.exs", path: "/Users/myuser/projects//my_app/mix.exs", version: 1, dirty?: false, document: %ElixirLS.LanguageServer.Experimental.SourceFile.Document{lines: {{:line, "defmodule MyApp.MixProject do", "\n", 1, true}, {:line, "  use Mix.Project", "\n", 2, true}, {:line, "", "\n", 3, true}, {:line, "  def project do", "\n", 4, true}, {:line, "    [", "\n", 5, true}, {:line, "      app: :my_app,", "\n", 6, true}, {:line, "      version: \"0.1.0\",", "\n", 7, true}, {:line, "      elixir: \"~> 1.14\",", "\n", 8, true}, {:line, "      elixirc_paths: elixirc_paths(Mix.env()),", "\n", 9, true}, {:line, "      start_permanent: Mix.env() == :prod,", "\n", 10, true}, {:line, "      aliases: aliases(),", "\n", 11, true}, {:line, "      deps: deps()", "\n", 12, true}, {:line, "    ]", "\n", 13, true}, {:line, "  end", "\n", 14, true}, {:line, "", "\n", 15, true}, {:line, "  # Configuration for the OTP application.", "\n", 16, true}, {:line, "  #", "\n", 17, true}, {:line, "  # Type `mix help compile.app` for more information.", "\n", 18, true}, {:line, "  def application do", "\n", 19, true}, {:line, "    [", "\n", 20, true}, {:line, "      mod: {MyApp.Application, []},", "\n", 21, true}, {:line, "      extra_applications: [:logger, :runtime_tools]", "\n", 22, true}, {:line, "    ]", "\n", 23, true}, {:line, "  end", "\n", 24, true}, {:line, "", "\n", 25, true}, {:line, "  # Specifies which paths to compile per environment.", "\n", 26, true}, {:line, "  defp elixirc_paths(:test), do: [\"lib\", \"test/support\"]", "\n", 27, true}, {:line, "  defp elixirc_paths(_), do: [\"lib\"]", "\n", 28, true}, {:line, "", "\n", 29, true}, {:line, "  # Specifies your project dependencies.", "\n", 30, true}, {:line, "  #", "\n", 31, true}, {:line, "  # Type `mix help deps` for examples and options.", "\n", 32, true}, {:line, "  defp deps do", "\n", 33, true}, {:line, "    [", "\n", 34, true}, {:line, "      {:phoenix, \"~> 1.7.0-rc.0\", override: true},", "\n", 35, true}, {:line, "      {:phoenix_html, \"~> 3.0\"},", "\n", 36, true}, {:line, "      {:phoenix_live_reload, \"~> 1.2\", only: :dev},", "\n", 37, true}, {:line, "      {:phoenix_live_view, \"~> 0.18.3\"},", "\n", 38, ...}, {:line, "      {:heroicons, \"~> 0.5\"},", "\n", ...}, {:line, "      {:floki, \">= 0.30.0\", only: :test},", ...}, {:line, ...}, {...}, ...}, starting_index: 1}}}}
Client ElixirLS.LanguageServer.Experimental.Server is alive

    (stdlib 4.1.1) gen.erl:256: :gen.do_call/4
    (elixir 1.14.2) lib/gen_server.ex:1035: GenServer.call/3
    (language_server 0.12.0) lib/language_server/experimental/server/state.ex:24: ElixirLS.LanguageServer.Experimental.Server.State.apply/2
    (stdlib 4.1.1) timer.erl:235: :timer.tc/1
    (language_server 0.12.0) lib/language_server/experimental/server.ex:76: ElixirLS.LanguageServer.Experimental.Server.apply_to_state/2
    (language_server 0.12.0) lib/language_server/experimental/server.ex:66: ElixirLS.LanguageServer.Experimental.Server.handle_notification/2
    (language_server 0.12.0) lib/language_server/experimental/server.ex:41: ElixirLS.LanguageServer.Experimental.Server.handle_cast/2
    (stdlib 4.1.1) gen_server.erl:1123: :gen_server.try_dispatch/4
[Warn  - 09:23:08.079] ** (exit) exited in: GenServer.call(ElixirLS.LanguageServer.JsonRpc, {:packet, %{"jsonrpc" => "2.0", "method" => "$/cancelRequest", "params" => %{"id" => 5}}}, 5000)
    ** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
    (elixir 1.14.2) lib/gen_server.ex:1027: GenServer.call/3
    (elixir 1.14.2) lib/stream.ex:481: anonymous fn/4 in Stream.each/2
    (elixir 1.14.2) lib/stream.ex:1651: Stream.do_element_resource/6
    (elixir 1.14.2) lib/stream.ex:1811: Enumerable.Stream.do_each/4
    (elixir 1.14.2) lib/stream.ex:689: Stream.run/1
    nofile:1: (file)
    (stdlib 4.1.1) erl_eval.erl:748: :erl_eval.do_apply/7
[Error - 09:23:09.128] ~/projects/elixir/elixir-ls/release/language_server.sh exited with code: 1

Expected behavior

The server shouldn't crash.

megalithic commented 1 year ago

👆 came here to report this as well. (happening with nvim-lspconfig too, not just coc.nvim).

lukaszsamson commented 1 year ago

Have you tried rebuilding elixir-ls? I cannot reproduce that.

@scohen There may be some issues with protocol consolidation after all

scohen commented 1 year ago

This isn't due to protocol consolidation, it's due to a project's builds tainting elixir_ls's modules. When a project builds, it overwrites all the enumerable protocols that elixir_ls outputs. I removed document's protocol implementations because of this, as there's no way to work around this reliably. Landing https://github.com/elixir-lsp/elixir-ls/pull/773/files will fix this, though I'm struggling to understand how the experimental protocol was enabled in the first place.

lukaszsamson commented 1 year ago

Maybe we should disable protocol consolidation on the project build?

scohen commented 1 year ago

hmm, interesting, I don't know if that'll fix it, because it will re-make the protocol dispatch module, which might have the same effect as consolidation. Now that I'm thinking a bit more, the default build mix env is test, which by default doesn't consolidate protocols. The fix is to carry forward without document defining the Enum protocol (which is a crying shame, because that's so convenient)

BigMassive commented 1 year ago

I, too, came to report this. Doom emacs on Ubuntu; asdf: 1.14.0-otp-25 Tried rebuilding - there are various warnings - but I'm new to this game. Crashes whenever a character is input.

lukaszsamson commented 1 year ago

I'm struggling to understand how the experimental protocol was enabled in the first place

@scohen Experimental.Server is started as an app child and PacketRouter was dispatching messages to both old and experimental servers. I disabled the experimental one for now.

BigMassive commented 1 year ago

Thanks for the fix - working now.