@@ -7,28 +7,40 @@ local Html = require 'proofwidgets.html'
77---
88--- Displays HTML that updates over time as a background Lean thread pushes
99--- new frames via the awaitRefresh RPC method.
10+ ---
11+ --- A subtle point: each tree rebuild (cursor move, panel re-render, ...)
12+ --- creates a fresh RefreshComponent element with its own polling loop. If
13+ --- the previous element's loop is left running, every server frame it
14+ --- receives forces a full `renderer:render()` on the orphaned subtree,
15+ --- which compounds with every rebuild until the infoview is constantly
16+ --- re-rendering itself ("hourglass + flicker even when idle"). To avoid
17+ --- this we publish a `__state` cancel handle; `transfer_state` carries it
18+ --- to the new element which immediately cancels the old polling loop.
1019--- @param ctx RenderContext
1120--- @param props { state : table , cancelTk : table }
1221--- @return Element
1322return function (ctx , props )
23+ local cancelled = false
24+
1425 local element = Element :new {
1526 __async_init = function (rerender )
16- -- Start the monitor call (fire-and-forget).
17- -- It loops forever server-side and cancels the background computation
18- -- when the RPC session closes.
27+ -- Start the monitor call (fire-and-forget). It loops forever server-side
28+ -- and cancels the background computation when the RPC session closes.
1929 async .run (function ()
2030 ctx :rpc_call (' ProofWidgets.RefreshComponent.monitor' , props )
2131 end )
2232
23- -- Poll for new HTML frames.
33+ -- Poll for new HTML frames. Bail as soon as a newer element has
34+ -- adopted our position (via `__state`), otherwise the orphaned loop
35+ -- would keep firing `renderer:render()` after every server frame.
2436 async .run (function ()
2537 local idx = 0
26- while true do
38+ while not cancelled do
2739 local response , err = ctx :rpc_call (
2840 ' ProofWidgets.RefreshComponent.awaitRefresh' ,
2941 { state = props .state , oldIdx = idx }
3042 )
31- if err or not response then
43+ if err or not response or cancelled then
3244 break
3345 end
3446 idx = response .idx
@@ -37,5 +49,17 @@ return function(ctx, props)
3749 end )
3850 end ,
3951 }
52+
53+ element .__state = {
54+ snapshot = function ()
55+ return function ()
56+ cancelled = true
57+ end
58+ end ,
59+ restore = function (_ , cancel_previous )
60+ cancel_previous ()
61+ end ,
62+ }
63+
4064 return element
4165end
0 commit comments