Skip to main content

DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_EsbuildCompiler_Mountain/Vine/Server/Notification/
OutputChannelAppend.rs

1#![allow(non_snake_case)]
2//! Cocoon → Mountain `outputChannel.append` notification.
3//! Twin of `output.append`; see `OutputCreate.rs` for the duplicate-wire
4//! rationale.
5
6use serde_json::Value;
7use tauri::Emitter;
8
9use crate::{Vine::Server::MountainVinegRPCService::MountainVinegRPCService, dev_log};
10
11pub async fn OutputChannelAppend(Service:&MountainVinegRPCService, Parameter:&Value) {
12	let ChannelName = Parameter
13		.get("channel")
14		.or_else(|| Parameter.get("name"))
15		.and_then(Value::as_str)
16		.unwrap_or("?");
17
18	// Try the per-channel coalescer first. The git extension's
19	// `[OperationManager]` traces and the typescript-language-features
20	// `[Info  - …]` lines arrive in tight 30+/100ms bursts; coalescing
21	// at a 50ms window collapses the IPC + Sky emit count without
22	// reordering text within a channel.
23	//
24	// Falls through to the legacy per-append path when:
25	//   - `OutputCoalesce=0` is set (debug escape hatch).
26	//   - The payload has no `value` field to buffer.
27	let TextValue = Parameter.get("value").and_then(Value::as_str);
28
29	let CoalesceEnqueued = match TextValue {
30		Some(Text) => {
31			super::OutputChannelCoalesce::TryEnqueue(
32				Service.ApplicationHandle(),
33				ChannelName.to_string(),
34				Text.to_string(),
35			)
36		},
37		None => false,
38	};
39
40	if CoalesceEnqueued {
41		return;
42	}
43
44	let _ = Service.ApplicationHandle().emit("sky://output/append", Parameter);
45
46	// Legacy per-append fire path - only reached under the disable hook
47	// or when the payload is missing a `value` field. The coalescer
48	// above is the steady-state path. Tags below preserve the previous
49	// channel-aware routing (Git/SCM at `grpc`, everything else at
50	// `output-verbose`).
51
52	// Char-aware truncation. Slicing a `&str` at `&S[..200]` panics when
53	// byte 200 lands inside a multi-byte UTF-8 codepoint (vscode.git's
54	// progress messages contain `•` which is 3 bytes; if the message is
55	// >200 bytes and the bullet sits across the boundary, the slice
56	// crashes the tokio worker - observed live during SCM viewlet open).
57	// Walk char boundaries instead so the cut always lands between codepoints.
58	let TruncatedValue = Parameter
59		.get("value")
60		.and_then(Value::as_str)
61		.map(|S| {
62			if S.len() > 200 {
63				let CutAt = S
64					.char_indices()
65					.map(|(Index, _)| Index)
66					.take_while(|Index| *Index <= 200)
67					.last()
68					.unwrap_or(0);
69				format!("{}…", &S[..CutAt])
70			} else {
71				S.to_string()
72			}
73		})
74		.unwrap_or_else(|| "<no-value>".to_string());
75
76	if ChannelName.eq_ignore_ascii_case("git")
77		|| ChannelName.eq_ignore_ascii_case("source control")
78		|| ChannelName.eq_ignore_ascii_case("scm")
79	{
80		dev_log!(
81			"grpc",
82			"[OutputChannel:{}] {}",
83			ChannelName,
84			TruncatedValue.trim_end_matches('\n')
85		);
86	} else {
87		dev_log!("output-verbose", "[OutputChannel] append channel={}", ChannelName);
88	}
89}