code-intelligence 1.0.0__py3-none-win_amd64.whl → 1.2.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.
@@ -1,19 +1,16 @@
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
1
  from .symdb import (
9
2
  Language,
10
3
  SymbolType,
11
4
  Symbol,
12
5
  Location,
13
- SymbolDatabase,
6
+ ImmideateSymbolDatabase,
7
+ LazySymbolDatabase,
14
8
  )
15
9
 
16
- __version__ = "1.0.0"
10
+ # Backward compatibility alias
11
+ SymbolDatabase = ImmideateSymbolDatabase
12
+
13
+ __version__ = "1.2.0"
17
14
  __version_info__ = tuple(int(i) for i in __version__.split('.'))
18
15
 
19
16
  __all__ = [
@@ -21,5 +18,7 @@ __all__ = [
21
18
  "SymbolType",
22
19
  "Symbol",
23
20
  "Location",
21
+ "ImmideateSymbolDatabase",
22
+ "LazySymbolDatabase",
24
23
  "SymbolDatabase",
25
24
  ]
Binary file
@@ -3,6 +3,7 @@ import ctypes
3
3
  import platform
4
4
  from enum import IntEnum
5
5
  from pathlib import Path
6
+ from dataclasses import dataclass, field
6
7
 
7
8
 
8
9
  class Language(IntEnum):
@@ -48,29 +49,29 @@ class SymbolType(IntEnum):
48
49
 
49
50
  Path_Like = str | Path
50
51
 
52
+ @dataclass(slots=True)
51
53
  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):
54
+ """Source code span."""
55
+ path: str = ""
56
+ line: int = 0
57
+ column: int = 0
58
+ end_line: int = 0
59
+ end_column: int = 0
60
+
61
+ def __repr__(self) -> str:
60
62
  return f"Location({self.path}:{self.line}:{self.column})"
61
63
 
64
+ @dataclass(slots=True)
62
65
  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):
66
+ """Language-agnostic symbol record."""
67
+ name: str = ""
68
+ symbol_type: SymbolType = SymbolType.UNKNOWN
69
+ definition: Location = field(default_factory=Location)
70
+ parent: str | None = None
71
+ signature: str | None = None
72
+ documentation: str | None = None
73
+
74
+ def __repr__(self) -> str:
74
75
  return f"Symbol({self.name}, {self.symbol_type.name}, {self.definition})"
75
76
 
76
77
  # Convenience properties for backward compatibility with TASK.md API
@@ -185,9 +186,69 @@ _lib.symbol_db_get_symbol.restype = ctypes.c_bool
185
186
  _lib.symbol_array_free.argtypes = [ctypes.c_void_p]
186
187
  _lib.symbol_array_free.restype = None
187
188
 
189
+ _lib.ci_flush_coverage.argtypes = []
190
+ _lib.ci_flush_coverage.restype = None
191
+
192
+ _lib.symbol_db_clear.argtypes = [ctypes.c_void_p, ctypes.c_int]
193
+ _lib.symbol_db_clear.restype = None
194
+
195
+ def _flush_coverage():
196
+ try:
197
+ _lib.ci_flush_coverage()
198
+ except Exception:
199
+ pass
188
200
 
189
201
  # Helper conversions
190
202
 
203
+ class CStringView:
204
+ """A view into a C string that delays decoding until needed."""
205
+ __slots__ = ('_ptr', '_decoded')
206
+
207
+ def __init__(self, c_char_p):
208
+ self._ptr = c_char_p
209
+ self._decoded = None
210
+
211
+ def __str__(self):
212
+ if self._decoded is None:
213
+ if self._ptr:
214
+ self._decoded = self._ptr.decode("utf-8", errors="replace")
215
+ else:
216
+ self._decoded = ""
217
+ return self._decoded
218
+
219
+ def __repr__(self):
220
+ return f"CStringView({str(self)!r})"
221
+
222
+ def __bool__(self):
223
+ return bool(self._ptr)
224
+
225
+ def __eq__(self, other):
226
+ if isinstance(other, CStringView):
227
+ return str(self) == str(other)
228
+ elif isinstance(other, str):
229
+ return str(self) == other
230
+ return False
231
+
232
+
233
+ def _to_py_symbol_lazy(c_symbol: _Symbol) -> Symbol:
234
+ """Convert C symbol to Python symbol with lazy string decoding."""
235
+ definition = Location(
236
+ path=str(CStringView(c_symbol.definition.path)), # Decode immediately for file paths (needed for path operations)
237
+ line=c_symbol.definition.line,
238
+ column=c_symbol.definition.column,
239
+ end_line=c_symbol.definition.end_line,
240
+ end_column=c_symbol.definition.end_column,
241
+ )
242
+ return Symbol(
243
+ name=str(CStringView(c_symbol.name)), # Most accessed field, decode immediately
244
+ symbol_type=SymbolType(c_symbol.symbol_type),
245
+ definition=definition,
246
+ parent=str(CStringView(c_symbol.parent)) if c_symbol.parent else None,
247
+ signature=CStringView(c_symbol.signature) if c_symbol.signature else None, # Keep as view
248
+ documentation=CStringView(c_symbol.documentation) if c_symbol.documentation else None, # Keep as view
249
+ )
250
+
251
+
191
252
  def _to_py_symbol(c_symbol: _Symbol) -> Symbol:
