@liendev/parser 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ This package provides the core parsing and analysis capabilities used by Lien's
6
6
 
7
7
  ## Features
8
8
 
9
- - **AST Parsing** — Tree-sitter-based parsing for TypeScript, JavaScript, Python, PHP, Rust, and Go
9
+ - **AST Parsing** — Tree-sitter-based parsing for TypeScript, JavaScript, Python, PHP, Rust, Go, Java, C#, Ruby, and Kotlin
10
10
  - **Semantic Chunking** — Split code into meaningful chunks respecting function/class boundaries
11
11
  - **Complexity Analysis** — Cyclomatic, cognitive, and Halstead complexity metrics
12
12
  - **Dependency Analysis** — Import/export tracking with transitive dependent resolution
@@ -24,8 +24,12 @@ This package provides the core parsing and analysis capabilities used by Lien's
24
24
  | PHP | Yes | Yes | Yes |
25
25
  | Rust | Yes | Yes | Yes |
26
26
  | Go | Yes | Yes | Yes |
27
+ | Java | Yes | Yes | Yes |
28
+ | C# | Yes | Yes | Yes |
29
+ | Ruby | Yes | Yes | Yes |
30
+ | Kotlin | Yes | Yes | Yes |
27
31
 
28
- Line-based chunking and symbol extraction are available for additional languages including Java, C#, Ruby, and Vue.
32
+ Line-based chunking and symbol extraction are available for additional languages including Vue, Liquid, C/C++, Swift, Scala, and Markdown.
29
33
 
30
34
  ## Usage
31
35
 
