jarvis-ai-assistant 0.1.177__py3-none-any.whl → 0.1.178__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/jarvis_lsp/base.py DELETED
@@ -1,66 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from abc import ABC, abstractmethod
3
- from typing import List, Dict, Any, Union
4
-
5
- class BaseLSP(ABC):
6
- """Base class for Language Server Protocol integration.
7
-
8
- Core LSP features needed for LLM-based code editing:
9
- 1. Code navigation and analysis
10
- 2. Code modification validation
11
- 3. Diagnostic information
12
- 4. Symbol analysis
13
- """
14
-
15
- language: Union[str, List[str]] = "" # Language identifier, should be overridden by subclasses
16
-
17
- @abstractmethod
18
- def initialize(self, workspace_path: str) -> bool:
19
- """Initialize LSP server for the workspace.
20
-
21
- Args:
22
- workspace_path: Root path of the workspace
23
-
24
- Returns:
25
- bool: True if initialization successful
26
- """
27
- return False
28
-
29
-
30
- @abstractmethod
31
- def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
32
- """Get diagnostics (errors, warnings) for file.
33
-
34
- Args:
35
- file_path: Path to the file
36
-
37
- Returns:
38
- List of diagnostic items:
39
- [
40
- {
41
- "range": {
42
- "start": {"line": int, "character": int},
43
- "end": {"line": int, "character": int}
44
- },
45
- "severity": 1 | 2 | 3 | 4, # Error=1, Warning=2, Info=3, Hint=4
46
- "code": str, # Error code if any
47
- "source": str, # Source of diagnostic (e.g. "pylint")
48
- "message": str, # Diagnostic message
49
- "relatedInformation": [ # Optional related info
50
- {
51
- "location": {
52
- "uri": str,
53
- "range": {...}
54
- },
55
- "message": str
56
- }
57
- ]
58
- }
59
- ]
60
- """
61
- return []
62
-
63
-
64
- def shutdown(self):
65
- """Shutdown LSP server cleanly."""
66
- pass
jarvis/jarvis_lsp/cpp.py DELETED
@@ -1,99 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import shutil
4
- import subprocess
5
- from typing import List, Dict, Optional, Any
6
- import json
7
- from jarvis.jarvis_lsp.base import BaseLSP
8
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
-
10
- class CPPLSP(BaseLSP):
11
- """C++ LSP implementation using clangd."""
12
-
13
- language = ["cpp", "c"]
14
-
15
- @staticmethod
16
- def check() -> bool:
17
- """Check if clangd is installed."""
18
- return shutil.which("clangd") is not None
19
-
20
- def __init__(self):
21
- self.workspace_path = ""
22
- self.clangd_process = None
23
- self.request_id = 0
24
-
25
- def initialize(self, workspace_path: str) -> bool:
26
- try:
27
- self.workspace_path = workspace_path
28
- # Start clangd process
29
- self.clangd_process = subprocess.Popen(
30
- ["clangd", "--background-index"],
31
- stdin=subprocess.PIPE,
32
- stdout=subprocess.PIPE,
33
- stderr=subprocess.PIPE
34
- )
35
-
36
- # Send initialize request
37
- self._send_request("initialize", {
38
- "processId": os.getpid(),
39
- "rootUri": f"file://{workspace_path}",
40
- "capabilities": {}
41
- })
42
-
43
- return True
44
- except Exception as e:
45
- PrettyOutput.print(f"C++ LSP 初始化失败: {str(e)}", OutputType.ERROR)
46
- return False
47
-
48
- def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
49
- """Send JSON-RPC request to clangd."""
50
- if not self.clangd_process:
51
- return None
52
-
53
- try:
54
- self.request_id += 1
55
- request = {
56
- "jsonrpc": "2.0",
57
- "id": self.request_id,
58
- "method": method,
59
- "params": params
60
- }
61
-
62
- self.clangd_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
63
- self.clangd_process.stdin.flush() # type: ignore
64
-
65
- response = json.loads(self.clangd_process.stdout.readline().decode()) # type: ignore
66
- return response.get("result")
67
- except Exception:
68
- return None
69
-
70
-
71
- def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
72
- # Send didOpen notification to trigger diagnostics
73
- self._send_request("textDocument/didOpen", {
74
- "textDocument": {
75
- "uri": f"file://{file_path}",
76
- "languageId": "cpp",
77
- "version": 1,
78
- "text": open(file_path).read()
79
- }
80
- })
81
-
82
- # Wait for diagnostic notification
83
- try:
84
- response = json.loads(self.clangd_process.stdout.readline().decode()) # type: ignore
85
- if response.get("method") == "textDocument/publishDiagnostics":
86
- return response.get("params", {}).get("diagnostics", [])
87
- except Exception:
88
- pass
89
- return []
90
-
91
-
92
- def shutdown(self):
93
- if self.clangd_process:
94
- try:
95
- self._send_request("shutdown", {})
96
- self.clangd_process.terminate()
97
- self.clangd_process = None
98
- except Exception:
99
- pass
jarvis/jarvis_lsp/go.py DELETED
@@ -1,104 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import shutil
4
- import subprocess
5
- from typing import List, Dict, Optional, Any
6
- import json
7
- from jarvis.jarvis_lsp.base import BaseLSP
8
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
-
10
- class GoLSP(BaseLSP):
11
- """Go LSP implementation using gopls."""
12
-
13
- language = "go"
14
-
15
- @staticmethod
16
- def check() -> bool:
17
- """Check if gopls is installed."""
18
- return shutil.which("gopls") is not None
19
-
20
- def __init__(self):
21
- self.workspace_path = ""
22
- self.gopls_process = None
23
- self.request_id = 0
24
-
25
- def initialize(self, workspace_path: str) -> bool:
26
- try:
27
- self.workspace_path = workspace_path
28
- # Start gopls process
29
- self.gopls_process = subprocess.Popen(
30
- ["gopls", "serve"],
31
- stdin=subprocess.PIPE,
32
- stdout=subprocess.PIPE,
33
- stderr=subprocess.PIPE
34
- )
35
-
36
- # Send initialize request
37
- self._send_request("initialize", {
38
- "processId": os.getpid(),
39
- "rootUri": f"file://{workspace_path}",
40
- "capabilities": {
41
- "textDocument": {
42
- "hover": {"contentFormat": ["markdown", "plaintext"]},
43
- "completion": {"completionItem": {"snippetSupport": True}},
44
- "signatureHelp": {"signatureInformation": {"documentationFormat": ["markdown", "plaintext"]}},
45
- }
46
- }
47
- })
48
-
49
- return True
50
- except Exception as e:
51
- PrettyOutput.print(f"Go LSP 初始化失败: {str(e)}", OutputType.ERROR)
52
- return False
53
-
54
- def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
55
- """Send JSON-RPC request to gopls."""
56
- if not self.gopls_process:
57
- return None
58
-
59
- try:
60
- self.request_id += 1
61
- request = {
62
- "jsonrpc": "2.0",
63
- "id": self.request_id,
64
- "method": method,
65
- "params": params
66
- }
67
-
68
- self.gopls_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
69
- self.gopls_process.stdin.flush() # type: ignore
70
-
71
- response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
72
- return response.get("result")
73
- except Exception:
74
- return None
75
-
76
- def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
77
- # Send didOpen notification to trigger diagnostics
78
- self._send_request("textDocument/didOpen", {
79
- "textDocument": {
80
- "uri": f"file://{file_path}",
81
- "languageId": "go",
82
- "version": 1,
83
- "text": open(file_path).read()
84
- }
85
- })
86
-
87
- # Wait for diagnostic notification
88
- try:
89
- response = json.loads(self.gopls_process.stdout.readline().decode()) # type: ignore
90
- if response.get("method") == "textDocument/publishDiagnostics":
91
- return response.get("params", {}).get("diagnostics", [])
92
- except Exception:
93
- pass
94
- return []
95
-
96
-
97
- def shutdown(self):
98
- if self.gopls_process:
99
- try:
100
- self._send_request("shutdown", {})
101
- self.gopls_process.terminate()
102
- self.gopls_process = None
103
- except Exception:
104
- pass
@@ -1,58 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from typing import List, Dict, Any
3
- import jedi
4
- from jarvis.jarvis_lsp.base import BaseLSP
5
-
6
- class PythonLSP(BaseLSP):
7
- """Python LSP implementation using jedi."""
8
-
9
- language = "python"
10
-
11
- def __init__(self):
12
- self.workspace_path = ""
13
- self.script_cache = {}
14
-
15
- def initialize(self, workspace_path: str) -> bool:
16
- self.workspace_path = workspace_path
17
- return True
18
-
19
- def _get_script(self, file_path: str):
20
- if file_path not in self.script_cache:
21
- try:
22
- with open(file_path, 'r', errors="ignore") as f:
23
- content = f.read()
24
- self.script_cache[file_path] = jedi.Script(code=content, path=file_path)
25
- except Exception:
26
- return None
27
- return self.script_cache[file_path]
28
-
29
-
30
- def _location_to_dict(self, location) -> Dict[str, Any]:
31
- return {
32
- "uri": location.module_path,
33
- "range": {
34
- "start": {"line": location.line - 1, "character": location.column},
35
- "end": {"line": location.line - 1, "character": location.column + len(location.name)}
36
- }
37
- }
38
-
39
- def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
40
- script = self._get_script(file_path)
41
- if not script:
42
- return []
43
- try:
44
- errors = script.get_syntax_errors()
45
- return [{
46
- "range": {
47
- "start": {"line": e.line - 1, "character": e.column},
48
- "end": {"line": e.line - 1, "character": e.column + 1}
49
- },
50
- "severity": 1, # Error
51
- "source": "jedi",
52
- "message": str(e)
53
- } for e in errors]
54
- except Exception:
55
- return []
56
-
57
- def shutdown(self):
58
- self.script_cache.clear()
@@ -1,169 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import importlib
3
- import inspect
4
- import os
5
- import re
6
- import sys
7
- from typing import Dict, Type, Optional, List
8
- from jarvis.jarvis_lsp.base import BaseLSP
9
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
10
- from jarvis.jarvis_utils.config import get_data_dir
11
-
12
- REQUIRED_METHODS = [
13
- ('initialize', ['workspace_path']),
14
- ('get_diagnostics', ['file_path']),
15
- ('shutdown', [])
16
- ]
17
-
18
- class LSPRegistry:
19
- """LSP server registry"""
20
-
21
- global_lsp_registry = None
22
-
23
- @staticmethod
24
- def get_lsp_dir() -> str:
25
- """Get LSP implementation directory."""
26
- user_lsp_dir = os.path.join(get_data_dir(), "lsp")
27
- if not os.path.exists(user_lsp_dir):
28
- try:
29
- os.makedirs(user_lsp_dir)
30
- with open(os.path.join(user_lsp_dir, "__init__.py"), "w", errors="ignore") as f:
31
- pass
32
- except Exception as e:
33
- PrettyOutput.print(f"创建 LSP 目录失败: {str(e)}", OutputType.ERROR)
34
- return ""
35
- return user_lsp_dir
36
-
37
- @staticmethod
38
- def check_lsp_implementation(lsp_class: Type[BaseLSP]) -> bool:
39
- """Check if the LSP class implements all necessary methods."""
40
- missing_methods = []
41
-
42
- for method_name, params in REQUIRED_METHODS:
43
- if not hasattr(lsp_class, method_name):
44
- missing_methods.append(method_name)
45
- continue
46
-
47
- method = getattr(lsp_class, method_name)
48
- if not callable(method):
49
- missing_methods.append(method_name)
50
- continue
51
-
52
- sig = inspect.signature(method)
53
- method_params = [p for p in sig.parameters if p != 'self']
54
- if len(method_params) != len(params):
55
- missing_methods.append(f"{method_name}(parameter mismatch)")
56
-
57
- if missing_methods:
58
- PrettyOutput.print(
59
- f"LSP {lsp_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
60
- OutputType.WARNING
61
- )
62
- return False
63
-
64
- return True
65
-
66
- @staticmethod
67
- def load_lsp_from_dir(directory: str) -> Dict[str, Type[BaseLSP]]:
68
- """Load LSP implementations from specified directory."""
69
- lsp_servers = {}
70
-
71
- if not os.path.exists(directory):
72
- PrettyOutput.print(f"LSP 目录不存在: {directory}", OutputType.WARNING)
73
- return lsp_servers
74
-
75
- package_name = None
76
- if directory == os.path.dirname(__file__):
77
- package_name = "jarvis.jarvis_lsp"
78
-
79
- if directory not in sys.path:
80
- sys.path.append(directory)
81
-
82
- for filename in os.listdir(directory):
83
- if filename.endswith('.py') and not filename.startswith('__'):
84
- module_name = filename[:-3]
85
- try:
86
- if package_name:
87
- module = importlib.import_module(f"{package_name}.{module_name}")
88
- else:
89
- module = importlib.import_module(module_name)
90
-
91
- for _, obj in inspect.getmembers(module):
92
- if (inspect.isclass(obj) and
93
- issubclass(obj, BaseLSP) and
94
- obj != BaseLSP and
95
- hasattr(obj, 'language')):
96
- if not LSPRegistry.check_lsp_implementation(obj):
97
- continue
98
- if hasattr(obj, 'check'):
99
- if not obj.check(): # type: ignore
100
- continue
101
- if isinstance(obj.language, str):
102
- lsp_servers[obj.language] = obj
103
- elif isinstance(obj.language, list):
104
- for lang in obj.language: # type: ignore
105
- lsp_servers[lang] = obj
106
- break
107
- except Exception as e:
108
- PrettyOutput.print(f"加载 LSP {module_name} 失败: {str(e)}", OutputType.ERROR)
109
-
110
- return lsp_servers
111
-
112
- @staticmethod
113
- def get_global_lsp_registry():
114
- """Get global LSP registry instance."""
115
- if LSPRegistry.global_lsp_registry is None:
116
- LSPRegistry.global_lsp_registry = LSPRegistry()
117
- return LSPRegistry.global_lsp_registry
118
-
119
- def __init__(self):
120
- """Initialize LSP registry."""
121
- self.lsp_servers: Dict[str, Type[BaseLSP]] = {}
122
-
123
- # Load from user LSP directory
124
- lsp_dir = LSPRegistry.get_lsp_dir()
125
- if lsp_dir and os.path.exists(lsp_dir):
126
- for language, lsp_class in LSPRegistry.load_lsp_from_dir(lsp_dir).items():
127
- self.register_lsp(language, lsp_class)
128
-
129
- # Load from built-in LSP directory
130
- lsp_dir = os.path.dirname(__file__)
131
- if lsp_dir and os.path.exists(lsp_dir):
132
- for language, lsp_class in LSPRegistry.load_lsp_from_dir(lsp_dir).items():
133
- self.register_lsp(language, lsp_class)
134
-
135
- def register_lsp(self, language: str, lsp_class: Type[BaseLSP]):
136
- """Register LSP implementation for a language."""
137
- self.lsp_servers[language] = lsp_class
138
-
139
- def create_lsp(self, language: str) -> Optional[BaseLSP]:
140
- """Create LSP instance for specified language."""
141
- if language not in self.lsp_servers:
142
- PrettyOutput.print(f"没有找到 LSP 支持的语言: {language}", OutputType.WARNING)
143
- return None
144
-
145
- try:
146
- lsp = self.lsp_servers[language]()
147
- return lsp
148
- except Exception as e:
149
- PrettyOutput.print(f"创建 LSP 失败: {str(e)}", OutputType.ERROR)
150
- return None
151
-
152
- def get_supported_languages(self) -> List[str]:
153
- """Get list of supported languages."""
154
- return list(self.lsp_servers.keys())
155
-
156
- @staticmethod
157
- def get_text_at_position(file_path: str, line: int, start_character: int) -> str:
158
- """Get text at position."""
159
- with open(file_path, 'r', errors="ignore") as file:
160
- lines = file.readlines()
161
- symbol = re.search(r'\b\w+\b', lines[line][start_character:])
162
- return symbol.group() if symbol else ""
163
-
164
- @staticmethod
165
- def get_line_at_position(file_path: str, line: int) -> str:
166
- """Get line at position."""
167
- with open(file_path, 'r', errors="ignore") as file:
168
- lines = file.readlines()
169
- return lines[line]
jarvis/jarvis_lsp/rust.py DELETED
@@ -1,107 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import shutil
4
- import subprocess
5
- from typing import List, Dict, Optional, Any
6
- import json
7
- from jarvis.jarvis_lsp.base import BaseLSP
8
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
-
10
- class RustLSP(BaseLSP):
11
- """Rust LSP implementation using rust-analyzer."""
12
-
13
- language = "rust"
14
-
15
- @staticmethod
16
- def check() -> bool:
17
- """Check if rust-analyzer is installed."""
18
- return shutil.which("rust-analyzer") is not None
19
-
20
- def __init__(self):
21
- self.workspace_path = ""
22
- self.analyzer_process = None
23
- self.request_id = 0
24
-
25
- def initialize(self, workspace_path: str) -> bool:
26
- try:
27
- self.workspace_path = workspace_path
28
- # Start rust-analyzer process
29
- self.analyzer_process = subprocess.Popen(
30
- ["rust-analyzer"],
31
- stdin=subprocess.PIPE,
32
- stdout=subprocess.PIPE,
33
- stderr=subprocess.PIPE
34
- )
35
-
36
- # Send initialize request
37
- self._send_request("initialize", {
38
- "processId": os.getpid(),
39
- "rootUri": f"file://{workspace_path}",
40
- "capabilities": {
41
- "textDocument": {
42
- "semanticTokens": {"full": True},
43
- "hover": {"contentFormat": ["markdown"]},
44
- "inlayHint": {"resolveSupport": {"properties": ["label.tooltip", "label.location", "label.command"]}},
45
- "completion": {"completionItem": {"snippetSupport": True}}
46
- }
47
- },
48
- "workspaceFolders": [{"uri": f"file://{workspace_path}", "name": "workspace"}]
49
- })
50
-
51
- return True
52
- except Exception as e:
53
- PrettyOutput.print(f"Rust LSP 初始化失败: {str(e)}", OutputType.ERROR)
54
- return False
55
-
56
- def _send_request(self, method: str, params: Dict) -> Optional[Dict]:
57
- """Send JSON-RPC request to rust-analyzer."""
58
- if not self.analyzer_process:
59
- return None
60
-
61
- try:
62
- self.request_id += 1
63
- request = {
64
- "jsonrpc": "2.0",
65
- "id": self.request_id,
66
- "method": method,
67
- "params": params
68
- }
69
-
70
- self.analyzer_process.stdin.write(json.dumps(request).encode() + b"\n") # type: ignore
71
- self.analyzer_process.stdin.flush() # type: ignore
72
-
73
- response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
74
- return response.get("result")
75
- except Exception:
76
- return None
77
-
78
-
79
- def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
80
- # Send didOpen notification to trigger diagnostics
81
- self._send_request("textDocument/didOpen", {
82
- "textDocument": {
83
- "uri": f"file://{file_path}",
84
- "languageId": "rust",
85
- "version": 1,
86
- "text": open(file_path).read()
87
- }
88
- })
89
-
90
- # Wait for diagnostic notification
91
- try:
92
- response = json.loads(self.analyzer_process.stdout.readline().decode()) # type: ignore
93
- if response.get("method") == "textDocument/publishDiagnostics":
94
- return response.get("params", {}).get("diagnostics", [])
95
- except Exception:
96
- pass
97
- return []
98
-
99
-
100
- def shutdown(self):
101
- if self.analyzer_process:
102
- try:
103
- self._send_request("shutdown", {})
104
- self.analyzer_process.terminate()
105
- self.analyzer_process = None
106
- except Exception:
107
- pass