luaur_analysis/functions/
autocomplete_string_params.rs1use crate::functions::convert_require_suggestions_to_autocomplete_entry_map::convert_require_suggestions_to_autocomplete_entry_map;
2use crate::functions::follow_type::follow_type_id;
3use crate::functions::get_method_containing_extern_type::get_method_containing_extern_type;
4use crate::functions::get_string_contents::get_string_contents;
5use crate::functions::get_type_alt_j::get_type_id;
6use crate::functions::process_require_suggestions::process_require_suggestions;
7use crate::records::file_resolver::FileResolver;
8use crate::records::function_type::FunctionType;
9use crate::records::intersection_type::IntersectionType;
10use crate::type_aliases::autocomplete_entry_map::AutocompleteEntryMap;
11use crate::type_aliases::module_ptr_module::ModulePtr;
12use crate::type_aliases::string_completion_callback::StringCompletionCallback;
13use luaur_ast::records::ast_expr::AstExpr;
14use luaur_ast::records::ast_expr_call::AstExprCall;
15use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
16use luaur_ast::records::ast_expr_error::AstExprError;
17use luaur_ast::records::ast_expr_interp_string::AstExprInterpString;
18use luaur_ast::records::ast_node::AstNode;
19use luaur_ast::records::position::Position;
20use luaur_ast::rtti::{ast_node_as, ast_node_as_const, ast_node_is};
21
22const K_REQUIRE_TAG_NAME: &str = "require";
23
24pub fn autocomplete_string_params(
25 module: &ModulePtr,
26 nodes: &alloc::vec::Vec<*mut AstNode>,
27 position: Position,
28 file_resolver: *mut FileResolver,
29 callback: StringCompletionCallback,
30) -> Option<AutocompleteEntryMap> {
31 if nodes.len() < 2 {
32 return None;
33 }
34
35 let last = *nodes.last()?;
36 if !unsafe { ast_node_is::<AstExprConstantString>(&*last) }
37 && !is_simple_interpolated_string(last)
38 && !unsafe { ast_node_is::<AstExprError>(&*last) }
39 {
40 return None;
41 }
42
43 if !unsafe { ast_node_is::<AstExprError>(&*last) } {
44 let last_location = unsafe { (*last).location };
45 if last_location.end == position || last_location.begin == position {
46 return None;
47 }
48 }
49
50 let candidate_node = nodes[nodes.len() - 2];
51 let candidate = unsafe { ast_node_as::<AstExprCall>(candidate_node) };
52 if candidate.is_null() {
53 return None;
54 }
55
56 let candidate_ref = unsafe { &*candidate };
57
58 if candidate_ref.args.size > 1 {
59 let first_arg = unsafe { *candidate_ref.args.data };
60 let first_arg_location = unsafe { (*first_arg).base.location };
61 if !first_arg_location.contains(position) {
62 return None;
63 }
64 }
65
66 let it = module
67 .ast_types
68 .find(&(candidate_ref.func as *const AstExpr))?;
69 let candidate_string = get_string_contents(last as *const AstNode);
70
71 let mut perform_callback = |func_type: &FunctionType| -> Option<AutocompleteEntryMap> {
72 for tag in &func_type.tags {
73 if tag == K_REQUIRE_TAG_NAME && !file_resolver.is_null() {
74 let suggestions = unsafe {
75 (*file_resolver)
76 .require_suggester
77 .as_ref()
78 .and_then(|suggester| {
79 suggester.get_require_suggestions_impl(&module.name, &candidate_string)
80 })
81 };
82 return convert_require_suggestions_to_autocomplete_entry_map(
83 process_require_suggestions(suggestions),
84 );
85 }
86
87 if let Some(ret) = callback(
88 tag.clone(),
89 get_method_containing_extern_type(module, candidate_ref.func),
90 candidate_string.clone(),
91 ) {
92 return Some(ret);
93 }
94 }
95
96 None
97 };
98
99 let followed_id = unsafe { follow_type_id(*it) };
100 let function_type = unsafe { get_type_id::<FunctionType>(followed_id) };
101 if !function_type.is_null() {
102 return perform_callback(unsafe { &*function_type });
103 }
104
105 let intersect = unsafe { get_type_id::<IntersectionType>(followed_id) };
106 if !intersect.is_null() {
107 for part in unsafe { &(*intersect).parts } {
108 let part = unsafe { follow_type_id(*part) };
109 let candidate_function_type = unsafe { get_type_id::<FunctionType>(part) };
110 if !candidate_function_type.is_null() {
111 if let Some(ret) = perform_callback(unsafe { &*candidate_function_type }) {
112 return Some(ret);
113 }
114 }
115 }
116 }
117
118 None
119}
120
121fn is_simple_interpolated_string(node: *const AstNode) -> bool {
122 let interp_string = unsafe { ast_node_as_const::<AstExprInterpString>(node) };
123 !interp_string.is_null() && unsafe { (*interp_string).expressions.size == 0 }
124}