chore: refactor jsonrpc and lsp handle functions and deal with fallout
This commit is contained in:
parent
975b344278
commit
6248e6329e
4 changed files with 212 additions and 168 deletions
|
|
@ -75,20 +75,38 @@ inductive Message where
|
|||
|
||||
def Batch := Array Message
|
||||
|
||||
-- data types for reading expected messages
|
||||
structure Request (α : Type) where
|
||||
id : RequestID
|
||||
param : α
|
||||
-- Compound type with simplified APIs for passing around
|
||||
-- jsonrpc data
|
||||
structure Request (α) where
|
||||
id : RequestID
|
||||
method : String
|
||||
param : α
|
||||
|
||||
structure Response (α : Type) where
|
||||
id : RequestID
|
||||
instance [ToJson α] : Coe (Request α) Message :=
|
||||
⟨fun r => Message.request r.id r.method (toStructured? r.param)⟩
|
||||
|
||||
structure Notification (α) where
|
||||
method : String
|
||||
param : α
|
||||
|
||||
instance [ToJson α] : Coe (Notification α) Message :=
|
||||
⟨fun r => Message.notification r.method (toStructured? r.param)⟩
|
||||
|
||||
structure Response (α) where
|
||||
id : RequestID
|
||||
result : α
|
||||
|
||||
structure Error where
|
||||
id : RequestID
|
||||
code : JsonNumber
|
||||
instance [ToJson α] : Coe (Response α) Message :=
|
||||
⟨fun r => Message.response r.id (toJson r.result)⟩
|
||||
|
||||
structure ResponseError (α) where
|
||||
id : RequestID
|
||||
code : ErrorCode
|
||||
message : String
|
||||
data? : Option Json
|
||||
data? : Option α := none
|
||||
|
||||
instance [ToJson α] : Coe (ResponseError α) Message :=
|
||||
⟨fun r => Message.responseError r.id r.code r.message (r.data?.map toJson)⟩
|
||||
|
||||
instance : Coe String RequestID := ⟨RequestID.str⟩
|
||||
instance : Coe JsonNumber RequestID := ⟨RequestID.num⟩
|
||||
|
|
@ -168,62 +186,69 @@ namespace IO.FS.Stream
|
|||
|
||||
open Lean
|
||||
open Lean.JsonRpc
|
||||
open IO
|
||||
|
||||
def readMessage (h : FS.Stream) (nBytes : Nat) : IO Message := do
|
||||
let j ← h.readJson nBytes
|
||||
match fromJson? j with
|
||||
| some m => pure m
|
||||
| none => throw $ userError ("JSON '" ++ j.compress ++ "' did not have the format of a JSON-RPC message")
|
||||
section
|
||||
variables (h : FS.Stream) (nBytes : Nat) (expectedMethod : String) (α) [FromJson α]
|
||||
|
||||
def readRequestAs (h : FS.Stream) (nBytes : Nat) (expectedMethod : String) (α : Type) [FromJson α] : IO (Request α) := do
|
||||
let m ← h.readMessage nBytes
|
||||
match m with
|
||||
| Message.request id method params? =>
|
||||
if method = expectedMethod then
|
||||
match params? with
|
||||
| some params =>
|
||||
let j := toJson params
|
||||
match fromJson? j with
|
||||
| some v => pure ⟨id, v⟩
|
||||
| none => throw $ userError ("unexpected param '" ++ j.compress ++ "' for method '" ++ expectedMethod ++ "'")
|
||||
| none => throw $ userError ("unexpected lack of param for method '" ++ expectedMethod ++ "'")
|
||||
else
|
||||
throw $ userError ("expected method '" ++ expectedMethod ++ "', got method '" ++ method ++ "'")
|
||||
| _ => throw $ userError "expected request, got other type of message"
|
||||
def readMessage : IO Message := do
|
||||
let j ← h.readJson nBytes
|
||||
match fromJson? j with
|
||||
| some m => pure m
|
||||
| none => throw $ userError ("JSON '" ++ j.compress ++ "' did not have the format of a JSON-RPC message")
|
||||
|
||||
def readNotificationAs (h : FS.Stream) (nBytes : Nat) (expectedMethod : String) (α : Type) [FromJson α] : IO α := do
|
||||
let m ← h.readMessage nBytes
|
||||
match m with
|
||||
| Message.notification method params? =>
|
||||
if method = expectedMethod then
|
||||
match params? with
|
||||
| some params =>
|
||||
let j := toJson params
|
||||
match fromJson? j with
|
||||
| some v => pure v
|
||||
| none => throw $ userError ("unexpected param '" ++ j.compress ++ "' for method '" ++ expectedMethod ++ "'")
|
||||
| none => throw $ userError ("unexpected lack of param for method '" ++ expectedMethod ++ "'")
|
||||
else
|
||||
throw $ userError ("expected method '" ++ expectedMethod ++ "', got method '" ++ method ++ "'")
|
||||
| _ => throw $ userError "expected notification, got other type of message"
|
||||
def readRequestAs : IO (Request α) := do
|
||||
let m ← h.readMessage nBytes
|
||||
match m with
|
||||
| Message.request id method params? =>
|
||||
if method = expectedMethod then
|
||||
match params? with
|
||||
| some params =>
|
||||
let j := toJson params
|
||||
match fromJson? j with
|
||||
| some v => pure ⟨id, expectedMethod, v⟩
|
||||
| none => throw $ userError ("unexpected param '" ++ j.compress ++ "' for method '" ++ expectedMethod ++ "'")
|
||||
| none => throw $ userError ("unexpected lack of param for method '" ++ expectedMethod ++ "'")
|
||||
else
|
||||
throw $ userError ("expected method '" ++ expectedMethod ++ "', got method '" ++ method ++ "'")
|
||||
| _ => throw $ userError "expected request, got other type of message"
|
||||
|
||||
def writeMessage (h : FS.Stream) (m : Message) : IO Unit :=
|
||||
h.writeJson (toJson m)
|
||||
def readNotificationAs : IO (Notification α) := do
|
||||
let m ← h.readMessage nBytes
|
||||
match m with
|
||||
| Message.notification method params? =>
|
||||
if method = expectedMethod then
|
||||
match params? with
|
||||
| some params =>
|
||||
let j := toJson params
|
||||
match fromJson? j with
|
||||
| some v => pure ⟨expectedMethod, v⟩
|
||||
| none => throw $ userError ("unexpected param '" ++ j.compress ++ "' for method '" ++ expectedMethod ++ "'")
|
||||
| none => throw $ userError ("unexpected lack of param for method '" ++ expectedMethod ++ "'")
|
||||
else
|
||||
throw $ userError ("expected method '" ++ expectedMethod ++ "', got method '" ++ method ++ "'")
|
||||
| _ => throw $ userError "expected notification, got other type of message"
|
||||
end
|
||||
|
||||
def writeRequest [ToJson α] (h : FS.Stream) (id : RequestID) (method : String) (params : α) : IO Unit :=
|
||||
h.writeMessage (Message.request id method (fromJson? (toJson params)))
|
||||
section
|
||||
variables [ToJson α] (h : FS.Stream)
|
||||
|
||||
def writeNotification [ToJson α] (h : FS.Stream) (method : String) (params : α) : IO Unit :=
|
||||
h.writeMessage (Message.notification method (fromJson? (toJson params)))
|
||||
def writeMessage (m : Message) : IO Unit :=
|
||||
h.writeJson (toJson m)
|
||||
|
||||
def writeResponse [ToJson α] (h : FS.Stream) (id : RequestID) (r : α) : IO Unit :=
|
||||
h.writeMessage (Message.response id (toJson r))
|
||||
def writeRequest (r : Request α) : IO Unit :=
|
||||
h.writeMessage r
|
||||
|
||||
def writeResponseError (h : FS.Stream) (id : RequestID) (code : ErrorCode) (message : String) : IO Unit :=
|
||||
h.writeMessage (Message.responseError id code message none)
|
||||
def writeNotification (n : Notification α) : IO Unit :=
|
||||
h.writeMessage n
|
||||
|
||||
def writeResponseErrorWithData [ToJson α] (h : FS.Stream) (id : RequestID) (code : ErrorCode) (message : String) (data : α) : IO Unit :=
|
||||
h.writeMessage (Message.responseError id code message (toJson data))
|
||||
def writeResponse (r : Response α) : IO Unit :=
|
||||
h.writeMessage r
|
||||
|
||||
def writeResponseError (e : ResponseError Unit) : IO Unit :=
|
||||
h.writeMessage (Message.responseError e.id e.code e.message none)
|
||||
|
||||
def writeResponseErrorWithData (e : ResponseError α) : IO Unit :=
|
||||
h.writeMessage e
|
||||
end
|
||||
|
||||
end IO.FS.Stream
|
||||
|
|
|
|||
|
|
@ -9,76 +9,82 @@ import Lean.Data.JsonRpc
|
|||
|
||||
/-! Reading/writing LSP messages from/to IO handles. -/
|
||||
|
||||
namespace Lean
|
||||
namespace Lsp
|
||||
namespace IO.FS.Stream
|
||||
|
||||
open IO
|
||||
open JsonRpc
|
||||
open Lean
|
||||
open Lean.JsonRpc
|
||||
|
||||
private def parseHeaderField (s : String) : Option (String × String) := do
|
||||
guard $ s ≠ "" ∧ s.takeRight 2 = "\r\n"
|
||||
let xs := (s.dropRight 2).splitOn ": "
|
||||
match xs with
|
||||
| [] => none
|
||||
| [_] => none
|
||||
| name :: value :: rest =>
|
||||
let value := ": ".intercalate (value :: rest)
|
||||
some ⟨name, value⟩
|
||||
section
|
||||
variables (h : FS.Stream) (expectedMethod : String) (α) [FromJson α]
|
||||
|
||||
private partial def readHeaderFields (h : FS.Stream) : IO (List (String × String)) := do
|
||||
let l ← h.getLine
|
||||
if l = "\r\n" then
|
||||
pure []
|
||||
else
|
||||
match parseHeaderField l with
|
||||
| some hf =>
|
||||
let tail ← readHeaderFields h
|
||||
pure (hf :: tail)
|
||||
| none => throw $ userError ("Invalid header field: " ++ l)
|
||||
private def parseHeaderField (s : String) : Option (String × String) := do
|
||||
guard $ s ≠ "" ∧ s.takeRight 2 = "\r\n"
|
||||
let xs := (s.dropRight 2).splitOn ": "
|
||||
match xs with
|
||||
| [] => none
|
||||
| [_] => none
|
||||
| name :: value :: rest =>
|
||||
let value := ": ".intercalate (value :: rest)
|
||||
some ⟨name, value⟩
|
||||
|
||||
/-- Returns the Content-Length. -/
|
||||
private def readLspHeader (h : FS.Stream) : IO Nat := do
|
||||
let fields ← readHeaderFields h
|
||||
match fields.lookup "Content-Length" with
|
||||
| some length => match length.toNat? with
|
||||
| some n => pure n
|
||||
| none => throw $ userError ("Content-Length header value '" ++ length ++ "' is not a Nat")
|
||||
| none => throw $ userError ("No Content-Length header in header fields: " ++ toString fields)
|
||||
private partial def readHeaderFields : IO (List (String × String)) := do
|
||||
let l ← h.getLine
|
||||
if l = "\r\n" then
|
||||
pure []
|
||||
else
|
||||
match parseHeaderField l with
|
||||
| some hf =>
|
||||
let tail ← readHeaderFields
|
||||
pure (hf :: tail)
|
||||
| none => throw $ userError ("Invalid header field: " ++ l)
|
||||
|
||||
def readLspMessage (h : FS.Stream) : IO Message := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readMessage nBytes
|
||||
/-- Returns the Content-Length. -/
|
||||
private def readLspHeader : IO Nat := do
|
||||
let fields ← readHeaderFields h
|
||||
match fields.lookup "Content-Length" with
|
||||
| some length => match length.toNat? with
|
||||
| some n => pure n
|
||||
| none => throw $ userError ("Content-Length header value '" ++ length ++ "' is not a Nat")
|
||||
| none => throw $ userError ("No Content-Length header in header fields: " ++ toString fields)
|
||||
|
||||
def readLspRequestAs (h : FS.Stream) (expectedMethod : String) (α : Type) [FromJson α] : IO (Request α) := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readRequestAs nBytes expectedMethod α
|
||||
def readLspMessage : IO Message := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readMessage nBytes
|
||||
|
||||
def readLspNotificationAs (h : FS.Stream) (expectedMethod : String) (α : Type) [FromJson α] : IO α := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readNotificationAs nBytes expectedMethod α
|
||||
def readLspRequestAs : IO (Request α) := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readRequestAs nBytes expectedMethod α
|
||||
|
||||
def writeLspMessage (h : FS.Stream) (msg : Message) : IO Unit := do
|
||||
-- inlined implementation instead of using jsonrpc's writeMessage
|
||||
-- to maintain the atomicity of putStr
|
||||
let j := (toJson msg).compress
|
||||
let header := "Content-Length: " ++ toString j.utf8ByteSize ++ "\r\n\r\n"
|
||||
h.putStr (header ++ j)
|
||||
h.flush
|
||||
def readLspNotificationAs : IO (Notification α) := do
|
||||
let nBytes ← readLspHeader h
|
||||
h.readNotificationAs nBytes expectedMethod α
|
||||
end
|
||||
|
||||
def writeLspRequest [ToJson α] (h : FS.Stream) (id : RequestID) (method : String) (params : α) : IO Unit :=
|
||||
writeLspMessage h (Message.request id method (fromJson? (toJson params)))
|
||||
section
|
||||
variables [ToJson α] (h : FS.Stream)
|
||||
|
||||
def writeLspNotification [ToJson α] (h : FS.Stream) (method : String) (r : α) : IO Unit :=
|
||||
writeLspMessage h (Message.notification method (fromJson? (toJson r)))
|
||||
def writeLspMessage (h : FS.Stream) (msg : Message) : IO Unit := do
|
||||
-- inlined implementation instead of using jsonrpc's writeMessage
|
||||
-- to maintain the atomicity of putStr
|
||||
let j := (toJson msg).compress
|
||||
let header := "Content-Length: " ++ toString j.utf8ByteSize ++ "\r\n\r\n"
|
||||
h.putStr (header ++ j)
|
||||
h.flush
|
||||
|
||||
def writeLspResponse [ToJson α] (h : FS.Stream) (id : RequestID) (r : α) : IO Unit :=
|
||||
writeLspMessage h (Message.response id (toJson r))
|
||||
def writeLspRequest (r : Request α): IO Unit :=
|
||||
h.writeLspMessage r
|
||||
|
||||
def writeLspResponseError (h : FS.Stream) (id : RequestID) (code : ErrorCode) (message : String) : IO Unit :=
|
||||
writeLspMessage h (Message.responseError id code message none)
|
||||
def writeLspNotification (n : Notification α) : IO Unit :=
|
||||
h.writeLspMessage n
|
||||
|
||||
def writeLspResponseErrorWithData [ToJson α] (h : FS.Stream) (id : RequestID) (code : ErrorCode) (message : String) (data : α) : IO Unit :=
|
||||
writeLspMessage h (Message.responseError id code message (toJson data))
|
||||
def writeLspResponse (r : Response α) : IO Unit :=
|
||||
h.writeLspMessage r
|
||||
|
||||
end Lsp
|
||||
end Lean
|
||||
def writeLspResponseError (e : ResponseError Unit) : IO Unit :=
|
||||
h.writeLspMessage (Message.responseError e.id e.code e.message none)
|
||||
|
||||
def writeLspResponseErrorWithData (e : ResponseError α) : IO Unit :=
|
||||
h.writeLspMessage e
|
||||
end
|
||||
|
||||
end IO.FS.Stream
|
||||
|
|
|
|||
|
|
@ -47,11 +47,15 @@ open Snapshots
|
|||
private def sendDiagnostics (h : FS.Stream) (uri : DocumentUri) (version : Nat) (text : FileMap) (log : MessageLog)
|
||||
: IO Unit := do
|
||||
let diagnostics ← log.msgs.mapM (msgToDiagnostic text)
|
||||
Lsp.writeLspNotification h "textDocument/publishDiagnostics"
|
||||
{ uri := uri,
|
||||
version? := version,
|
||||
h.writeLspNotification {
|
||||
method := "textDocument/publishDiagnostics"
|
||||
param := {
|
||||
uri := uri
|
||||
version? := version
|
||||
diagnostics := diagnostics.toArray
|
||||
: PublishDiagnosticsParams }
|
||||
: PublishDiagnosticsParams
|
||||
}
|
||||
}
|
||||
|
||||
private def logSnapContent (s : Snapshot) (text : FileMap) : IO Unit :=
|
||||
IO.eprintln s!"[{s.beginPos}, {s.endPos}]: `{text.source.extract s.beginPos (s.endPos-1)}`"
|
||||
|
|
@ -257,7 +261,7 @@ def handleRequest (id : RequestID) (method : String) (params : Json)
|
|||
|
||||
partial def mainLoop : ServerM Unit := do
|
||||
let st ← read
|
||||
let msg ← readLspMessage st.hIn
|
||||
let msg ← st.hIn.readLspMessage
|
||||
let pendingRequests ← st.pendingRequestsRef.get
|
||||
let filterFinishedTasks : PendingRequestMap → RequestID → Task (Except IO.Error Unit)
|
||||
→ ServerM PendingRequestMap := fun acc id task => do
|
||||
|
|
@ -283,8 +287,8 @@ def initAndRunWorker (i o e : FS.Stream) : IO Unit := do
|
|||
let i ← maybeTee "fwIn.txt" false i
|
||||
let o ← maybeTee "fwOut.txt" true o
|
||||
-- TODO(WN): act in accordance with InitializeParams
|
||||
let _ ← Lsp.readLspRequestAs i "initialize" InitializeParams
|
||||
let param ← Lsp.readLspNotificationAs i "textDocument/didOpen" DidOpenTextDocumentParams
|
||||
let _ ← i.readLspRequestAs "initialize" InitializeParams
|
||||
let ⟨_, param⟩ ← i.readLspNotificationAs "textDocument/didOpen" DidOpenTextDocumentParams
|
||||
let e ← e.withPrefix s!"[{param.textDocument.uri}] "
|
||||
let _ ← IO.setStderr e
|
||||
|
||||
|
|
@ -323,4 +327,4 @@ def workerMain : IO UInt32 := do
|
|||
|
||||
end FileWorker
|
||||
end Server
|
||||
end Lean
|
||||
end Lean
|
||||
|
|
|
|||
|
|
@ -69,15 +69,16 @@ open JsonRpc
|
|||
open System.FilePath
|
||||
|
||||
structure OpenDocument where
|
||||
version : Nat
|
||||
text : FileMap
|
||||
version : Nat
|
||||
text : FileMap
|
||||
headerAst : Syntax
|
||||
|
||||
def workerCfg : Process.StdioConfig :=
|
||||
{ stdin := Process.Stdio.piped,
|
||||
stdout := Process.Stdio.piped,
|
||||
/- We pass workers' stderr through to the editor. -/
|
||||
stderr := Process.Stdio.inherit }
|
||||
def workerCfg : Process.StdioConfig := {
|
||||
stdin := Process.Stdio.piped
|
||||
stdout := Process.Stdio.piped
|
||||
/- We pass workers' stderr through to the editor. -/
|
||||
stderr := Process.Stdio.inherit
|
||||
}
|
||||
|
||||
-- Events that a forwarding task of a worker signals to the main task
|
||||
inductive WorkerEvent where
|
||||
|
|
@ -98,10 +99,10 @@ inductive WorkerState where
|
|||
abbrev PendingRequestMap := RBMap RequestID JsonRpc.Message (fun a b => Decidable.decide (a < b))
|
||||
|
||||
structure FileWorker where
|
||||
doc : OpenDocument
|
||||
proc : Process.Child workerCfg
|
||||
commTask : Task WorkerEvent
|
||||
state : WorkerState
|
||||
doc : OpenDocument
|
||||
proc : Process.Child workerCfg
|
||||
commTask : Task WorkerEvent
|
||||
state : WorkerState
|
||||
-- NOTE: this should not be mutated outside of namespace FileWorker
|
||||
pendingRequestsRef : IO.Ref PendingRequestMap
|
||||
|
||||
|
|
@ -114,7 +115,7 @@ def stdout (fw : FileWorker) : FS.Stream :=
|
|||
FS.Stream.ofHandle fw.proc.stdout
|
||||
|
||||
def readMessage (fw : FileWorker) : IO JsonRpc.Message := do
|
||||
let msg ← readLspMessage fw.stdout
|
||||
let msg ← fw.stdout.readLspMessage
|
||||
match msg with
|
||||
| Message.request id method params? =>
|
||||
fw.pendingRequestsRef.modify (fun pendingRequests => pendingRequests.erase id)
|
||||
|
|
@ -122,19 +123,18 @@ def readMessage (fw : FileWorker) : IO JsonRpc.Message := do
|
|||
msg
|
||||
|
||||
def writeMessage (fw : FileWorker) (msg : JsonRpc.Message) : IO Unit :=
|
||||
writeLspMessage fw.stdin msg
|
||||
fw.stdin.writeLspMessage msg
|
||||
|
||||
def writeNotification [ToJson α] (fw : FileWorker) (method : String) (param : α) : IO Unit :=
|
||||
writeLspNotification fw.stdin method param
|
||||
def writeNotification [ToJson α] (fw : FileWorker) (n : Notification α) : IO Unit :=
|
||||
fw.stdin.writeLspNotification n
|
||||
|
||||
def writeRequest [ToJson α] (fw : FileWorker) (id : RequestID) (method : String) (param : α) : IO Unit := do
|
||||
writeLspRequest fw.stdin id method param
|
||||
fw.pendingRequestsRef.modify $ fun pendingRequests =>
|
||||
pendingRequests.insert id (Message.request id method (Json.toStructured? param))
|
||||
def writeRequest [ToJson α] (fw : FileWorker) (r : Request α) : IO Unit := do
|
||||
fw.stdin.writeLspRequest r
|
||||
fw.pendingRequestsRef.modify (fun pendingRequests => pendingRequests.insert r.id r)
|
||||
|
||||
def errorPendingRequests (fw : FileWorker) (hOut : FS.Stream) (code : ErrorCode) (msg : String) : IO Unit := do
|
||||
let pendingRequests ← fw.pendingRequestsRef.modifyGet (fun pendingRequests => (pendingRequests, RBMap.empty))
|
||||
pendingRequests.forM (fun id _ => writeLspResponseError hOut id code msg)
|
||||
pendingRequests.forM (fun id _ => hOut.writeLspResponseError {id := id, code := code, message := msg})
|
||||
|
||||
end FileWorker
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ partial def fwdMsgTask (fw : FileWorker) : ServerM (Task WorkerEvent) := do
|
|||
try
|
||||
let msg ← fw.readMessage
|
||||
-- NOTE: Writes to Lean I/O channels are atomic, so these won't trample on each other.
|
||||
writeLspMessage o msg
|
||||
o.writeLspMessage msg
|
||||
loop
|
||||
catch err =>
|
||||
-- NOTE: if writeLspMessage from above errors we will block here, but the main task will
|
||||
|
|
@ -218,14 +218,17 @@ def startFileWorker (uri : DocumentUri) (version : Nat) (text : FileMap) : Serve
|
|||
}
|
||||
let commTask ← fwdMsgTask commTaskFw
|
||||
let fw : FileWorker := { commTaskFw with commTask := commTask }
|
||||
writeLspRequest fw.stdin (0 : Nat) "initialize" st.initParams
|
||||
fw.writeNotification "textDocument/didOpen" {
|
||||
textDocument := {
|
||||
uri := uri
|
||||
languageId := "lean"
|
||||
version := version
|
||||
text := text.source
|
||||
} : DidOpenTextDocumentParams
|
||||
fw.stdin.writeLspRequest ⟨0, "initialize", st.initParams⟩
|
||||
fw.writeNotification {
|
||||
method := "textDocument/didOpen"
|
||||
param := {
|
||||
textDocument := {
|
||||
uri := uri
|
||||
languageId := "lean"
|
||||
version := version
|
||||
text := text.source
|
||||
} : DidOpenTextDocumentParams
|
||||
}
|
||||
}
|
||||
updateFileWorkers uri fw
|
||||
|
||||
|
|
@ -298,7 +301,7 @@ def handleDidChange (p : DidChangeTextDocumentParams) : ServerM Unit := do
|
|||
match fw.state with
|
||||
| WorkerState.crashed queuedMsgs => restartCrashedFileWorker doc.uri newFw (queuedMsgs.push msg)
|
||||
| WorkerState.running =>
|
||||
try fw.writeNotification "textDocument/didChange" p
|
||||
try fw.writeNotification ⟨"textDocument/didChange", p⟩
|
||||
catch _ => handleCrash doc.uri #[msg]
|
||||
|
||||
def handleDidClose (p : DidCloseTextDocumentParams) : ServerM Unit :=
|
||||
|
|
@ -310,7 +313,7 @@ def handleCancelRequest (p : CancelParams) : ServerM Unit := do
|
|||
match fw.state with
|
||||
| WorkerState.crashed queuedMsgs => restartCrashedFileWorker uri fw queuedMsgs
|
||||
| WorkerState.running =>
|
||||
try fw.writeNotification "$/cancelRequest" p
|
||||
try fw.writeNotification ⟨"$/cancelRequest", p⟩
|
||||
catch _ => handleCrash uri #[]
|
||||
|
||||
def handleRequest (id : RequestID) (method : String) (params : Json) : ServerM Unit := do
|
||||
|
|
@ -322,7 +325,7 @@ def handleRequest (id : RequestID) (method : String) (params : Json) : ServerM U
|
|||
match fw.state with
|
||||
| WorkerState.crashed queuedMsgs => restartCrashedFileWorker uri fw (queuedMsgs.push msg)
|
||||
| WorkerState.running =>
|
||||
try fw.writeRequest id method parsedParams
|
||||
try fw.writeRequest ⟨id, method, parsedParams⟩
|
||||
catch _ => handleCrash uri #[msg]
|
||||
match method with
|
||||
| "textDocument/hover" => handle HoverParams
|
||||
|
|
@ -348,7 +351,7 @@ inductive ServerEvent where
|
|||
| ClientError (e : IO.Error)
|
||||
|
||||
def runClientTask : ServerM (Task ServerEvent) := do
|
||||
let readMsg : IO ServerEvent := ServerEvent.ClientMsg (←readLspMessage (←read).hIn)
|
||||
let readMsg : IO ServerEvent := ServerEvent.ClientMsg (←(←read).hIn.readLspMessage)
|
||||
let clientTask ← (←IO.asTask readMsg).map $ fun
|
||||
| Except.ok ev => ev
|
||||
| Except.error e => ServerEvent.ClientError e
|
||||
|
|
@ -369,7 +372,7 @@ partial def mainLoop (clientTask : Task ServerEvent) : ServerM Unit := do
|
|||
match msg with
|
||||
| Message.request id "shutdown" _ =>
|
||||
shutdown
|
||||
writeLspResponse st.hOut id Json.null
|
||||
st.hOut.writeLspResponse ⟨id, Json.null⟩
|
||||
| Message.request id method (some params) =>
|
||||
handleRequest id method (toJson params)
|
||||
let newClientTask ← runClientTask
|
||||
|
|
@ -399,10 +402,10 @@ def mkLeanServerCapabilities : ServerCapabilities :=
|
|||
def initAndRunWatchdogAux : ServerM Unit := do
|
||||
let st ← read
|
||||
try
|
||||
let _ ← readLspNotificationAs st.hIn "initialized" InitializedParams
|
||||
let _ ← st.hIn.readLspNotificationAs "initialized" InitializedParams
|
||||
let clientTask ← runClientTask
|
||||
mainLoop clientTask
|
||||
let Message.notification "exit" none ← readLspMessage st.hIn
|
||||
let Message.notification "exit" none ← st.hIn.readLspMessage
|
||||
| throwServerError "Expected an exit notification"
|
||||
catch err =>
|
||||
shutdown
|
||||
|
|
@ -416,12 +419,18 @@ def initAndRunWatchdog (i o e : FS.Stream) : IO Unit := do
|
|||
let i ← maybeTee "wdIn.txt" false i
|
||||
let o ← maybeTee "wdOut.txt" true o
|
||||
let e ← maybeTee "wdErr.txt" true e
|
||||
let initRequest ← readLspRequestAs i "initialize" InitializeParams
|
||||
writeLspResponse o initRequest.id
|
||||
{ capabilities := mkLeanServerCapabilities,
|
||||
serverInfo? := some { name := "Lean 4 server"
|
||||
version? := "0.0.1" }
|
||||
: InitializeResult }
|
||||
let initRequest ← i.readLspRequestAs "initialize" InitializeParams
|
||||
o.writeLspResponse {
|
||||
id := initRequest.id
|
||||
result := {
|
||||
capabilities := mkLeanServerCapabilities
|
||||
serverInfo? := some {
|
||||
name := "Lean 4 server"
|
||||
version? := "0.0.1"
|
||||
}
|
||||
: InitializeResult
|
||||
}
|
||||
}
|
||||
ReaderT.run
|
||||
initAndRunWatchdogAux
|
||||
{ hIn := i
|
||||
|
|
@ -458,4 +467,4 @@ def watchdogMain : IO UInt32 := do
|
|||
|
||||
end Watchdog
|
||||
end Server
|
||||
end Lean
|
||||
end Lean
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue