Skip to main content

Vine/Server/Notification/
SetTextEditorDecorations.rs

1//! Cocoon → `window.setTextEditorDecorations` notification.
2//! Channel-drain batching: ~5-200 calls per extension per second during
3//! scroll; one renderer event per frame (16 ms window, drain stragglers).
4//! Uses `Arc<dyn RendererEmitter>` captured once from
5//! `VineHost::RendererEmitter()` so the drain task never holds a reference
6//! to the full host across await points.
7
8use std::sync::{Arc, OnceLock};
9
10use serde_json::{Value, json};
11use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
12
13use crate::{
14	Host::{RendererEmitter, VineHost},
15	dev_log,
16};
17
18struct DecoSetChannel {
19	Sender:UnboundedSender<Value>,
20}
21
22static DECO_SET_CH:OnceLock<DecoSetChannel> = OnceLock::new();
23
24fn GetOrInitChannel(Emitter:Arc<dyn RendererEmitter>) -> &'static DecoSetChannel {
25	DECO_SET_CH.get_or_init(|| {
26		let (Tx, mut Rx) = unbounded_channel::<Value>();
27
28		tokio::spawn(async move {
29			let mut Buf:Vec<Value> = Vec::with_capacity(64);
30
31			loop {
32				match Rx.recv().await {
33					None => break,
34					Some(V) => Buf.push(V),
35				}
36
37				// Drain everything already queued without blocking.
38				Rx.recv_many(&mut Buf, 4096).await;
39
40				// One animation frame - let stragglers accumulate.
41				tokio::time::sleep(std::time::Duration::from_millis(16)).await;
42
43				// Drain again after the frame window.
44				Rx.recv_many(&mut Buf, 4096).await;
45
46				if Buf.is_empty() {
47					continue;
48				}
49
50				let Count = Buf.len();
51
52				let Batch:Vec<Value> = Buf.drain(..).collect();
53
54				Emitter.Emit("sky://decoration/set-ranges", json!({ "batch": Batch }));
55
56				dev_log!("sky-emit", "[DecoSet] emitted batch={}", Count);
57			}
58		});
59
60		DecoSetChannel { Sender:Tx }
61	})
62}
63
64pub async fn SetTextEditorDecorations(Host:&dyn VineHost, Parameter:&Value) {
65	let Ch = GetOrInitChannel(Host.RendererEmitter());
66
67	let _ = Ch.Sender.send(Parameter.clone());
68}