192
253
  definition = Location(
193
254
  path=c_symbol.definition.path.decode("utf-8", errors="replace") if c_symbol.definition.path else "",
@@ -206,6 +267,48 @@ def _to_py_symbol(c_symbol: _Symbol) -> Symbol:
206
267
  )
207
268
 
208
269
 
270
+ class SymbolArrayView:
271
+ """A view into a C symbol array that manages the C memory and provides lazy access."""
272
+
273
+ def __init__(self, ptr: ctypes.POINTER(_Symbol), count: int):
274
+ self._ptr = ptr
275
+ self._count = count
276
+ self._symbols = None # Lazy converted symbols
277
+
278
+ def __len__(self):
279
+ return self._count
280
+
281
+ def __getitem__(self, index):
282
+ if isinstance(index, slice):
283
+ # Handle slice objects
284
+ start, stop, step = index.indices(self._count)
285
+ return [self[i] for i in range(start, stop, step)]
286
+
287
+ if index < 0:
288
+ index = self._count + index
289
+ if index < 0 or index >= self._count:
290
+ raise IndexError("Symbol array index out of range")
291
+
292
+ # Lazy conversion - convert only accessed symbols
293
+ if self._symbols is None:
294
+ self._symbols = [None] * self._count
295
+
296
+ if self._symbols[index] is None:
297
+ self._symbols[index] = _to_py_symbol_lazy(self._ptr[index])
298
+
299
+ return self._symbols[index]
300
+
301
+ def __iter__(self):
302
+ for i in range(self._count):
303
+ yield self[i]
304
+
305
+ def __del__(self):
306
+ # Free the C array when Python object is garbage collected
307
+ if hasattr(self, '_ptr') and self._ptr:
308
+ _lib.symbol_array_free(self._ptr)
309
+ self._ptr = None
310
+
311
+
209
312
  def _convert_symbol_array(ptr: ctypes.POINTER(_Symbol), count: int) -> list[Symbol]:
210
313
  if not ptr or count <= 0:
211
314
  return []
@@ -216,9 +319,21 @@ def _convert_symbol_array(ptr: ctypes.POINTER(_Symbol), count: int) -> list[Symb
216
319
  _lib.symbol_array_free(ptr)
217
320
 
218
321
 
219
- class SymbolDatabase:
220
- """Main class for code intelligence operations."""
322
+ def _convert_symbol_array_lazy(ptr: ctypes.POINTER(_Symbol), count: int) -> SymbolArrayView:
323
+ """Convert C symbol array to a lazy Python view without immediate copying."""
324
+ if not ptr or count <= 0:
325
+ if ptr:
326
+ _lib.symbol_array_free(ptr) # Free empty array
327
+ return SymbolArrayView(None, 0)
328
+
329
+ return SymbolArrayView(ptr, count)
330
+
331
+
332
+
333
+ # --- Backend Implementations ---
221
334
 
335
+ class _BaseSymbolDatabase:
336
+ """Base class for code intelligence operations (not for direct use)."""
222
337
  def __init__(self):
223
338
  self._handle = _lib.symbol_db_create()
224
339
  if not self._handle:
@@ -229,6 +344,19 @@ class SymbolDatabase:
229
344
  _lib.symbol_db_destroy(self._handle)
230
345
  self._handle = None
231
346
 
347
+ def clear(self, *, reuse_memory: bool = True):
348
+ """Reset the database state while keeping the same Python object.
349
+
350
+ Frees the current native database state and reinitializes it. Any
351
+ previously scanned files, symbols, and project root are discarded.
352
+
353
+ Args:
354
+ reuse_memory: If True (default), keep allocated arena memory for reuse.
355
+ If False, free all memory and start fresh.
356
+ """
357
+ if self._handle:
358
+ _lib.symbol_db_clear(self._handle, ctypes.c_int(1 if reuse_memory else 0))
359
+
232
360
  def set_project_root(self, path: Path_Like):
233
361
  _lib.symbol_db_set_project_root(self._handle, os.fsencode(path))
234
362
 
@@ -251,9 +379,6 @@ class SymbolDatabase:
251
379
  )
