diff --git a/rslib/src/storage/note/mod.rs b/rslib/src/storage/note/mod.rs index b9798f26f..03c82e919 100644 --- a/rslib/src/storage/note/mod.rs +++ b/rslib/src/storage/note/mod.rs @@ -6,10 +6,12 @@ use std::collections::HashSet; use rusqlite::params; use rusqlite::Row; +use unicase::UniCase; use crate::import_export::package::NoteMeta; use crate::notes::NoteTags; use crate::prelude::*; +use crate::tags::immediate_parent_name_unicase; use crate::tags::join_tags; use crate::tags::split_tags; @@ -217,16 +219,20 @@ impl super::SqliteStorage { .map_err(Into::into) } - pub(crate) fn all_tags_in_notes(&self) -> Result> { + /// All tags referenced by notes, and any parent tags as well. + pub(crate) fn all_tags_in_notes(&self) -> Result>> { let mut stmt = self .db .prepare_cached("select tags from notes where tags != ''")?; let mut query = stmt.query([])?; - let mut seen: HashSet = HashSet::new(); + let mut seen: HashSet> = HashSet::new(); while let Some(rows) = query.next()? { for tag in split_tags(rows.get_ref_unwrap(0).as_str()?) { - if !seen.contains(tag) { - seen.insert(tag.to_string()); + seen.insert(UniCase::new(tag.to_string())); + let mut tag_unicase = UniCase::new(tag); + while let Some(parent_name) = immediate_parent_name_unicase(tag_unicase) { + seen.insert(UniCase::new(parent_name.to_string())); + tag_unicase = UniCase::new(&parent_name); } } } diff --git a/rslib/src/tags/mod.rs b/rslib/src/tags/mod.rs index c05dacab5..aab335a3d 100644 --- a/rslib/src/tags/mod.rs +++ b/rslib/src/tags/mod.rs @@ -55,7 +55,7 @@ fn is_tag_separator(c: char) -> bool { c == ' ' || c == '\u{3000}' } -fn immediate_parent_name_unicase(tag_name: UniCase<&str>) -> Option> { +pub(crate) fn immediate_parent_name_unicase(tag_name: UniCase<&str>) -> Option> { tag_name.rsplit_once('\x1f').map(|t| t.0).map(UniCase::new) } diff --git a/rslib/src/tags/remove.rs b/rslib/src/tags/remove.rs index 15606db1d..187b38669 100644 --- a/rslib/src/tags/remove.rs +++ b/rslib/src/tags/remove.rs @@ -1,6 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +use unicase::UniCase; + use super::matcher::TagMatcher; use crate::prelude::*; @@ -84,7 +86,7 @@ impl Collection { .storage .all_tags()? .into_iter() - .filter(|tag| !in_notes.contains(&tag.name)); + .filter(|tag| !in_notes.contains(&UniCase::new(tag.name.clone()))); for tag in need_remove { self.remove_single_tag_undoable(tag)?; count += 1;