code-intelligence 4.8.2__py3-none-macosx_11_0_x86_64.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,27 @@
|
|
|
1
|
+
from .goober import (
|
|
2
|
+
Language,
|
|
3
|
+
Definition_Link,
|
|
4
|
+
scan_file,
|
|
5
|
+
find_symbols_in_text,
|
|
6
|
+
init,
|
|
7
|
+
stop,
|
|
8
|
+
clear,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# PROUD - Bump when you are proud of the release.
|
|
12
|
+
# DEFAULT - Just normal/okay releases.
|
|
13
|
+
# SHAME - Bump when fixing things too embarrassing to admit.
|
|
14
|
+
# PROUD.DEFAULT.SHAME
|
|
15
|
+
# \ | /
|
|
16
|
+
__version__ = "4.8.2"
|
|
17
|
+
__version_info__ = tuple(int(i) for i in __version__.split('.'))
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"Language",
|
|
21
|
+
"Definition_Link",
|
|
22
|
+
"scan_file",
|
|
23
|
+
"find_symbols_in_text",
|
|
24
|
+
"init",
|
|
25
|
+
"stop",
|
|
26
|
+
"clear",
|
|
27
|
+
]
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
import platform
|
|
5
|
+
import ctypes as C
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from dataclasses import dataclass, fields
|
|
8
|
+
from enum import IntEnum, IntFlag
|
|
9
|
+
from typing import Callable, TypeVar
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
DIR = Path(__file__).parent
|
|
13
|
+
|
|
14
|
+
class Str_C(C.Structure):
|
|
15
|
+
_fields_ = [
|
|
16
|
+
("data" , C.c_char_p),
|
|
17
|
+
("length", C.c_uint32),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
def to_bytes(self) -> bytes:
|
|
21
|
+
return C.string_at(self.data, self.length)
|
|
22
|
+
|
|
23
|
+
def to_str_c(s: str | bytes) -> Str_C:
|
|
24
|
+
if isinstance(s, str):
|
|
25
|
+
s = s.encode()
|
|
26
|
+
return Str_C(s, len(s))
|
|
27
|
+
|
|
28
|
+
_lib = C.CDLL(str(DIR / "libcode_intelligence.so"))
|
|
29
|
+
|
|
30
|
+
SCAN_CALLBACK = C.CFUNCTYPE(None, C.c_void_p)
|
|
31
|
+
_lib.enqueue_scan_file.argtypes = [SCAN_CALLBACK, C.c_void_p, Str_C, C.c_uint64]
|
|
32
|
+
_lib.enqueue_scan_file.restype = None
|
|
33
|
+
|
|
34
|
+
class Definition_Link_C(C.Structure):
|
|
35
|
+
_fields_ = [
|
|
36
|
+
("start_offset_in_utf8_text", C.c_uint32),
|
|
37
|
+
("end_offset_in_utf8_text" , C.c_uint32),
|
|
38
|
+
("source_file" , C.c_char_p),
|
|
39
|
+
("line_0_based" , C.c_uint32),
|
|
40
|
+
("column_0_based" , C.c_uint32),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
FIND_CALLBACK = C.CFUNCTYPE(None, C.c_void_p, C.POINTER(Definition_Link_C), C.c_int, C.c_void_p)
|
|
44
|
+
_lib.enqueue_find_symbols_in_text.argtypes = [FIND_CALLBACK, C.c_void_p, Str_C, C.c_bool, C.c_bool, C.POINTER(Str_C), C.c_uint32]
|
|
45
|
+
_lib.enqueue_find_symbols_in_text.restype = None
|
|
46
|
+
|
|
47
|
+
_lib.enqueue_stop.argtypes = []
|
|
48
|
+
_lib.enqueue_stop.restype = None
|
|
49
|
+
|
|
50
|
+
_lib.enqueue_clear.argtypes = []
|
|
51
|
+
_lib.enqueue_clear.restype = None
|
|
52
|
+
|
|
53
|
+
_lib.start_workers.argtypes = []
|
|
54
|
+
_lib.start_workers.restype = None
|
|
55
|
+
|
|
56
|
+
_lib.get_pending_tasks_count.argtypes = []
|
|
57
|
+
_lib.get_pending_tasks_count.restype = C.c_uint64
|
|
58
|
+
|
|
59
|
+
_lib.free_memory.argtypes = [C.c_void_p]
|
|
60
|
+
_lib.free_memory.restype = None
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
T = TypeVar("T")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def positional_repr(cls: type) -> type:
|
|
67
|
+
def __repr__(self):
|
|
68
|
+
values = ', '.join(repr(getattr(self, f.name)) for f in fields(self))
|
|
69
|
+
return f"{cls.__name__}({values})"
|
|
70
|
+
cls.__repr__ = __repr__
|
|
71
|
+
return cls
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class Language(IntEnum):
|
|
75
|
+
UNKNOWN = 0
|
|
76
|
+
PYTHON = 1
|
|
77
|
+
JAVASCRIPT = 2
|
|
78
|
+
GOLANG = 3
|
|
79
|
+
PHP = 4
|
|
80
|
+
RUST = 5
|
|
81
|
+
CPP = 6
|
|
82
|
+
JAVA = 7
|
|
83
|
+
C_SHARP = 8
|
|
84
|
+
KOTLIN = 9
|
|
85
|
+
SWIFT = 10
|
|
86
|
+
DART = 11
|
|
87
|
+
R_LANG = 12
|
|
88
|
+
RUBY = 13
|
|
89
|
+
LUA = 14
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
@positional_repr
|
|
94
|
+
class Definition_Link:
|
|
95
|
+
start_offset_in_text: int = 0
|
|
96
|
+
end_offset_in_text : int = 0
|
|
97
|
+
source_file : str = ""
|
|
98
|
+
line_0_based : int = 0
|
|
99
|
+
column_0_based : int = 0
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def from_c(cls, link: Definition_Link_C) -> "Definition_Link":
|
|
103
|
+
return cls(
|
|
104
|
+
start_offset_in_text=link.start_offset_in_utf8_text,
|
|
105
|
+
end_offset_in_text=link.end_offset_in_utf8_text,
|
|
106
|
+
source_file=link.source_file.decode('utf-8'),
|
|
107
|
+
line_0_based=link.line_0_based,
|
|
108
|
+
column_0_based=link.column_0_based,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Using ["..."] syntax to create a copy independant of
|
|
113
|
+
# whether any other library sets the signature for those functions.
|
|
114
|
+
Py_IncRef = C.pythonapi["Py_IncRef"]
|
|
115
|
+
Py_IncRef.argtypes = [C.py_object]
|
|
116
|
+
Py_IncRef.restype = None
|
|
117
|
+
|
|
118
|
+
Py_DecRef = C.pythonapi["Py_DecRef"]
|
|
119
|
+
Py_DecRef.argtypes = [C.py_object]
|
|
120
|
+
Py_DecRef.restype = None
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# IMPORTANT: KEEP THIS ALIVE FOR GO THREAD WORKER
|
|
124
|
+
C_NO_CALLBACK = SCAN_CALLBACK(0)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _wrap_data_ptr(tup: T) -> int:
|
|
128
|
+
# MANUALLY INCREMENT THE REFERENCE COUNT
|
|
129
|
+
# We are now responsible for its lifetime
|
|
130
|
+
Py_IncRef(C.py_object(tup))
|
|
131
|
+
|
|
132
|
+
return id(tup)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _unwrap_data_ptr(data_ptr: int) -> T:
|
|
136
|
+
assert data_ptr, "Received a NULL pointer"
|
|
137
|
+
|
|
138
|
+
tup = C.cast(data_ptr, C.py_object).value
|
|
139
|
+
|
|
140
|
+
# MANUALLY DECREMENT THE REFERENCE COUNT.
|
|
141
|
+
# We are fulfilling our promise
|
|
142
|
+
Py_DecRef(C.py_object(tup))
|
|
143
|
+
|
|
144
|
+
return tup
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def scan_file(file: str | Path, language: Language, callback: Callable[[T | None], None] | None = None, data: T | None = None) -> bool:
|
|
148
|
+
if callback is None:
|
|
149
|
+
c_callback, data_ptr = C_NO_CALLBACK, 0
|
|
150
|
+
else:
|
|
151
|
+
c_callback, data_ptr = C_AFTER_SCAN, _wrap_data_ptr((callback, data))
|
|
152
|
+
|
|
153
|
+
# print(f"[PY] sending next request {file}")
|
|
154
|
+
_lib.enqueue_scan_file(c_callback, data_ptr, to_str_c(os.fsencode(file)), language)
|
|
155
|
+
|
|
156
|
+
# never drop the request (it would be a memory leak)
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@SCAN_CALLBACK
|
|
161
|
+
def C_AFTER_SCAN(data_ptr: int) -> None:
|
|
162
|
+
callback, data = _unwrap_data_ptr(data_ptr)
|
|
163
|
+
assert callback is not None, "Don't feed youself NULL callbacks!"
|
|
164
|
+
callback(data)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def find_symbols_in_text(text: str, callback: Callable[[T | None, list[Definition_Link]], None] | None = None, data: T | None = None, ignore_word_like: bool = False, sync: bool = False, first_look_at_these_files: list[Path|str] = []) -> bool:
|
|
168
|
+
if callback is None:
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
c_callback, data_ptr = C_AFTER_FIND, _wrap_data_ptr((callback, data))
|
|
172
|
+
|
|
173
|
+
first_look_at_these_files_c = (Str_C * len(first_look_at_these_files))()
|
|
174
|
+
for i, it in enumerate(first_look_at_these_files):
|
|
175
|
+
first_look_at_these_files_c[i] = to_str_c(os.fsencode(it))
|
|
176
|
+
|
|
177
|
+
# print("[PY] sending find_symbols_in_text")
|
|
178
|
+
_lib.enqueue_find_symbols_in_text(c_callback, data_ptr, to_str_c(text), ignore_word_like, sync, first_look_at_these_files_c, len(first_look_at_these_files))
|
|
179
|
+
|
|
180
|
+
# never drop the request (it would be a memory leak)
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@FIND_CALLBACK
|
|
185
|
+
def C_AFTER_FIND(data_ptr: int, links_ptr: C.POINTER(Definition_Link_C), count: int, strings_handle: int) -> None:
|
|
186
|
+
callback, data = _unwrap_data_ptr(data_ptr)
|
|
187
|
+
try:
|
|
188
|
+
assert callback is not None, "Don't feed youself NULL callbacks!"
|
|
189
|
+
results = [Definition_Link()] * count
|
|
190
|
+
for i in range(count):
|
|
191
|
+
results[i] = Definition_Link.from_c(links_ptr[i])
|
|
192
|
+
callback(data, results)
|
|
193
|
+
finally:
|
|
194
|
+
if links_ptr: _lib.free_memory(links_ptr)
|
|
195
|
+
if strings_handle: _lib.free_memory(strings_handle)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def pending_tasks() -> int:
|
|
199
|
+
return int(_lib.get_pending_tasks_count())
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def init() -> None:
|
|
203
|
+
_lib.start_workers()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def stop() -> None:
|
|
207
|
+
_lib.enqueue_stop()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def clear() -> None:
|
|
211
|
+
_lib.enqueue_clear()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def wait_for_callbacks_in_seconds(timeout_s: float, step_s: float = 0.15) -> bool:
|
|
215
|
+
# print(f"[PY] Waiting for callbacks (~{timeout_s}s)...")
|
|
216
|
+
deadline = time.time() + timeout_s
|
|
217
|
+
while True:
|
|
218
|
+
if pending_tasks() == 0:
|
|
219
|
+
# print("[PY] All pending tasks have completed.")
|
|
220
|
+
return True
|
|
221
|
+
|
|
222
|
+
if time.time() >= deadline:
|
|
223
|
+
print("[PY] Timeout reached.")
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
# print(f"[PY] pending_tasks={pending_tasks()}")
|
|
227
|
+
time.sleep(step_s)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
if len(sys.argv) <= 2:
|
|
232
|
+
print("usage: python goober.py text folder...")
|
|
233
|
+
exit(1)
|
|
234
|
+
|
|
235
|
+
import atexit
|
|
236
|
+
atexit.register(stop)
|
|
237
|
+
|
|
238
|
+
def scan_callback(data: Path) -> None:
|
|
239
|
+
print(f"[PY] {data}")
|
|
240
|
+
|
|
241
|
+
def find_callback(data: tuple[str, str], links: list[Definition_Link]) -> None:
|
|
242
|
+
text, label = data
|
|
243
|
+
print(f"[PY] Callback for '{label}': Found {len(links)} symbols in text \"{text}\"")
|
|
244
|
+
for res in links:
|
|
245
|
+
matched_symbol = text[res.start_offset_in_text:res.end_offset_in_text]
|
|
246
|
+
print(f" '{matched_symbol}' -> {res.source_file}:{res.line_0_based}:{res.column_0_based}")
|
|
247
|
+
|
|
248
|
+
for folder in sys.argv[2:]:
|
|
249
|
+
path = Path(folder)
|
|
250
|
+
if path.is_dir():
|
|
251
|
+
for file in path.rglob("*.py"):
|
|
252
|
+
scan_file(file, Language.PYTHON, scan_callback, file)
|
|
253
|
+
else:
|
|
254
|
+
scan_file(path, Language.PYTHON, scan_callback, path)
|
|
255
|
+
|
|
256
|
+
text = sys.argv[1]
|
|
257
|
+
find_symbols_in_text(text, find_callback, data=(text, "before"),
|
|
258
|
+
first_look_at_these_files=["hello.py", "yay.so"])
|
|
259
|
+
|
|
260
|
+
print("[PY] WAIT, WAIT, WAIT!")
|
|
261
|
+
wait_for_callbacks_in_seconds(20)
|
|
262
|
+
|
|
263
|
+
find_symbols_in_text(text, find_callback, data=(text, "after"))
|
|
264
|
+
|
|
265
|
+
wait_for_callbacks_in_seconds(1)
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
code_intelligence-4.8.2.dist-info/METADATA,sha256=vYFdhKMd6QQFE_ofbbj2pRKZe0BU3Avv-HhCacGtkzI,96
|
|
2
|
+
code_intelligence-4.8.2.dist-info/RECORD,,
|
|
3
|
+
code_intelligence-4.8.2.dist-info/WHEEL,sha256=fP6FXRdXCjH8ll6K3YWkkgIJi7DgDAtlR6s0hai6FzM,103
|
|
4
|
+
code_intelligence/__init__.py,sha256=oBt0zByIG6xwSRyhARy9UDEajO818XIvgjaEfzItoyw,560
|
|
5
|
+
code_intelligence/goober.py,sha256=9U3i9wqIZrofwxWM8-GJzyVbFjgHaZ0XX3o9On-0S1c,7909
|
|
6
|
+
code_intelligence/libcode_intelligence.so,sha256=9NjM3S2BI9ATXE7SwFiPbGYaldn_wrizTzVlVkQIKvQ,23748736
|
|
7
|
+
code_intelligence/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|