libsurfer/
variable_name_type.rs1use derive_more::{Display, FromStr};
2use enum_iterator::Sequence;
3use itertools::Itertools;
4use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7
8use crate::displayed_item_tree::Node;
9use crate::wave_container::{ScopeRefExt, VariableRefExt};
10use crate::{displayed_item::DisplayedItem, wave_container::VariableRef, wave_data::WaveData};
11
12#[derive(PartialEq, Copy, Clone, Debug, Deserialize, Display, FromStr, Serialize, Sequence)]
13pub enum VariableNameType {
14 Local,
16
17 Unique,
19
20 Global,
22}
23
24const ELLIPSIS: &str = "…";
25
26impl WaveData {
27 pub fn compute_variable_display_names(&mut self) {
28 let full_names: Vec<&VariableRef> = self
30 .items_tree
31 .iter()
32 .filter_map(|node| {
33 self.displayed_items
34 .get(&node.item_ref)
35 .and_then(|item| match item {
36 DisplayedItem::Variable(variable) => Some(&variable.variable_ref),
37 _ => None,
38 })
39 })
40 .unique()
41 .collect();
42 let minimal_map = compute_minimal_display_map(&full_names);
44
45 for Node { item_ref, .. } in self.items_tree.iter() {
47 self.displayed_items
48 .entry(*item_ref)
49 .and_modify(|item| match item {
50 DisplayedItem::Variable(variable) => {
51 variable.display_name = match variable.display_name_type {
52 VariableNameType::Local => variable.variable_ref.name.clone(),
53 VariableNameType::Global => variable.variable_ref.full_path_string(),
54 VariableNameType::Unique => minimal_map
55 .get(&variable.variable_ref.full_path_string())
56 .cloned()
57 .unwrap_or_else(|| variable.variable_ref.name.clone()),
58 };
59 if self.display_variable_indices {
60 let index = self
61 .inner
62 .as_waves()
63 .unwrap()
64 .variable_meta(&variable.variable_ref)
65 .ok()
66 .as_ref()
67 .and_then(|meta| meta.index)
68 .map(|index| format!(" {index}"))
69 .unwrap_or_default();
70 variable.display_name = format!("{}{}", variable.display_name, index);
71 }
72 }
73 DisplayedItem::Divider(_) => {}
74 DisplayedItem::Marker(_) => {}
75 DisplayedItem::TimeLine(_) => {}
76 DisplayedItem::Placeholder(_) => {}
77 DisplayedItem::Stream(_) => {}
78 DisplayedItem::Group(_) => {}
79 });
80 }
81 }
82
83 pub fn force_variable_name_type(&mut self, name_type: VariableNameType) {
84 for Node { item_ref, .. } in self.items_tree.iter() {
85 self.displayed_items.entry(*item_ref).and_modify(|item| {
86 if let DisplayedItem::Variable(variable) = item {
87 variable.display_name_type = name_type;
88 }
89 });
90 }
91 self.default_variable_name_type = name_type;
92 self.compute_variable_display_names();
93 }
94}
95
96fn compute_minimal_display_map(all_variables: &[&VariableRef]) -> HashMap<String, String> {
99 let mut groups: HashMap<String, Vec<&VariableRef>> = HashMap::new();
102 for v in all_variables {
103 groups.entry(v.name.clone()).or_default().push(v);
104 }
105
106 let mut result: HashMap<String, String> = HashMap::with_capacity(all_variables.len());
107
108 for (_local, vars) in groups {
109 if vars.len() == 1 {
110 let v = vars[0];
111 result.insert(v.full_path_string(), v.name.clone());
112 continue;
113 }
114
115 let mut entries: Vec<(Vec<String>, &VariableRef)> = vars
117 .iter()
118 .map(|v| {
119 let rev: Vec<String> = v.path.strs().iter().rev().cloned().collect();
120 (rev, *v)
121 })
122 .collect();
123
124 entries.sort_by(|a, b| a.0.cmp(&b.0));
125
126 fn common_prefix_len(a: &[String], b: &[String]) -> usize {
128 a.iter().zip(b.iter()).take_while(|(x, y)| x == y).count()
129 }
130
131 for (i, (path_i, var_i)) in entries.iter().enumerate() {
132 let mut need = 0usize;
133 if i > 0 {
134 let common = common_prefix_len(path_i, &entries[i - 1].0);
135 need = need.max(common + 1);
136 }
137 if i + 1 < entries.len() {
138 let common = common_prefix_len(path_i, &entries[i + 1].0);
139 need = need.max(common + 1);
140 }
141
142 if need == 0 || path_i.is_empty() {
144 result.insert(var_i.full_path_string(), var_i.name.clone());
145 continue;
146 }
147
148 let take = need.min(path_i.len());
150 let scope = path_i.iter().take(take).rev().cloned().join(".");
151 let prefix = if take < path_i.len() { ELLIPSIS } else { "" };
152
153 let display = if scope.is_empty() {
154 var_i.name.clone()
155 } else {
156 format!("{}{}.{}", prefix, scope, var_i.name)
157 };
158 result.insert(var_i.full_path_string(), display);
159 }
160 }
161
162 result
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn minimal_display_map_unique_locals() {
171 let v1 = VariableRef::from_hierarchy_string("top.a");
172 let v2 = VariableRef::from_hierarchy_string("top.b");
173 let vars = vec![&v1, &v2];
174 let map = compute_minimal_display_map(&vars);
175 assert_eq!(
176 map.get(&v1.full_path_string())
177 .map(std::string::String::as_str),
178 Some("a")
179 );
180 assert_eq!(
181 map.get(&v2.full_path_string())
182 .map(std::string::String::as_str),
183 Some("b")
184 );
185 }
186
187 #[test]
188 fn minimal_display_map_collisions() {
189 let v1 = VariableRef::from_hierarchy_string("top.dut.x");
190 let v2 = VariableRef::from_hierarchy_string("other.dut.x");
191 let v3 = VariableRef::from_hierarchy_string("top.sub.x");
192 let vars = vec![&v1, &v2, &v3];
193 let map = compute_minimal_display_map(&vars);
194
195 assert_eq!(
196 map.get(&v1.full_path_string())
197 .map(std::string::String::as_str),
198 Some("top.dut.x")
199 );
200 assert_eq!(
201 map.get(&v2.full_path_string())
202 .map(std::string::String::as_str),
203 Some("other.dut.x")
204 );
205 assert_eq!(
206 map.get(&v3.full_path_string())
207 .map(std::string::String::as_str),
208 Some(ELLIPSIS.to_owned() + "sub.x").as_deref()
209 );
210 }
211
212 #[test]
213 fn minimal_display_map_root_and_scoped() {
214 let v1 = VariableRef::from_hierarchy_string("x");
215 let v2 = VariableRef::from_hierarchy_string("a.x");
216 let vars = vec![&v1, &v2];
217 let map = compute_minimal_display_map(&vars);
218 assert_eq!(
219 map.get(&v1.full_path_string())
220 .map(std::string::String::as_str),
221 Some("x")
222 );
223 assert_eq!(
224 map.get(&v2.full_path_string())
225 .map(std::string::String::as_str),
226 Some("a.x")
227 );
228 }
229}