1use std::collections::HashSet;
2
3use crate::commands::find::{ReferenceMatch, SymbolMatch};
4
5#[must_use]
7pub fn merge_symbol_results(results: Vec<Vec<SymbolMatch>>) -> Vec<SymbolMatch> {
8 let mut merged: Vec<SymbolMatch> = results.into_iter().flatten().collect();
9 merged.sort_by(|a, b| a.path.cmp(&b.path).then(a.line.cmp(&b.line)));
10
11 let mut seen = HashSet::new();
13 merged.retain(|m| seen.insert((m.path.clone(), m.line)));
14 merged
15}
16
17#[must_use]
19pub fn merge_reference_results(results: Vec<Vec<ReferenceMatch>>) -> Vec<ReferenceMatch> {
20 let mut merged: Vec<ReferenceMatch> = results.into_iter().flatten().collect();
21
22 let mut seen = HashSet::new();
24 merged.retain(|m| seen.insert((m.path.clone(), m.line)));
25
26 merged.sort_by(|a, b| {
28 b.is_definition
29 .cmp(&a.is_definition)
30 .then(a.path.cmp(&b.path))
31 .then(a.line.cmp(&b.line))
32 });
33 merged
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 fn sym(path: &str, line: u32, kind: &str) -> SymbolMatch {
41 SymbolMatch {
42 path: path.to_string(),
43 line,
44 kind: kind.to_string(),
45 preview: String::new(),
46 body: None,
47 }
48 }
49
50 fn refm(path: &str, line: u32, is_def: bool) -> ReferenceMatch {
51 ReferenceMatch {
52 path: path.to_string(),
53 line,
54 preview: String::new(),
55 is_definition: is_def,
56 containing_symbol: None,
57 }
58 }
59
60 #[test]
61 fn merge_symbols_deduplicates() {
62 let r1 = vec![sym("src/a.rs", 10, "function")];
63 let r2 = vec![sym("src/a.rs", 10, "function"), sym("src/b.ts", 5, "class")];
64
65 let merged = merge_symbol_results(vec![r1, r2]);
66 assert_eq!(merged.len(), 2);
67 assert_eq!(merged[0].path, "src/a.rs");
68 assert_eq!(merged[1].path, "src/b.ts");
69 }
70
71 #[test]
72 fn merge_symbols_sorted_by_path_line() {
73 let r1 = vec![sym("src/z.rs", 1, "function")];
74 let r2 = vec![sym("src/a.rs", 1, "function")];
75
76 let merged = merge_symbol_results(vec![r1, r2]);
77 assert_eq!(merged[0].path, "src/a.rs");
78 assert_eq!(merged[1].path, "src/z.rs");
79 }
80
81 #[test]
82 fn merge_refs_deduplicates() {
83 let r1 = vec![refm("src/a.rs", 10, true)];
84 let r2 = vec![refm("src/a.rs", 10, true), refm("src/b.rs", 20, false)];
85
86 let merged = merge_reference_results(vec![r1, r2]);
87 assert_eq!(merged.len(), 2);
88 }
89
90 #[test]
91 fn merge_refs_definition_first() {
92 let r1 = vec![refm("src/b.rs", 20, false)];
93 let r2 = vec![refm("src/a.rs", 10, true)];
94
95 let merged = merge_reference_results(vec![r1, r2]);
96 assert!(merged[0].is_definition);
97 assert_eq!(merged[0].path, "src/a.rs");
98 }
99
100 #[test]
101 fn merge_empty_inputs() {
102 assert!(merge_symbol_results(vec![]).is_empty());
103 assert!(merge_reference_results(vec![]).is_empty());
104 }
105}