lean4-htt/src/Lean/Server/FileWorker/Utils.lean
Wojciech Nawrocki 2f5fbf3b13 feat: support multiple RPC sessions
Motivation: we may want to also use RPC in editor insets, or multiple
webviews in general.
2021-08-24 08:57:41 -07:00

112 lines
3.6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2020 Wojciech Nawrocki. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Nawrocki, Marc Huisinga
-/
import Lean.Server.Utils
import Lean.Server.Snapshots
import Lean.Server.AsyncList
import Lean.Server.Rpc.Basic
namespace Lean.Server.FileWorker
open Snapshots
open IO
def logSnapContent (s : Snapshot) (text : FileMap) : IO Unit :=
IO.eprintln s!"[{s.beginPos}, {s.endPos}]: ```\n{text.source.extract s.beginPos (s.endPos-1)}\n```"
inductive ElabTaskError where
| aborted
| eof
| ioError (e : IO.Error)
instance : Coe IO.Error ElabTaskError :=
⟨ElabTaskError.ioError⟩
structure CancelToken where
ref : IO.Ref Bool
deriving Inhabited
namespace CancelToken
def new : IO CancelToken :=
CancelToken.mk <$> IO.mkRef false
def check [MonadExceptOf ElabTaskError m] [MonadLiftT (ST RealWorld) m] [Monad m] (tk : CancelToken) : m Unit := do
let c ← tk.ref.get
if c then
throw ElabTaskError.aborted
def set (tk : CancelToken) : IO Unit :=
tk.ref.set true
end CancelToken
/-- A document editable in the sense that we track the environment
and parser state after each command so that edits can be applied
without recompiling code appearing earlier in the file. -/
structure EditableDocument where
meta : DocumentMeta
/- The first snapshot is that after the header. -/
headerSnap : Snapshot
/- Subsequent snapshots occur after each command. -/
cmdSnaps : AsyncList ElabTaskError Snapshot
cancelTk : CancelToken
deriving Inhabited
structure RpcSession where
/-- Objects that are being kept alive for the RPC client, together with their type names,
mapped to by their RPC reference.
Note that we may currently have multiple references to the same object. It is only disposed
of once all of those are gone. This simplifies the client a bit as it can drop every reference
received separately. -/
aliveRefs : Std.PersistentHashMap Lsp.RpcRef (Name × NonScalar)
/-- Value to use for the next `RpcRef`. It is monotonically increasing to avoid any possible
bugs resulting from its reuse. -/
nextRef : USize
/-- The `IO.monoMsNow` time when the session expires. See `$/lean/rpc/keepAlive`. -/
expireTime : Nat
namespace RpcSession
def keepAliveTimeMs : Nat :=
30000
def new : IO (UInt64 × RpcSession) := do
/- We generate a random ID to ensure that session IDs do not repeat across re-initializations
and worker restarts. Otherwise, the client may attempt to use outdated references. -/
let newId ← ByteArray.toUInt64LE! <$> IO.getRandomBytes 8
let newSesh := {
aliveRefs := Std.PersistentHashMap.empty
nextRef := 0
expireTime := (← IO.monoMsNow) + keepAliveTimeMs
}
return (newId, newSesh)
def store (st : RpcSession) (typeName : Name) (obj : NonScalar) : Lsp.RpcRef × RpcSession :=
let ref := ⟨st.nextRef⟩
let st' := { st with aliveRefs := st.aliveRefs.insert ref (typeName, obj)
nextRef := st.nextRef + 1 }
(ref, st')
def release (st : RpcSession) (ref : Lsp.RpcRef) : Bool × RpcSession :=
let released := st.aliveRefs.contains ref
(released, { st with aliveRefs := st.aliveRefs.erase ref })
def keptAlive (s : RpcSession) : IO RpcSession := do
return { s with expireTime := (← IO.monoMsNow) + keepAliveTimeMs }
def hasExpired (s : RpcSession) : IO Bool :=
return s.expireTime ≤ (← IO.monoMsNow)
end RpcSession
instance [Monad m] [MonadStateOf RpcSession m] : MonadRpcSession m where
rpcStoreRef typeName obj := modifyGet fun st => st.store typeName obj
rpcGetRef r := return (←get).aliveRefs.find? r
rpcReleaseRef r := modifyGet fun st => st.release r
end Lean.Server.FileWorker