vtcode_core/utils/
transcript.rs

1use 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}