Skip to main content

claude_rust_tools/infrastructure/
lsp_tool.rs

1use claude_rust_errors::AppResult;
2use claude_rust_types::{PermissionLevel, Tool};
3use serde_json::{Value, json};
4
5/// Tool for LSP operations (diagnostics, hover, goto definition, etc.).
6///
7/// TODO: Phase 4 — integrate with real LSP server.
8pub struct LSPTool;
9
10impl LSPTool {
11    pub fn new() -> Self {
12        Self
13    }
14}
15
16#[async_trait::async_trait]
17impl Tool for LSPTool {
18    fn name(&self) -> &str {
19        "lsp"
20    }
21
22    fn description(&self) -> &str {
23        "Perform LSP operations such as diagnostics, hover, goto definition, references, and code actions."
24    }
25
26    fn input_schema(&self) -> Value {
27        json!({
28            "type": "object",
29            "properties": {
30                "operation": {
31                    "type": "string",
32                    "description": "LSP operation: \"diagnostics\", \"hover\", \"goto_definition\", \"references\", or \"code_actions\"",
33                    "enum": ["diagnostics", "hover", "goto_definition", "references", "code_actions"]
34                },
35                "file_path": {
36                    "type": "string",
37                    "description": "Path to the file for the LSP operation"
38                },
39                "line": {
40                    "type": "integer",
41                    "description": "Line number (0-indexed) for position-based operations"
42                },
43                "character": {
44                    "type": "integer",
45                    "description": "Character offset (0-indexed) for position-based operations"
46                }
47            },
48            "required": ["operation", "file_path"]
49        })
50    }
51
52    fn permission_level(&self) -> PermissionLevel {
53        PermissionLevel::ReadOnly
54    }
55
56    fn is_lsp(&self) -> bool { true }
57    fn is_read_only(&self, _input: &Value) -> bool { true }
58    fn is_concurrent_safe(&self, _input: &Value) -> bool { true }
59
60    async fn execute(&self, input: Value) -> AppResult<String> {
61        let operation = input
62            .get("operation")
63            .and_then(|v| v.as_str())
64            .ok_or_else(|| claude_rust_errors::AppError::Tool("missing 'operation' field".into()))?;
65
66        let file_path = input
67            .get("file_path")
68            .and_then(|v| v.as_str())
69            .ok_or_else(|| claude_rust_errors::AppError::Tool("missing 'file_path' field".into()))?;
70
71        let line = input.get("line").and_then(|v| v.as_u64());
72        let character = input.get("character").and_then(|v| v.as_u64());
73
74        // TODO: Phase 4 — replace stub with real LSP integration
75        tracing::info!(operation, file_path, ?line, ?character, "LSP operation (stub)");
76
77        Ok(format!(
78            "LSP server not running. Operation '{operation}' on '{file_path}' cannot be performed.\n\
79             Hint: LSP integration will be available in Phase 4."
80        ))
81    }
82}