123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224(*
* SPDX-FileCopyrightText: 2024 The Forester Project Contributors AND The RedPRL Development Team
*
* SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0 WITH LLVM-exception
*
*)moduleL=Lsp.TypesmoduleRPC=JsonrpcmoduleServer=Lsp_servermoduleAnalysis=AnalysismoduleLsp_state=Lsp_statemoduleLspEio=LspEiomoduleLsp_shims=Lsp_shimsmoduleCall_hierarchy=Call_hierarchymoduleChange_configuration=Change_configurationmoduleCode_action=Code_actionmoduleCode_lens=Code_lensmoduleCompletion=CompletionmoduleDefinitions=DefinitionsmoduleDid_change=Did_changemoduleDid_open=Did_openmoduleDocument_link=Document_linkmoduleDocument_symbols=Document_symbolsmoduleHighlight=HighlightmoduleHover=HovermoduleInlay_hint=Inlay_hintmodulePublish=PublishmoduleSemantic_tokens=Semantic_tokensmoduleWorkspace_symbols=Workspace_symbolsmoduleDid_create_files=Did_create_filesopenForester_coreopenForester_compileropenServeropenLsp_errorletunwrapopterr=matchoptwith|Someopt->opt|None->raise@@Lsp_errorerrletprint_exnexn=letmsg=Printexc.to_stringexnandstack=Printexc.get_backtrace()inEio.traceln"%s\n%s"msgstackletsupported_code_actions=[L.CodeActionKind.Other"new tree"]letsupported_commands=["new tree"]letserver_capabilities=lettextDocumentSync=letopts=L.TextDocumentSyncOptions.create~change:L.TextDocumentSyncKind.Full~openClose:true~save:(`SaveOptions(L.SaveOptions.create~includeText:false()))()in`TextDocumentSyncOptionsoptsinlethoverProvider=letopts=L.HoverOptions.create()in`HoverOptionsoptsinletcodeActionProvider=letopts=L.CodeActionOptions.create~codeActionKinds:supported_code_actions()in`CodeActionOptionsoptsinletexecuteCommandProvider=L.ExecuteCommandOptions.create~commands:supported_commands()inletinlayHintProvider=letopts=L.InlayHintOptions.create()in`InlayHintOptionsoptsinletdefinitionProvider=`DefinitionOptions(L.DefinitionOptions.create())inletcompletionProvider=L.CompletionOptions.create~triggerCharacters:["\\";"{";"(";"["]~allCommitCharacters:["}";")";"]"]()inletdocumentLinkProvider=L.DocumentLinkOptions.create~resolveProvider:true~workDoneProgress:false()inletworkspaceSymbolProvider=`WorkspaceSymbolOptions(L.WorkspaceSymbolOptions.create())inletdocumentSymbolProvider=`DocumentSymbolOptions(L.DocumentSymbolOptions.create())inletworkspace=L.ServerCapabilities.create_workspace~fileOperations:(L.FileOperationOptions.create~didCreate:{filters=[L.FileOperationFilter.create~pattern:(L.FileOperationPattern.create~glob:"**/*.tree"())()]}())()in(* [NOTE: Position Encodings]
For various historical reasons, the spec states that we are _required_ to support UTF-16.
This causes more trouble than it's worth, so we always select UTF-8 as our encoding, even
if the client doesn't support it. *)letpositionEncoding=L.PositionEncodingKind.UTF8in(* [FIXME: Reed M, 09/06/2022] The current verison of the LSP library doesn't support 'positionEncoding' *)L.ServerCapabilities.create~textDocumentSync~hoverProvider~codeActionProvider~executeCommandProvider~inlayHintProvider~positionEncoding~completionProvider~definitionProvider~documentSymbolProvider~documentLinkProvider~workspaceSymbolProvider~workspace()letsupports_utf8_encoding(init_params:L.InitializeParams.t)=letposition_encodings=Option.value~default:[]@@Option.bindinit_params.capabilities.general@@fungcap->gcap.positionEncodingsinList.memL.PositionEncodingKind.UTF8position_encodings(** Perform the LSP initialization handshake.
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize *)letinitialize()=let(id,req)=unwrap(Request.recv())@@Handshake_error"Initialization must begin with a request."inmatchreqwith|E(Initializeinit_paramsasinit_req)->begin(* [HACK: Position Encodings]
If the client doesn't support UTF-8, we shouldn't give up, as it might be using UTF-8 anyways...
Therefore, we just produce a warning, and try to use UTF-8 regardless. *)ifnot(supports_utf8_encodinginit_params)thenEio.traceln"Warning: client does not support UTF-8 encoding, which may lead to inconsistent positions.";letresp=L.InitializeResult.create~capabilities:server_capabilities()inRequest.respondidinit_reqresp;letnotif=unwrap(Notification.recv())@@Handshake_error"Initialization must complete with an initialized notification."inmatchnotifwith|Initialized->Eio.traceln"Initialized!"|_->raise@@Lsp_error(Handshake_error"Initialization must complete with an initialized notification.")end|(E_)->raise@@Lsp_error(Handshake_error"Initialization must begin with an initialize request.")(** Perform the LSP shutdown sequence.
See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#exit *)letshutdown()=letnotif=unwrap(Notification.recv())@@Shutdown_error"No requests can be recieved after a shutdown request."inmatchnotifwith|Exit->()|_->raise@@Lsp_error(Shutdown_error"The only notification that can be recieved after a shutdown request is exit.")(** {1 Main Event Loop} *)letrecevent_loop()=matchrecv()with|Somepacket->let_=matchpacketwith|RPC.Packet.Requestreq->letresp=Request.handlereqinsend(RPC.Packet.Responseresp)|RPC.Packet.Notificationnotif->Notification.handlenotif|_->Eio.traceln"Recieved unexpected packet type."|exceptionexn->print_exnexninifshould_shutdown()thenshutdown()elseevent_loop()|None->Eio.traceln"Recieved an invalid message. Shutting down...@."letstart~env~(config:Config.t)=letlsp_io=LspEio.initenvin(* FIXME: A "batch run" should fail early. The lsp should start even when
there are errors *)letforest=Driver.language_server~env~configinServer.run~init:{forest;lsp_io;should_shutdown=false;}@@fun()->begininitialize();event_loop()end