srcodex 0.2.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.
- srcodex/__init__.py +0 -0
- srcodex/backend/__init__.py +0 -0
- srcodex/backend/chat.py +79 -0
- srcodex/backend/main.py +98 -0
- srcodex/backend/services/__init__.py +0 -0
- srcodex/backend/services/claude_service.py +754 -0
- srcodex/backend/services/config_loader.py +113 -0
- srcodex/backend/services/file_access_tools.py +279 -0
- srcodex/backend/services/file_tree.py +480 -0
- srcodex/backend/services/graph_tools.py +874 -0
- srcodex/backend/services/logger_setup.py +91 -0
- srcodex/backend/services/session_manager.py +81 -0
- srcodex/backend/services/status_tracker.py +91 -0
- srcodex/cli.py +255 -0
- srcodex/core/__init__.py +0 -0
- srcodex/core/config.py +113 -0
- srcodex/core/logger.py +23 -0
- srcodex/indexer/__init__.py +0 -0
- srcodex/indexer/cscope_client.py +183 -0
- srcodex/indexer/ctags_compat.py +223 -0
- srcodex/indexer/ctags_parser.py +456 -0
- srcodex/indexer/explorer.py +135 -0
- srcodex/indexer/field_access_analyzer.py +436 -0
- srcodex/indexer/indexer.py +664 -0
- srcodex/indexer/reference_ingestor.py +293 -0
- srcodex/indexer/reference_resolver.py +544 -0
- srcodex/tui/__init__.py +0 -0
- srcodex/tui/app.py +103 -0
- srcodex/tui/app.tcss +24 -0
- srcodex/tui/components/__init__.py +0 -0
- srcodex/tui/components/bars/__init__.py +0 -0
- srcodex/tui/components/bars/chat_header.py +48 -0
- srcodex/tui/components/bars/code_tab_bar.py +157 -0
- srcodex/tui/components/bars/footer_bar.py +128 -0
- srcodex/tui/components/bars/left_tab.py +54 -0
- srcodex/tui/components/logger.py +57 -0
- srcodex/tui/components/panels/__init__.py +0 -0
- srcodex/tui/components/panels/chat_panel.py +523 -0
- srcodex/tui/components/panels/code_panel.py +229 -0
- srcodex/tui/components/panels/side_panel.py +128 -0
- srcodex/tui/components/views/__init__.py +0 -0
- srcodex/tui/components/views/explorer_view.py +20 -0
- srcodex/tui/components/views/search_view.py +148 -0
- srcodex/tui/components/widgets/__init__.py +0 -0
- srcodex/tui/components/widgets/file_browser.py +16 -0
- srcodex/tui/components/widgets/find_box.py +85 -0
- srcodex-0.2.0.dist-info/METADATA +170 -0
- srcodex-0.2.0.dist-info/RECORD +52 -0
- srcodex-0.2.0.dist-info/WHEEL +5 -0
- srcodex-0.2.0.dist-info/entry_points.txt +2 -0
- srcodex-0.2.0.dist-info/licenses/LICENSE +21 -0
- srcodex-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Reference Ingestor - Raw cscope output to database storage
|
|
4
|
+
Ingests cscope query results into raw_references table (untrusted sensor data)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sqlite3
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import List, Tuple
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
|
|
12
|
+
from .cscope_client import CscopeClient
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ReferenceIngestor:
|
|
16
|
+
"""Ingests raw cscope query results into raw_references table"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, db_conn: sqlite3.Connection, source_root: Path, cscope_dir: Path):
|
|
19
|
+
"""
|
|
20
|
+
Initialize reference ingestor
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
db_conn: SQLite database connection (must have raw_references table)
|
|
24
|
+
source_root: Root directory of source code (for path normalization)
|
|
25
|
+
cscope_dir: Directory containing cscope.out
|
|
26
|
+
"""
|
|
27
|
+
self.conn = db_conn
|
|
28
|
+
self.source_root = source_root
|
|
29
|
+
self.cscope_client = CscopeClient(str(cscope_dir))
|
|
30
|
+
|
|
31
|
+
def _normalize_path(self, cscope_path: str) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Normalize cscope output path to canonical rel_posix format.
|
|
34
|
+
Args:
|
|
35
|
+
cscope_path: Path from cscope output (should be rel_posix from source_root)
|
|
36
|
+
Returns:
|
|
37
|
+
Canonical rel_posix path (relative to source_root)
|
|
38
|
+
"""
|
|
39
|
+
path = Path(cscope_path)
|
|
40
|
+
|
|
41
|
+
if path.is_absolute():
|
|
42
|
+
try:
|
|
43
|
+
return path.relative_to(self.source_root).as_posix()
|
|
44
|
+
except ValueError:
|
|
45
|
+
return path.as_posix()
|
|
46
|
+
|
|
47
|
+
# Normalize to POSIX format
|
|
48
|
+
return path.as_posix()
|
|
49
|
+
|
|
50
|
+
def get_all_functions(self) -> List[Tuple[int, str]]:
|
|
51
|
+
"""
|
|
52
|
+
Query all function symbols from database (implementations only, not prototypes)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
List of (symbol_id, function_name) tuples
|
|
56
|
+
|
|
57
|
+
Note: Excludes prototypes to avoid querying cscope multiple times for the same function
|
|
58
|
+
"""
|
|
59
|
+
cursor = self.conn.cursor()
|
|
60
|
+
cursor.execute(
|
|
61
|
+
"SELECT id, name FROM symbols WHERE type = 'function' AND kind_raw = 'function' ORDER BY id"
|
|
62
|
+
)
|
|
63
|
+
return cursor.fetchall()
|
|
64
|
+
|
|
65
|
+
def get_all_headers(self) -> List[Tuple[str, str]]:
|
|
66
|
+
"""
|
|
67
|
+
Query all header files from database
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of (file_path, basename) tuples
|
|
71
|
+
Example: [("power.h", "power.h"), ("common/voltage.h", "voltage.h")]
|
|
72
|
+
"""
|
|
73
|
+
cursor = self.conn.cursor()
|
|
74
|
+
cursor.execute(
|
|
75
|
+
"SELECT path FROM files WHERE path LIKE '%.h' ORDER BY path"
|
|
76
|
+
)
|
|
77
|
+
files = cursor.fetchall()
|
|
78
|
+
|
|
79
|
+
# Return (full_path, basename) tuples for cscope queries
|
|
80
|
+
return [(row['path'], Path(row['path']).name) for row in files]
|
|
81
|
+
|
|
82
|
+
def ingest_callees(self, clear_existing: bool = False) -> int:
|
|
83
|
+
"""
|
|
84
|
+
Ingest callgraph data: query cscope -2 (callees) for all functions
|
|
85
|
+
|
|
86
|
+
For each function in symbols table:
|
|
87
|
+
- Query cscope find_callees(function_name)
|
|
88
|
+
- Store results in raw_references with query_type='callees'
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
clear_existing: If True, delete existing raw_references before ingestion
|
|
92
|
+
Returns:
|
|
93
|
+
Number of raw references inserted
|
|
94
|
+
"""
|
|
95
|
+
if clear_existing:
|
|
96
|
+
print("Clearing existing raw_references...")
|
|
97
|
+
self.conn.execute("DELETE FROM raw_references WHERE query_type = 'callees'")
|
|
98
|
+
self.conn.commit()
|
|
99
|
+
|
|
100
|
+
# Get all functions from symbols table
|
|
101
|
+
functions = self.get_all_functions()
|
|
102
|
+
print(f"Found {len(functions)} functions to query")
|
|
103
|
+
|
|
104
|
+
if not functions:
|
|
105
|
+
print("Warning: No functions found in symbols table")
|
|
106
|
+
return 0
|
|
107
|
+
|
|
108
|
+
# Prepare batch insert data
|
|
109
|
+
raw_refs_batch = []
|
|
110
|
+
|
|
111
|
+
# Query cscope for each function with progress bar
|
|
112
|
+
print("Querying cscope for callees...")
|
|
113
|
+
for symbol_id, function_name in tqdm(functions, desc="Ingesting callees"):
|
|
114
|
+
try:
|
|
115
|
+
# Query cscope: find functions called by this function
|
|
116
|
+
results = self.cscope_client.find_callees(function_name)
|
|
117
|
+
|
|
118
|
+
# Convert each Reference to raw_references row
|
|
119
|
+
for ref in results:
|
|
120
|
+
normalized_path = self._normalize_path(ref.file_path)
|
|
121
|
+
raw_refs_batch.append((
|
|
122
|
+
'callees', # query_type
|
|
123
|
+
function_name, # query_symbol (the function we queried)
|
|
124
|
+
normalized_path, # source_file
|
|
125
|
+
ref.function, # source_function (from cscope output)
|
|
126
|
+
ref.line_number, # line_number
|
|
127
|
+
ref.line_text, # line_text
|
|
128
|
+
))
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
# Don't fail entire ingestion on single query error
|
|
132
|
+
tqdm.write(f"Warning: Failed to query {function_name}: {e}")
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Batch insert with transaction
|
|
136
|
+
if raw_refs_batch:
|
|
137
|
+
print(f"Inserting {len(raw_refs_batch)} raw references...")
|
|
138
|
+
cursor = self.conn.cursor()
|
|
139
|
+
cursor.executemany(
|
|
140
|
+
"""INSERT INTO raw_references
|
|
141
|
+
(query_type, query_symbol, source_file, source_function, line_number, line_text)
|
|
142
|
+
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
143
|
+
raw_refs_batch
|
|
144
|
+
)
|
|
145
|
+
self.conn.commit()
|
|
146
|
+
print(f"Inserted {len(raw_refs_batch)} raw references")
|
|
147
|
+
else:
|
|
148
|
+
print("Warning: No references found")
|
|
149
|
+
|
|
150
|
+
return len(raw_refs_batch)
|
|
151
|
+
|
|
152
|
+
def ingest_callers(self, clear_existing: bool = False) -> int:
|
|
153
|
+
"""
|
|
154
|
+
Ingest reverse callgraph data: query cscope -3 (callers) for all functions
|
|
155
|
+
|
|
156
|
+
For each function in symbols table:
|
|
157
|
+
- Query cscope find_callers(function_name)
|
|
158
|
+
- Store results in raw_references with query_type='callers'
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
clear_existing: If True, delete existing raw_references before ingestion
|
|
162
|
+
Returns:
|
|
163
|
+
Number of raw references inserted
|
|
164
|
+
"""
|
|
165
|
+
if clear_existing:
|
|
166
|
+
print("Clearing existing callers references...")
|
|
167
|
+
self.conn.execute("DELETE FROM raw_references WHERE query_type = 'callers'")
|
|
168
|
+
self.conn.commit()
|
|
169
|
+
|
|
170
|
+
# Get all functions from symbols table
|
|
171
|
+
functions = self.get_all_functions()
|
|
172
|
+
print(f"Found {len(functions)} functions to query for callers")
|
|
173
|
+
|
|
174
|
+
if not functions:
|
|
175
|
+
print("Warning: No functions found in symbols table")
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
# Prepare batch insert data
|
|
179
|
+
raw_refs_batch = []
|
|
180
|
+
|
|
181
|
+
# Query cscope for each function with progress bar
|
|
182
|
+
print("Querying cscope for callers...")
|
|
183
|
+
for symbol_id, function_name in tqdm(functions, desc="Ingesting callers"):
|
|
184
|
+
try:
|
|
185
|
+
# Query cscope: find functions that call this function
|
|
186
|
+
results = self.cscope_client.find_callers(function_name)
|
|
187
|
+
|
|
188
|
+
# Convert each Reference to raw_references row
|
|
189
|
+
for ref in results:
|
|
190
|
+
normalized_path = self._normalize_path(ref.file_path)
|
|
191
|
+
raw_refs_batch.append((
|
|
192
|
+
'callers', # query_type
|
|
193
|
+
function_name, # query_symbol (the function we queried)
|
|
194
|
+
normalized_path, # source_file
|
|
195
|
+
ref.function, # source_function (caller)
|
|
196
|
+
ref.line_number, # line_number
|
|
197
|
+
ref.line_text, # line_text
|
|
198
|
+
))
|
|
199
|
+
|
|
200
|
+
except Exception as e:
|
|
201
|
+
# Don't fail entire ingestion on single query error
|
|
202
|
+
tqdm.write(f"Warning: Failed to query {function_name}: {e}")
|
|
203
|
+
continue
|
|
204
|
+
|
|
205
|
+
# Batch insert with transaction
|
|
206
|
+
if raw_refs_batch:
|
|
207
|
+
print(f"Inserting {len(raw_refs_batch)} raw references...")
|
|
208
|
+
cursor = self.conn.cursor()
|
|
209
|
+
cursor.executemany(
|
|
210
|
+
"""INSERT INTO raw_references
|
|
211
|
+
(query_type, query_symbol, source_file, source_function, line_number, line_text)
|
|
212
|
+
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
213
|
+
raw_refs_batch
|
|
214
|
+
)
|
|
215
|
+
self.conn.commit()
|
|
216
|
+
print(f"Inserted {len(raw_refs_batch)} callers references")
|
|
217
|
+
else:
|
|
218
|
+
print("Warning: No callers references found")
|
|
219
|
+
|
|
220
|
+
return len(raw_refs_batch)
|
|
221
|
+
|
|
222
|
+
def ingest_includes(self, clear_existing: bool = False) -> int:
|
|
223
|
+
"""
|
|
224
|
+
Ingest include graph data: query cscope -8 (files including) for all headers
|
|
225
|
+
|
|
226
|
+
For each .h file in files table:
|
|
227
|
+
- Query cscope find_files_including(header_name)
|
|
228
|
+
- Store results in raw_references with query_type='includes'
|
|
229
|
+
|
|
230
|
+
Note: source_function will be "<global>" since includes are file-level
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
clear_existing: If True, delete existing includes references before ingestion
|
|
234
|
+
Returns:
|
|
235
|
+
Number of raw references inserted
|
|
236
|
+
"""
|
|
237
|
+
if clear_existing:
|
|
238
|
+
print("Clearing existing includes references...")
|
|
239
|
+
self.conn.execute("DELETE FROM raw_references WHERE query_type = 'includes'")
|
|
240
|
+
self.conn.commit()
|
|
241
|
+
|
|
242
|
+
# Get all header files from files table
|
|
243
|
+
headers = self.get_all_headers()
|
|
244
|
+
print(f"Found {len(headers)} header files to query for includes")
|
|
245
|
+
|
|
246
|
+
if not headers:
|
|
247
|
+
print("Warning: No header files found in files table")
|
|
248
|
+
return 0
|
|
249
|
+
|
|
250
|
+
# Prepare batch insert data
|
|
251
|
+
raw_refs_batch = []
|
|
252
|
+
|
|
253
|
+
# Query cscope for each header with progress bar
|
|
254
|
+
print("Querying cscope for includes...")
|
|
255
|
+
for full_path, basename in tqdm(headers, desc="Ingesting includes"):
|
|
256
|
+
try:
|
|
257
|
+
# Query cscope: find files that include this header
|
|
258
|
+
# NOTE: cscope -8 queries by basename, not full path
|
|
259
|
+
results = self.cscope_client.find_files_including(basename)
|
|
260
|
+
|
|
261
|
+
# Convert each Reference to raw_references row
|
|
262
|
+
for ref in results:
|
|
263
|
+
normalized_path = self._normalize_path(ref.file_path)
|
|
264
|
+
raw_refs_batch.append((
|
|
265
|
+
'includes', # query_type
|
|
266
|
+
basename, # query_symbol (the header we queried)
|
|
267
|
+
normalized_path, # source_file (file that includes the header)
|
|
268
|
+
'<global>', # source_function (includes are file-level)
|
|
269
|
+
ref.line_number, # line_number
|
|
270
|
+
ref.line_text, # line_text (the #include directive)
|
|
271
|
+
))
|
|
272
|
+
|
|
273
|
+
except Exception as e:
|
|
274
|
+
# Don't fail entire ingestion on single query error
|
|
275
|
+
tqdm.write(f"Warning: Failed to query {basename}: {e}")
|
|
276
|
+
continue
|
|
277
|
+
|
|
278
|
+
# Batch insert with transaction
|
|
279
|
+
if raw_refs_batch:
|
|
280
|
+
print(f"Inserting {len(raw_refs_batch)} raw references...")
|
|
281
|
+
cursor = self.conn.cursor()
|
|
282
|
+
cursor.executemany(
|
|
283
|
+
"""INSERT INTO raw_references
|
|
284
|
+
(query_type, query_symbol, source_file, source_function, line_number, line_text)
|
|
285
|
+
VALUES (?, ?, ?, ?, ?, ?)""",
|
|
286
|
+
raw_refs_batch
|
|
287
|
+
)
|
|
288
|
+
self.conn.commit()
|
|
289
|
+
print(f"Inserted {len(raw_refs_batch)} includes references")
|
|
290
|
+
else:
|
|
291
|
+
print("Warning: No includes references found")
|
|
292
|
+
|
|
293
|
+
return len(raw_refs_batch)
|