Implement and archive Phase 5 integration acceptance
This commit is contained in:
@@ -720,14 +720,14 @@ impl App {
|
||||
for f in &input.forms {
|
||||
catalog.append(&f.catalog());
|
||||
}
|
||||
ensure!(
|
||||
!catalog
|
||||
.objects
|
||||
.iter()
|
||||
.any(|o| o.name.eq_ignore_ascii_case(new)),
|
||||
"Name {new} bereits vorhanden"
|
||||
);
|
||||
let form_name = self.design_form()?.root.name.clone();
|
||||
ensure!(
|
||||
catalog.objects.iter().filter(|o| o.name.eq_ignore_ascii_case(new)).all(|o| {
|
||||
node_key(&selected).1.is_some() && o.array && o.class == selected.class
|
||||
&& o.parent_form.as_deref().is_some_and(|name| name.eq_ignore_ascii_case(&form_name))
|
||||
}),
|
||||
"Name {new} bereits vorhanden; nur indizierte Controls derselben Klasse und Form können ein Array bilden"
|
||||
);
|
||||
let target = if selected.class == Class::Form {
|
||||
catalog.find(&old)
|
||||
} else {
|
||||
@@ -887,27 +887,52 @@ impl App {
|
||||
.join("; ")
|
||||
)
|
||||
})?;
|
||||
let mut candidate_catalog = forms::FormCatalog::default();
|
||||
for form in &candidate.forms {
|
||||
candidate_catalog.append(&form.catalog());
|
||||
}
|
||||
let bindings = |references: &[(tb_frontend::SourcePos, String, u16)],
|
||||
files: &[tb_frontend::source::SourceFile],
|
||||
objects: &forms::FormCatalog,
|
||||
rename_old: bool| {
|
||||
let mut result = Vec::new();
|
||||
for (p, n, o) in references {
|
||||
let mut n = n.clone();
|
||||
let object = &objects.objects[*o as usize];
|
||||
let mut name = object.name.clone();
|
||||
let mut owner = object.parent_form.clone();
|
||||
if rename_old && *o == target {
|
||||
name = new.to_uppercase();
|
||||
if n == old {
|
||||
n = new.to_uppercase();
|
||||
} else if let Some(r) = handlers.get(&n) {
|
||||
n = r.to_uppercase();
|
||||
}
|
||||
}
|
||||
result.push((files[p.source as usize].path.clone(), n, *o));
|
||||
if rename_old && selected.class == Class::Form && owner.as_deref() == Some(&old) {
|
||||
owner = Some(new.to_uppercase());
|
||||
}
|
||||
// Merging arrays changes catalog indices. Compare semantic object identities.
|
||||
result.push((
|
||||
files[p.source as usize].path.clone(),
|
||||
n,
|
||||
name,
|
||||
owner,
|
||||
object.class.name(),
|
||||
object.array,
|
||||
));
|
||||
}
|
||||
result.sort();
|
||||
result
|
||||
};
|
||||
ensure!(
|
||||
bindings(&bound.objects, &source_files, true)
|
||||
== bindings(&candidate_bound.objects, &candidate_compiled.sources, false),
|
||||
bindings(&bound.objects, &source_files, &catalog, true)
|
||||
== bindings(
|
||||
&candidate_bound.objects,
|
||||
&candidate_compiled.sources,
|
||||
&candidate_catalog,
|
||||
false
|
||||
),
|
||||
"Unklare oder verdeckte Objektverweise; nichts geändert"
|
||||
);
|
||||
self.project
|
||||
|
||||
@@ -924,3 +924,63 @@ fn events_with_the_same_name_are_local_to_their_form() {
|
||||
.code()
|
||||
.contains("SUB Form_LOAD"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexed_controls_merge_atomically_with_bindings_and_undo() {
|
||||
let t = Temp::new();
|
||||
let mut a = t.app();
|
||||
let root = root(&mut a);
|
||||
let first = a
|
||||
.design_place(C::CommandButton, 0, root, Rect::new(1, 1, 8, 2))
|
||||
.unwrap();
|
||||
property(&mut a, "INDEX", "0");
|
||||
let target = node(&mut a, first).name;
|
||||
let second = a
|
||||
.design_place(C::CommandButton, 0, root, Rect::new(1, 4, 8, 2))
|
||||
.unwrap();
|
||||
let old = node(&mut a, second).name;
|
||||
// Scalar and duplicate-index merges must leave every document untouched.
|
||||
let before = a.design_form().unwrap().clone();
|
||||
assert!(a.design_rename(&target).is_err());
|
||||
assert_eq!(*a.design_form().unwrap(), before);
|
||||
property(&mut a, "INDEX", "0");
|
||||
let before = a.design_form().unwrap().clone();
|
||||
assert!(a.design_rename(&target).is_err());
|
||||
assert_eq!(*a.design_form().unwrap(), before);
|
||||
property(&mut a, "INDEX", "1");
|
||||
let unrelated = a
|
||||
.design_place(C::TextBox, 0, root, Rect::new(20, 1, 8, 2))
|
||||
.unwrap();
|
||||
let unrelated_name = node(&mut a, unrelated).name;
|
||||
property(&mut a, "INDEX", "2");
|
||||
let before = a.design_form().unwrap().clone();
|
||||
assert!(a.design_rename(&target).is_err());
|
||||
assert_eq!(*a.design_form().unwrap(), before);
|
||||
a.design_select(second, false).unwrap();
|
||||
let doc = a.design_document().unwrap();
|
||||
let source = format!("SUB {old}_Click(Index AS INTEGER)\n{old}(1).Caption = {target}(0).Caption\n{unrelated_name}(2).Text = \"kept\"\nEND SUB\n");
|
||||
a.project.replace_text(doc, 0..0, &source).unwrap();
|
||||
let before = a.design_form().unwrap().clone();
|
||||
a.design_rename(&target).unwrap();
|
||||
assert_eq!(node(&mut a, second).name, target);
|
||||
let code = &a.design_form().unwrap().code;
|
||||
assert!(code
|
||||
.to_uppercase()
|
||||
.contains(&format!("SUB {}_CLICK", target.to_uppercase())));
|
||||
assert!(code.contains(&format!("{target}(1).Caption")));
|
||||
assert!(code.contains(&format!("{unrelated_name}(2).Text")));
|
||||
a.execute(Command::Undo);
|
||||
assert_eq!(*a.design_form().unwrap(), before);
|
||||
// Two handlers for the merged event are ambiguous and cannot be committed.
|
||||
a.project
|
||||
.replace_text(
|
||||
doc,
|
||||
0..0,
|
||||
&format!("SUB {target}_Click(Index AS INTEGER)\nEND SUB\n"),
|
||||
)
|
||||
.unwrap();
|
||||
let before = a.design_form().unwrap().clone();
|
||||
a.design_select(second, false).unwrap();
|
||||
assert!(a.design_rename(&target).is_err());
|
||||
assert_eq!(*a.design_form().unwrap(), before);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user