@@ -1 +1 @@
1
- {"version":3,"file":"symbol-helpers.d.ts","sourceRoot":"","sources":["../../../src/ast/extractors/symbol-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA0BjF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAerF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK/F"}
1
+ {"version":3,"file":"symbol-helpers.d.ts","sourceRoot":"","sources":["../../../src/ast/extractors/symbol-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAqCjF;AASD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAerF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK/F"}
@@ -2,7 +2,22 @@
2
2
  * Extract function/method signature
3
3
  */
4
4
  export function extractSignature(node, content) {
5
- // Get the first line of the function (up to opening brace or arrow)
5
+ // Preferred: bound the signature by where the function body begins. This is
6
+ // language-agnostic — it works for brace languages (`… ) {`), colon languages
7
+ // (Python `… ):`), and `end` languages (Ruby `def … )`). The legacy brace/arrow
8
+ // scan below walked an entire no-brace body into the "signature" (e.g. Python,
9
+ // Ruby), which this avoids.
10
+ const bodyNode = node.childForFieldName('body');
11
+ if (bodyNode) {
12
+ const signature = content
13
+ .slice(node.startIndex, bodyNode.startIndex)
14
+ .replace(/\s+/g, ' ') // collapse newlines/indentation (matches the old join-with-space)
15
+ .replace(/(\{|=>|:)\s*$/, '') // drop a dangling block-opener if it was captured
16
+ .trim();
17
+ return clampSignatureLength(signature);
18
+ }
19
+ // Fallback: nodes with no `body` field (e.g. Rust trait signatures, abstract
20
+ // interface methods). Preserve the original first-line / brace-scan behavior.
6
21
  const startLine = node.startPosition.row;
7
22
  const lines = content.split('\n');
8
23
  let signature = lines[startLine] || '';
@@ -16,11 +31,13 @@ export function extractSignature(node, content) {
16
31
  }
17
32
  // Clean up signature
18
33
  signature = signature.split('{')[0].split('=>')[0].trim();
19
- // Limit length
20
- if (signature.length > 200) {
21
- signature = signature.substring(0, 197) + '...';
22
- }
23
- return signature;
34
+ return clampSignatureLength(signature);
35
+ }
36
+ /**
37
+ * Truncate an over-long signature to keep stored chunks compact.
38
+ */
39
+ function clampSignatureLength(signature) {
40
+ return signature.length > 200 ? signature.substring(0, 197) + '...' : signature;
24
41
  }
25
42
  /**
26
43
  * Extract parameter list from function node
@@ -1 +1 @@
1
- {"version":3,"file":"symbol-helpers.js","sourceRoot":"","sources":["../../../src/ast/extractors/symbol-helpers.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB,EAAE,OAAe;IACvE,oEAAoE;IACpE,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAEvC,wEAAwE;IACxE,IAAI,WAAW,GAAG,SAAS,CAAC;IAC5B,OACE,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG;QAClC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EACzB,CAAC;QACD,WAAW,EAAE,CAAC;QACd,SAAS,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,qBAAqB;IACrB,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE1D,eAAe;IACf,IAAI,SAAS,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC3B,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;IAClD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAuB,EAAE,QAAgB;IACzE,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,uBAAuB;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU;QAAE,OAAO,UAAU,CAAC;IAEnC,2BAA2B;IAC3B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACtB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAuB,EAAE,QAAgB;IACzE,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAC7D,IAAI,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IAEtC,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"symbol-helpers.js","sourceRoot":"","sources":["../../../src/ast/extractors/symbol-helpers.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB,EAAE,OAAe;IACvE,4EAA4E;IAC5E,8EAA8E;IAC9E,gFAAgF;IAChF,+EAA+E;IAC/E,4BAA4B;IAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,SAAS,GAAG,OAAO;aACtB,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC;aAC3C,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,kEAAkE;aACvF,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,kDAAkD;aAC/E,IAAI,EAAE,CAAC;QACV,OAAO,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAEvC,wEAAwE;IACxE,IAAI,WAAW,GAAG,SAAS,CAAC;IAC5B,OACE,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG;QAClC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QACxB,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EACzB,CAAC;QACD,WAAW,EAAE,CAAC;QACd,SAAS,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,qBAAqB;IACrB,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE1D,OAAO,oBAAoB,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,SAAiB;IAC7C,OAAO,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAuB,EAAE,QAAgB;IACzE,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,uBAAuB;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACxD,IAAI,CAAC,UAAU;QAAE,OAAO,UAAU,CAAC;IAEnC,2BAA2B;IAC3B,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,aAAa,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YACtB,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAuB,EAAE,QAAgB;IACzE,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAC7D,IAAI,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IAEtC,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,83 @@
1
+ import type Parser from 'tree-sitter';
2
+ import type { SymbolInfo } from '../types.js';
3
+ import type { LanguageDefinition } from './types.js';
4
+ import type { LanguageTraverser, DeclarationFunctionInfo } from '../traversers/types.js';
5
+ import type { LanguageExportExtractor, LanguageImportExtractor, LanguageSymbolExtractor } from '../extractors/types.js';
6
+ /**
7
+ * Kotlin AST traverser.
8
+ *
9
+ * Methods live inside `class_declaration` / `object_declaration` bodies
10
+ * (`class_body` / `enum_class_body`). `companion_object` is transparent — its
11
+ * members are traversed so their methods are captured (attributed to the
12
+ * enclosing class). Lambdas can appear in property initializers
13
+ * (`val f = { … }`).
14
+ */
15
+ export declare class KotlinTraverser implements LanguageTraverser {
16
+ targetNodeTypes: string[];
17
+ containerTypes: string[];
18
+ declarationTypes: string[];
19
+ functionTypes: string[];
20
+ shouldExtractChildren(node: Parser.SyntaxNode): boolean;
21
+ isDeclarationWithFunction(node: Parser.SyntaxNode): boolean;
22
+ getContainerBody(node: Parser.SyntaxNode): Parser.SyntaxNode | null;
23
+ shouldTraverseChildren(node: Parser.SyntaxNode): boolean;
24
+ findParentContainerName(node: Parser.SyntaxNode): string | undefined;
25
+ findFunctionInDeclaration(node: Parser.SyntaxNode): DeclarationFunctionInfo;
26
+ }
27
+ /**
28
+ * Kotlin export extractor.
29
+ *
30
+ * Kotlin has no `export` keyword and is `public` by default — top-level
31
+ * declarations and (non-private/internal) members are importable. Interface
32
+ * members are implicitly public.
33
+ */
34
+ export declare class KotlinExportExtractor implements LanguageExportExtractor {
35
+ extractExports(rootNode: Parser.SyntaxNode): string[];
36
+ private extractFromNode;
37
+ private extractMembers;
38
+ private propertyName;
39
+ }
40
+ /**
41
+ * Kotlin import extractor.
42
+ *
43
+ * Handles `import a.b.C`, wildcard `import a.b.*`, and aliased
44
+ * `import a.b.C as D`. The grammar nests `import_header` nodes inside an
45
+ * `import_list`; the engine's import scan descends one level to reach them
46
+ * (see `collectImportNodes` in ast/symbols.ts). Standard-library imports
47
+ * (kotlin.*, kotlinx.*, java.*, javax.*) are filtered out.
48
+ */
49
+ export declare class KotlinImportExtractor implements LanguageImportExtractor {
50
+ readonly importNodeTypes: string[];
51
+ extractImportPath(node: Parser.SyntaxNode): string | null;
52
+ processImportSymbols(node: Parser.SyntaxNode): {
53
+ importPath: string;
54
+ symbols: string[];
55
+ } | null;
56
+ private getImportPath;
57
+ }
58
+ /**
59
+ * Kotlin symbol extractor.
60
+ *
61
+ * Handles `function_declaration`, `class_declaration` (class / interface / enum
62
+ * variants — distinguished by the keyword token), and `object_declaration`.
63
+ * `SymbolInfo.type` only has `function|method|class|interface`, so objects and
64
+ * enums map to `class` (keeping the keyword in the signature).
65
+ *
66
+ * Call sites: `call_expression` — `foo()` (simple_identifier) and `a.b.c()`
67
+ * (the member is the `simple_identifier` inside the trailing `navigation_suffix`).
68
+ */
69
+ export declare class KotlinSymbolExtractor implements LanguageSymbolExtractor {
70
+ readonly symbolNodeTypes: string[];
71
+ extractSymbol(node: Parser.SyntaxNode, content: string, parentClass?: string): SymbolInfo | null;
72
+ extractCallSite(node: Parser.SyntaxNode): {
73
+ symbol: string;
74
+ line: number;
75
+ key: string;
76
+ } | null;
77
+ private extractFunctionInfo;
78
+ private extractClassInfo;
79
+ private extractObjectInfo;
80
+ private makeSymbol;
81
+ }
82
+ export declare const kotlinDefinition: LanguageDefinition;
83
+ //# sourceMappingURL=kotlin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kotlin.d.ts","sourceRoot":"","sources":["../../../src/ast/languages/kotlin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACzF,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AA+HhC;;;;;;;;GAQG;AACH,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,eAAe,WAA4B;IAE3C,cAAc,WAA+C;IAE7D,gBAAgB,WAA4B;IAE5C,aAAa,WAA4C;IAEzD,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IAIvD,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IAI3D,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI;IAKnE,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO;IASxD,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,SAAS;IAWpE,yBAAyB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,uBAAuB;CAS5E;AAMD;;;;;;GAMG;AACH,qBAAa,qBAAsB,YAAW,uBAAuB;IACnE,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,EAAE;IAgBrD,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,YAAY;CAKrB;AAmBD;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,YAAW,uBAAuB;IACnE,QAAQ,CAAC,eAAe,WAAqB;IAE7C,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI;IAMzD,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI;IAoB/F,OAAO,CAAC,aAAa;CAMtB;AAMD;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,YAAW,uBAAuB;IACnE,QAAQ,CAAC,eAAe,WAAuE;IAE/F,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAahG,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAoB9F,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,UAAU;CAcnB;AAMD,eAAO,MAAM,gBAAgB,EAAE,kBAoH9B,CAAC"}
@@ -0,0 +1,506 @@
1
+ import Kotlin from 'tree-sitter-kotlin';
2
+ import { calculateComplexity } from '../complexity/index.js';
3
+ // =============================================================================
4
+ // HELPERS
5
+ //
6
+ // The tree-sitter-kotlin grammar (fwcd) does NOT assign field names, so unlike
7
+ // the Java definition we locate children by node TYPE rather than via
8
+ // `childForFieldName`. These helpers centralize that.
9
+ // =============================================================================
10
+ /** First named child of a given type. */
11
+ function childByType(node, type) {
12
+ return node.namedChildren.find(child => child.type === type) ?? null;
13
+ }
14
+ /** Whether a node has a child token of a given type (incl. anonymous keyword tokens). */
15
+ function hasTokenChild(node, type) {
16
+ return node.children.some(child => child.type === type);
17
+ }
18
+ /**
19
+ * The function-valued initializer of a property, if the property's initializer
20
+ * is *directly* a lambda or anonymous function (`val f = { … }`). Checks the
21
+ * initializer (the last named child) rather than any descendant, so a lambda
22
+ * passed as a call argument (`val n = xs.count { it > 0 }`) is NOT treated as a
23
+ * function-valued property.
24
+ */
25
+ function propertyInitializerFunction(node) {
26
+ const initializer = node.namedChildren.at(-1);
27
+ if (!initializer)
28
+ return null;
29
+ return initializer.type === 'lambda_literal' || initializer.type === 'anonymous_function'
30
+ ? initializer
31
+ : null;
32
+ }
33
+ /** The `function_body` child of a function_declaration ({ … } block OR `= expr`). */
34
+ function functionBody(node) {
35
+ return childByType(node, 'function_body');
36
+ }
37
+ /**
38
+ * The function name: the first direct `simple_identifier` child of a
39
+ * function_declaration. (Extension-function receivers are `user_type` nodes, so
40
+ * the bare simple_identifier is the name in the common case.)
41
+ */
42
+ function functionName(node) {
43
+ return childByType(node, 'simple_identifier')?.text;
44
+ }
45
+ /** The declared name of a class/object declaration (`type_identifier`). */
46
+ function declarationName(node) {
47
+ return childByType(node, 'type_identifier')?.text;
48
+ }
49
+ const SIGNATURE_MAX = 200;
50
+ function clamp(signature) {
51
+ return signature.length > SIGNATURE_MAX
52
+ ? signature.slice(0, SIGNATURE_MAX - 3) + '...'
53
+ : signature;
54
+ }
55
+ /**
56
+ * Function signature, bounded by where the body begins. `function_body` covers
57
+ * both block bodies (`{ … }`) and expression bodies (`= expr`, which starts at
58
+ * the `=`), so slicing up to its start yields a clean `fun foo(a: Int): Int`
59
+ * for both. Abstract/interface methods have no body — use the whole node.
60
+ */
61
+ function functionSignature(node, content) {
62
+ const body = functionBody(node);
63
+ const end = body ? body.startIndex : node.endIndex;
64
+ const signature = content
65
+ .slice(node.startIndex, end)
66
+ .replace(/\s+/g, ' ')
67
+ .replace(/(\{|=)\s*$/, '') // drop a trailing block-opener / expression `=` if captured
68
+ .trim();
69
+ return clamp(signature);
70
+ }
71
+ /** Parameter texts from a function's `function_value_parameters`. */
72
+ function functionParameters(node) {
73
+ const params = childByType(node, 'function_value_parameters');
74
+ if (!params)
75
+ return [];
76
+ return params.namedChildren.filter(p => p.text.trim()).map(p => p.text);
77
+ }
78
+ /**
79
+ * Return type: the type node that sits between the parameter list and the body.
80
+ * Kotlin's grammar emits it as `user_type` / `nullable_type` / `function_type`.
81
+ */
82
+ function functionReturnType(node) {
83
+ const children = node.namedChildren;
84
+ const paramsIndex = children.findIndex(c => c.type === 'function_value_parameters');
85
+ if (paramsIndex === -1)
86
+ return undefined;
87
+ for (let i = paramsIndex + 1; i < children.length; i++) {
88
+ const c = children[i];
89
+ if (c.type === 'function_body')
90
+ break;
91
+ if (c.type === 'user_type' || c.type === 'nullable_type' || c.type === 'function_type') {
92
+ return c.text;
93
+ }
94
+ }
95
+ return undefined;
96
+ }
97
+ /** Visibility modifier text (`private` / `internal` / `protected` / `public`), if any. */
98
+ function visibilityModifier(node) {
99
+ const modifiers = childByType(node, 'modifiers');
100
+ if (!modifiers)
101
+ return null;
102
+ return modifiers.namedChildren.find(c => c.type === 'visibility_modifier')?.text ?? null;
103
+ }
104
+ /**
105
+ * Kotlin declarations are `public` by default. We treat a declaration as
106
+ * "exported" (importable / part of the API) unless it is explicitly `private`
107
+ * or `internal`. `protected` members stay visible to subclasses, so we keep
108
+ * them. (Inverse of Java's "has a `public` modifier" check.)
109
+ */
110
+ function isExported(node) {
111
+ const visibility = visibilityModifier(node);
112
+ return visibility !== 'private' && visibility !== 'internal';
113
+ }
114
+ // =============================================================================
115
+ // TRAVERSER
116
+ // =============================================================================
117
+ /**
118
+ * Kotlin AST traverser.
119
+ *
120
+ * Methods live inside `class_declaration` / `object_declaration` bodies
121
+ * (`class_body` / `enum_class_body`). `companion_object` is transparent — its
122
+ * members are traversed so their methods are captured (attributed to the
123
+ * enclosing class). Lambdas can appear in property initializers
124
+ * (`val f = { … }`).
125
+ */
126
+ export class KotlinTraverser {
127
+ targetNodeTypes = ['function_declaration'];
128
+ containerTypes = ['class_declaration', 'object_declaration'];
129
+ declarationTypes = ['property_declaration'];
130
+ functionTypes = ['lambda_literal', 'anonymous_function'];
131
+ shouldExtractChildren(node) {
132
+ return this.containerTypes.includes(node.type);
133
+ }
134
+ isDeclarationWithFunction(node) {
135
+ return node.type === 'property_declaration' && propertyInitializerFunction(node) !== null;
136
+ }
137
+ getContainerBody(node) {
138
+ if (!this.containerTypes.includes(node.type))
139
+ return null;
140
+ return childByType(node, 'class_body') ?? childByType(node, 'enum_class_body');
141
+ }
142
+ shouldTraverseChildren(node) {
143
+ return (node.type === 'source_file' ||
144
+ node.type === 'class_body' ||
145
+ node.type === 'enum_class_body' ||
146
+ node.type === 'companion_object');
147
+ }
148
+ findParentContainerName(node) {
149
+ let current = node.parent;
150
+ while (current) {
151
+ if (current.type === 'class_declaration' || current.type === 'object_declaration') {
152
+ return declarationName(current);
153
+ }
154
+ current = current.parent;
155
+ }
156
+ return undefined;
157
+ }
158
+ findFunctionInDeclaration(node) {
159
+ if (node.type !== 'property_declaration') {
160
+ return { hasFunction: false, functionNode: null };
161
+ }
162
+ const fn = propertyInitializerFunction(node);
163
+ return fn
164
+ ? { hasFunction: true, functionNode: fn }
165
+ : { hasFunction: false, functionNode: null };
166
+ }
167
+ }
168
+ // =============================================================================
169
+ // EXPORT EXTRACTOR
170
+ // =============================================================================
171
+ /**
172
+ * Kotlin export extractor.
173
+ *
174
+ * Kotlin has no `export` keyword and is `public` by default — top-level
175
+ * declarations and (non-private/internal) members are importable. Interface
176
+ * members are implicitly public.
177
+ */
178
+ export class KotlinExportExtractor {
179
+ extractExports(rootNode) {
180
+ const exports = [];
181
+ const seen = new Set();
182
+ const addExport = (name) => {
183
+ if (name && !seen.has(name)) {
184
+ seen.add(name);
185
+ exports.push(name);
186
+ }
187
+ };
188
+ rootNode.namedChildren.forEach(child => this.extractFromNode(child, addExport));
189
+ return exports;
190
+ }
191
+ extractFromNode(node, addExport) {
192
+ switch (node.type) {
193
+ case 'function_declaration':
194
+ if (isExported(node))
195
+ addExport(functionName(node));
196
+ break;
197
+ case 'property_declaration':
198
+ if (isExported(node))
199
+ addExport(this.propertyName(node));
200
+ break;
201
+ case 'class_declaration':
202
+ case 'object_declaration':
203
+ // A private/internal container exposes nothing — skip it and its members.
204
+ if (isExported(node)) {
205
+ addExport(declarationName(node));
206
+ this.extractMembers(node, addExport);
207
+ }
208
+ break;
209
+ }
210
+ }
211
+ extractMembers(container, addExport) {
212
+ const body = childByType(container, 'class_body') ?? childByType(container, 'enum_class_body');
213
+ if (!body)
214
+ return;
215
+ const isInterface = hasTokenChild(container, 'interface');
216
+ body.namedChildren.forEach(member => {
217
+ if (member.type === 'function_declaration') {
218
+ if (isInterface || isExported(member))
219
+ addExport(functionName(member));
220
+ }
221
+ else if (member.type === 'property_declaration') {
222
+ if (isInterface || isExported(member))
223
+ addExport(this.propertyName(member));
224
+ }
225
+ else if (member.type === 'companion_object') {
226
+ // Companion members are reached via the enclosing class name → part of its API.
227
+ member.namedChildren
228
+ .filter(m => m.type === 'class_body')
229
+ .forEach(cb => cb.namedChildren
230
+ .filter(m => m.type === 'function_declaration' && isExported(m))
231
+ .forEach(m => addExport(functionName(m))));
232
+ }
233
+ });
234
+ }
235
+ propertyName(node) {
236
+ // property_declaration → variable_declaration → simple_identifier
237
+ const variable = childByType(node, 'variable_declaration');
238
+ return (variable ? childByType(variable, 'simple_identifier') : null)?.text;
239
+ }
240
+ }
241
+ // =============================================================================
242
+ // IMPORT EXTRACTOR
243
+ // =============================================================================
244
+ /**
245
+ * Kotlin/JVM standard-library prefixes that aren't useful as dependency edges.
246
+ * Note: `kotlinx.*` (coroutines, serialization, …) are separate external
247
+ * libraries, not the stdlib, so they are kept as real import edges.
248
+ */
249
+ function isKotlinStdLib(importPath) {
250
+ return (importPath.startsWith('kotlin.') ||
251
+ importPath.startsWith('java.') ||
252
+ importPath.startsWith('javax.'));
253
+ }
254
+ /**
255
+ * Kotlin import extractor.
256
+ *
257
+ * Handles `import a.b.C`, wildcard `import a.b.*`, and aliased
258
+ * `import a.b.C as D`. The grammar nests `import_header` nodes inside an
259
+ * `import_list`; the engine's import scan descends one level to reach them
260
+ * (see `collectImportNodes` in ast/symbols.ts). Standard-library imports
261
+ * (kotlin.*, kotlinx.*, java.*, javax.*) are filtered out.
262
+ */
263
+ export class KotlinImportExtractor {
264
+ importNodeTypes = ['import_header'];
265
+ extractImportPath(node) {
266
+ const path = this.getImportPath(node);
267
+ if (!path || isKotlinStdLib(path))
268
+ return null;
269
+ return path;
270
+ }
271
+ processImportSymbols(node) {
272
+ const path = this.getImportPath(node);
273
+ if (!path || isKotlinStdLib(path))
274
+ return null;
275
+ const parts = path.split('.');
276
+ const lastPart = parts[parts.length - 1];
277
+ if (lastPart === '*') {
278
+ const packagePath = parts.slice(0, -1).join('.');
279
+ const packageName = parts[parts.length - 2];
280
+ return { importPath: packagePath, symbols: packageName ? [packageName] : [] };
281
+ }
282
+ // `import a.b.C as D` — the imported symbol is the alias name when present.
283
+ // import_alias wraps the alias as a `type_identifier` (its text is `as D`).
284
+ const alias = childByType(node, 'import_alias');
285
+ const aliasName = alias ? childByType(alias, 'type_identifier')?.text : undefined;
286
+ return { importPath: path, symbols: [aliasName ?? lastPart] };
287
+ }
288
+ getImportPath(node) {
289
+ const identifier = childByType(node, 'identifier');
290
+ if (!identifier)
291
+ return null;
292
+ const hasWildcard = hasTokenChild(node, 'wildcard_import');
293
+ return hasWildcard ? `${identifier.text}.*` : identifier.text;
294
+ }
295
+ }
296
+ // =============================================================================
297
+ // SYMBOL EXTRACTOR
298
+ // =============================================================================
299
+ /**
300
+ * Kotlin symbol extractor.
301
+ *
302
+ * Handles `function_declaration`, `class_declaration` (class / interface / enum
303
+ * variants — distinguished by the keyword token), and `object_declaration`.
304
+ * `SymbolInfo.type` only has `function|method|class|interface`, so objects and
305
+ * enums map to `class` (keeping the keyword in the signature).
306
+ *
307
+ * Call sites: `call_expression` — `foo()` (simple_identifier) and `a.b.c()`
308
+ * (the member is the `simple_identifier` inside the trailing `navigation_suffix`).
309
+ */
310
+ export class KotlinSymbolExtractor {
311
+ symbolNodeTypes = ['function_declaration', 'class_declaration', 'object_declaration'];
312
+ extractSymbol(node, content, parentClass) {
313
+ switch (node.type) {
314
+ case 'function_declaration':
315
+ return this.extractFunctionInfo(node, content, parentClass);
316
+ case 'class_declaration':
317
+ return this.extractClassInfo(node);
318
+ case 'object_declaration':
319
+ return this.extractObjectInfo(node);
320
+ default:
321
+ return null;
322
+ }
323
+ }
324
+ extractCallSite(node) {
325
+ if (node.type !== 'call_expression')
326
+ return null;
327
+ const line = node.startPosition.row + 1;
328
+ const callee = node.namedChild(0);
329
+ if (!callee)
330
+ return null;
331
+ let name;
332
+ if (callee.type === 'simple_identifier') {
333
+ name = callee.text; // foo()
334
+ }
335
+ else if (callee.type === 'navigation_expression') {
336
+ // a.b.c() — the called member is the simple_identifier in the last navigation_suffix
337
+ const suffix = callee.namedChildren.filter(c => c.type === 'navigation_suffix').at(-1);
338
+ name = (suffix ? childByType(suffix, 'simple_identifier') : null)?.text;
339
+ }
340
+ if (!name)
341
+ return null;
342
+ return { symbol: name, line, key: `${name}:${line}` };
343
+ }
344
+ extractFunctionInfo(node, content, parentClass) {
345
+ const name = functionName(node);
346
+ if (!name)
347
+ return null;
348
+ return {
349
+ name,
350
+ type: parentClass ? 'method' : 'function',
351
+ startLine: node.startPosition.row + 1,
352
+ endLine: node.endPosition.row + 1,
353
+ parentClass,
354
+ signature: functionSignature(node, content),
355
+ parameters: functionParameters(node),
356
+ returnType: functionReturnType(node),
357
+ complexity: calculateComplexity(node),
358
+ };
359
+ }
360
+ extractClassInfo(node) {
361
+ const name = declarationName(node);
362
+ if (!name)
363
+ return null;
364
+ if (hasTokenChild(node, 'interface')) {
365
+ return this.makeSymbol(node, name, 'interface', `interface ${name}`);
366
+ }
367
+ if (hasTokenChild(node, 'enum')) {
368
+ return this.makeSymbol(node, name, 'class', `enum class ${name}`);
369
+ }
370
+ return this.makeSymbol(node, name, 'class', `class ${name}`);
371
+ }
372
+ extractObjectInfo(node) {
373
+ const name = declarationName(node);
374
+ if (!name)
375
+ return null;
376
+ return this.makeSymbol(node, name, 'class', `object ${name}`);
377
+ }
378
+ makeSymbol(node, name, type, signature) {
379
+ return {
380
+ name,
381
+ type,
382
+ startLine: node.startPosition.row + 1,
383
+ endLine: node.endPosition.row + 1,
384
+ signature,
385
+ };
386
+ }
387
+ }
388
+ // =============================================================================
389
+ // LANGUAGE DEFINITION
390
+ // =============================================================================
391
+ export const kotlinDefinition = {
392
+ id: 'kotlin',
393
+ extensions: ['kt'],
394
+ grammar: Kotlin,
395
+ traverser: new KotlinTraverser(),
396
+ exportExtractor: new KotlinExportExtractor(),
397
+ importExtractor: new KotlinImportExtractor(),
398
+ symbolExtractor: new KotlinSymbolExtractor(),
399
+ complexity: {
400
+ decisionPoints: [
401
+ 'if_expression',
402
+ 'when_entry',
403
+ 'for_statement',
404
+ 'while_statement',
405
+ 'do_while_statement',
406
+ 'catch_block',
407
+ 'elvis_expression',
408
+ 'conjunction_expression', // &&
409
+ 'disjunction_expression', // ||
410
+ ],
411
+ nestingTypes: [
412
+ 'if_expression',
413
+ 'when_expression',
414
+ 'for_statement',
415
+ 'while_statement',
416
+ 'do_while_statement',
417
+ 'catch_block',
418
+ 'lambda_literal',
419
+ 'anonymous_function',
420
+ ],
421
+ nonNestingTypes: ['when_entry', 'elvis_expression'],
422
+ lambdaTypes: ['lambda_literal', 'anonymous_function'],
423
+ operatorSymbols: new Set([
424
+ '+',
425
+ '-',
426
+ '*',
427
+ '/',
428
+ '%',
429
+ '==',
430
+ '!=',
431
+ '===',
432
+ '!==',
433
+ '<',
434
+ '>',
435
+ '<=',
436
+ '>=',
437
+ '=',
438
+ '+=',
439
+ '-=',
440
+ '*=',
441
+ '/=',
442
+ '%=',
443
+ '&&',
444
+ '||',
445
+ '!',
446
+ '?:',
447
+ '?.',
448
+ '.',
449
+ '::',
450
+ '->',
451
+ '..',
452
+ '(',
453
+ ')',
454
+ '[',
455
+ ']',
456
+ '{',
457
+ '}',
458
+ ]),
459
+ operatorKeywords: new Set([
460
+ 'if',
461
+ 'else',
462
+ 'when',
463
+ 'for',
464
+ 'while',
465
+ 'do',
466
+ 'return',
467
+ 'break',
468
+ 'continue',
469
+ 'throw',
470
+ 'try',
471
+ 'catch',
472
+ 'finally',
473
+ 'fun',
474
+ 'class',
475
+ 'object',
476
+ 'interface',
477
+ 'enum',
478
+ 'val',
479
+ 'var',
480
+ 'is',
481
+ 'as',
482
+ 'in',
483
+ 'by',
484
+ 'import',
485
+ 'package',
486
+ 'override',
487
+ 'abstract',
488
+ 'open',
489
+ 'sealed',
490
+ 'data',
491
+ 'suspend',
492
+ 'companion',
493
+ 'private',
494
+ 'internal',
495
+ 'protected',
496
+ 'public',
497
+ 'this',
498
+ 'super',
499
+ 'null',
500
+ ]),
501
+ },
502
+ symbols: {
503
+ callExpressionTypes: ['call_expression'],
504
+ },
505
+ };
506
+ //# sourceMappingURL=kotlin.js.map