Delphi-Konzept: DFM/FMX Support hinzugefügt
- 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
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
# 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.
|
||||
|
||||
Reference in New Issue
Block a user