mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-28 12:26:52 -06:00
#361: a replacement that only changes the case replaces every occurrence; the text's chars are collected once
This commit is contained in:
@@ -1009,9 +1009,14 @@ fn find_replace(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let doc = s.doc_mut()?;
|
||||
let mut hits = Vec::new();
|
||||
let needle: Vec<char> = find.chars().collect();
|
||||
let r_chars: Vec<char> = replace.as_deref().map(|r| r.chars().collect()).unwrap_or_default();
|
||||
for i in doc.subtree(page) {
|
||||
let Some(n) = doc.node(i) else { continue };
|
||||
let text_hit = matches!(&n.kind, NodeKind::Text(t) if find_ci(&t.characters.chars().collect::<Vec<_>>(), &needle, 0).is_some());
|
||||
let text: Option<Vec<char>> = match &n.kind {
|
||||
NodeKind::Text(t) => Some(t.characters.chars().collect()),
|
||||
_ => None,
|
||||
};
|
||||
let text_hit = text.as_deref().is_some_and(|c| find_ci(c, &needle, 0).is_some());
|
||||
let name_hit = names && find_ci(&n.name.chars().collect::<Vec<_>>(), &needle, 0).is_some();
|
||||
if !(text_hit || name_hit) || n.is_page() {
|
||||
continue;
|
||||
@@ -1031,24 +1036,16 @@ fn find_replace(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
out.extend(chars.get(at..).unwrap_or_default());
|
||||
n.name = out;
|
||||
}
|
||||
if let NodeKind::Text(t) = &mut n.kind {
|
||||
// Replace occurrence by occurrence so styles shift correctly.
|
||||
// The search resumes after each inserted replacement, so a replacement that
|
||||
// contains `find` neither loops forever nor stops the other occurrences.
|
||||
let mut guard = 0;
|
||||
if let (NodeKind::Text(t), Some(mut chars)) = (&mut n.kind, text) {
|
||||
// Replace occurrence by occurrence so styles shift correctly. The search resumes
|
||||
// after each inserted replacement, so a replacement that matches `find` (in any
|
||||
// case) neither loops forever nor stops the other occurrences.
|
||||
let mut cursor = 0;
|
||||
let len = find.chars().count();
|
||||
let r_len = r.chars().count();
|
||||
loop {
|
||||
let from = t.characters.char_indices().nth(cursor).map_or(t.characters.len(), |(b, _)| b);
|
||||
let Some(pos) = t.characters.get(from..).and_then(|tail| tail.find(find.as_str())) else { break };
|
||||
let start = cursor + t.characters.get(from..from + pos).map_or(0, |x| x.chars().count());
|
||||
t.replace_range(start, start + len, r);
|
||||
cursor = start + r_len;
|
||||
guard += 1;
|
||||
if guard > 10_000 {
|
||||
break;
|
||||
}
|
||||
for _ in 0..10_000 {
|
||||
let Some(start) = find_ci(&chars, &needle, cursor) else { break };
|
||||
t.replace_range(start, start + needle.len(), r);
|
||||
chars.splice(start..start + needle.len(), r_chars.iter().copied());
|
||||
cursor = start + r_chars.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,3 +611,13 @@ fn find_replace_matches_and_replaces_case_insensitively() {
|
||||
assert_eq!(chars(&s, u), "İ bye bye");
|
||||
assert_eq!(s.doc().unwrap().node(f).unwrap().name, "bye box");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_replace_changing_only_the_case_replaces_every_occurrence() {
|
||||
let mut s = sess();
|
||||
let t = id(&s.execute("text.create", &json!({"text": "figcraft and FIGCRAFT, figcraft", "x": 0, "y": 0})).unwrap());
|
||||
let f = id(&s.execute("frame.create", &json!({"x": 0, "y": 80, "width": 50, "height": 50, "name": "figcraft / figcraft"})).unwrap());
|
||||
s.execute("edit.findReplace", &json!({"find": "figcraft", "replace": "FigCraft", "layerNames": true})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().node(t).unwrap().kind.text().unwrap().characters, "FigCraft and FigCraft, FigCraft");
|
||||
assert_eq!(s.doc().unwrap().node(f).unwrap().name, "FigCraft / FigCraft");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user