diff --git a/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/dart-extractor.test.ts b/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/dart-extractor.test.ts index e9f98d6..0947b11 100644 --- a/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/dart-extractor.test.ts +++ b/understand-anything-plugin/packages/core/src/plugins/extractors/__tests__/dart-extractor.test.ts @@ -190,4 +190,33 @@ describe("DartExtractor", () => { parser.delete(); }); }); + + describe("extractStructure - mixins", () => { + it("extracts a plain mixin as a class-like entry", () => { + const { tree, parser, root } = parse(`mixin Walker { + void walk() {} +} +`); + const result = extractor.extractStructure(root); + + expect(result.classes).toHaveLength(1); + expect(result.classes[0].name).toBe("Walker"); + expect(result.classes[0].methods).toContain("walk"); + tree.delete(); + parser.delete(); + }); + + it("extracts a mixin with an `on` constraint", () => { + const { tree, parser, root } = parse(`mixin Runner on Walker { + void run() {} +} +`); + const result = extractor.extractStructure(root); + + expect(result.classes[0].name).toBe("Runner"); + expect(result.classes[0].methods).toContain("run"); + tree.delete(); + parser.delete(); + }); + }); }); diff --git a/understand-anything-plugin/packages/core/src/plugins/extractors/dart-extractor.ts b/understand-anything-plugin/packages/core/src/plugins/extractors/dart-extractor.ts index baae9c1..20e6cac 100644 --- a/understand-anything-plugin/packages/core/src/plugins/extractors/dart-extractor.ts +++ b/understand-anything-plugin/packages/core/src/plugins/extractors/dart-extractor.ts @@ -233,7 +233,10 @@ export class DartExtractor implements LanguageExtractor { this.extractTopLevelFunction(node, functions, exports); break; case "class_definition": - this.extractClassDefinition(node, classes, functions, exports); + this.extractClassLikeDeclaration(node, "class_body", classes, functions, exports); + break; + case "mixin_declaration": + this.extractClassLikeDeclaration(node, "class_body", classes, functions, exports); break; } } @@ -261,8 +264,19 @@ export class DartExtractor implements LanguageExtractor { } } - private extractClassDefinition( + /** + * Extract a class-like declaration that uses a `class_body`-shaped member + * container. Used by `class_definition`, `mixin_declaration`, and (Task 8) + * `extension_declaration`. The only difference between these shapes is the + * body's node type name, which is passed in via `bodyNodeType`. + * + * Anonymous variants (e.g. `extension on Foo` with no name) are handled by + * the caller — this method requires `declNode` to have a leading + * `identifier` child for the name. + */ + private extractClassLikeDeclaration( declNode: TreeSitterNode, + bodyNodeType: string, classes: StructuralAnalysis["classes"], functions: StructuralAnalysis["functions"], exports: StructuralAnalysis["exports"], @@ -274,7 +288,7 @@ export class DartExtractor implements LanguageExtractor { const methods: string[] = []; const properties: string[] = []; - const body = findChild(declNode, "class_body"); + const body = findChild(declNode, bodyNodeType); if (body) { collectClassBody(body, methods, properties, functions, exports); }