lexical-lsp / lexical

Lexical is a next-generation elixir language server
782 stars 77 forks source link

Lexical silently fails to start in nvim nightly #625

Closed iurimateus closed 4 months ago

iurimateus commented 4 months ago

After https://github.com/neovim/neovim/pull/27526, nvim's initialize request contains the following additional values "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports" for capabilities.textDocument.codeAction.codeActionLiteralSupport.codeActionKind.valueSet (unsure if intentional).

This causes Lexical to silently fail to initialize, in the same way as described in https://github.com/lexical-lsp/lexical/issues/306#issuecomment-1662224646.

Neovim's LSP log shows only .../vim/lsp/rpc.lua:282 "rpc.send" { id = 1, jsonrpc = "2.0", method = "initialize", ...}, without the corresponding "rpc.receive"; no messages after.

Lexical log ``` 10:09:37.625 [info] Child {LoggerFileBackend, :general_log} of Supervisor Logger.Backends.Supervisor started Pid: #PID<0.119.0> Start Call: Logger.Backends.Watcher.start_link({{LoggerFileBackend, :general_log}, {LoggerFileBackend, :general_log}}) Restart: :transient Shutdown: 5000 Type: :worker 10:09:37.625 [info] Child Logger.Backends.Supervisor of Supervisor Logger.Backends.Internal started Pid: #PID<0.118.0> Start Call: Logger.Backends.Supervisor.start_link([{LoggerFileBackend, :general_log}]) Restart: :permanent Shutdown: :infinity Type: :supervisor 10:09:37.625 [info] Child Logger.Backends.Internal of Supervisor Logger.Supervisor started Pid: #PID<0.114.0> Start Call: Logger.Backends.Internal.start_link([]) Restart: :permanent Shutdown: :infinity Type: :supervisor 10:09:37.625 [info] Application logger started at :nonode@nohost 10:09:38.383 [info] Child :ttb_autostart of Supervisor :runtime_tools_sup started Pid: #PID<0.1478.0> Start Call: :ttb_autostart.start_link() Restart: :temporary Shutdown: 3000 Type: :worker 10:09:38.383 [info] Application runtime_tools started at :nonode@nohost 10:09:38.385 [info] Application erts started at :nonode@nohost 10:09:38.385 [info] Application lx_lexical_shared started at :nonode@nohost 10:09:38.385 [info] Application lx_lexical_test started at :nonode@nohost 10:09:38.387 [info] Child LXSnowflake.Generator of Supervisor #PID<0.1485.0> (Supervisor.Default) started Pid: #PID<0.1486.0> Start Call: LXSnowflake.Generator.start_link(1704070800000, 1) Restart: :permanent Shutdown: 5000 Type: :worker 10:09:38.387 [info] Application lx_snowflake started at :nonode@nohost 10:09:38.387 [info] Application lx_sourceror started at :nonode@nohost 10:09:38.387 [info] Application lx_common started at :nonode@nohost 10:09:38.387 [info] Application lx_elixir_sense started at :nonode@nohost 10:09:38.395 [info] Application jason started at :nonode@nohost 10:09:38.395 [info] Application logger_file_backend started at :nonode@nohost 10:09:38.395 [info] Application lx_path_glob started at :nonode@nohost 10:09:38.396 [info] Application lx_proto started at :nonode@nohost 10:09:38.396 [info] Application lx_protocol started at :nonode@nohost 10:09:38.411 [info] Child LXical.Document.Store of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1499.0> Start Call: LXical.Document.Store.start_link([derive: [analysis: &LXical.Ast.analyze/1]]) Restart: :permanent Shutdown: 5000 Type: :worker 10:09:38.414 [info] Child LXical.Server of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1500.0> Start Call: LXical.Server.start_link([]) Restart: :permanent Shutdown: 5000 Type: :worker 10:09:38.414 [info] Child LXical.Server.ProjectSupervisor of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1501.0> Start Call: DynamicSupervisor.start_link([name: LXical.Server.ProjectSupervisor, strategy: :one_for_one]) Restart: :permanent Shutdown: :infinity Type: :supervisor 10:09:38.414 [info] Child LXical.Server.Provider.Queue.Supervisor of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1502.0> Start Call: Task.Supervisor.start_link([name: LXical.Server.Provider.Queue.Supervisor]) Restart: :permanent Shutdown: :infinity Type: :supervisor 10:09:38.417 [info] Child LXical.Server.Provider.Queue of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1503.0> Start Call: LXical.Server.Provider.Queue.start_link([]) Restart: :permanent Shutdown: 5000 Type: :worker 10:09:38.417 [info] Child LXical.Server.Transport.StdIO of Supervisor LXical.Server.Supervisor started Pid: #PID<0.1504.0> Start Call: LXical.Server.Transport.StdIO.start_link(:standard_io, &LXical.Server.protocol_message/1) Restart: :permanent Shutdown: 5000 Type: :worker 10:09:38.417 [info] Application lx_server started at :nonode@nohost ```

