jarvis-ai-assistant 0.7.8__py3-none-any.whl → 1.0.2__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 +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Dict
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Optional
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
@dataclass
|
|
8
10
|
class Symbol:
|
|
9
11
|
"""表示代码中的单个符号。"""
|
|
12
|
+
|
|
10
13
|
name: str
|
|
11
14
|
kind: str # 例如:'function'(函数)、'class'(类)、'variable'(变量)、'import'(导入)
|
|
12
15
|
file_path: str
|
|
@@ -17,7 +20,7 @@ class Symbol:
|
|
|
17
20
|
# 根据需要添加更多字段,例如父作用域
|
|
18
21
|
parent: Optional[str] = None
|
|
19
22
|
# 定义位置(对于引用/调用,指向符号定义的位置)
|
|
20
|
-
definition_location: Optional[
|
|
23
|
+
definition_location: Optional["Symbol"] = None # 指向定义Symbol的引用
|
|
21
24
|
is_definition: bool = False # 如果此符号是定义则为True,如果是引用/调用则为False
|
|
22
25
|
|
|
23
26
|
|
|
@@ -46,13 +49,17 @@ class SymbolTable:
|
|
|
46
49
|
cache_file = self._get_cache_file()
|
|
47
50
|
if os.path.exists(cache_file):
|
|
48
51
|
try:
|
|
49
|
-
with open(cache_file,
|
|
52
|
+
with open(cache_file, "r", encoding="utf-8") as f:
|
|
50
53
|
data = json.load(f)
|
|
51
54
|
# 将JSON数据转换回Symbol对象
|
|
52
|
-
self.symbols_by_name = self._deserialize_symbols(
|
|
53
|
-
|
|
55
|
+
self.symbols_by_name = self._deserialize_symbols(
|
|
56
|
+
data.get("symbols_by_name", {})
|
|
57
|
+
)
|
|
58
|
+
self.symbols_by_file = self._deserialize_symbols(
|
|
59
|
+
data.get("symbols_by_file", {})
|
|
60
|
+
)
|
|
54
61
|
# 加载文件修改时间
|
|
55
|
-
self._file_mtimes = data.get(
|
|
62
|
+
self._file_mtimes = data.get("file_mtimes", {})
|
|
56
63
|
except Exception:
|
|
57
64
|
# 如果缓存加载失败,则从空表开始
|
|
58
65
|
pass
|
|
@@ -63,88 +70,94 @@ class SymbolTable:
|
|
|
63
70
|
# 确保缓存目录存在
|
|
64
71
|
os.makedirs(self.cache_dir, exist_ok=True)
|
|
65
72
|
cache_file = self._get_cache_file()
|
|
66
|
-
|
|
73
|
+
|
|
67
74
|
# 保存前更新文件修改时间
|
|
68
75
|
self._update_file_mtimes()
|
|
69
|
-
|
|
76
|
+
|
|
70
77
|
# 序列化符号以便JSON存储
|
|
71
78
|
data = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
"symbols_by_name": self._serialize_symbols(self.symbols_by_name),
|
|
80
|
+
"symbols_by_file": self._serialize_symbols(self.symbols_by_file),
|
|
81
|
+
"file_mtimes": self._file_mtimes,
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
with open(cache_file,
|
|
83
|
+
|
|
84
|
+
with open(cache_file, "w", encoding="utf-8") as f:
|
|
78
85
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
79
86
|
except Exception:
|
|
80
87
|
# 如果缓存保存失败,则继续而不缓存
|
|
81
88
|
pass
|
|
82
89
|
|
|
83
|
-
def _serialize_symbols(
|
|
90
|
+
def _serialize_symbols(
|
|
91
|
+
self, symbol_dict: Dict[str, List[Symbol]]
|
|
92
|
+
) -> Dict[str, List[dict]]:
|
|
84
93
|
"""将Symbol对象转换为可序列化的字典。"""
|
|
85
94
|
serialized = {}
|
|
86
95
|
for key, symbols in symbol_dict.items():
|
|
87
96
|
serialized[key] = [self._symbol_to_dict(symbol) for symbol in symbols]
|
|
88
97
|
return serialized
|
|
89
98
|
|
|
90
|
-
def _deserialize_symbols(
|
|
99
|
+
def _deserialize_symbols(
|
|
100
|
+
self, symbol_dict: Dict[str, List[dict]]
|
|
101
|
+
) -> Dict[str, List[Symbol]]:
|
|
91
102
|
"""将序列化的字典转换回Symbol对象。"""
|
|
92
103
|
deserialized = {}
|
|
93
104
|
for key, symbol_data_list in symbol_dict.items():
|
|
94
|
-
deserialized[key] = [
|
|
105
|
+
deserialized[key] = [
|
|
106
|
+
self._dict_to_symbol(data) for data in symbol_data_list
|
|
107
|
+
]
|
|
95
108
|
return deserialized
|
|
96
109
|
|
|
97
110
|
def _symbol_to_dict(self, symbol: Symbol) -> dict:
|
|
98
111
|
"""将Symbol对象转换为字典。"""
|
|
99
112
|
result = {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
113
|
+
"name": symbol.name,
|
|
114
|
+
"kind": symbol.kind,
|
|
115
|
+
"file_path": symbol.file_path,
|
|
116
|
+
"line_start": symbol.line_start,
|
|
117
|
+
"line_end": symbol.line_end,
|
|
118
|
+
"signature": symbol.signature,
|
|
119
|
+
"docstring": symbol.docstring,
|
|
120
|
+
"parent": symbol.parent,
|
|
121
|
+
"is_definition": getattr(symbol, "is_definition", False),
|
|
109
122
|
}
|
|
110
123
|
# 序列化定义位置(只保存基本信息,避免循环引用)
|
|
111
|
-
if hasattr(symbol,
|
|
112
|
-
result[
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
if hasattr(symbol, "definition_location") and symbol.definition_location:
|
|
125
|
+
result["definition_location"] = {
|
|
126
|
+
"file_path": symbol.definition_location.file_path,
|
|
127
|
+
"line_start": symbol.definition_location.line_start,
|
|
128
|
+
"line_end": symbol.definition_location.line_end,
|
|
129
|
+
"name": symbol.definition_location.name,
|
|
117
130
|
}
|
|
118
131
|
return result
|
|
119
132
|
|
|
120
133
|
def _dict_to_symbol(self, data: dict) -> Symbol:
|
|
121
134
|
"""将字典转换回Symbol对象。"""
|
|
122
135
|
symbol = Symbol(
|
|
123
|
-
name=data[
|
|
124
|
-
kind=data[
|
|
125
|
-
file_path=data[
|
|
126
|
-
line_start=data[
|
|
127
|
-
line_end=data[
|
|
128
|
-
signature=data.get(
|
|
129
|
-
docstring=data.get(
|
|
130
|
-
parent=data.get(
|
|
131
|
-
is_definition=data.get(
|
|
136
|
+
name=data["name"],
|
|
137
|
+
kind=data["kind"],
|
|
138
|
+
file_path=data["file_path"],
|
|
139
|
+
line_start=data["line_start"],
|
|
140
|
+
line_end=data["line_end"],
|
|
141
|
+
signature=data.get("signature"),
|
|
142
|
+
docstring=data.get("docstring"),
|
|
143
|
+
parent=data.get("parent"),
|
|
144
|
+
is_definition=data.get("is_definition", False),
|
|
132
145
|
)
|
|
133
146
|
# 恢复定义位置(创建临时 Symbol 对象)
|
|
134
|
-
if
|
|
135
|
-
def_loc = data[
|
|
147
|
+
if "definition_location" in data and data["definition_location"]:
|
|
148
|
+
def_loc = data["definition_location"]
|
|
136
149
|
symbol.definition_location = Symbol(
|
|
137
|
-
name=def_loc[
|
|
138
|
-
kind=
|
|
139
|
-
file_path=def_loc[
|
|
140
|
-
line_start=def_loc[
|
|
141
|
-
line_end=def_loc[
|
|
150
|
+
name=def_loc["name"],
|
|
151
|
+
kind="", # 未知类型
|
|
152
|
+
file_path=def_loc["file_path"],
|
|
153
|
+
line_start=def_loc["line_start"],
|
|
154
|
+
line_end=def_loc["line_end"],
|
|
142
155
|
)
|
|
143
156
|
return symbol
|
|
144
157
|
|
|
145
158
|
def add_symbol(self, symbol: Symbol, save_to_cache: bool = False):
|
|
146
159
|
"""向表中添加符号。
|
|
147
|
-
|
|
160
|
+
|
|
148
161
|
参数:
|
|
149
162
|
symbol: 要添加的符号
|
|
150
163
|
save_to_cache: 如果为True,立即保存到缓存。默认为False以提高性能。
|
|
@@ -157,11 +170,11 @@ class SymbolTable:
|
|
|
157
170
|
if symbol.file_path not in self.symbols_by_file:
|
|
158
171
|
self.symbols_by_file[symbol.file_path] = []
|
|
159
172
|
self.symbols_by_file[symbol.file_path].append(symbol)
|
|
160
|
-
|
|
173
|
+
|
|
161
174
|
# 仅在明确请求时保存到缓存(出于性能考虑)
|
|
162
175
|
if save_to_cache:
|
|
163
176
|
self._save_to_cache()
|
|
164
|
-
|
|
177
|
+
|
|
165
178
|
def save_cache(self):
|
|
166
179
|
"""将整个符号表保存到缓存。批量操作后调用此方法。"""
|
|
167
180
|
self._save_to_cache()
|
|
@@ -172,9 +185,7 @@ class SymbolTable:
|
|
|
172
185
|
如果提供了file_path,则搜索仅限于该文件。
|
|
173
186
|
"""
|
|
174
187
|
if file_path:
|
|
175
|
-
return [
|
|
176
|
-
s for s in self.get_file_symbols(file_path) if s.name == name
|
|
177
|
-
]
|
|
188
|
+
return [s for s in self.get_file_symbols(file_path) if s.name == name]
|
|
178
189
|
return self.symbols_by_name.get(name, [])
|
|
179
190
|
|
|
180
191
|
def get_file_symbols(self, file_path: str) -> List[Symbol]:
|
|
@@ -188,19 +199,20 @@ class SymbolTable:
|
|
|
188
199
|
for symbol in symbols_to_remove:
|
|
189
200
|
if symbol.name in self.symbols_by_name:
|
|
190
201
|
self.symbols_by_name[symbol.name] = [
|
|
191
|
-
s
|
|
202
|
+
s
|
|
203
|
+
for s in self.symbols_by_name[symbol.name]
|
|
192
204
|
if s.file_path != file_path
|
|
193
205
|
]
|
|
194
206
|
if not self.symbols_by_name[symbol.name]:
|
|
195
207
|
del self.symbols_by_name[symbol.name]
|
|
196
|
-
|
|
208
|
+
|
|
197
209
|
# 移除文件修改时间跟踪
|
|
198
210
|
if file_path in self._file_mtimes:
|
|
199
211
|
del self._file_mtimes[file_path]
|
|
200
|
-
|
|
212
|
+
|
|
201
213
|
# 清除后保存到缓存
|
|
202
214
|
self._save_to_cache()
|
|
203
|
-
|
|
215
|
+
|
|
204
216
|
def _update_file_mtimes(self):
|
|
205
217
|
"""更新所有跟踪文件的修改时间。"""
|
|
206
218
|
for file_path in list(self.symbols_by_file.keys()):
|
|
@@ -210,28 +222,28 @@ class SymbolTable:
|
|
|
210
222
|
except Exception:
|
|
211
223
|
# 如果无法获取修改时间,则从跟踪中移除
|
|
212
224
|
self._file_mtimes.pop(file_path, None)
|
|
213
|
-
|
|
225
|
+
|
|
214
226
|
def is_file_stale(self, file_path: str) -> bool:
|
|
215
227
|
"""检查文件自缓存后是否已被修改。
|
|
216
|
-
|
|
228
|
+
|
|
217
229
|
参数:
|
|
218
230
|
file_path: 要检查的文件路径
|
|
219
|
-
|
|
231
|
+
|
|
220
232
|
返回:
|
|
221
233
|
如果文件比缓存新则为True,否则为False
|
|
222
234
|
"""
|
|
223
235
|
if file_path not in self.symbols_by_file:
|
|
224
236
|
# 文件不在缓存中,视为已过期(需要加载)
|
|
225
237
|
return True
|
|
226
|
-
|
|
238
|
+
|
|
227
239
|
if file_path not in self._file_mtimes:
|
|
228
240
|
# 没有记录修改时间,视为已过期
|
|
229
241
|
return True
|
|
230
|
-
|
|
242
|
+
|
|
231
243
|
if not os.path.exists(file_path):
|
|
232
244
|
# 文件不存在,不算过期(将由clear_file_symbols处理)
|
|
233
245
|
return False
|
|
234
|
-
|
|
246
|
+
|
|
235
247
|
try:
|
|
236
248
|
current_mtime = os.path.getmtime(file_path)
|
|
237
249
|
cached_mtime = self._file_mtimes.get(file_path, 0)
|
|
@@ -249,4 +261,4 @@ class SymbolExtractor:
|
|
|
249
261
|
从代码中提取符号(函数、类、变量等)。
|
|
250
262
|
此方法应由特定语言的子类实现。
|
|
251
263
|
"""
|
|
252
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
|
264
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from typing import List
|
|
2
|
+
from typing import List
|
|
3
|
+
from typing import Optional
|
|
3
4
|
|
|
4
|
-
from tree_sitter import Language
|
|
5
|
+
from tree_sitter import Language
|
|
6
|
+
from tree_sitter import Node
|
|
7
|
+
from tree_sitter import Parser
|
|
8
|
+
from tree_sitter import Query
|
|
9
|
+
from tree_sitter import QueryCursor
|
|
5
10
|
|
|
6
|
-
from .
|
|
11
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
12
|
+
|
|
13
|
+
from .symbol_extractor import Symbol
|
|
14
|
+
from .symbol_extractor import SymbolExtractor
|
|
7
15
|
|
|
8
16
|
|
|
9
17
|
class TreeSitterExtractor(SymbolExtractor):
|
|
@@ -14,45 +22,110 @@ class TreeSitterExtractor(SymbolExtractor):
|
|
|
14
22
|
"""
|
|
15
23
|
|
|
16
24
|
def __init__(self, language: Language, symbol_query: str):
|
|
17
|
-
# 如果传入的是 PyCapsule,需要转换为 Language 对象
|
|
18
|
-
if not isinstance(language, Language):
|
|
19
|
-
language = Language(language)
|
|
20
25
|
self.language = language
|
|
21
26
|
self.parser = Parser()
|
|
22
|
-
#
|
|
23
|
-
|
|
27
|
+
# 设置language(tree-sitter的Parser需要Language对象)
|
|
28
|
+
try:
|
|
29
|
+
# 尝试使用set_language方法(如果可用)
|
|
30
|
+
if hasattr(self.parser, "set_language"):
|
|
31
|
+
self.parser.set_language(self.language)
|
|
32
|
+
else:
|
|
33
|
+
# 否则直接赋值language属性
|
|
34
|
+
self.parser.language = self.language
|
|
35
|
+
except (AttributeError, TypeError):
|
|
36
|
+
# 如果都失败,尝试创建Language对象
|
|
37
|
+
try:
|
|
38
|
+
from tree_sitter import Language as LangClass
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
lang_obj = LangClass(language)
|
|
42
|
+
self.parser.language = lang_obj
|
|
43
|
+
self.language = lang_obj
|
|
44
|
+
except Exception:
|
|
45
|
+
self.parser.language = language
|
|
46
|
+
except Exception:
|
|
47
|
+
# 最后的fallback:直接赋值
|
|
48
|
+
self.parser.language = language
|
|
24
49
|
self.symbol_query = symbol_query
|
|
25
50
|
|
|
26
51
|
def extract_symbols(self, file_path: str, content: str) -> List[Symbol]:
|
|
27
52
|
"""
|
|
28
53
|
Parses the code with tree-sitter and extracts symbols based on the query.
|
|
29
54
|
"""
|
|
55
|
+
# 检查内容是否为空或只包含空白
|
|
56
|
+
if not content or not content.strip():
|
|
57
|
+
return []
|
|
58
|
+
|
|
59
|
+
symbols = []
|
|
60
|
+
|
|
30
61
|
try:
|
|
62
|
+
# 解析代码(即使有语法错误,tree-sitter也能部分解析)
|
|
31
63
|
tree = self.parser.parse(bytes(content, "utf8"))
|
|
32
|
-
|
|
33
|
-
|
|
64
|
+
|
|
65
|
+
# 检查解析是否成功(tree.root_node 应该存在)
|
|
66
|
+
if tree is None or tree.root_node is None:
|
|
67
|
+
return []
|
|
68
|
+
|
|
69
|
+
# 尝试构造查询
|
|
70
|
+
query = None
|
|
71
|
+
try:
|
|
72
|
+
query = Query(self.language, self.symbol_query)
|
|
73
|
+
except Exception as query_error:
|
|
74
|
+
# Query 构造失败(可能是查询语法问题),静默返回空列表
|
|
75
|
+
import os
|
|
76
|
+
|
|
77
|
+
if os.getenv("DEBUG_TREE_SITTER", "").lower() in ("1", "true", "yes"):
|
|
78
|
+
PrettyOutput.auto_print(
|
|
79
|
+
f"Error creating query for {file_path}: {query_error}"
|
|
80
|
+
)
|
|
81
|
+
return []
|
|
82
|
+
|
|
34
83
|
# 使用 QueryCursor 执行查询
|
|
84
|
+
# 即使有语法错误,tree-sitter也能部分解析,所以可以提取部分符号
|
|
35
85
|
cursor = QueryCursor(query)
|
|
36
86
|
matches = cursor.matches(tree.root_node)
|
|
37
|
-
|
|
38
|
-
symbols = []
|
|
87
|
+
|
|
39
88
|
# matches 返回格式: [(pattern_index, {capture_name: [nodes]})]
|
|
40
89
|
for pattern_index, captures_dict in matches:
|
|
41
90
|
for capture_name, nodes in captures_dict.items():
|
|
42
91
|
for node in nodes:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
92
|
+
try:
|
|
93
|
+
# 跳过错误节点(tree-sitter会用ERROR节点标记语法错误)
|
|
94
|
+
# 错误节点通常没有有效的文本内容或位置信息
|
|
95
|
+
if node.type == "ERROR":
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
symbol = self._create_symbol_from_capture(
|
|
99
|
+
node, capture_name, file_path
|
|
100
|
+
)
|
|
101
|
+
if symbol:
|
|
102
|
+
symbols.append(symbol)
|
|
103
|
+
except Exception:
|
|
104
|
+
# 单个符号提取失败,继续处理其他符号
|
|
105
|
+
# 这样可以确保即使部分符号提取失败,也能返回已成功提取的符号
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
# 即使有语法错误,也返回已成功提取的符号
|
|
46
109
|
return symbols
|
|
47
110
|
except Exception as e:
|
|
48
|
-
|
|
49
|
-
|
|
111
|
+
# 如果解析完全失败(不是语法错误,而是其他严重错误),返回空列表
|
|
112
|
+
# 只在调试模式下打印错误信息
|
|
113
|
+
import os
|
|
114
|
+
|
|
115
|
+
if os.getenv("DEBUG_TREE_SITTER", "").lower() in ("1", "true", "yes"):
|
|
116
|
+
PrettyOutput.auto_print(
|
|
117
|
+
f"Error extracting symbols from {file_path} with tree-sitter: {e}"
|
|
118
|
+
)
|
|
119
|
+
# 即使解析失败,也返回已提取的符号(如果有的话)
|
|
120
|
+
return symbols
|
|
50
121
|
|
|
51
122
|
@abstractmethod
|
|
52
|
-
def _create_symbol_from_capture(
|
|
123
|
+
def _create_symbol_from_capture(
|
|
124
|
+
self, node: Node, name: str, file_path: str
|
|
125
|
+
) -> Optional[Symbol]:
|
|
53
126
|
"""
|
|
54
127
|
Creates a Symbol object from a tree-sitter query capture.
|
|
55
128
|
This method must be implemented by subclasses to map capture names
|
|
56
129
|
(e.g., "function.name") to Symbol attributes.
|
|
57
130
|
"""
|
|
58
|
-
raise NotImplementedError
|
|
131
|
+
raise NotImplementedError
|