1use std::collections::{HashMap, HashSet};
7
8use tower_lsp::lsp_types::{Diagnostic, Url};
9
10pub fn publish_plan(
15 previously_dirty: &HashSet<Url>,
16 new_by_uri: HashMap<Url, Vec<Diagnostic>>,
17) -> (Vec<(Url, Vec<Diagnostic>)>, HashSet<Url>) {
18 let mut publishes: Vec<(Url, Vec<Diagnostic>)> = Vec::new();
19 let mut dirty: HashSet<Url> = HashSet::new();
20 for (uri, diags) in new_by_uri {
21 if !diags.is_empty() {
22 dirty.insert(uri.clone());
23 publishes.push((uri, diags));
24 } else if previously_dirty.contains(&uri) {
25 publishes.push((uri, Vec::new()));
27 }
28 }
29 let analysed: HashSet<&Url> = publishes.iter().map(|(u, _)| u).collect();
32 let mut gone: Vec<Url> = previously_dirty
33 .iter()
34 .filter(|u| !dirty.contains(*u) && !analysed.contains(u))
35 .cloned()
36 .collect();
37 gone.sort_by(|a, b| a.as_str().cmp(b.as_str()));
38 for uri in gone {
39 publishes.push((uri, Vec::new()));
40 }
41 (publishes, dirty)
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 fn uri(s: &str) -> Url {
49 Url::parse(&format!("file:///{s}")).unwrap()
50 }
51 fn diag(msg: &str) -> Diagnostic {
52 Diagnostic {
53 message: msg.to_string(),
54 ..Default::default()
55 }
56 }
57
58 #[test]
59 fn publishes_new_and_clears_fixed() {
60 let prev: HashSet<Url> = [uri("a.bynk"), uri("b.bynk")].into_iter().collect();
61 let mut new = HashMap::new();
62 new.insert(uri("a.bynk"), vec![diag("still broken")]);
63 new.insert(uri("b.bynk"), vec![]); new.insert(uri("c.bynk"), vec![]); let (publishes, dirty) = publish_plan(&prev, new);
67 let by: HashMap<_, _> = publishes
68 .iter()
69 .map(|(u, d)| (u.clone(), d.len()))
70 .collect();
71 assert_eq!(by.get(&uri("a.bynk")), Some(&1), "still-broken republished");
72 assert_eq!(
73 by.get(&uri("b.bynk")),
74 Some(&0),
75 "fixed file gets an empty publish"
76 );
77 assert!(
78 !by.contains_key(&uri("c.bynk")),
79 "never-dirty clean file is not published"
80 );
81 assert!(dirty.contains(&uri("a.bynk")) && !dirty.contains(&uri("b.bynk")));
82 }
83
84 #[test]
85 fn vanished_files_clear() {
86 let prev: HashSet<Url> = [uri("gone.bynk")].into_iter().collect();
87 let (publishes, dirty) = publish_plan(&prev, HashMap::new());
88 assert_eq!(publishes, vec![(uri("gone.bynk"), Vec::new())]);
89 assert!(dirty.is_empty());
90 }
91
92 #[test]
93 fn newly_broken_file_enters_the_dirty_set() {
94 let prev = HashSet::new();
95 let mut new = HashMap::new();
96 new.insert(uri("a.bynk"), vec![diag("boom")]);
97 let (publishes, dirty) = publish_plan(&prev, new);
98 assert_eq!(publishes.len(), 1);
99 assert!(dirty.contains(&uri("a.bynk")));
100 }
101}