The following test fails with

1) test initialize parses a request from neovim nightly (Lexical.Protocol.Integrations.InitializeTest)
     apps/protocol/test/lexical/protocol/integration/initialize_test.exs:29
     match (=) failed
     code:  assert {:ok, %Requests.Initialize{lsp: %Requests.Initialize.LSP{} = parsed}} =
              Requests.Initialize.parse(neovim_initialize_nightly())
     left:  {:ok, %Lexical.Protocol.Requests.Initialize{lsp: %Lexical.Protocol.Requests.Initialize.LSP{} = parsed}}
     right: {:error, {:invalid_constant, "Empty"}}
     stacktrace:
       test/lexical/protocol/integration/initialize_test.exs:30: (test)
test ```diff diff --git a/apps/protocol/test/lexical/protocol/integration/initialize_test.exs b/apps/protocol/test/lexical/protocol/integration/initialize_test.exs index 3292fca..d9d2f4c 100644 --- a/apps/protocol/test/lexical/protocol/integration/initialize_test.exs +++ b/apps/protocol/test/lexical/protocol/integration/initialize_test.exs @@ -26,6 +26,26 @@ defmodule Lexical.Protocol.Integrations.InitializeTest do validate_completion(text_doc_capabilities) end + test "initialize parses a request from neovim nightly" do + assert {:ok, %Requests.Initialize{lsp: %Requests.Initialize.LSP{} = parsed}} = + Requests.Initialize.parse(neovim_initialize_nightly()) + + assert parsed.client_info.name == "Neovim" + assert parsed.client_info.version == "0.10.0-dev+cc15ba212" + + assert %Types.ClientCapabilities{} = capabilities = parsed.capabilities + + assert %TextDocument.ClientCapabilities{} = text_doc_capabilities = capabilities.text_document + + validate_workspace(capabilities) + + validate_call_hierarchy(text_doc_capabilities) + validate_code_action(text_doc_capabilities) + validate_declaration(text_doc_capabilities) + validate_definition(text_doc_capabilities) + validate_completion(text_doc_capabilities) + end + test "initialize parse a request from zed" do assert {:ok, %Requests.Initialize{lsp: %Requests.Initialize.LSP{} = parsed}} = Requests.Initialize.parse(zed_initialize()) @@ -235,6 +255,171 @@ defmodule Lexical.Protocol.Integrations.InitializeTest do |> Jason.decode!() end + def neovim_initialize_nightly do + ~S( + { + "id": 1, + "jsonrpc": "2.0", + "method": "initialize", + "params": { + "capabilities": { + "textDocument": { + "callHierarchy": { + "dynamicRegistration": false + }, + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": ["", 'Empty', 'QuickFix', 'Refactor', 'RefactorExtract', 'RefactorInline', 'RefactorRewrite', 'Source', 'SourceOrganizeImports', "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports"] + } + }, + "dataSupport": true, + "dynamicRegistration": false, + "isPreferredSupport": true, + "resolveSupport": { + "properties": [ "edit" ] + } + }, + "completion": { + "completionItem": { + "commitCharactersSupport": false, + "deprecatedSupport": false, + "documentationFormat": [ "markdown", "plaintext" ], + "preselectSupport": false, + "snippetSupport": false + }, + "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 ] + }, + "contextSupport": false, + "dynamicRegistration": false + }, + "declaration": { + "linkSupport": true + }, + "definition": { + "linkSupport": true + }, + "documentHighlight": { + "dynamicRegistration": false + }, + "documentSymbol": { + "dynamicRegistration": false, + "hierarchicalDocumentSymbolSupport": 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 ] + } + }, + "hover": { + "contentFormat": [ "markdown", "plaintext" ], + "dynamicRegistration": false + }, + "implementation": { + "linkSupport": true + }, + "publishDiagnostics": { + "relatedInformation": true, + "tagSupport": { + "valueSet": [ 1, 2 ] + } + }, + "references": { + "dynamicRegistration": false + }, + "rename": { + "dynamicRegistration": false, + "prepareSupport": true + }, + "semanticTokens": { + "augmentsSyntaxTokens": true, + "dynamicRegistration": false, + "formats": [ "relative" ], + "multilineTokenSupport": false, + "overlappingTokenSupport": true, + "requests": { + "full": { + "delta": true + }, + "range": false + }, + "serverCancelSupport": false, + "tokenModifiers": [ "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" ], + "tokenTypes": [ "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" ] + }, + "signatureHelp": { + "dynamicRegistration": false, + "signatureInformation": { + "activeParameterSupport": true, + "documentationFormat": [ "markdown", "plaintext" ], + "parameterInformation": { + "labelOffsetSupport": true + } + } + }, + "synchronization": { + "didSave": true, + "dynamicRegistration": false, + "willSave": true, + "willSaveWaitUntil": true + }, + "typeDefinition": { + "linkSupport": true + } + }, + "window": { + "showDocument": { + "support": true + }, + "showMessage": { + "messageActionItem": { + "additionalPropertiesSupport": false + } + }, + "workDoneProgress": true + }, + "workspace": { + "applyEdit": true, + "configuration": true, + "didChangeWatchedFiles": { + "dynamicRegistration": false, + "relativePatternSupport": true + }, + "semanticTokens": { + "refreshSupport": true + }, + "symbol": { + "dynamicRegistration": false, + "hierarchicalWorkspaceSymbolSupport": 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 ] + } + }, + "workspaceEdit": { + "resourceOperations": ["rename", "create", "delete"] + }, + "workspaceFolders": true + } + }, + "clientInfo": { + "name": "Neovim", + "version": "0.10.0-dev+cc15ba212" + }, + "processId": 81648, + "rootPath": "/Users/scottming/Code/lexical", + "rootUri": "file:///Users/scottming/Code/lexical", + "trace": "off", + "workspaceFolders": [ + { + "name": "/Users/scottming/Code/lexical", + "uri": "file:///Users/scottming/Code/lexical" + } + ] + } + } + ) + |> Jason.decode!() + end + def zed_initialize do # Zed Preview 0.106.2 ~S( ```

