code-intelligence 1.0.0__py3-none-win_amd64.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,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Code Intelligence Library
|
|
3
|
+
|
|
4
|
+
A lightweight, fast Tree-sitter based code intelligence library.
|
|
5
|
+
Provides symbol database functionality for code analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .symdb import (
|
|
9
|
+
Language,
|
|
10
|
+
SymbolType,
|
|
11
|
+
Symbol,
|
|
12
|
+
Location,
|
|
13
|
+
SymbolDatabase,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__version__ = "1.0.0"
|
|
17
|
+
__version_info__ = tuple(int(i) for i in __version__.split('.'))
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Language",
|
|
21
|
+
"SymbolType",
|
|
22
|
+
"Symbol",
|
|
23
|
+
"Location",
|
|
24
|
+
"SymbolDatabase",
|
|
25
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import ctypes
|
|
3
|
+
import platform
|
|
4
|
+
from enum import IntEnum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Language(IntEnum):
|
|
9
|
+
# UNKNOWN = 0
|
|
10
|
+
PYTHON = 1
|
|
11
|
+
JAVASCRIPT = 2
|
|
12
|
+
TYPESCRIPT = 3
|
|
13
|
+
CPP = 4
|
|
14
|
+
C = 5
|
|
15
|
+
JAVA = 6
|
|
16
|
+
RUST = 7
|
|
17
|
+
GO = 8
|
|
18
|
+
PHP = 9
|
|
19
|
+
RUBY = 10
|
|
20
|
+
CSHARP = 11
|
|
21
|
+
KOTLIN = 12
|
|
22
|
+
SWIFT = 13
|
|
23
|
+
SCALA = 14
|
|
24
|
+
DART = 15
|
|
25
|
+
LUA = 16
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SymbolType(IntEnum):
|
|
29
|
+
UNKNOWN = 0
|
|
30
|
+
CLASS = 1
|
|
31
|
+
FUNCTION = 2
|
|
32
|
+
METHOD = 3
|
|
33
|
+
VARIABLE = 4
|
|
34
|
+
MODULE = 5
|
|
35
|
+
IMPORT = 6
|
|
36
|
+
FROM_IMPORT = 7
|
|
37
|
+
CONSTANT = 8
|
|
38
|
+
PROPERTY = 9
|
|
39
|
+
INTERFACE = 10
|
|
40
|
+
STRUCT = 11
|
|
41
|
+
ENUM = 12
|
|
42
|
+
NAMESPACE = 13
|
|
43
|
+
PACKAGE = 14
|
|
44
|
+
FIELD = 15
|
|
45
|
+
PARAMETER = 16
|
|
46
|
+
LOCAL = 17
|
|
47
|
+
TYPE = 18
|
|
48
|
+
|
|
49
|
+
Path_Like = str | Path
|
|
50
|
+
|
|
51
|
+
class Location:
|
|
52
|
+
def __init__(self, path: str, line: int, column: int, end_line: int = 0, end_column: int = 0):
|
|
53
|
+
self.path = path
|
|
54
|
+
self.line = line
|
|
55
|
+
self.column = column
|
|
56
|
+
self.end_line = end_line
|
|
57
|
+
self.end_column = end_column
|
|
58
|
+
|
|
59
|
+
def __repr__(self):
|
|
60
|
+
return f"Location({self.path}:{self.line}:{self.column})"
|
|
61
|
+
|
|
62
|
+
class Symbol:
|
|
63
|
+
def __init__(self, name: str, symbol_type: SymbolType, definition: Location,
|
|
64
|
+
parent: str | None = None, signature: str | None = None,
|
|
65
|
+
documentation: str | None = None):
|
|
66
|
+
self.name = name
|
|
67
|
+
self.symbol_type = symbol_type
|
|
68
|
+
self.definition = definition
|
|
69
|
+
self.parent = parent
|
|
70
|
+
self.signature = signature
|
|
71
|
+
self.documentation = documentation
|
|
72
|
+
|
|
73
|
+
def __repr__(self):
|
|
74
|
+
return f"Symbol({self.name}, {self.symbol_type.name}, {self.definition})"
|
|
75
|
+
|
|
76
|
+
# Convenience properties for backward compatibility with TASK.md API
|
|
77
|
+
@property
|
|
78
|
+
def filename(self) -> str:
|
|
79
|
+
return self.definition.path
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def line(self) -> int:
|
|
83
|
+
return self.definition.line
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def column(self) -> int:
|
|
87
|
+
return self.definition.column
|
|
88
|
+
|
|
89
|
+
# Keep location as alias for backward compatibility
|
|
90
|
+
@property
|
|
91
|
+
def location(self) -> "Location":
|
|
92
|
+
return self.definition
|
|
93
|
+
|
|
94
|
+
# --- Private ctypes Implementation ---
|
|
95
|
+
|
|
96
|
+
# Matches ci_location_t (field name 'path')
|
|
97
|
+
class _Location(ctypes.Structure):
|
|
98
|
+
_fields_ = [
|
|
99
|
+
("path", ctypes.c_char_p),
|
|
100
|
+
("line", ctypes.c_uint32),
|
|
101
|
+
("column", ctypes.c_uint32),
|
|
102
|
+
("end_line", ctypes.c_uint32),
|
|
103
|
+
("end_column", ctypes.c_uint32),
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
# Matches ci_symbol_t (field name 'definition')
|
|
107
|
+
class _Symbol(ctypes.Structure):
|
|
108
|
+
_fields_ = [
|
|
109
|
+
("name", ctypes.c_char_p),
|
|
110
|
+
("symbol_type", ctypes.c_int),
|
|
111
|
+
("definition", _Location),
|
|
112
|
+
("parent", ctypes.c_char_p),
|
|
113
|
+
("signature", ctypes.c_char_p),
|
|
114
|
+
("documentation", ctypes.c_char_p),
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
CURRENT_OS = platform.system().lower()
|
|
119
|
+
|
|
120
|
+
if CURRENT_OS == "darwin":
|
|
121
|
+
LIB_EXT = ".dylib"
|
|
122
|
+
elif CURRENT_OS == "windows":
|
|
123
|
+
LIB_EXT = ".dll"
|
|
124
|
+
else:
|
|
125
|
+
LIB_EXT = ".so"
|
|
126
|
+
|
|
127
|
+
def _load_library(name: str):
|
|
128
|
+
# Try module directory first (where nob.py now puts the library)
|
|
129
|
+
lib_path = Path(__file__).parent / f"{name}{LIB_EXT}"
|
|
130
|
+
if not lib_path.exists():
|
|
131
|
+
# Fallback to build directory for backward compatibility
|
|
132
|
+
lib_path = Path(__file__).parent.parent / "build" / f"{name}{LIB_EXT}"
|
|
133
|
+
if not lib_path.exists():
|
|
134
|
+
# Final fallback to project root
|
|
135
|
+
lib_path = Path(__file__).parent.parent / f"{name}{LIB_EXT}"
|
|
136
|
+
if not lib_path.exists():
|
|
137
|
+
raise ImportError(
|
|
138
|
+
f"Cannot find compiled library at {lib_path}. Please build it first using 'python nob.py lib'."
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
lib = ctypes.CDLL(os.fsdecode(lib_path))
|
|
142
|
+
return lib
|
|
143
|
+
|
|
144
|
+
_lib = _load_library("_code_intelligence")
|
|
145
|
+
|
|
146
|
+
# C function signatures (match code_intelligence.c)
|
|
147
|
+
_lib.symbol_db_create.argtypes = []
|
|
148
|
+
_lib.symbol_db_create.restype = ctypes.c_void_p
|
|
149
|
+
|
|
150
|
+
_lib.symbol_db_destroy.argtypes = [ctypes.c_void_p]
|
|
151
|
+
_lib.symbol_db_destroy.restype = None
|
|
152
|
+
|
|
153
|
+
_lib.symbol_db_set_project_root.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
|
154
|
+
_lib.symbol_db_set_project_root.restype = None
|
|
155
|
+
|
|
156
|
+
_lib.symbol_db_scan_file.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int]
|
|
157
|
+
_lib.symbol_db_scan_file.restype = ctypes.c_bool
|
|
158
|
+
|
|
159
|
+
# Batch scan
|
|
160
|
+
_lib.symbol_db_scan_files.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p), ctypes.c_uint32, ctypes.c_int]
|
|
161
|
+
_lib.symbol_db_scan_files.restype = ctypes.c_bool
|
|
162
|
+
|
|
163
|
+
_lib.symbol_db_remove_file.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
|
164
|
+
_lib.symbol_db_remove_file.restype = ctypes.c_bool
|
|
165
|
+
|
|
166
|
+
# Update file (re-scan and replace)
|
|
167
|
+
_lib.symbol_db_update_file.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int]
|
|
168
|
+
_lib.symbol_db_update_file.restype = ctypes.c_bool
|
|
169
|
+
|
|
170
|
+
# Arrays with out_count
|
|
171
|
+
_lib.symbol_db_find_definitions.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32)]
|
|
172
|
+
_lib.symbol_db_find_definitions.restype = ctypes.POINTER(_Symbol)
|
|
173
|
+
|
|
174
|
+
_lib.symbol_db_find_references.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint32)]
|
|
175
|
+
_lib.symbol_db_find_references.restype = ctypes.POINTER(_Symbol)
|
|
176
|
+
|
|
177
|
+
_lib.symbol_db_get_all_symbols.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint32)]
|
|
178
|
+
_lib.symbol_db_get_all_symbols.restype = ctypes.POINTER(_Symbol)
|
|
179
|
+
|
|
180
|
+
# bool return + out _Symbol
|
|
181
|
+
_lib.symbol_db_get_symbol.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32, ctypes.c_uint32, ctypes.POINTER(_Symbol)]
|
|
182
|
+
_lib.symbol_db_get_symbol.restype = ctypes.c_bool
|
|
183
|
+
|
|
184
|
+
# Free for arrays (void*)
|
|
185
|
+
_lib.symbol_array_free.argtypes = [ctypes.c_void_p]
|
|
186
|
+
_lib.symbol_array_free.restype = None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# Helper conversions
|
|
190
|
+
|
|
191
|
+
def _to_py_symbol(c_symbol: _Symbol) -> Symbol:
|
|
192
|
+
definition = Location(
|
|
193
|
+
path=c_symbol.definition.path.decode("utf-8", errors="replace") if c_symbol.definition.path else "",
|
|
194
|
+
line=c_symbol.definition.line,
|
|
195
|
+
column=c_symbol.definition.column,
|
|
196
|
+
end_line=c_symbol.definition.end_line,
|
|
197
|
+
end_column=c_symbol.definition.end_column,
|
|
198
|
+
)
|
|
199
|
+
return Symbol(
|
|
200
|
+
name=c_symbol.name.decode("utf-8", errors="replace") if c_symbol.name else "",
|
|
201
|
+
symbol_type=SymbolType(c_symbol.symbol_type),
|
|
202
|
+
definition=definition,
|
|
203
|
+
parent=c_symbol.parent.decode("utf-8", errors="replace") if c_symbol.parent else None,
|
|
204
|
+
signature=c_symbol.signature.decode("utf-8", errors="replace") if c_symbol.signature else None,
|
|
205
|
+
documentation=c_symbol.documentation.decode("utf-8", errors="replace") if c_symbol.documentation else None,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _convert_symbol_array(ptr: ctypes.POINTER(_Symbol), count: int) -> list[Symbol]:
|
|
210
|
+
if not ptr or count <= 0:
|
|
211
|
+
return []
|
|
212
|
+
try:
|
|
213
|
+
return [_to_py_symbol(ptr[i]) for i in range(count)]
|
|
214
|
+
finally:
|
|
215
|
+
# Free the shallow-copy array buffer
|
|
216
|
+
_lib.symbol_array_free(ptr)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class SymbolDatabase:
|
|
220
|
+
"""Main class for code intelligence operations."""
|
|
221
|
+
|
|
222
|
+
def __init__(self):
|
|
223
|
+
self._handle = _lib.symbol_db_create()
|
|
224
|
+
if not self._handle:
|
|
225
|
+
raise RuntimeError("Failed to create symbol database")
|
|
226
|
+
|
|
227
|
+
def __del__(self):
|
|
228
|
+
if hasattr(self, "_handle") and self._handle:
|
|
229
|
+
_lib.symbol_db_destroy(self._handle)
|
|
230
|
+
self._handle = None
|
|
231
|
+
|
|
232
|
+
def set_project_root(self, path: Path_Like):
|
|
233
|
+
_lib.symbol_db_set_project_root(self._handle, os.fsencode(path))
|
|
234
|
+
|
|
235
|
+
def scan_file(self, path: Path_Like, language: Language) -> bool:
|
|
236
|
+
return _lib.symbol_db_scan_file(
|
|
237
|
+
self._handle,
|
|
238
|
+
os.fsencode(path),
|
|
239
|
+
int(language),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
def scan_files(self, paths: list[Path_Like], language: Language) -> bool:
|
|
243
|
+
arr = (ctypes.c_char_p * len(paths))()
|
|
244
|
+
for i, p in enumerate(paths):
|
|
245
|
+
arr[i] = os.fsencode(p)
|
|
246
|
+
return _lib.symbol_db_scan_files(
|
|
247
|
+
self._handle,
|
|
248
|
+
arr,
|
|
249
|
+
ctypes.c_uint32(len(paths)),
|
|
250
|
+
int(language),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
def init_project(self, files_by_language: dict[Language, list[Path_Like]]) -> bool:
|
|
254
|
+
"""Batch scan once per language, then rely on update_file for changes.
|
|
255
|
+
Returns True if all scans succeed.
|
|
256
|
+
"""
|
|
257
|
+
all_ok = True
|
|
258
|
+
for lang, files in files_by_language.items():
|
|
259
|
+
if not files:
|
|
260
|
+
continue
|
|
261
|
+
ok = self.scan_files(files, lang)
|
|
262
|
+
if not ok:
|
|
263
|
+
all_ok = False
|
|
264
|
+
return all_ok
|
|
265
|
+
|
|
266
|
+
def remove_file(self, path: Path_Like) -> bool:
|
|
267
|
+
return _lib.symbol_db_remove_file(
|
|
268
|
+
self._handle,
|
|
269
|
+
os.fsencode(path),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
def update_file(self, path: Path_Like, language: Language) -> bool:
|
|
273
|
+
return _lib.symbol_db_update_file(
|
|
274
|
+
self._handle,
|
|
275
|
+
os.fsencode(path),
|
|
276
|
+
int(language),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
def find_definitions(self, symbol_name: str) -> list[Symbol]:
|
|
280
|
+
count = ctypes.c_uint32(0)
|
|
281
|
+
ptr = _lib.symbol_db_find_definitions(
|
|
282
|
+
self._handle,
|
|
283
|
+
symbol_name.encode("utf-8"),
|
|
284
|
+
ctypes.byref(count),
|
|
285
|
+
)
|
|
286
|
+
return _convert_symbol_array(ptr, int(count.value))
|
|
287
|
+
|
|
288
|
+
# Alias for backward compatibility with TASK.md API
|
|
289
|
+
def find_symbols(self, symbol_name: str) -> list[Symbol]:
|
|
290
|
+
return self.find_definitions(symbol_name)
|
|
291
|
+
|
|
292
|
+
def find_references(self, symbol_name: str) -> list[Symbol]:
|
|
293
|
+
count = ctypes.c_uint32(0)
|
|
294
|
+
ptr = _lib.symbol_db_find_references(
|
|
295
|
+
self._handle,
|
|
296
|
+
symbol_name.encode("utf-8"),
|
|
297
|
+
ctypes.byref(count),
|
|
298
|
+
)
|
|
299
|
+
return _convert_symbol_array(ptr, int(count.value))
|
|
300
|
+
|
|
301
|
+
def get_all_symbols(self) -> list[Symbol]:
|
|
302
|
+
count = ctypes.c_uint32(0)
|
|
303
|
+
ptr = _lib.symbol_db_get_all_symbols(self._handle, ctypes.byref(count))
|
|
304
|
+
return _convert_symbol_array(ptr, int(count.value))
|
|
305
|
+
|
|
306
|
+
def get_symbol(self, path: Path_Like, line: int, column: int) -> Symbol | None:
|
|
307
|
+
"""Get symbol at location, returning the resolved definition symbol."""
|
|
308
|
+
out = _Symbol()
|
|
309
|
+
ok = _lib.symbol_db_get_symbol(
|
|
310
|
+
self._handle,
|
|
311
|
+
os.fsencode(path),
|
|
312
|
+
ctypes.c_uint32(line),
|
|
313
|
+
ctypes.c_uint32(column),
|
|
314
|
+
ctypes.byref(out),
|
|
315
|
+
)
|
|
316
|
+
return _to_py_symbol(out) if ok else None
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
code_intelligence-1.0.0.dist-info/METADATA,sha256=-kVPTwVAKXJdc-nkZrfnQroG87bxbr6gPuMC6EnAdPg,100
|
|
2
|
+
code_intelligence-1.0.0.dist-info/RECORD,,
|
|
3
|
+
code_intelligence-1.0.0.dist-info/WHEEL,sha256=aD7J6E7Slys9i0J65v6Ehfi0DHYa5rImRhUDp62Iwh0,98
|
|
4
|
+
code_intelligence/__init__.py,sha256=24ugVb05tJOwKsb8e8lfsXmWVKLNBx_EgigOj1WmEHM,472
|
|
5
|
+
code_intelligence/_code_intelligence.dll,sha256=Gl-xAufdN5dkwutuasXcWbqbfac1H0bElFp1VRQf1kk,6471680
|
|
6
|
+
code_intelligence/symdb.py,sha256=2DFljllwCiJbgnnFgawzN-18AB0LFXJfA2fVADuFExQ,10482
|