rust_analyzer_mcp/lsp/
handlers.rs1use anyhow::Result;
2use log::info;
3use serde_json::{json, Value};
4
5use super::client::RustAnalyzerClient;
6
7impl RustAnalyzerClient {
8 pub async fn hover(&mut self, uri: &str, line: u32, character: u32) -> Result<Value> {
9 let params = json!({
10 "textDocument": { "uri": uri },
11 "position": { "line": line, "character": character }
12 });
13
14 self.send_request("textDocument/hover", Some(params)).await
15 }
16
17 pub async fn definition(&mut self, uri: &str, line: u32, character: u32) -> Result<Value> {
18 let params = json!({
19 "textDocument": { "uri": uri },
20 "position": { "line": line, "character": character }
21 });
22
23 self.send_request("textDocument/definition", Some(params))
24 .await
25 }
26
27 pub async fn references(&mut self, uri: &str, line: u32, character: u32) -> Result<Value> {
28 let params = json!({
29 "textDocument": { "uri": uri },
30 "position": { "line": line, "character": character },
31 "context": { "includeDeclaration": true }
32 });
33
34 self.send_request("textDocument/references", Some(params))
35 .await
36 }
37
38 pub async fn completion(&mut self, uri: &str, line: u32, character: u32) -> Result<Value> {
39 let params = json!({
40 "textDocument": { "uri": uri },
41 "position": { "line": line, "character": character }
42 });
43
44 self.send_request("textDocument/completion", Some(params))
45 .await
46 }
47
48 pub async fn document_symbols(&mut self, uri: &str) -> Result<Value> {
49 let params = json!({
50 "textDocument": { "uri": uri }
51 });
52
53 self.send_request("textDocument/documentSymbol", Some(params))
54 .await
55 }
56
57 pub async fn formatting(&mut self, uri: &str) -> Result<Value> {
58 let params = json!({
59 "textDocument": { "uri": uri },
60 "options": {
61 "tabSize": 4,
62 "insertSpaces": true
63 }
64 });
65
66 self.send_request("textDocument/formatting", Some(params))
67 .await
68 }
69
70 pub async fn diagnostics(&mut self, uri: &str) -> Result<Value> {
71 let diag_lock = self.diagnostics.lock().await;
73 info!("Looking for diagnostics for URI: {}", uri);
74 info!(
75 "Available URIs with diagnostics: {:?}",
76 diag_lock.keys().collect::<Vec<_>>()
77 );
78 if let Some(diags) = diag_lock.get(uri) {
79 info!("Found {} stored diagnostics for {}", diags.len(), uri);
80 return Ok(json!(diags));
81 }
82 drop(diag_lock);
83
84 info!("No stored diagnostics for {}, trying pull model", uri);
85 let params = json!({
87 "textDocument": { "uri": uri }
88 });
89
90 let response = self
91 .send_request("textDocument/diagnostic", Some(params))
92 .await?;
93
94 if let Some(items) = response.get("items") {
96 Ok(items.clone())
97 } else {
98 Ok(json!([]))
99 }
100 }
101
102 pub async fn workspace_diagnostics(&mut self) -> Result<Value> {
103 let params = json!({
105 "identifier": "rust-analyzer",
106 "previousResultId": null
107 });
108
109 match self
110 .send_request("workspace/diagnostic", Some(params))
111 .await
112 {
113 Ok(response) => Ok(response),
114 Err(e) if self.is_gone() => Err(e),
116 Err(_) => {
117 let mut all_diagnostics = json!({});
119 let open_docs = self.open_documents.lock().await.clone();
120
121 for doc_uri in open_docs.iter() {
122 if let Ok(diag) = self.diagnostics(doc_uri).await {
123 all_diagnostics[doc_uri] = diag;
124 }
125 }
126
127 Ok(all_diagnostics)
128 }
129 }
130 }
131
132 pub async fn code_actions(
133 &mut self,
134 uri: &str,
135 start_line: u32,
136 start_char: u32,
137 end_line: u32,
138 end_char: u32,
139 ) -> Result<Value> {
140 let diagnostics = self.diagnostics(uri).await.unwrap_or(json!([]));
142
143 let filtered_diagnostics = filter_diagnostics_in_range(&diagnostics, start_line, end_line);
145
146 let params = json!({
147 "textDocument": { "uri": uri },
148 "range": {
149 "start": { "line": start_line, "character": start_char },
150 "end": { "line": end_line, "character": end_char }
151 },
152 "context": {
153 "diagnostics": filtered_diagnostics,
154 "only": ["quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source"]
155 }
156 });
157
158 self.send_request("textDocument/codeAction", Some(params))
159 .await
160 }
161}
162
163fn filter_diagnostics_in_range(diagnostics: &Value, start_line: u32, end_line: u32) -> Value {
164 let Some(diag_array) = diagnostics.as_array() else {
165 return json!([]);
166 };
167
168 let filtered: Vec<Value> = diag_array
169 .iter()
170 .filter(|d| {
171 let Some(range) = d.get("range") else {
172 return false;
173 };
174 let Some(start) = range.get("start") else {
175 return false;
176 };
177 let Some(end) = range.get("end") else {
178 return false;
179 };
180
181 let diag_start_line = start.get("line").and_then(|l| l.as_u64()).unwrap_or(0) as u32;
182 let diag_end_line = end.get("line").and_then(|l| l.as_u64()).unwrap_or(0) as u32;
183
184 diag_start_line <= end_line && diag_end_line >= start_line
186 })
187 .cloned()
188 .collect();
189
190 json!(filtered)
191}