ignore classes feature

This commit is contained in:
Joe 2025-12-19 01:15:14 -06:00
parent 929bc254fa
commit db87fa010f
4 changed files with 81 additions and 2 deletions

View file

@ -161,6 +161,17 @@ pub fn syncback_dir_no_meta<'sync>(
for new_child_ref in new_inst.children() { for new_child_ref in new_inst.children() {
let new_child = snapshot.get_new_instance(*new_child_ref).unwrap(); let new_child = snapshot.get_new_instance(*new_child_ref).unwrap();
// Skip instances of ignored classes
if snapshot.should_ignore_class(&new_child.class) {
// Also remove from old_child_map so it won't be marked as removed
old_child_map.remove(new_child.name.as_str());
log::debug!(
"Skipping instance {} because its class {} is ignored",
new_child.name,
new_child.class
);
continue;
}
if let Some(old_child) = old_child_map.remove(new_child.name.as_str()) { if let Some(old_child) = old_child_map.remove(new_child.name.as_str()) {
if old_child.metadata().relevant_paths.is_empty() { if old_child.metadata().relevant_paths.is_empty() {
log::debug!( log::debug!(
@ -186,10 +197,32 @@ pub fn syncback_dir_no_meta<'sync>(
} }
} }
// Any children that are in the old dom but not the new one are removed. // Any children that are in the old dom but not the new one are removed.
removed_children.extend(old_child_map.into_values()); // Filter out instances of ignored classes from removal.
removed_children.extend(old_child_map.into_values().filter(|inst| {
if snapshot.should_ignore_class(inst.class_name().as_str()) {
log::debug!(
"Not removing instance {} because its class {} is ignored",
inst.name(),
inst.class_name()
);
false
} else {
true
}
}));
} else { } else {
// There is no old instance. Just add every child. // There is no old instance. Just add every child.
for new_child_ref in new_inst.children() { for new_child_ref in new_inst.children() {
let new_child = snapshot.get_new_instance(*new_child_ref).unwrap();
// Skip instances of ignored classes
if snapshot.should_ignore_class(&new_child.class) {
log::debug!(
"Skipping instance {} because its class {} is ignored",
new_child.name,
new_child.class
);
continue;
}
children.push(snapshot.with_joined_path(*new_child_ref, None)?); children.push(snapshot.with_joined_path(*new_child_ref, None)?);
} }
} }

View file

@ -474,6 +474,18 @@ pub fn syncback_project<'sync>(
// All of the children in this loop are by their nature not in the // All of the children in this loop are by their nature not in the
// project, so we just need to run syncback on them. // project, so we just need to run syncback on them.
for (name, new_child) in new_child_map.drain() { for (name, new_child) in new_child_map.drain() {
// Skip instances of ignored classes
if snapshot.should_ignore_class(&new_child.class) {
// Also remove from old_child_map so it won't be marked as removed
old_child_map.remove(name.as_str());
log::debug!(
"Skipping instance {} because its class {} is ignored",
new_child.name,
new_child.class
);
continue;
}
let parent_path = match ref_to_path_map.get(&new_child.parent()) { let parent_path = match ref_to_path_map.get(&new_child.parent()) {
Some(path) => path.clone(), Some(path) => path.clone(),
None => { None => {
@ -502,7 +514,19 @@ pub fn syncback_project<'sync>(
} }
} }
} }
removed_descendants.extend(old_child_map.drain().map(|(_, v)| v)); // Filter out instances of ignored classes from removal
removed_descendants.extend(old_child_map.drain().filter_map(|(_, inst)| {
if snapshot.should_ignore_class(inst.class_name().as_str()) {
log::debug!(
"Not removing instance {} because its class {} is ignored",
inst.name(),
inst.class_name()
);
None
} else {
Some(inst)
}
}));
node_changed_map.push((&node.properties, &node.attributes, old_inst)) node_changed_map.push((&node.properties, &node.attributes, old_inst))
} }
let mut fs_snapshot = FsSnapshot::new(); let mut fs_snapshot = FsSnapshot::new();

View file

@ -385,6 +385,10 @@ pub struct SyncbackRules {
/// syncback. /// syncback.
#[serde(default, skip_serializing_if = "IndexMap::is_empty")] #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
ignore_properties: IndexMap<Ustr, Vec<Ustr>>, ignore_properties: IndexMap<Ustr, Vec<Ustr>>,
/// A list of class names to ignore entirely during syncback.
/// Instances of these classes will not be added, removed, or synced.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
ignore_classes: Vec<String>,
/// Whether or not the `CurrentCamera` of `Workspace` is included in the /// Whether or not the `CurrentCamera` of `Workspace` is included in the
/// syncback or not. Defaults to `false`. /// syncback or not. Defaults to `false`.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]

View file

@ -206,6 +206,24 @@ impl<'sync> SyncbackSnapshot<'sync> {
.as_ref() .as_ref()
.map(|rules| rules.ignore_trees.as_slice()) .map(|rules| rules.ignore_trees.as_slice())
} }
/// Returns user-specified ignore classes.
#[inline]
pub fn ignore_classes(&self) -> Option<&[String]> {
self.data
.project
.syncback_rules
.as_ref()
.map(|rules| rules.ignore_classes.as_slice())
}
/// Checks if a class name should be ignored during syncback.
#[inline]
pub fn should_ignore_class(&self, class_name: &str) -> bool {
self.ignore_classes()
.map(|classes| classes.iter().any(|c| c == class_name))
.unwrap_or(false)
}
} }
pub fn filter_out_property(inst: &Instance, prop_name: &str) -> bool { pub fn filter_out_property(inst: &Instance, prop_name: &str) -> bool {