1use crate::enums::autocomplete_entry_kind::AutocompleteEntryKind;
2use crate::enums::type_correct_kind::TypeCorrectKind;
3use crate::functions::autocomplete_keywords::autocomplete_keywords;
4use crate::functions::get_paren_recommendation::get_paren_recommendation;
5use crate::functions::is_binding_legal_at_current_position::is_binding_legal_at_current_position;
6use crate::functions::is_identifier::is_identifier;
7use crate::functions::is_in_local_names::is_in_local_names;
8use crate::functions::is_valid_break_continue_context::is_valid_break_continue_context;
9use crate::functions::to_string_symbol::to_string_symbol;
10use crate::records::autocomplete_entry::AutocompleteEntry;
11use crate::records::binding::Binding;
12use crate::records::module::Module;
13use crate::type_aliases::autocomplete_entry_map::AutocompleteEntryMap;
14use crate::type_aliases::scope_ptr_type::ScopePtr;
15
16use alloc::collections::BTreeMap;
17use alloc::string::String;
18
19use luaur_ast::records::ast_expr_function::AstExprFunction;
20use luaur_ast::records::ast_node::AstNode;
21use luaur_ast::records::ast_stat_block::AstStatBlock;
22use luaur_ast::records::ast_stat_error::AstStatError;
23use luaur_ast::records::ast_stat_for::AstStatFor;
24use luaur_ast::records::ast_stat_for_in::AstStatForIn;
25use luaur_ast::records::ast_stat_if::AstStatIf;
26use luaur_ast::records::ast_stat_repeat::AstStatRepeat;
27use luaur_ast::records::ast_stat_while::AstStatWhile;
28
29use luaur_ast::records::position::Position;
30
31use luaur_ast::rtti::ast_node_as;
32use luaur_ast::rtti::ast_node_is;
33use luaur_common::FFlag;
34
35#[allow(dead_code)]
36const kStatementStartingKeywords_DEPRECATED: [&str; 12] = [
37 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
38 "type", "export",
39];
40const K_STATEMENT_STARTING_KEYWORDS_DEPRECATED: [&str; 12] = [
41 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
42 "type", "export",
43];
44
45#[allow(dead_code)]
46const kStatementStartingKeywords_CONST: [&str; 13] = [
47 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
48 "type", "export", "const",
49];
50const K_STATEMENT_STARTING_KEYWORDS_CONST: [&str; 13] = [
51 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
52 "type", "export", "const",
53];
54
55#[allow(dead_code)]
56const kStatementStartingKeywords_EXPORT: [&str; 14] = [
57 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
58 "type", "export", "const", "export",
59];
60const K_STATEMENT_STARTING_KEYWORDS_EXPORT: [&str; 14] = [
61 "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue",
62 "type", "export", "const", "export",
63];
64
65pub fn autocomplete_statement(
66 module: &Module,
67 ancestry: &alloc::vec::Vec<*mut AstNode>,
68 scope_at_position: &ScopePtr,
69 position: &mut Position,
70) -> AutocompleteEntryMap {
71 let mut result: AutocompleteEntryMap = BTreeMap::new();
72
73 if is_in_local_names(ancestry, *position) {
74 autocomplete_keywords(ancestry, *position, &mut result);
75 return result;
76 }
77
78 let mut scope = Some(scope_at_position.clone());
79 while let Some(scope_ref) = scope {
80 for (name, binding) in &scope_ref.bindings {
81 if !is_binding_legal_at_current_position(name, binding, *position) {
82 continue;
83 }
84
85 let n = unsafe { to_string_symbol(name) };
86 if !result.contains_key(&n) {
87 result.insert(
88 n.clone(),
89 AutocompleteEntry {
90 kind: AutocompleteEntryKind::Binding,
91 r#type: Some(binding.type_id),
92 deprecated: binding.deprecated,
93 wrong_index_type: false,
94 type_correct: TypeCorrectKind::None,
95 containing_extern_type: None,
96 prop: None,
97 documentation_symbol: binding.documentation_symbol.clone(),
98 tags: Default::default(),
99 parens: get_paren_recommendation(
100 binding.type_id,
101 ancestry,
102 TypeCorrectKind::None,
103 ),
104 insert_text: None,
105 indexed_with_self: false,
106 },
107 );
108 }
109 }
110
111 scope = scope_ref.parent.clone();
112 }
113
114 let should_include_break_and_continue = is_valid_break_continue_context(ancestry, *position);
115
116 if FFlag::LuauExportValueSyntax.get() && FFlag::LuauAutocompleteExport.get() {
117 for &kw in &K_STATEMENT_STARTING_KEYWORDS_EXPORT {
118 if (kw != "break" && kw != "continue") || should_include_break_and_continue {
119 result.insert(
120 kw.to_string(),
121 AutocompleteEntry {
122 kind: AutocompleteEntryKind::Keyword,
123 r#type: None,
124 deprecated: false,
125 wrong_index_type: false,
126 type_correct: TypeCorrectKind::None,
127 containing_extern_type: None,
128 prop: None,
129 documentation_symbol: None,
130 tags: Default::default(),
131 parens: Default::default(),
132 insert_text: None,
133 indexed_with_self: false,
134 },
135 );
136 }
137 }
138 } else if FFlag::LuauAutocompleteConst.get() {
139 for &kw in &K_STATEMENT_STARTING_KEYWORDS_CONST {
140 if (kw != "break" && kw != "continue") || should_include_break_and_continue {
141 result.insert(
142 kw.to_string(),
143 AutocompleteEntry {
144 kind: AutocompleteEntryKind::Keyword,
145 r#type: None,
146 deprecated: false,
147 wrong_index_type: false,
148 type_correct: TypeCorrectKind::None,
149 containing_extern_type: None,
150 prop: None,
151 documentation_symbol: None,
152 tags: Default::default(),
153 parens: Default::default(),
154 insert_text: None,
155 indexed_with_self: false,
156 },
157 );
158 }
159 }
160 } else {
161 for &kw in &K_STATEMENT_STARTING_KEYWORDS_DEPRECATED {
162 if (kw != "break" && kw != "continue") || should_include_break_and_continue {
163 result.insert(
164 kw.to_string(),
165 AutocompleteEntry {
166 kind: AutocompleteEntryKind::Keyword,
167 r#type: None,
168 deprecated: false,
169 wrong_index_type: false,
170 type_correct: TypeCorrectKind::None,
171 containing_extern_type: None,
172 prop: None,
173 documentation_symbol: None,
174 tags: Default::default(),
175 parens: Default::default(),
176 insert_text: None,
177 indexed_with_self: false,
178 },
179 );
180 }
181 }
182 }
183
184 for it_idx in (0..ancestry.len()).rev() {
185 let node_ptr = ancestry[it_idx];
186 let ast_node = node_ptr as *mut AstNode;
187
188 if unsafe { ast_node_is::<AstStatForIn>(&*ast_node) }
189 && !unsafe { (*ast_node_as::<AstStatForIn>(ast_node)).body }.is_null()
190 {
191 let stat_for_in = unsafe { ast_node_as::<AstStatForIn>(ast_node) };
192 if !unsafe { (*(*stat_for_in).body).has_end } {
193 result.insert(
194 "end".to_string(),
195 AutocompleteEntry {
196 kind: AutocompleteEntryKind::Keyword,
197 r#type: None,
198 deprecated: false,
199 wrong_index_type: false,
200 type_correct: TypeCorrectKind::None,
201 containing_extern_type: None,
202 prop: None,
203 documentation_symbol: None,
204 tags: Default::default(),
205 parens: Default::default(),
206 insert_text: None,
207 indexed_with_self: false,
208 },
209 );
210 }
211 } else if unsafe { ast_node_is::<AstStatFor>(&*ast_node) } {
212 let stat_for = unsafe { ast_node_as::<AstStatFor>(ast_node) };
213 if !unsafe { (*(*stat_for).body).has_end } {
214 result.insert(
215 "end".to_string(),
216 AutocompleteEntry {
217 kind: AutocompleteEntryKind::Keyword,
218 r#type: None,
219 deprecated: false,
220 wrong_index_type: false,
221 type_correct: TypeCorrectKind::None,
222 containing_extern_type: None,
223 prop: None,
224 documentation_symbol: None,
225 tags: Default::default(),
226 parens: Default::default(),
227 insert_text: None,
228 indexed_with_self: false,
229 },
230 );
231 }
232 } else if unsafe { ast_node_is::<AstStatIf>(&*ast_node) } {
233 let stat_if = unsafe { ast_node_as::<AstStatIf>(ast_node) };
234 let mut has_end = unsafe { (*stat_if).thenbody }.is_null()
235 || unsafe { (*(*stat_if).thenbody).has_end };
236 if !unsafe { (*stat_if).elsebody }.is_null() {
237 let elsebody = unsafe { (*stat_if).elsebody };
238 let elsebody_node = elsebody as *mut AstNode;
239 if unsafe { ast_node_is::<AstStatBlock>(&*elsebody_node) } {
240 let else_block = unsafe { ast_node_as::<AstStatBlock>(elsebody_node) };
241 has_end = unsafe { (*else_block).has_end };
242 }
243 }
244 if !has_end {
245 result.insert(
246 "end".to_string(),
247 AutocompleteEntry {
248 kind: AutocompleteEntryKind::Keyword,
249 r#type: None,
250 deprecated: false,
251 wrong_index_type: false,
252 type_correct: TypeCorrectKind::None,
253 containing_extern_type: None,
254 prop: None,
255 documentation_symbol: None,
256 tags: Default::default(),
257 parens: Default::default(),
258 insert_text: None,
259 indexed_with_self: false,
260 },
261 );
262 }
263 } else if unsafe { ast_node_is::<AstStatWhile>(&*ast_node) } {
264 let stat_while = unsafe { ast_node_as::<AstStatWhile>(ast_node) };
265 if !unsafe { (*(*stat_while).body).has_end } {
266 result.insert(
267 "end".to_string(),
268 AutocompleteEntry {
269 kind: AutocompleteEntryKind::Keyword,
270 r#type: None,
271 deprecated: false,
272 wrong_index_type: false,
273 type_correct: TypeCorrectKind::None,
274 containing_extern_type: None,
275 prop: None,
276 documentation_symbol: None,
277 tags: Default::default(),
278 parens: Default::default(),
279 insert_text: None,
280 indexed_with_self: false,
281 },
282 );
283 }
284 } else if unsafe { ast_node_is::<AstExprFunction>(&*ast_node) } {
285 let expr_function = unsafe { ast_node_as::<AstExprFunction>(ast_node) };
286 if !unsafe { (*expr_function).body }.is_null()
287 && !unsafe { (*(*expr_function).body).has_end }
288 {
289 result.insert(
290 "end".to_string(),
291 AutocompleteEntry {
292 kind: AutocompleteEntryKind::Keyword,
293 r#type: None,
294 deprecated: false,
295 wrong_index_type: false,
296 type_correct: TypeCorrectKind::None,
297 containing_extern_type: None,
298 prop: None,
299 documentation_symbol: None,
300 tags: Default::default(),
301 parens: Default::default(),
302 insert_text: None,
303 indexed_with_self: false,
304 },
305 );
306 }
307 }
308
309 if unsafe { ast_node_is::<AstStatBlock>(&*ast_node) } {
310 let expr_block = unsafe { ast_node_as::<AstStatBlock>(ast_node) };
311 if !unsafe { (*expr_block).has_end } {
312 result.insert(
313 "end".to_string(),
314 AutocompleteEntry {
315 kind: AutocompleteEntryKind::Keyword,
316 r#type: None,
317 deprecated: false,
318 wrong_index_type: false,
319 type_correct: TypeCorrectKind::None,
320 containing_extern_type: None,
321 prop: None,
322 documentation_symbol: None,
323 tags: Default::default(),
324 parens: Default::default(),
325 insert_text: None,
326 indexed_with_self: false,
327 },
328 );
329 }
330 }
331 }
332
333 if ancestry.len() >= 2 {
334 let parent = ancestry[ancestry.len() - 2];
335 if unsafe { ast_node_is::<AstStatIf>(&*parent) } {
336 let stat_if = unsafe { ast_node_as::<AstStatIf>(parent) };
337 let elsebody = unsafe { (*stat_if).elsebody };
338 let else_location = unsafe { (*stat_if).else_location };
339 if elsebody.is_null()
340 || (else_location.is_some() && else_location.unwrap().containsClosed(*position))
341 {
342 result.insert(
343 "else".to_string(),
344 AutocompleteEntry {
345 kind: AutocompleteEntryKind::Keyword,
346 r#type: None,
347 deprecated: false,
348 wrong_index_type: false,
349 type_correct: TypeCorrectKind::None,
350 containing_extern_type: None,
351 prop: None,
352 documentation_symbol: None,
353 tags: Default::default(),
354 parens: Default::default(),
355 insert_text: None,
356 indexed_with_self: false,
357 },
358 );
359 result.insert(
360 "elseif".to_string(),
361 AutocompleteEntry {
362 kind: AutocompleteEntryKind::Keyword,
363 r#type: None,
364 deprecated: false,
365 wrong_index_type: false,
366 type_correct: TypeCorrectKind::None,
367 containing_extern_type: None,
368 prop: None,
369 documentation_symbol: None,
370 tags: Default::default(),
371 parens: Default::default(),
372 insert_text: None,
373 indexed_with_self: false,
374 },
375 );
376 }
377 }
378
379 if unsafe { ast_node_is::<AstStatRepeat>(&*parent) } {
380 let stat_repeat = unsafe { ast_node_as::<AstStatRepeat>(parent) };
381 if !unsafe { (*(*stat_repeat).body).has_end } {
382 result.insert(
383 "until".to_string(),
384 AutocompleteEntry {
385 kind: AutocompleteEntryKind::Keyword,
386 r#type: None,
387 deprecated: false,
388 wrong_index_type: false,
389 type_correct: TypeCorrectKind::None,
390 containing_extern_type: None,
391 prop: None,
392 documentation_symbol: None,
393 tags: Default::default(),
394 parens: Default::default(),
395 insert_text: None,
396 indexed_with_self: false,
397 },
398 );
399 }
400 }
401 }
402
403 if ancestry.len() >= 4 {
404 let iter3 = ancestry[ancestry.len() - 4];
405 let stat_if_ptr = unsafe {
406 if ast_node_is::<AstStatIf>(&*iter3.cast::<AstNode>()) {
407 ast_node_as::<AstStatIf>(iter3)
408 } else {
409 core::ptr::null_mut()
410 }
411 };
412 if !stat_if_ptr.is_null()
413 && unsafe { (*stat_if_ptr).elsebody }.is_null()
414 && ancestry[ancestry.len() - 3].is_null() == false
415 && unsafe { ast_node_is::<AstStatBlock>(&*ancestry[ancestry.len() - 3]) }
416 && unsafe { ast_node_is::<AstStatError>(&*ancestry[ancestry.len() - 2]) }
417 && is_identifier(ancestry[ancestry.len() - 1])
418 {
419 result.insert(
420 "else".to_string(),
421 AutocompleteEntry {
422 kind: AutocompleteEntryKind::Keyword,
423 r#type: None,
424 deprecated: false,
425 wrong_index_type: false,
426 type_correct: TypeCorrectKind::None,
427 containing_extern_type: None,
428 prop: None,
429 documentation_symbol: None,
430 tags: Default::default(),
431 parens: Default::default(),
432 insert_text: None,
433 indexed_with_self: false,
434 },
435 );
436 result.insert(
437 "elseif".to_string(),
438 AutocompleteEntry {
439 kind: AutocompleteEntryKind::Keyword,
440 r#type: None,
441 deprecated: false,
442 wrong_index_type: false,
443 type_correct: TypeCorrectKind::None,
444 containing_extern_type: None,
445 prop: None,
446 documentation_symbol: None,
447 tags: Default::default(),
448 parens: Default::default(),
449 insert_text: None,
450 indexed_with_self: false,
451 },
452 );
453 }
454 }
455
456 let mut found_repeat: *mut AstStatRepeat = core::ptr::null_mut();
459 for &node in ancestry.iter().rev() {
460 if unsafe { ast_node_is::<AstStatRepeat>(&*node) } {
461 found_repeat = unsafe { ast_node_as::<AstStatRepeat>(node) };
462 break;
463 }
464 }
465 if !found_repeat.is_null() && !unsafe { (*(*found_repeat).body).has_end } {
466 result.insert(
467 "until".to_string(),
468 AutocompleteEntry {
469 kind: AutocompleteEntryKind::Keyword,
470 r#type: None,
471 deprecated: false,
472 wrong_index_type: false,
473 type_correct: TypeCorrectKind::None,
474 containing_extern_type: None,
475 prop: None,
476 documentation_symbol: None,
477 tags: Default::default(),
478 parens: Default::default(),
479 insert_text: None,
480 indexed_with_self: false,
481 },
482 );
483 }
484
485 result
486}