jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.6__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +243 -139
- jarvis/jarvis_agent/agent_manager.py +5 -10
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +265 -15
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +113 -98
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +6 -12
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +77 -14
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +12 -21
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/task_analyzer.py +26 -4
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/web_server.py +55 -20
- jarvis/jarvis_c2rust/__init__.py +5 -5
- jarvis/jarvis_c2rust/cli.py +461 -499
- jarvis/jarvis_c2rust/collector.py +45 -53
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +264 -132
- jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +1592 -395
- jarvis/jarvis_c2rust/transpiler.py +1722 -1064
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +2 -3
- jarvis/jarvis_code_agent/code_agent.py +394 -320
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
- jarvis/jarvis_code_agent/lint.py +258 -27
- jarvis/jarvis_code_agent/utils.py +0 -1
- jarvis/jarvis_code_analysis/code_review.py +19 -24
- jarvis/jarvis_data/config_schema.json +53 -26
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +44 -49
- jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
- jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +79 -61
- jarvis/jarvis_multi_agent/main.py +3 -7
- jarvis/jarvis_platform/base.py +469 -199
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +65 -27
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +31 -42
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +49 -51
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +220 -3520
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/cli.py +29 -6
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +83 -4
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +7 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +7 -7
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +20 -23
- jarvis/jarvis_tools/edit_file.py +1066 -0
- jarvis/jarvis_tools/execute_script.py +42 -21
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +11 -20
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1525 -87
- jarvis/jarvis_tools/read_symbols.py +2 -3
- jarvis/jarvis_tools/read_webpage.py +7 -10
- jarvis/jarvis_tools/registry.py +370 -181
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +105 -0
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +3 -7
- jarvis/jarvis_tools/sub_agent.py +17 -6
- jarvis/jarvis_tools/sub_code_agent.py +14 -16
- jarvis/jarvis_tools/virtual_tty.py +54 -32
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +98 -63
- jarvis/jarvis_utils/embedding.py +5 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +81 -67
- jarvis/jarvis_utils/input.py +24 -49
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +33 -35
- jarvis/jarvis_utils/utils.py +245 -202
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- jarvis/jarvis_agent/edit_file_handler.py +0 -584
- jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
- jarvis/jarvis_agent/task_planner.py +0 -496
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
|
-
from typing import List, Optional, Set
|
|
4
|
+
from typing import Any, List, Optional, Set
|
|
5
5
|
|
|
6
6
|
from .dependency_analyzer import DependencyGraph
|
|
7
7
|
from .file_ignore import filter_walk_dirs
|
|
@@ -36,7 +36,9 @@ class ContextManager:
|
|
|
36
36
|
|
|
37
37
|
def __init__(self, project_root: str):
|
|
38
38
|
self.project_root = project_root
|
|
39
|
-
|
|
39
|
+
# Create cache directory path relative to project root
|
|
40
|
+
cache_dir = os.path.join(project_root, ".jarvis", "symbol_cache")
|
|
41
|
+
self.symbol_table = SymbolTable(cache_dir)
|
|
40
42
|
self.dependency_graph = DependencyGraph()
|
|
41
43
|
self._file_cache: dict[str, str] = {} # Cache file contents
|
|
42
44
|
|
|
@@ -94,6 +96,10 @@ class ContextManager:
|
|
|
94
96
|
"""
|
|
95
97
|
references: List[Reference] = []
|
|
96
98
|
|
|
99
|
+
# Check if file is stale and update if needed
|
|
100
|
+
if file_path and self.symbol_table.is_file_stale(file_path):
|
|
101
|
+
self._refresh_file_symbols(file_path)
|
|
102
|
+
|
|
97
103
|
# Find symbol definitions
|
|
98
104
|
symbols = self.symbol_table.find_symbol(symbol_name, file_path)
|
|
99
105
|
if not symbols:
|
|
@@ -114,6 +120,10 @@ class ContextManager:
|
|
|
114
120
|
|
|
115
121
|
# Search for references in each file
|
|
116
122
|
for file_path_to_search in search_files:
|
|
123
|
+
# Check if file is stale and update if needed
|
|
124
|
+
if self.symbol_table.is_file_stale(file_path_to_search):
|
|
125
|
+
self._refresh_file_symbols(file_path_to_search)
|
|
126
|
+
|
|
117
127
|
content = self._get_file_content(file_path_to_search)
|
|
118
128
|
if not content:
|
|
119
129
|
continue
|
|
@@ -158,6 +168,10 @@ class ContextManager:
|
|
|
158
168
|
Returns:
|
|
159
169
|
Symbol object if found, None otherwise
|
|
160
170
|
"""
|
|
171
|
+
# Check if file is stale and update if needed
|
|
172
|
+
if file_path and self.symbol_table.is_file_stale(file_path):
|
|
173
|
+
self._refresh_file_symbols(file_path)
|
|
174
|
+
|
|
161
175
|
symbols = self.symbol_table.find_symbol(symbol_name, file_path)
|
|
162
176
|
if symbols:
|
|
163
177
|
# Return the first definition (could be enhanced to find the most relevant one)
|
|
@@ -186,6 +200,13 @@ class ContextManager:
|
|
|
186
200
|
symbols = extractor.extract_symbols(file_path, content)
|
|
187
201
|
for symbol in symbols:
|
|
188
202
|
self.symbol_table.add_symbol(symbol)
|
|
203
|
+
|
|
204
|
+
# Update file modification time after extracting symbols
|
|
205
|
+
if os.path.exists(file_path):
|
|
206
|
+
try:
|
|
207
|
+
self.symbol_table._file_mtimes[file_path] = os.path.getmtime(file_path)
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
189
210
|
|
|
190
211
|
# 5. Analyze dependencies
|
|
191
212
|
analyzer = get_dependency_analyzer(language)
|
|
@@ -196,6 +217,26 @@ class ContextManager:
|
|
|
196
217
|
dep_path = self._resolve_dependency_path(file_path, dep.from_module)
|
|
197
218
|
if dep_path:
|
|
198
219
|
self.dependency_graph.add_dependency(file_path, dep_path)
|
|
220
|
+
|
|
221
|
+
# 6. Save updated symbols to cache
|
|
222
|
+
self.symbol_table.save_cache()
|
|
223
|
+
|
|
224
|
+
def _refresh_file_symbols(self, file_path: str):
|
|
225
|
+
"""Refresh symbols for a file that has been modified externally.
|
|
226
|
+
|
|
227
|
+
This method is called when a file is detected to be stale (modified
|
|
228
|
+
outside of Jarvis's control).
|
|
229
|
+
"""
|
|
230
|
+
if not os.path.exists(file_path):
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
235
|
+
content = f.read()
|
|
236
|
+
self.update_context_for_file(file_path, content)
|
|
237
|
+
except Exception:
|
|
238
|
+
# If we can't read the file, skip refresh
|
|
239
|
+
pass
|
|
199
240
|
|
|
200
241
|
def _get_file_content(self, file_path: str) -> Optional[str]:
|
|
201
242
|
"""Get file content, using cache if available."""
|
|
@@ -215,6 +256,9 @@ class ContextManager:
|
|
|
215
256
|
|
|
216
257
|
def _find_current_scope(self, file_path: str, line_num: int) -> Optional[Symbol]:
|
|
217
258
|
"""Find the function or class that contains the given line."""
|
|
259
|
+
# Check if file is stale and update if needed
|
|
260
|
+
if self.symbol_table.is_file_stale(file_path):
|
|
261
|
+
self._refresh_file_symbols(file_path)
|
|
218
262
|
symbols = self.symbol_table.get_file_symbols(file_path)
|
|
219
263
|
|
|
220
264
|
# Find the innermost scope containing the line
|
|
@@ -232,7 +276,17 @@ class ContextManager:
|
|
|
232
276
|
return current_scope
|
|
233
277
|
|
|
234
278
|
def _find_used_symbols(self, file_path: str, content: str, line_start: int, line_end: int) -> List[Symbol]:
|
|
235
|
-
"""Find symbols used in the specified line range.
|
|
279
|
+
"""Find symbols used in the specified line range.
|
|
280
|
+
|
|
281
|
+
改进版本:
|
|
282
|
+
1. 区分定义和调用:检查符号是否在当前行范围内定义
|
|
283
|
+
2. 获取定义位置:优先使用 LSP,如果不支持则使用 tree-sitter
|
|
284
|
+
3. 为每个使用的符号添加定义位置信息
|
|
285
|
+
"""
|
|
286
|
+
# Check if file is stale and update if needed
|
|
287
|
+
if self.symbol_table.is_file_stale(file_path):
|
|
288
|
+
self._refresh_file_symbols(file_path)
|
|
289
|
+
|
|
236
290
|
# Extract the code in the range
|
|
237
291
|
lines = content.split('\n')
|
|
238
292
|
region_content = '\n'.join(lines[line_start-1:line_end])
|
|
@@ -240,19 +294,250 @@ class ContextManager:
|
|
|
240
294
|
used_symbols: List[Symbol] = []
|
|
241
295
|
all_symbols = self.symbol_table.get_file_symbols(file_path)
|
|
242
296
|
|
|
297
|
+
# 尝试获取 LSP 客户端(优先使用)
|
|
298
|
+
lsp_client = None
|
|
299
|
+
try:
|
|
300
|
+
from jarvis.jarvis_tools.lsp_client import LSPClientTool
|
|
301
|
+
lsp_client = LSPClientTool.get_client_for_file(file_path, self.project_root)
|
|
302
|
+
except Exception:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
# 尝试获取 tree-sitter 提取器(作为后备)
|
|
306
|
+
treesitter_extractor = None
|
|
307
|
+
try:
|
|
308
|
+
from jarvis.jarvis_code_agent.code_analyzer.language_support import (
|
|
309
|
+
detect_language,
|
|
310
|
+
get_symbol_extractor,
|
|
311
|
+
)
|
|
312
|
+
language = detect_language(file_path)
|
|
313
|
+
if language:
|
|
314
|
+
treesitter_extractor = get_symbol_extractor(language)
|
|
315
|
+
except Exception:
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
# 用于跟踪已处理的符号,避免重复
|
|
319
|
+
processed_symbols = {} # {symbol_name: (symbol, is_definition, definition_location)}
|
|
320
|
+
|
|
243
321
|
# Simple pattern matching to find symbol usage
|
|
244
322
|
for symbol in all_symbols:
|
|
245
323
|
if symbol.kind == 'import':
|
|
246
324
|
continue
|
|
247
325
|
|
|
248
326
|
pattern = r'\b' + re.escape(symbol.name) + r'\b'
|
|
249
|
-
|
|
250
|
-
|
|
327
|
+
matches = list(re.finditer(pattern, region_content))
|
|
328
|
+
if not matches:
|
|
329
|
+
continue
|
|
330
|
+
|
|
331
|
+
# 检查符号是否在当前行范围内定义
|
|
332
|
+
is_definition_in_range = (
|
|
333
|
+
symbol.file_path == file_path and
|
|
334
|
+
symbol.line_start >= line_start and
|
|
335
|
+
symbol.line_start <= line_end
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# 检查是否有调用(不在定义行的使用)
|
|
339
|
+
has_calls = False
|
|
340
|
+
call_line = None
|
|
341
|
+
call_column = None
|
|
342
|
+
for match in matches:
|
|
343
|
+
match_start = match.start()
|
|
344
|
+
match_line_in_region = region_content[:match_start].count('\n') + 1
|
|
345
|
+
match_line = line_start + match_line_in_region - 1
|
|
346
|
+
|
|
347
|
+
# 如果使用位置不在定义行,则认为是调用
|
|
348
|
+
if not is_definition_in_range or match_line != symbol.line_start:
|
|
349
|
+
has_calls = True
|
|
350
|
+
call_line = match_line
|
|
351
|
+
# 计算列号
|
|
352
|
+
line_start_pos = region_content[:match_start].rfind('\n') + 1
|
|
353
|
+
call_column = match_start - line_start_pos
|
|
354
|
+
break
|
|
355
|
+
|
|
356
|
+
# 处理定义
|
|
357
|
+
if is_definition_in_range:
|
|
358
|
+
symbol.is_definition = True
|
|
359
|
+
if symbol.name not in processed_symbols:
|
|
360
|
+
processed_symbols[symbol.name] = (symbol, True, None)
|
|
361
|
+
|
|
362
|
+
# 处理调用
|
|
363
|
+
if has_calls or not is_definition_in_range:
|
|
364
|
+
# 创建或更新引用符号
|
|
365
|
+
if symbol.name in processed_symbols:
|
|
366
|
+
existing_symbol, existing_is_def, existing_def_loc = processed_symbols[symbol.name]
|
|
367
|
+
# 如果已有定义,跳过;否则更新定义位置
|
|
368
|
+
if not existing_is_def and not existing_def_loc:
|
|
369
|
+
# 尝试获取定义位置
|
|
370
|
+
definition_location = self._find_definition_location(
|
|
371
|
+
symbol.name,
|
|
372
|
+
file_path,
|
|
373
|
+
call_line or line_start,
|
|
374
|
+
call_column or 0,
|
|
375
|
+
lsp_client,
|
|
376
|
+
treesitter_extractor,
|
|
377
|
+
content,
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
if definition_location:
|
|
381
|
+
processed_symbols[symbol.name] = (existing_symbol, False, definition_location)
|
|
382
|
+
else:
|
|
383
|
+
# 从符号表中查找
|
|
384
|
+
definition_symbols = self.symbol_table.find_symbol(symbol.name)
|
|
385
|
+
if definition_symbols:
|
|
386
|
+
def_symbol = definition_symbols[0]
|
|
387
|
+
definition_location = Symbol(
|
|
388
|
+
name=def_symbol.name,
|
|
389
|
+
kind=def_symbol.kind,
|
|
390
|
+
file_path=def_symbol.file_path,
|
|
391
|
+
line_start=def_symbol.line_start,
|
|
392
|
+
line_end=def_symbol.line_end,
|
|
393
|
+
signature=def_symbol.signature,
|
|
394
|
+
)
|
|
395
|
+
processed_symbols[symbol.name] = (existing_symbol, False, definition_location)
|
|
396
|
+
else:
|
|
397
|
+
# 创建新的引用符号
|
|
398
|
+
reference_symbol = Symbol(
|
|
399
|
+
name=symbol.name,
|
|
400
|
+
kind=symbol.kind,
|
|
401
|
+
file_path=file_path,
|
|
402
|
+
line_start=call_line or line_start,
|
|
403
|
+
line_end=call_line or line_start,
|
|
404
|
+
signature=symbol.signature,
|
|
405
|
+
docstring=symbol.docstring,
|
|
406
|
+
parent=symbol.parent,
|
|
407
|
+
is_definition=False,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# 尝试获取定义位置
|
|
411
|
+
definition_location = self._find_definition_location(
|
|
412
|
+
symbol.name,
|
|
413
|
+
file_path,
|
|
414
|
+
call_line or line_start,
|
|
415
|
+
call_column or 0,
|
|
416
|
+
lsp_client,
|
|
417
|
+
treesitter_extractor,
|
|
418
|
+
content,
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
if definition_location:
|
|
422
|
+
reference_symbol.definition_location = definition_location
|
|
423
|
+
else:
|
|
424
|
+
# 从符号表中查找
|
|
425
|
+
definition_symbols = self.symbol_table.find_symbol(symbol.name)
|
|
426
|
+
if definition_symbols:
|
|
427
|
+
def_symbol = definition_symbols[0]
|
|
428
|
+
reference_symbol.definition_location = Symbol(
|
|
429
|
+
name=def_symbol.name,
|
|
430
|
+
kind=def_symbol.kind,
|
|
431
|
+
file_path=def_symbol.file_path,
|
|
432
|
+
line_start=def_symbol.line_start,
|
|
433
|
+
line_end=def_symbol.line_end,
|
|
434
|
+
signature=def_symbol.signature,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
processed_symbols[symbol.name] = (reference_symbol, False, reference_symbol.definition_location)
|
|
438
|
+
|
|
439
|
+
# 将处理后的符号添加到结果列表
|
|
440
|
+
for symbol, is_def, def_loc in processed_symbols.values():
|
|
441
|
+
if is_def:
|
|
442
|
+
symbol.is_definition = True
|
|
443
|
+
if def_loc:
|
|
444
|
+
symbol.definition_location = def_loc
|
|
445
|
+
used_symbols.append(symbol)
|
|
251
446
|
|
|
252
447
|
return used_symbols
|
|
448
|
+
|
|
449
|
+
def _find_definition_location(
|
|
450
|
+
self,
|
|
451
|
+
symbol_name: str,
|
|
452
|
+
file_path: str,
|
|
453
|
+
line: int,
|
|
454
|
+
column: int,
|
|
455
|
+
lsp_client: Optional[Any],
|
|
456
|
+
treesitter_extractor: Optional[Any],
|
|
457
|
+
content: str,
|
|
458
|
+
) -> Optional[Symbol]:
|
|
459
|
+
"""查找符号的定义位置。
|
|
460
|
+
|
|
461
|
+
优先使用 LSP,如果不支持则使用 tree-sitter。
|
|
462
|
+
|
|
463
|
+
Args:
|
|
464
|
+
symbol_name: 符号名称
|
|
465
|
+
file_path: 文件路径
|
|
466
|
+
line: 行号(1-based)
|
|
467
|
+
column: 列号(0-based)
|
|
468
|
+
lsp_client: LSP 客户端(可选)
|
|
469
|
+
treesitter_extractor: tree-sitter 提取器(可选)
|
|
470
|
+
content: 文件内容
|
|
471
|
+
|
|
472
|
+
Returns:
|
|
473
|
+
定义位置的 Symbol 对象,如果找不到则返回 None
|
|
474
|
+
"""
|
|
475
|
+
# 优先使用 LSP
|
|
476
|
+
if lsp_client:
|
|
477
|
+
try:
|
|
478
|
+
# LSP 使用 0-based 行号
|
|
479
|
+
definition = lsp_client.get_definition(file_path, line - 1, column)
|
|
480
|
+
if definition:
|
|
481
|
+
# 处理 LSP 返回的定义(可能是单个对象或列表)
|
|
482
|
+
if isinstance(definition, list):
|
|
483
|
+
if definition:
|
|
484
|
+
definition = definition[0]
|
|
485
|
+
else:
|
|
486
|
+
return None
|
|
487
|
+
|
|
488
|
+
# 解析 LSP 返回的定义位置
|
|
489
|
+
uri = definition.get("uri", "")
|
|
490
|
+
if uri.startswith("file://"):
|
|
491
|
+
def_file_path = uri[7:] # 移除 "file://" 前缀
|
|
492
|
+
else:
|
|
493
|
+
def_file_path = uri
|
|
494
|
+
|
|
495
|
+
range_info = definition.get("range", {})
|
|
496
|
+
start = range_info.get("start", {})
|
|
497
|
+
end = range_info.get("end", {})
|
|
498
|
+
|
|
499
|
+
# LSP 使用 0-based 行号,转换为 1-based
|
|
500
|
+
def_line_start = start.get("line", 0) + 1
|
|
501
|
+
def_line_end = end.get("line", 0) + 1
|
|
502
|
+
|
|
503
|
+
return Symbol(
|
|
504
|
+
name=symbol_name,
|
|
505
|
+
kind="", # LSP 可能不提供类型信息
|
|
506
|
+
file_path=def_file_path,
|
|
507
|
+
line_start=def_line_start,
|
|
508
|
+
line_end=def_line_end,
|
|
509
|
+
)
|
|
510
|
+
except Exception:
|
|
511
|
+
# LSP 失败,继续尝试 tree-sitter
|
|
512
|
+
pass
|
|
513
|
+
|
|
514
|
+
# 后备:使用 tree-sitter
|
|
515
|
+
if treesitter_extractor:
|
|
516
|
+
try:
|
|
517
|
+
# 从符号表中查找定义(tree-sitter 已经提取了符号)
|
|
518
|
+
definition_symbols = self.symbol_table.find_symbol(symbol_name)
|
|
519
|
+
if definition_symbols:
|
|
520
|
+
# 选择第一个定义(可以改进为选择最相关的)
|
|
521
|
+
def_symbol = definition_symbols[0]
|
|
522
|
+
return Symbol(
|
|
523
|
+
name=def_symbol.name,
|
|
524
|
+
kind=def_symbol.kind,
|
|
525
|
+
file_path=def_symbol.file_path,
|
|
526
|
+
line_start=def_symbol.line_start,
|
|
527
|
+
line_end=def_symbol.line_end,
|
|
528
|
+
signature=def_symbol.signature,
|
|
529
|
+
)
|
|
530
|
+
except Exception:
|
|
531
|
+
pass
|
|
532
|
+
|
|
533
|
+
return None
|
|
253
534
|
|
|
254
535
|
def _find_imported_symbols(self, file_path: str) -> List[Symbol]:
|
|
255
536
|
"""Find all imported symbols in a file."""
|
|
537
|
+
# Check if file is stale and update if needed
|
|
538
|
+
if self.symbol_table.is_file_stale(file_path):
|
|
539
|
+
self._refresh_file_symbols(file_path)
|
|
540
|
+
|
|
256
541
|
symbols = self.symbol_table.get_file_symbols(file_path)
|
|
257
542
|
return [s for s in symbols if s.kind == 'import']
|
|
258
543
|
|
|
@@ -52,6 +52,27 @@ try:
|
|
|
52
52
|
except (ImportError, RuntimeError):
|
|
53
53
|
pass # 静默失败,tree-sitter可能不可用
|
|
54
54
|
|
|
55
|
+
# JavaScript语言支持(可选,需要tree-sitter)
|
|
56
|
+
try:
|
|
57
|
+
from .languages import JavaScriptLanguageSupport
|
|
58
|
+
register_language(JavaScriptLanguageSupport())
|
|
59
|
+
except (ImportError, RuntimeError):
|
|
60
|
+
pass # 静默失败,tree-sitter可能不可用
|
|
61
|
+
|
|
62
|
+
# TypeScript语言支持(可选,需要tree-sitter)
|
|
63
|
+
try:
|
|
64
|
+
from .languages import TypeScriptLanguageSupport
|
|
65
|
+
register_language(TypeScriptLanguageSupport())
|
|
66
|
+
except (ImportError, RuntimeError):
|
|
67
|
+
pass # 静默失败,tree-sitter可能不可用
|
|
68
|
+
|
|
69
|
+
# Java语言支持(可选,需要tree-sitter)
|
|
70
|
+
try:
|
|
71
|
+
from .languages import JavaLanguageSupport
|
|
72
|
+
register_language(JavaLanguageSupport())
|
|
73
|
+
except (ImportError, RuntimeError):
|
|
74
|
+
pass # 静默失败,tree-sitter可能不可用
|
|
75
|
+
|
|
55
76
|
|
|
56
77
|
def detect_language(file_path: str) -> Optional[str]:
|
|
57
78
|
"""检测文件的编程语言。
|
|
@@ -12,20 +12,38 @@ __all__ = ['PythonLanguageSupport']
|
|
|
12
12
|
|
|
13
13
|
# 尝试导入tree-sitter相关的语言支持
|
|
14
14
|
try:
|
|
15
|
-
from .rust_language import RustLanguageSupport
|
|
15
|
+
from .rust_language import RustLanguageSupport # noqa: F401
|
|
16
16
|
__all__.append('RustLanguageSupport')
|
|
17
17
|
except (ImportError, RuntimeError):
|
|
18
18
|
pass
|
|
19
19
|
|
|
20
20
|
try:
|
|
21
|
-
from .go_language import GoLanguageSupport
|
|
21
|
+
from .go_language import GoLanguageSupport # noqa: F401
|
|
22
22
|
__all__.append('GoLanguageSupport')
|
|
23
23
|
except (ImportError, RuntimeError):
|
|
24
24
|
pass
|
|
25
25
|
|
|
26
26
|
try:
|
|
27
|
-
from .c_cpp_language import CLanguageSupport, CppLanguageSupport
|
|
27
|
+
from .c_cpp_language import CLanguageSupport, CppLanguageSupport # noqa: F401
|
|
28
28
|
__all__.extend(['CLanguageSupport', 'CppLanguageSupport'])
|
|
29
29
|
except (ImportError, RuntimeError):
|
|
30
30
|
pass
|
|
31
31
|
|
|
32
|
+
try:
|
|
33
|
+
from .javascript_language import JavaScriptLanguageSupport # noqa: F401
|
|
34
|
+
__all__.append('JavaScriptLanguageSupport')
|
|
35
|
+
except (ImportError, RuntimeError):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from .typescript_language import TypeScriptLanguageSupport # noqa: F401
|
|
40
|
+
__all__.append('TypeScriptLanguageSupport')
|
|
41
|
+
except (ImportError, RuntimeError):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
from .java_language import JavaLanguageSupport # noqa: F401
|
|
46
|
+
__all__.append('JavaLanguageSupport')
|
|
47
|
+
except (ImportError, RuntimeError):
|
|
48
|
+
pass
|
|
49
|
+
|
|
@@ -15,7 +15,29 @@ from ..tree_sitter_extractor import TreeSitterExtractor
|
|
|
15
15
|
|
|
16
16
|
# --- C/C++ Symbol Query ---
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
# C语言查询(不包含class_specifier,因为C不支持class)
|
|
19
|
+
C_SYMBOL_QUERY = """
|
|
20
|
+
(function_declarator
|
|
21
|
+
declarator: (identifier) @function.name)
|
|
22
|
+
|
|
23
|
+
(struct_specifier
|
|
24
|
+
name: (type_identifier) @struct.name)
|
|
25
|
+
|
|
26
|
+
(union_specifier
|
|
27
|
+
name: (type_identifier) @union.name)
|
|
28
|
+
|
|
29
|
+
(enum_specifier
|
|
30
|
+
name: (type_identifier) @enum.name)
|
|
31
|
+
|
|
32
|
+
(preproc_def
|
|
33
|
+
name: (identifier) @macro.name)
|
|
34
|
+
|
|
35
|
+
(type_definition
|
|
36
|
+
declarator: (type_identifier) @typedef.name)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# C++语言查询(包含class_specifier)
|
|
40
|
+
CPP_SYMBOL_QUERY = """
|
|
19
41
|
(function_declarator
|
|
20
42
|
declarator: (identifier) @function.name)
|
|
21
43
|
|
|
@@ -30,6 +52,17 @@ C_CPP_SYMBOL_QUERY = """
|
|
|
30
52
|
|
|
31
53
|
(enum_specifier
|
|
32
54
|
name: (type_identifier) @enum.name)
|
|
55
|
+
|
|
56
|
+
(namespace_definition
|
|
57
|
+
name: (identifier)? @namespace.name)
|
|
58
|
+
|
|
59
|
+
(preproc_def
|
|
60
|
+
name: (identifier) @macro.name)
|
|
61
|
+
|
|
62
|
+
(type_definition
|
|
63
|
+
declarator: (type_identifier) @typedef.name)
|
|
64
|
+
|
|
65
|
+
(template_declaration) @template
|
|
33
66
|
"""
|
|
34
67
|
|
|
35
68
|
# --- C/C++ Language Setup ---
|
|
@@ -55,7 +88,7 @@ class CSymbolExtractor(TreeSitterExtractor):
|
|
|
55
88
|
def __init__(self):
|
|
56
89
|
if not C_LANGUAGE:
|
|
57
90
|
raise RuntimeError("C tree-sitter grammar not available.")
|
|
58
|
-
super().__init__(C_LANGUAGE,
|
|
91
|
+
super().__init__(C_LANGUAGE, C_SYMBOL_QUERY)
|
|
59
92
|
|
|
60
93
|
def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
|
|
61
94
|
kind_map = {
|
|
@@ -63,6 +96,8 @@ class CSymbolExtractor(TreeSitterExtractor):
|
|
|
63
96
|
"struct.name": "struct",
|
|
64
97
|
"union.name": "union",
|
|
65
98
|
"enum.name": "enum",
|
|
99
|
+
"macro.name": "macro",
|
|
100
|
+
"typedef.name": "typedef",
|
|
66
101
|
}
|
|
67
102
|
symbol_kind = kind_map.get(name)
|
|
68
103
|
if not symbol_kind:
|
|
@@ -83,7 +118,7 @@ class CppSymbolExtractor(TreeSitterExtractor):
|
|
|
83
118
|
def __init__(self):
|
|
84
119
|
if not CPP_LANGUAGE:
|
|
85
120
|
raise RuntimeError("C++ tree-sitter grammar not available.")
|
|
86
|
-
super().__init__(CPP_LANGUAGE,
|
|
121
|
+
super().__init__(CPP_LANGUAGE, CPP_SYMBOL_QUERY)
|
|
87
122
|
|
|
88
123
|
def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
|
|
89
124
|
kind_map = {
|
|
@@ -92,13 +127,46 @@ class CppSymbolExtractor(TreeSitterExtractor):
|
|
|
92
127
|
"class.name": "class",
|
|
93
128
|
"union.name": "union",
|
|
94
129
|
"enum.name": "enum",
|
|
130
|
+
"namespace.name": "namespace",
|
|
131
|
+
"macro.name": "macro",
|
|
132
|
+
"typedef.name": "typedef",
|
|
133
|
+
"template": "template",
|
|
95
134
|
}
|
|
96
135
|
symbol_kind = kind_map.get(name)
|
|
97
136
|
if not symbol_kind:
|
|
98
137
|
return None
|
|
99
138
|
|
|
139
|
+
# For anonymous namespaces, use a generated name
|
|
140
|
+
if name == "namespace.name":
|
|
141
|
+
symbol_name = node.text.decode('utf8') if node.text else "<anonymous_namespace>"
|
|
142
|
+
elif name == "template":
|
|
143
|
+
# For template declarations, extract the template name or use a generic name
|
|
144
|
+
# Try to find the function/class name after template
|
|
145
|
+
template_text = node.text.decode('utf8').strip()
|
|
146
|
+
# Extract template parameters and the following declaration
|
|
147
|
+
# This is a simplified extraction - in practice, you might want more sophisticated parsing
|
|
148
|
+
if 'template' in template_text:
|
|
149
|
+
# Try to extract the name after template<...>
|
|
150
|
+
parts = template_text.split('>', 1)
|
|
151
|
+
if len(parts) > 1:
|
|
152
|
+
# Look for function/class name in the second part
|
|
153
|
+
match = re.search(r'\b(function|class|struct)\s+(\w+)', parts[1])
|
|
154
|
+
if match:
|
|
155
|
+
symbol_name = f"template_{match.group(2)}"
|
|
156
|
+
else:
|
|
157
|
+
symbol_name = "template"
|
|
158
|
+
else:
|
|
159
|
+
symbol_name = "template"
|
|
160
|
+
else:
|
|
161
|
+
symbol_name = "template"
|
|
162
|
+
else:
|
|
163
|
+
symbol_name = node.text.decode('utf8')
|
|
164
|
+
|
|
165
|
+
if not symbol_name:
|
|
166
|
+
return None
|
|
167
|
+
|
|
100
168
|
return Symbol(
|
|
101
|
-
name=
|
|
169
|
+
name=symbol_name,
|
|
102
170
|
kind=symbol_kind,
|
|
103
171
|
file_path=file_path,
|
|
104
172
|
line_start=node.start_point[0] + 1,
|
|
@@ -26,8 +26,16 @@ GO_SYMBOL_QUERY = """
|
|
|
26
26
|
(type_spec
|
|
27
27
|
name: (type_identifier) @type.name))
|
|
28
28
|
|
|
29
|
-
(
|
|
30
|
-
|
|
29
|
+
(type_declaration
|
|
30
|
+
(type_spec
|
|
31
|
+
name: (type_identifier) @interface.name
|
|
32
|
+
type: (interface_type)))
|
|
33
|
+
|
|
34
|
+
(const_declaration) @const
|
|
35
|
+
|
|
36
|
+
(var_declaration) @var
|
|
37
|
+
|
|
38
|
+
(struct_type) @struct
|
|
31
39
|
"""
|
|
32
40
|
|
|
33
41
|
# --- Go Language Setup ---
|
|
@@ -56,14 +64,38 @@ class GoSymbolExtractor(TreeSitterExtractor):
|
|
|
56
64
|
"method.name": "method",
|
|
57
65
|
"type.name": "type",
|
|
58
66
|
"interface.name": "interface",
|
|
67
|
+
"const": "const",
|
|
68
|
+
"var": "var",
|
|
69
|
+
"struct": "struct",
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
symbol_kind = kind_map.get(name)
|
|
62
73
|
if not symbol_kind:
|
|
63
74
|
return None
|
|
64
75
|
|
|
76
|
+
# For const/var/struct, extract the first identifier as name
|
|
77
|
+
if symbol_kind in ("const", "var", "struct"):
|
|
78
|
+
# Try to find the first identifier in the declaration
|
|
79
|
+
node_text = node.text.decode('utf8').strip()
|
|
80
|
+
# Extract first identifier after const/var/struct keyword
|
|
81
|
+
if symbol_kind == "const":
|
|
82
|
+
match = re.search(r'const\s+(\w+)', node_text)
|
|
83
|
+
elif symbol_kind == "var":
|
|
84
|
+
match = re.search(r'var\s+(\w+)', node_text)
|
|
85
|
+
else: # struct
|
|
86
|
+
# For struct, try to find struct name or use a generic name
|
|
87
|
+
match = re.search(r'struct\s+(\w+)', node_text)
|
|
88
|
+
|
|
89
|
+
if match:
|
|
90
|
+
symbol_name = match.group(1)
|
|
91
|
+
else:
|
|
92
|
+
# Fallback: use the kind as name
|
|
93
|
+
symbol_name = symbol_kind
|
|
94
|
+
else:
|
|
95
|
+
symbol_name = node.text.decode('utf8')
|
|
96
|
+
|
|
65
97
|
return Symbol(
|
|
66
|
-
name=
|
|
98
|
+
name=symbol_name,
|
|
67
99
|
kind=symbol_kind,
|
|
68
100
|
file_path=file_path,
|
|
69
101
|
line_start=node.start_point[0] + 1,
|