codemap-kotlin 0.1.0__py3-none-any.whl
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.
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Kotlin indexer built on tree-sitter-kotlin.
|
|
2
|
+
|
|
3
|
+
The Kotlin grammar uses ``class_declaration`` for both ``class`` and
|
|
4
|
+
``interface``. The keyword token disambiguates. ``object_declaration``
|
|
5
|
+
is its own node type for singleton objects.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path, PurePosixPath
|
|
11
|
+
from typing import ClassVar
|
|
12
|
+
|
|
13
|
+
import tree_sitter
|
|
14
|
+
import tree_sitter_kotlin
|
|
15
|
+
|
|
16
|
+
from codemap.core.models import Diagnostic, Edge, IndexResult, Range, Symbol
|
|
17
|
+
from codemap.core.symbol import Descriptor, DescriptorKind, SymbolID
|
|
18
|
+
from codemap.indexers.base import IndexContext
|
|
19
|
+
|
|
20
|
+
SCHEME = "scip-kotlin"
|
|
21
|
+
LANG = "kotlin"
|
|
22
|
+
|
|
23
|
+
_KT_LANG = tree_sitter.Language(tree_sitter_kotlin.language())
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class KotlinIndexer:
|
|
27
|
+
name: ClassVar[str] = "kotlin"
|
|
28
|
+
version: ClassVar[str] = "0.1.0"
|
|
29
|
+
file_patterns: ClassVar[list[str]] = ["*.kt", "*.kts"]
|
|
30
|
+
languages: ClassVar[list[str]] = [LANG]
|
|
31
|
+
|
|
32
|
+
def supports(self, path: Path) -> bool:
|
|
33
|
+
return path.suffix in {".kt", ".kts"}
|
|
34
|
+
|
|
35
|
+
def index_file(
|
|
36
|
+
self,
|
|
37
|
+
path: Path,
|
|
38
|
+
source: bytes,
|
|
39
|
+
ctx: IndexContext,
|
|
40
|
+
) -> IndexResult:
|
|
41
|
+
try:
|
|
42
|
+
source.decode("utf-8")
|
|
43
|
+
except UnicodeDecodeError as exc:
|
|
44
|
+
return IndexResult(
|
|
45
|
+
diagnostics=[
|
|
46
|
+
Diagnostic(
|
|
47
|
+
severity="error",
|
|
48
|
+
file=ctx.relative_path,
|
|
49
|
+
code="KT002",
|
|
50
|
+
message=f"not valid UTF-8: {exc}",
|
|
51
|
+
producer=self.name,
|
|
52
|
+
)
|
|
53
|
+
]
|
|
54
|
+
)
|
|
55
|
+
parser = tree_sitter.Parser(_KT_LANG)
|
|
56
|
+
tree = parser.parse(source)
|
|
57
|
+
visitor = _Visitor(ctx.relative_path)
|
|
58
|
+
visitor.visit(tree.root_node)
|
|
59
|
+
diagnostics = list(visitor.diagnostics)
|
|
60
|
+
if tree.root_node.has_error:
|
|
61
|
+
diagnostics.append(
|
|
62
|
+
Diagnostic(
|
|
63
|
+
severity="warning",
|
|
64
|
+
file=ctx.relative_path,
|
|
65
|
+
range=Range(start_line=1, end_line=1),
|
|
66
|
+
code="KT001",
|
|
67
|
+
message="tree-sitter reported parse errors; symbols may be incomplete",
|
|
68
|
+
producer=self.name,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
return IndexResult(
|
|
72
|
+
symbols=visitor.symbols,
|
|
73
|
+
edges=visitor.edges,
|
|
74
|
+
diagnostics=diagnostics,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class _Visitor:
|
|
79
|
+
def __init__(self, relative_path: PurePosixPath) -> None:
|
|
80
|
+
self.relative_path = relative_path
|
|
81
|
+
self.symbols: list[Symbol] = []
|
|
82
|
+
self.edges: list[Edge] = []
|
|
83
|
+
self.diagnostics: list[Diagnostic] = []
|
|
84
|
+
self._type_stack: list[str] = []
|
|
85
|
+
self._package: str = ""
|
|
86
|
+
|
|
87
|
+
def visit(self, node: tree_sitter.Node) -> None:
|
|
88
|
+
kind = node.type
|
|
89
|
+
if kind == "package_header":
|
|
90
|
+
self._set_package(node)
|
|
91
|
+
return
|
|
92
|
+
if kind == "class_declaration":
|
|
93
|
+
self._visit_class_or_interface(node)
|
|
94
|
+
return
|
|
95
|
+
if kind == "object_declaration":
|
|
96
|
+
self._visit_object(node)
|
|
97
|
+
return
|
|
98
|
+
if kind == "function_declaration":
|
|
99
|
+
self._visit_function(node)
|
|
100
|
+
return
|
|
101
|
+
if kind == "property_declaration":
|
|
102
|
+
self._visit_property(node)
|
|
103
|
+
return
|
|
104
|
+
for child in node.children:
|
|
105
|
+
self.visit(child)
|
|
106
|
+
|
|
107
|
+
# ----------------------------------------------------- packages
|
|
108
|
+
|
|
109
|
+
def _set_package(self, node: tree_sitter.Node) -> None:
|
|
110
|
+
for child in node.children:
|
|
111
|
+
if child.type == "qualified_identifier":
|
|
112
|
+
self._package = _node_text(child)
|
|
113
|
+
return
|
|
114
|
+
if child.type == "identifier":
|
|
115
|
+
self._package = _node_text(child)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
# --------------------------------------------------- type-level
|
|
119
|
+
|
|
120
|
+
def _visit_class_or_interface(self, node: tree_sitter.Node) -> None:
|
|
121
|
+
kotlin_kind = "class"
|
|
122
|
+
for child in node.children:
|
|
123
|
+
if child.type == "interface":
|
|
124
|
+
kotlin_kind = "interface"
|
|
125
|
+
break
|
|
126
|
+
self._visit_type(node, kotlin_kind=kotlin_kind)
|
|
127
|
+
|
|
128
|
+
def _visit_object(self, node: tree_sitter.Node) -> None:
|
|
129
|
+
self._visit_type(node, kotlin_kind="object")
|
|
130
|
+
|
|
131
|
+
def _visit_type(self, node: tree_sitter.Node, *, kotlin_kind: str) -> None:
|
|
132
|
+
name = _first_identifier(node)
|
|
133
|
+
if name is None:
|
|
134
|
+
return
|
|
135
|
+
sid = self._make_id(name, kind=DescriptorKind.TYPE)
|
|
136
|
+
extra: dict[str, str] = {"kotlin_kind": kotlin_kind}
|
|
137
|
+
if self._package:
|
|
138
|
+
extra["package"] = self._package
|
|
139
|
+
self.symbols.append(
|
|
140
|
+
Symbol(
|
|
141
|
+
id=sid,
|
|
142
|
+
kind="class",
|
|
143
|
+
language=LANG,
|
|
144
|
+
file=self.relative_path,
|
|
145
|
+
range=_node_range(node),
|
|
146
|
+
extra=extra,
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
body = _find_class_body(node)
|
|
150
|
+
if body is None:
|
|
151
|
+
return
|
|
152
|
+
self._type_stack.append(name)
|
|
153
|
+
try:
|
|
154
|
+
for child in body.children:
|
|
155
|
+
self.visit(child)
|
|
156
|
+
finally:
|
|
157
|
+
self._type_stack.pop()
|
|
158
|
+
|
|
159
|
+
# ----------------------------------------------------- functions
|
|
160
|
+
|
|
161
|
+
def _visit_function(self, node: tree_sitter.Node) -> None:
|
|
162
|
+
name = _first_identifier(node)
|
|
163
|
+
if name is None:
|
|
164
|
+
return
|
|
165
|
+
kind: str = "method" if self._type_stack else "function"
|
|
166
|
+
sid = self._make_id(name, kind=DescriptorKind.METHOD)
|
|
167
|
+
self.symbols.append(
|
|
168
|
+
Symbol(
|
|
169
|
+
id=sid,
|
|
170
|
+
kind=kind, # type: ignore[arg-type]
|
|
171
|
+
language=LANG,
|
|
172
|
+
file=self.relative_path,
|
|
173
|
+
range=_node_range(node),
|
|
174
|
+
signature=f"fun {name}()",
|
|
175
|
+
)
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# ---------------------------------------------------- properties
|
|
179
|
+
|
|
180
|
+
def _visit_property(self, node: tree_sitter.Node) -> None:
|
|
181
|
+
# property_declaration > variable_declaration > identifier
|
|
182
|
+
for child in node.children:
|
|
183
|
+
if child.type == "variable_declaration":
|
|
184
|
+
for grand in child.children:
|
|
185
|
+
if grand.type == "identifier":
|
|
186
|
+
name = _node_text(grand)
|
|
187
|
+
if not name:
|
|
188
|
+
return
|
|
189
|
+
sym_kind: str = "field" if self._type_stack else "variable"
|
|
190
|
+
sid = self._make_id(name, kind=DescriptorKind.TERM)
|
|
191
|
+
self.symbols.append(
|
|
192
|
+
Symbol(
|
|
193
|
+
id=sid,
|
|
194
|
+
kind=sym_kind, # type: ignore[arg-type]
|
|
195
|
+
language=LANG,
|
|
196
|
+
file=self.relative_path,
|
|
197
|
+
range=_node_range(node),
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
# -------------------------------------------------------- helpers
|
|
203
|
+
|
|
204
|
+
def _make_id(self, name: str, *, kind: DescriptorKind) -> SymbolID:
|
|
205
|
+
descriptors = list(_path_namespaces(self.relative_path))
|
|
206
|
+
descriptors.extend(Descriptor(name=t, kind=DescriptorKind.TYPE) for t in self._type_stack)
|
|
207
|
+
descriptors.append(Descriptor(name=name, kind=kind))
|
|
208
|
+
return SymbolID(scheme=SCHEME, descriptors=tuple(descriptors))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
# Pure helpers
|
|
213
|
+
# ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _path_namespaces(path: PurePosixPath) -> list[Descriptor]:
|
|
217
|
+
return [Descriptor(name=part, kind=DescriptorKind.NAMESPACE) for part in path.parts]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _node_range(node: tree_sitter.Node) -> Range:
|
|
221
|
+
sr, sc = node.start_point
|
|
222
|
+
er, ec = node.end_point
|
|
223
|
+
return Range(
|
|
224
|
+
start_line=sr + 1,
|
|
225
|
+
start_col=sc,
|
|
226
|
+
end_line=max(er + 1, sr + 1),
|
|
227
|
+
end_col=ec,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _node_text(node: tree_sitter.Node) -> str:
|
|
232
|
+
return node.text.decode("utf-8") if node.text is not None else ""
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _first_identifier(node: tree_sitter.Node) -> str | None:
|
|
236
|
+
for child in node.children:
|
|
237
|
+
if child.type == "identifier":
|
|
238
|
+
return _node_text(child)
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _find_class_body(node: tree_sitter.Node) -> tree_sitter.Node | None:
|
|
243
|
+
for child in node.children:
|
|
244
|
+
if child.type == "class_body":
|
|
245
|
+
return child
|
|
246
|
+
return None
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codemap-kotlin
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Kotlin indexer plugin for CodeMap
|
|
5
|
+
Project-URL: Homepage, https://github.com/qxbyte/codemap
|
|
6
|
+
Author: CodeMap Contributors
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: codemap,indexer,kotlin,tree-sitter
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Kotlin
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Software Development
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: codemap-core<0.2,>=0.1.0
|
|
15
|
+
Requires-Dist: tree-sitter-kotlin>=1.1
|
|
16
|
+
Requires-Dist: tree-sitter>=0.25
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# codemap-kotlin
|
|
22
|
+
|
|
23
|
+
> A Kotlin indexer for [CodeMap](https://github.com/qxbyte/codemap),
|
|
24
|
+
> shipped as an independent PyPI package.
|
|
25
|
+
|
|
26
|
+
## What it captures
|
|
27
|
+
|
|
28
|
+
Backed by `tree-sitter-kotlin`:
|
|
29
|
+
|
|
30
|
+
| AST node | Symbol kind |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `class_declaration` (keyword `class`) | `class` (with `extra.kotlin_kind=class`) |
|
|
33
|
+
| `class_declaration` (keyword `interface`) | `class` (with `extra.kotlin_kind=interface`) |
|
|
34
|
+
| `object_declaration` | `class` (with `extra.kotlin_kind=object`) |
|
|
35
|
+
| `function_declaration` (free) | `function` |
|
|
36
|
+
| `function_declaration` (inside type) | `method` |
|
|
37
|
+
| `property_declaration` (top-level) | `variable` |
|
|
38
|
+
| `property_declaration` (inside type) | `field` |
|
|
39
|
+
|
|
40
|
+
`package_header` is captured as `extra.package` on type symbols.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install codemap-kotlin
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## SymbolID encoding
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
scip-kotlin . . . src/main/kotlin/User.kt/User#hello().
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Limits
|
|
55
|
+
|
|
56
|
+
* Companion object members are not attached to the enclosing class.
|
|
57
|
+
* Generic-parameter descriptors are dropped.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
codemap_kotlin/__init__.py,sha256=LFXuLyq1fQ3H9mAsXLNa70qKkazOIiG5EbkhHVgdFSY,178
|
|
2
|
+
codemap_kotlin/indexer.py,sha256=jljcukt5DTz1krucKAD_OO-CEb33bpklr3vYmEyVY6A,8374
|
|
3
|
+
codemap_kotlin-0.1.0.dist-info/METADATA,sha256=kxyWC7edGBoQBs-NLjUlfjFs9L0DLzWdMjbJFNWbKKw,1680
|
|
4
|
+
codemap_kotlin-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
5
|
+
codemap_kotlin-0.1.0.dist-info/entry_points.txt,sha256=SSa4uWNnC79Hl8rOIeO53pnT7TrejeJSoJ4gYgXoLTM,57
|
|
6
|
+
codemap_kotlin-0.1.0.dist-info/RECORD,,
|