diff --git a/server/leanserver/GameServer/FileWorker.lean b/server/leanserver/GameServer/FileWorker.lean index b8f1d73..1029f62 100644 --- a/server/leanserver/GameServer/FileWorker.lean +++ b/server/leanserver/GameServer/FileWorker.lean @@ -4,6 +4,8 @@ import Lean import GameServer.EnvExtensions import GameServer.RpcHandlers +import Lean.Server.FileWorker + namespace MyModule open Lean open Elab @@ -158,7 +160,7 @@ where : AsyncElabM (Option Snapshot) := do cancelTk.check let s ← get - let lastSnap := s.snaps.back + let .some lastSnap := s.snaps.back? | panic! "empty snapshots" if lastSnap.isAtEnd then publishDiagnostics m lastSnap.diagnostics.toArray ctx.hOut publishProgressDone m ctx.hOut @@ -194,7 +196,7 @@ where def unfoldSnaps (m : DocumentMeta) (snaps : Array Snapshot) (cancelTk : CancelToken) : ReaderT WorkerContext IO (AsyncList ElabTaskError Snapshot) := do let ctx ← read - let headerSnap := snaps[0]! + let some headerSnap := snaps[0]? | panic! "empty snapshots" if headerSnap.msgLog.hasErrors then -- Treat header processing errors as fatal so users aren't swamped with -- followup errors @@ -211,29 +213,31 @@ end Elab section Updates +/-- Given the new document, updates editable doc state. -/ def updateDocument (newMeta : DocumentMeta) : WorkerM Unit := do let ctx ← read let oldDoc := (←get).doc - -- The watchdog only restarts the file worker when the semantic content of the header changes. - -- If e.g. a newline is deleted, it will not restart this file worker, but we still - -- need to reparse the header so that the offsets are correct. + oldDoc.cancelTk.set + let initHeaderStx := (← get).initHeaderStx let (newHeaderStx, newMpState, _) ← Parser.parseHeader newMeta.mkInputContext let cancelTk ← CancelToken.new - -- Wait for at least one snapshot from the old doc, we don't want to unnecessarily re-run `print-paths` let headSnapTask := oldDoc.cmdSnaps.waitHead? - let newSnaps ← EIO.mapTask (ε := ElabTaskError) (t := headSnapTask) fun headSnap?? => do - let headSnap? ← MonadExcept.ofExcept headSnap?? + let newSnaps ← if initHeaderStx != newHeaderStx then + EIO.asTask (ε := ElabTaskError) (prio := .dedicated) do + IO.sleep ctx.initParams.editDelay.toUInt32 + cancelTk.check + IO.Process.exit 2 + else EIO.mapTask (ε := ElabTaskError) (t := headSnapTask) (prio := .dedicated) fun headSnap?? => do -- There is always at least one snapshot absent exceptions - let headSnap := headSnap?.get! + let some headSnap ← MonadExcept.ofExcept headSnap?? | panic! "empty snapshots" let newHeaderSnap := { headSnap with stx := newHeaderStx, mpState := newMpState } - oldDoc.cancelTk.set let changePos := oldDoc.meta.text.source.firstDiffPos newMeta.text.source -- Ignore exceptions, we are only interested in the successful snapshots let (cmdSnaps, _) ← oldDoc.cmdSnaps.getFinishedPrefix -- NOTE(WN): we invalidate eagerly as `endPos` consumes input greedily. To re-elaborate only -- when really necessary, we could do a whitespace-aware `Syntax` comparison instead. - let mut validSnaps := cmdSnaps.takeWhile (fun s => s.endPos < changePos) - if validSnaps.length ≤ 1 then + let mut validSnaps ← pure (cmdSnaps.takeWhile (fun s => s.endPos < changePos)) + if h : validSnaps.length ≤ 1 then validSnaps := [newHeaderSnap] else /- When at least one valid non-header snap exists, it may happen that a change does not fall @@ -241,15 +245,20 @@ section Updates We check for this here. We do not currently handle crazy grammars in which an appended token can merge two or more previous commands into one. To do so would require reparsing the entire file. -/ - let mut lastSnap := validSnaps.getLast! - let preLastSnap := if validSnaps.length ≥ 2 - then validSnaps.get! (validSnaps.length - 2) - else newHeaderSnap + have : validSnaps.length ≥ 2 := Nat.gt_of_not_le h + let mut lastSnap := validSnaps.getLast (by subst ·; simp at h) + let preLastSnap := + have : 0 < validSnaps.length := Nat.lt_of_lt_of_le (by decide) this + have : validSnaps.length - 2 < validSnaps.length := Nat.sub_lt this (by decide) + validSnaps[validSnaps.length - 2] let newLastStx ← parseNextCmd newMeta.mkInputContext preLastSnap if newLastStx != lastSnap.stx then validSnaps := validSnaps.dropLast - unfoldSnaps newMeta validSnaps.toArray cancelTk ctx - modify fun st => { st with doc := ⟨newMeta, AsyncList.delayed newSnaps, cancelTk⟩ } + -- wait for a bit, giving the initial `cancelTk.check` in `nextCmdSnap` time to trigger + -- before kicking off any expensive elaboration (TODO: make expensive elaboration cancelable) + unfoldCmdSnaps newMeta validSnaps.toArray cancelTk ctx + (startAfterMs := ctx.initParams.editDelay.toUInt32) + modify fun st => { st with doc := { meta := newMeta, cmdSnaps := AsyncList.delayed newSnaps, cancelTk } } end Updates diff --git a/server/leanserver/lean-toolchain b/server/leanserver/lean-toolchain index a01662b..1060ced 100644 --- a/server/leanserver/lean-toolchain +++ b/server/leanserver/lean-toolchain @@ -1 +1 @@ -leanprover/lean4:nightly-2022-12-05 +leanprover/lean4:nightly-2023-01-16 diff --git a/server/testgame/lake-manifest.json b/server/testgame/lake-manifest.json index dac8477..6fe8eb2 100644 --- a/server/testgame/lake-manifest.json +++ b/server/testgame/lake-manifest.json @@ -10,13 +10,13 @@ {"git": {"url": "https://github.com/leanprover/lake", "subDir?": null, - "rev": "235383015cdcb0082777b0347b84dba01843c79c", + "rev": "d0b530530f14dde97a547b03abf87eee06360d60", "name": "lake", "inputRev?": "master"}}, {"git": {"url": "https://github.com/leanprover/doc-gen4", "subDir?": null, - "rev": "7009910876145a9a1220359f968ba7045dd05290", + "rev": "09dbebed9ca85368152bb5146ef6f1271b1be425", "name": "doc-gen4", "inputRev?": "main"}}, {"git": @@ -28,7 +28,7 @@ {"git": {"url": "https://github.com/leanprover-community/mathlib4.git", "subDir?": null, - "rev": "573c745b2edb348902bf14e5b4166e50c68db8f6", + "rev": "1771130ddfb56ca04da3296cf3693a9e58eb00c6", "name": "mathlib", "inputRev?": "master"}}, {"git": @@ -40,13 +40,13 @@ {"git": {"url": "https://github.com/JLimperg/aesop", "subDir?": null, - "rev": "ddced06b6b76483fe8794f2b516c57980a08fcef", + "rev": "645e92db52499582bb31984396a5e41772241012", "name": "aesop", "inputRev?": "master"}}, {"git": {"url": "https://github.com/hargonix/LeanInk", "subDir?": null, - "rev": "9f3101452135964ac9107ec8e9910bfd14022bbc", + "rev": "2447df5cc6e48eb965c3c3fba87e46d353b5e9f1", "name": "leanInk", "inputRev?": "doc-gen"}}, {"path": {"name": "GameServer", "dir": "./../leanserver"}}, @@ -59,6 +59,6 @@ {"git": {"url": "https://github.com/leanprover/std4", "subDir?": null, - "rev": "6196cf930a95664987eba8aee62ae7802d51428c", + "rev": "5770b609aeae209cb80ac74655ee8c750c12aabd", "name": "std", "inputRev?": "main"}}]} diff --git a/server/testgame/lean-toolchain b/server/testgame/lean-toolchain index a01662b..1060ced 100644 --- a/server/testgame/lean-toolchain +++ b/server/testgame/lean-toolchain @@ -1 +1 @@ -leanprover/lean4:nightly-2022-12-05 +leanprover/lean4:nightly-2023-01-16