chore: remove leftover debug scripts and stale docs
Delete dead dev scratch (debug_python_ast*.js, test_python_inheritance.js), the obsolete tree-sitter-dart native patch (Dart loads via WASM now and the package is no longer a dependency), and orphaned docs superseded by the current code and the agent-eval skill (IMPLEMENTATION_PLAN.md, DELPHI-SUPPORT.md, run-interactive-test.md). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
c3f1e273d4
commit
2fc0df7108
@@ -1,157 +0,0 @@
|
||||
# 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 | — | `<item>...</end>` 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
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
Testing with a large Delphi codebase (~3,400 files, ~244k nodes) uncovered performance bottlenecks in the reference resolution pipeline. The following fixes **benefit all languages**, not just Pascal:
|
||||
|
||||
| Fix | Scope | Impact |
|
||||
|-----|-------|--------|
|
||||
| **Fuzzy match index** — replaced O(n) linear scan with lazily-built case-insensitive `Map` index | `name-matcher.ts` (all languages) | O(1) lookup per ref instead of iterating all nodes |
|
||||
| **Import mapping cache** — cached per-file import mappings instead of re-reading/re-parsing for every ref | `import-resolver.ts` (all languages) | Eliminated redundant file I/O during resolution |
|
||||
| **Kind cache** — pre-populated `getNodesByKind` results during warm-up | `resolution/index.ts` (all languages) | Avoided repeated DB queries for the same node kinds |
|
||||
| **Pascal built-in filtering** — skip known RTL/VCL/FMX identifiers before resolution | `resolution/index.ts` (Pascal-specific) | ~60 built-in identifiers filtered out early |
|
||||
| **Method index for `defProc`** — replaced O(n) `find()` with `Map` lookup when linking implementation bodies to declarations | `tree-sitter.ts` (Pascal-specific) | O(1) per implementation body |
|
||||
| **Delphi-specific excludes** — `__history/**`, `__recovery/**`, `*.dcu` added to default excludes | `types.ts` (Pascal-specific) | Skips Delphi IDE temp files during indexing |
|
||||
|
||||
**Result:** Reference resolution on a large Delphi project dropped from **~30 minutes to ~15 seconds** (120x speedup). The general improvements (fuzzy index, import cache, kind cache) will benefit all CodeGraph users.
|
||||
|
||||
## 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()`, method index |
|
||||
| `src/resolution/index.ts` | Pascal built-in filtering, kind cache, cache clearing |
|
||||
| `src/resolution/import-resolver.ts` | Import mapping cache |
|
||||
| `src/resolution/name-matcher.ts` | Fuzzy match index (case-insensitive `Map`) |
|
||||
| `package.json` | `tree-sitter-pascal` in `optionalDependencies` (pinned commit) |
|
||||
| `__tests__/extraction.test.ts` | 37 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.
|
||||
|
||||
## Testing Instructions
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js >= 18
|
||||
- npm
|
||||
- Git
|
||||
|
||||
### 1. Clone and build
|
||||
|
||||
```bash
|
||||
git clone -b delphi-support https://github.com/omonien/codegraph.git
|
||||
cd codegraph
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 2. Link globally
|
||||
|
||||
```bash
|
||||
npm link
|
||||
```
|
||||
|
||||
Verify with:
|
||||
|
||||
```bash
|
||||
codegraph --version
|
||||
```
|
||||
|
||||
### 3. Index a Delphi project
|
||||
|
||||
```bash
|
||||
cd /path/to/your/delphi-project
|
||||
codegraph init -i
|
||||
codegraph index
|
||||
```
|
||||
|
||||
### 4. Query the code graph
|
||||
|
||||
```bash
|
||||
codegraph status # Show index statistics
|
||||
codegraph query "TFormMain" # Search for a symbol
|
||||
codegraph context "What does TCustomer do?" # Build AI context
|
||||
```
|
||||
|
||||
### 5. Set up the MCP server (for Claude Code)
|
||||
|
||||
```bash
|
||||
codegraph install
|
||||
```
|
||||
|
||||
This configures the MCP server, tool permissions, auto-sync hooks, and CLAUDE.md in one step. After that, start Claude Code in the project — CodeGraph tools will be available immediately.
|
||||
|
||||
### 6. Clean up
|
||||
|
||||
```bash
|
||||
npm unlink -g @colbymchenry/codegraph # Remove global link
|
||||
rm -rf /path/to/delphi-project/.codegraph # Remove project index
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
const { getParser, initGrammars, loadAllGrammars } = require('./dist/extraction/grammars');
|
||||
|
||||
(async () => {
|
||||
await initGrammars();
|
||||
await loadAllGrammars();
|
||||
|
||||
const parser = getParser('python');
|
||||
|
||||
const code = `class Child(Parent):
|
||||
pass`;
|
||||
|
||||
const tree = parser.parse(code);
|
||||
|
||||
function walk(node, depth = 0) {
|
||||
const indent = ' '.repeat(depth);
|
||||
const preview = node.text.substring(0, 30).replace(/\n/g, '\\n');
|
||||
console.log(`${indent}${node.type} [${node.startPosition.row}:${node.startPosition.column}] "${preview}"`);
|
||||
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child) walk(child, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
walk(tree.rootNode);
|
||||
})();
|
||||
@@ -1,26 +0,0 @@
|
||||
const { getParser, initGrammars, loadAllGrammars } = require('./dist/extraction/grammars');
|
||||
|
||||
(async () => {
|
||||
await initGrammars();
|
||||
await loadAllGrammars();
|
||||
|
||||
const parser = getParser('python');
|
||||
|
||||
const code = `class Child(Parent, Mixin, Base):
|
||||
pass`;
|
||||
|
||||
const tree = parser.parse(code);
|
||||
|
||||
function walk(node, depth = 0) {
|
||||
const indent = ' '.repeat(depth);
|
||||
const preview = node.text.substring(0, 40).replace(/\n/g, '\\n');
|
||||
console.log(`${indent}${node.type} "${preview}"`);
|
||||
|
||||
for (let i = 0; i < node.namedChildCount; i++) {
|
||||
const child = node.namedChild(i);
|
||||
if (child) walk(child, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
walk(tree.rootNode);
|
||||
})();
|
||||
@@ -1,131 +0,0 @@
|
||||
# Running the agent-behavior test (how agents actually use codegraph)
|
||||
|
||||
This explains how to measure **how a Claude Code agent uses the codegraph MCP
|
||||
tools** on a real repo — which tools it calls (does it lead with
|
||||
`codegraph_explore`?), how many follow-up `Read`/`Grep`s it does, and the token
|
||||
cost. Use it when changing tool guidance (`server-instructions.ts`,
|
||||
`instructions-template.ts`, tool descriptions) or retrieval, to verify the
|
||||
change actually shifts agent behavior.
|
||||
|
||||
Scripts live in `scripts/agent-eval/`.
|
||||
|
||||
## Why two harnesses (read this first)
|
||||
|
||||
| | Interactive (`itrun.sh`) | Headless (`run-agent.sh`) |
|
||||
|---|---|---|
|
||||
| Drives | the real TUI via tmux | `claude -p` print mode |
|
||||
| Subagent it picks | **Explore** (matches real UX) | general-purpose (diverges) |
|
||||
| Metrics | tool breakdown (from session logs) + `Done(…)` token summary | exact per-tool calls + tokens/cost (stream-json) |
|
||||
| Cost | Claude Max subscription | API $ (`total_cost_usd`) |
|
||||
|
||||
**Headless `claude -p` does NOT reproduce what users see** — it silently picks
|
||||
the general-purpose subagent, while interactive sessions delegate to the
|
||||
read-first **Explore** subagent. So for "what does my session actually do," use
|
||||
the interactive harness. For a clean per-tool/token breakdown in one shot, use
|
||||
headless (and ask for the Explore subagent in the prompt if you want that path).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **tmux 3.0+**
|
||||
- A logged-in `claude` CLI (Claude Max or API).
|
||||
- codegraph configured as an MCP server (`claude mcp list` shows `codegraph`).
|
||||
The interactive harness uses your global config, so it runs whatever
|
||||
`codegraph` resolves to — point that at your dev build (`npm link` / the
|
||||
symlinked global) to test local changes.
|
||||
- A target repo, cloned and indexed:
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/square/okhttp /tmp/corpus/okhttp
|
||||
cd /tmp/corpus/okhttp && codegraph init -i
|
||||
```
|
||||
Good scale spread for a sweep: Alamofire (~100 files), Excalidraw (~600),
|
||||
OkHttp (~640), VS Code (~10k).
|
||||
|
||||
## Interactive test (the faithful one)
|
||||
|
||||
```bash
|
||||
scripts/agent-eval/itrun.sh <repo-path> <label> "<question>"
|
||||
```
|
||||
|
||||
Example:
|
||||
```bash
|
||||
scripts/agent-eval/itrun.sh /tmp/corpus/vscode vscode \
|
||||
"How does the extension host communicate with the main process?"
|
||||
```
|
||||
|
||||
It opens `claude` in a tmux session, types the question, waits for the agent to
|
||||
finish, then prints:
|
||||
- the `Done (N tool uses · Xk tokens · Ym)` subagent summary (from the pane),
|
||||
- the `Context Xk/1.0M` main-session size,
|
||||
- a **tool breakdown** parsed from the session logs (main + subagents), ending
|
||||
in a `VERDICT: codegraph_explore used Nx | Read N | Grep/Bash N` line.
|
||||
|
||||
### Startup robustness (so unattended runs don't silently no-op)
|
||||
|
||||
Two things bite an unattended driver before the prompt even runs:
|
||||
- **The `❯` glyph is drawn ~6s before the input accepts keystrokes.** Waiting
|
||||
for `❯` is necessary but not sufficient. The harness sends the prompt, then
|
||||
**verifies a chunk of it actually landed in the input box**, retrying until it
|
||||
does — so it can't type into a not-yet-live input and submit nothing.
|
||||
- **First time claude opens a repo it shows "Is this a project you trust?"**
|
||||
(which also contains `❯`). The harness detects that dialog and presses Enter
|
||||
to accept it before typing.
|
||||
|
||||
If the prompt never lands or work never starts, the harness now **fails loudly**
|
||||
(non-zero exit) instead of capturing an empty pane and reporting a bogus run.
|
||||
|
||||
### How completion is detected (the tricky part)
|
||||
|
||||
Claude's TUI redraws in place, so you can't just wait for output to stop. The
|
||||
harness polls `tmux capture-pane` and treats the pane as **busy** when it shows
|
||||
the spinner's elapsed-time-in-parens — `(8s · …)` / `(1m 3s · …)`, matched by
|
||||
`\(([0-9]+m )?[0-9]+s ·`. That's the *universal* working signal: it shows during
|
||||
the pre-stream **thinking** phase (`(8s · thinking with max effort)`, which has
|
||||
no token arrow yet) *and* during streaming. The `↓ N`/`↑ N` token arrow,
|
||||
`esc to interrupt`, and `Initializing…` are OR'd in as belt-and-braces (some TUI
|
||||
versions show one but not the others). It declares **idle** when the `❯` prompt
|
||||
is present and not busy for 10 consecutive polls (~5s, long enough to ride out
|
||||
mid-conversation thinking gaps that briefly drop the spinner). (Technique
|
||||
adapted from devpit's `WaitForIdle`.)
|
||||
|
||||
### Where the breakdown comes from
|
||||
|
||||
`parse-session.mjs` reads the newest session log under
|
||||
`~/.claude/projects/<escaped-cwd>/<session>.jsonl` and its subagent transcripts
|
||||
under `<session>/subagents/*.jsonl`. The **subagent** file is where the real
|
||||
tool calls are — the main log only shows the `Agent` delegation. You can run it
|
||||
standalone:
|
||||
```bash
|
||||
node scripts/agent-eval/parse-session.mjs /tmp/corpus/vscode
|
||||
```
|
||||
|
||||
## Headless test (clean tokens, forceable Explore path)
|
||||
|
||||
```bash
|
||||
scripts/agent-eval/run-agent.sh <repo-path> <label> "<question>"
|
||||
```
|
||||
Writes stream-json and prints the tool sequence + exact tokens/cost. To
|
||||
reproduce the Explore-subagent path headlessly, ask for it:
|
||||
`"Use an Explore subagent to investigate, then answer: …"`.
|
||||
|
||||
## Running a sweep
|
||||
|
||||
Single runs vary a lot (the VS Code question has ranged 26–37 tool uses /
|
||||
88–105k tokens across runs). For a real signal, run N≥3 and take the median:
|
||||
```bash
|
||||
for i in 1 2 3; do
|
||||
scripts/agent-eval/itrun.sh /tmp/corpus/vscode "vscode-$i" "<question>"
|
||||
done
|
||||
```
|
||||
|
||||
## What "good" looks like
|
||||
|
||||
After the explore-first guidance (PR #191), an understanding question should
|
||||
show the agent **leading with `codegraph_explore`** and using `search`/`node`
|
||||
to fill gaps — not a wall of `Read`/`Grep`. Example faithful run:
|
||||
`VERDICT: codegraph_explore used 3x | Read 8 | Grep/Bash 1`. If `explore` is 0
|
||||
and `Read`/`Grep` dominate, the guidance regressed.
|
||||
|
||||
## Output artifacts
|
||||
|
||||
Transcripts and logs go to `$AGENT_EVAL_OUT` (default `/tmp/agent-eval/`):
|
||||
`itrun-<label>.txt` (pane capture), `run-<label>.jsonl` (headless stream-json).
|
||||
@@ -1,112 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Patches tree-sitter-dart to use NAPI bindings compatible with tree-sitter 0.22+
|
||||
*
|
||||
* tree-sitter-dart v1.0.0 ships with NAN-style bindings that are incompatible
|
||||
* with tree-sitter 0.22+ which expects NAPI-style bindings with type-tagged
|
||||
* externals. This script rewrites the binding files and rebuilds.
|
||||
*/
|
||||
const { writeFileSync, existsSync } = require('fs');
|
||||
const { join } = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const DART_DIR = join(__dirname, '..', 'node_modules', 'tree-sitter-dart');
|
||||
|
||||
if (!existsSync(DART_DIR)) {
|
||||
// tree-sitter-dart not installed, skip
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Check if already patched (look for NAPI-style binding)
|
||||
const bindingPath = join(DART_DIR, 'bindings', 'node', 'binding.cc');
|
||||
const { readFileSync } = require('fs');
|
||||
try {
|
||||
const existing = readFileSync(bindingPath, 'utf8');
|
||||
if (existing.includes('napi.h')) {
|
||||
// Already patched, check if build exists
|
||||
const buildPath = join(DART_DIR, 'build', 'Release', 'tree_sitter_dart_binding.node');
|
||||
if (existsSync(buildPath)) {
|
||||
console.log('tree-sitter-dart: already patched and built.');
|
||||
process.exit(0);
|
||||
}
|
||||
// Patched but not built, fall through to rebuild
|
||||
}
|
||||
} catch {
|
||||
// Can't read, continue with patch
|
||||
}
|
||||
|
||||
console.log('Patching tree-sitter-dart for NAPI compatibility...');
|
||||
|
||||
// Write NAPI-compatible binding.cc
|
||||
const bindingCC = `#include <napi.h>
|
||||
|
||||
typedef struct TSLanguage TSLanguage;
|
||||
|
||||
extern "C" TSLanguage *tree_sitter_dart();
|
||||
|
||||
// "tree-sitter", "language" hashed with BLAKE2
|
||||
const napi_type_tag LANGUAGE_TYPE_TAG = {
|
||||
0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
|
||||
};
|
||||
|
||||
Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
||||
exports["name"] = Napi::String::New(env, "dart");
|
||||
auto language = Napi::External<TSLanguage>::New(env, tree_sitter_dart());
|
||||
language.TypeTag(&LANGUAGE_TYPE_TAG);
|
||||
exports["language"] = language;
|
||||
return exports;
|
||||
}
|
||||
|
||||
NODE_API_MODULE(tree_sitter_dart_binding, Init)
|
||||
`;
|
||||
writeFileSync(bindingPath, bindingCC);
|
||||
|
||||
// Write NAPI-compatible binding.gyp
|
||||
const bindingGyp = `{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "tree_sitter_dart_binding",
|
||||
"dependencies": [
|
||||
"<!(node -p \\"require('node-addon-api').targets\\"):node_addon_api_except"
|
||||
],
|
||||
"include_dirs": [
|
||||
"src"
|
||||
],
|
||||
"sources": [
|
||||
"src/parser.c",
|
||||
"bindings/node/binding.cc",
|
||||
"src/scanner.c"
|
||||
],
|
||||
"conditions": [
|
||||
["OS!='win'", {
|
||||
"cflags_c": [
|
||||
"-std=c99"
|
||||
]
|
||||
}, {
|
||||
"cflags_c": [
|
||||
"/std:c11",
|
||||
"/utf-8"
|
||||
]
|
||||
}]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
writeFileSync(join(DART_DIR, 'binding.gyp'), bindingGyp);
|
||||
|
||||
// Rebuild native module
|
||||
try {
|
||||
execSync('npx node-gyp rebuild', {
|
||||
cwd: DART_DIR,
|
||||
stdio: 'pipe',
|
||||
timeout: 120000,
|
||||
});
|
||||
console.log('tree-sitter-dart: patched and rebuilt successfully.');
|
||||
} catch (error) {
|
||||
console.error('Warning: Failed to rebuild tree-sitter-dart native module.');
|
||||
console.error('Dart language support may not work.');
|
||||
if (process.env.DEBUG) {
|
||||
console.error(error.stderr?.toString());
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
const { extractFromSource } = require('./dist/extraction');
|
||||
const { initGrammars, loadAllGrammars } = require('./dist/extraction/grammars');
|
||||
|
||||
(async () => {
|
||||
await initGrammars();
|
||||
await loadAllGrammars();
|
||||
|
||||
const code = `
|
||||
class Parent:
|
||||
pass
|
||||
|
||||
class Child(Parent):
|
||||
pass
|
||||
|
||||
class Multiple(Parent, Mixin):
|
||||
pass
|
||||
`;
|
||||
|
||||
const result = extractFromSource('test.py', code);
|
||||
|
||||
console.log('=== NODES ===');
|
||||
result.nodes.forEach(n => {
|
||||
console.log(`${n.kind}: ${n.name} (line ${n.startLine})`);
|
||||
});
|
||||
|
||||
console.log('\n=== UNRESOLVED REFERENCES ===');
|
||||
result.unresolvedReferences.forEach(r => {
|
||||
console.log(`${r.referenceKind}: ${r.referenceName} (from ${r.fromNodeId})`);
|
||||
});
|
||||
|
||||
console.log('\n=== EDGES ===');
|
||||
result.edges.forEach(e => {
|
||||
console.log(`${e.kind}: ${e.source} -> ${e.target}`);
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user