jarvis-ai-assistant 0.1.132__py3-none-any.whl → 0.1.138__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +330 -347
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +143 -0
- jarvis/jarvis_agent/main.py +12 -13
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +92 -64
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +263 -177
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -59
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +179 -0
- jarvis/jarvis_git_squash/main.py +7 -7
- jarvis/jarvis_lsp/base.py +11 -53
- jarvis/jarvis_lsp/cpp.py +13 -28
- jarvis/jarvis_lsp/go.py +13 -28
- jarvis/jarvis_lsp/python.py +8 -27
- jarvis/jarvis_lsp/registry.py +21 -83
- jarvis/jarvis_lsp/rust.py +15 -30
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +10 -51
- jarvis/jarvis_multi_agent/main.py +43 -0
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +67 -89
- jarvis/jarvis_platform/base.py +14 -13
- jarvis/jarvis_platform/kimi.py +25 -28
- jarvis/jarvis_platform/ollama.py +24 -26
- jarvis/jarvis_platform/openai.py +15 -19
- jarvis/jarvis_platform/oyi.py +48 -50
- jarvis/jarvis_platform/registry.py +29 -44
- jarvis/jarvis_platform/yuanbao.py +39 -43
- jarvis/jarvis_platform_manager/main.py +81 -81
- jarvis/jarvis_platform_manager/openai_test.py +21 -21
- jarvis/jarvis_rag/file_processors.py +18 -18
- jarvis/jarvis_rag/main.py +262 -278
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +85 -78
- jarvis/jarvis_tools/ask_user.py +8 -8
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +9 -9
- jarvis/jarvis_tools/code_review.py +40 -21
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/create_sub_agent.py +0 -1
- jarvis/jarvis_tools/execute_python_script.py +3 -3
- jarvis/jarvis_tools/execute_shell.py +11 -11
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_analyzer.py +116 -105
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +105 -40
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +123 -39
- jarvis/jarvis_tools/function_analyzer.py +140 -57
- jarvis/jarvis_tools/git_commiter.py +10 -10
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +22 -67
- jarvis/jarvis_tools/project_analyzer.py +137 -53
- jarvis/jarvis_tools/rag.py +15 -20
- jarvis/jarvis_tools/read_code.py +25 -23
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/search_web.py +23 -353
- jarvis/jarvis_tools/tool_generator.py +19 -19
- jarvis/jarvis_utils/config.py +36 -96
- jarvis/jarvis_utils/embedding.py +83 -83
- jarvis/jarvis_utils/git_utils.py +20 -20
- jarvis/jarvis_utils/globals.py +18 -6
- jarvis/jarvis_utils/input.py +10 -9
- jarvis/jarvis_utils/methodology.py +141 -140
- jarvis/jarvis_utils/output.py +13 -13
- jarvis/jarvis_utils/utils.py +23 -71
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +6 -15
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +4 -3
- jarvis/jarvis_tools/lsp_find_definition.py +0 -150
- jarvis/jarvis_tools/lsp_find_references.py +0 -127
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.132.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
jarvis/jarvis_lsp/cpp.py
CHANGED
|
@@ -8,19 +8,19 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class CPPLSP(BaseLSP):
|
|
10
10
|
"""C++ LSP implementation using clangd."""
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
language = ["cpp", "c"]
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
15
15
|
def check() -> bool:
|
|
16
16
|
"""Check if clangd is installed."""
|
|
17
17
|
return shutil.which("clangd") is not None
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def __init__(self):
|
|
20
20
|
self.workspace_path = ""
|
|
21
21
|
self.clangd_process = None
|
|
22
22
|
self.request_id = 0
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def initialize(self, workspace_path: str) -> bool:
|
|
25
25
|
try:
|
|
26
26
|
self.workspace_path = workspace_path
|
|
@@ -31,24 +31,24 @@ class CPPLSP(BaseLSP):
|
|
|
31
31
|
stdout=subprocess.PIPE,
|
|
32
32
|
stderr=subprocess.PIPE
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Send initialize request
|
|
36
36
|
self._send_request("initialize", {
|
|
37
37
|
"processId": os.getpid(),
|
|
38
38
|
"rootUri": f"file://{workspace_path}",
|
|
39
39
|
"capabilities": {}
|
|
40
40
|
})
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
return True
|
|
43
43
|
except Exception as e:
|
|
44
44
|
PrettyOutput.print(f"C++ LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
45
45
|
return False
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|
|
48
48
|
"""Send JSON-RPC request to clangd."""
|
|
49
49
|
if not self.clangd_process:
|
|
50
50
|
return None
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
try:
|
|
53
53
|
self.request_id += 1
|
|
54
54
|
request = {
|
|
@@ -57,31 +57,16 @@ class CPPLSP(BaseLSP):
|
|
|
57
57
|
"method": method,
|
|
58
58
|
"params": params
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
self.clangd_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
|
|
62
62
|
self.clangd_process.stdin.flush() # type: ignore
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
response = json.loads(self.clangd_process.stdout.readline().decode()) # type: ignore
|
|
65
65
|
return response.get("result")
|
|
66
66
|
except Exception:
|
|
67
67
|
return None
|
|
68
|
-
|
|
69
|
-
def find_references(self, file_path: str, position: Tuple[int, int]) -> List[Dict[str, Any]]:
|
|
70
|
-
result = self._send_request("textDocument/references", {
|
|
71
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
72
|
-
"position": {"line": position[0], "character": position[1]},
|
|
73
|
-
"context": {"includeDeclaration": True}
|
|
74
|
-
})
|
|
75
|
-
return result or [] # type: ignore
|
|
76
|
-
|
|
77
|
-
def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
|
|
78
|
-
result = self._send_request("textDocument/definition", {
|
|
79
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
80
|
-
"position": {"line": position[0], "character": position[1]}
|
|
81
|
-
})
|
|
82
|
-
return result[0] if result else None
|
|
83
68
|
|
|
84
|
-
|
|
69
|
+
|
|
85
70
|
def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
|
|
86
71
|
# Send didOpen notification to trigger diagnostics
|
|
87
72
|
self._send_request("textDocument/didOpen", {
|
|
@@ -92,7 +77,7 @@ class CPPLSP(BaseLSP):
|
|
|
92
77
|
"text": open(file_path).read()
|
|
93
78
|
}
|
|
94
79
|
})
|
|
95
|
-
|
|
80
|
+
|
|
96
81
|
# Wait for diagnostic notification
|
|
97
82
|
try:
|
|
98
83
|
response = json.loads(self.clangd_process.stdout.readline().decode()) # type: ignore
|
|
@@ -101,8 +86,8 @@ class CPPLSP(BaseLSP):
|
|
|
101
86
|
except Exception:
|
|
102
87
|
pass
|
|
103
88
|
return []
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
|
|
90
|
+
|
|
106
91
|
def shutdown(self):
|
|
107
92
|
if self.clangd_process:
|
|
108
93
|
try:
|
jarvis/jarvis_lsp/go.py
CHANGED
|
@@ -8,19 +8,19 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class GoLSP(BaseLSP):
|
|
10
10
|
"""Go LSP implementation using gopls."""
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
language = "go"
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
15
15
|
def check() -> bool:
|
|
16
16
|
"""Check if gopls is installed."""
|
|
17
17
|
return shutil.which("gopls") is not None
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def __init__(self):
|
|
20
20
|
self.workspace_path = ""
|
|
21
21
|
self.gopls_process = None
|
|
22
22
|
self.request_id = 0
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def initialize(self, workspace_path: str) -> bool:
|
|
25
25
|
try:
|
|
26
26
|
self.workspace_path = workspace_path
|
|
@@ -31,7 +31,7 @@ class GoLSP(BaseLSP):
|
|
|
31
31
|
stdout=subprocess.PIPE,
|
|
32
32
|
stderr=subprocess.PIPE
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Send initialize request
|
|
36
36
|
self._send_request("initialize", {
|
|
37
37
|
"processId": os.getpid(),
|
|
@@ -44,17 +44,17 @@ class GoLSP(BaseLSP):
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
})
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
return True
|
|
49
49
|
except Exception as e:
|
|
50
50
|
PrettyOutput.print(f"Go LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
51
51
|
return False
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|
|
54
54
|
"""Send JSON-RPC request to gopls."""
|
|
55
55
|
if not self.gopls_process:
|
|
56
56
|
return None
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
try:
|
|
59
59
|
self.request_id += 1
|
|
60
60
|
request = {
|
|
@@ -63,30 +63,15 @@ class GoLSP(BaseLSP):
|
|
|
63
63
|
"method": method,
|
|
64
64
|
"params": params
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
self.gopls_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
|
|
68
68
|
self.gopls_process.stdin.flush() # type: ignore
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
|
|
71
71
|
return response.get("result")
|
|
72
72
|
except Exception:
|
|
73
73
|
return None
|
|
74
|
-
|
|
75
|
-
def find_references(self, file_path: str, position: Tuple[int, int]) -> List[Dict[str, Any]]:
|
|
76
|
-
result = self._send_request("textDocument/references", {
|
|
77
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
78
|
-
"position": {"line": position[0], "character": position[1]},
|
|
79
|
-
"context": {"includeDeclaration": True}
|
|
80
|
-
})
|
|
81
|
-
return result or [] # type: ignore
|
|
82
|
-
|
|
83
|
-
def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
|
|
84
|
-
result = self._send_request("textDocument/definition", {
|
|
85
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
86
|
-
"position": {"line": position[0], "character": position[1]}
|
|
87
|
-
})
|
|
88
|
-
return result[0] if result else None
|
|
89
|
-
|
|
74
|
+
|
|
90
75
|
def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
|
|
91
76
|
# Send didOpen notification to trigger diagnostics
|
|
92
77
|
self._send_request("textDocument/didOpen", {
|
|
@@ -97,7 +82,7 @@ class GoLSP(BaseLSP):
|
|
|
97
82
|
"text": open(file_path).read()
|
|
98
83
|
}
|
|
99
84
|
})
|
|
100
|
-
|
|
85
|
+
|
|
101
86
|
# Wait for diagnostic notification
|
|
102
87
|
try:
|
|
103
88
|
response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
|
|
@@ -106,8 +91,8 @@ class GoLSP(BaseLSP):
|
|
|
106
91
|
except Exception:
|
|
107
92
|
pass
|
|
108
93
|
return []
|
|
109
|
-
|
|
110
|
-
|
|
94
|
+
|
|
95
|
+
|
|
111
96
|
def shutdown(self):
|
|
112
97
|
if self.gopls_process:
|
|
113
98
|
try:
|
jarvis/jarvis_lsp/python.py
CHANGED
|
@@ -4,17 +4,17 @@ from jarvis.jarvis_lsp.base import BaseLSP
|
|
|
4
4
|
|
|
5
5
|
class PythonLSP(BaseLSP):
|
|
6
6
|
"""Python LSP implementation using jedi."""
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
language = "python"
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
def __init__(self):
|
|
11
11
|
self.workspace_path = ""
|
|
12
12
|
self.script_cache = {}
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def initialize(self, workspace_path: str) -> bool:
|
|
15
15
|
self.workspace_path = workspace_path
|
|
16
16
|
return True
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
def _get_script(self, file_path: str):
|
|
19
19
|
if file_path not in self.script_cache:
|
|
20
20
|
try:
|
|
@@ -24,27 +24,8 @@ class PythonLSP(BaseLSP):
|
|
|
24
24
|
except Exception:
|
|
25
25
|
return None
|
|
26
26
|
return self.script_cache[file_path]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
script = self._get_script(file_path)
|
|
30
|
-
if not script:
|
|
31
|
-
return []
|
|
32
|
-
try:
|
|
33
|
-
refs = script.get_references(line=position[0] + 1, column=position[1])
|
|
34
|
-
return [self._location_to_dict(ref) for ref in refs]
|
|
35
|
-
except Exception:
|
|
36
|
-
return []
|
|
37
|
-
|
|
38
|
-
def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
|
|
39
|
-
script = self._get_script(file_path)
|
|
40
|
-
if not script:
|
|
41
|
-
return None
|
|
42
|
-
try:
|
|
43
|
-
defs = script.goto(line=position[0] + 1, column=position[1])
|
|
44
|
-
return self._location_to_dict(defs[0]) if defs else None
|
|
45
|
-
except Exception:
|
|
46
|
-
return None
|
|
47
|
-
|
|
27
|
+
|
|
28
|
+
|
|
48
29
|
def _location_to_dict(self, location) -> Dict[str, Any]:
|
|
49
30
|
return {
|
|
50
31
|
"uri": location.module_path,
|
|
@@ -53,7 +34,7 @@ class PythonLSP(BaseLSP):
|
|
|
53
34
|
"end": {"line": location.line - 1, "character": location.column + len(location.name)}
|
|
54
35
|
}
|
|
55
36
|
}
|
|
56
|
-
|
|
37
|
+
|
|
57
38
|
def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
|
|
58
39
|
script = self._get_script(file_path)
|
|
59
40
|
if not script:
|
|
@@ -71,6 +52,6 @@ class PythonLSP(BaseLSP):
|
|
|
71
52
|
} for e in errors]
|
|
72
53
|
except Exception:
|
|
73
54
|
return []
|
|
74
|
-
|
|
55
|
+
|
|
75
56
|
def shutdown(self):
|
|
76
57
|
self.script_cache.clear()
|
jarvis/jarvis_lsp/registry.py
CHANGED
|
@@ -9,8 +9,6 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
9
9
|
|
|
10
10
|
REQUIRED_METHODS = [
|
|
11
11
|
('initialize', ['workspace_path']),
|
|
12
|
-
('find_references', ['file_path', 'position']),
|
|
13
|
-
('find_definition', ['file_path', 'position']),
|
|
14
12
|
('get_diagnostics', ['file_path']),
|
|
15
13
|
('shutdown', [])
|
|
16
14
|
]
|
|
@@ -38,47 +36,47 @@ class LSPRegistry:
|
|
|
38
36
|
def check_lsp_implementation(lsp_class: Type[BaseLSP]) -> bool:
|
|
39
37
|
"""Check if the LSP class implements all necessary methods."""
|
|
40
38
|
missing_methods = []
|
|
41
|
-
|
|
39
|
+
|
|
42
40
|
for method_name, params in REQUIRED_METHODS:
|
|
43
41
|
if not hasattr(lsp_class, method_name):
|
|
44
42
|
missing_methods.append(method_name)
|
|
45
43
|
continue
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
method = getattr(lsp_class, method_name)
|
|
48
46
|
if not callable(method):
|
|
49
47
|
missing_methods.append(method_name)
|
|
50
48
|
continue
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
sig = inspect.signature(method)
|
|
53
51
|
method_params = [p for p in sig.parameters if p != 'self']
|
|
54
52
|
if len(method_params) != len(params):
|
|
55
53
|
missing_methods.append(f"{method_name}(parameter mismatch)")
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
if missing_methods:
|
|
58
56
|
PrettyOutput.print(
|
|
59
|
-
f"LSP {lsp_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
57
|
+
f"LSP {lsp_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
60
58
|
OutputType.WARNING
|
|
61
59
|
)
|
|
62
60
|
return False
|
|
63
|
-
|
|
61
|
+
|
|
64
62
|
return True
|
|
65
63
|
|
|
66
64
|
@staticmethod
|
|
67
65
|
def load_lsp_from_dir(directory: str) -> Dict[str, Type[BaseLSP]]:
|
|
68
66
|
"""Load LSP implementations from specified directory."""
|
|
69
67
|
lsp_servers = {}
|
|
70
|
-
|
|
68
|
+
|
|
71
69
|
if not os.path.exists(directory):
|
|
72
70
|
PrettyOutput.print(f"LSP 目录不存在: {directory}", OutputType.WARNING)
|
|
73
71
|
return lsp_servers
|
|
74
|
-
|
|
72
|
+
|
|
75
73
|
package_name = None
|
|
76
74
|
if directory == os.path.dirname(__file__):
|
|
77
75
|
package_name = "jarvis.jarvis_lsp"
|
|
78
|
-
|
|
76
|
+
|
|
79
77
|
if directory not in sys.path:
|
|
80
78
|
sys.path.append(directory)
|
|
81
|
-
|
|
79
|
+
|
|
82
80
|
for filename in os.listdir(directory):
|
|
83
81
|
if filename.endswith('.py') and not filename.startswith('__'):
|
|
84
82
|
module_name = filename[:-3]
|
|
@@ -87,10 +85,10 @@ class LSPRegistry:
|
|
|
87
85
|
module = importlib.import_module(f"{package_name}.{module_name}")
|
|
88
86
|
else:
|
|
89
87
|
module = importlib.import_module(module_name)
|
|
90
|
-
|
|
88
|
+
|
|
91
89
|
for _, obj in inspect.getmembers(module):
|
|
92
|
-
if (inspect.isclass(obj) and
|
|
93
|
-
issubclass(obj, BaseLSP) and
|
|
90
|
+
if (inspect.isclass(obj) and
|
|
91
|
+
issubclass(obj, BaseLSP) and
|
|
94
92
|
obj != BaseLSP and
|
|
95
93
|
hasattr(obj, 'language')):
|
|
96
94
|
if not LSPRegistry.check_lsp_implementation(obj):
|
|
@@ -106,7 +104,7 @@ class LSPRegistry:
|
|
|
106
104
|
break
|
|
107
105
|
except Exception as e:
|
|
108
106
|
PrettyOutput.print(f"加载 LSP {module_name} 失败: {str(e)}", OutputType.ERROR)
|
|
109
|
-
|
|
107
|
+
|
|
110
108
|
return lsp_servers
|
|
111
109
|
|
|
112
110
|
@staticmethod
|
|
@@ -115,17 +113,17 @@ class LSPRegistry:
|
|
|
115
113
|
if LSPRegistry.global_lsp_registry is None:
|
|
116
114
|
LSPRegistry.global_lsp_registry = LSPRegistry()
|
|
117
115
|
return LSPRegistry.global_lsp_registry
|
|
118
|
-
|
|
116
|
+
|
|
119
117
|
def __init__(self):
|
|
120
118
|
"""Initialize LSP registry."""
|
|
121
119
|
self.lsp_servers: Dict[str, Type[BaseLSP]] = {}
|
|
122
|
-
|
|
120
|
+
|
|
123
121
|
# Load from user LSP directory
|
|
124
122
|
lsp_dir = LSPRegistry.get_lsp_dir()
|
|
125
123
|
if lsp_dir and os.path.exists(lsp_dir):
|
|
126
124
|
for language, lsp_class in LSPRegistry.load_lsp_from_dir(lsp_dir).items():
|
|
127
125
|
self.register_lsp(language, lsp_class)
|
|
128
|
-
|
|
126
|
+
|
|
129
127
|
# Load from built-in LSP directory
|
|
130
128
|
lsp_dir = os.path.dirname(__file__)
|
|
131
129
|
if lsp_dir and os.path.exists(lsp_dir):
|
|
@@ -135,13 +133,13 @@ class LSPRegistry:
|
|
|
135
133
|
def register_lsp(self, language: str, lsp_class: Type[BaseLSP]):
|
|
136
134
|
"""Register LSP implementation for a language."""
|
|
137
135
|
self.lsp_servers[language] = lsp_class
|
|
138
|
-
|
|
136
|
+
|
|
139
137
|
def create_lsp(self, language: str) -> Optional[BaseLSP]:
|
|
140
138
|
"""Create LSP instance for specified language."""
|
|
141
139
|
if language not in self.lsp_servers:
|
|
142
140
|
PrettyOutput.print(f"没有找到 LSP 支持的语言: {language}", OutputType.WARNING)
|
|
143
141
|
return None
|
|
144
|
-
|
|
142
|
+
|
|
145
143
|
try:
|
|
146
144
|
lsp = self.lsp_servers[language]()
|
|
147
145
|
return lsp
|
|
@@ -152,7 +150,7 @@ class LSPRegistry:
|
|
|
152
150
|
def get_supported_languages(self) -> List[str]:
|
|
153
151
|
"""Get list of supported languages."""
|
|
154
152
|
return list(self.lsp_servers.keys())
|
|
155
|
-
|
|
153
|
+
|
|
156
154
|
@staticmethod
|
|
157
155
|
def get_text_at_position(file_path: str, line: int, start_character: int) -> str:
|
|
158
156
|
"""Get text at position."""
|
|
@@ -160,70 +158,10 @@ class LSPRegistry:
|
|
|
160
158
|
lines = file.readlines()
|
|
161
159
|
symbol = re.search(r'\b\w+\b', lines[line][start_character:])
|
|
162
160
|
return symbol.group() if symbol else ""
|
|
163
|
-
|
|
161
|
+
|
|
164
162
|
@staticmethod
|
|
165
163
|
def get_line_at_position(file_path: str, line: int) -> str:
|
|
166
164
|
"""Get line at position."""
|
|
167
165
|
with open(file_path, 'r', errors="ignore") as file:
|
|
168
166
|
lines = file.readlines()
|
|
169
167
|
return lines[line]
|
|
170
|
-
|
|
171
|
-
def main():
|
|
172
|
-
"""CLI entry point for LSP testing."""
|
|
173
|
-
import argparse
|
|
174
|
-
|
|
175
|
-
parser = argparse.ArgumentParser(description='LSP functionality testing')
|
|
176
|
-
parser.add_argument('--language', type=str, required=True, help='Programming language')
|
|
177
|
-
parser.add_argument('--file', type=str, required=True, help='File to analyze')
|
|
178
|
-
parser.add_argument('--action', choices=['symbols', 'diagnostics', 'references', 'definition'],
|
|
179
|
-
required=True, help='Action to perform')
|
|
180
|
-
parser.add_argument('--line', type=int, help='Line number (0-based) for references/definition')
|
|
181
|
-
parser.add_argument('--character', type=int, help='Character position for references/definition')
|
|
182
|
-
|
|
183
|
-
args = parser.parse_args()
|
|
184
|
-
|
|
185
|
-
# Initialize LSP
|
|
186
|
-
registry = LSPRegistry.get_global_lsp_registry()
|
|
187
|
-
lsp = registry.create_lsp(args.language)
|
|
188
|
-
|
|
189
|
-
if not lsp:
|
|
190
|
-
PrettyOutput.print(f"没有 LSP 支持的语言: {args.language}", OutputType.WARNING)
|
|
191
|
-
return 1
|
|
192
|
-
|
|
193
|
-
if not lsp.initialize(os.path.abspath(os.getcwd())):
|
|
194
|
-
PrettyOutput.print("LSP 初始化失败", OutputType.WARNING)
|
|
195
|
-
return 1
|
|
196
|
-
|
|
197
|
-
try:
|
|
198
|
-
if args.action == 'diagnostics':
|
|
199
|
-
diagnostics = lsp.get_diagnostics(args.file)
|
|
200
|
-
for diag in diagnostics:
|
|
201
|
-
severity = ['Error', 'Warning', 'Info', 'Hint'][diag['severity'] - 1]
|
|
202
|
-
PrettyOutput.print(f"{severity} 在 {diag['range']['start']['line']}:{diag['range']['start']['character']}: {diag['message']}", OutputType.INFO)
|
|
203
|
-
|
|
204
|
-
elif args.action in ('references', 'definition'):
|
|
205
|
-
if args.line is None or args.character is None:
|
|
206
|
-
PrettyOutput.print("需要行和字符位置用于 references/definition", OutputType.WARNING)
|
|
207
|
-
return 1
|
|
208
|
-
|
|
209
|
-
if args.action == 'references':
|
|
210
|
-
refs = lsp.find_references(args.file, (args.line, args.character))
|
|
211
|
-
for ref in refs:
|
|
212
|
-
PrettyOutput.print(f"引用在 {ref['uri']} 在 {ref['range']['start']['line']}:{ref['range']['start']['character']}\n行: {LSPRegistry.get_line_at_position(ref['uri'], ref['range']['start']['line'])}", OutputType.INFO)
|
|
213
|
-
else:
|
|
214
|
-
defn = lsp.find_definition(args.file, (args.line, args.character))
|
|
215
|
-
if defn:
|
|
216
|
-
PrettyOutput.print(f"定义在 {defn['uri']} 在 {defn['range']['start']['line']}:{defn['range']['start']['character']}\n行: {LSPRegistry.get_line_at_position(defn['uri'], defn['range']['start']['line'])}", OutputType.INFO)
|
|
217
|
-
else:
|
|
218
|
-
PrettyOutput.print("没有找到定义", OutputType.WARNING)
|
|
219
|
-
|
|
220
|
-
except Exception as e:
|
|
221
|
-
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
222
|
-
return 1
|
|
223
|
-
finally:
|
|
224
|
-
lsp.shutdown()
|
|
225
|
-
|
|
226
|
-
return 0
|
|
227
|
-
|
|
228
|
-
if __name__ == "__main__":
|
|
229
|
-
exit(main())
|
jarvis/jarvis_lsp/rust.py
CHANGED
|
@@ -8,19 +8,19 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class RustLSP(BaseLSP):
|
|
10
10
|
"""Rust LSP implementation using rust-analyzer."""
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
language = "rust"
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
@staticmethod
|
|
15
15
|
def check() -> bool:
|
|
16
16
|
"""Check if rust-analyzer is installed."""
|
|
17
17
|
return shutil.which("rust-analyzer") is not None
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def __init__(self):
|
|
20
20
|
self.workspace_path = ""
|
|
21
21
|
self.analyzer_process = None
|
|
22
22
|
self.request_id = 0
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def initialize(self, workspace_path: str) -> bool:
|
|
25
25
|
try:
|
|
26
26
|
self.workspace_path = workspace_path
|
|
@@ -31,7 +31,7 @@ class RustLSP(BaseLSP):
|
|
|
31
31
|
stdout=subprocess.PIPE,
|
|
32
32
|
stderr=subprocess.PIPE
|
|
33
33
|
)
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
# Send initialize request
|
|
36
36
|
self._send_request("initialize", {
|
|
37
37
|
"processId": os.getpid(),
|
|
@@ -46,17 +46,17 @@ class RustLSP(BaseLSP):
|
|
|
46
46
|
},
|
|
47
47
|
"workspaceFolders": [{"uri": f"file://{workspace_path}", "name": "workspace"}]
|
|
48
48
|
})
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
return True
|
|
51
51
|
except Exception as e:
|
|
52
52
|
PrettyOutput.print(f"Rust LSP 初始化失败: {str(e)}", OutputType.ERROR)
|
|
53
53
|
return False
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
|
|
56
56
|
"""Send JSON-RPC request to rust-analyzer."""
|
|
57
57
|
if not self.analyzer_process:
|
|
58
58
|
return None
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
try:
|
|
61
61
|
self.request_id += 1
|
|
62
62
|
request = {
|
|
@@ -65,31 +65,16 @@ class RustLSP(BaseLSP):
|
|
|
65
65
|
"method": method,
|
|
66
66
|
"params": params
|
|
67
67
|
}
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
self.analyzer_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
|
|
70
70
|
self.analyzer_process.stdin.flush() # type: ignore
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
|
|
73
73
|
return response.get("result")
|
|
74
74
|
except Exception:
|
|
75
75
|
return None
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
result = self._send_request("textDocument/references", {
|
|
79
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
80
|
-
"position": {"line": position[0], "character": position[1]},
|
|
81
|
-
"context": {"includeDeclaration": True}
|
|
82
|
-
})
|
|
83
|
-
return result or [] # type: ignore
|
|
84
|
-
|
|
85
|
-
def find_definition(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
|
|
86
|
-
result = self._send_request("textDocument/definition", {
|
|
87
|
-
"textDocument": {"uri": f"file://{file_path}"},
|
|
88
|
-
"position": {"line": position[0], "character": position[1]}
|
|
89
|
-
})
|
|
90
|
-
return result[0] if result else None
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
|
|
77
|
+
|
|
93
78
|
def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
|
|
94
79
|
# Send didOpen notification to trigger diagnostics
|
|
95
80
|
self._send_request("textDocument/didOpen", {
|
|
@@ -100,7 +85,7 @@ class RustLSP(BaseLSP):
|
|
|
100
85
|
"text": open(file_path).read()
|
|
101
86
|
}
|
|
102
87
|
})
|
|
103
|
-
|
|
88
|
+
|
|
104
89
|
# Wait for diagnostic notification
|
|
105
90
|
try:
|
|
106
91
|
response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
|
|
@@ -109,8 +94,8 @@ class RustLSP(BaseLSP):
|
|
|
109
94
|
except Exception:
|
|
110
95
|
pass
|
|
111
96
|
return []
|
|
112
|
-
|
|
113
|
-
|
|
97
|
+
|
|
98
|
+
|
|
114
99
|
def shutdown(self):
|
|
115
100
|
if self.analyzer_process:
|
|
116
101
|
try:
|