252
380
 
253
381
  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
382
  all_ok = True
258
383
  for lang, files in files_by_language.items():
259
384
  if not files:
@@ -276,6 +401,20 @@ class SymbolDatabase:
276
401
  int(language),
277
402
  )
278
403
 
404
+ def get_symbol(self, path: Path_Like, line: int, column: int) -> Symbol | None:
405
+ out = _Symbol()
406
+ ok = _lib.symbol_db_get_symbol(
407
+ self._handle,
408
+ os.fsencode(path),
409
+ ctypes.c_uint32(line),
410
+ ctypes.c_uint32(column),
411
+ ctypes.byref(out),
412
+ )
413
+ return _to_py_symbol(out) if ok else None
414
+
415
+
416
+ class ImmideateSymbolDatabase(_BaseSymbolDatabase):
417
+ """Symbol database using copying (eager) backend."""
279
418
  def find_definitions(self, symbol_name: str) -> list[Symbol]:
280
419
  count = ctypes.c_uint32(0)
281
420
  ptr = _lib.symbol_db_find_definitions(
@@ -285,7 +424,6 @@ class SymbolDatabase:
285
424
  )
286
425
  return _convert_symbol_array(ptr, int(count.value))
287
426
 
288
- # Alias for backward compatibility with TASK.md API
289
427
  def find_symbols(self, symbol_name: str) -> list[Symbol]:
290
428
  return self.find_definitions(symbol_name)
291
429
 
@@ -303,14 +441,32 @@ class SymbolDatabase:
303
441
  ptr = _lib.symbol_db_get_all_symbols(self._handle, ctypes.byref(count))
304
442
  return _convert_symbol_array(ptr, int(count.value))
305
443
 
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(
444
+
445
+ class LazySymbolDatabase(_BaseSymbolDatabase):
446
+ """Symbol database using lazy backend."""
447
+ def find_definitions(self, symbol_name: str) -> SymbolArrayView:
448
+ count = ctypes.c_uint32(0)
449
+ ptr = _lib.symbol_db_find_definitions(
310
450
  self._handle,
311
- os.fsencode(path),
312
- ctypes.c_uint32(line),
313
- ctypes.c_uint32(column),
314
- ctypes.byref(out),
451
+ symbol_name.encode("utf-8"),
452
+ ctypes.byref(count),
315
453
  )
316
- return _to_py_symbol(out) if ok else None
454
+ return _convert_symbol_array_lazy(ptr, int(count.value))
455
+
456
+ def find_symbols(self, symbol_name: str) -> SymbolArrayView:
457
+ return self.find_definitions(symbol_name)
458
+
459
+ def find_references(self, symbol_name: str) -> SymbolArrayView:
460
+ count = ctypes.c_uint32(0)
461
+ ptr = _lib.symbol_db_find_references(
462
+ self._handle,
463
+ symbol_name.encode("utf-8"),
464
+ ctypes.byref(count),
465
+ )
466
+ return _convert_symbol_array_lazy(ptr, int(count.value))
467
+
468
+ def get_all_symbols(self) -> SymbolArrayView:
469
+ count = ctypes.c_uint32(0)
470
+ ptr = _lib.symbol_db_get_all_symbols(self._handle, ctypes.byref(count))
471
+ return _convert_symbol_array_lazy(ptr, int(count.value))
472
+
@@ -1,4 +1,4 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code_intelligence
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Code intelligence library
@@ -0,0 +1,6 @@
1
+ code_intelligence-1.2.0.dist-info/METADATA,sha256=hKTZsfBd4PDEY2n4goorqCM_HwqJ8figLIksMS0xueE,100
2
+ code_intelligence-1.2.0.dist-info/RECORD,,
3
+ code_intelligence-1.2.0.dist-info/WHEEL,sha256=aD7J6E7Slys9i0J65v6Ehfi0DHYa5rImRhUDp62Iwh0,98
4
+ code_intelligence/__init__.py,sha256=gruQfSmjho6PUbWU7ZW7OZP2KZ1fsvx5Z-9vAwfnM1g,475
5
+ code_intelligence/_code_intelligence.dll,sha256=LNP8bT3rTDsLKrG-1OHbTIdJN7-VNd3MRi0PI2HzHIQ,6474240
6
+ code_intelligence/symdb.py,sha256=r8fkV4YeKKM4Tajr82L389g7Mv4ftMt_K_I7y8LxEZs,15850
@@ -1,6 +0,0 @@
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