Skip to main content

luaur_analysis/records/
autocomplete_node_finder.rs

1use alloc::vec::Vec;
2use luaur_ast::records::ast_node::AstNode;
3use luaur_ast::records::ast_visitor::AstVisitor;
4use luaur_ast::records::position::Position;
5
6#[derive(Debug, Clone)]
7pub struct AutocompleteNodeFinder {
8    pub(crate) pos: Position,
9    pub(crate) ancestry: Vec<*mut AstNode>,
10}
11
12impl AutocompleteNodeFinder {
13    pub fn new(pos: Position) -> Self {
14        Self {
15            pos,
16            ancestry: Vec::new(),
17        }
18    }
19}
20
21impl AstVisitor for AutocompleteNodeFinder {
22    fn visit_node(&mut self, _node: *mut core::ffi::c_void) -> bool {
23        true
24    }
25
26    fn visit_expr(&mut self, node: *mut core::ffi::c_void) -> bool {
27        let expr = node as *mut luaur_ast::records::ast_expr::AstExpr;
28        unsafe {
29            let loc = (*expr).base.location;
30            if loc.begin <= self.pos && self.pos <= loc.end && loc.begin != loc.end {
31                self.ancestry.push(expr as *mut AstNode);
32                return true;
33            }
34        }
35        false
36    }
37
38    fn visit_stat(&mut self, node: *mut core::ffi::c_void) -> bool {
39        let stat = node as *mut luaur_ast::records::ast_stat::AstStat;
40        unsafe {
41            let loc = (*stat).base.location;
42            let has_semicolon = (*stat).has_semicolon;
43            if loc.begin < self.pos
44                && (if has_semicolon {
45                    self.pos < loc.end
46                } else {
47                    self.pos <= loc.end
48                })
49            {
50                self.ancestry.push(stat as *mut AstNode);
51                return true;
52            }
53        }
54        false
55    }
56
57    fn visit_type(&mut self, node: *mut core::ffi::c_void) -> bool {
58        let ty = node as *mut luaur_ast::records::ast_type::AstType;
59        unsafe {
60            let loc = (*ty).base.location;
61            if loc.begin < self.pos && self.pos <= loc.end {
62                self.ancestry.push(ty as *mut AstNode);
63                return true;
64            }
65        }
66        false
67    }
68
69    fn visit_type_error(&mut self, node: *mut core::ffi::c_void) -> bool {
70        let ty = node as *mut luaur_ast::records::ast_type_error::AstTypeError;
71        unsafe {
72            if (*ty).is_missing && (*ty).base.base.location.containsClosed(self.pos) {
73                self.ancestry.push(ty as *mut AstNode);
74                return true;
75            }
76        }
77        false
78    }
79
80    fn visit_type_pack(&mut self, _node: *mut core::ffi::c_void) -> bool {
81        true
82    }
83
84    fn visit_stat_block(&mut self, node: *mut core::ffi::c_void) -> bool {
85        let block = node as *mut luaur_ast::records::ast_stat_block::AstStatBlock;
86        if self.ancestry.is_empty() {
87            self.ancestry.push(block as *mut AstNode);
88            return true;
89        }
90
91        unsafe {
92            let last = *self.ancestry.last().unwrap();
93            if (*last).is::<luaur_ast::records::ast_expr_index_name::AstExprIndexName>() {
94                return false;
95            }
96            if (*last).is::<luaur_ast::records::ast_type_error::AstTypeError>() {
97                return false;
98            }
99
100            let loc = (*block).base.base.location;
101            if loc.begin == self.pos {
102                if let Some(expr) = (*last).as_expr_const().as_ref() {
103                    if !(*last).is::<luaur_ast::records::ast_expr_function::AstExprFunction>() {
104                        return false;
105                    }
106                }
107                if (*last).as_type().as_ref().is_some() {
108                    return false;
109                }
110            }
111
112            if loc.begin <= self.pos && self.pos <= loc.end {
113                self.ancestry.push(block as *mut AstNode);
114                return true;
115            }
116        }
117        false
118    }
119}