Implement and archive Phase 5 form designer

This commit is contained in:
2026-09-06 20:19:57 +02:00
parent e3dc9028bf
commit ce97700a98
27 changed files with 3625 additions and 167 deletions

View File

@@ -751,3 +751,17 @@ impl FormCatalog {
false
}
}
/// Tastenkombinationen, die der Forms-Host für Menü-Shortcuts liefert.
pub fn menu_shortcuts() -> Vec<String> {
let mut out = vec![String::new()];
for prefix in ["", "Ctrl+", "Alt+", "Shift+", "Ctrl+Shift+"] {
for n in 1..=10 {
out.push(format!("{prefix}F{n}"));
}
}
for c in ('A'..='Z').filter(|c| *c != 'C') {
out.push(format!("Ctrl+{c}"));
}
out
}

View File

@@ -421,6 +421,23 @@ pub fn lower_with_forms(
(hir, s.diags)
}
/// Semantisch gebundene Objekt-/Ereignisnamen für transaktionale IDE-Umbenennungen.
/// Aufrufer validieren die gesamte Übersetzungseinheit einschließlich ihrer Imports.
#[derive(Default)]
pub struct BoundFormReferences {
pub objects: Vec<(SourcePos, String, u16)>,
pub procedures: Vec<(SourcePos, String)>,
pub diagnostics: Vec<Diagnostic>,
}
pub fn bound_form_references(module: &Module, catalog: &FormCatalog) -> BoundFormReferences {
let mut s = new_sema(module, catalog);
s.references = Some(BoundFormReferences::default());
s.run(module);
let mut references = s.references.unwrap();
references.diagnostics = s.diags;
references
}
fn new_sema(module: &Module, catalog: &FormCatalog) -> Sema {
Sema {
diags: Vec::new(),
@@ -446,6 +463,7 @@ fn new_sema(module: &Module, catalog: &FormCatalog) -> Sema {
forms: catalog.clone(),
module_name: module.name.clone(),
event_procs: Vec::new(),
references: None,
}
}
@@ -550,6 +568,7 @@ struct Sema {
forms: FormCatalog,
module_name: String,
event_procs: Vec<hir::HEventProc>,
references: Option<BoundFormReferences>,
}
impl Sema {
@@ -942,6 +961,9 @@ impl Sema {
{
return;
}
if let Some(r) = &mut self.references {
r.objects.push((proc.pos, proc.sig.name.clone(), object));
}
let mut expected: Vec<(&str, forms::EventParamType)> = Vec::new();
if self.forms.objects[object as usize].array {
expected.push(("INDEX", forms::EventParamType::Integer));
@@ -1049,6 +1071,14 @@ impl Sema {
) -> Option<(u16, forms::FormObject)> {
if let Some(parent) = parent {
if let Some((id, object)) = self.forms.find_in(name, parent) {
if let Some(r) = &mut self.references {
r.objects.push((pos, name.into(), id));
}
if let Some((form, _)) = self.forms.find(parent) {
if let Some(r) = &mut self.references {
r.objects.push((pos, parent.into(), form));
}
}
return Some((id, object.clone()));
}
self.err(
@@ -1067,7 +1097,11 @@ impl Sema {
pos: SourcePos,
) -> Option<(u16, crate::forms::FormObject)> {
if let Some((id, object)) = self.find_object(name) {
return Some((id, object.clone()));
let object = object.clone();
if let Some(r) = &mut self.references {
r.objects.push((pos, name.into(), id));
}
return Some((id, object));
}
self.err(pos, format!("Unknown object '{name}'"));
None
@@ -2391,7 +2425,11 @@ impl Sema {
OptionKind::Base(b) => self.option_base = *b,
},
Stmt::TypeDecl { .. } => {} // bereits in Pass 1 registriert
Stmt::Declare { .. } => {} // bereits in Pass 1 registriert
Stmt::Declare { sig, pos } => {
if let Some(r) = &mut self.references {
r.procedures.push((*pos, sig.name.clone()));
}
} // in Pass 1 registriert
Stmt::Call {
name, args, pos, ..
} => {
@@ -3377,6 +3415,9 @@ impl Sema {
pos: SourcePos,
out: &mut Vec<HStmt>,
) {
if let Some(r) = &mut self.references {
r.procedures.push((pos, name.into()));
}
if name == "CLIPBOARD.ADDITEM" {
if args.len() != 1 {
self.err(pos, "Argument-count mismatch for CLIPBOARD.ADDITEM");
@@ -4834,6 +4875,9 @@ impl Sema {
.map(|(id, info)| (id, info.clone()))
{
if args.is_none() || info.array {
if let Some(r) = &mut self.references {
r.objects.push((pos, name.into(), object));
}
let Ok(index) = self.object_index(object, args, scope, pos) else {
return (HExpr::Int(0), Ty::Unknown);
};