edit: this is a bug in nvim that's being addressed, but I expected some output from Lexical.

scottming commented 4 months ago

It looks like neovim passed the wrong parameters. After I deleted the valuesets starting with uppercase letters, the tests were able to pass.

                                "Empty",
                                "QuickFix",
                                "Refactor",
                                "RefactorExtract",
                                "RefactorInline",
                                "RefactorRewrite",
                                "Source",
                                "SourceOrganizeImports",
Json ```elixir { "id": 1, "jsonrpc": "2.0", "method": "initialize", "params": { "capabilities": { "copilot": { "openURL": true }, "general": { "positionEncodings": [ "utf-16" ] }, "textDocument": { "callHierarchy": { "dynamicRegistration": false }, "codeAction": { "codeActionLiteralSupport": { "codeActionKind": { "valueSet": [ "", "Empty", "QuickFix", "Refactor", "RefactorExtract", "RefactorInline", "RefactorRewrite", "Source", "SourceOrganizeImports", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" ] } }, "dataSupport": true, "dynamicRegistration": true, "isPreferredSupport": true, "resolveSupport": { "properties": [ "edit" ] } }, "completion": { "completionItem": { "commitCharactersSupport": false, "deprecatedSupport": false, "documentationFormat": [ "markdown", "plaintext" ], "preselectSupport": false, "snippetSupport": false }, "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 ] }, "contextSupport": false, "dynamicRegistration": false }, "declaration": { "linkSupport": true }, "definition": { "dynamicRegistration": true, "linkSupport": true }, "diagnostic": { "dynamicRegistration": false }, "documentHighlight": { "dynamicRegistration": false }, "documentSymbol": { "dynamicRegistration": false, "hierarchicalDocumentSymbolSupport": 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 ] } }, "formatting": { "dynamicRegistration": true }, "hover": { "contentFormat": [ "markdown", "plaintext" ], "dynamicRegistration": true }, "implementation": { "linkSupport": true }, "inlayHint": { "dynamicRegistration": true, "resolveSupport": { "properties": [ "textEdits", "tooltip", "location", "command" ] } }, "publishDiagnostics": { "dataSupport": true, "relatedInformation": true, "tagSupport": { "valueSet": [ 1, 2 ] } }, "rangeFormatting": { "dynamicRegistration": true }, "references": { "dynamicRegistration": false }, "rename": { "dynamicRegistration": true, "prepareSupport": true }, "semanticTokens": { "augmentsSyntaxTokens": true, "dynamicRegistration": false, "formats": [ "relative" ], "multilineTokenSupport": false, "overlappingTokenSupport": true, "requests": { "full": { "delta": true }, "range": false }, "serverCancelSupport": false, "tokenModifiers": [ "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" ], "tokenTypes": [ "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" ] }, "signatureHelp": { "dynamicRegistration": false, "signatureInformation": { "activeParameterSupport": true, "documentationFormat": [ "markdown", "plaintext" ], "parameterInformation": { "labelOffsetSupport": true } } }, "synchronization": { "didSave": true, "dynamicRegistration": false, "willSave": true, "willSaveWaitUntil": true }, "typeDefinition": { "linkSupport": true } }, "window": { "showDocument": { "support": true }, "showMessage": { "messageActionItem": { "additionalPropertiesSupport": false } }, "workDoneProgress": true }, "workspace": { "applyEdit": true, "configuration": true, "didChangeConfiguration": { "dynamicRegistration": false }, "didChangeWatchedFiles": { "dynamicRegistration": true, "relativePatternSupport": true }, "inlayHint": { "refreshSupport": true }, "semanticTokens": { "refreshSupport": true }, "symbol": { "dynamicRegistration": false, "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 ] } }, "workspaceEdit": { "resourceOperations": [ "rename", "create", "delete" ] }, "workspaceFolders": true } }, "clientInfo": { "name": "Neovim", "version": "0.10.0-dev+Homebrew" }, "processId": 13594, "rootPath": "/Users/scottming/Code/lexical", "rootUri": "file:///Users/scottming/Code/lexical", "trace": "off", "workspaceFolders": [ { "name": "/Users/scottming/Code/lexical", "uri": "file:///Users/scottming/Code/lexical" } ] } } ```
iurimateus commented 4 months ago

Correct, sorry for the noise.

I wonder if Lexical could emit some logs on similar occasions? Like the issue referenced, it's hard to debug without information.

In any case, we can close this.

megalithic commented 4 months ago

Correct, sorry for the noise.

I wonder if Lexical could emit some logs on similar occasions? Like the issue referenced, it's hard to debug without information.

In any case, we can close this.

Concur with emitting output to the project/.lexical/*.log for these types of things.