term_rustdoc/tree/textline/
fold.rs1use super::TreeLines;
2use crate::tree::{DModule, DocTree, IDMap, ID};
3use rustc_hash::FxHashSet as HashSet;
4use rustdoc_types::ItemEnum;
5
6#[derive(Default, PartialEq, Eq)]
8enum Kind {
9 #[default]
11 ExpandAll,
12 ExpandZero,
14 ExpandToFirstLevelModules,
22 CurrentModule,
28}
29
30#[derive(Default)]
32pub struct Fold {
33 kind: Kind,
34 expand: HashSet<ID>,
36}
37
38impl TreeLines {
42 pub fn expand_all_including_impls(&mut self) {
44 *self = Self::new_with(self.doc(), |doc| doc.dmodule_show_prettier()).0;
45 }
46
47 pub fn dmodule(&self) -> &DModule {
48 self.doc.dmodule()
49 }
50
51 pub fn idmap(&self) -> &IDMap {
52 &self.doc
53 }
54
55 pub fn expand_all(&mut self) {
56 self._expand_all();
57 self.lines = self.dmodule().item_tree(self.idmap()).cache_lines().0;
58 }
59
60 pub(super) fn _expand_all(&mut self) {
61 fn traversal_id(m: &DModule, mods: &mut HashSet<ID>) {
62 mods.insert(m.id.clone());
63 for submod in &m.modules {
64 traversal_id(submod, mods);
65 }
66 }
67 self.fold.kind = Kind::ExpandAll;
68 self.fold.expand.clear();
69 traversal_id(self.doc().dmodule(), &mut self.fold.expand);
70 }
71
72 pub fn expand_zero_level(&mut self) {
73 self.fold.kind = Kind::ExpandZero;
74 self.fold.expand.clear();
75 self.fold.expand.insert(self.dmodule().id.clone());
76 self.update_cached_lines(|dmod, map, _| {
77 let mut root = dmod.item_tree_only_in_one_specified_mod(map);
78 root.extend(
79 dmod.modules
80 .iter()
81 .map(|m| node!(ModuleFolded: map, Module, &m.id)),
82 );
83 root
84 });
85 }
86
87 pub fn expand_to_first_level_modules(&mut self) {
88 self.fold.kind = Kind::ExpandToFirstLevelModules;
89 let dmod = &self.dmodule().modules;
90 self.fold.expand = dmod.iter().map(|m| m.id.clone()).collect();
91 self.update_cached_lines(|dmod, map, mods| {
92 let mut root = dmod.item_tree_only_in_one_specified_mod(map);
93 for m in &dmod.modules {
94 let tree = if mods.contains(&m.id) {
95 let mut tree = m.item_tree_only_in_one_specified_mod(map);
96 for submod in &m.modules {
97 let leaf = node!(ModuleFolded: map, Module, &submod.id);
98 tree.push(leaf);
99 }
100 tree
101 } else {
102 node!(ModuleFolded: map, Module, &m.id)
103 };
104 root.push(tree);
105 }
106 root
107 });
108 }
109}
110
111impl TreeLines {
112 pub fn expand_toggle(&mut self, id: ID) {
116 fn modules_traversal(
117 dmod: &DModule,
118 map: &IDMap,
119 parent: &mut DocTree,
120 should_stop: &mut impl FnMut(&DModule) -> bool,
121 ) {
122 for m in &dmod.modules {
123 if should_stop(m) {
124 let node = node!(ModuleFolded: map, Module, &m.id);
125 parent.push(node);
126 } else {
127 let mut node = m.item_tree_only_in_one_specified_mod(map);
128 modules_traversal(m, map, &mut node, should_stop);
129 parent.push(node);
130 };
131 }
132 }
133
134 if self.fold.kind == Kind::CurrentModule {
135 return;
143 }
144
145 if !self.check_id(&id) {
146 return;
147 }
148 let mods = &mut self.fold.expand;
149 if mods.contains(&id) {
150 mods.remove(&id);
151 } else {
152 mods.insert(id);
153 }
154 self.update_cached_lines(|dmod, map, mods| {
155 let mut root = dmod.item_tree_only_in_one_specified_mod(map);
156 modules_traversal(dmod, map, &mut root, &mut |m| !mods.contains(&m.id));
157 root
158 });
159 }
160}
161
162impl TreeLines {
163 pub fn expand_current_module_only(&mut self, id: ID) {
164 self.fold.kind = Kind::CurrentModule;
165 if !self.check_id(&id) {
166 return;
167 }
168 self.fold.expand.clear();
169 self.fold.expand.insert(id);
170 self._expand_current_module_only();
171 }
172
173 fn _expand_current_module_only(&mut self) {
174 fn modules_traversal(
175 dmod: &DModule,
176 map: &IDMap,
177 parent: &mut DocTree,
178 should_stop: &mut impl FnMut(&DModule) -> bool,
179 ) {
180 for m in &dmod.modules {
181 if should_stop(m) {
182 let mut node = m.item_tree_only_in_one_specified_mod(map);
184 node.extend(
185 m.modules
186 .iter()
187 .map(|m| node!(@name ModuleFolded: map, &m.id)),
188 );
189 parent.push(node);
190 } else {
193 let mut node = node!(@name ModuleFolded: map, &m.id);
195 modules_traversal(m, map, &mut node, should_stop);
196 parent.push(node);
197 };
198 }
199 }
200 self.update_cached_lines(|dmod, map, mods| {
201 let mut root = node!(Module: map, &dmod.id);
202 if mods.contains(&dmod.id) {
203 let iter = dmod.item_tree_only_in_one_specified_mod(map).tree.leaves;
205 root.tree.extend(iter);
206 }
207 modules_traversal(dmod, map, &mut root, &mut |m| mods.contains(&m.id));
208 root
209 });
210 }
211}
212
213impl TreeLines {
214 fn check_id(&self, id: &ID) -> bool {
216 if !self.dmodule().modules.iter().any(|_m| {
217 self.idmap()
219 .get_item(id)
220 .map(|item| match &item.inner {
221 ItemEnum::Module(_) => true,
222 ItemEnum::Import(reepxort) => {
223 if let Some(id) = &reepxort.id {
224 if let Some(item) = self.idmap().get_item(&id.0) {
225 return matches!(item.inner, ItemEnum::Module(_));
226 }
227 }
228 false
229 }
230 _ => false,
231 })
232 .unwrap_or(false)
233 }) {
234 error!(
235 "ID({id}) is not a non-module item `{}` {:?}",
236 self.idmap().name(&id),
237 self.idmap().get_item(id)
238 );
239 return false;
240 }
241 true
242 }
243
244 fn update_cached_lines(&mut self, f: impl FnOnce(&DModule, &IDMap, &HashSet<ID>) -> DocTree) {
245 let map = self.idmap();
246 let dmod = &self.dmodule();
247 let mods = &self.fold.expand;
248 if mods.is_empty() {
249 self.lines = self.dmodule().item_tree(map).cache_lines().0;
251 return;
252 }
253 let root = f(dmod, map, mods);
254 self.lines = root.cache_lines().0;
255 }
256}