123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235openSigs(** Current state of a document. *)typenonrecdoc_state={uri:Lsp.Types.DocumentUri.t;languageId:string;version:int;content:string;}(** {2 Server interface for some IO substrate} *)moduleMake(IO:IO)=structopenLsp.TypesmodulePosition=PositionmoduleRange=RangemoduleDiagnostic=DiagnosticmoduleDiagnosticSeverity=DiagnosticSeverity(** The server baseclass *)classvirtualbase_server=objectmethodvirtualon_notification:notify_back:(Lsp.Server_notification.t->unitIO.t)->Lsp.Client_notification.t->unitIO.tmethodvirtualon_request:'a.'aLsp.Client_request.t->'aIO.t(** Set to true if the client requested to exit *)methodmust_quit=falseend(** A wrapper to more easily reply to notifications *)classnotify_back~notify_back?version~(uri:DocumentUri.t)()=object(** Send a log message to the editor *)methodsend_log_msg~type_msg:unitIO.t=letparams=ShowMessageParams.create~type_~message:msginnotify_back(Lsp.Server_notification.LogMessageparams)(** Send diagnostics for the current document *)methodsend_diagnostic(l:Diagnostic.tlist):unitIO.t=letparams=PublishDiagnosticsParams.create~uri?version~diagnostics:l()innotify_back(Lsp.Server_notification.PublishDiagnosticsparams)(** Send a notification (general purpose method) *)methodsend_notification(n:Lsp.Server_notification.t)=notify_backnend(** Current state of a document. *)typenonrecdoc_state=doc_state={uri:DocumentUri.t;languageId:string;version:int;content:string;}(** An easily overloadable class. Pick the methods you want to support.
The user must provide at least the callbacks for document lifecycle:
open, close, update. The most basic LSP server should check documents
when they're updated and report diagnostics back to the editor. *)classvirtualserver=object(self)inheritbase_servervalmutable_quit=falsevaldocs:(DocumentUri.t,doc_state)Hashtbl.t=Hashtbl.create16method!must_quit=_quit(** Find current state of the given document, if present. *)methodfind_doc(uri:DocumentUri.t):doc_stateoption=trySome(Hashtbl.finddocsuri)withNot_found->None(** Override to process other requests *)methodon_request_unhandled:typer.rLsp.Client_request.t->rIO.t=fun_r->IO.failwith"TODO: handle this request"(** Parameter for how to synchronize content with the editor *)methodconfig_sync_opts:TextDocumentSyncOptions.t=TextDocumentSyncOptions.create~change:TextDocumentSyncKind.Incremental~willSave:false()methodon_req_initialize(_i:InitializeParams.t):InitializeResult.tIO.t=letsync_opts=self#config_sync_optsinletcapabilities=ServerCapabilities.create~textDocumentSync:(`TextDocumentSyncOptionssync_opts)()inIO.return@@InitializeResult.create~capabilities()(** Called when the user hovers on some identifier in the document *)methodon_req_hover~uri:_~pos:_(_:doc_state):Hover.toptionIO.t=IO.returnNone(** Called when the user requests completion in the document *)methodon_req_completion~uri:_~pos:_~ctx:_(_:doc_state):[`CompletionListofCompletionList.t|`ListofCompletionItem.tlist]optionIO.t=IO.returnNone(** Called when the user wants to jump-to-definition *)methodon_req_definition~uri:_~pos:_(_:doc_state):Locations.toptionIO.t=IO.returnNonemethodon_request:typer.rLsp.Client_request.t->rIO.t=fun(r:_Lsp.Client_request.t)->beginmatchrwith|Lsp.Client_request.Shutdown->_quit<-true;IO.return()|Lsp.Client_request.Initializei->self#on_req_initializei|Lsp.Client_request.TextDocumentHover{textDocument;position}->letdoc_st=Hashtbl.finddocstextDocument.uriinself#on_req_hover~uri:textDocument.uri~pos:positiondoc_st|Lsp.Client_request.TextDocumentCompletion{textDocument;position;context}->letdoc_st=Hashtbl.finddocstextDocument.uriinself#on_req_completion~uri:textDocument.uri~pos:position~ctx:contextdoc_st|Lsp.Client_request.TextDocumentDefinition{textDocument;position}->letdoc_st=Hashtbl.finddocstextDocument.uriinself#on_req_definition~uri:textDocument.uri~pos:positiondoc_st|Lsp.Client_request.TextDocumentDeclaration_|Lsp.Client_request.TextDocumentTypeDefinition_|Lsp.Client_request.TextDocumentCodeLens_|Lsp.Client_request.TextDocumentCodeLensResolve_|Lsp.Client_request.TextDocumentPrepareRename_|Lsp.Client_request.TextDocumentRename_|Lsp.Client_request.TextDocumentLink_|Lsp.Client_request.TextDocumentLinkResolve_|Lsp.Client_request.DocumentSymbol_|Lsp.Client_request.WorkspaceSymbol_|Lsp.Client_request.DebugEcho_|Lsp.Client_request.DebugTextDocumentGet_|Lsp.Client_request.TextDocumentReferences_|Lsp.Client_request.TextDocumentHighlight_|Lsp.Client_request.TextDocumentFoldingRange_|Lsp.Client_request.SignatureHelp_|Lsp.Client_request.CodeAction_|Lsp.Client_request.CompletionItemResolve_|Lsp.Client_request.WillSaveWaitUntilTextDocument_|Lsp.Client_request.TextDocumentFormatting_|Lsp.Client_request.TextDocumentOnTypeFormatting_|Lsp.Client_request.TextDocumentColorPresentation_|Lsp.Client_request.TextDocumentColor_|Lsp.Client_request.SelectionRange_|Lsp.Client_request.ExecuteCommand_|Lsp.Client_request.UnknownRequest_->self#on_request_unhandledrend(** Called when a document is opened *)methodvirtualon_notif_doc_did_open:notify_back:notify_back->TextDocumentItem.t->content:string->unitIO.tmethodvirtualon_notif_doc_did_close:notify_back:notify_back->TextDocumentIdentifier.t->unitIO.t(** Called when the document changes. *)methodvirtualon_notif_doc_did_change:notify_back:notify_back->VersionedTextDocumentIdentifier.t->TextDocumentContentChangeEvent.tlist->old_content:string->new_content:string->unitIO.t(** Override to handle unprocessed notifications *)methodon_notification_unhandled~notify_back:_(_n:Lsp.Client_notification.t):unitIO.t=IO.return()methodon_notification~notify_back(n:Lsp.Client_notification.t):unitIO.t=letopenLsp.Typesinbeginmatchnwith|Lsp.Client_notification.TextDocumentDidOpen{DidOpenTextDocumentParams.textDocument=doc}->letnotify_back=newnotify_back~uri:doc.uri~version:doc.version~notify_back()inletst={uri=doc.uri;version=doc.version;content=doc.text;languageId=doc.languageId;}inHashtbl.replacedocsdoc.urist;self#on_notif_doc_did_open~notify_backdoc~content:st.content|Lsp.Client_notification.TextDocumentDidClose{textDocument=doc}->letnotify_back=newnotify_back~uri:doc.uri~notify_back()inself#on_notif_doc_did_close~notify_backdoc|Lsp.Client_notification.TextDocumentDidChange{textDocument=doc;contentChanges=c}->letnotify_back=newnotify_back~uri:doc.uri~notify_back()inbeginmatchHashtbl.finddocsdoc.uriwith|exceptionNot_found->IO.failwith"unknown document"|st->letold_content=st.contentinletnew_doc:Lsp.Text_document.t=letdoc=Lsp.Text_document.make(DidOpenTextDocumentParams.create~textDocument:(TextDocumentItem.create~languageId:st.languageId~uri:doc.uri~version:st.version~text:st.content))inList.fold_left(fundev->Lsp.Text_document.apply_content_changedev)doccinletnew_st={stwithcontent=Lsp.Text_document.textnew_doc;version=Lsp.Text_document.versionnew_doc;}inHashtbl.replacedocsdoc.urinew_st;self#on_notif_doc_did_change~notify_backdocc~old_content~new_content:new_st.contentend|Lsp.Client_notification.Exit->_quit<-true;IO.return()|Lsp.Client_notification.DidSaveTextDocument_|Lsp.Client_notification.WillSaveTextDocument_|Lsp.Client_notification.ChangeWorkspaceFolders_|Lsp.Client_notification.ChangeConfiguration_|Lsp.Client_notification.Initialized|Lsp.Client_notification.Unknown_notification_|Lsp.Client_notification.CancelRequest_->self#on_notification_unhandled~notify_backnendendend