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.
| 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-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.
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
object <Name>: <Typ> — das Formular selbstobject <Name>: <Typ> innerhalb eines anderen object-Blocks<Key> = <Value> — einfache ZuweisungOn<Event> = <MethodenName> — Verknüpfung zu Pascal-Methodeend schließt jeden object-Blockinherited <Name>: <Typ> statt object bei geerbten Formulareninline <Name>: <Typ> für inline erstellte ObjekteEvent-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)
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.
Analog zum LiquidExtractor und SvelteExtractor wird ein DfmExtractor als Custom Extractor implementiert — ohne tree-sitter, rein Regex/Zeilen-basiert.
| DFM-Element | CodeGraph NodeKind | Beispiel |
|---|---|---|
Top-Level object |
component |
Form1: TForm1 |
Verschachtelte object |
component |
Button1: TButton |
inherited |
component |
Geerbte Komponente |
| 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 |
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 ")"
Die Zuordnung DFM → PAS erfolgt über den Dateinamen:
MainForm.dfm gehört zu MainForm.pas (gleicher Basename)Button1Click → Methode TForm1.Button1Click in der zugehörigen .pas-DateiDiese Verknüpfung erfolgt in der Resolution-Phase, nicht beim Parsen.
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.
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.