feat: add Kotlin language support
Add end-to-end Kotlin parsing, symbol extraction, and visibility detection. Extract findSiblingChild helper into utils.ts for clean AST traversal of Kotlin's modifiers/visibility_modifier sibling pattern. Fix pre-existing duplicate ftsLoaded declaration in kuzu-adapter.ts. Files changed: - supported-languages.ts: add Kotlin enum member - parser-loader.ts, parse-worker.ts: register tree-sitter-kotlin - tree-sitter-queries.ts: add Kotlin queries for classes, interfaces, objects, functions, properties, imports, calls, and heritage - parsing-processor.ts, parse-worker.ts: add Kotlin visibility detection - call-processor.ts, parse-worker.ts: add Kotlin builtins and node types - utils.ts: add .kt/.kts extension mapping and findSiblingChild helper - package.json: add tree-sitter-kotlin dependency
This commit is contained in:
Generated
+26
@@ -30,6 +30,7 @@
|
||||
"tree-sitter-go": "^0.21.0",
|
||||
"tree-sitter-java": "^0.21.0",
|
||||
"tree-sitter-javascript": "^0.21.0",
|
||||
"tree-sitter-kotlin": "^0.3.8",
|
||||
"tree-sitter-php": "^0.23.12",
|
||||
"tree-sitter-python": "^0.21.0",
|
||||
"tree-sitter-rust": "^0.21.0",
|
||||
@@ -4379,6 +4380,31 @@
|
||||
"node": "^18 || ^20 || >= 21"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-kotlin": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-kotlin/-/tree-sitter-kotlin-0.3.8.tgz",
|
||||
"integrity": "sha512-A4obq6bjzmYrA+F0JLLoheFPcofFkctNaZSpnDd+GPn1SfVZLY4/GG4C0cYVBTOShuPBGGAOPLM1JWLZQV4m1g==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^7.1.0",
|
||||
"node-gyp-build": "^4.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tree-sitter": "^0.21.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tree_sitter": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-kotlin/node_modules/node-addon-api": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
||||
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tree-sitter-php": {
|
||||
"version": "0.23.12",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-php/-/tree-sitter-php-0.23.12.tgz",
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
"tree-sitter-go": "^0.21.0",
|
||||
"tree-sitter-java": "^0.21.0",
|
||||
"tree-sitter-javascript": "^0.21.0",
|
||||
"tree-sitter-kotlin": "^0.3.8",
|
||||
"tree-sitter-php": "^0.23.12",
|
||||
"tree-sitter-python": "^0.21.0",
|
||||
"tree-sitter-rust": "^0.21.0",
|
||||
|
||||
@@ -9,6 +9,7 @@ export enum SupportedLanguages {
|
||||
Go = 'go',
|
||||
Rust = 'rust',
|
||||
PHP = 'php',
|
||||
Kotlin = 'kotlin',
|
||||
// Ruby = 'ruby',
|
||||
// Swift = 'swift',
|
||||
}
|
||||
@@ -37,6 +37,9 @@ const FUNCTION_NODE_TYPES = new Set([
|
||||
// Rust
|
||||
'function_item',
|
||||
'impl_item', // Methods inside impl blocks
|
||||
// Kotlin (function_declaration already included above via JS/TS)
|
||||
'anonymous_function',
|
||||
'lambda_literal',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -336,6 +339,12 @@ const BUILT_IN_NAMES = new Set([
|
||||
'mutex_lock', 'mutex_unlock', 'mutex_init',
|
||||
'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
|
||||
'get', 'put',
|
||||
// Kotlin stdlib
|
||||
'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
|
||||
'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
|
||||
'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
|
||||
'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
|
||||
'repeat', 'synchronized',
|
||||
]);
|
||||
|
||||
const isBuiltInOrNoise = (name: string): boolean => BUILT_IN_NAMES.has(name);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
|
||||
import { generateId } from '../../lib/utils.js';
|
||||
import { SymbolTable } from './symbol-table.js';
|
||||
import { ASTCache } from './ast-cache.js';
|
||||
import { getLanguageFromFilename, yieldToEventLoop } from './utils.js';
|
||||
import { findSiblingChild, getLanguageFromFilename, yieldToEventLoop } from './utils.js';
|
||||
import { WorkerPool } from './workers/worker-pool.js';
|
||||
import type { ParseWorkerResult, ParseWorkerInput, ExtractedImport, ExtractedCall, ExtractedHeritage } from './workers/parse-worker.js';
|
||||
|
||||
@@ -114,6 +114,23 @@ const isNodeExported = (node: any, name: string, language: string): boolean => {
|
||||
case 'cpp':
|
||||
return false;
|
||||
|
||||
// Kotlin: Default visibility is public (unlike Java)
|
||||
// visibility_modifier is inside modifiers, a sibling of the name node within the declaration
|
||||
case 'kotlin':
|
||||
while (current) {
|
||||
if (current.parent) {
|
||||
const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
|
||||
if (visMod) {
|
||||
const text = visMod.text;
|
||||
if (text === 'private' || text === 'internal' || text === 'protected') return false;
|
||||
if (text === 'public') return true;
|
||||
}
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -396,6 +396,68 @@ export const PHP_QUERIES = `
|
||||
[(name) (qualified_name)] @heritage.trait))) @heritage
|
||||
`;
|
||||
|
||||
// Kotlin queries - works with tree-sitter-kotlin (fwcd/tree-sitter-kotlin)
|
||||
// Based on official tags.scm; functions use simple_identifier, classes use type_identifier
|
||||
export const KOTLIN_QUERIES = `
|
||||
; ── Classes (regular, data, sealed, enum) ────────────────────────────────
|
||||
(class_declaration
|
||||
(type_identifier) @name) @definition.class
|
||||
|
||||
; ── Interfaces ─────────────────────────────────────────────────────────────
|
||||
(interface_declaration
|
||||
(type_identifier) @name) @definition.interface
|
||||
|
||||
; ── Object declarations (Kotlin singletons) ──────────────────────────────
|
||||
(object_declaration
|
||||
(type_identifier) @name) @definition.class
|
||||
|
||||
; ── Companion objects (named only) ───────────────────────────────────────
|
||||
(companion_object
|
||||
(type_identifier) @name) @definition.class
|
||||
|
||||
; ── Functions (top-level, member, extension) ──────────────────────────────
|
||||
(function_declaration
|
||||
(simple_identifier) @name) @definition.function
|
||||
|
||||
; ── Properties ───────────────────────────────────────────────────────────
|
||||
(property_declaration
|
||||
(variable_declaration
|
||||
(simple_identifier) @name)) @definition.property
|
||||
|
||||
; ── Enum entries ─────────────────────────────────────────────────────────
|
||||
(enum_entry
|
||||
(simple_identifier) @name) @definition.property
|
||||
|
||||
; ── Type aliases ─────────────────────────────────────────────────────────
|
||||
(type_alias
|
||||
(type_identifier) @name) @definition.type
|
||||
|
||||
; ── Imports ──────────────────────────────────────────────────────────────
|
||||
(import_header
|
||||
(identifier) @import.source) @import
|
||||
|
||||
; ── Function calls (direct) ──────────────────────────────────────────────
|
||||
(call_expression
|
||||
(simple_identifier) @call.name) @call
|
||||
|
||||
; ── Method calls (via navigation: obj.method()) ──────────────────────────
|
||||
(call_expression
|
||||
(navigation_expression
|
||||
(navigation_suffix
|
||||
(simple_identifier) @call.name))) @call
|
||||
|
||||
; ── Constructor invocations ──────────────────────────────────────────────
|
||||
(constructor_invocation
|
||||
(user_type
|
||||
(type_identifier) @call.name)) @call
|
||||
|
||||
; ── Heritage: extends / implements via delegation_specifier ──────────────
|
||||
(class_declaration
|
||||
(type_identifier) @heritage.class
|
||||
(delegation_specifier
|
||||
(user_type (type_identifier) @heritage.extends))) @heritage
|
||||
`;
|
||||
|
||||
export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
||||
[SupportedLanguages.TypeScript]: TYPESCRIPT_QUERIES,
|
||||
[SupportedLanguages.JavaScript]: JAVASCRIPT_QUERIES,
|
||||
@@ -407,5 +469,6 @@ export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
||||
[SupportedLanguages.CSharp]: CSHARP_QUERIES,
|
||||
[SupportedLanguages.Rust]: RUST_QUERIES,
|
||||
[SupportedLanguages.PHP]: PHP_QUERIES,
|
||||
[SupportedLanguages.Kotlin]: KOTLIN_QUERIES,
|
||||
};
|
||||
|
||||
@@ -6,6 +6,23 @@ import { SupportedLanguages } from '../../config/supported-languages.js';
|
||||
*/
|
||||
export const yieldToEventLoop = (): Promise<void> => new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
/**
|
||||
* Find a child of `childType` within a sibling node of `siblingType`.
|
||||
* Used for Kotlin AST traversal where visibility_modifier lives inside a modifiers sibling.
|
||||
*/
|
||||
export const findSiblingChild = (parent: any, siblingType: string, childType: string): any | null => {
|
||||
for (let i = 0; i < parent.childCount; i++) {
|
||||
const sibling = parent.child(i);
|
||||
if (sibling?.type === siblingType) {
|
||||
for (let j = 0; j < sibling.childCount; j++) {
|
||||
const child = sibling.child(j);
|
||||
if (child?.type === childType) return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map file extension to SupportedLanguage enum
|
||||
*/
|
||||
@@ -31,6 +48,8 @@ export const getLanguageFromFilename = (filename: string): SupportedLanguages |
|
||||
if (filename.endsWith('.go')) return SupportedLanguages.Go;
|
||||
// Rust
|
||||
if (filename.endsWith('.rs')) return SupportedLanguages.Rust;
|
||||
// Kotlin
|
||||
if (filename.endsWith('.kt') || filename.endsWith('.kts')) return SupportedLanguages.Kotlin;
|
||||
// PHP (all common extensions)
|
||||
if (filename.endsWith('.php') || filename.endsWith('.phtml') ||
|
||||
filename.endsWith('.php3') || filename.endsWith('.php4') ||
|
||||
|
||||
@@ -10,9 +10,10 @@ import CSharp from 'tree-sitter-c-sharp';
|
||||
import Go from 'tree-sitter-go';
|
||||
import Rust from 'tree-sitter-rust';
|
||||
import PHP from 'tree-sitter-php';
|
||||
import Kotlin from 'tree-sitter-kotlin';
|
||||
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
||||
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
||||
import { getLanguageFromFilename } from '../utils.js';
|
||||
import { findSiblingChild, getLanguageFromFilename } from '../utils.js';
|
||||
import { generateId } from '../../../lib/utils.js';
|
||||
|
||||
// ============================================================================
|
||||
@@ -103,6 +104,7 @@ const languageMap: Record<string, any> = {
|
||||
[SupportedLanguages.Go]: Go,
|
||||
[SupportedLanguages.Rust]: Rust,
|
||||
[SupportedLanguages.PHP]: PHP.php_only,
|
||||
[SupportedLanguages.Kotlin]: Kotlin,
|
||||
};
|
||||
|
||||
const setLanguage = (language: SupportedLanguages, filePath: string): void => {
|
||||
@@ -206,6 +208,23 @@ const isNodeExported = (node: any, name: string, language: string): boolean => {
|
||||
// Top-level functions (no parent class) are globally accessible
|
||||
return true;
|
||||
|
||||
// Kotlin: Default visibility is public (unlike Java)
|
||||
// visibility_modifier is inside modifiers, a sibling of the name node within the declaration
|
||||
case 'kotlin':
|
||||
while (current) {
|
||||
if (current.parent) {
|
||||
const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
|
||||
if (visMod) {
|
||||
const text = visMod.text;
|
||||
if (text === 'private' || text === 'internal' || text === 'protected') return false;
|
||||
if (text === 'public') return true;
|
||||
}
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
// No visibility modifier = public (Kotlin default)
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -222,6 +241,8 @@ const FUNCTION_NODE_TYPES = new Set([
|
||||
'method_declaration', 'constructor_declaration',
|
||||
'local_function_statement', 'function_item', 'impl_item',
|
||||
'anonymous_function_creation_expression', // PHP anonymous functions
|
||||
// Kotlin (function_declaration already included above via JS/TS)
|
||||
'anonymous_function', 'lambda_literal',
|
||||
]);
|
||||
|
||||
/** Walk up AST to find enclosing function, return its generateId or null for top-level */
|
||||
@@ -336,6 +357,12 @@ const BUILT_INS = new Set([
|
||||
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
|
||||
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
|
||||
'dd', 'dump',
|
||||
// Kotlin stdlib
|
||||
'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
|
||||
'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
|
||||
'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
|
||||
'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
|
||||
'repeat', 'synchronized',
|
||||
]);
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -676,7 +676,6 @@ export const getEmbeddingTableName = (): string => EMBEDDING_TABLE_NAME;
|
||||
* Load the FTS extension (required before using FTS functions).
|
||||
* Safe to call multiple times — tracks loaded state.
|
||||
*/
|
||||
let ftsLoaded = false;
|
||||
export const loadFTSExtension = async (): Promise<void> => {
|
||||
if (ftsLoaded) return;
|
||||
if (!conn) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import CSharp from 'tree-sitter-c-sharp';
|
||||
import Go from 'tree-sitter-go';
|
||||
import Rust from 'tree-sitter-rust';
|
||||
import PHP from 'tree-sitter-php';
|
||||
import Kotlin from 'tree-sitter-kotlin';
|
||||
import { SupportedLanguages } from '../../config/supported-languages.js';
|
||||
|
||||
let parser: Parser | null = null;
|
||||
@@ -25,6 +26,7 @@ const languageMap: Record<string, any> = {
|
||||
[SupportedLanguages.Go]: Go,
|
||||
[SupportedLanguages.Rust]: Rust,
|
||||
[SupportedLanguages.PHP]: PHP.php_only,
|
||||
[SupportedLanguages.Kotlin]: Kotlin,
|
||||
};
|
||||
|
||||
export const loadParser = async (): Promise<Parser> => {
|
||||
|
||||
Reference in New Issue
Block a user