Skip to main content

Mountain/Vine/Server/Notification/
SetTextEditorDecorations.rs

1
2//! Cocoon → Mountain `window.setTextEditorDecorations` notification.
3//!
4//! Fired whenever an extension calls `editor.setDecorations(decorationType,
5//! rangesOrOptions)`. Mountain forwards the payload as
6//! `sky://decoration/set-ranges` so Sky's ICodeEditorService can apply the
7//! ranges to the Monaco editor for the matching URI.
8//!
9//! Payload shape (from Cocoon `Window/Namespace.ts`):
10//! ```json
11//! {
12//!   "decorationTypeKey": "GitLens.blame",
13//!   "uri": "file:///path/to/file.ts",
14//!   "rangesOrOptions": [
15//!     { "range": { "startLineNumber": 1, "startColumn": 1, "endLineNumber": 1, "endColumn": 80 } }
16//!   ]
17//! }
18//! ```
19//!
20//! Channel-drain batching: ~5-200 calls per extension per second during
21//! scroll; one Tauri event per frame (16 ms window, drain stragglers).
22
23use std::sync::OnceLock;
24
25use serde_json::Value;
26use tauri::{AppHandle, Emitter};
27use tokio::sync::mpsc::{UnboundedSender, unbounded_channel};
28
29use crate::{Vine::Server::MountainVinegRPCService::MountainVinegRPCService, dev_log};
30
31struct DecoSetItem {
32	Handle:AppHandle,
33
34	Payload:Value,
35}
36
37struct DecoSetChannel {
38	Sender:UnboundedSender<DecoSetItem>,
39}
40
41static DECO_SET_CH:OnceLock<DecoSetChannel> = OnceLock::new();
42
43fn GetOrInitChannel(Handle:&AppHandle) -> &'static DecoSetChannel {
44	DECO_SET_CH.get_or_init(|| {
45		let (Tx, mut Rx) = unbounded_channel::<DecoSetItem>();
46
47		tokio::spawn(async move {
48			let mut Buf:Vec<DecoSetItem> = Vec::with_capacity(64);
49
50			loop {
51				match Rx.recv().await {
52					None => break,
53					Some(Item) => Buf.push(Item),
54				}
55
56				// Drain stragglers within one animation frame
57				Rx.recv_many(&mut Buf, 4096).await;
58				tokio::time::sleep(std::time::Duration::from_millis(16)).await;
59				Rx.recv_many(&mut Buf, 4096).await;
60
61				if Buf.is_empty() {
62					continue;
63				}
64
65				// Batch all queued set-ranges calls into one Tauri event
66				let Handle = Buf[0].Handle.clone();
67				let Batch:Vec<Value> = Buf.drain(..).map(|I| I.Payload).collect();
68
69				match Handle.emit("sky://decoration/set-ranges", serde_json::json!({ "batch": Batch })) {
70					Ok(()) => dev_log!("sky-emit", "[DecoSet] emitted batch={}", Batch.len()),
71					Err(E) => dev_log!("sky-emit", "[DecoSet] emit failed: {}", E),
72				}
73			}
74		});
75
76		DecoSetChannel { Sender:Tx }
77	})
78}
79
80pub async fn SetTextEditorDecorations(Service:&MountainVinegRPCService, Parameter:&Value) {
81	let Ch = GetOrInitChannel(Service.ApplicationHandle());
82
83	let _ = Ch
84		.Sender
85		.send(DecoSetItem { Handle:Service.ApplicationHandle().clone(), Payload:Parameter.clone() });
86}