Skip to main content

grug_ls/server/
hover.rs

1use lsp_server::{Connection, ErrorCode, Message, RequestId, Response};
2use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind, Position, Range};
3use vfs::FileSystem;
4
5use crate::{
6    server::{
7        Server,
8        document::Document,
9        mod_api::ModApi,
10        utils::get_spot_info,
11    },
12};
13
14impl Server {
15    fn get_hover(
16        mod_api: &ModApi,
17        document: &Document,
18        node: &tree_sitter::Node<'_>,
19    ) -> Option<String> {
20        let range = node.byte_range();
21        if node.kind() == "identifier" {
22            let name = &document.content[range];
23            let spot_info = get_spot_info(document, node);
24            for var in spot_info.variables.into_iter() {
25                if var.name.as_bytes() == name {
26                    return Some(format!("{}: {}", var.name, var.r#type.as_str()));
27                }
28            }
29
30            let name = String::from_utf8(name.to_vec()).ok()?;
31
32            if let Some(func) = mod_api.game_functions.get(&name) {
33                let mut text = func.format(&name);
34                text.push_str("\n\n");
35
36                text.push_str(&func.description);
37
38                return Some(text);
39            }
40        } else if node.kind() == "helper_identifier" {
41            let name = &document.content[range];
42            
43            if let Some(helper) = document.helpers.iter().find(|helper| helper.name.as_bytes() == name) {
44                return Some(helper.format());
45            }
46        } else if node.kind() == "on_identifier" {
47            let name = String::from_utf8(document.content[range].to_vec()).ok()?;
48            if let Some(entity) = mod_api.entities.get(&document.entity_type) {
49                if let Some(on_func) = entity.on_functions.get(&name) {
50                    let mut text = name;
51                    text.push_str("\n\n");
52
53                    text.push_str(&on_func.description);
54                    return Some(text);
55                }
56            }
57        }
58
59        None
60    }
61    pub fn handle_hover(&self, params: HoverParams, connection: &mut Connection, id: RequestId) {
62        let uri = params.text_document_position_params.text_document.uri.as_str();
63        
64        // We probably wont need to use this server on TCP
65        assert!(uri.starts_with("file://"));
66
67        let path = &uri["file.//".len()..];
68
69        if !self.file_system.exists(path).unwrap_or(false) {
70            connection.sender.send(Message::Response(Response::new_err(id, ErrorCode::InvalidRequest as i32, format!("File doesnt exist: {}", path)))).unwrap();
71            return;
72        }
73
74        let document = &self.document_map.get(path).unwrap();
75
76        let ast = &document.tree;
77
78        let point = tree_sitter::Point {
79            column: params.text_document_position_params.position.character as usize,
80            row: params.text_document_position_params.position.line as usize,
81        };
82
83        let node = ast
84            .root_node()
85            .named_descendant_for_point_range(point, point)
86            .unwrap();
87
88        let range = node.range();
89
90        let content = Self::get_hover(&self.mod_api, document, &node);
91        if content.is_none() {
92            connection.sender.send(Message::Response(Response::new_ok(id, serde_json::Value::Null))).unwrap();
93            return;
94        }
95        let mut content = content.unwrap().as_bytes().to_vec();
96
97        let var = document
98            .global_vars
99            .iter()
100            .find(|var| var.name.as_bytes() == &content);
101        if let Some(var) = var {
102            content = format!("{}: {}", var.name, var.r#type.as_str())
103                .as_bytes()
104                .to_vec();
105        }
106
107        let res = Response::new_ok(id, Hover {
108            contents: HoverContents::Markup(MarkupContent {
109                kind: MarkupKind::PlainText,
110                value: String::from_utf8(content).unwrap(),
111            }),
112            range: Some(Range {
113                start: Position {
114                    line: range.start_point.row as u32,
115                    character: range.start_point.column as u32,
116                },
117                end: Position {
118                    line: range.end_point.row as u32,
119                    character: range.end_point.column as u32,
120                },
121            }),
122        });
123
124        connection.sender.send(Message::Response(res)).unwrap();
125    }
126}