nemethf/eglot-x

Dart language-server doesn't play nicely with experimental rust-analyzer configuration

Closed this issue · 5 comments

AS the title says, see dart-lang/sdk#55935. Disabling eglot-x with eglot-x-disable lets me still run the server, and perhaps ultimately the issue is with dart's language server being too strict, but maybe there's a better way of handling this.

This results in eglot failing to run the dart language-server, with errors like this:

[jsonrpc] D[00:39:52.098] Running language server: dart language-server --client-id emacs.eglot-dart
[jsonrpc] e[00:39:52.099] --> initialize[1] {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":183783,"clientInfo":{"name":"Eglot","version":"1.17"},"rootPath":"/home/tlater/Documents/Work/Sonnenshift/frontend/","rootUri":"file:///home/tlater/Documents/Work/Sonnenshift/frontend","initializationOptions":{},"capabilities":{"workspace":{"applyEdit":true,"executeCommand":{"dynamicRegistration":false},"workspaceEdit":{"documentChanges":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":false},"configuration":true,"workspaceFolders":true},"textDocument":{"synchronization":{"dynamicRegistration":false,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":false,"completionItem":{"snippetSupport":true,"deprecatedSupport":true,"resolveSupport":{"properties":["documentation","details","additionalTextEdits"]},"tagSupport":{"valueSet":[1]}},"contextSupport":true},"hover":{"dynamicRegistration":false,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":false,"signatureInformation":{"parameterInformation":{"labelOffsetSupport":true},"documentationFormat":["markdown","plaintext"],"activeParameterSupport":true}},"references":{"dynamicRegistration":false},"definition":{"dynamicRegistration":false,"linkSupport":true},"declaration":{"dynamicRegistration":false,"linkSupport":true},"implementation":{"dynamicRegistration":false,"linkSupport":true},"typeDefinition":{"dynamicRegistration":false,"linkSupport":true},"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]}},"documentHighlight":{"dynamicRegistration":false},"codeAction":{"dynamicRegistration":false,"resolveSupport":{"properties":["edit","command"]},"dataSupport":true,"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"isPreferredSupport":true},"formatting":{"dynamicRegistration":false},"rangeFormatting":{"dynamicRegistration":false},"rename":{"dynamicRegistration":false},"inlayHint":{"dynamicRegistration":false},"publishDiagnostics":{"relatedInformation":false,"codeDescriptionSupport":false,"tagSupport":{"valueSet":[1,2]}}},"window":{"showDocument":{"support":true},"workDoneProgress":true},"general":{"positionEncodings":["utf-32","utf-8","utf-16"]},"experimental":{"snippetTextEdit":true,"serverStatusNotification":true,"colorDiagnosticOutput":true,"openServerLogs":true,"commands":{"commands":["rust-analyzer.runSingle","rust-analyzer.showReferences","rust-analyzer.gotoLocation","rust-analyzer.rename","editor.action.rename"]},"hoverActions":true,"localDocs":true},"offsetEncoding":["utf-32","utf-16"]},"workspaceFolders":[{"uri":"file:///home/tlater/Documents/Work/Sonnenshift/frontend","name":"~/Documents/Work/Sonnenshift/frontend/"}]}}
[jsonrpc] e[00:39:52.276] <-- initialize[1] {"id":1,"jsonrpc":"2.0","error":{"code":-32001,"message":"An error occurred while handling initialize request"}}
[jsonrpc] D[00:39:52.276] Connection state change: `killed
'

----------b---y---e---b---y---e----------
[jsonrpc] e[00:39:52.283] <-- window/logMessage {"jsonrpc":"2.0","method":"window/logMessage","params":{"message":"An error occurred while handling initialize request: type '_Map<String, dynamic>' is not a subtype of type 'List<dynamic>?' in type cast\n#0      new LspClientCapabilities (package:analysis_server/src/lsp/client_capabilities.dart:201:45)\n#1      LspAnalysisServer.handleClientConnection (package:analysis_server/src/lsp/lsp_analysis_server.dart:382:27)\n#2      InitializeMessageHandler.handle (package:analysis_server/src/lsp/handlers/handler_initialize.dart:27:12)\n#3      MessageHandler.handleMessage (package:analysis_server/src/lsp/handlers/handlers.dart:334:12)\n#4      ServerStateMessageHandler.handleMessage (package:analysis_server/src/lsp/handlers/handlers.dart:396:25)\n#5      LspAnalysisServer._handleRequestMessage (package:analysis_server/src/lsp/lsp_analysis_server.dart:1010:41)\n#6      LspAnalysisServer.handleMessage.<anonymous closure>.<anonymous closure> (package:analysis_server/src/lsp/lsp_analysis_server.dart:461:21)\n#7      OperationPerformanceImpl.runAsync (package:analyzer/src/util/performance/operation_performance.dart:172:29)\n#8      LspAnalysisServer.handleMessage.<anonymous closure> (package:analysis_server/src/lsp/lsp_analysis_server.dart:444:33)\n#9      _rootRun (dart:async/zone.dart:1399:13)\n#10     _CustomZone.run (dart:async/zone.dart:1301:19)\n#11     _runZoned (dart:async/zone.dart:1826:10)\n#12     runZonedGuarded (dart:async/zone.dart:1814:12)\n#13     LspAnalysisServer.handleMessage (package:analysis_server/src/lsp/lsp_analysis_server.dart:436:5)\n#14     LspByteStreamServerChannel._readMessage (package:analysis_server/src/lsp/channel/lsp_byte_stream_channel.dart:84:16)\n#15     LspByteStreamServerChannel.listen.<anonymous closure> (package:analysis_server/src/lsp/channel/lsp_byte_stream_channel.dart:53:24)\n#16     _rootRunUnary (dart:async/zone.dart:1407:47)\n#17     _CustomZone.runUnary (dart:async/zone.dart:1308:19)\n#18     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1217:7)\n#19     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:365:11)\n#20     _DelayedData.perform (dart:async/stream_impl.dart:541:14)\n#21     _PendingEvents.handleNext (dart:async/stream_impl.dart:646:11)\n#22     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:617:7)\n#23     _rootRun (dart:async/zone.dart:1391:47)\n#24     _CustomZone.run (dart:async/zone.dart:1301:19)\n#25     _CustomZone.runGuarded (dart:async/zone.dart:1209:7)\n#26     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1249:23)\n#27     _rootRun (dart:async/zone.dart:1399:13)\n#28     _CustomZone.run (dart:async/zone.dart:1301:19)\n#29     _CustomZone.runGuarded (dart:async/zone.dart:1209:7)\n#30     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1249:23)\n#31     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)\n#32     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)\n#33     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:118:13)\n#34     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:185:5)\n","type":1}}
[stderr]  
[stderr]  
[stderr]  nil
[stderr]  nil
[stderr]  Process EGLOT (frontend/(dart-mode dart-ts-mode)) stderr finished

Having read that issue, it seems you just need to get a version of the server that contains the fix for the issue.

Also, according to the LSP specification, the type of the experimental field is LSPAny. So I think the dart server should accept a map instead of a list.

Additionally, I think it's not (in general) a bad idea to advertise the client capabilities to every server, that way a server can implement an experimental feature of another sever without the modification of the client. This can potentially lead to a faster adoption of a new protocol feature. As an example, the rust-analyzer server implemented the "encoding negotiation" extension of the clangd server, which later led to the positionEncodings feature in the LSP specification.


What does the dart server expect in the experimental list? Is this documented somewhere? Is there anything interesting that eglot-x should support?

I just re-read that issue. It seems it's enough to customize `eglot-x-client-commands' for the problem to go away. Just remove everything for the command list.

Having read that issue, it seems you just need to get a version of the server that contains the fix for the issue.

I thought so too, but that seems to be specific to the exact reported issue in some way. I couldn't get it to work with much newer versions of dart.

It seems it's enough to customize `eglot-x-client-commands' for the problem to go away.

I'll give it a shot! Still unfortunate to have this issue at all, it took a good amount of time to trace it down to eglot-x since the error is rather cryptic.

What does the dart server expect in the experimental list? Is this documented somewhere? Is there anything interesting that eglot-x should support?

I'm not sure yet! I've only just started using dart, this came up while setting up my first environment. Dart works a lot with "devices", so there may well be some features which could be exposed, but I'm not sure if their server handles any of that or if they just make a plugin to do it in vscode land. Upstream doesn't seem terribly interested in supporting anything besides vscode and android-studio.

I'll cycle back when I'm a bit more familiar with the ecosystem :)

It seems it's enough to customize `eglot-x-client-commands' for the problem to go away.

I'll give it a shot!

Yep, that did it:

(leaf dart-mode
  :mode `(,(rx ".dart" string-end))
  :hook (dart-mode-hook . (lambda () (set (make-local-variable 'eglot-x-client-commands) '()))))

Should we leave this open for a potential less manual solution or just assume dart is the only language server with this quirk and that people who need it will find this issue?

Let's close it. It can be reopened when necessary.