From 88782ebdc99a06cbbeeb43b58eb31e116cbbe3f7 Mon Sep 17 00:00:00 2001 From: adit Date: Sun, 21 Dec 2025 15:51:24 +0700 Subject: [PATCH 1/2] add dart parser --- pnpm-lock.yaml | 10 +- src/package.json | 2 +- .../parseSourceCodeDefinitions.dart.spec.ts | 260 ++++++++++++++++++ src/services/tree-sitter/index.ts | 2 + src/services/tree-sitter/languageParser.ts | 5 + src/services/tree-sitter/queries/dart.ts | 112 ++++++++ src/services/tree-sitter/queries/index.ts | 1 + 7 files changed, 386 insertions(+), 6 deletions(-) create mode 100644 src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts create mode 100644 src/services/tree-sitter/queries/dart.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 971fcd4cc2..5e881423b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -823,8 +823,8 @@ importers: specifier: ^0.2.3 version: 0.2.4 tree-sitter-wasms: - specifier: ^0.1.12 - version: 0.1.12 + specifier: ^0.1.13 + version: 0.1.13 turndown: specifier: ^7.2.0 version: 7.2.0 @@ -9471,8 +9471,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - tree-sitter-wasms@0.1.12: - resolution: {integrity: sha512-N9Jp+dkB23Ul5Gw0utm+3pvG4km4Fxsi2jmtMFg7ivzwqWPlSyrYQIrOmcX+79taVfcHEA+NzP0hl7vXL8DNUQ==} + tree-sitter-wasms@0.1.13: + resolution: {integrity: sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==} trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -20054,7 +20054,7 @@ snapshots: tree-kill@1.2.2: {} - tree-sitter-wasms@0.1.12: {} + tree-sitter-wasms@0.1.13: {} trim-lines@3.0.1: {} diff --git a/src/package.json b/src/package.json index ce2a20b170..259a6ee171 100644 --- a/src/package.json +++ b/src/package.json @@ -524,7 +524,7 @@ "strip-bom": "^5.0.0", "tiktoken": "^1.0.21", "tmp": "^0.2.3", - "tree-sitter-wasms": "^0.1.12", + "tree-sitter-wasms": "^0.1.13", "turndown": "^7.2.0", "uuid": "^11.1.0", "vscode-material-icons": "^0.1.1", diff --git a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts new file mode 100644 index 0000000000..873a70ff0f --- /dev/null +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts @@ -0,0 +1,260 @@ +import { testParseSourceCodeDefinitions, debugLog } from "./helpers" +import { dartQuery } from "../queries" + +// Dart test options +const dartOptions = { + language: "dart", + wasmFile: "tree-sitter-dart.wasm", + queryString: dartQuery, + extKey: "dart", +} + +describe("parseSourceCodeDefinitionsForFile with Dart", () => { + let parseResult: string | undefined + + beforeAll(async () => { + const dartCode = ` +// Class definition with constructor +class MyClass { + String name; + int age; + + MyClass(this.name, this.age); + + void greet() { + print('Hello, \$name!'); + } + + String get description => 'Name: \$name, Age: \$age'; + + set description(String value) { + // setter implementation + } +} + +// Factory constructor +class LoggerFactory { + LoggerFactory._(); + + factory LoggerFactory.create() { + return LoggerFactory._(); + } +} + +// Mixin declaration +mixin MyMixin { + void mixinMethod() { + print('Mixin method called'); + } +} + +// Extension declaration +extension StringExtension on String { + int get length => this.length; + + String capitalize() { + return '\${this[0].toUpperCase()}\${substring(1)}'; + } +} + +// Enum declaration +enum Status { + active, + inactive, + pending; + + String get displayName { + switch (this) { + case Status.active: + return 'Active'; + case Status.inactive: + return 'Inactive'; + case Status.pending: + return 'Pending'; + } + } +} + +// Type alias +typedef MyType = Map; +typedef Callback = void Function(String); + +// Standalone function +void mainFunction() { + var instance = MyClass('test', 25); + instance.greet(); + + var logger = LoggerFactory.create(); + + var status = Status.active; + print(status.displayName); +} + +// Function with parameters +String formatUser(String name, int age) { + return '\$name is \$age years old'; +} + +// Operator overloading +class Vector { + final double x, y; + + Vector(this.x, this.y); + + Vector operator +(Vector other) { + return Vector(x + other.x, y + other.y); + } + + bool operator ==(Object other) { + return other is Vector && x == other.x && y == other.y; + } +} + +// Class with mixin +class EnhancedClass with MyMixin { + void doSomething() { + mixinMethod(); + } +} + +// Generic class +class Container { + T value; + + Container(this.value); + + T get() => value; + + void set(T newValue) { + value = newValue; + } +} + +// Abstract class +abstract class Shape { + double get area; + + void draw(); +} + +// Interface-like class +class Drawable { + void render() {} +} + +// Static methods and properties +class MathUtils { + static const double pi = 3.14159; + + static double circleArea(double radius) { + return pi * radius * radius; + } +} + +// Cascade notation usage +void cascadeExample() { + var builder = StringBuilder() + ..write('Hello') + ..write(' ') + ..write('World') + ..toString(); +} + +class StringBuilder { + String _buffer = ''; + + void write(String text) { + _buffer += text; + } + + String toString() => _buffer; +} +` + parseResult = await testParseSourceCodeDefinitions("test.dart", dartCode, dartOptions) + debugLog("Dart Parse Result:", parseResult) + }) + + it("should parse class definitions", () => { + expect(parseResult).toMatch(/class MyClass \{/) + expect(parseResult).toMatch(/class LoggerFactory \{/) + expect(parseResult).toMatch(/class Vector \{/) + expect(parseResult).toMatch(/class EnhancedClass with/) + expect(parseResult).toMatch(/class Container { + expect(parseResult).toMatch(/greet/) + expect(parseResult).toMatch(/description/) // getter + expect(parseResult).toMatch(/create/) // factory constructor + expect(parseResult).toMatch(/operator \+/) // operator + expect(parseResult).toMatch(/operator ==/) // operator + expect(parseResult).toMatch(/doSomething/) + expect(parseResult).toMatch(/get/) + expect(parseResult).toMatch(/set/) + expect(parseResult).toMatch(/render/) + expect(parseResult).toMatch(/circleArea/) + expect(parseResult).toMatch(/write/) + expect(parseResult).toMatch(/toString/) + }) + + it("should parse mixin declarations", () => { + expect(parseResult).toMatch(/mixin MyMixin/) + }) + + it("should parse extension declarations", () => { + expect(parseResult).toMatch(/extension StringExtension/) + }) + + it("should parse enum declarations", () => { + expect(parseResult).toMatch(/enum Status/) + }) + + it("should parse type aliases", () => { + expect(parseResult).toMatch(/typedef MyType/) + expect(parseResult).toMatch(/typedef Callback/) + }) + + it("should parse function definitions", () => { + expect(parseResult).toMatch(/mainFunction/) + expect(parseResult).toMatch(/formatUser/) + expect(parseResult).toMatch(/cascadeExample/) + }) + + it("should handle constructor definitions", () => { + expect(parseResult).toMatch(/MyClass\(/) // constructor + expect(parseResult).toMatch(/Vector\(/) // constructor + expect(parseResult).toMatch(/Container\(/) // constructor + }) + + it("should handle getter and setter definitions", () => { + expect(parseResult).toMatch(/get description/) + expect(parseResult).toMatch(/set description/) + expect(parseResult).toMatch(/get area/) + expect(parseResult).toMatch(/get length/) // extension getter + }) + + it("should handle operator definitions", () => { + expect(parseResult).toMatch(/operator \+/) + expect(parseResult).toMatch(/operator ==/) + }) + + it("should handle factory constructors", () => { + expect(parseResult).toMatch(/factory LoggerFactory\.create\(/) + }) + + it("should handle generic classes", () => { + expect(parseResult).toMatch(/class Container/) + }) + + it("should handle abstract classes", () => { + expect(parseResult).toMatch(/abstract class Shape/) + }) + + it("should handle static methods and properties", () => { + expect(parseResult).toMatch(/static double circleArea/) + }) +}) diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 145ba84730..9b563d365f 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -91,6 +91,8 @@ const extensions = [ "erb", // Visual Basic .NET "vb", + // Dart + "dart", ].map((e) => `.${e}`) export { extensions } diff --git a/src/services/tree-sitter/languageParser.ts b/src/services/tree-sitter/languageParser.ts index a8ac0a9ead..5446662d55 100644 --- a/src/services/tree-sitter/languageParser.ts +++ b/src/services/tree-sitter/languageParser.ts @@ -28,6 +28,7 @@ import { embeddedTemplateQuery, elispQuery, elixirQuery, + dartQuery, } from "./queries" export interface LanguageParser { @@ -137,6 +138,10 @@ export async function loadRequiredLanguageParsers(filesToParse: string[], source language = await loadLanguage("c_sharp", sourceDirectory) query = new Query(language, csharpQuery) break + case "dart": + language = await loadLanguage("dart", sourceDirectory) + query = new Query(language, dartQuery) + break case "rb": language = await loadLanguage("ruby", sourceDirectory) query = new Query(language, rubyQuery) diff --git a/src/services/tree-sitter/queries/dart.ts b/src/services/tree-sitter/queries/dart.ts new file mode 100644 index 0000000000..3b7823f324 --- /dev/null +++ b/src/services/tree-sitter/queries/dart.ts @@ -0,0 +1,112 @@ +/* +Dart Tree-sitter Query Patterns + +This file contains query patterns for Dart language constructs: +- class definitions - Captures standard class definitions +- method signatures - Captures all method types (getters, setters, constructors, factory, operators) +- function signatures - Captures standalone function definitions +- mixin declarations - Captures Dart mixin definitions +- extension declarations - Captures extension methods on existing types +- enum declarations - Captures enum definitions +- type aliases - Captures typedef declarations +- references - Captures class instantiation, method calls, property access +*/ +export default ` +; Class definitions +(class_definition + name: (identifier) @name) @definition.class + +; Method signatures (various types) +(method_signature + (function_signature)) @definition.method + +(method_signature +(getter_signature + name: (identifier) @name)) @definition.method + +(method_signature +(setter_signature + name: (identifier) @name)) @definition.method + +(method_signature + (function_signature + name: (identifier) @name)) @definition.method + +(method_signature + (factory_constructor_signature + (identifier) @name)) @definition.method + +(method_signature + (constructor_signature + name: (identifier) @name)) @definition.method + +(method_signature + (operator_signature)) @definition.method + +(method_signature) @definition.method + +; Type aliases +(type_alias + (type_identifier) @name) @definition.type + +; Mixin declarations +(mixin_declaration + (mixin) + (identifier) @name) @definition.mixin + +; Extension declarations +(extension_declaration + name: (identifier) @name) @definition.extension + +; Enum declarations +(enum_declaration + name: (identifier) @name) @definition.enum + +; Function signatures +(function_signature + name: (identifier) @name) @definition.function + +; References +(new_expression + (type_identifier) @name) @reference.class + +(initialized_variable_definition + name: (identifier) + value: (identifier) @name + value: (selector + "!"? + (argument_part + (arguments + (argument)*))?)?) @reference.class + +(assignment_expression + left: (assignable_expression + (identifier) + (unconditional_assignable_selector + "." + (identifier) @name))) @reference.call + +(assignment_expression + left: (assignable_expression + (identifier) + (conditional_assignable_selector + "?." + (identifier) @name))) @reference.call + +((identifier) @name + (selector + "!"? + (conditional_assignable_selector + "?." (identifier) @name)? + (unconditional_assignable_selector + "."? (identifier) @name)? + (argument_part + (arguments + (argument)*))?)* + (cascade_section + (cascade_selector + (identifier)) @name + (argument_part + (arguments + (argument)*))?)?) @reference.call +` diff --git a/src/services/tree-sitter/queries/index.ts b/src/services/tree-sitter/queries/index.ts index de9b9cafa3..c8ec8bf6b2 100644 --- a/src/services/tree-sitter/queries/index.ts +++ b/src/services/tree-sitter/queries/index.ts @@ -1,4 +1,5 @@ export { solidityQuery } from "./solidity" +export { default as dartQuery } from "./dart" export { default as phpQuery } from "./php" export { vueQuery } from "./vue" export { default as typescriptQuery } from "./typescript" From 8e8589b5e845bb6667fd27f6f9ffcc376f35be2b Mon Sep 17 00:00:00 2001 From: adit Date: Thu, 8 Jan 2026 16:29:45 +0700 Subject: [PATCH 2/2] fix: split duplicate value field in Dart tree-sitter query pattern --- .../parseSourceCodeDefinitions.dart.spec.ts | 14 ++++++++++++++ src/services/tree-sitter/queries/dart.ts | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts index 873a70ff0f..5270adadbe 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.dart.spec.ts @@ -257,4 +257,18 @@ class StringBuilder { it("should handle static methods and properties", () => { expect(parseResult).toMatch(/static double circleArea/) }) + + it("should parse variable initialization with identifier reference", () => { + expect(parseResult).toMatch(/var instance = MyClass/) + expect(parseResult).toMatch(/var status = Status/) + }) + + it("should parse variable initialization with method calls", () => { + expect(parseResult).toMatch(/var logger = LoggerFactory\.create\(\)/) + }) + + it("should parse reference patterns", () => { + expect(parseResult).toMatch(/instance\.greet/) + expect(parseResult).toMatch(/status\.displayName/) + }) }) diff --git a/src/services/tree-sitter/queries/dart.ts b/src/services/tree-sitter/queries/dart.ts index 3b7823f324..27718cbdce 100644 --- a/src/services/tree-sitter/queries/dart.ts +++ b/src/services/tree-sitter/queries/dart.ts @@ -70,9 +70,14 @@ export default ` (new_expression (type_identifier) @name) @reference.class +; Variables initialized with identifier reference +(initialized_variable_definition + name: (identifier) + value: (identifier) @name) @reference.class + +; Variables initialized with selector/method call (initialized_variable_definition name: (identifier) - value: (identifier) @name value: (selector "!"? (argument_part