From 9f415002e4b5988a6dd371fe1be9e021aff8b751 Mon Sep 17 00:00:00 2001 From: Olaf Monien Date: Wed, 11 Feb 2026 10:04:55 +0100 Subject: [PATCH] docs: Replace planning docs with DELPHI-SUPPORT.md for PR Remove Delphi/ planning folder (concept docs, fixtures, resolution notes) that served as implementation guidance. Add DELPHI-SUPPORT.md describing the integration for stakeholder review: motivation, feature matrix, architecture decisions, and test coverage. Co-Authored-By: Claude Opus 4.6 --- DELPHI-SUPPORT.md | 79 ++++++ Delphi/Docs/01-Implementation-Plan.md | 118 --------- Delphi/Docs/02-Capture-Convention.md | 97 ------- Delphi/Docs/03-AST-Referenz.md | 139 ---------- Delphi/Docs/04-Checklist.md | 71 ----- Delphi/Docs/05-NodeKind-Mapping.md | 86 ------- Delphi/Docs/06-Integration-Guide.md | 357 -------------------------- Delphi/Docs/07-DFM-FMX-Support.md | 149 ----------- Delphi/README.md | 41 --- Delphi/fixtures/App.dpr | 19 -- Delphi/fixtures/MainForm.dfm | 92 ------- Delphi/fixtures/UAuth.pas | 81 ------ Delphi/fixtures/UTypes.pas | 75 ------ Delphi/resolution/UnitResolution.md | 45 ---- 14 files changed, 79 insertions(+), 1370 deletions(-) create mode 100644 DELPHI-SUPPORT.md delete mode 100644 Delphi/Docs/01-Implementation-Plan.md delete mode 100644 Delphi/Docs/02-Capture-Convention.md delete mode 100644 Delphi/Docs/03-AST-Referenz.md delete mode 100644 Delphi/Docs/04-Checklist.md delete mode 100644 Delphi/Docs/05-NodeKind-Mapping.md delete mode 100644 Delphi/Docs/06-Integration-Guide.md delete mode 100644 Delphi/Docs/07-DFM-FMX-Support.md delete mode 100644 Delphi/README.md delete mode 100644 Delphi/fixtures/App.dpr delete mode 100644 Delphi/fixtures/MainForm.dfm delete mode 100644 Delphi/fixtures/UAuth.pas delete mode 100644 Delphi/fixtures/UTypes.pas delete mode 100644 Delphi/resolution/UnitResolution.md diff --git a/DELPHI-SUPPORT.md b/DELPHI-SUPPORT.md new file mode 100644 index 0000000..5542205 --- /dev/null +++ b/DELPHI-SUPPORT.md @@ -0,0 +1,79 @@ +# Pascal / Delphi Support for CodeGraph + +## Why Delphi? + +Delphi (Object Pascal) remains one of the most widely used languages for Windows desktop and enterprise applications. With an estimated **1.5–3 million active developers** and a strong presence in industries like healthcare, finance, logistics, and government, Delphi projects often involve large, long-lived codebases that benefit significantly from semantic code intelligence. + +Many Delphi codebases have grown over decades — making structural understanding, impact analysis, and cross-file navigation exactly the kind of tooling gap CodeGraph is designed to fill. + +Adding Delphi support positions CodeGraph as a uniquely valuable tool for a community that has historically been underserved by modern static analysis and AI-assisted development tools. + +## What Was Implemented + +### Pascal / Object Pascal (tree-sitter) + +Full extraction support for `.pas`, `.dpr`, `.dpk`, and `.lpr` files using the `tree-sitter-pascal` grammar: + +| Feature | NodeKind | Details | +|---------|----------|---------| +| Units / Programs | `module` | `unit`, `program`, `package`, `library` | +| Classes | `class` | Including inheritance and interface implementation | +| Records | `class` | Treated as classes (consistent with AST structure) | +| Interfaces | `interface` | With GUID support | +| Methods | `method` | Constructor, destructor, procedures, functions | +| Functions / Procedures | `function` | Top-level (non-class) routines | +| Properties | `property` | With read/write accessors | +| Fields | `field` | Class and record fields | +| Constants | `constant` | `const` declarations | +| Enums | `enum` | With enum members | +| Type Aliases | `type_alias` | `type TFoo = ...` | +| Uses / Imports | `import` | `uses` clause extraction | +| Function Calls | — | `calls` edges for call graph | +| Visibility | — | `public`, `private`, `protected` on methods/fields | +| Static Methods | — | `class function` / `class procedure` | +| Containment | — | `contains` edges (class → method, unit → type, etc.) | +| Inheritance | — | `extends` / `implements` edges | + +### DFM / FMX Form Files (custom extractor) + +Support for Delphi form files (`.dfm` for VCL, `.fmx` for FireMonkey) using a regex-based custom extractor — no tree-sitter grammar exists for this format: + +| Feature | NodeKind / EdgeKind | Details | +|---------|---------------------|---------| +| Components | `component` | `object Button1: TButton` | +| Nested hierarchy | `contains` | Panel1 → Button1 | +| Event handlers | `references` (unresolved) | `OnClick = Button1Click` → links UI to Pascal methods | +| `inherited` keyword | `component` | Inherited form components | +| Multi-line properties | — | Correctly skipped during parsing | +| Item collections | — | `...` blocks correctly handled | + +The DFM ↔ PAS linkage via event handlers enables **cross-file impact analysis**: renaming a method in `.pas` immediately reveals which UI components reference it. + +## Architecture + +The implementation follows CodeGraph's established patterns: + +- **Pascal extraction** uses the standard `TreeSitterExtractor` with a Pascal-specific `LanguageExtractor` configuration and a `visitPascalNode()` hook for AST nodes that require special handling (e.g., `declType` wrappers, `defProc` implementation bodies) +- **DFM/FMX extraction** uses a `DfmExtractor` class — analogous to `LiquidExtractor` and `SvelteExtractor` — that parses the line-based format with regex +- **Routing** in `extractFromSource()` dispatches `.dfm`/`.fmx` files to `DfmExtractor` before reaching the tree-sitter path +- **`tree-sitter-pascal`** is declared as an `optionalDependency` (consistent with all other grammars), pinned to a specific commit for reproducible builds + +## Files Changed + +| File | Change | +|------|--------| +| `src/types.ts` | Added `'pascal'` to `Language` type, file patterns to `DEFAULT_CONFIG.include` | +| `src/extraction/grammars.ts` | Grammar loader, extension mappings (`.pas`, `.dpr`, `.dpk`, `.lpr`, `.dfm`, `.fmx`), display name | +| `src/extraction/tree-sitter.ts` | Pascal `LanguageExtractor`, `visitPascalNode()` with 7 helper methods, `DfmExtractor` class, routing in `extractFromSource()` | +| `package.json` | `tree-sitter-pascal` in `optionalDependencies` (pinned commit) | +| `__tests__/extraction.test.ts` | 36 new tests covering all Pascal and DFM extraction features | + +## Test Results + +- **36 new tests**, all passing +- **0 regressions** — the same 28 pre-existing failures (unrelated: missing Swift/Dart grammars, database path issues, MCP truncation test) are unchanged +- Tests cover: language detection, modules, imports, classes, records, interfaces, methods, visibility, static methods, enums, properties, constants, type aliases, calls, containment, full fixture files (UAuth.pas, UTypes.pas, MainForm.dfm) + +## Dependency Note + +The npm package `tree-sitter-pascal@0.0.1` is outdated (uses NAN bindings, incompatible with Node.js v24+). The implementation uses the actively maintained GitHub repository ([Isopod/tree-sitter-pascal](https://github.com/Isopod/tree-sitter-pascal), v0.10.2) with a pinned commit hash for deterministic builds. This is consistent with how `@sengac/tree-sitter-dart` handles a similar situation. diff --git a/Delphi/Docs/01-Implementation-Plan.md b/Delphi/Docs/01-Implementation-Plan.md deleted file mode 100644 index ecd1a25..0000000 --- a/Delphi/Docs/01-Implementation-Plan.md +++ /dev/null @@ -1,118 +0,0 @@ -# Implementation Plan: Pascal/Delphi in CodeGraph - -## 0) Prämissen - -- CodeGraph extrahiert **Nodes** und **Edges** über **tree-sitter** und eine `LanguageExtractor`-Konfiguration pro Sprache in `src/extraction/tree-sitter.ts`. -- Sprachen werden zentral in drei Dateien registriert: `grammars.ts`, `types.ts`, und `tree-sitter.ts`. -- Danach folgt **Reference Resolution** (Calls → Definitionen, imports → Dateien, inheritance, framework patterns). -- **Grammar:** [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) (unterstützt Delphi, FreePascal und Standard-Pascal). - -## 1) Parser / Grammar - -npm-Paket: `tree-sitter-pascal` - -**Konkrete AST-Knoten (Auswahl):** - -| tree-sitter-pascal Typ | Beschreibung | -|---|---| -| `unit`, `program`, `library` | Top-Level-Module | -| `moduleName` | Name des Moduls (Kind-Knoten von `unit`/`program`/`library`) | -| `declClass` | Klassen, Records, Objects (enthält `kClass`/`kRecord`/`kObject`) | -| `declIntf` | Interfaces (`kInterface`/`kDispInterface`) | -| `declProc` | Procedures, Functions, Methods, Constructors, Destructors | -| `declProp` | Properties | -| `declField` | Felder in Klassen/Records | -| `declEnum` | Aufzählungstypen | -| `declEnumValue` | Einzelne Enum-Werte | -| `declType` | Type-Deklarationen (enthält `declClass`/`declIntf`/`declEnum`/etc.) | -| `declUses` | `uses`-Klauseln (imports) | -| `declConst` | Konstanten-Deklarationen | -| `declSection` | Sichtbarkeitsbereiche (`kPrivate`/`kPublic`/`kProtected`/`kPublished`) | -| `declHelper` | Class/Record Helpers | -| `declExports` | exports-Klauseln | -| `defProc` | Implementations-Body einer Prozedur/Funktion | -| `exprCall` | Funktionsaufrufe | -| `exprDot` | Qualifizierte Zugriffe (`Obj.Method`) | -| `declArgs` | Parameterliste | -| `declArg` | Einzelner Parameter | - -**Tasks:** -- `tree-sitter-pascal` als npm-Dependency hinzufügen -- Grammar-Loader in `src/extraction/grammars.ts` registrieren: `pascal: () => require('tree-sitter-pascal')` -- Extension-Mapping und Language-Typ anlegen (siehe Integration Guide) - -## 2) File-Erkennung - -Extensions (MVP): -- `.pas` (Units) -- `.dpr` (Delphi Program) -- `.dpk` (Delphi Package) -- `.lpr` (Lazarus Program) - -Optional (Phase 2): -- `.inc` (Include-Fragmente) – erst später, da Parser möglicherweise keinen vollständigen AST liefert. - -## 3) Extraktion (MVP) - -### 3.1 Nodes - -| Delphi-Konzept | tree-sitter Typ | → CodeGraph `NodeKind` | -|---|---|---| -| Unit / Program / Library | `unit` / `program` / `library` | `module` | -| Klasse / Record / Object | `declClass` | `class` | -| Interface | `declIntf` | `interface` | -| Procedure / Function (top-level) | `declProc` (mit `kProcedure`/`kFunction`) | `function` | -| Methode / Constructor / Destructor | `declProc` (mit `kConstructor`/`kDestructor`, oder innerhalb `declClass`) | `method` | -| Property | `declProp` | `property` | -| Feld | `declField` | `field` | -| Enum | `declEnum` | `enum` | -| Enum-Wert | `declEnumValue` | `enum_member` | -| Type-Alias | `declType` (einfache Alias-Formen) | `type_alias` | -| Konstante | `declConst` | `constant` | -| Uses-Klausel | `declUses` | `import` | - -### 3.2 Edges - -| Beziehung | Erkennung | → CodeGraph `EdgeKind` | -|---|---|---| -| `uses X, Y;` | `declUses` → Kind-Knoten `identifier` | `imports` | -| Klassen-Vererbung | `declClass` → `parent` Feld → erster `typeref` | `extends` | -| Interface-Implementierung | `declClass` → `parent` Feld → weitere `typeref`s | `implements` | -| Funktionsaufruf | `exprCall` | `calls` | -| Qualifizierter Aufruf | `exprDot` als Elternteil von `exprCall` | `calls` | -| Enthaltensein | Parent-Child im AST (z.B. `declClass` → `declProc`) | `contains` | -| Instanziierung | `exprCall` mit `TClassName.Create` | `instantiates` | - -## 4) Reference Resolution (MVP) - -### 4.1 Unit-Auflösung (`uses`) -- Index-Phase: Map `unitName → fileId` (aus `moduleName` Knoten in `unit`/`program`/`library`) -- Resolution: Bei `uses Foo, Bar;` Edges auf Ziel-Units setzen. -- Spezialfälle (Phase 2): `Foo in 'path\Foo.pas'` (DPR/DPK), Namespaces (`System.SysUtils`). - -### 4.2 Call-Auflösung (best effort) -- Extraktion: Name aus `exprCall`, Qualifier aus `exprDot` -- Heuristik-Reihenfolge: - 1. lokale Procs/Funcs in der gleichen Unit - 2. Methoden in der gleichen Klasse - 3. Units aus `uses` - 4. globaler Fallback nach Name - -## 5) Tests - -- Fixtures aus `fixtures/` in die Test-Suite aufnehmen -- Assertions: - - Node-Anzahl + zentrale Namen prüfen - - `uses`-Edges (imports) - - `extends`/`implements`-Edges - - einfache Call-Edges - - Sichtbarkeiten (public/private/protected) - - Properties, Felder, Enums - -## 6) Definition of Done - -- `codegraph index` indexiert Delphi/Pascal-Dateien ohne Crash -- `codegraph_search` findet Klassen/Methoden/Properties -- `codegraph_callers/callees` liefert sinnvolle Ergebnisse für einfache Fälle -- `uses`-Graph stimmt (Unit-Abhängigkeiten) -- Sichtbarkeiten werden korrekt extrahiert diff --git a/Delphi/Docs/02-Capture-Convention.md b/Delphi/Docs/02-Capture-Convention.md deleted file mode 100644 index e4a1b01..0000000 --- a/Delphi/Docs/02-Capture-Convention.md +++ /dev/null @@ -1,97 +0,0 @@ -# Capture Convention: Wie Pascal/Delphi an CodeGraph andockt - -## Architektur-Übersicht - -CodeGraph verwendet **keine** `.scm`-Query-Dateien für die Extraktion. Stattdessen wird die Logik über ein `LanguageExtractor`-Objekt in `src/extraction/tree-sitter.ts` definiert. Dieses Objekt mappt tree-sitter AST-Knotentypen auf die interne Verarbeitungslogik. - -### Zentrale Dateien für die Integration - -| Datei | Änderung | -|---|---| -| `src/extraction/grammars.ts` | Grammar-Loader, Extension-Mapping, Display-Name | -| `src/types.ts` | `Language`-Union-Type, `DEFAULT_CONFIG.include` Patterns | -| `src/extraction/tree-sitter.ts` | `EXTRACTORS`-Map mit `LanguageExtractor`-Konfiguration | - -## LanguageExtractor-Interface - -Die Konfiguration definiert, welche AST-Knotentypen welcher Semantik entsprechen: - -```typescript -interface LanguageExtractor { - functionTypes: string[]; // Top-Level Funktionen/Prozeduren - classTypes: string[]; // Klassen, Records, Objects - methodTypes: string[]; // Methoden, Konstruktoren, Destruktoren - interfaceTypes: string[]; // Interfaces - structTypes: string[]; // Structs (in Delphi: leer) - enumTypes: string[]; // Enums - typeAliasTypes: string[]; // Type-Aliase - importTypes: string[]; // uses-Klauseln - callTypes: string[]; // Funktionsaufrufe - variableTypes: string[]; // Variable/Feld-Deklarationen - nameField: string; // AST-Feld für den Namen - bodyField: string; // AST-Feld für den Body - paramsField: string; // AST-Feld für Parameter - returnField?: string; // AST-Feld für Rückgabetyp - getSignature?: (node, source) => string | undefined; - getVisibility?: (node) => 'public' | 'private' | 'protected' | 'internal' | undefined; - isExported?: (node, source) => boolean; - isStatic?: (node) => boolean; - isConst?: (node) => boolean; -} -``` - -## Konkrete Zuordnung für Pascal/Delphi - -| LanguageExtractor-Feld | tree-sitter-pascal Typ(en) | Anmerkung | -|---|---|---| -| `functionTypes` | `['declProc']` | Top-Level, gefiltert nach Kontext (kein Parent `declClass`) | -| `classTypes` | `['declClass']` | Enthält class, record, object | -| `methodTypes` | `['declProc']` | Innerhalb `declClass`, inkl. constructor/destructor | -| `interfaceTypes` | `['declIntf']` | interface, dispinterface | -| `structTypes` | `[]` | Records werden über `declClass` (mit `kRecord`) abgedeckt | -| `enumTypes` | `['declEnum']` | Innerhalb von `declType` | -| `typeAliasTypes` | `['declType']` | Einfache `type X = Y` Deklarationen | -| `importTypes` | `['declUses']` | `uses`-Klauseln | -| `callTypes` | `['exprCall']` | Funktions-/Methodenaufrufe | -| `variableTypes` | `['declField', 'declConst']` | Felder und Konstanten | -| `nameField` | `'name'` | `declProc`, `declConst`, `declField`, `declEnumValue` haben `name`-Feld | -| `bodyField` | `'body'` | Nur bei `defProc` (Implementierungen) | -| `paramsField` | `'args'` | `declProc` hat `args`-Feld (`declArgs`) | -| `returnField` | `'type'` | `declProc` hat optionales `type`-Feld | - -## Delphi-spezifische Herausforderungen - -### declProc: Funktion oder Methode? - -`declProc` wird sowohl für Top-Level-Funktionen als auch für Methoden verwendet. Die Unterscheidung erfolgt über den Kontext: -- **Top-Level**: Parent ist `interface` (der Unit-interface-Abschnitt) oder `implementation` -- **Methode**: Parent ist `declClass` oder `declIntf` -- **Kind-Unterscheidung**: Enthält `kProcedure`, `kFunction`, `kConstructor`, oder `kDestructor` - -### Sichtbarkeit über declSection - -Delphi gruppiert Mitglieder in Sichtbarkeitsbereichen: -``` -declClass → declSection (kPublic) → declProc, declField, declProp - → declSection (kPrivate) → declField -``` -Die `getVisibility`-Funktion muss den nächsten `declSection`-Vorfahren inspizieren. - -### Vererbung und Interface-Implementierung - -`declClass` hat ein `parent`-Feld mit einer Liste von `typeref`-Knoten: -- Der **erste** `typeref` ist typischerweise die Basisklasse (`extends`) -- Weitere `typeref`s sind implementierte Interfaces (`implements`) - -### `with`-Statements (Phase 2) - -`with`-Statements erzeugen einen impliziten Scope, der Call-Qualifier verschleiert: -```delphi -with MyObj do - DoSomething; // ist eigentlich MyObj.DoSomething -``` -Im MVP wird dies **nicht** aufgelöst – Calls innerhalb von `with` werden ohne Qualifier extrahiert. - -### Namespaces - -Delphi-Unit-Namen können Punkte enthalten (`System.SysUtils`). Der `moduleName`-Knoten enthält den vollständigen Namen. Die Resolution muss sowohl exakte Matches als auch Suffix-Matches unterstützen. diff --git a/Delphi/Docs/03-AST-Referenz.md b/Delphi/Docs/03-AST-Referenz.md deleted file mode 100644 index 35c30b7..0000000 --- a/Delphi/Docs/03-AST-Referenz.md +++ /dev/null @@ -1,139 +0,0 @@ -# AST-Referenz: tree-sitter-pascal (verifiziert) - -Dieses Dokument zeigt die **tatsächlichen** AST-Knotentypen aus der [`tree-sitter-pascal`](https://github.com/Isopod/tree-sitter-pascal) Grammar, verifiziert anhand der `node-types.json` des Pakets. - -> **Hinweis:** CodeGraph nutzt keine `.scm`-Dateien zur Extraktion, sondern ein `LanguageExtractor`-Objekt in TypeScript. Die hier dokumentierten AST-Strukturen zeigen, wie der Parser Delphi-Code als Baum darstellt. - -## A) Top-Level Module - -### unit -``` -(unit (kUnit) (moduleName (identifier)) (interface ...) (implementation ...) (kEndDot)) -``` - -### program -``` -(program (kProgram) (moduleName (identifier)) ...body...) -``` - -### library -``` -(library (kLibrary) (moduleName (identifier)) ...body...) -``` - -## B) Deklarationen - -### declClass (Klassen, Records, Objects) -``` -(declType - name: (identifier) ← "TMyClass" - value: (type (declClass - (kClass) ← oder (kRecord), (kObject) - parent: ((typeref) (typeref) ...) ← Basisklasse + Interfaces - (declSection (kPublic) ← Sichtbarkeit - (declProc ...) ← Methoden - (declField ...) ← Felder - (declProp ...) ← Properties - ) - (kEnd)))) -``` - -### declIntf (Interfaces) -``` -(declType - name: (identifier) ← "IMyInterface" - value: (type (declIntf - (kInterface) - parent: ((typeref) ...) ← Basis-Interface - guid: (guid ...) ← optionale GUID - (declProc ...) (declProp ...) - (kEnd)))) -``` - -### declProc (Procedures, Functions, Methods, Constructors, Destructors) -``` -(declProc - (kFunction) ← oder kProcedure, kConstructor, kDestructor - name: (identifier) ← "MyMethod" - args: (declArgs (declArg name: (identifier) type: (type ...))) - type: (type ...) ← Rückgabetyp (nur bei functions) - attribute: (procAttribute ...)) ← z.B. override, virtual, static -``` - -**Unterscheidung Funktion vs. Methode:** -- Top-Level: `declProc` als Kind von `interface`/`implementation` -- Methode: `declProc` als Kind von `declClass` oder `declIntf` -- Statisch: enthält `kClass` Kind-Knoten (`class procedure`) - -### declProp (Properties) -``` -(declProp (kProperty) name: (identifier) type: (type ...) (kRead) (identifier) (kWrite) (identifier)) -``` - -### declField (Felder) -``` -(declField name: (identifier) type: (type ...)) -``` - -### declEnum (Enums) -``` -(declEnum (declEnumValue name: (identifier)) (declEnumValue name: (identifier)) ...) -``` - -### declUses (uses-Klauseln) -``` -(declUses (kUses) (identifier) (identifier) ...) -``` -Optional mit Pfadangabe: `(kIn) (literalString)` für `uses Foo in 'path/Foo.pas'` - -### declConst (Konstanten) -``` -(declConst name: (identifier) type: (type ...) defaultValue: (defaultValue ...)) -``` - -## C) Ausdrücke - -### exprCall (Funktionsaufruf) -``` -(exprCall (identifier) ...args...) ← WriteLn(...) -``` - -### exprDot (qualifizierter Zugriff) -``` -(exprDot lhs: (identifier) operator: (kDot) rhs: (exprCall (identifier) ...)) ← Svc.Login(...) -(exprDot lhs: (identifier) operator: (kDot) rhs: (identifier)) ← TAuthService.Create -``` - -## D) Sichtbarkeit (declSection) -``` -(declSection (kPublic)) ← oder kPrivate, kProtected, kPublished, kStrict -``` -Gilt für alle nachfolgenden Deklarationen bis zum nächsten `declSection` oder `kEnd`. - -## E) Vererbung (parent-Feld von declClass) - -Das `parent`-Feld enthält eine Liste von `typeref`-Knoten: -- **Erster** `typeref` = Basisklasse → EdgeKind `extends` -- **Weitere** `typeref`s = Interfaces → EdgeKind `implements` - -``` -(declClass (kClass) parent: ((typeref (identifier "TInterfacedObject")) (typeref (identifier "ITokenValidator"))) ...) -``` - -## F) Zusammenfassung: Relevante Knoten für CodeGraph - -| AST-Knotentyp | Feld `name` | Weitere wichtige Felder | -|---|---|---| -| `unit`/`program`/`library` | Kind: `moduleName` | `interface`, `implementation` | -| `declClass` | via Parent `declType` | `parent` (Vererbung), `declSection` | -| `declIntf` | via Parent `declType` | `parent`, `guid` | -| `declProc` | `name` | `args`, `type`, `attribute` | -| `declProp` | `name` | `type` | -| `declField` | `name` | `type` | -| `declEnum` | — | Kind: `declEnumValue` | -| `declEnumValue` | `name` | `value` | -| `declConst` | `name` | `type`, `defaultValue` | -| `declUses` | — | Kind: `identifier` | -| `exprCall` | — | Kind: `identifier` (callee) | -| `exprDot` | — | `lhs`, `rhs` | - diff --git a/Delphi/Docs/04-Checklist.md b/Delphi/Docs/04-Checklist.md deleted file mode 100644 index 00a59b1..0000000 --- a/Delphi/Docs/04-Checklist.md +++ /dev/null @@ -1,71 +0,0 @@ -# Checklist: Pascal/Delphi Support in CodeGraph - -## 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'`, `'**/*.dfm'`, `'**/*.fmx'` erweitern -- [ ] `src/extraction/grammars.ts`: Grammar-Loader `pascal: () => require('tree-sitter-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 - -## B) Node-Extraktion -- [ ] `unit` / `program` / `library` → NodeKind `module` -- [ ] `declClass` → NodeKind `class` (inkl. record, object) -- [ ] `declIntf` → NodeKind `interface` -- [ ] `declProc` (top-level) → NodeKind `function` -- [ ] `declProc` (in Klasse) → NodeKind `method` (inkl. constructor/destructor) -- [ ] `declProp` → NodeKind `property` -- [ ] `declField` → NodeKind `field` -- [ ] `declEnum` → NodeKind `enum` -- [ ] `declEnumValue` → NodeKind `enum_member` -- [ ] `declConst` → NodeKind `constant` -- [ ] `declType` (einfach) → NodeKind `type_alias` - -## C) Edge-Extraktion -- [ ] `declUses` → EdgeKind `imports` -- [ ] `declClass.parent[0]` → EdgeKind `extends` -- [ ] `declClass.parent[1..]` → EdgeKind `implements` -- [ ] `exprCall` → EdgeKind `calls` -- [ ] `exprDot` + `exprCall` → EdgeKind `calls` (qualifiziert) -- [ ] `TClass.Create` → EdgeKind `instantiates` -- [ ] Parent-Child Containment → EdgeKind `contains` - -## D) Sichtbarkeit & Attribute -- [ ] `getVisibility`: `declSection` → public/private/protected -- [ ] `isStatic`: `kClass` in `declProc` → true -- [ ] `getSignature`: Parameter + Rückgabetyp extrahieren - -## E) Resolution -- [ ] `moduleName` → `unitName → fileId` Mapping -- [ ] `uses X;` → Unit-Node auflösen -- [ ] `uses X in 'path'` → Datei direkt auflösen (Phase 2) -- [ ] Call-Resolution: gleiche Unit → gleiche Klasse → uses-Units → global - -## F) Testing -- [ ] Fixtures in Test-Suite aufnehmen (`UAuth.pas`, `App.dpr`, `UTypes.pas`) -- [ ] Nodes: Anzahl + Namen verifizieren -- [ ] Edges: imports/extends/implements/calls prüfen -- [ ] Sichtbarkeiten prüfen -- [ ] Properties, Enums, Konstanten prüfen - -## G) Polishing -- [ ] Language in Supported-Languages-Dokumentation aufnehmen -- [ ] CLI-Hilfe / Config-Validierung aktualisieren - -## 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 : `) -- [ ] 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. -- [ ] **`.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. diff --git a/Delphi/Docs/05-NodeKind-Mapping.md b/Delphi/Docs/05-NodeKind-Mapping.md deleted file mode 100644 index 5c8660d..0000000 --- a/Delphi/Docs/05-NodeKind-Mapping.md +++ /dev/null @@ -1,86 +0,0 @@ -# NodeKind Mapping: Delphi → CodeGraph - -Dieses Dokument zeigt die explizite Zuordnung von Delphi/Pascal-Konzepten zu den bestehenden CodeGraph `NodeKind`- und `EdgeKind`-Werten. - -> **Ergebnis:** Alle Delphi-Konzepte lassen sich auf existierende `NodeKind`-Werte abbilden. Es werden **keine neuen** Kinds benötigt. - -## Node-Mapping - -| Delphi-Konzept | tree-sitter Typ | CodeGraph `NodeKind` | Anmerkungen | -|---|---|---|---| -| Unit | `unit` + `moduleName` | `module` | Kompilierungseinheit, vergleichbar mit Modulen | -| Program | `program` + `moduleName` | `module` | Ausführbarer Einstiegspunkt | -| Library | `library` + `moduleName` | `module` | DLL/shared library | -| Klasse | `declClass` (mit `kClass`) | `class` | Standard-Klasse | -| Record | `declClass` (mit `kRecord`) | `class` | Value-Type, aber strukturell wie Klasse | -| Object | `declClass` (mit `kObject`) | `class` | Legacy, wie Klasse behandeln | -| Interface | `declIntf` (mit `kInterface`) | `interface` | — | -| DispInterface | `declIntf` (mit `kDispInterface`) | `interface` | COM-spezifisch | -| Procedure (top-level) | `declProc` (mit `kProcedure`) | `function` | Ohne Rückgabetyp | -| Function (top-level) | `declProc` (mit `kFunction`) | `function` | Mit Rückgabetyp | -| Methode | `declProc` in `declClass` | `method` | Instance-Methode | -| Constructor | `declProc` (mit `kConstructor`) | `method` | Spezielle Methode | -| Destructor | `declProc` (mit `kDestructor`) | `method` | Spezielle Methode | -| Class Method | `declProc` (mit `kClass`) | `method` | Statisch, `isStatic = true` | -| Property | `declProp` | `property` | Mit Getter/Setter | -| Feld | `declField` | `field` | — | -| Enum | `declEnum` | `enum` | — | -| Enum-Wert | `declEnumValue` | `enum_member` | — | -| Type-Alias | `declType` (einfach) | `type_alias` | `type TMyInt = Integer;` | -| Konstante | `declConst` | `constant` | — | -| Uses-Eintrag | `declUses` → `identifier` | `import` | Pro Unit-Name ein Import-Node | - -## Edge-Mapping - -| Delphi-Beziehung | Erkennung | CodeGraph `EdgeKind` | -|---|---|---| -| `uses X;` | `declUses` → Kind `identifier` | `imports` | -| Basisklasse | `declClass.parent[0]` (erster `typeref`) | `extends` | -| Interface-Impl. | `declClass.parent[1..]` (weitere `typeref`s) | `implements` | -| Funktionsaufruf | `exprCall` | `calls` | -| Qualifizierter Aufruf | `exprDot` → `exprCall` | `calls` | -| `TClass.Create` | `exprDot` mit `.Create` Suffix | `instantiates` | -| 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 | -| `exports`-Klauseln | `declExports` → könnte als `export` NodeKind abgebildet werden | -| Generics | `typerefTpl` → beeinflusst Typ-Referenzen, nicht den NodeKind | - -## Sichtbarkeit - -| Delphi-Keyword | CodeGraph Visibility | -|---|---| -| `published` | `public` (CodeGraph kennt kein `published`) | -| `public` | `public` | -| `protected` | `protected` | -| `private` | `private` | -| `strict private` | `private` | -| `strict protected` | `protected` | -| (keine Angabe) | `public` (Default in Delphi-Klassen) | - diff --git a/Delphi/Docs/06-Integration-Guide.md b/Delphi/Docs/06-Integration-Guide.md deleted file mode 100644 index ff9f109..0000000 --- a/Delphi/Docs/06-Integration-Guide.md +++ /dev/null @@ -1,357 +0,0 @@ -# Integration Guide: Konkrete Code-Änderungen - -Dieses Dokument zeigt die **exakten Änderungen** an den CodeGraph-Quelldateien, die für Pascal/Delphi-Support nötig sind. - -## 1) Dependency installieren - -```bash -npm install tree-sitter-pascal -``` - -## 2) `src/types.ts` — Language-Typ erweitern - -```typescript -// Zum Language Union-Type hinzufügen (nach 'liquid'): -export type Language = - | 'typescript' - // ...bestehende Sprachen... - | 'liquid' - | 'pascal' // ← NEU - | 'unknown'; -``` - -**DEFAULT_CONFIG.include** erweitern: - -```typescript -include: [ - // ...bestehende Patterns... - // Liquid (Shopify themes) - '**/*.liquid', - // Pascal / Delphi ← NEU - '**/*.pas', - '**/*.dpr', - '**/*.dpk', - '**/*.lpr', -], -``` - -## 3) `src/extraction/grammars.ts` — Grammar registrieren - -### grammarLoaders Map: - -```typescript -const grammarLoaders: Record = { - // ...bestehende Loader... - dart: () => { - return require('@sengac/tree-sitter-dart'); - }, - pascal: () => { // ← NEU - // eslint-disable-next-line @typescript-eslint/no-require-imports - return require('tree-sitter-pascal'); - }, -}; -``` - -### EXTENSION_MAP: - -```typescript -export const EXTENSION_MAP: Record = { - // ...bestehende Mappings... - '.liquid': 'liquid', - '.svelte': 'svelte', - '.pas': 'pascal', // ← NEU - '.dpr': 'pascal', // ← NEU - '.dpk': 'pascal', // ← NEU - '.lpr': 'pascal', // ← NEU -}; -``` - -### getLanguageDisplayName(): - -```typescript -const names: Record = { - // ...bestehende Namen... - liquid: 'Liquid', - pascal: 'Pascal / Delphi', // ← NEU - unknown: 'Unknown', -}; -``` - -## 4) `src/extraction/tree-sitter.ts` — LanguageExtractor - -```typescript -pascal: { - functionTypes: ['declProc'], // Top-Level Funktionen/Prozeduren - classTypes: ['declClass'], // Klassen, Records, Objects - methodTypes: ['declProc'], // Methoden (in Klasse), Konstruktoren, Destruktoren - interfaceTypes: ['declIntf'], // Interfaces - structTypes: [], // Records über declClass abgedeckt - enumTypes: ['declEnum'], // Aufzählungen - typeAliasTypes: ['declType'], // Type-Aliase - importTypes: ['declUses'], // uses-Klauseln - callTypes: ['exprCall'], // Funktionsaufrufe - variableTypes: ['declField', 'declConst'], // Felder und Konstanten - nameField: 'name', - bodyField: 'body', // nur bei defProc (Implementierung) - paramsField: 'args', // declArgs - returnField: 'type', - getSignature: (node, source) => { - const args = getChildByField(node, 'args'); - const returnType = getChildByField(node, 'type'); - if (!args) return undefined; - const argsText = getNodeText(args, source); - return returnType - ? getNodeText(returnType, source) + ' ' + argsText - : argsText; - }, - getVisibility: (node) => { - // Suche den nächsten declSection-Vorfahren - let current = node.parent; - while (current) { - if (current.type === 'declSection') { - for (let i = 0; i < current.childCount; i++) { - const child = current.child(i); - if (child?.type === 'kPublic' || child?.type === 'kPublished') - return 'public'; - if (child?.type === 'kPrivate') return 'private'; - if (child?.type === 'kProtected') return 'protected'; - } - } - current = current.parent; - } - return undefined; - }, - isStatic: (node) => { - // class procedure / class function - for (let i = 0; i < node.childCount; i++) { - if (node.child(i)?.type === 'kClass') return true; - } - return false; - }, - isConst: (node) => { - return node.type === 'declConst'; - }, -}, -``` - -## 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 = { - // ...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 - diff --git a/Delphi/Docs/07-DFM-FMX-Support.md b/Delphi/Docs/07-DFM-FMX-Support.md deleted file mode 100644 index 93a330f..0000000 --- a/Delphi/Docs/07-DFM-FMX-Support.md +++ /dev/null @@ -1,149 +0,0 @@ -# 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 : ` — das Formular selbst -2. **Verschachtelte Komponenten:** `object : ` innerhalb eines anderen `object`-Blocks -3. **Properties:** ` = ` — einfache Zuweisung -4. **Event-Handler:** `On = ` — Verknüpfung zu Pascal-Methode -5. **Ende:** `end` schließt jeden `object`-Block -6. **Vererbung:** `inherited : ` statt `object` bei geerbten Formularen -7. **Inline-Objekte:** `inline : ` 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 : " oder "inherited : ": - → neuen component-Node erstellen - → contains-Edge vom Parent - → auf Stack pushen - wenn "end": - → Stack poppen - wenn " = " 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. - diff --git a/Delphi/README.md b/Delphi/README.md deleted file mode 100644 index 304616f..0000000 --- a/Delphi/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# CodeGraph Pascal/Delphi Support — Konzept & Bauplan - -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. Zusätzlich werden **DFM/FMX-Formulardateien** unterstützt, um UI-Komponenten und Event-Handler-Verknüpfungen zu erfassen. - -## Inhalt - -| Verzeichnis / Datei | Beschreibung | -|---|---| -| `Docs/01-Implementation-Plan.md` | Implementierungs-Roadmap mit konkreten AST-Typen | -| `Docs/02-Capture-Convention.md` | Wie Pascal an CodeGraphs `LanguageExtractor`-Interface andockt | -| `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` + 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 - -CodeGraph verwendet **keine** per-Sprache Plugin-Verzeichnisse. Die Integration erfolgt zentral in drei Dateien: - -1. **`src/types.ts`** — `Language` Union-Type und `DEFAULT_CONFIG.include` Patterns -2. **`src/extraction/grammars.ts`** — Grammar-Loader, Extension-Mapping, Display-Name -3. **`src/extraction/tree-sitter.ts`** — `LanguageExtractor`-Konfiguration in der `EXTRACTORS` Map - -## Wie du damit arbeitest - -1. **Integration Guide lesen** (`Docs/06-Integration-Guide.md`) — enthält die exakten Code-Änderungen -2. **`npm install tree-sitter-pascal`** als Dependency hinzufügen -3. **Änderungen in den drei Dateien** vornehmen (types.ts, grammars.ts, tree-sitter.ts) -4. **Resolver erweitern** (mindestens `uses` → Unit-File, einfache Call-Auflösung) -5. **Fixtures in die Tests aufnehmen** und Assertions hinzufügen - -## Grammar & Parsing - -**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. diff --git a/Delphi/fixtures/App.dpr b/Delphi/fixtures/App.dpr deleted file mode 100644 index 14ac145..0000000 --- a/Delphi/fixtures/App.dpr +++ /dev/null @@ -1,19 +0,0 @@ -program App; - -{$APPTYPE CONSOLE} - -uses - System.SysUtils, - UAuth in 'UAuth.pas'; - -var - Svc: TAuthService; - -begin - Svc := TAuthService.Create; - try - Writeln(Svc.Login('olaf', 'secret')); - finally - Svc.Free; - end; -end. diff --git a/Delphi/fixtures/MainForm.dfm b/Delphi/fixtures/MainForm.dfm deleted file mode 100644 index 998536e..0000000 --- a/Delphi/fixtures/MainForm.dfm +++ /dev/null @@ -1,92 +0,0 @@ -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 - diff --git a/Delphi/fixtures/UAuth.pas b/Delphi/fixtures/UAuth.pas deleted file mode 100644 index 51a8425..0000000 --- a/Delphi/fixtures/UAuth.pas +++ /dev/null @@ -1,81 +0,0 @@ -/// Unit UAuth: Authentication-Service mit Interface-Implementierung. -/// Testet: uses, interface (mit GUID), Klasse mit Vererbung + Interface, -/// Sichtbarkeitsbereiche (private/public), Felder, Properties, -/// Methoden, Constructor, Destructor, Funktionsaufrufe. -unit UAuth; - -interface - -uses - System.SysUtils, - System.Classes; - -type - /// Interface für Token-Validierung - ITokenValidator = interface - ['{11111111-1111-1111-1111-111111111111}'] - function Validate(const AToken: string): Boolean; - end; - - /// Auth-Service mit Token-Validierung - TAuthService = class(TInterfacedObject, ITokenValidator) - private - FToken: string; - FLoginCount: Integer; - procedure IncLoginCount; - protected - function GetToken: string; - public - constructor Create; - destructor Destroy; override; - function Validate(const AToken: string): Boolean; - function Login(const AUser, APass: string): string; - property Token: string read GetToken; - property LoginCount: Integer read FLoginCount; - end; - -implementation - -{ TAuthService } - -constructor TAuthService.Create; -begin - inherited Create; - FToken := ''; - FLoginCount := 0; -end; - -destructor TAuthService.Destroy; -begin - FToken := ''; - inherited Destroy; -end; - -procedure TAuthService.IncLoginCount; -begin - Inc(FLoginCount); -end; - -function TAuthService.GetToken: string; -begin - Result := FToken; -end; - -function TAuthService.Validate(const AToken: string): Boolean; -begin - Result := AToken <> ''; -end; - -function TAuthService.Login(const AUser, APass: string): string; -begin - IncLoginCount; - if Validate(AUser + ':' + APass) then - begin - FToken := AUser; - Result := 'ok'; - end - else - Result := ''; -end; - -end. diff --git a/Delphi/fixtures/UTypes.pas b/Delphi/fixtures/UTypes.pas deleted file mode 100644 index 4458d31..0000000 --- a/Delphi/fixtures/UTypes.pas +++ /dev/null @@ -1,75 +0,0 @@ -/// Unit UTypes: Delphi-Typsystem-Fixture. -/// Testet: Enums, Records (mit Feldern), Type-Aliase, -/// Konstanten, verschachtelte Typen, Class-Methoden (static). -unit UTypes; - -interface - -uses - System.SysUtils; - -const - C_MAX_RETRIES = 3; - C_DEFAULT_NAME = 'Guest'; - -type - /// Enum: Benutzerrolle - TUserRole = (urAdmin, urEditor, urViewer); - - /// Record: Punkt mit X/Y-Koordinaten - TPoint2D = record - X: Double; - Y: Double; - end; - - /// Type-Alias - TUserName = string; - - /// Klasse mit class method (static), verschachteltem Typ und Enum-Verwendung - TUserInfo = class - public - type - /// Verschachtelter Record innerhalb der Klasse - TAddress = record - Street: string; - City: string; - Zip: string; - end; - private - FName: TUserName; - FRole: TUserRole; - FAddress: TAddress; - public - constructor Create(const AName: TUserName; ARole: TUserRole); - function GetDisplayName: string; - class function CreateAdmin(const AName: TUserName): TUserInfo; static; - property Name: TUserName read FName write FName; - property Role: TUserRole read FRole; - property Address: TAddress read FAddress write FAddress; - end; - -implementation - -{ TUserInfo } - -constructor TUserInfo.Create(const AName: TUserName; ARole: TUserRole); -begin - FName := AName; - FRole := ARole; -end; - -function TUserInfo.GetDisplayName: string; -begin - if FRole = urAdmin then - Result := '[Admin] ' + FName - else - Result := FName; -end; - -class function TUserInfo.CreateAdmin(const AName: TUserName): TUserInfo; -begin - Result := TUserInfo.Create(AName, urAdmin); -end; - -end. - diff --git a/Delphi/resolution/UnitResolution.md b/Delphi/resolution/UnitResolution.md deleted file mode 100644 index 3e71ce6..0000000 --- a/Delphi/resolution/UnitResolution.md +++ /dev/null @@ -1,45 +0,0 @@ -# Unit Resolution Heuristics (uses → File) - -## Input - -- Extrahierte Edges vom Typ `imports` aus `declUses`-Knoten. -- Extrahierte Nodes vom Typ `module` aus `unit`/`program`/`library` (Name aus `moduleName`-Kind-Knoten). - -## AST-Struktur - -``` -(declUses - (kUses) - (identifier "System.SysUtils") ← Unit-Name - (identifier "UAuth") ← Unit-Name - (kIn) (literalString "'UAuth.pas'") ← optionaler Pfad -) -``` - -## MVP Algorithm - -1. Build `unitName → nodeId` Map für alle indexierten Dateien (aus `moduleName`-Knoten). -2. Für jeden `imports`-Edge mit Zielname `X`: - - Exakter Match auf `X` - - Case-insensitiver Match (Delphi ist case-insensitive) - - Namespace-Normalisierung: - - `System.SysUtils` → versuche auch `SysUtils` (letzter Segment) - - Dateiname-basierter Fallback: `X` → `X.pas` in den indexierten Dateien suchen -3. Falls aufgelöst: `edge.to_node_id = targetNodeId` -4. Falls nicht aufgelöst: `edge.to_symbol = X` beibehalten (Suche funktioniert trotzdem) - -## DPR/DPK `in 'path'` (Phase 2) - -In Delphi-Projektdateien kann ein expliziter Pfad angegeben werden: - -```delphi -uses - Foo in 'src/Foo.pas', - Bar in 'Bar.pas'; -``` - -Im AST erkennbar durch `(kIn) (literalString)` nach dem `(identifier)`. - -**Enhancement:** -- `literalString` extrahieren und direkt zur Datei auflösen -- Pfad relativ zur DPR/DPK-Datei interpretieren