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:
@@ -3,9 +3,9 @@
|
||||
## A) Plumbing (Infrastruktur)
|
||||
- [ ] `npm install tree-sitter-pascal` als Dependency
|
||||
- [ ] `src/types.ts`: `'pascal'` zum `Language` Union-Type hinzufügen
|
||||
- [ ] `src/types.ts`: `DEFAULT_CONFIG.include` um `'**/*.pas'`, `'**/*.dpr'`, `'**/*.dpk'`, `'**/*.lpr'` erweitern
|
||||
- [ ] `src/types.ts`: `DEFAULT_CONFIG.include` um `'**/*.pas'`, `'**/*.dpr'`, `'**/*.dpk'`, `'**/*.lpr'`, `'**/*.dfm'`, `'**/*.fmx'` erweitern
|
||||
- [ ] `src/extraction/grammars.ts`: Grammar-Loader `pascal: () => require('tree-sitter-pascal')`
|
||||
- [ ] `src/extraction/grammars.ts`: Extension-Mapping `.pas`, `.dpr`, `.dpk`, `.lpr` → `'pascal'`
|
||||
- [ ] `src/extraction/grammars.ts`: Extension-Mapping `.pas`, `.dpr`, `.dpk`, `.lpr`, `.dfm`, `.fmx` → `'pascal'`
|
||||
- [ ] `src/extraction/grammars.ts`: Display-Name `pascal: 'Pascal / Delphi'`
|
||||
- [ ] `src/extraction/tree-sitter.ts`: `LanguageExtractor` für `pascal` in `EXTRACTORS` Map
|
||||
|
||||
@@ -53,9 +53,19 @@
|
||||
- [ ] Language in Supported-Languages-Dokumentation aufnehmen
|
||||
- [ ] CLI-Hilfe / Config-Validierung aktualisieren
|
||||
|
||||
## H) Bekannte Einschränkungen (für spätere Phasen)
|
||||
## H) DFM/FMX Form-Dateien (Phase 1b)
|
||||
- [ ] `DfmExtractor`-Klasse in `src/extraction/tree-sitter.ts` implementieren (analog `LiquidExtractor`)
|
||||
- [ ] Routing in `extractFromSource()`: `.dfm`/`.fmx` → `DfmExtractor`
|
||||
- [ ] Komponenten als NodeKind `component` extrahieren (`object <Name>: <Typ>`)
|
||||
- [ ] Verschachtelung als EdgeKind `contains` abbilden
|
||||
- [ ] Event-Handler (`OnClick = MethodName`) als `UnresolvedReference` speichern → EdgeKind `references`
|
||||
- [ ] Mehrzeilige Properties korrekt überspringen
|
||||
- [ ] `inherited`-Blöcke als `component` behandeln
|
||||
- [ ] DFM-Fixture (`MainForm.dfm`) in Test-Suite aufnehmen
|
||||
- [ ] Resolution: Event-Handler-Namen zu Methoden in zugehöriger `.pas`-Datei auflösen
|
||||
|
||||
## I) Bekannte Einschränkungen (für spätere Phasen)
|
||||
- [ ] **`with`-Statements** (Phase 2): Verschleiern den Qualifier bei Aufrufen. Im MVP werden Calls innerhalb von `with`-Blöcken ohne Qualifier extrahiert.
|
||||
- [ ] **`.dfm`/`.fmx` Form-Dateien** (Zukunft): Enthalten Komponenten-Deklarationen und Event-Verknüpfungen. Aktuell nicht unterstützt.
|
||||
- [ ] **`.inc` Include-Dateien** (Optional): Enthalten Code-Fragmente, die per `{$I filename}` eingebunden werden. Parser liefert möglicherweise keinen vollständigen AST.
|
||||
- [ ] **Class Helpers / Record Helpers** (Phase 2): `declHelper`-Knoten erweitern existierende Typen. Erfordert spezielle Resolution-Logik.
|
||||
- [ ] **Generics** (Phase 2): `typerefTpl` und `genericTpl` für generische Typen.
|
||||
|
||||
@@ -43,13 +43,32 @@ Dieses Dokument zeigt die explizite Zuordnung von Delphi/Pascal-Konzepten zu den
|
||||
| Unit enthält Klasse | AST Parent-Child | `contains` |
|
||||
| Klasse enthält Methode | AST Parent-Child | `contains` |
|
||||
|
||||
## DFM/FMX Node-Mapping
|
||||
|
||||
DFM/FMX-Dateien werden durch einen eigenen `DfmExtractor` (Regex-basiert, analog `LiquidExtractor`) verarbeitet.
|
||||
|
||||
| DFM-Konzept | Erkennung | CodeGraph `NodeKind` | Anmerkungen |
|
||||
|---|---|---|---|
|
||||
| Formular | `object Form1: TForm1` (Top-Level) | `component` | Root-Komponente der DFM-Datei |
|
||||
| UI-Komponente | `object Button1: TButton` (verschachtelt) | `component` | Jede Komponente wird ein Node |
|
||||
| Geerbte Komponente | `inherited Form1: TForm1` | `component` | Formular-Vererbung |
|
||||
|
||||
## DFM/FMX Edge-Mapping
|
||||
|
||||
| DFM-Beziehung | Erkennung | CodeGraph `EdgeKind` | Beschreibung |
|
||||
|---|---|---|---|
|
||||
| Verschachtelung | `object` innerhalb `object` | `contains` | Panel1 enthält Button1 |
|
||||
| Event-Handler | `OnClick = Button1Click` | `references` | Komponente → Methode in `.pas` |
|
||||
| Datei enthält Formular | Top-Level `object` | `contains` | DFM-Datei → Root-Komponente |
|
||||
|
||||
> **Hinweis:** Event-Handler-Verknüpfungen werden als `UnresolvedReference` gespeichert und in der Resolution-Phase zur entsprechenden Methode in der zugehörigen `.pas`-Datei aufgelöst.
|
||||
|
||||
## Nicht abgebildete Konzepte (Phase 2+)
|
||||
|
||||
| Konzept | Grund |
|
||||
|---|---|
|
||||
| `with`-Statement | Kein eigener EdgeKind nötig; erschwert nur die Call-Qualifizierung |
|
||||
| Class Helper | Kein eigener NodeKind; als `class` mit speziellem Bezug zum erweiterten Typ |
|
||||
| `.dfm`/`.fmx` Komponenten | Kein Code im Pascal-Sinne; erfordert eigenen Parser |
|
||||
| `exports`-Klauseln | `declExports` → könnte als `export` NodeKind abgebildet werden |
|
||||
| Generics | `typerefTpl` → beeinflusst Typ-Referenzen, nicht den NodeKind |
|
||||
|
||||
|
||||
@@ -134,9 +134,224 @@ pascal: {
|
||||
},
|
||||
```
|
||||
|
||||
## Hinweise
|
||||
## Hinweise zur Pascal-Integration
|
||||
|
||||
- `declProc` wird sowohl für `functionTypes` als auch `methodTypes` verwendet. CodeGraph unterscheidet anhand des AST-Kontexts (Parent ist `declClass` → method, sonst → function).
|
||||
- Die `getVisibility`-Funktion traversiert aufwärts zum `declSection`-Knoten.
|
||||
- Records werden als `class` behandelt, da `declClass` mit `kRecord` Kind-Knoten beides abdeckt.
|
||||
|
||||
---
|
||||
|
||||
## 5) DFM/FMX-Support: Custom Extractor
|
||||
|
||||
DFM/FMX-Dateien verwenden ein einfaches zeilenbasiertes Textformat (kein tree-sitter Parser vorhanden). Die Integration erfolgt analog zu `LiquidExtractor` und `SvelteExtractor` als **Custom Extractor**.
|
||||
|
||||
### Extension-Mapping ergänzen (`src/extraction/grammars.ts`)
|
||||
|
||||
```typescript
|
||||
export const EXTENSION_MAP: Record<string, Language> = {
|
||||
// ...bestehende Mappings...
|
||||
'.pas': 'pascal',
|
||||
'.dpr': 'pascal',
|
||||
'.dpk': 'pascal',
|
||||
'.lpr': 'pascal',
|
||||
'.dfm': 'pascal', // ← NEU: DFM-Formulare
|
||||
'.fmx': 'pascal', // ← NEU: FMX-Formulare
|
||||
};
|
||||
```
|
||||
|
||||
> **Hinweis:** DFM/FMX werden als Sprache `pascal` registriert. Die Unterscheidung zwischen tree-sitter-Parsing (`.pas`) und Custom Extractor (`.dfm`/`.fmx`) erfolgt in `extractFromSource()` anhand der Dateiendung.
|
||||
|
||||
### DEFAULT_CONFIG.include ergänzen (`src/types.ts`)
|
||||
|
||||
```typescript
|
||||
include: [
|
||||
// ...bestehende Patterns...
|
||||
'**/*.pas',
|
||||
'**/*.dpr',
|
||||
'**/*.dpk',
|
||||
'**/*.lpr',
|
||||
'**/*.dfm', // ← NEU
|
||||
'**/*.fmx', // ← NEU
|
||||
],
|
||||
```
|
||||
|
||||
### Routing in `extractFromSource()` (`src/extraction/tree-sitter.ts`)
|
||||
|
||||
```typescript
|
||||
export function extractFromSource(
|
||||
filePath: string,
|
||||
source: string,
|
||||
language?: Language
|
||||
): ExtractionResult {
|
||||
const detectedLanguage = language || detectLanguage(filePath);
|
||||
|
||||
// Use custom extractor for Svelte
|
||||
if (detectedLanguage === 'svelte') {
|
||||
const extractor = new SvelteExtractor(filePath, source);
|
||||
return extractor.extract();
|
||||
}
|
||||
|
||||
// Use custom extractor for Liquid
|
||||
if (detectedLanguage === 'liquid') {
|
||||
const extractor = new LiquidExtractor(filePath, source);
|
||||
return extractor.extract();
|
||||
}
|
||||
|
||||
// Use custom extractor for DFM/FMX form files ← NEU
|
||||
if (detectedLanguage === 'pascal' &&
|
||||
(filePath.endsWith('.dfm') || filePath.endsWith('.fmx'))) {
|
||||
const extractor = new DfmExtractor(filePath, source);
|
||||
return extractor.extract();
|
||||
}
|
||||
|
||||
const extractor = new TreeSitterExtractor(filePath, source, detectedLanguage);
|
||||
return extractor.extract();
|
||||
}
|
||||
```
|
||||
|
||||
### DfmExtractor Klasse (Coding Style analog LiquidExtractor)
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Custom extractor for Delphi DFM/FMX form files.
|
||||
*
|
||||
* DFM/FMX files describe the visual component hierarchy and event handler
|
||||
* bindings. They use a simple text format (object/end blocks) that we parse
|
||||
* with regex — no tree-sitter grammar exists for this format.
|
||||
*
|
||||
* Extracted information:
|
||||
* - Components as NodeKind `component`
|
||||
* - Nesting as EdgeKind `contains`
|
||||
* - Event handlers (OnClick = MethodName) as UnresolvedReference → EdgeKind `references`
|
||||
*/
|
||||
export class DfmExtractor {
|
||||
private filePath: string;
|
||||
private source: string;
|
||||
private nodes: Node[] = [];
|
||||
private edges: Edge[] = [];
|
||||
private unresolvedReferences: UnresolvedReference[] = [];
|
||||
private errors: ExtractionError[] = [];
|
||||
|
||||
constructor(filePath: string, source: string) {
|
||||
this.filePath = filePath;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract components and event handler references from DFM/FMX source
|
||||
*/
|
||||
extract(): ExtractionResult {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const fileNode = this.createFileNode();
|
||||
this.parseComponents(fileNode.id);
|
||||
} catch (error) {
|
||||
captureException(error, { operation: 'dfm-extraction', filePath: this.filePath });
|
||||
this.errors.push({
|
||||
message: `DFM extraction error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
severity: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
nodes: this.nodes,
|
||||
edges: this.edges,
|
||||
unresolvedReferences: this.unresolvedReferences,
|
||||
errors: this.errors,
|
||||
durationMs: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
|
||||
/** Create a file node for the DFM form file */
|
||||
private createFileNode(): Node { /* ... analog LiquidExtractor ... */ }
|
||||
|
||||
/** Parse object/end blocks and extract components + event handlers */
|
||||
private parseComponents(fileNodeId: string): void {
|
||||
const lines = this.source.split('\n');
|
||||
const stack: string[] = [fileNodeId]; // Stack der Parent-Node-IDs
|
||||
|
||||
const objectPattern = /^\s*(object|inherited|inline)\s+(\w+)\s*:\s*(\w+)/;
|
||||
const eventPattern = /^\s*(On\w+)\s*=\s*(\w+)/;
|
||||
const endPattern = /^\s*end\s*$/;
|
||||
const multiLineStart = /=\s*\(\s*$/;
|
||||
let inMultiLine = false;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const lineNum = i + 1;
|
||||
|
||||
// Mehrzeilige Properties überspringen
|
||||
if (inMultiLine) {
|
||||
if (line.trimEnd().endsWith(')')) inMultiLine = false;
|
||||
continue;
|
||||
}
|
||||
if (multiLineStart.test(line)) {
|
||||
inMultiLine = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Component-Deklaration
|
||||
const objMatch = line.match(objectPattern);
|
||||
if (objMatch) {
|
||||
const [, , name, typeName] = objMatch;
|
||||
const nodeId = generateNodeId(this.filePath, 'component', name, lineNum);
|
||||
this.nodes.push({
|
||||
id: nodeId,
|
||||
kind: 'component',
|
||||
name,
|
||||
qualifiedName: `${this.filePath}#${name}`,
|
||||
filePath: this.filePath,
|
||||
language: 'pascal',
|
||||
startLine: lineNum,
|
||||
endLine: lineNum, // wird beim zugehörigen 'end' aktualisiert
|
||||
startColumn: 0,
|
||||
endColumn: line.length,
|
||||
metadata: { componentType: typeName },
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
this.edges.push({
|
||||
source: stack[stack.length - 1],
|
||||
target: nodeId,
|
||||
kind: 'contains',
|
||||
});
|
||||
stack.push(nodeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Event-Handler
|
||||
const eventMatch = line.match(eventPattern);
|
||||
if (eventMatch) {
|
||||
const [, , methodName] = eventMatch;
|
||||
this.unresolvedReferences.push({
|
||||
sourceNodeId: stack[stack.length - 1],
|
||||
targetName: methodName,
|
||||
kind: 'references',
|
||||
filePath: this.filePath,
|
||||
line: lineNum,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Block-Ende
|
||||
if (endPattern.test(line)) {
|
||||
if (stack.length > 1) stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Coding-Style-Hinweise
|
||||
|
||||
Der `DfmExtractor` folgt dem bestehenden Coding Style der Custom Extractors:
|
||||
|
||||
- **JSDoc-Kommentare** (`/** */`) für jede öffentliche Methode und die Klasse selbst
|
||||
- **Private Felder** für `nodes`, `edges`, `unresolvedReferences`, `errors`
|
||||
- **`generateNodeId()`** für deterministische Node-IDs
|
||||
- **`captureException()`** für Error-Tracking in catch-Blöcken
|
||||
- **`ExtractionResult`** als Return-Type von `extract()`
|
||||
- **`UnresolvedReference`** für Event-Handler (werden in der Resolution-Phase aufgelöst)
|
||||
- **Kein tree-sitter** — rein Regex/zeilenbasiertes Parsing
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+8
-7
@@ -2,7 +2,7 @@
|
||||
|
||||
Dieses Verzeichnis enthält einen **umsetzbaren Bauplan** für Pascal/Delphi-Support in CodeGraph. Es basiert auf der tatsächlichen CodeGraph-Architektur und den verifizierten AST-Knotentypen von [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal).
|
||||
|
||||
**Ziel:** CodeGraph soll **Pascal/Delphi**-Dateien indexieren und daraus **Nodes** (Units, Klassen, Methoden, Properties, Enums, …) sowie **Edges** (uses/imports, calls, extends, implements, …) extrahieren.
|
||||
**Ziel:** CodeGraph soll **Pascal/Delphi**-Dateien indexieren und daraus **Nodes** (Units, Klassen, Methoden, Properties, Enums, …) sowie **Edges** (uses/imports, calls, extends, implements, …) extrahieren. Zusätzlich werden **DFM/FMX-Formulardateien** unterstützt, um UI-Komponenten und Event-Handler-Verknüpfungen zu erfassen.
|
||||
|
||||
## Inhalt
|
||||
|
||||
@@ -13,8 +13,9 @@ Dieses Verzeichnis enthält einen **umsetzbaren Bauplan** für Pascal/Delphi-Sup
|
||||
| `Docs/03-AST-Referenz.md` | Verifizierte AST-Knotentypen aus tree-sitter-pascal |
|
||||
| `Docs/04-Checklist.md` | Umsetzungs-Checkliste mit bekannten Einschränkungen |
|
||||
| `Docs/05-NodeKind-Mapping.md` | Explizite Zuordnung Delphi → CodeGraph NodeKind/EdgeKind |
|
||||
| `Docs/06-Integration-Guide.md` | Konkrete Code-Diffs für `grammars.ts`, `types.ts`, `tree-sitter.ts` |
|
||||
| `fixtures/` | Delphi-Beispieldateien zum Testen der Extraktion |
|
||||
| `Docs/06-Integration-Guide.md` | Konkrete Code-Diffs für `grammars.ts`, `types.ts`, `tree-sitter.ts` + DfmExtractor |
|
||||
| `Docs/07-DFM-FMX-Support.md` | DFM/FMX Form-Dateien: Format-Referenz & Extraktions-Konzept |
|
||||
| `fixtures/` | Delphi-Beispieldateien zum Testen (`.pas`, `.dpr`, `.dfm`) |
|
||||
| `resolution/` | Resolver-Heuristiken (Unit-Mapping, Call-Resolution) |
|
||||
|
||||
## Architektur-Übersicht
|
||||
@@ -33,8 +34,8 @@ CodeGraph verwendet **keine** per-Sprache Plugin-Verzeichnisse. Die Integration
|
||||
4. **Resolver erweitern** (mindestens `uses` → Unit-File, einfache Call-Auflösung)
|
||||
5. **Fixtures in die Tests aufnehmen** und Assertions hinzufügen
|
||||
|
||||
## Grammar
|
||||
## Grammar & Parsing
|
||||
|
||||
Verwendet wird: [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) (npm: `tree-sitter-pascal`)
|
||||
- Unterstützt: Delphi, FreePascal, Standard-Pascal
|
||||
- Dateierweiterungen: `.pas`, `.dpr`, `.dpk`, `.lpr`
|
||||
**Pascal-Dateien** (`.pas`, `.dpr`, `.dpk`, `.lpr`) werden mit [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) geparst (AST-basierte Extraktion via `LanguageExtractor`).
|
||||
|
||||
**DFM/FMX-Dateien** (`.dfm`, `.fmx`) werden mit einem **Custom Extractor** (`DfmExtractor`) verarbeitet — analog zu `LiquidExtractor` und `SvelteExtractor`. Das DFM-Textformat ist zeilenbasiert und wird per Regex geparst. Besonders wertvoll: Event-Handler wie `OnClick = Button1Click` erzeugen `references`-Edges zu den zugehörigen Methoden in der `.pas`-Datei.
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
object frmMain: TfrmMain
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = 'CodeGraph DFM Fixture'
|
||||
ClientHeight = 480
|
||||
ClientWidth = 640
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -12
|
||||
Font.Name = 'Segoe UI'
|
||||
OnCreate = FormCreate
|
||||
OnDestroy = FormDestroy
|
||||
PixelsPerInch = 96
|
||||
TextHeight = 15
|
||||
object pnlTop: TPanel
|
||||
Left = 0
|
||||
Top = 0
|
||||
Width = 640
|
||||
Height = 50
|
||||
Align = alTop
|
||||
BevelOuter = bvNone
|
||||
TabOrder = 0
|
||||
object lblTitle: TLabel
|
||||
Left = 16
|
||||
Top = 16
|
||||
Width = 200
|
||||
Height = 15
|
||||
Caption = 'Authentication Service'
|
||||
end
|
||||
object btnLogin: TButton
|
||||
Left = 540
|
||||
Top = 12
|
||||
Width = 80
|
||||
Height = 25
|
||||
Caption = 'Login'
|
||||
TabOrder = 0
|
||||
OnClick = btnLoginClick
|
||||
end
|
||||
end
|
||||
object pnlContent: TPanel
|
||||
Left = 0
|
||||
Top = 50
|
||||
Width = 640
|
||||
Height = 390
|
||||
Align = alClient
|
||||
BevelOuter = bvNone
|
||||
TabOrder = 1
|
||||
object edtUsername: TEdit
|
||||
Left = 16
|
||||
Top = 16
|
||||
Width = 300
|
||||
Height = 23
|
||||
TabOrder = 0
|
||||
TextHint = 'Username'
|
||||
OnChange = edtUsernameChange
|
||||
end
|
||||
object edtPassword: TEdit
|
||||
Left = 16
|
||||
Top = 48
|
||||
Width = 300
|
||||
Height = 23
|
||||
PasswordChar = '*'
|
||||
TabOrder = 1
|
||||
TextHint = 'Password'
|
||||
OnKeyPress = edtPasswordKeyPress
|
||||
end
|
||||
object mmoLog: TMemo
|
||||
Left = 16
|
||||
Top = 88
|
||||
Width = 608
|
||||
Height = 280
|
||||
ReadOnly = True
|
||||
ScrollBars = ssVertical
|
||||
TabOrder = 2
|
||||
end
|
||||
end
|
||||
object pnlStatus: TStatusBar
|
||||
Left = 0
|
||||
Top = 440
|
||||
Width = 640
|
||||
Height = 40
|
||||
Panels = <
|
||||
item
|
||||
Width = 200
|
||||
end
|
||||
item
|
||||
Width = 200
|
||||
end>
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user