- Neues Dokument 07-DFM-FMX-Support.md: Format-Referenz, Extraktions-Strategie, DfmExtractor-Konzept - Neues Fixture MainForm.dfm: Verschachtelte Komponenten, Event-Handler (FormCreate, btnLoginClick, etc.) - Integration Guide erweitert: DfmExtractor-Implementierung (analog LiquidExtractor), Coding-Style-Hinweise - NodeKind-Mapping erweitert: DFM-Komponenten als component, Event-Handler als references, Verschachtelung als contains - Checklist: DFM/FMX von 'Zukunft' auf konkreten Plan (Phase 1b) hochgestuft - README aktualisiert: DFM/FMX-Support in Ziel, Inhalt und Grammar-Abschnitt aufgenommen
150 lines
5.4 KiB
Markdown
150 lines
5.4 KiB
Markdown
# DFM/FMX Form-Dateien: Format-Referenz & Extraktions-Konzept
|
|
|
|
Delphi-Projekte bestehen nicht nur aus `.pas`-Dateien, sondern auch aus **Form-Dateien** (`.dfm` für VCL, `.fmx` für FireMonkey). Diese enthalten die visuelle Komponentenhierarchie und — besonders wertvoll für CodeGraph — die **Event-Handler-Verknüpfungen** zwischen UI-Komponenten und Pascal-Methoden.
|
|
|
|
## Warum DFM/FMX für CodeGraph relevant sind
|
|
|
|
| Information | Wert für CodeGraph |
|
|
|---|---|
|
|
| Komponenten-Deklarationen (`object Button1: TButton`) | NodeKind `component` — zeigt welche UI-Elemente existieren |
|
|
| Event-Handler (`OnClick = Button1Click`) | EdgeKind `references` — verknüpft UI mit Code in `.pas` |
|
|
| Komponenten-Hierarchie (verschachtelte `object`-Blöcke) | EdgeKind `contains` — zeigt Parent-Child-Beziehungen |
|
|
| Komponenten-Typ (`TButton`, `TPanel`, `TEdit`) | Metadata — Typ-Information für Impact-Analyse |
|
|
|
|
**Besonders wertvoll:** Wenn ein Entwickler eine Methode in der `.pas`-Datei umbenennt, zeigt CodeGraph sofort, dass ein DFM-Event-Handler darauf verweist → Impact-Analyse funktioniert über Dateigrenzen hinweg.
|
|
|
|
## DFM-Textformat
|
|
|
|
DFM-Dateien können in **Text** oder **Binär** gespeichert werden. Moderne Delphi-Projekte verwenden fast ausschließlich das Textformat (besser für Versionskontrolle). FMX-Dateien verwenden das gleiche Textformat.
|
|
|
|
### Grundstruktur
|
|
|
|
```
|
|
object Form1: TForm1
|
|
Left = 0
|
|
Top = 0
|
|
Caption = 'Hauptformular'
|
|
ClientHeight = 400
|
|
ClientWidth = 600
|
|
OnCreate = FormCreate
|
|
OnDestroy = FormDestroy
|
|
object Panel1: TPanel
|
|
Left = 0
|
|
Top = 0
|
|
Width = 600
|
|
Height = 50
|
|
Align = alTop
|
|
object Label1: TLabel
|
|
Left = 16
|
|
Top = 16
|
|
Caption = 'Willkommen'
|
|
end
|
|
object Button1: TButton
|
|
Left = 500
|
|
Top = 12
|
|
Caption = 'Login'
|
|
OnClick = Button1Click
|
|
end
|
|
end
|
|
object Memo1: TMemo
|
|
Left = 0
|
|
Top = 50
|
|
Width = 600
|
|
Height = 350
|
|
Align = alClient
|
|
end
|
|
end
|
|
```
|
|
|
|
### Syntax-Regeln
|
|
|
|
1. **Top-Level:** `object <Name>: <Typ>` — das Formular selbst
|
|
2. **Verschachtelte Komponenten:** `object <Name>: <Typ>` innerhalb eines anderen `object`-Blocks
|
|
3. **Properties:** `<Key> = <Value>` — einfache Zuweisung
|
|
4. **Event-Handler:** `On<Event> = <MethodenName>` — Verknüpfung zu Pascal-Methode
|
|
5. **Ende:** `end` schließt jeden `object`-Block
|
|
6. **Vererbung:** `inherited <Name>: <Typ>` statt `object` bei geerbten Formularen
|
|
7. **Inline-Objekte:** `inline <Name>: <Typ>` für inline erstellte Objekte
|
|
|
|
### Event-Handler erkennen
|
|
|
|
Event-Handler sind Properties, deren **Key mit `On` beginnt** und deren **Value ein Bezeichner** (kein String, keine Zahl) ist:
|
|
|
|
```
|
|
OnClick = Button1Click ← Event-Handler → references Button1Click
|
|
OnChange = EditChanged ← Event-Handler → references EditChanged
|
|
Caption = 'Nicht ein Event' ← Normales Property (String-Wert)
|
|
Left = 100 ← Normales Property (Zahl-Wert)
|
|
Align = alTop ← Normales Property (Enum-Wert)
|
|
```
|
|
|
|
### Mehrzeilige Properties
|
|
|
|
Einige Properties erstrecken sich über mehrere Zeilen:
|
|
|
|
```
|
|
SQL.Strings = (
|
|
'SELECT * FROM users'
|
|
'WHERE active = 1'
|
|
'ORDER BY name')
|
|
Items.Strings = (
|
|
'Option A'
|
|
'Option B')
|
|
```
|
|
|
|
Diese sind für CodeGraph weniger relevant, müssen aber beim Parsen korrekt übersprungen werden.
|
|
|
|
## Extraktions-Strategie: DfmExtractor
|
|
|
|
Analog zum `LiquidExtractor` und `SvelteExtractor` wird ein **`DfmExtractor`** als Custom Extractor implementiert — **ohne tree-sitter**, rein Regex/Zeilen-basiert.
|
|
|
|
### Zu extrahierende Nodes
|
|
|
|
| DFM-Element | CodeGraph NodeKind | Beispiel |
|
|
|---|---|---|
|
|
| Top-Level `object` | `component` | `Form1: TForm1` |
|
|
| Verschachtelte `object` | `component` | `Button1: TButton` |
|
|
| `inherited` | `component` | Geerbte Komponente |
|
|
|
|
### Zu extrahierende Edges
|
|
|
|
| DFM-Beziehung | CodeGraph EdgeKind | Beschreibung |
|
|
|---|---|---|
|
|
| Verschachtelung | `contains` | Panel1 enthält Button1 |
|
|
| Event-Handler | `references` | Button1 → `Button1Click` (Methode in .pas) |
|
|
| Datei enthält Komponente | `contains` | DFM-Datei → Form1 |
|
|
|
|
### Parsing-Algorithmus (Pseudocode)
|
|
|
|
```
|
|
für jede Zeile:
|
|
wenn "object <Name>: <Typ>" oder "inherited <Name>: <Typ>":
|
|
→ neuen component-Node erstellen
|
|
→ contains-Edge vom Parent
|
|
→ auf Stack pushen
|
|
wenn "end":
|
|
→ Stack poppen
|
|
wenn "<Key> = <Value>" und Key beginnt mit "On" und Value ist Bezeichner:
|
|
→ references-Edge: aktuelle Komponente → Value (Methodenname)
|
|
→ als UnresolvedReference speichern (wird später zur .pas-Methode aufgelöst)
|
|
wenn mehrzeiliges Property (endet mit "("):
|
|
→ Zeilen überspringen bis ")"
|
|
```
|
|
|
|
### Verknüpfung DFM ↔ PAS
|
|
|
|
Die Zuordnung DFM → PAS erfolgt über den **Dateinamen**:
|
|
- `MainForm.dfm` gehört zu `MainForm.pas` (gleicher Basename)
|
|
- Event-Handler `Button1Click` → Methode `TForm1.Button1Click` in der zugehörigen `.pas`-Datei
|
|
|
|
Diese Verknüpfung erfolgt in der **Resolution-Phase**, nicht beim Parsen.
|
|
|
|
## Kein tree-sitter nötig
|
|
|
|
Es gibt keinen tree-sitter Parser für DFM/FMX. Das Format ist aber so einfach strukturiert (zeilenbasiert, keine komplexe Grammatik), dass ein Regex-basierter Parser vollkommen ausreicht — genau wie bei Liquid-Templates.
|
|
|
|
## Phase
|
|
|
|
DFM/FMX-Support wird als **Phase 1b** eingeplant — parallel zum Pascal-Support, da die Verknüpfung DFM ↔ PAS den größten Mehrwert liefert, wenn beides gleichzeitig verfügbar ist.
|
|
|