vtcode_core/utils/
transcript.rs1use once_cell::sync::Lazy;
2use parking_lot::RwLock;
3
4const MAX_LINES: usize = 4000;
5
6static TRANSCRIPT: Lazy<RwLock<Vec<String>>> = Lazy::new(|| RwLock::new(Vec::new()));
7
8pub fn append(line: &str) {
9 let mut log = TRANSCRIPT.write();
10 if log.len() == MAX_LINES {
11 let drop_count = MAX_LINES / 5;
12 log.drain(0..drop_count);
13 }
14 log.push(line.to_string());
15}
16
17pub fn replace_last(count: usize, lines: &[String]) {
18 let mut log = TRANSCRIPT.write();
19 let remove = count.min(log.len());
20 for _ in 0..remove {
21 log.pop();
22 }
23 for line in lines {
24 if log.len() == MAX_LINES {
25 let drop_count = MAX_LINES / 5;
26 log.drain(0..drop_count);
27 }
28 log.push(line.clone());
29 }
30}
31
32pub fn snapshot() -> Vec<String> {
33 TRANSCRIPT.read().clone()
34}
35
36pub fn len() -> usize {
37 TRANSCRIPT.read().len()
38}
39
40pub fn clear() {
41 TRANSCRIPT.write().clear();
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn append_and_snapshot_store_lines() {
50 clear();
51 append("first");
52 append("second");
53 assert_eq!(len(), 2);
54 let snap = snapshot();
55 assert_eq!(snap, vec!["first".to_string(), "second".to_string()]);
56 clear();
57 }
58
59 #[test]
60 fn transcript_drops_oldest_chunk_when_full() {
61 clear();
62 for idx in 0..MAX_LINES {
63 append(&format!("line {idx}"));
64 }
65 assert_eq!(len(), MAX_LINES);
66 for extra in 0..10 {
67 append(&format!("extra {extra}"));
68 }
69 assert_eq!(len(), MAX_LINES - (MAX_LINES / 5) + 10);
70 let snap = snapshot();
71 assert_eq!(snap.first().unwrap(), &format!("line {}", MAX_LINES / 5));
72 clear();
73 }
74}