aja-codeintel 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.
- aja_codeintel-0.1.0.dist-info/METADATA +436 -0
- aja_codeintel-0.1.0.dist-info/RECORD +68 -0
- aja_codeintel-0.1.0.dist-info/WHEEL +5 -0
- aja_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
- aja_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
- aja_codeintel-0.1.0.dist-info/top_level.txt +1 -0
- codeintel_cli/__init__.py +1 -0
- codeintel_cli/__main__.py +4 -0
- codeintel_cli/cli.py +41 -0
- codeintel_cli/commands/__init__.py +1 -0
- codeintel_cli/commands/graph/__init__.py +18 -0
- codeintel_cli/commands/graph/deps_cmd.py +35 -0
- codeintel_cli/commands/graph/related_cmd.py +121 -0
- codeintel_cli/commands/graph/relsymbols_cmd.py +347 -0
- codeintel_cli/commands/graph/reverse_related_cmd.py +54 -0
- codeintel_cli/commands/nav/__init__.py +12 -0
- codeintel_cli/commands/nav/copy_cmd.py +101 -0
- codeintel_cli/commands/nav/open_cmd.py +18 -0
- codeintel_cli/commands/nav/where_cmd.py +21 -0
- codeintel_cli/commands/project/__init__.py +26 -0
- codeintel_cli/commands/project/context_cmd.py +326 -0
- codeintel_cli/commands/project/folder_cmd.py +51 -0
- codeintel_cli/commands/project/imports_cmd.py +90 -0
- codeintel_cli/commands/project/models_cmd.py +98 -0
- codeintel_cli/commands/project/modeltree_cmd.py +476 -0
- codeintel_cli/commands/project/new.py +0 -0
- codeintel_cli/commands/project/resolve_cmd.py +29 -0
- codeintel_cli/commands/project/scan_cmd.py +51 -0
- codeintel_cli/commands/project/servicemap_cmd.py +180 -0
- codeintel_cli/commands/project/tree_cmd.py +203 -0
- codeintel_cli/commands/project/version_cmd.py +14 -0
- codeintel_cli/context/java_context.py +180 -0
- codeintel_cli/context/java_rel.py +299 -0
- codeintel_cli/context/java_service.py +291 -0
- codeintel_cli/context/python_context.py +91 -0
- codeintel_cli/context/python_rel.py +251 -0
- codeintel_cli/context/python_service.py +205 -0
- codeintel_cli/core/fuzzy.py +72 -0
- codeintel_cli/core/opener.py +37 -0
- codeintel_cli/core/project.py +34 -0
- codeintel_cli/core/resolve_folder.py +68 -0
- codeintel_cli/core/resolve_model_target.py +92 -0
- codeintel_cli/core/resolve_target.py +53 -0
- codeintel_cli/core/timing.py +13 -0
- codeintel_cli/core/where.py +77 -0
- codeintel_cli/db/__init__.py +7 -0
- codeintel_cli/db/cache.py +224 -0
- codeintel_cli/db/operations.py +333 -0
- codeintel_cli/db/schema.py +102 -0
- codeintel_cli/errors.py +78 -0
- codeintel_cli/graph/__init__.py +1 -0
- codeintel_cli/graph/builder.py +149 -0
- codeintel_cli/graph/query.py +30 -0
- codeintel_cli/graph/traverse.py +49 -0
- codeintel_cli/lang/__init__.py +0 -0
- codeintel_cli/lang/java/__init__.py +0 -0
- codeintel_cli/lang/java/engine.py +18 -0
- codeintel_cli/lang/java/models.py +105 -0
- codeintel_cli/lang/java/resolve.py +49 -0
- codeintel_cli/lang/python/__init__.py +0 -0
- codeintel_cli/lang/python/engine.py +8 -0
- codeintel_cli/lang/python/models.py +86 -0
- codeintel_cli/lang/router.py +24 -0
- codeintel_cli/parser/imports.py +26 -0
- codeintel_cli/parser/resolve.py +49 -0
- codeintel_cli/parser/symbols.py +92 -0
- codeintel_cli/scanner/__init__.py +0 -0
- codeintel_cli/scanner/scanner.py +41 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from ..errors import InvalidPathError
|
|
6
|
+
from .project import find_project_root
|
|
7
|
+
from .fuzzy import rank_paths, fuzzy_is_confident
|
|
8
|
+
from ..scanner.scanner import find_all_supported_files
|
|
9
|
+
|
|
10
|
+
def _is_domain_model_path(p: Path) -> bool:
|
|
11
|
+
parts = {x.lower() for x in p.parts}
|
|
12
|
+
return bool(parts & {"model", "models", "entity", "entities", "domain"})
|
|
13
|
+
|
|
14
|
+
def _java_model_annotation_hint(path: Path) -> bool:
|
|
15
|
+
try:
|
|
16
|
+
t = path.read_text(encoding="utf-8", errors="ignore")
|
|
17
|
+
except Exception:
|
|
18
|
+
return False
|
|
19
|
+
return any(x in t for x in ("@Entity", "@Table", "@Embeddable", "@MappedSuperclass"))
|
|
20
|
+
|
|
21
|
+
def _has_class_definition(path: Path) -> bool:
|
|
22
|
+
try:
|
|
23
|
+
text = path.read_text(encoding="utf-8", errors="ignore")
|
|
24
|
+
return "class " in text and len(text.strip()) > 50
|
|
25
|
+
except Exception:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
def _is_java_model_file(p: Path) -> bool:
|
|
29
|
+
if p.suffix.lower() != ".java":
|
|
30
|
+
return False
|
|
31
|
+
if p.name == "__init__.py":
|
|
32
|
+
return False
|
|
33
|
+
if not (_is_domain_model_path(p) or _java_model_annotation_hint(p)):
|
|
34
|
+
return False
|
|
35
|
+
return _has_class_definition(p)
|
|
36
|
+
|
|
37
|
+
def _is_python_model_file(p: Path) -> bool:
|
|
38
|
+
if p.suffix.lower() != ".py":
|
|
39
|
+
return False
|
|
40
|
+
if p.name in {"__init__.py", "models.py"}:
|
|
41
|
+
return False
|
|
42
|
+
if not _is_domain_model_path(p):
|
|
43
|
+
return False
|
|
44
|
+
return _has_class_definition(p)
|
|
45
|
+
|
|
46
|
+
def resolve_model_target_file(query: str, root: str = ".", top: int = 3) -> tuple[Path, Path]:
|
|
47
|
+
root_path = Path(root).resolve()
|
|
48
|
+
project_root = find_project_root(root_path)
|
|
49
|
+
|
|
50
|
+
q = Path(query)
|
|
51
|
+
if q.exists() and q.is_file():
|
|
52
|
+
return q.resolve(), project_root
|
|
53
|
+
|
|
54
|
+
files = find_all_supported_files(project_root)
|
|
55
|
+
model_files: list[Path] = [p for p in files if _is_java_model_file(p) or _is_python_model_file(p)]
|
|
56
|
+
|
|
57
|
+
if not model_files:
|
|
58
|
+
raise InvalidPathError(message="No model/entity files found", path=project_root)
|
|
59
|
+
|
|
60
|
+
qstem_lower = Path(query).stem.lower()
|
|
61
|
+
exact_matches = [f for f in model_files if f.stem.lower() == qstem_lower]
|
|
62
|
+
|
|
63
|
+
if len(exact_matches) == 1:
|
|
64
|
+
return exact_matches[0].resolve(), project_root
|
|
65
|
+
|
|
66
|
+
if len(exact_matches) > 1:
|
|
67
|
+
exact_matches = exact_matches[:top]
|
|
68
|
+
|
|
69
|
+
ranked = rank_paths(query, exact_matches if exact_matches else model_files, project_root, top=top)
|
|
70
|
+
|
|
71
|
+
if not ranked:
|
|
72
|
+
raise InvalidPathError(message="Model not found", path=Path(query))
|
|
73
|
+
|
|
74
|
+
if fuzzy_is_confident(ranked):
|
|
75
|
+
top_score = ranked[0][1]
|
|
76
|
+
ties = [x for x in ranked if abs(x[1] - top_score) < 0.02]
|
|
77
|
+
if len(ties) == 1:
|
|
78
|
+
return ranked[0][0].resolve(), project_root
|
|
79
|
+
|
|
80
|
+
typer.echo("Select model:")
|
|
81
|
+
for i, (p, score) in enumerate(ranked, 1):
|
|
82
|
+
try:
|
|
83
|
+
rel = p.relative_to(project_root)
|
|
84
|
+
except Exception:
|
|
85
|
+
rel = p
|
|
86
|
+
typer.echo(f"{i}. {rel.as_posix()} score={score:.2f}")
|
|
87
|
+
|
|
88
|
+
choice = typer.prompt("Select number (0=cancel)", type=int, default=0)
|
|
89
|
+
if choice == 0 or choice < 1 or choice > len(ranked):
|
|
90
|
+
raise typer.Exit()
|
|
91
|
+
|
|
92
|
+
return ranked[choice - 1][0].resolve(), project_root
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from ..errors import InvalidPathError
|
|
6
|
+
from .project import find_project_root
|
|
7
|
+
from .fuzzy import rank_paths, fuzzy_is_confident
|
|
8
|
+
from ..scanner.scanner import find_all_supported_files
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def resolve_target_file(query: str, root: str = ".", top: int = 3) -> tuple[Path, Path]:
|
|
12
|
+
root_path = Path(root).resolve()
|
|
13
|
+
project_root = find_project_root(root_path)
|
|
14
|
+
|
|
15
|
+
q = Path(query)
|
|
16
|
+
if q.exists() and q.is_file():
|
|
17
|
+
return q.resolve(), project_root
|
|
18
|
+
|
|
19
|
+
all_files = find_all_supported_files(project_root)
|
|
20
|
+
|
|
21
|
+
qstem_lower = Path(query).stem.lower()
|
|
22
|
+
exact_matches = [f for f in all_files if f.stem.lower() == qstem_lower and f.name != "__init__.py"]
|
|
23
|
+
|
|
24
|
+
if len(exact_matches) == 1:
|
|
25
|
+
return exact_matches[0].resolve(), project_root
|
|
26
|
+
|
|
27
|
+
if len(exact_matches) > 1:
|
|
28
|
+
exact_matches = exact_matches[:top]
|
|
29
|
+
|
|
30
|
+
ranked = rank_paths(query, all_files if not exact_matches else exact_matches, project_root, top=top)
|
|
31
|
+
|
|
32
|
+
if not ranked:
|
|
33
|
+
raise InvalidPathError(message="File not found", path=Path(query))
|
|
34
|
+
|
|
35
|
+
if fuzzy_is_confident(ranked):
|
|
36
|
+
top_score = ranked[0][1]
|
|
37
|
+
ties = [x for x in ranked if abs(x[1] - top_score) < 0.02]
|
|
38
|
+
if len(ties) == 1:
|
|
39
|
+
return ranked[0][0].resolve(), project_root
|
|
40
|
+
|
|
41
|
+
typer.echo("Did you mean:")
|
|
42
|
+
for i, (p, score) in enumerate(ranked, 1):
|
|
43
|
+
try:
|
|
44
|
+
rel = p.relative_to(project_root)
|
|
45
|
+
except Exception:
|
|
46
|
+
rel = p
|
|
47
|
+
typer.echo(f"{i}. {rel.as_posix()} score={score:.2f}")
|
|
48
|
+
|
|
49
|
+
choice = typer.prompt("Select number (0=cancel)", type=int, default=0)
|
|
50
|
+
if choice == 0 or choice < 1 or choice > len(ranked):
|
|
51
|
+
raise typer.Exit()
|
|
52
|
+
|
|
53
|
+
return ranked[choice - 1][0].resolve(), project_root
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _py_file_to_module(path: Path, root: Path) -> str | None:
|
|
7
|
+
if path.suffix.lower() != ".py":
|
|
8
|
+
return None
|
|
9
|
+
try:
|
|
10
|
+
rel = path.resolve().relative_to(root.resolve())
|
|
11
|
+
except Exception:
|
|
12
|
+
return None
|
|
13
|
+
parts = list(rel.parts)
|
|
14
|
+
if not parts:
|
|
15
|
+
return None
|
|
16
|
+
if parts[-1] == "__init__.py":
|
|
17
|
+
parts = parts[:-1]
|
|
18
|
+
else:
|
|
19
|
+
parts[-1] = Path(parts[-1]).stem
|
|
20
|
+
if not parts:
|
|
21
|
+
return None
|
|
22
|
+
return ".".join(parts)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _java_file_to_module(path: Path, root: Path) -> str | None:
|
|
26
|
+
if path.suffix.lower() != ".java":
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
p = path.resolve()
|
|
30
|
+
try:
|
|
31
|
+
rel = p.relative_to(root.resolve())
|
|
32
|
+
except Exception:
|
|
33
|
+
rel = p
|
|
34
|
+
|
|
35
|
+
parts = list(rel.parts)
|
|
36
|
+
if not parts:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
low = [x.lower() for x in parts]
|
|
40
|
+
|
|
41
|
+
def build_from(start_idx: int) -> str | None:
|
|
42
|
+
if start_idx >= len(parts):
|
|
43
|
+
return None
|
|
44
|
+
segs = parts[start_idx:]
|
|
45
|
+
if not segs:
|
|
46
|
+
return None
|
|
47
|
+
last = Path(segs[-1]).stem
|
|
48
|
+
segs = list(segs[:-1]) + [last]
|
|
49
|
+
segs = [s for s in segs if s and s not in (".", "..")]
|
|
50
|
+
if not segs:
|
|
51
|
+
return None
|
|
52
|
+
return ".".join(segs)
|
|
53
|
+
|
|
54
|
+
if "src" in low:
|
|
55
|
+
i = low.index("src")
|
|
56
|
+
if i + 3 < len(parts) and low[i + 1] in ("main", "test") and low[i + 2] == "java":
|
|
57
|
+
out = build_from(i + 3)
|
|
58
|
+
if out:
|
|
59
|
+
return out
|
|
60
|
+
|
|
61
|
+
if "java" in low:
|
|
62
|
+
i = low.index("java")
|
|
63
|
+
out = build_from(i + 1)
|
|
64
|
+
if out:
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
if "com" in low:
|
|
68
|
+
i = low.index("com")
|
|
69
|
+
out = build_from(i)
|
|
70
|
+
if out:
|
|
71
|
+
return out
|
|
72
|
+
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def file_to_module(path: Path, root: Path) -> str | None:
|
|
77
|
+
return _py_file_to_module(path, root) or _java_file_to_module(path, root)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .schema import init_db, get_db_path
|
|
8
|
+
from .operations import (
|
|
9
|
+
is_file_cached,
|
|
10
|
+
upsert_file,
|
|
11
|
+
clear_file_data,
|
|
12
|
+
insert_import,
|
|
13
|
+
insert_symbol,
|
|
14
|
+
insert_model,
|
|
15
|
+
get_all_files,
|
|
16
|
+
get_file_id,
|
|
17
|
+
get_file_imports,
|
|
18
|
+
get_file_symbols,
|
|
19
|
+
get_models,
|
|
20
|
+
needs_rescan,
|
|
21
|
+
get_imports_map_for_paths,
|
|
22
|
+
get_all_file_id_map,
|
|
23
|
+
get_unresolved_import_rows,
|
|
24
|
+
get_file_path_and_lang,
|
|
25
|
+
update_import_resolved,
|
|
26
|
+
)
|
|
27
|
+
from ..scanner.scanner import find_all_supported_files
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _encode_py_import(mod: str, level: int) -> str:
|
|
31
|
+
mod = (mod or "").strip()
|
|
32
|
+
level = int(level or 0)
|
|
33
|
+
if level <= 0:
|
|
34
|
+
return mod
|
|
35
|
+
return ("." * level) + mod
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CacheManager:
|
|
39
|
+
def __init__(self, project_root: Path):
|
|
40
|
+
self.project_root = project_root.resolve()
|
|
41
|
+
self.db_path = get_db_path(self.project_root)
|
|
42
|
+
self.conn: sqlite3.Connection | None = None
|
|
43
|
+
|
|
44
|
+
def __enter__(self):
|
|
45
|
+
self.conn = init_db(self.db_path)
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
49
|
+
if not self.conn:
|
|
50
|
+
return
|
|
51
|
+
if exc_type is None:
|
|
52
|
+
self.conn.commit()
|
|
53
|
+
else:
|
|
54
|
+
self.conn.rollback()
|
|
55
|
+
self.conn.close()
|
|
56
|
+
|
|
57
|
+
def needs_rescan(self) -> bool:
|
|
58
|
+
if not self.conn:
|
|
59
|
+
return True
|
|
60
|
+
return needs_rescan(self.conn, self.project_root)
|
|
61
|
+
|
|
62
|
+
def scan_project(self, verbose: bool = False) -> int:
|
|
63
|
+
if not self.conn:
|
|
64
|
+
raise RuntimeError("CacheManager not initialized")
|
|
65
|
+
|
|
66
|
+
files = find_all_supported_files(self.project_root)
|
|
67
|
+
scanned = 0
|
|
68
|
+
|
|
69
|
+
for file_path in files:
|
|
70
|
+
file_path = file_path.resolve()
|
|
71
|
+
if is_file_cached(self.conn, file_path):
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
content = file_path.read_text(encoding="utf-8", errors="ignore")
|
|
76
|
+
except Exception:
|
|
77
|
+
content = ""
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
rel_path = str(file_path.relative_to(self.project_root))
|
|
81
|
+
except Exception:
|
|
82
|
+
rel_path = str(file_path)
|
|
83
|
+
|
|
84
|
+
language = file_path.suffix.lstrip(".").lower()
|
|
85
|
+
file_id = upsert_file(self.conn, file_path, rel_path, language, content)
|
|
86
|
+
|
|
87
|
+
clear_file_data(self.conn, file_id)
|
|
88
|
+
self._index_file(file_id, file_path, content, language)
|
|
89
|
+
|
|
90
|
+
scanned += 1
|
|
91
|
+
if verbose and scanned % 10 == 0:
|
|
92
|
+
print(f"Indexed {scanned} files...")
|
|
93
|
+
|
|
94
|
+
self._resolve_import_edges()
|
|
95
|
+
self.conn.commit()
|
|
96
|
+
return scanned
|
|
97
|
+
|
|
98
|
+
def _index_file(self, file_id: int, file_path: Path, content: str, language: str) -> None:
|
|
99
|
+
if language == "py":
|
|
100
|
+
self._index_python_file(file_id, file_path, content)
|
|
101
|
+
elif language == "java":
|
|
102
|
+
self._index_java_file(file_id, file_path, content)
|
|
103
|
+
|
|
104
|
+
def _index_python_file(self, file_id: int, file_path: Path, content: str) -> None:
|
|
105
|
+
import ast
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
tree = ast.parse(content)
|
|
109
|
+
except Exception:
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
for node in ast.walk(tree):
|
|
113
|
+
if isinstance(node, ast.Import):
|
|
114
|
+
for alias in node.names:
|
|
115
|
+
if alias.name:
|
|
116
|
+
insert_import(self.conn, file_id, alias.name, None)
|
|
117
|
+
elif isinstance(node, ast.ImportFrom):
|
|
118
|
+
mod = node.module or ""
|
|
119
|
+
lvl = int(getattr(node, "level", 0) or 0)
|
|
120
|
+
insert_import(self.conn, file_id, _encode_py_import(mod, lvl), None)
|
|
121
|
+
elif isinstance(node, ast.ClassDef):
|
|
122
|
+
insert_symbol(self.conn, file_id, node.name, "class", getattr(node, "lineno", 1))
|
|
123
|
+
elif isinstance(node, ast.FunctionDef):
|
|
124
|
+
insert_symbol(self.conn, file_id, node.name, "function", getattr(node, "lineno", 1))
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
from ..lang.router import extract_models_for_file
|
|
128
|
+
|
|
129
|
+
models = extract_models_for_file(file_path, self.project_root)
|
|
130
|
+
for model in models or []:
|
|
131
|
+
fields = [(f.name, f.type, f.name.lower() == "id") for f in (model.fields or [])]
|
|
132
|
+
insert_model(self.conn, file_id, model.name, fields, [])
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
def _index_java_file(self, file_id: int, file_path: Path, content: str) -> None:
|
|
137
|
+
import re
|
|
138
|
+
|
|
139
|
+
for m in re.finditer(r"import\s+([\w.]+)\s*;", content):
|
|
140
|
+
imp = (m.group(1) or "").strip()
|
|
141
|
+
if imp:
|
|
142
|
+
insert_import(self.conn, file_id, imp, None)
|
|
143
|
+
|
|
144
|
+
for m in re.finditer(r"\b(class|interface|enum|record)\s+([A-Za-z_]\w*)", content):
|
|
145
|
+
insert_symbol(self.conn, file_id, m.group(2), m.group(1), None)
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
from ..context.java_rel import java_fields_and_rels
|
|
149
|
+
|
|
150
|
+
fields, rels = java_fields_and_rels(file_path)
|
|
151
|
+
if fields:
|
|
152
|
+
relationships = [{"kind": r.kind, "target": r.target, "field": r.field} for r in rels]
|
|
153
|
+
insert_model(self.conn, file_id, file_path.stem, fields, relationships)
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
def _resolve_import_edges(self) -> None:
|
|
158
|
+
if not self.conn:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
from ..parser.resolve import resolve_import_to_file
|
|
162
|
+
from ..lang.java.resolve import resolve_java_import_to_file
|
|
163
|
+
|
|
164
|
+
file_id_by_path = get_all_file_id_map(self.conn)
|
|
165
|
+
rows = get_unresolved_import_rows(self.conn)
|
|
166
|
+
|
|
167
|
+
for imp_row_id, src_file_id, imp in rows:
|
|
168
|
+
meta = get_file_path_and_lang(self.conn, src_file_id)
|
|
169
|
+
if not meta:
|
|
170
|
+
continue
|
|
171
|
+
src_path_str, lang = meta
|
|
172
|
+
src_path = Path(src_path_str)
|
|
173
|
+
|
|
174
|
+
target: Path | None = None
|
|
175
|
+
|
|
176
|
+
if lang == "py":
|
|
177
|
+
s = (imp or "").strip()
|
|
178
|
+
if not s:
|
|
179
|
+
continue
|
|
180
|
+
level = 0
|
|
181
|
+
while level < len(s) and s[level] == ".":
|
|
182
|
+
level += 1
|
|
183
|
+
mod = s[level:] if level > 0 else s
|
|
184
|
+
target = resolve_import_to_file(mod, level, src_path, self.project_root)
|
|
185
|
+
|
|
186
|
+
elif lang == "java":
|
|
187
|
+
s = (imp or "").strip()
|
|
188
|
+
if not s or s.endswith(".*"):
|
|
189
|
+
continue
|
|
190
|
+
target = resolve_java_import_to_file(s, self.project_root)
|
|
191
|
+
|
|
192
|
+
if not target:
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
tid = file_id_by_path.get(str(target.resolve()))
|
|
196
|
+
if tid:
|
|
197
|
+
update_import_resolved(self.conn, imp_row_id, tid)
|
|
198
|
+
|
|
199
|
+
def get_cached_files(self) -> list[Path]:
|
|
200
|
+
if not self.conn:
|
|
201
|
+
return []
|
|
202
|
+
return get_all_files(self.conn)
|
|
203
|
+
|
|
204
|
+
def get_file_data(self, file_path: Path) -> dict[str, Any]:
|
|
205
|
+
if not self.conn:
|
|
206
|
+
return {}
|
|
207
|
+
file_id = get_file_id(self.conn, file_path)
|
|
208
|
+
if not file_id:
|
|
209
|
+
return {}
|
|
210
|
+
return {"imports": get_file_imports(self.conn, file_id), "symbols": get_file_symbols(self.conn, file_id)}
|
|
211
|
+
|
|
212
|
+
def get_all_models(self) -> list[dict]:
|
|
213
|
+
if not self.conn:
|
|
214
|
+
return []
|
|
215
|
+
return get_models(self.conn)
|
|
216
|
+
|
|
217
|
+
def get_imports_map(self, files: list[Path]) -> dict[str, list[str]]:
|
|
218
|
+
if not self.conn:
|
|
219
|
+
return {}
|
|
220
|
+
return get_imports_map_for_paths(self.conn, files)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def get_cache(project_root: Path) -> CacheManager:
|
|
224
|
+
return CacheManager(project_root)
|