mirror of
https://github.com/c-cube/linol.git
synced 2025-12-06 03:05:31 -05:00
Problem: Linol can only send notifications from the server to the client, but not requests. Solution: The solution was inspired by Haskell's `lsp` package. We first maintain a `server_request_handler_pair` data structure which represents some server request and its associated response handler. Now `notify_back` may take these fields and use it to actually handle a request and its response. Changes to `notify_back` are propagated throughout `server.ml`. On `jsonrpc2.ml`, we refactor the notification and request handlers so that we may handle the response and batch response handlers and batch call as a courtesy. For a response, we keep a hash table that tracks an ID to the `server_request_handler_pair` in other to be able to call the handler that was associated with the response. After we get such response (indexed by the server request's ID), we remove it from the hash table to prevent a memory leak. Since the server needs to keep track of the server request IDs, I added a mutable `id_counter` in order to generate a fresh LSP ID (using `fresh_lsp_id`) for every outgoing request. For debugging, if we error while decoding a JSON, I now print the exception too.
604 lines
23 KiB
OCaml
604 lines
23 KiB
OCaml
open Sigs
|
|
|
|
type nonrec doc_state = {
|
|
uri: Lsp.Types.DocumentUri.t;
|
|
languageId: string;
|
|
version: int;
|
|
content: string;
|
|
}
|
|
(** Current state of a document. *)
|
|
|
|
(** {2 Request ID}
|
|
|
|
unique ID of a request, used by JSONRPC to map each request to its reply. *)
|
|
module Req_id = struct
|
|
type t = Jsonrpc.Id.t
|
|
|
|
(** String representation of the ID *)
|
|
let to_string : t -> string = function
|
|
| `String s -> s
|
|
| `Int i -> string_of_int i
|
|
end
|
|
|
|
(** {2 Server interface for some IO substrate} *)
|
|
module Make (IO : IO) = struct
|
|
open Lsp.Types
|
|
module Position = Position
|
|
module Range = Range
|
|
module Diagnostic = Diagnostic
|
|
module DiagnosticSeverity = DiagnosticSeverity
|
|
module Req_id = Req_id
|
|
|
|
(** A variant carrying a [Lsp.Server_request.t] and a handler for its return
|
|
value. The request is stored in order to allow us to discriminate its
|
|
existential variable. *)
|
|
type server_request_handler_pair =
|
|
| Request_and_handler :
|
|
'from_server Lsp.Server_request.t
|
|
* (('from_server, Jsonrpc.Response.Error.t) result -> unit IO.t)
|
|
-> server_request_handler_pair
|
|
|
|
type send_request = server_request_handler_pair -> Req_id.t IO.t
|
|
(** The type of the action that sends a request from the server to the client
|
|
and handles its response. *)
|
|
|
|
(** The server baseclass *)
|
|
class virtual base_server =
|
|
object
|
|
method virtual on_notification
|
|
: notify_back:(Lsp.Server_notification.t -> unit IO.t) ->
|
|
server_request:send_request ->
|
|
Lsp.Client_notification.t ->
|
|
unit IO.t
|
|
|
|
method virtual on_request
|
|
: 'a.
|
|
notify_back:(Lsp.Server_notification.t -> unit IO.t) ->
|
|
server_request:send_request ->
|
|
id:Req_id.t ->
|
|
'a Lsp.Client_request.t ->
|
|
'a IO.t
|
|
(** Method called to handle client requests.
|
|
@param notify_back an object used to reply to the client, send progress
|
|
messages, diagnostics, etc.
|
|
@param id the query RPC ID, can be used for tracing, cancellation, etc. *)
|
|
|
|
method must_quit = false
|
|
(** Set to true if the client requested to exit *)
|
|
end
|
|
|
|
(** A wrapper to more easily reply to notifications *)
|
|
class notify_back ~notify_back ~server_request ~workDoneToken
|
|
~partialResultToken:_ ?version ?(uri : DocumentUri.t option) () =
|
|
object
|
|
val mutable uri = uri
|
|
method set_uri u = uri <- Some u
|
|
|
|
method send_log_msg ~type_ msg : unit IO.t =
|
|
let params = LogMessageParams.create ~type_ ~message:msg in
|
|
notify_back (Lsp.Server_notification.LogMessage params)
|
|
(** Send a log message to the editor *)
|
|
|
|
method send_diagnostic (l : Diagnostic.t list) : unit IO.t =
|
|
match uri with
|
|
| None ->
|
|
IO.failwith "notify_back: cannot publish diagnostics, no URI given"
|
|
| Some uri ->
|
|
let params =
|
|
PublishDiagnosticsParams.create ~uri ?version ~diagnostics:l ()
|
|
in
|
|
notify_back (Lsp.Server_notification.PublishDiagnostics params)
|
|
(** Send diagnostics for the current document *)
|
|
|
|
method telemetry json : unit IO.t =
|
|
notify_back @@ Lsp.Server_notification.TelemetryNotification json
|
|
|
|
method cancel_request (id : Jsonrpc.Id.t) : unit IO.t =
|
|
notify_back @@ CancelRequest id
|
|
|
|
method work_done_progress_begin (p : Lsp.Types.WorkDoneProgressBegin.t)
|
|
: unit IO.t =
|
|
match workDoneToken with
|
|
| Some token ->
|
|
notify_back
|
|
@@ WorkDoneProgress
|
|
{ value = Lsp.Server_notification.Progress.Begin p; token }
|
|
| None -> IO.return ()
|
|
|
|
method work_done_progress_report (p : Lsp.Types.WorkDoneProgressReport.t)
|
|
: unit IO.t =
|
|
match workDoneToken with
|
|
| Some token ->
|
|
notify_back
|
|
@@ WorkDoneProgress
|
|
{ value = Lsp.Server_notification.Progress.Report p; token }
|
|
| None -> IO.return ()
|
|
|
|
method work_done_progress_end (p : Lsp.Types.WorkDoneProgressEnd.t)
|
|
: unit IO.t =
|
|
match workDoneToken with
|
|
| Some token ->
|
|
notify_back
|
|
@@ WorkDoneProgress
|
|
{ value = Lsp.Server_notification.Progress.End p; token }
|
|
| None -> IO.return ()
|
|
|
|
method send_notification (n : Lsp.Server_notification.t) = notify_back n
|
|
(** Send a notification from the server to the client (general purpose method) *)
|
|
|
|
method send_request
|
|
: 'from_server.
|
|
'from_server Lsp.Server_request.t ->
|
|
(('from_server, Jsonrpc.Response.Error.t) result -> unit IO.t) ->
|
|
Req_id.t IO.t =
|
|
fun r h -> server_request @@ Request_and_handler (r, h)
|
|
(** Send a request from the server to the client (general purpose method) *)
|
|
end
|
|
|
|
type nonrec doc_state = doc_state = {
|
|
uri: DocumentUri.t;
|
|
languageId: string;
|
|
version: int;
|
|
content: string;
|
|
}
|
|
(** Current state of a document. *)
|
|
|
|
(** 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. *)
|
|
class virtual server =
|
|
object (self)
|
|
inherit base_server
|
|
val mutable _quit = false
|
|
val docs : (DocumentUri.t, doc_state) Hashtbl.t = Hashtbl.create 16
|
|
method! must_quit = _quit
|
|
|
|
method find_doc (uri : DocumentUri.t) : doc_state option =
|
|
try Some (Hashtbl.find docs uri) with Not_found -> None
|
|
(** Find current state of the given document, if present. *)
|
|
|
|
method on_request_unhandled : type r.
|
|
notify_back:notify_back ->
|
|
id:Req_id.t ->
|
|
r Lsp.Client_request.t ->
|
|
r IO.t =
|
|
fun ~notify_back:(_ : notify_back) ~id:_ _r ->
|
|
Log.debug (fun k -> k "req: unhandled request");
|
|
IO.failwith "TODO: handle this request"
|
|
(** Override to process other requests *)
|
|
|
|
method config_sync_opts : TextDocumentSyncOptions.t =
|
|
TextDocumentSyncOptions.create ~change:TextDocumentSyncKind.Incremental
|
|
~openClose:true
|
|
~save:(`SaveOptions (SaveOptions.create ~includeText:false ()))
|
|
~willSave:false ()
|
|
(** Parameter for how to synchronize content with the editor *)
|
|
|
|
method config_completion : CompletionOptions.t option = None
|
|
(** Configuration for the completion API.
|
|
@since 0.4 *)
|
|
|
|
method config_code_lens_options : CodeLensOptions.t option = None
|
|
(** @since 0.3 *)
|
|
|
|
method config_definition
|
|
: [ `Bool of bool | `DefinitionOptions of DefinitionOptions.t ] option
|
|
=
|
|
None
|
|
(** @since 0.3 *)
|
|
|
|
method config_hover
|
|
: [ `Bool of bool | `HoverOptions of HoverOptions.t ] option =
|
|
None
|
|
(** @since 0.3 *)
|
|
|
|
method config_symbol
|
|
: [ `Bool of bool
|
|
| `DocumentSymbolOptions of DocumentSymbolOptions.t
|
|
]
|
|
option =
|
|
None
|
|
(** @since 0.3 *)
|
|
|
|
method config_code_action_provider
|
|
: [ `CodeActionOptions of CodeActionOptions.t | `Bool of bool ] =
|
|
`Bool false
|
|
(** @since 0.3 *)
|
|
|
|
method config_modify_capabilities (c : ServerCapabilities.t)
|
|
: ServerCapabilities.t =
|
|
c
|
|
(** Modify capabilities before sending them back to the client.
|
|
By default we just return them unmodified.
|
|
@since 0.3 *)
|
|
|
|
method config_list_commands : string list = []
|
|
(** List of commands available *)
|
|
|
|
method on_req_initialize ~notify_back:(_ : notify_back)
|
|
(_i : InitializeParams.t) : InitializeResult.t IO.t =
|
|
let sync_opts = self#config_sync_opts in
|
|
let capabilities =
|
|
ServerCapabilities.create
|
|
?codeLensProvider:self#config_code_lens_options
|
|
~codeActionProvider:self#config_code_action_provider
|
|
~executeCommandProvider:
|
|
(ExecuteCommandOptions.create ~commands:self#config_list_commands
|
|
())
|
|
?completionProvider:self#config_completion
|
|
?definitionProvider:self#config_definition
|
|
?hoverProvider:self#config_hover
|
|
?documentSymbolProvider:self#config_symbol
|
|
~textDocumentSync:(`TextDocumentSyncOptions sync_opts) ()
|
|
|> self#config_modify_capabilities
|
|
in
|
|
IO.return @@ InitializeResult.create ~capabilities ()
|
|
|
|
method on_req_hover ~notify_back:(_ : notify_back) ~id:_ ~uri:_ ~pos:_
|
|
~workDoneToken:_ (_ : doc_state) : Hover.t option IO.t =
|
|
IO.return None
|
|
(** Called when the user hovers on some identifier in the document *)
|
|
|
|
method on_req_completion ~notify_back:(_ : notify_back) ~id:_ ~uri:_
|
|
~pos:_ ~ctx:_ ~workDoneToken:_ ~partialResultToken:_ (_ : doc_state)
|
|
: [ `CompletionList of CompletionList.t
|
|
| `List of CompletionItem.t list
|
|
]
|
|
option
|
|
IO.t =
|
|
IO.return None
|
|
(** Called when the user requests completion in the document *)
|
|
|
|
method on_req_definition ~notify_back:(_ : notify_back) ~id:_ ~uri:_
|
|
~pos:_ ~workDoneToken:_ ~partialResultToken:_ (_ : doc_state)
|
|
: Locations.t option IO.t =
|
|
IO.return None
|
|
(** Called when the user wants to jump-to-definition *)
|
|
|
|
method on_req_code_lens ~notify_back:(_ : notify_back) ~id:_ ~uri:_
|
|
~workDoneToken:_ ~partialResultToken:_ (_ : doc_state)
|
|
: CodeLens.t list IO.t =
|
|
IO.return []
|
|
(** List code lenses for the given document
|
|
@since 0.3 *)
|
|
|
|
method on_req_code_lens_resolve ~notify_back:(_ : notify_back) ~id:_
|
|
(cl : CodeLens.t) : CodeLens.t IO.t =
|
|
IO.return cl
|
|
(** Code lens resolution, must return a code lens with non null "command"
|
|
@since 0.3 *)
|
|
|
|
method on_req_code_action ~notify_back:(_ : notify_back) ~id:_
|
|
(_c : CodeActionParams.t) : CodeActionResult.t IO.t =
|
|
IO.return None
|
|
(** Code action.
|
|
@since 0.3 *)
|
|
|
|
method on_req_execute_command ~notify_back:(_ : notify_back) ~id:_
|
|
~workDoneToken:_ (_c : string) (_args : Yojson.Safe.t list option)
|
|
: Yojson.Safe.t IO.t =
|
|
IO.return `Null
|
|
(** Execute a command with given arguments.
|
|
@since 0.3 *)
|
|
|
|
method on_req_symbol ~notify_back:(_ : notify_back) ~id:_ ~uri:_
|
|
~workDoneToken:_ ~partialResultToken:_ ()
|
|
: [ `DocumentSymbol of DocumentSymbol.t list
|
|
| `SymbolInformation of SymbolInformation.t list
|
|
]
|
|
option
|
|
IO.t =
|
|
IO.return None
|
|
(** List symbols in this document.
|
|
@since 0.3 *)
|
|
|
|
method on_request : type r.
|
|
notify_back:_ ->
|
|
server_request:_ ->
|
|
id:Req_id.t ->
|
|
r Lsp.Client_request.t ->
|
|
r IO.t =
|
|
fun ~notify_back ~server_request ~id (r : _ Lsp.Client_request.t) ->
|
|
Log.debug (fun k ->
|
|
k "handle request[id=%s] <opaque>" (Req_id.to_string id));
|
|
|
|
match r with
|
|
| Lsp.Client_request.Shutdown ->
|
|
Log.info (fun k -> k "shutdown");
|
|
_quit <- true;
|
|
IO.return ()
|
|
| Lsp.Client_request.Initialize i ->
|
|
Log.debug (fun k -> k "req: initialize");
|
|
let notify_back =
|
|
new notify_back
|
|
~partialResultToken:None ~workDoneToken:i.workDoneToken
|
|
~notify_back ~server_request ()
|
|
in
|
|
self#on_req_initialize ~notify_back i
|
|
| Lsp.Client_request.TextDocumentHover
|
|
{ textDocument; position; workDoneToken } ->
|
|
let uri = textDocument.uri in
|
|
Log.debug (fun k -> k "req: hover '%s'" (DocumentUri.to_path uri));
|
|
|
|
(match Hashtbl.find_opt docs uri with
|
|
| None -> IO.return None
|
|
| Some doc_st ->
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken ~partialResultToken:None ~uri ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_req_hover ~notify_back ~id ~uri ~pos:position
|
|
~workDoneToken doc_st)
|
|
| Lsp.Client_request.TextDocumentCompletion
|
|
{
|
|
textDocument;
|
|
position;
|
|
context;
|
|
workDoneToken;
|
|
partialResultToken;
|
|
} ->
|
|
let uri = textDocument.uri in
|
|
Log.debug (fun k ->
|
|
k "req: complete '%s'" (DocumentUri.to_path uri));
|
|
(match Hashtbl.find_opt docs uri with
|
|
| None -> IO.return None
|
|
| Some doc_st ->
|
|
let notify_back =
|
|
new notify_back
|
|
~partialResultToken ~workDoneToken ~uri ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_req_completion ~notify_back ~id ~uri ~workDoneToken
|
|
~partialResultToken ~pos:position ~ctx:context doc_st)
|
|
| Lsp.Client_request.TextDocumentDefinition
|
|
{ textDocument; position; workDoneToken; partialResultToken } ->
|
|
let uri = textDocument.uri in
|
|
Log.debug (fun k ->
|
|
k "req: definition '%s'" (DocumentUri.to_path uri));
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken ~partialResultToken ~uri ~notify_back
|
|
~server_request ()
|
|
in
|
|
|
|
(match Hashtbl.find_opt docs uri with
|
|
| None -> IO.return None
|
|
| Some doc_st ->
|
|
self#on_req_definition ~notify_back ~id ~workDoneToken
|
|
~partialResultToken ~uri ~pos:position doc_st)
|
|
| Lsp.Client_request.TextDocumentCodeLens
|
|
{ textDocument; workDoneToken; partialResultToken } ->
|
|
let uri = textDocument.uri in
|
|
Log.debug (fun k ->
|
|
k "req: codelens '%s'" (DocumentUri.to_path uri));
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken ~partialResultToken ~uri ~notify_back
|
|
~server_request ()
|
|
in
|
|
|
|
(match Hashtbl.find_opt docs uri with
|
|
| None -> IO.return []
|
|
| Some doc_st ->
|
|
self#on_req_code_lens ~notify_back ~id ~uri ~workDoneToken
|
|
~partialResultToken doc_st)
|
|
| Lsp.Client_request.TextDocumentCodeLensResolve cl ->
|
|
Log.debug (fun k -> k "req: codelens resolve");
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:None ~partialResultToken:None ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_req_code_lens_resolve ~notify_back ~id cl
|
|
| Lsp.Client_request.ExecuteCommand
|
|
{ command; arguments; workDoneToken } ->
|
|
Log.debug (fun k -> k "req: execute command '%s'" command);
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken ~partialResultToken:None ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_req_execute_command ~notify_back ~id ~workDoneToken command
|
|
arguments
|
|
| Lsp.Client_request.DocumentSymbol
|
|
{ textDocument = d; workDoneToken; partialResultToken } ->
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken ~partialResultToken ~notify_back ~server_request
|
|
()
|
|
in
|
|
self#on_req_symbol ~notify_back ~id ~uri:d.uri ~workDoneToken
|
|
~partialResultToken ()
|
|
| Lsp.Client_request.CodeAction a ->
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:a.workDoneToken
|
|
~partialResultToken:a.partialResultToken ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_req_code_action ~notify_back ~id a
|
|
| Lsp.Client_request.CodeActionResolve _
|
|
| Lsp.Client_request.LinkedEditingRange _
|
|
| Lsp.Client_request.TextDocumentDeclaration _
|
|
| Lsp.Client_request.TextDocumentTypeDefinition _
|
|
| Lsp.Client_request.TextDocumentPrepareRename _
|
|
| Lsp.Client_request.TextDocumentRename _
|
|
| Lsp.Client_request.TextDocumentLink _
|
|
| Lsp.Client_request.TextDocumentLinkResolve _
|
|
| 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.CompletionItemResolve _
|
|
| Lsp.Client_request.WillSaveWaitUntilTextDocument _
|
|
| Lsp.Client_request.TextDocumentFormatting _
|
|
| Lsp.Client_request.TextDocumentMoniker _
|
|
| Lsp.Client_request.TextDocumentOnTypeFormatting _
|
|
| Lsp.Client_request.TextDocumentColorPresentation _
|
|
| Lsp.Client_request.TextDocumentColor _
|
|
| Lsp.Client_request.SelectionRange _
|
|
| Lsp.Client_request.SemanticTokensDelta _
|
|
| Lsp.Client_request.SemanticTokensFull _
|
|
| Lsp.Client_request.SemanticTokensRange _
|
|
| Lsp.Client_request.TextDocumentImplementation _
|
|
| Lsp.Client_request.TextDocumentPrepareCallHierarchy _
|
|
| Lsp.Client_request.TextDocumentRangeFormatting _
|
|
| Lsp.Client_request.CallHierarchyIncomingCalls _
|
|
| Lsp.Client_request.CallHierarchyOutgoingCalls _
|
|
| Lsp.Client_request.WillCreateFiles _
|
|
| Lsp.Client_request.WillDeleteFiles _
|
|
| Lsp.Client_request.WillRenameFiles _
|
|
| Lsp.Client_request.UnknownRequest _ ->
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:None ~partialResultToken:None ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_request_unhandled ~notify_back ~id r
|
|
|
|
method virtual on_notif_doc_did_open
|
|
: notify_back:notify_back ->
|
|
TextDocumentItem.t ->
|
|
content:string ->
|
|
unit IO.t
|
|
(** Called when a document is opened *)
|
|
|
|
method virtual on_notif_doc_did_close
|
|
: notify_back:notify_back -> TextDocumentIdentifier.t -> unit IO.t
|
|
|
|
method virtual on_notif_doc_did_change
|
|
: notify_back:notify_back ->
|
|
VersionedTextDocumentIdentifier.t ->
|
|
TextDocumentContentChangeEvent.t list ->
|
|
old_content:string ->
|
|
new_content:string ->
|
|
unit IO.t
|
|
(** Called when the document changes. *)
|
|
|
|
method on_notification_unhandled ~notify_back:(_ : notify_back)
|
|
(_n : Lsp.Client_notification.t) : unit IO.t =
|
|
IO.return ()
|
|
(** Override to handle unprocessed notifications *)
|
|
|
|
method on_notification ~notify_back ~server_request
|
|
(n : Lsp.Client_notification.t) : unit IO.t =
|
|
let open Lsp.Types in
|
|
match n with
|
|
| Lsp.Client_notification.TextDocumentDidOpen
|
|
{ DidOpenTextDocumentParams.textDocument = doc } ->
|
|
Log.debug (fun k ->
|
|
k "notif: did open '%s'" (DocumentUri.to_path doc.uri));
|
|
let notify_back =
|
|
new notify_back
|
|
~uri:doc.uri ~workDoneToken:None ~partialResultToken:None
|
|
~version:doc.version ~notify_back ~server_request ()
|
|
in
|
|
let st =
|
|
{
|
|
uri = doc.uri;
|
|
version = doc.version;
|
|
content = doc.text;
|
|
languageId = doc.languageId;
|
|
}
|
|
in
|
|
Hashtbl.replace docs doc.uri st;
|
|
self#on_notif_doc_did_open
|
|
~notify_back:(notify_back : notify_back)
|
|
doc ~content:st.content
|
|
| Lsp.Client_notification.TextDocumentDidClose { textDocument = doc } ->
|
|
Log.debug (fun k ->
|
|
k "notif: did close '%s'" (DocumentUri.to_path doc.uri));
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:None ~partialResultToken:None ~uri:doc.uri
|
|
~notify_back ~server_request ()
|
|
in
|
|
self#on_notif_doc_did_close
|
|
~notify_back:(notify_back : notify_back)
|
|
doc
|
|
| Lsp.Client_notification.TextDocumentDidChange
|
|
{ textDocument = doc; contentChanges = c } ->
|
|
Log.debug (fun k ->
|
|
k "notif: did change '%s'" (DocumentUri.to_path doc.uri));
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:None ~partialResultToken:None ~uri:doc.uri
|
|
~notify_back ~server_request ()
|
|
in
|
|
|
|
let old_doc =
|
|
match Hashtbl.find_opt docs doc.uri with
|
|
| None ->
|
|
(* WTF vscode. Well let's try and deal with it. *)
|
|
Log.err (fun k ->
|
|
k "unknown document: '%s'" (DocumentUri.to_path doc.uri));
|
|
let version = doc.version in
|
|
|
|
let languageId = "" in
|
|
(* FIXME*)
|
|
Lsp.Text_document.make
|
|
(DidOpenTextDocumentParams.create
|
|
~textDocument:
|
|
(TextDocumentItem.create ~languageId ~uri:doc.uri ~version
|
|
~text:""))
|
|
| Some st ->
|
|
Lsp.Text_document.make
|
|
(DidOpenTextDocumentParams.create
|
|
~textDocument:
|
|
(TextDocumentItem.create ~languageId:st.languageId
|
|
~uri:doc.uri ~version:st.version ~text:st.content))
|
|
in
|
|
|
|
let new_doc : Lsp.Text_document.t =
|
|
List.fold_left
|
|
(fun d ev -> Lsp.Text_document.apply_content_change d ev)
|
|
old_doc c
|
|
in
|
|
|
|
let new_st : doc_state =
|
|
{
|
|
uri = doc.uri;
|
|
languageId = Lsp.Text_document.languageId new_doc;
|
|
content = Lsp.Text_document.text new_doc;
|
|
version = Lsp.Text_document.version new_doc;
|
|
}
|
|
in
|
|
|
|
Hashtbl.replace docs doc.uri new_st;
|
|
self#on_notif_doc_did_change
|
|
~notify_back:(notify_back : notify_back)
|
|
doc c
|
|
~old_content:(Lsp.Text_document.text old_doc)
|
|
~new_content:new_st.content
|
|
| 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.UnknownNotification _
|
|
| Lsp.Client_notification.CancelRequest _
|
|
| Lsp.Client_notification.WorkDoneProgressCancel _
|
|
| Lsp.Client_notification.SetTrace _
|
|
| Lsp.Client_notification.DidChangeWatchedFiles _
|
|
| Lsp.Client_notification.DidCreateFiles _
|
|
| Lsp.Client_notification.DidDeleteFiles _
|
|
| Lsp.Client_notification.DidRenameFiles _
|
|
| Lsp.Client_notification.LogTrace _ ->
|
|
let notify_back =
|
|
new notify_back
|
|
~workDoneToken:None ~partialResultToken:None ~notify_back
|
|
~server_request ()
|
|
in
|
|
self#on_notification_unhandled
|
|
~notify_back:(notify_back : notify_back)
|
|
n
|
|
end
|
|
end
|