1pub mod symbol_resolver;
32
33pub use symbol_resolver::{SymbolResolver, SymbolScope};
34
35use crate::types::{HoverInfo, MarkupContent, Position, Symbol};
36use std::collections::HashMap;
37
38pub struct HoverProvider {
40 symbol_index: HashMap<String, Symbol>,
42}
43
44impl HoverProvider {
45 pub fn new() -> Self {
47 Self {
48 symbol_index: HashMap::new(),
49 }
50 }
51
52 pub fn index_symbols(&mut self, symbols: Vec<Symbol>) {
54 self.symbol_index.clear();
55 for symbol in symbols {
56 self.symbol_index.insert(symbol.name.clone(), symbol);
57 }
58 }
59
60 pub fn get_hover_info(&self, code: &str, position: Position) -> Option<HoverInfo> {
62 let symbol = self.find_symbol_at_position(code, position)?;
64
65 let mut content = String::new();
67
68 content.push_str(&format!("**{}** `{:?}`\n\n", symbol.name, symbol.kind));
70
71 if let Some(doc) = &symbol.documentation {
73 content.push_str(doc);
74 content.push_str("\n\n");
75 }
76
77 if let Some(def) = &symbol.definition {
79 content.push_str(&format!(
80 "**Defined at**: `{}`:{}-{}\n",
81 def.uri, def.range.start.line, def.range.start.character
82 ));
83 }
84
85 let usage_count = symbol.references.len();
87 content.push_str(&format!("**References**: {}", usage_count));
88
89 let hover_info = HoverInfo::new(MarkupContent::markdown(content)).with_range(symbol.range);
90
91 Some(hover_info)
92 }
93
94 fn find_symbol_at_position(&self, code: &str, position: Position) -> Option<Symbol> {
96 let target_offset = self.position_to_offset(code, position)?;
98
99 for symbol in self.symbol_index.values() {
101 let start_offset = self.position_to_offset(code, symbol.range.start)?;
102 let end_offset = self.position_to_offset(code, symbol.range.end)?;
103
104 if target_offset >= start_offset && target_offset <= end_offset {
105 return Some(symbol.clone());
106 }
107 }
108
109 None
110 }
111
112 fn position_to_offset(&self, code: &str, position: Position) -> Option<usize> {
114 let mut offset = 0;
115 let mut current_line = 0;
116 let mut current_char = 0;
117
118 for ch in code.chars() {
119 if current_line == position.line && current_char == position.character {
120 return Some(offset);
121 }
122
123 if ch == '\n' {
124 current_line += 1;
125 current_char = 0;
126 } else {
127 current_char += 1;
128 }
129
130 offset += ch.len_utf8();
131 }
132
133 if current_line == position.line && current_char == position.character {
135 return Some(offset);
136 }
137
138 None
139 }
140
141 pub fn get_type_info(&self, symbol_name: &str) -> Option<String> {
143 self.symbol_index
144 .get(symbol_name)
145 .map(|symbol| format!("{:?}", symbol.kind))
146 }
147
148 pub fn get_definition_location(&self, symbol_name: &str) -> Option<(String, u32, u32)> {
150 self.symbol_index.get(symbol_name).and_then(|symbol| {
151 symbol.definition.as_ref().map(|def| {
152 (
153 def.uri.clone(),
154 def.range.start.line,
155 def.range.start.character,
156 )
157 })
158 })
159 }
160
161 pub fn get_documentation(&self, symbol_name: &str) -> Option<String> {
163 self.symbol_index
164 .get(symbol_name)
165 .and_then(|symbol| symbol.documentation.clone())
166 }
167
168 pub fn get_usage_count(&self, symbol_name: &str) -> usize {
170 self.symbol_index
171 .get(symbol_name)
172 .map(|symbol| symbol.references.len())
173 .unwrap_or(0)
174 }
175}
176
177impl Default for HoverProvider {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186 use crate::types::{Definition, Range, SymbolKind};
187
188 #[test]
189 fn test_hover_provider_creation() {
190 let provider = HoverProvider::new();
191 assert_eq!(provider.symbol_index.len(), 0);
192 }
193
194 #[test]
195 fn test_index_symbols() {
196 let mut provider = HoverProvider::new();
197 let symbol = Symbol {
198 name: "test_fn".to_string(),
199 kind: SymbolKind::Function,
200 range: Range::new(Position::new(0, 0), Position::new(0, 7)),
201 definition: None,
202 references: vec![],
203 documentation: None,
204 };
205
206 provider.index_symbols(vec![symbol.clone()]);
207 assert_eq!(provider.symbol_index.len(), 1);
208 assert!(provider.symbol_index.contains_key("test_fn"));
209 }
210
211 #[test]
212 fn test_get_type_info() {
213 let mut provider = HoverProvider::new();
214 let symbol = Symbol {
215 name: "my_var".to_string(),
216 kind: SymbolKind::Variable,
217 range: Range::new(Position::new(0, 0), Position::new(0, 6)),
218 definition: None,
219 references: vec![],
220 documentation: None,
221 };
222
223 provider.index_symbols(vec![symbol]);
224 let type_info = provider.get_type_info("my_var");
225 assert!(type_info.is_some());
226 }
227
228 #[test]
229 fn test_get_definition_location() {
230 let mut provider = HoverProvider::new();
231 let definition = Definition {
232 uri: "file://test.rs".to_string(),
233 range: Range::new(Position::new(5, 0), Position::new(5, 10)),
234 };
235
236 let symbol = Symbol {
237 name: "my_fn".to_string(),
238 kind: SymbolKind::Function,
239 range: Range::new(Position::new(0, 0), Position::new(0, 5)),
240 definition: Some(definition),
241 references: vec![],
242 documentation: None,
243 };
244
245 provider.index_symbols(vec![symbol]);
246 let location = provider.get_definition_location("my_fn");
247 assert!(location.is_some());
248 let (uri, line, char) = location.unwrap();
249 assert_eq!(uri, "file://test.rs");
250 assert_eq!(line, 5);
251 assert_eq!(char, 0);
252 }
253
254 #[test]
255 fn test_get_documentation() {
256 let mut provider = HoverProvider::new();
257 let symbol = Symbol {
258 name: "documented_fn".to_string(),
259 kind: SymbolKind::Function,
260 range: Range::new(Position::new(0, 0), Position::new(0, 13)),
261 definition: None,
262 references: vec![],
263 documentation: Some("This is a test function".to_string()),
264 };
265
266 provider.index_symbols(vec![symbol]);
267 let doc = provider.get_documentation("documented_fn");
268 assert_eq!(doc, Some("This is a test function".to_string()));
269 }
270
271 #[test]
272 fn test_get_usage_count() {
273 let mut provider = HoverProvider::new();
274 let symbol = Symbol {
275 name: "used_fn".to_string(),
276 kind: SymbolKind::Function,
277 range: Range::new(Position::new(0, 0), Position::new(0, 7)),
278 definition: None,
279 references: vec![
280 crate::types::Reference {
281 uri: "file://test.rs".to_string(),
282 range: Range::new(Position::new(10, 0), Position::new(10, 7)),
283 },
284 crate::types::Reference {
285 uri: "file://test.rs".to_string(),
286 range: Range::new(Position::new(20, 0), Position::new(20, 7)),
287 },
288 ],
289 documentation: None,
290 };
291
292 provider.index_symbols(vec![symbol]);
293 let count = provider.get_usage_count("used_fn");
294 assert_eq!(count, 2);
295 }
296
297 #[test]
298 fn test_position_to_offset() {
299 let provider = HoverProvider::new();
300 let code = "fn main() {\n println!(\"hello\");\n}";
301
302 let offset = provider.position_to_offset(code, Position::new(0, 0));
304 assert_eq!(offset, Some(0));
305
306 let offset = provider.position_to_offset(code, Position::new(1, 0));
308 assert_eq!(offset, Some(12)); let offset = provider.position_to_offset(code, Position::new(0, 2));
312 assert_eq!(offset, Some(2)); }
314
315 #[test]
316 fn test_find_symbol_at_position() {
317 let mut provider = HoverProvider::new();
318 let symbol = Symbol {
319 name: "test_fn".to_string(),
320 kind: SymbolKind::Function,
321 range: Range::new(Position::new(0, 0), Position::new(0, 7)),
322 definition: None,
323 references: vec![],
324 documentation: None,
325 };
326
327 provider.index_symbols(vec![symbol.clone()]);
328
329 let code = "test_fn";
330 let found = provider.find_symbol_at_position(code, Position::new(0, 3));
331 assert!(found.is_some());
332 assert_eq!(found.unwrap().name, "test_fn");
333 }
334
335 #[test]
336 fn test_get_hover_info() {
337 let mut provider = HoverProvider::new();
338 let symbol = Symbol {
339 name: "my_function".to_string(),
340 kind: SymbolKind::Function,
341 range: Range::new(Position::new(0, 0), Position::new(0, 11)),
342 definition: Some(Definition {
343 uri: "file://test.rs".to_string(),
344 range: Range::new(Position::new(5, 0), Position::new(5, 11)),
345 }),
346 references: vec![],
347 documentation: Some("A test function".to_string()),
348 };
349
350 provider.index_symbols(vec![symbol]);
351
352 let code = "my_function";
353 let hover = provider.get_hover_info(code, Position::new(0, 5));
354 assert!(hover.is_some());
355
356 let hover_info = hover.unwrap();
357 assert!(hover_info.contents.value.contains("my_function"));
358 assert!(hover_info.contents.value.contains("A test function"));
359 assert!(hover_info.range.is_some());
360 }
361}