codespine 0.1.3__tar.gz → 0.1.5__tar.gz
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.
- {codespine-0.1.3 → codespine-0.1.5}/PKG-INFO +1 -1
- {codespine-0.1.3 → codespine-0.1.5}/codespine/__init__.py +1 -1
- {codespine-0.1.3 → codespine-0.1.5}/codespine/cli.py +40 -4
- {codespine-0.1.3 → codespine-0.1.5}/codespine/indexer/engine.py +38 -6
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/PKG-INFO +1 -1
- {codespine-0.1.3 → codespine-0.1.5}/pyproject.toml +1 -1
- {codespine-0.1.3 → codespine-0.1.5}/LICENSE +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/README.md +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/community.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/context.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/coupling.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/deadcode.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/flow.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/analysis/impact.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/config.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/db/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/db/schema.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/db/store.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/diff/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/diff/branch_diff.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/indexer/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/indexer/call_resolver.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/indexer/java_parser.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/indexer/symbol_builder.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/mcp/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/mcp/server.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/noise/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/noise/blocklist.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/bm25.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/fuzzy.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/hybrid.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/rrf.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/search/vector.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/watch/__init__.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine/watch/watcher.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/SOURCES.txt +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/dependency_links.txt +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/entry_points.txt +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/requires.txt +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/codespine.egg-info/top_level.txt +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/gindex.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/setup.cfg +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/tests/test_branch_diff_normalize.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/tests/test_call_resolver.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/tests/test_index_and_hybrid.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/tests/test_java_parser.py +0 -0
- {codespine-0.1.3 → codespine-0.1.5}/tests/test_search_ranking.py +0 -0
|
@@ -88,10 +88,46 @@ def analyse(path: str, full: bool) -> None:
|
|
|
88
88
|
abs_path = os.path.abspath(path)
|
|
89
89
|
store = GraphStore(read_only=False)
|
|
90
90
|
indexer = JavaIndexer(store)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
parse_state = {"shown": False, "indexed": 0, "total": 0, "last_ts": 0.0}
|
|
92
|
+
|
|
93
|
+
def _progress(event: str, payload: dict) -> None:
|
|
94
|
+
now = time.perf_counter()
|
|
95
|
+
if event == "scan_done":
|
|
96
|
+
_phase("Walking files...", f"{int(payload.get('files_found', 0))} files found")
|
|
97
|
+
return
|
|
98
|
+
if event == "plan_done":
|
|
99
|
+
to_index = int(payload.get("files_to_index", 0))
|
|
100
|
+
deleted = int(payload.get("deleted_files", 0))
|
|
101
|
+
mode = str(payload.get("mode", "incremental"))
|
|
102
|
+
parse_state["total"] = to_index
|
|
103
|
+
_phase("Index mode...", f"{mode} ({to_index} files to index, {deleted} deleted)")
|
|
104
|
+
if to_index == 0:
|
|
105
|
+
_phase("Parsing code...", "0/0")
|
|
106
|
+
return
|
|
107
|
+
if event == "parse_progress":
|
|
108
|
+
indexed = int(payload.get("indexed", 0))
|
|
109
|
+
total = int(payload.get("total", 0))
|
|
110
|
+
parse_state["indexed"] = indexed
|
|
111
|
+
parse_state["total"] = total
|
|
112
|
+
if total == 0:
|
|
113
|
+
return
|
|
114
|
+
if indexed == total or (now - parse_state["last_ts"]) >= 0.2:
|
|
115
|
+
click.echo(f"\rParsing code... {indexed}/{total}", nl=False)
|
|
116
|
+
parse_state["shown"] = True
|
|
117
|
+
parse_state["last_ts"] = now
|
|
118
|
+
return
|
|
119
|
+
if event == "resolve_calls_start" and parse_state["shown"]:
|
|
120
|
+
click.echo()
|
|
121
|
+
parse_state["shown"] = False
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
result = indexer.index_project(abs_path, full=full, progress=_progress)
|
|
125
|
+
if parse_state["shown"]:
|
|
126
|
+
click.echo()
|
|
127
|
+
if parse_state["total"] == 0:
|
|
128
|
+
_phase("Parsing code...", "0/0")
|
|
129
|
+
elif parse_state["indexed"] < parse_state["total"]:
|
|
130
|
+
_phase("Parsing code...", f"{parse_state['indexed']}/{parse_state['total']}")
|
|
95
131
|
_phase("Tracing calls...", f"{result.calls_resolved} calls resolved")
|
|
96
132
|
_phase("Analyzing types...", f"{result.type_relationships} type relationships")
|
|
97
133
|
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
+
from typing import Callable
|
|
5
6
|
|
|
6
7
|
from codespine.indexer.call_resolver import resolve_calls
|
|
7
8
|
from codespine.indexer.java_parser import parse_java_source
|
|
@@ -25,12 +26,18 @@ class JavaIndexer:
|
|
|
25
26
|
def __init__(self, store):
|
|
26
27
|
self.store = store
|
|
27
28
|
|
|
28
|
-
def index_project(
|
|
29
|
+
def index_project(
|
|
30
|
+
self,
|
|
31
|
+
root_path: str,
|
|
32
|
+
full: bool = True,
|
|
33
|
+
progress: Callable[[str, dict], None] | None = None,
|
|
34
|
+
) -> IndexResult:
|
|
29
35
|
root_path = os.path.abspath(root_path)
|
|
30
36
|
project_id = os.path.basename(root_path)
|
|
31
37
|
current_files = self._collect_java_files(root_path)
|
|
32
|
-
|
|
38
|
+
self._emit(progress, "scan_done", files_found=len(current_files))
|
|
33
39
|
db_files = self.store.project_file_hashes(project_id) if not full else {}
|
|
40
|
+
current_hashes = self._hash_files(project_id, root_path, current_files) if not full else {}
|
|
34
41
|
|
|
35
42
|
if full:
|
|
36
43
|
to_reindex = current_files
|
|
@@ -45,6 +52,13 @@ class JavaIndexer:
|
|
|
45
52
|
old = db_files.get(fid, {}).get("hash")
|
|
46
53
|
if old != digest:
|
|
47
54
|
to_reindex.append(file_path)
|
|
55
|
+
self._emit(
|
|
56
|
+
progress,
|
|
57
|
+
"plan_done",
|
|
58
|
+
files_to_index=len(to_reindex),
|
|
59
|
+
deleted_files=len(deleted_file_ids),
|
|
60
|
+
mode="full" if full else "incremental",
|
|
61
|
+
)
|
|
48
62
|
|
|
49
63
|
files_indexed = 0
|
|
50
64
|
classes_indexed = 0
|
|
@@ -151,12 +165,23 @@ class JavaIndexer:
|
|
|
151
165
|
}
|
|
152
166
|
class_methods[cls.fqcn][method.signature] = m_id
|
|
153
167
|
files_indexed += 1
|
|
168
|
+
self._emit(
|
|
169
|
+
progress,
|
|
170
|
+
"parse_progress",
|
|
171
|
+
indexed=files_indexed,
|
|
172
|
+
total=len(to_reindex),
|
|
173
|
+
file_path=file_path,
|
|
174
|
+
)
|
|
154
175
|
|
|
176
|
+
self._emit(progress, "resolve_calls_start")
|
|
155
177
|
for src, dst, confidence, reason in resolve_calls(method_catalog, method_calls, method_context, class_catalog):
|
|
156
178
|
self.store.add_call(src, dst, confidence, reason)
|
|
157
179
|
calls_resolved += 1
|
|
180
|
+
self._emit(progress, "resolve_calls_done", calls_resolved=calls_resolved)
|
|
158
181
|
|
|
182
|
+
self._emit(progress, "resolve_types_start")
|
|
159
183
|
type_relationships += self._build_inheritance_edges(class_meta, class_catalog, class_methods)
|
|
184
|
+
self._emit(progress, "resolve_types_done", type_relationships=type_relationships)
|
|
160
185
|
|
|
161
186
|
return IndexResult(
|
|
162
187
|
project_id=project_id,
|
|
@@ -172,10 +197,11 @@ class JavaIndexer:
|
|
|
172
197
|
@staticmethod
|
|
173
198
|
def _collect_java_files(root_path: str) -> list[str]:
|
|
174
199
|
out: list[str] = []
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
skip_dirs = {".git", "target", "build", "out", ".idea", ".gradle", ".mvn", "node_modules"}
|
|
201
|
+
for root, dirs, files in os.walk(root_path, topdown=True):
|
|
202
|
+
dirs[:] = [d for d in dirs if d not in skip_dirs]
|
|
203
|
+
normalized = root.replace("\\", "/")
|
|
204
|
+
if "/src/" not in normalized and not normalized.endswith("/src"):
|
|
179
205
|
continue
|
|
180
206
|
for filename in files:
|
|
181
207
|
if filename.endswith(".java"):
|
|
@@ -303,3 +329,9 @@ class JavaIndexer:
|
|
|
303
329
|
self.store.add_reference("OVERRIDES", "Method", method_id, "Method", iface_method, 1.0)
|
|
304
330
|
rel_count += 1
|
|
305
331
|
return rel_count
|
|
332
|
+
|
|
333
|
+
@staticmethod
|
|
334
|
+
def _emit(progress: Callable[[str, dict], None] | None, event: str, **payload: object) -> None:
|
|
335
|
+
if progress is None:
|
|
336
|
+
return
|
|
337
|
+
progress(event, payload)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|