1 | /**************************************************************************/ |
2 | /* gdscript_editor.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "gdscript.h" |
32 | |
33 | #include "gdscript_analyzer.h" |
34 | #include "gdscript_compiler.h" |
35 | #include "gdscript_parser.h" |
36 | #include "gdscript_tokenizer.h" |
37 | #include "gdscript_utility_functions.h" |
38 | |
39 | #ifdef TOOLS_ENABLED |
40 | #include "editor/script_templates/templates.gen.h" |
41 | #endif |
42 | |
43 | #include "core/config/engine.h" |
44 | #include "core/core_constants.h" |
45 | #include "core/io/file_access.h" |
46 | |
47 | #ifdef TOOLS_ENABLED |
48 | #include "core/config/project_settings.h" |
49 | #include "editor/editor_file_system.h" |
50 | #include "editor/editor_settings.h" |
51 | #endif |
52 | |
53 | void GDScriptLanguage::(List<String> *p_delimiters) const { |
54 | p_delimiters->push_back("#" ); |
55 | } |
56 | |
57 | void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { |
58 | p_delimiters->push_back("\" \"" ); |
59 | p_delimiters->push_back("' '" ); |
60 | p_delimiters->push_back("\"\"\" \"\"\"" ); |
61 | p_delimiters->push_back("''' '''" ); |
62 | } |
63 | |
64 | bool GDScriptLanguage::is_using_templates() { |
65 | return true; |
66 | } |
67 | |
68 | Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { |
69 | Ref<GDScript> scr; |
70 | scr.instantiate(); |
71 | String processed_template = p_template; |
72 | bool type_hints = false; |
73 | #ifdef TOOLS_ENABLED |
74 | type_hints = EDITOR_GET("text_editor/completion/add_type_hints" ); |
75 | #endif |
76 | if (!type_hints) { |
77 | processed_template = processed_template.replace(": int" , "" ) |
78 | .replace(": String" , "" ) |
79 | .replace(": Array[String]" , "" ) |
80 | .replace(": float" , "" ) |
81 | .replace(": CharFXTransform" , "" ) |
82 | .replace(":=" , "=" ) |
83 | .replace(" -> String" , "" ) |
84 | .replace(" -> int" , "" ) |
85 | .replace(" -> bool" , "" ) |
86 | .replace(" -> void" , "" ); |
87 | } |
88 | |
89 | processed_template = processed_template.replace("_BASE_" , p_base_class_name) |
90 | .replace("_CLASS_" , p_class_name.to_pascal_case()) |
91 | .replace("_TS_" , _get_indentation()); |
92 | scr->set_source_code(processed_template); |
93 | return scr; |
94 | } |
95 | |
96 | Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { |
97 | Vector<ScriptLanguage::ScriptTemplate> templates; |
98 | #ifdef TOOLS_ENABLED |
99 | for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { |
100 | if (TEMPLATES[i].inherit == p_object) { |
101 | templates.append(TEMPLATES[i]); |
102 | } |
103 | } |
104 | #endif |
105 | return templates; |
106 | } |
107 | |
108 | static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, HashMap<int, String> &r_funcs) { |
109 | for (int i = 0; i < p_class->members.size(); i++) { |
110 | if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) { |
111 | const GDScriptParser::FunctionNode *function = p_class->members[i].function; |
112 | r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name); |
113 | } else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) { |
114 | String new_prefix = p_class->members[i].m_class->identifier->name; |
115 | get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs); |
116 | } |
117 | } |
118 | } |
119 | |
120 | bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const { |
121 | GDScriptParser parser; |
122 | GDScriptAnalyzer analyzer(&parser); |
123 | |
124 | Error err = parser.parse(p_script, p_path, false); |
125 | if (err == OK) { |
126 | err = analyzer.analyze(); |
127 | } |
128 | #ifdef DEBUG_ENABLED |
129 | if (r_warnings) { |
130 | for (const GDScriptWarning &E : parser.get_warnings()) { |
131 | const GDScriptWarning &warn = E; |
132 | ScriptLanguage::Warning w; |
133 | w.start_line = warn.start_line; |
134 | w.end_line = warn.end_line; |
135 | w.leftmost_column = warn.leftmost_column; |
136 | w.rightmost_column = warn.rightmost_column; |
137 | w.code = (int)warn.code; |
138 | w.string_code = GDScriptWarning::get_name_from_code(warn.code); |
139 | w.message = warn.get_message(); |
140 | r_warnings->push_back(w); |
141 | } |
142 | } |
143 | #endif |
144 | if (err) { |
145 | if (r_errors) { |
146 | for (const GDScriptParser::ParserError &pe : parser.get_errors()) { |
147 | ScriptLanguage::ScriptError e; |
148 | e.path = p_path; |
149 | e.line = pe.line; |
150 | e.column = pe.column; |
151 | e.message = pe.message; |
152 | r_errors->push_back(e); |
153 | } |
154 | |
155 | for (KeyValue<String, Ref<GDScriptParserRef>> E : analyzer.get_depended_parsers()) { |
156 | GDScriptParser *depended_parser = E.value->get_parser(); |
157 | for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) { |
158 | ScriptLanguage::ScriptError e; |
159 | e.path = E.key; |
160 | e.line = pe.line; |
161 | e.column = pe.column; |
162 | e.message = pe.message; |
163 | r_errors->push_back(e); |
164 | } |
165 | } |
166 | } |
167 | return false; |
168 | } else { |
169 | const GDScriptParser::ClassNode *cl = parser.get_tree(); |
170 | HashMap<int, String> funcs; |
171 | |
172 | get_function_names_recursively(cl, "" , funcs); |
173 | |
174 | for (const KeyValue<int, String> &E : funcs) { |
175 | r_functions->push_back(E.value + ":" + itos(E.key)); |
176 | } |
177 | } |
178 | |
179 | #ifdef DEBUG_ENABLED |
180 | if (r_safe_lines) { |
181 | const HashSet<int> &unsafe_lines = parser.get_unsafe_lines(); |
182 | for (int i = 1; i <= parser.get_last_line_number(); i++) { |
183 | if (!unsafe_lines.has(i)) { |
184 | r_safe_lines->insert(i); |
185 | } |
186 | } |
187 | } |
188 | #endif |
189 | |
190 | return true; |
191 | } |
192 | |
193 | bool GDScriptLanguage::has_named_classes() const { |
194 | return false; |
195 | } |
196 | |
197 | bool GDScriptLanguage::supports_builtin_mode() const { |
198 | return true; |
199 | } |
200 | |
201 | bool GDScriptLanguage::supports_documentation() const { |
202 | return true; |
203 | } |
204 | |
205 | int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const { |
206 | GDScriptTokenizer tokenizer; |
207 | tokenizer.set_source_code(p_code); |
208 | int indent = 0; |
209 | GDScriptTokenizer::Token current = tokenizer.scan(); |
210 | while (current.type != GDScriptTokenizer::Token::TK_EOF && current.type != GDScriptTokenizer::Token::ERROR) { |
211 | if (current.type == GDScriptTokenizer::Token::INDENT) { |
212 | indent++; |
213 | } else if (current.type == GDScriptTokenizer::Token::DEDENT) { |
214 | indent--; |
215 | } |
216 | if (indent == 0 && current.type == GDScriptTokenizer::Token::FUNC) { |
217 | current = tokenizer.scan(); |
218 | if (current.is_identifier()) { |
219 | String identifier = current.get_identifier(); |
220 | if (identifier == p_function) { |
221 | return current.start_line; |
222 | } |
223 | } |
224 | } |
225 | current = tokenizer.scan(); |
226 | } |
227 | return -1; |
228 | } |
229 | |
230 | Script *GDScriptLanguage::create_script() const { |
231 | return memnew(GDScript); |
232 | } |
233 | |
234 | /* DEBUGGER FUNCTIONS */ |
235 | |
236 | thread_local int GDScriptLanguage::_debug_parse_err_line = -1; |
237 | thread_local String GDScriptLanguage::_debug_parse_err_file; |
238 | thread_local String GDScriptLanguage::_debug_error; |
239 | |
240 | bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) { |
241 | // break because of parse error |
242 | |
243 | if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) { |
244 | _debug_parse_err_line = p_line; |
245 | _debug_parse_err_file = p_file; |
246 | _debug_error = p_error; |
247 | EngineDebugger::get_script_debugger()->debug(this, false, true); |
248 | // Because this is thread local, clear the memory afterwards. |
249 | _debug_parse_err_file = String(); |
250 | _debug_error = String(); |
251 | return true; |
252 | } else { |
253 | return false; |
254 | } |
255 | } |
256 | |
257 | bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) { |
258 | if (EngineDebugger::is_active()) { |
259 | _debug_parse_err_line = -1; |
260 | _debug_parse_err_file = "" ; |
261 | _debug_error = p_error; |
262 | bool is_error_breakpoint = p_error != "Breakpoint" ; |
263 | EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint); |
264 | // Because this is thread local, clear the memory afterwards. |
265 | _debug_parse_err_file = String(); |
266 | _debug_error = String(); |
267 | return true; |
268 | } else { |
269 | return false; |
270 | } |
271 | } |
272 | |
273 | String GDScriptLanguage::debug_get_error() const { |
274 | return _debug_error; |
275 | } |
276 | |
277 | int GDScriptLanguage::debug_get_stack_level_count() const { |
278 | if (_debug_parse_err_line >= 0) { |
279 | return 1; |
280 | } |
281 | |
282 | return _call_stack.stack_pos; |
283 | } |
284 | |
285 | int GDScriptLanguage::debug_get_stack_level_line(int p_level) const { |
286 | if (_debug_parse_err_line >= 0) { |
287 | return _debug_parse_err_line; |
288 | } |
289 | |
290 | ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, -1); |
291 | |
292 | int l = _call_stack.stack_pos - p_level - 1; |
293 | |
294 | return *(_call_stack.levels[l].line); |
295 | } |
296 | |
297 | String GDScriptLanguage::debug_get_stack_level_function(int p_level) const { |
298 | if (_debug_parse_err_line >= 0) { |
299 | return "" ; |
300 | } |
301 | |
302 | ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "" ); |
303 | int l = _call_stack.stack_pos - p_level - 1; |
304 | return _call_stack.levels[l].function->get_name(); |
305 | } |
306 | |
307 | String GDScriptLanguage::debug_get_stack_level_source(int p_level) const { |
308 | if (_debug_parse_err_line >= 0) { |
309 | return _debug_parse_err_file; |
310 | } |
311 | |
312 | ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, "" ); |
313 | int l = _call_stack.stack_pos - p_level - 1; |
314 | return _call_stack.levels[l].function->get_source(); |
315 | } |
316 | |
317 | void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { |
318 | if (_debug_parse_err_line >= 0) { |
319 | return; |
320 | } |
321 | |
322 | ERR_FAIL_INDEX(p_level, _call_stack.stack_pos); |
323 | int l = _call_stack.stack_pos - p_level - 1; |
324 | |
325 | GDScriptFunction *f = _call_stack.levels[l].function; |
326 | |
327 | List<Pair<StringName, int>> locals; |
328 | |
329 | f->debug_get_stack_member_state(*_call_stack.levels[l].line, &locals); |
330 | for (const Pair<StringName, int> &E : locals) { |
331 | p_locals->push_back(E.first); |
332 | p_values->push_back(_call_stack.levels[l].stack[E.second]); |
333 | } |
334 | } |
335 | |
336 | void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { |
337 | if (_debug_parse_err_line >= 0) { |
338 | return; |
339 | } |
340 | |
341 | ERR_FAIL_INDEX(p_level, _call_stack.stack_pos); |
342 | int l = _call_stack.stack_pos - p_level - 1; |
343 | |
344 | GDScriptInstance *instance = _call_stack.levels[l].instance; |
345 | |
346 | if (!instance) { |
347 | return; |
348 | } |
349 | |
350 | Ref<GDScript> scr = instance->get_script(); |
351 | ERR_FAIL_COND(scr.is_null()); |
352 | |
353 | const HashMap<StringName, GDScript::MemberInfo> &mi = scr->debug_get_member_indices(); |
354 | |
355 | for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) { |
356 | p_members->push_back(E.key); |
357 | p_values->push_back(instance->debug_get_member_by_index(E.value.index)); |
358 | } |
359 | } |
360 | |
361 | ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) { |
362 | if (_debug_parse_err_line >= 0) { |
363 | return nullptr; |
364 | } |
365 | |
366 | ERR_FAIL_INDEX_V(p_level, _call_stack.stack_pos, nullptr); |
367 | |
368 | int l = _call_stack.stack_pos - p_level - 1; |
369 | ScriptInstance *instance = _call_stack.levels[l].instance; |
370 | |
371 | return instance; |
372 | } |
373 | |
374 | void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) { |
375 | const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map(); |
376 | const Variant *gl_array = GDScriptLanguage::get_singleton()->get_global_array(); |
377 | |
378 | List<Pair<String, Variant>> cinfo; |
379 | get_public_constants(&cinfo); |
380 | |
381 | for (const KeyValue<StringName, int> &E : name_idx) { |
382 | if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) { |
383 | continue; |
384 | } |
385 | |
386 | bool is_script_constant = false; |
387 | for (List<Pair<String, Variant>>::Element *CE = cinfo.front(); CE; CE = CE->next()) { |
388 | if (CE->get().first == E.key) { |
389 | is_script_constant = true; |
390 | break; |
391 | } |
392 | } |
393 | if (is_script_constant) { |
394 | continue; |
395 | } |
396 | |
397 | const Variant &var = gl_array[E.value]; |
398 | if (Object *obj = var) { |
399 | if (Object::cast_to<GDScriptNativeClass>(obj)) { |
400 | continue; |
401 | } |
402 | } |
403 | |
404 | bool skip = false; |
405 | for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { |
406 | if (E.key == CoreConstants::get_global_constant_name(i)) { |
407 | skip = true; |
408 | break; |
409 | } |
410 | } |
411 | if (skip) { |
412 | continue; |
413 | } |
414 | |
415 | p_globals->push_back(E.key); |
416 | p_values->push_back(var); |
417 | } |
418 | } |
419 | |
420 | String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { |
421 | return "" ; |
422 | } |
423 | |
424 | void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const { |
425 | p_extensions->push_back("gd" ); |
426 | } |
427 | |
428 | void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const { |
429 | List<StringName> functions; |
430 | GDScriptUtilityFunctions::get_function_list(&functions); |
431 | |
432 | for (const StringName &E : functions) { |
433 | p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E)); |
434 | } |
435 | |
436 | // Not really "functions", but show in documentation. |
437 | { |
438 | MethodInfo mi; |
439 | mi.name = "preload" ; |
440 | mi.arguments.push_back(PropertyInfo(Variant::STRING, "path" )); |
441 | mi.return_val = PropertyInfo(Variant::OBJECT, "" , PROPERTY_HINT_RESOURCE_TYPE, "Resource" ); |
442 | p_functions->push_back(mi); |
443 | } |
444 | { |
445 | MethodInfo mi; |
446 | mi.name = "assert" ; |
447 | mi.return_val.type = Variant::NIL; |
448 | mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition" )); |
449 | mi.arguments.push_back(PropertyInfo(Variant::STRING, "message" )); |
450 | mi.default_arguments.push_back(String()); |
451 | p_functions->push_back(mi); |
452 | } |
453 | } |
454 | |
455 | void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const { |
456 | Pair<String, Variant> pi; |
457 | pi.first = "PI" ; |
458 | pi.second = Math_PI; |
459 | p_constants->push_back(pi); |
460 | |
461 | Pair<String, Variant> tau; |
462 | tau.first = "TAU" ; |
463 | tau.second = Math_TAU; |
464 | p_constants->push_back(tau); |
465 | |
466 | Pair<String, Variant> infinity; |
467 | infinity.first = "INF" ; |
468 | infinity.second = INFINITY; |
469 | p_constants->push_back(infinity); |
470 | |
471 | Pair<String, Variant> nan; |
472 | nan.first = "NAN" ; |
473 | nan.second = NAN; |
474 | p_constants->push_back(nan); |
475 | } |
476 | |
477 | void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const { |
478 | GDScriptParser parser; |
479 | List<MethodInfo> annotations; |
480 | parser.get_annotation_list(&annotations); |
481 | |
482 | for (const MethodInfo &E : annotations) { |
483 | p_annotations->push_back(E); |
484 | } |
485 | } |
486 | |
487 | String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const { |
488 | #ifdef TOOLS_ENABLED |
489 | bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints" ); |
490 | #else |
491 | bool th = false; |
492 | #endif |
493 | |
494 | String s = "func " + p_name + "(" ; |
495 | if (p_args.size()) { |
496 | for (int i = 0; i < p_args.size(); i++) { |
497 | if (i > 0) { |
498 | s += ", " ; |
499 | } |
500 | s += p_args[i].get_slice(":" , 0); |
501 | if (th) { |
502 | String type = p_args[i].get_slice(":" , 1); |
503 | if (!type.is_empty() && type != "var" ) { |
504 | s += ": " + type; |
505 | } |
506 | } |
507 | } |
508 | } |
509 | s += String(")" ) + (th ? " -> void" : "" ) + ":\n" + _get_indentation() + "pass # Replace with function body.\n" ; |
510 | |
511 | return s; |
512 | } |
513 | |
514 | //////// COMPLETION ////////// |
515 | |
516 | #ifdef TOOLS_ENABLED |
517 | |
518 | #define COMPLETION_RECURSION_LIMIT 200 |
519 | |
520 | struct GDScriptCompletionIdentifier { |
521 | GDScriptParser::DataType type; |
522 | String enumeration; |
523 | Variant value; |
524 | const GDScriptParser::ExpressionNode *assigned_expression = nullptr; |
525 | }; |
526 | |
527 | // LOCATION METHODS |
528 | // These methods are used to populate the `CodeCompletionOption::location` integer. |
529 | // For these methods, the location is based on the depth in the inheritance chain that the property |
530 | // appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D |
531 | // will have a "better" (lower) location "score" than a property that is found on CanvasItem. |
532 | |
533 | static int _get_property_location(StringName p_class, StringName p_property) { |
534 | if (!ClassDB::has_property(p_class, p_property)) { |
535 | return ScriptLanguage::LOCATION_OTHER; |
536 | } |
537 | |
538 | int depth = 0; |
539 | StringName class_test = p_class; |
540 | while (class_test && !ClassDB::has_property(class_test, p_property, true)) { |
541 | class_test = ClassDB::get_parent_class(class_test); |
542 | depth++; |
543 | } |
544 | |
545 | return depth | ScriptLanguage::LOCATION_PARENT_MASK; |
546 | } |
547 | |
548 | static int _get_constant_location(StringName p_class, StringName p_constant) { |
549 | if (!ClassDB::has_integer_constant(p_class, p_constant)) { |
550 | return ScriptLanguage::LOCATION_OTHER; |
551 | } |
552 | |
553 | int depth = 0; |
554 | StringName class_test = p_class; |
555 | while (class_test && !ClassDB::has_integer_constant(class_test, p_constant, true)) { |
556 | class_test = ClassDB::get_parent_class(class_test); |
557 | depth++; |
558 | } |
559 | |
560 | return depth | ScriptLanguage::LOCATION_PARENT_MASK; |
561 | } |
562 | |
563 | static int _get_signal_location(StringName p_class, StringName p_signal) { |
564 | if (!ClassDB::has_signal(p_class, p_signal)) { |
565 | return ScriptLanguage::LOCATION_OTHER; |
566 | } |
567 | |
568 | int depth = 0; |
569 | StringName class_test = p_class; |
570 | while (class_test && !ClassDB::has_signal(class_test, p_signal, true)) { |
571 | class_test = ClassDB::get_parent_class(class_test); |
572 | depth++; |
573 | } |
574 | |
575 | return depth | ScriptLanguage::LOCATION_PARENT_MASK; |
576 | } |
577 | |
578 | static int _get_method_location(StringName p_class, StringName p_method) { |
579 | if (!ClassDB::has_method(p_class, p_method)) { |
580 | return ScriptLanguage::LOCATION_OTHER; |
581 | } |
582 | |
583 | int depth = 0; |
584 | StringName class_test = p_class; |
585 | while (class_test && !ClassDB::has_method(class_test, p_method, true)) { |
586 | class_test = ClassDB::get_parent_class(class_test); |
587 | depth++; |
588 | } |
589 | |
590 | return depth | ScriptLanguage::LOCATION_PARENT_MASK; |
591 | } |
592 | |
593 | static int _get_enum_constant_location(StringName p_class, StringName p_enum_constant) { |
594 | if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) { |
595 | return ScriptLanguage::LOCATION_OTHER; |
596 | } |
597 | |
598 | int depth = 0; |
599 | StringName class_test = p_class; |
600 | while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) { |
601 | class_test = ClassDB::get_parent_class(class_test); |
602 | depth++; |
603 | } |
604 | |
605 | return depth | ScriptLanguage::LOCATION_PARENT_MASK; |
606 | } |
607 | |
608 | // END LOCATION METHODS |
609 | |
610 | static String _trim_parent_class(const String &p_class, const String &p_base_class) { |
611 | if (p_base_class.is_empty()) { |
612 | return p_class; |
613 | } |
614 | Vector<String> names = p_class.split("." , false, 1); |
615 | if (names.size() == 2) { |
616 | String first = names[0]; |
617 | String rest = names[1]; |
618 | if (ClassDB::class_exists(p_base_class) && ClassDB::class_exists(first) && ClassDB::is_parent_class(p_base_class, first)) { |
619 | return rest; |
620 | } |
621 | } |
622 | return p_class; |
623 | } |
624 | |
625 | static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg, const String &p_base_class = "" ) { |
626 | String class_name = p_info.class_name; |
627 | bool is_enum = p_info.type == Variant::INT && p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM; |
628 | // PROPERTY_USAGE_CLASS_IS_BITFIELD: BitField[T] isn't supported (yet?), use plain int. |
629 | |
630 | if ((p_info.type == Variant::OBJECT || is_enum) && !class_name.is_empty()) { |
631 | if (is_enum && CoreConstants::is_global_enum(p_info.class_name)) { |
632 | return class_name; |
633 | } |
634 | return _trim_parent_class(class_name, p_base_class); |
635 | } else if (p_info.type == Variant::ARRAY && p_info.hint == PROPERTY_HINT_ARRAY_TYPE && !p_info.hint_string.is_empty()) { |
636 | return "Array[" + _trim_parent_class(p_info.hint_string, p_base_class) + "]" ; |
637 | } else if (p_info.type == Variant::NIL) { |
638 | if (p_is_arg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) { |
639 | return "Variant" ; |
640 | } else { |
641 | return "void" ; |
642 | } |
643 | } |
644 | |
645 | return Variant::get_type_name(p_info.type); |
646 | } |
647 | |
648 | static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool p_is_annotation = false) { |
649 | String arghint; |
650 | if (!p_is_annotation) { |
651 | arghint += _get_visual_datatype(p_info.return_val, false) + " " ; |
652 | } |
653 | arghint += p_info.name + "(" ; |
654 | |
655 | int def_args = p_info.arguments.size() - p_info.default_arguments.size(); |
656 | int i = 0; |
657 | for (const PropertyInfo &E : p_info.arguments) { |
658 | if (i > 0) { |
659 | arghint += ", " ; |
660 | } |
661 | |
662 | if (i == p_arg_idx) { |
663 | arghint += String::chr(0xFFFF); |
664 | } |
665 | arghint += E.name + ": " + _get_visual_datatype(E, true); |
666 | |
667 | if (i - def_args >= 0) { |
668 | arghint += String(" = " ) + p_info.default_arguments[i - def_args].get_construct_string(); |
669 | } |
670 | |
671 | if (i == p_arg_idx) { |
672 | arghint += String::chr(0xFFFF); |
673 | } |
674 | |
675 | i++; |
676 | } |
677 | |
678 | if (p_info.flags & METHOD_FLAG_VARARG) { |
679 | if (p_info.arguments.size() > 0) { |
680 | arghint += ", " ; |
681 | } |
682 | if (p_arg_idx >= p_info.arguments.size()) { |
683 | arghint += String::chr(0xFFFF); |
684 | } |
685 | arghint += "..." ; |
686 | if (p_arg_idx >= p_info.arguments.size()) { |
687 | arghint += String::chr(0xFFFF); |
688 | } |
689 | } |
690 | |
691 | arghint += ")" ; |
692 | |
693 | return arghint; |
694 | } |
695 | |
696 | static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx) { |
697 | String arghint; |
698 | |
699 | if (p_function->get_datatype().builtin_type == Variant::NIL) { |
700 | arghint = "void " + p_function->identifier->name.operator String() + "(" ; |
701 | } else { |
702 | arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name.operator String() + "(" ; |
703 | } |
704 | |
705 | for (int i = 0; i < p_function->parameters.size(); i++) { |
706 | if (i > 0) { |
707 | arghint += ", " ; |
708 | } |
709 | |
710 | if (i == p_arg_idx) { |
711 | arghint += String::chr(0xFFFF); |
712 | } |
713 | const GDScriptParser::ParameterNode *par = p_function->parameters[i]; |
714 | if (!par->get_datatype().is_hard_type()) { |
715 | arghint += par->identifier->name.operator String() + ": Variant" ; |
716 | } else { |
717 | arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string(); |
718 | } |
719 | |
720 | if (par->initializer) { |
721 | String def_val = "<unknown>" ; |
722 | switch (par->initializer->type) { |
723 | case GDScriptParser::Node::LITERAL: { |
724 | const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer); |
725 | def_val = literal->value.get_construct_string(); |
726 | } break; |
727 | case GDScriptParser::Node::IDENTIFIER: { |
728 | const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer); |
729 | def_val = id->name.operator String(); |
730 | } break; |
731 | case GDScriptParser::Node::CALL: { |
732 | const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer); |
733 | if (call->is_constant && call->reduced) { |
734 | def_val = call->function_name.operator String() + call->reduced_value.operator String(); |
735 | } |
736 | } break; |
737 | case GDScriptParser::Node::ARRAY: { |
738 | const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer); |
739 | if (arr->is_constant && arr->reduced) { |
740 | def_val = arr->reduced_value.operator String(); |
741 | } |
742 | } break; |
743 | case GDScriptParser::Node::DICTIONARY: { |
744 | const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer); |
745 | if (dict->is_constant && dict->reduced) { |
746 | def_val = dict->reduced_value.operator String(); |
747 | } |
748 | } break; |
749 | case GDScriptParser::Node::SUBSCRIPT: { |
750 | const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer); |
751 | if (sub->is_constant) { |
752 | if (sub->datatype.kind == GDScriptParser::DataType::ENUM) { |
753 | def_val = sub->get_datatype().to_string(); |
754 | } else if (sub->reduced) { |
755 | const Variant::Type vt = sub->reduced_value.get_type(); |
756 | if (vt == Variant::Type::NIL || vt == Variant::Type::FLOAT || vt == Variant::Type::INT || vt == Variant::Type::STRING || vt == Variant::Type::STRING_NAME || vt == Variant::Type::BOOL || vt == Variant::Type::NODE_PATH) { |
757 | def_val = sub->reduced_value.operator String(); |
758 | } else { |
759 | def_val = sub->get_datatype().to_string() + sub->reduced_value.operator String(); |
760 | } |
761 | } |
762 | } |
763 | } break; |
764 | default: |
765 | break; |
766 | } |
767 | arghint += " = " + def_val; |
768 | } |
769 | if (i == p_arg_idx) { |
770 | arghint += String::chr(0xFFFF); |
771 | } |
772 | } |
773 | |
774 | arghint += ")" ; |
775 | |
776 | return arghint; |
777 | } |
778 | |
779 | static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list) { |
780 | const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes" ) ? "'" : "\"" ; |
781 | |
782 | for (int i = 0; i < p_dir->get_file_count(); i++) { |
783 | ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH); |
784 | option.insert_text = option.display.quote(quote_style); |
785 | r_list.insert(option.display, option); |
786 | } |
787 | |
788 | for (int i = 0; i < p_dir->get_subdir_count(); i++) { |
789 | _get_directory_contents(p_dir->get_subdir(i), r_list); |
790 | } |
791 | } |
792 | |
793 | static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { |
794 | if (p_annotation->name == SNAME("@export_range" )) { |
795 | if (p_argument == 3 || p_argument == 4 || p_argument == 5) { |
796 | // Slider hint. |
797 | ScriptLanguage::CodeCompletionOption slider1("or_greater" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
798 | slider1.insert_text = slider1.display.quote(p_quote_style); |
799 | r_result.insert(slider1.display, slider1); |
800 | ScriptLanguage::CodeCompletionOption slider2("or_less" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
801 | slider2.insert_text = slider2.display.quote(p_quote_style); |
802 | r_result.insert(slider2.display, slider2); |
803 | ScriptLanguage::CodeCompletionOption slider3("hide_slider" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
804 | slider3.insert_text = slider3.display.quote(p_quote_style); |
805 | r_result.insert(slider3.display, slider3); |
806 | } |
807 | } else if (p_annotation->name == SNAME("@export_exp_easing" )) { |
808 | if (p_argument == 0 || p_argument == 1) { |
809 | // Easing hint. |
810 | ScriptLanguage::CodeCompletionOption hint1("attenuation" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
811 | hint1.insert_text = hint1.display.quote(p_quote_style); |
812 | r_result.insert(hint1.display, hint1); |
813 | ScriptLanguage::CodeCompletionOption hint2("inout" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
814 | hint2.insert_text = hint2.display.quote(p_quote_style); |
815 | r_result.insert(hint2.display, hint2); |
816 | } |
817 | } else if (p_annotation->name == SNAME("@export_node_path" )) { |
818 | ScriptLanguage::CodeCompletionOption node("Node" , ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
819 | node.insert_text = node.display.quote(p_quote_style); |
820 | r_result.insert(node.display, node); |
821 | |
822 | List<StringName> native_classes; |
823 | ClassDB::get_inheriters_from_class("Node" , &native_classes); |
824 | for (const StringName &E : native_classes) { |
825 | if (!ClassDB::is_class_exposed(E)) { |
826 | continue; |
827 | } |
828 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
829 | option.insert_text = option.display.quote(p_quote_style); |
830 | r_result.insert(option.display, option); |
831 | } |
832 | |
833 | List<StringName> global_script_classes; |
834 | ScriptServer::get_global_class_list(&global_script_classes); |
835 | for (const StringName &E : global_script_classes) { |
836 | if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node" )) { |
837 | continue; |
838 | } |
839 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
840 | option.insert_text = option.display.quote(p_quote_style); |
841 | r_result.insert(option.display, option); |
842 | } |
843 | } else if (p_annotation->name == SNAME("@warning_ignore" )) { |
844 | for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) { |
845 | ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
846 | warning.insert_text = warning.display.quote(p_quote_style); |
847 | r_result.insert(warning.display, warning); |
848 | } |
849 | } else if (p_annotation->name == SNAME("@rpc" )) { |
850 | if (p_argument == 0 || p_argument == 1 || p_argument == 2) { |
851 | static const char *options[7] = { "call_local" , "call_remote" , "any_peer" , "authority" , "reliable" , "unreliable" , "unreliable_ordered" }; |
852 | for (int i = 0; i < 7; i++) { |
853 | ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
854 | option.insert_text = option.display.quote(p_quote_style); |
855 | r_result.insert(option.display, option); |
856 | } |
857 | } |
858 | } |
859 | } |
860 | |
861 | static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) { |
862 | for (int i = 0; i < Variant::VARIANT_MAX; i++) { |
863 | if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) { |
864 | ScriptLanguage::CodeCompletionOption option("null" , ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
865 | r_result.insert(option.display, option); |
866 | } else { |
867 | ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
868 | r_result.insert(option.display, option); |
869 | } |
870 | } |
871 | } |
872 | |
873 | static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { |
874 | // Built-in Variant Types |
875 | _find_built_in_variants(r_result, true); |
876 | |
877 | List<StringName> native_types; |
878 | ClassDB::get_class_list(&native_types); |
879 | for (const StringName &E : native_types) { |
880 | if (ClassDB::is_class_exposed(E) && !Engine::get_singleton()->has_singleton(E)) { |
881 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
882 | r_result.insert(option.display, option); |
883 | } |
884 | } |
885 | |
886 | if (p_context.current_class) { |
887 | if (!p_inherit_only && p_context.current_class->base_type.is_set()) { |
888 | // Native enums from base class |
889 | List<StringName> enums; |
890 | ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums); |
891 | for (const StringName &E : enums) { |
892 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM); |
893 | r_result.insert(option.display, option); |
894 | } |
895 | } |
896 | // Check current class for potential types |
897 | const GDScriptParser::ClassNode *current = p_context.current_class; |
898 | while (current) { |
899 | for (int i = 0; i < current->members.size(); i++) { |
900 | const GDScriptParser::ClassNode::Member &member = current->members[i]; |
901 | switch (member.type) { |
902 | case GDScriptParser::ClassNode::Member::CLASS: { |
903 | ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL); |
904 | r_result.insert(option.display, option); |
905 | } break; |
906 | case GDScriptParser::ClassNode::Member::ENUM: { |
907 | if (!p_inherit_only) { |
908 | ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL); |
909 | r_result.insert(option.display, option); |
910 | } |
911 | } break; |
912 | case GDScriptParser::ClassNode::Member::CONSTANT: { |
913 | if (member.constant->get_datatype().is_meta_type && p_context.current_class->outer != nullptr) { |
914 | ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL); |
915 | r_result.insert(option.display, option); |
916 | } |
917 | } break; |
918 | default: |
919 | break; |
920 | } |
921 | } |
922 | current = current->outer; |
923 | } |
924 | } |
925 | |
926 | // Global scripts |
927 | List<StringName> global_classes; |
928 | ScriptServer::get_global_class_list(&global_classes); |
929 | for (const StringName &E : global_classes) { |
930 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); |
931 | r_result.insert(option.display, option); |
932 | } |
933 | |
934 | // Autoload singletons |
935 | HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); |
936 | |
937 | for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) { |
938 | const ProjectSettings::AutoloadInfo &info = E.value; |
939 | if (!info.is_singleton || info.path.get_extension().to_lower() != "gd" ) { |
940 | continue; |
941 | } |
942 | ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); |
943 | r_result.insert(option.display, option); |
944 | } |
945 | } |
946 | |
947 | static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth = 0) { |
948 | for (int i = 0; i < p_suite->locals.size(); i++) { |
949 | ScriptLanguage::CodeCompletionOption option; |
950 | int location = p_recursion_depth == 0 ? ScriptLanguage::LOCATION_LOCAL : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK); |
951 | if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) { |
952 | option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); |
953 | option.default_value = p_suite->locals[i].constant->initializer->reduced_value; |
954 | } else { |
955 | option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, location); |
956 | } |
957 | r_result.insert(option.display, option); |
958 | } |
959 | if (p_suite->parent_block) { |
960 | _find_identifiers_in_suite(p_suite->parent_block, r_result, p_recursion_depth + 1); |
961 | } |
962 | } |
963 | |
964 | static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth); |
965 | |
966 | static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { |
967 | ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); |
968 | |
969 | if (!p_parent_only) { |
970 | bool outer = false; |
971 | const GDScriptParser::ClassNode *clss = p_class; |
972 | int classes_processed = 0; |
973 | while (clss) { |
974 | for (int i = 0; i < clss->members.size(); i++) { |
975 | const int location = p_recursion_depth == 0 ? classes_processed : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK); |
976 | const GDScriptParser::ClassNode::Member &member = clss->members[i]; |
977 | ScriptLanguage::CodeCompletionOption option; |
978 | switch (member.type) { |
979 | case GDScriptParser::ClassNode::Member::VARIABLE: |
980 | if (p_only_functions || outer || (p_static)) { |
981 | continue; |
982 | } |
983 | option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); |
984 | break; |
985 | case GDScriptParser::ClassNode::Member::CONSTANT: |
986 | if (p_only_functions) { |
987 | continue; |
988 | } |
989 | if (r_result.has(member.constant->identifier->name)) { |
990 | continue; |
991 | } |
992 | option = ScriptLanguage::CodeCompletionOption(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); |
993 | if (member.constant->initializer) { |
994 | option.default_value = member.constant->initializer->reduced_value; |
995 | } |
996 | break; |
997 | case GDScriptParser::ClassNode::Member::CLASS: |
998 | if (p_only_functions) { |
999 | continue; |
1000 | } |
1001 | option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location); |
1002 | break; |
1003 | case GDScriptParser::ClassNode::Member::ENUM_VALUE: |
1004 | if (p_only_functions) { |
1005 | continue; |
1006 | } |
1007 | option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); |
1008 | break; |
1009 | case GDScriptParser::ClassNode::Member::ENUM: |
1010 | if (p_only_functions) { |
1011 | continue; |
1012 | } |
1013 | option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location); |
1014 | break; |
1015 | case GDScriptParser::ClassNode::Member::FUNCTION: |
1016 | if (outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@" )) { |
1017 | continue; |
1018 | } |
1019 | option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); |
1020 | if (member.function->parameters.size() > 0) { |
1021 | option.insert_text += "(" ; |
1022 | } else { |
1023 | option.insert_text += "()" ; |
1024 | } |
1025 | break; |
1026 | case GDScriptParser::ClassNode::Member::SIGNAL: |
1027 | if (p_only_functions || outer || p_static) { |
1028 | continue; |
1029 | } |
1030 | option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); |
1031 | break; |
1032 | case GDScriptParser::ClassNode::Member::GROUP: |
1033 | break; // No-op, but silences warnings. |
1034 | case GDScriptParser::ClassNode::Member::UNDEFINED: |
1035 | break; |
1036 | } |
1037 | r_result.insert(option.display, option); |
1038 | } |
1039 | outer = true; |
1040 | clss = clss->outer; |
1041 | classes_processed++; |
1042 | } |
1043 | } |
1044 | |
1045 | // Parents. |
1046 | GDScriptCompletionIdentifier base_type; |
1047 | base_type.type = p_class->base_type; |
1048 | base_type.type.is_meta_type = p_static; |
1049 | |
1050 | _find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1); |
1051 | } |
1052 | |
1053 | static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { |
1054 | ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT); |
1055 | |
1056 | GDScriptParser::DataType base_type = p_base.type; |
1057 | |
1058 | if (base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) { |
1059 | ScriptLanguage::CodeCompletionOption option("new" , ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL); |
1060 | option.insert_text += "(" ; |
1061 | r_result.insert(option.display, option); |
1062 | } |
1063 | |
1064 | while (!base_type.has_no_type()) { |
1065 | switch (base_type.kind) { |
1066 | case GDScriptParser::DataType::CLASS: { |
1067 | _find_identifiers_in_class(base_type.class_type, p_only_functions, base_type.is_meta_type, false, r_result, p_recursion_depth); |
1068 | // This already finds all parent identifiers, so we are done. |
1069 | base_type = GDScriptParser::DataType(); |
1070 | } break; |
1071 | case GDScriptParser::DataType::SCRIPT: { |
1072 | Ref<Script> scr = base_type.script_type; |
1073 | if (scr.is_valid()) { |
1074 | if (!p_only_functions) { |
1075 | if (!base_type.is_meta_type) { |
1076 | List<PropertyInfo> members; |
1077 | scr->get_script_property_list(&members); |
1078 | for (const PropertyInfo &E : members) { |
1079 | int location = p_recursion_depth + _get_property_location(scr->get_class_name(), E.name); |
1080 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); |
1081 | r_result.insert(option.display, option); |
1082 | } |
1083 | |
1084 | List<MethodInfo> signals; |
1085 | scr->get_script_signal_list(&signals); |
1086 | for (const MethodInfo &E : signals) { |
1087 | int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name); |
1088 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); |
1089 | r_result.insert(option.display, option); |
1090 | } |
1091 | } |
1092 | HashMap<StringName, Variant> constants; |
1093 | scr->get_constants(&constants); |
1094 | for (const KeyValue<StringName, Variant> &E : constants) { |
1095 | int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key); |
1096 | ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); |
1097 | r_result.insert(option.display, option); |
1098 | } |
1099 | } |
1100 | |
1101 | List<MethodInfo> methods; |
1102 | scr->get_script_method_list(&methods); |
1103 | for (const MethodInfo &E : methods) { |
1104 | if (E.name.begins_with("@" )) { |
1105 | continue; |
1106 | } |
1107 | int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name); |
1108 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); |
1109 | if (E.arguments.size()) { |
1110 | option.insert_text += "(" ; |
1111 | } else { |
1112 | option.insert_text += "()" ; |
1113 | } |
1114 | r_result.insert(option.display, option); |
1115 | } |
1116 | |
1117 | Ref<Script> base_script = scr->get_base_script(); |
1118 | if (base_script.is_valid()) { |
1119 | base_type.script_type = base_script; |
1120 | } else { |
1121 | base_type.kind = GDScriptParser::DataType::NATIVE; |
1122 | base_type.native_type = scr->get_instance_base_type(); |
1123 | } |
1124 | } else { |
1125 | return; |
1126 | } |
1127 | } break; |
1128 | case GDScriptParser::DataType::NATIVE: { |
1129 | StringName type = base_type.native_type; |
1130 | if (!ClassDB::class_exists(type)) { |
1131 | return; |
1132 | } |
1133 | |
1134 | if (!p_only_functions) { |
1135 | List<String> constants; |
1136 | ClassDB::get_integer_constant_list(type, &constants); |
1137 | for (const String &E : constants) { |
1138 | int location = p_recursion_depth + _get_constant_location(type, StringName(E)); |
1139 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); |
1140 | r_result.insert(option.display, option); |
1141 | } |
1142 | |
1143 | if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) { |
1144 | List<PropertyInfo> pinfo; |
1145 | ClassDB::get_property_list(type, &pinfo); |
1146 | for (const PropertyInfo &E : pinfo) { |
1147 | if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) { |
1148 | continue; |
1149 | } |
1150 | if (E.name.contains("/" )) { |
1151 | continue; |
1152 | } |
1153 | int location = p_recursion_depth + _get_property_location(type, E.name); |
1154 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); |
1155 | r_result.insert(option.display, option); |
1156 | } |
1157 | |
1158 | List<MethodInfo> signals; |
1159 | ClassDB::get_signal_list(type, &signals); |
1160 | for (const MethodInfo &E : signals) { |
1161 | int location = p_recursion_depth + _get_signal_location(type, StringName(E.name)); |
1162 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); |
1163 | r_result.insert(option.display, option); |
1164 | } |
1165 | } |
1166 | } |
1167 | |
1168 | bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type); |
1169 | |
1170 | List<MethodInfo> methods; |
1171 | ClassDB::get_method_list(type, &methods, false, true); |
1172 | for (const MethodInfo &E : methods) { |
1173 | if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) { |
1174 | continue; |
1175 | } |
1176 | if (E.name.begins_with("_" )) { |
1177 | continue; |
1178 | } |
1179 | int location = p_recursion_depth + _get_method_location(type, E.name); |
1180 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location); |
1181 | if (E.arguments.size()) { |
1182 | option.insert_text += "(" ; |
1183 | } else { |
1184 | option.insert_text += "()" ; |
1185 | } |
1186 | r_result.insert(option.display, option); |
1187 | } |
1188 | return; |
1189 | } break; |
1190 | case GDScriptParser::DataType::ENUM: |
1191 | case GDScriptParser::DataType::BUILTIN: { |
1192 | Callable::CallError err; |
1193 | Variant tmp; |
1194 | Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err); |
1195 | if (err.error != Callable::CallError::CALL_OK) { |
1196 | return; |
1197 | } |
1198 | |
1199 | if (!p_only_functions) { |
1200 | List<PropertyInfo> members; |
1201 | if (p_base.value.get_type() != Variant::NIL) { |
1202 | p_base.value.get_property_list(&members); |
1203 | } else { |
1204 | tmp.get_property_list(&members); |
1205 | } |
1206 | |
1207 | for (const PropertyInfo &E : members) { |
1208 | if (!String(E.name).contains("/" )) { |
1209 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER); |
1210 | if (GDScriptParser::theme_color_names.has(E.name)) { |
1211 | option.theme_color_name = GDScriptParser::theme_color_names[E.name]; |
1212 | } |
1213 | r_result.insert(option.display, option); |
1214 | } |
1215 | } |
1216 | } |
1217 | |
1218 | List<MethodInfo> methods; |
1219 | tmp.get_method_list(&methods); |
1220 | for (const MethodInfo &E : methods) { |
1221 | if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) { |
1222 | // Enum types are static and cannot change, therefore we skip non-const dictionary methods. |
1223 | continue; |
1224 | } |
1225 | ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
1226 | if (E.arguments.size()) { |
1227 | option.insert_text += "(" ; |
1228 | } else { |
1229 | option.insert_text += "()" ; |
1230 | } |
1231 | r_result.insert(option.display, option); |
1232 | } |
1233 | |
1234 | return; |
1235 | } break; |
1236 | default: { |
1237 | return; |
1238 | } break; |
1239 | } |
1240 | } |
1241 | } |
1242 | |
1243 | static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) { |
1244 | if (!p_only_functions && p_context.current_suite) { |
1245 | // This includes function parameters, since they are also locals. |
1246 | _find_identifiers_in_suite(p_context.current_suite, r_result); |
1247 | } |
1248 | |
1249 | if (p_context.current_class) { |
1250 | _find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth); |
1251 | } |
1252 | |
1253 | List<StringName> functions; |
1254 | GDScriptUtilityFunctions::get_function_list(&functions); |
1255 | |
1256 | for (const StringName &E : functions) { |
1257 | MethodInfo function = GDScriptUtilityFunctions::get_function_info(E); |
1258 | ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
1259 | if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) { |
1260 | option.insert_text += "(" ; |
1261 | } else { |
1262 | option.insert_text += "()" ; |
1263 | } |
1264 | r_result.insert(option.display, option); |
1265 | } |
1266 | |
1267 | if (p_only_functions) { |
1268 | return; |
1269 | } |
1270 | |
1271 | _find_built_in_variants(r_result); |
1272 | |
1273 | static const char *_keywords[] = { |
1274 | "true" , "false" , "PI" , "TAU" , "INF" , "NAN" , "null" , "self" , "super" , |
1275 | "break" , "breakpoint" , "continue" , "pass" , "return" , |
1276 | nullptr |
1277 | }; |
1278 | |
1279 | const char **kw = _keywords; |
1280 | while (*kw) { |
1281 | ScriptLanguage::CodeCompletionOption option(*kw, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
1282 | r_result.insert(option.display, option); |
1283 | kw++; |
1284 | } |
1285 | |
1286 | static const char *_keywords_with_space[] = { |
1287 | "and" , "not" , "or" , "in" , "as" , "class" , "class_name" , "extends" , "is" , "func" , "signal" , "await" , |
1288 | "const" , "enum" , "static" , "var" , "if" , "elif" , "else" , "for" , "match" , "while" , |
1289 | nullptr |
1290 | }; |
1291 | |
1292 | const char **kws = _keywords_with_space; |
1293 | while (*kws) { |
1294 | ScriptLanguage::CodeCompletionOption option(*kws, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
1295 | option.insert_text += " " ; |
1296 | r_result.insert(option.display, option); |
1297 | kws++; |
1298 | } |
1299 | |
1300 | static const char *_keywords_with_args[] = { |
1301 | "assert" , "preload" , |
1302 | nullptr |
1303 | }; |
1304 | |
1305 | const char **kwa = _keywords_with_args; |
1306 | while (*kwa) { |
1307 | ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
1308 | option.insert_text += "(" ; |
1309 | r_result.insert(option.display, option); |
1310 | kwa++; |
1311 | } |
1312 | |
1313 | List<StringName> utility_func_names; |
1314 | Variant::get_utility_function_list(&utility_func_names); |
1315 | |
1316 | for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { |
1317 | ScriptLanguage::CodeCompletionOption option(E->get(), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
1318 | option.insert_text += "(" ; |
1319 | r_result.insert(option.display, option); |
1320 | } |
1321 | |
1322 | for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { |
1323 | if (!E.value.is_singleton) { |
1324 | continue; |
1325 | } |
1326 | ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); |
1327 | r_result.insert(option.display, option); |
1328 | } |
1329 | |
1330 | // Native classes and global constants. |
1331 | for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) { |
1332 | ScriptLanguage::CodeCompletionOption option; |
1333 | if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) { |
1334 | option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CLASS); |
1335 | } else { |
1336 | option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); |
1337 | } |
1338 | r_result.insert(option.display, option); |
1339 | } |
1340 | |
1341 | // Global classes |
1342 | List<StringName> global_classes; |
1343 | ScriptServer::get_global_class_list(&global_classes); |
1344 | for (const StringName &E : global_classes) { |
1345 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE); |
1346 | r_result.insert(option.display, option); |
1347 | } |
1348 | } |
1349 | |
1350 | static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value) { |
1351 | GDScriptCompletionIdentifier ci; |
1352 | ci.value = p_value; |
1353 | ci.type.is_constant = true; |
1354 | ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1355 | ci.type.kind = GDScriptParser::DataType::BUILTIN; |
1356 | ci.type.builtin_type = p_value.get_type(); |
1357 | |
1358 | if (ci.type.builtin_type == Variant::OBJECT) { |
1359 | Object *obj = p_value.operator Object *(); |
1360 | if (!obj) { |
1361 | return ci; |
1362 | } |
1363 | ci.type.native_type = obj->get_class_name(); |
1364 | Ref<Script> scr = p_value; |
1365 | if (scr.is_valid()) { |
1366 | ci.type.is_meta_type = true; |
1367 | } else { |
1368 | ci.type.is_meta_type = false; |
1369 | scr = obj->get_script(); |
1370 | } |
1371 | if (scr.is_valid()) { |
1372 | ci.type.script_type = scr; |
1373 | ci.type.kind = GDScriptParser::DataType::SCRIPT; |
1374 | ci.type.native_type = scr->get_instance_base_type(); |
1375 | } else { |
1376 | ci.type.kind = GDScriptParser::DataType::NATIVE; |
1377 | } |
1378 | } |
1379 | |
1380 | return ci; |
1381 | } |
1382 | |
1383 | static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) { |
1384 | GDScriptCompletionIdentifier ci; |
1385 | |
1386 | if (p_property.type == Variant::NIL) { |
1387 | // Variant |
1388 | return ci; |
1389 | } |
1390 | |
1391 | if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { |
1392 | ci.enumeration = p_property.class_name; |
1393 | } |
1394 | |
1395 | ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1396 | ci.type.builtin_type = p_property.type; |
1397 | if (p_property.type == Variant::OBJECT) { |
1398 | ci.type.kind = GDScriptParser::DataType::NATIVE; |
1399 | ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name; |
1400 | } else { |
1401 | ci.type.kind = GDScriptParser::DataType::BUILTIN; |
1402 | } |
1403 | return ci; |
1404 | } |
1405 | |
1406 | #define MAX_COMPLETION_RECURSION 100 |
1407 | struct RecursionCheck { |
1408 | int *counter; |
1409 | _FORCE_INLINE_ bool check() { |
1410 | return (*counter) > MAX_COMPLETION_RECURSION; |
1411 | } |
1412 | RecursionCheck(int *p_counter) : |
1413 | counter(p_counter) { |
1414 | (*counter)++; |
1415 | } |
1416 | ~RecursionCheck() { |
1417 | (*counter)--; |
1418 | } |
1419 | }; |
1420 | |
1421 | static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type); |
1422 | static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type); |
1423 | static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type); |
1424 | |
1425 | static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) { |
1426 | if (p_expression) { |
1427 | switch (p_expression->type) { |
1428 | case GDScriptParser::Node::IDENTIFIER: { |
1429 | const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); |
1430 | if (id->name == p_name) { |
1431 | return true; |
1432 | } |
1433 | } break; |
1434 | case GDScriptParser::Node::CAST: { |
1435 | const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); |
1436 | return _is_expression_named_identifier(cn->operand, p_name); |
1437 | } break; |
1438 | default: |
1439 | break; |
1440 | } |
1441 | } |
1442 | |
1443 | return false; |
1444 | } |
1445 | |
1446 | static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) { |
1447 | bool found = false; |
1448 | |
1449 | if (p_expression == nullptr) { |
1450 | return false; |
1451 | } |
1452 | |
1453 | static int recursion_depth = 0; |
1454 | RecursionCheck recursion(&recursion_depth); |
1455 | if (unlikely(recursion.check())) { |
1456 | ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type." ); |
1457 | } |
1458 | |
1459 | if (p_expression->is_constant) { |
1460 | // Already has a value, so just use that. |
1461 | r_type = _type_from_variant(p_expression->reduced_value); |
1462 | switch (p_expression->get_datatype().kind) { |
1463 | case GDScriptParser::DataType::ENUM: |
1464 | case GDScriptParser::DataType::CLASS: |
1465 | r_type.type = p_expression->get_datatype(); |
1466 | break; |
1467 | default: |
1468 | break; |
1469 | } |
1470 | found = true; |
1471 | } else { |
1472 | switch (p_expression->type) { |
1473 | case GDScriptParser::Node::LITERAL: { |
1474 | const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(p_expression); |
1475 | r_type = _type_from_variant(literal->value); |
1476 | found = true; |
1477 | } break; |
1478 | case GDScriptParser::Node::SELF: { |
1479 | if (p_context.current_class) { |
1480 | if (p_context.type != GDScriptParser::COMPLETION_SUPER_METHOD) { |
1481 | r_type.type = p_context.current_class->get_datatype(); |
1482 | } else { |
1483 | r_type.type = p_context.current_class->base_type; |
1484 | } |
1485 | found = true; |
1486 | } |
1487 | } break; |
1488 | case GDScriptParser::Node::IDENTIFIER: { |
1489 | const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression); |
1490 | found = _guess_identifier_type(p_context, id, r_type); |
1491 | } break; |
1492 | case GDScriptParser::Node::DICTIONARY: { |
1493 | // Try to recreate the dictionary. |
1494 | const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression); |
1495 | Dictionary d; |
1496 | bool full = true; |
1497 | for (int i = 0; i < dn->elements.size(); i++) { |
1498 | GDScriptCompletionIdentifier key; |
1499 | if (_guess_expression_type(p_context, dn->elements[i].key, key)) { |
1500 | if (!key.type.is_constant) { |
1501 | full = false; |
1502 | break; |
1503 | } |
1504 | GDScriptCompletionIdentifier value; |
1505 | if (_guess_expression_type(p_context, dn->elements[i].value, value)) { |
1506 | if (!value.type.is_constant) { |
1507 | full = false; |
1508 | break; |
1509 | } |
1510 | d[key.value] = value.value; |
1511 | } else { |
1512 | full = false; |
1513 | break; |
1514 | } |
1515 | } else { |
1516 | full = false; |
1517 | break; |
1518 | } |
1519 | } |
1520 | if (full) { |
1521 | r_type.value = d; |
1522 | r_type.type.is_constant = true; |
1523 | } |
1524 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1525 | r_type.type.kind = GDScriptParser::DataType::BUILTIN; |
1526 | r_type.type.builtin_type = Variant::DICTIONARY; |
1527 | found = true; |
1528 | } break; |
1529 | case GDScriptParser::Node::ARRAY: { |
1530 | // Try to recreate the array |
1531 | const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression); |
1532 | Array a; |
1533 | bool full = true; |
1534 | a.resize(an->elements.size()); |
1535 | for (int i = 0; i < an->elements.size(); i++) { |
1536 | GDScriptCompletionIdentifier value; |
1537 | if (_guess_expression_type(p_context, an->elements[i], value)) { |
1538 | if (value.type.is_constant) { |
1539 | a[i] = value.value; |
1540 | } else { |
1541 | full = false; |
1542 | break; |
1543 | } |
1544 | } else { |
1545 | full = false; |
1546 | break; |
1547 | } |
1548 | } |
1549 | if (full) { |
1550 | // If not fully constant, setting this value is detrimental to the inference. |
1551 | r_type.value = a; |
1552 | } |
1553 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1554 | r_type.type.kind = GDScriptParser::DataType::BUILTIN; |
1555 | r_type.type.builtin_type = Variant::ARRAY; |
1556 | found = true; |
1557 | } break; |
1558 | case GDScriptParser::Node::CAST: { |
1559 | const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression); |
1560 | GDScriptCompletionIdentifier value; |
1561 | if (_guess_expression_type(p_context, cn->operand, r_type)) { |
1562 | r_type.type = cn->get_datatype(); |
1563 | found = true; |
1564 | } |
1565 | } break; |
1566 | case GDScriptParser::Node::CALL: { |
1567 | const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression); |
1568 | if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { |
1569 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1570 | r_type.type.kind = GDScriptParser::DataType::BUILTIN; |
1571 | r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name); |
1572 | found = true; |
1573 | break; |
1574 | } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) { |
1575 | MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name); |
1576 | r_type = _type_from_property(mi.return_val); |
1577 | found = true; |
1578 | break; |
1579 | } else { |
1580 | GDScriptParser::CompletionContext c = p_context; |
1581 | c.current_line = call->start_line; |
1582 | |
1583 | GDScriptParser::Node::Type callee_type = call->get_callee_type(); |
1584 | |
1585 | GDScriptCompletionIdentifier base; |
1586 | if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) { |
1587 | // Simple call, so base is 'self'. |
1588 | if (p_context.current_class) { |
1589 | if (call->is_super) { |
1590 | base.type = p_context.current_class->base_type; |
1591 | base.value = p_context.base; |
1592 | } else { |
1593 | base.type.kind = GDScriptParser::DataType::CLASS; |
1594 | base.type.type_source = GDScriptParser::DataType::INFERRED; |
1595 | base.type.is_constant = true; |
1596 | base.type.class_type = p_context.current_class; |
1597 | base.value = p_context.base; |
1598 | } |
1599 | } else { |
1600 | break; |
1601 | } |
1602 | } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) { |
1603 | if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) { |
1604 | found = false; |
1605 | break; |
1606 | } |
1607 | } else { |
1608 | break; |
1609 | } |
1610 | |
1611 | // Try call if constant methods with constant arguments |
1612 | if (base.type.is_constant && base.value.get_type() == Variant::OBJECT) { |
1613 | GDScriptParser::DataType native_type = base.type; |
1614 | |
1615 | while (native_type.kind == GDScriptParser::DataType::CLASS) { |
1616 | native_type = native_type.class_type->base_type; |
1617 | } |
1618 | |
1619 | while (native_type.kind == GDScriptParser::DataType::SCRIPT) { |
1620 | if (native_type.script_type.is_valid()) { |
1621 | Ref<Script> parent = native_type.script_type->get_base_script(); |
1622 | if (parent.is_valid()) { |
1623 | native_type.script_type = parent; |
1624 | } else { |
1625 | native_type.kind = GDScriptParser::DataType::NATIVE; |
1626 | native_type.native_type = native_type.script_type->get_instance_base_type(); |
1627 | if (!ClassDB::class_exists(native_type.native_type)) { |
1628 | native_type.kind = GDScriptParser::DataType::UNRESOLVED; |
1629 | } |
1630 | } |
1631 | } |
1632 | } |
1633 | |
1634 | if (native_type.kind == GDScriptParser::DataType::NATIVE) { |
1635 | MethodBind *mb = ClassDB::get_method(native_type.native_type, call->function_name); |
1636 | if (mb && mb->is_const()) { |
1637 | bool all_is_const = true; |
1638 | Vector<Variant> args; |
1639 | GDScriptParser::CompletionContext c2 = p_context; |
1640 | c2.current_line = call->start_line; |
1641 | for (int i = 0; all_is_const && i < call->arguments.size(); i++) { |
1642 | GDScriptCompletionIdentifier arg; |
1643 | |
1644 | if (!call->arguments[i]->is_constant) { |
1645 | all_is_const = false; |
1646 | } |
1647 | } |
1648 | |
1649 | Object *baseptr = base.value; |
1650 | |
1651 | if (all_is_const && call->function_name == SNAME("get_node" ) && ClassDB::is_parent_class(native_type.native_type, SNAME("Node" )) && args.size()) { |
1652 | String arg1 = args[0]; |
1653 | if (arg1.begins_with("/root/" )) { |
1654 | String which = arg1.get_slice("/" , 2); |
1655 | if (!which.is_empty()) { |
1656 | // Try singletons first |
1657 | if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) { |
1658 | r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]); |
1659 | found = true; |
1660 | } else { |
1661 | for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { |
1662 | String name = E.key; |
1663 | if (name == which) { |
1664 | String script = E.value.path; |
1665 | |
1666 | if (!script.begins_with("res://" )) { |
1667 | script = "res://" + script; |
1668 | } |
1669 | |
1670 | if (!script.ends_with(".gd" )) { |
1671 | // not a script, try find the script anyway, |
1672 | // may have some success |
1673 | script = script.get_basename() + ".gd" ; |
1674 | } |
1675 | |
1676 | if (FileAccess::exists(script)) { |
1677 | Error err = OK; |
1678 | Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err); |
1679 | if (err == OK) { |
1680 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
1681 | r_type.type.script_path = script; |
1682 | r_type.type.class_type = parser->get_parser()->get_tree(); |
1683 | r_type.type.is_constant = false; |
1684 | r_type.type.kind = GDScriptParser::DataType::CLASS; |
1685 | r_type.value = Variant(); |
1686 | p_context.dependent_parsers.push_back(parser); |
1687 | found = true; |
1688 | } |
1689 | } |
1690 | break; |
1691 | } |
1692 | } |
1693 | } |
1694 | } |
1695 | } |
1696 | } |
1697 | |
1698 | if (!found && all_is_const && baseptr) { |
1699 | Vector<const Variant *> argptr; |
1700 | for (int i = 0; i < args.size(); i++) { |
1701 | argptr.push_back(&args[i]); |
1702 | } |
1703 | |
1704 | Callable::CallError ce; |
1705 | Variant ret = mb->call(baseptr, (const Variant **)argptr.ptr(), argptr.size(), ce); |
1706 | |
1707 | if (ce.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) { |
1708 | if (ret.get_type() != Variant::OBJECT || ret.operator Object *() != nullptr) { |
1709 | r_type = _type_from_variant(ret); |
1710 | found = true; |
1711 | } |
1712 | } |
1713 | } |
1714 | } |
1715 | } |
1716 | } |
1717 | |
1718 | if (!found && base.value.get_type() != Variant::NIL) { |
1719 | found = _guess_method_return_type_from_base(c, base, call->function_name, r_type); |
1720 | } |
1721 | } |
1722 | } break; |
1723 | case GDScriptParser::Node::SUBSCRIPT: { |
1724 | const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression); |
1725 | if (subscript->is_attribute) { |
1726 | GDScriptParser::CompletionContext c = p_context; |
1727 | c.current_line = subscript->start_line; |
1728 | |
1729 | GDScriptCompletionIdentifier base; |
1730 | if (!_guess_expression_type(c, subscript->base, base)) { |
1731 | found = false; |
1732 | break; |
1733 | } |
1734 | |
1735 | if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) { |
1736 | Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)]; |
1737 | r_type = _type_from_variant(value); |
1738 | found = true; |
1739 | break; |
1740 | } |
1741 | |
1742 | const GDScriptParser::DictionaryNode *dn = nullptr; |
1743 | if (subscript->base->type == GDScriptParser::Node::DICTIONARY) { |
1744 | dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base); |
1745 | } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) { |
1746 | dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression); |
1747 | } |
1748 | |
1749 | if (dn) { |
1750 | for (int i = 0; i < dn->elements.size(); i++) { |
1751 | GDScriptCompletionIdentifier key; |
1752 | if (!_guess_expression_type(c, dn->elements[i].key, key)) { |
1753 | continue; |
1754 | } |
1755 | if (key.value == String(subscript->attribute->name)) { |
1756 | r_type.assigned_expression = dn->elements[i].value; |
1757 | found = _guess_expression_type(c, dn->elements[i].value, r_type); |
1758 | break; |
1759 | } |
1760 | } |
1761 | } |
1762 | |
1763 | if (!found) { |
1764 | found = _guess_identifier_type_from_base(c, base, subscript->attribute->name, r_type); |
1765 | } |
1766 | } else { |
1767 | if (subscript->index == nullptr) { |
1768 | found = false; |
1769 | break; |
1770 | } |
1771 | |
1772 | GDScriptParser::CompletionContext c = p_context; |
1773 | c.current_line = subscript->start_line; |
1774 | |
1775 | GDScriptCompletionIdentifier base; |
1776 | if (!_guess_expression_type(c, subscript->base, base)) { |
1777 | found = false; |
1778 | break; |
1779 | } |
1780 | |
1781 | GDScriptCompletionIdentifier index; |
1782 | if (!_guess_expression_type(c, subscript->index, index)) { |
1783 | found = false; |
1784 | break; |
1785 | } |
1786 | |
1787 | if (base.value.in(index.value)) { |
1788 | Variant value = base.value.get(index.value); |
1789 | r_type = _type_from_variant(value); |
1790 | found = true; |
1791 | break; |
1792 | } |
1793 | |
1794 | // Look if it is a dictionary node. |
1795 | const GDScriptParser::DictionaryNode *dn = nullptr; |
1796 | if (subscript->base->type == GDScriptParser::Node::DICTIONARY) { |
1797 | dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base); |
1798 | } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) { |
1799 | dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression); |
1800 | } |
1801 | |
1802 | if (dn) { |
1803 | for (int i = 0; i < dn->elements.size(); i++) { |
1804 | GDScriptCompletionIdentifier key; |
1805 | if (!_guess_expression_type(c, dn->elements[i].key, key)) { |
1806 | continue; |
1807 | } |
1808 | if (key.value == index.value) { |
1809 | r_type.assigned_expression = dn->elements[i].value; |
1810 | found = _guess_expression_type(p_context, dn->elements[i].value, r_type); |
1811 | break; |
1812 | } |
1813 | } |
1814 | } |
1815 | |
1816 | // Look if it is an array node. |
1817 | if (!found && index.value.is_num()) { |
1818 | int idx = index.value; |
1819 | const GDScriptParser::ArrayNode *an = nullptr; |
1820 | if (subscript->base->type == GDScriptParser::Node::ARRAY) { |
1821 | an = static_cast<const GDScriptParser::ArrayNode *>(subscript->base); |
1822 | } else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::ARRAY) { |
1823 | an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression); |
1824 | } |
1825 | |
1826 | if (an && idx >= 0 && an->elements.size() > idx) { |
1827 | r_type.assigned_expression = an->elements[idx]; |
1828 | found = _guess_expression_type(c, an->elements[idx], r_type); |
1829 | break; |
1830 | } |
1831 | } |
1832 | |
1833 | // Look for valid indexing in other types |
1834 | if (!found && (index.value.get_type() == Variant::STRING || index.value.get_type() == Variant::NODE_PATH)) { |
1835 | StringName id = index.value; |
1836 | found = _guess_identifier_type_from_base(c, base, id, r_type); |
1837 | } else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) { |
1838 | Callable::CallError err; |
1839 | Variant base_val; |
1840 | Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err); |
1841 | bool valid = false; |
1842 | Variant res = base_val.get(index.value, &valid); |
1843 | if (valid) { |
1844 | r_type = _type_from_variant(res); |
1845 | r_type.value = Variant(); |
1846 | r_type.type.is_constant = false; |
1847 | found = true; |
1848 | } |
1849 | } |
1850 | } |
1851 | } break; |
1852 | case GDScriptParser::Node::BINARY_OPERATOR: { |
1853 | const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression); |
1854 | |
1855 | if (op->variant_op == Variant::OP_MAX) { |
1856 | break; |
1857 | } |
1858 | |
1859 | GDScriptParser::CompletionContext context = p_context; |
1860 | context.current_line = op->start_line; |
1861 | |
1862 | GDScriptCompletionIdentifier p1; |
1863 | GDScriptCompletionIdentifier p2; |
1864 | |
1865 | if (!_guess_expression_type(context, op->left_operand, p1)) { |
1866 | found = false; |
1867 | break; |
1868 | } |
1869 | |
1870 | if (!_guess_expression_type(context, op->right_operand, p2)) { |
1871 | found = false; |
1872 | break; |
1873 | } |
1874 | |
1875 | Callable::CallError ce; |
1876 | bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT; |
1877 | Variant d1; |
1878 | Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce); |
1879 | Variant d2; |
1880 | Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce); |
1881 | |
1882 | Variant v1 = (v1_use_value) ? p1.value : d1; |
1883 | bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT; |
1884 | Variant v2 = (v2_use_value) ? p2.value : d2; |
1885 | // avoid potential invalid ops |
1886 | if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) { |
1887 | v2 = 1; |
1888 | v2_use_value = false; |
1889 | } |
1890 | if (op->variant_op == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) { |
1891 | v2 = 1.0; |
1892 | v2_use_value = false; |
1893 | } |
1894 | |
1895 | Variant res; |
1896 | bool valid; |
1897 | Variant::evaluate(op->variant_op, v1, v2, res, valid); |
1898 | if (!valid) { |
1899 | found = false; |
1900 | break; |
1901 | } |
1902 | r_type = _type_from_variant(res); |
1903 | if (!v1_use_value || !v2_use_value) { |
1904 | r_type.value = Variant(); |
1905 | r_type.type.is_constant = false; |
1906 | } |
1907 | |
1908 | found = true; |
1909 | } break; |
1910 | default: |
1911 | break; |
1912 | } |
1913 | } |
1914 | |
1915 | // It may have found a null, but that's never useful |
1916 | if (found && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) { |
1917 | found = false; |
1918 | } |
1919 | |
1920 | // Check type hint last. For collections we want chance to get the actual value first |
1921 | // This way we can detect types from the content of dictionaries and arrays |
1922 | if (!found && p_expression->get_datatype().is_hard_type()) { |
1923 | r_type.type = p_expression->get_datatype(); |
1924 | if (!r_type.assigned_expression) { |
1925 | r_type.assigned_expression = p_expression; |
1926 | } |
1927 | found = true; |
1928 | } |
1929 | |
1930 | return found; |
1931 | } |
1932 | |
1933 | static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) { |
1934 | static int recursion_depth = 0; |
1935 | RecursionCheck recursion(&recursion_depth); |
1936 | if (unlikely(recursion.check())) { |
1937 | ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type." ); |
1938 | } |
1939 | |
1940 | // Look in blocks first. |
1941 | int last_assign_line = -1; |
1942 | const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr; |
1943 | GDScriptParser::DataType id_type; |
1944 | GDScriptParser::SuiteNode *suite = p_context.current_suite; |
1945 | bool is_function_parameter = false; |
1946 | |
1947 | bool can_be_local = true; |
1948 | switch (p_identifier->source) { |
1949 | case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: |
1950 | case GDScriptParser::IdentifierNode::MEMBER_CONSTANT: |
1951 | case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: |
1952 | case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: |
1953 | case GDScriptParser::IdentifierNode::MEMBER_CLASS: |
1954 | case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: |
1955 | case GDScriptParser::IdentifierNode::STATIC_VARIABLE: |
1956 | can_be_local = false; |
1957 | break; |
1958 | default: |
1959 | break; |
1960 | } |
1961 | |
1962 | if (can_be_local && suite && suite->has_local(p_identifier->name)) { |
1963 | const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name); |
1964 | |
1965 | id_type = local.get_datatype(); |
1966 | |
1967 | // Check initializer as the first assignment. |
1968 | switch (local.type) { |
1969 | case GDScriptParser::SuiteNode::Local::VARIABLE: |
1970 | if (local.variable->initializer) { |
1971 | last_assign_line = local.variable->initializer->end_line; |
1972 | last_assigned_expression = local.variable->initializer; |
1973 | } |
1974 | break; |
1975 | case GDScriptParser::SuiteNode::Local::CONSTANT: |
1976 | if (local.constant->initializer) { |
1977 | last_assign_line = local.constant->initializer->end_line; |
1978 | last_assigned_expression = local.constant->initializer; |
1979 | } |
1980 | break; |
1981 | case GDScriptParser::SuiteNode::Local::PARAMETER: |
1982 | if (local.parameter->initializer) { |
1983 | last_assign_line = local.parameter->initializer->end_line; |
1984 | last_assigned_expression = local.parameter->initializer; |
1985 | } |
1986 | is_function_parameter = true; |
1987 | break; |
1988 | default: |
1989 | break; |
1990 | } |
1991 | } |
1992 | |
1993 | while (suite) { |
1994 | for (int i = 0; i < suite->statements.size(); i++) { |
1995 | if (suite->statements[i]->end_line >= p_context.current_line) { |
1996 | break; |
1997 | } |
1998 | |
1999 | switch (suite->statements[i]->type) { |
2000 | case GDScriptParser::Node::ASSIGNMENT: { |
2001 | const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]); |
2002 | if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) { |
2003 | const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee); |
2004 | if (id->name == p_identifier->name && id->source == p_identifier->source) { |
2005 | last_assign_line = assign->assigned_value->end_line; |
2006 | last_assigned_expression = assign->assigned_value; |
2007 | } |
2008 | } |
2009 | } break; |
2010 | default: |
2011 | // TODO: Check sub blocks (control flow statements) as they might also reassign stuff. |
2012 | break; |
2013 | } |
2014 | } |
2015 | |
2016 | if (suite->parent_if && suite->parent_if->condition && suite->parent_if->condition->type == GDScriptParser::Node::TYPE_TEST) { |
2017 | // Operator `is` used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common.. |
2018 | // Super dirty hack, but very useful. |
2019 | // Credit: Zylann. |
2020 | // TODO: this could be hacked to detect ANDed conditions too... |
2021 | const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition); |
2022 | if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) { |
2023 | // Bingo. |
2024 | GDScriptParser::CompletionContext c = p_context; |
2025 | c.current_line = type_test->operand->start_line; |
2026 | c.current_suite = suite; |
2027 | if (type_test->test_datatype.is_hard_type()) { |
2028 | id_type = type_test->test_datatype; |
2029 | if (last_assign_line < c.current_line) { |
2030 | // Override last assignment. |
2031 | last_assign_line = c.current_line; |
2032 | last_assigned_expression = nullptr; |
2033 | } |
2034 | } |
2035 | } |
2036 | } |
2037 | |
2038 | suite = suite->parent_block; |
2039 | } |
2040 | |
2041 | if (last_assigned_expression && last_assign_line < p_context.current_line) { |
2042 | GDScriptParser::CompletionContext c = p_context; |
2043 | c.current_line = last_assign_line; |
2044 | r_type.assigned_expression = last_assigned_expression; |
2045 | if (_guess_expression_type(c, last_assigned_expression, r_type)) { |
2046 | return true; |
2047 | } |
2048 | } |
2049 | |
2050 | if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) { |
2051 | // Check if it's override of native function, then we can assume the type from the signature. |
2052 | GDScriptParser::DataType base_type = p_context.current_class->base_type; |
2053 | while (base_type.is_set()) { |
2054 | switch (base_type.kind) { |
2055 | case GDScriptParser::DataType::CLASS: |
2056 | if (base_type.class_type->has_function(p_context.current_function->identifier->name)) { |
2057 | GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function; |
2058 | if (parent_function->parameters_indices.has(p_identifier->name)) { |
2059 | const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]]; |
2060 | if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) { |
2061 | id_type = parameter->get_datatype(); |
2062 | } |
2063 | if (parameter->initializer) { |
2064 | GDScriptParser::CompletionContext c = p_context; |
2065 | c.current_function = parent_function; |
2066 | c.current_class = base_type.class_type; |
2067 | c.base = nullptr; |
2068 | if (_guess_expression_type(c, parameter->initializer, r_type)) { |
2069 | return true; |
2070 | } |
2071 | } |
2072 | } |
2073 | } |
2074 | base_type = base_type.class_type->base_type; |
2075 | break; |
2076 | case GDScriptParser::DataType::NATIVE: { |
2077 | if (id_type.is_set() && !id_type.is_variant()) { |
2078 | base_type = GDScriptParser::DataType(); |
2079 | break; |
2080 | } |
2081 | MethodInfo info; |
2082 | if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) { |
2083 | for (const PropertyInfo &E : info.arguments) { |
2084 | if (E.name == p_identifier->name) { |
2085 | r_type = _type_from_property(E); |
2086 | return true; |
2087 | } |
2088 | } |
2089 | } |
2090 | base_type = GDScriptParser::DataType(); |
2091 | } break; |
2092 | default: |
2093 | break; |
2094 | } |
2095 | } |
2096 | } |
2097 | |
2098 | if (id_type.is_set() && !id_type.is_variant()) { |
2099 | r_type.type = id_type; |
2100 | return true; |
2101 | } |
2102 | |
2103 | // Check current class (including inheritance). |
2104 | if (p_context.current_class) { |
2105 | GDScriptCompletionIdentifier base; |
2106 | base.value = p_context.base; |
2107 | base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2108 | base.type.kind = GDScriptParser::DataType::CLASS; |
2109 | base.type.class_type = p_context.current_class; |
2110 | base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static; |
2111 | |
2112 | if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, r_type)) { |
2113 | return true; |
2114 | } |
2115 | } |
2116 | |
2117 | // Check global scripts. |
2118 | if (ScriptServer::is_global_class(p_identifier->name)) { |
2119 | String script = ScriptServer::get_global_class_path(p_identifier->name); |
2120 | if (script.to_lower().ends_with(".gd" )) { |
2121 | Error err = OK; |
2122 | Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err); |
2123 | if (err == OK) { |
2124 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2125 | r_type.type.script_path = script; |
2126 | r_type.type.class_type = parser->get_parser()->get_tree(); |
2127 | r_type.type.is_meta_type = true; |
2128 | r_type.type.is_constant = false; |
2129 | r_type.type.kind = GDScriptParser::DataType::CLASS; |
2130 | r_type.value = Variant(); |
2131 | p_context.dependent_parsers.push_back(parser); |
2132 | return true; |
2133 | } |
2134 | } else { |
2135 | Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name)); |
2136 | if (scr.is_valid()) { |
2137 | r_type = _type_from_variant(scr); |
2138 | r_type.type.is_meta_type = true; |
2139 | return true; |
2140 | } |
2141 | } |
2142 | return false; |
2143 | } |
2144 | |
2145 | // Check global variables (including autoloads). |
2146 | if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) { |
2147 | r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]); |
2148 | return true; |
2149 | } |
2150 | |
2151 | // Check ClassDB. |
2152 | if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) { |
2153 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2154 | r_type.type.kind = GDScriptParser::DataType::NATIVE; |
2155 | r_type.type.native_type = p_identifier->name; |
2156 | r_type.type.is_constant = true; |
2157 | if (Engine::get_singleton()->has_singleton(p_identifier->name)) { |
2158 | r_type.type.is_meta_type = false; |
2159 | r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name); |
2160 | } else { |
2161 | r_type.type.is_meta_type = true; |
2162 | r_type.value = Variant(); |
2163 | } |
2164 | } |
2165 | |
2166 | return false; |
2167 | } |
2168 | |
2169 | static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) { |
2170 | static int recursion_depth = 0; |
2171 | RecursionCheck recursion(&recursion_depth); |
2172 | if (unlikely(recursion.check())) { |
2173 | ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type." ); |
2174 | } |
2175 | |
2176 | GDScriptParser::DataType base_type = p_base.type; |
2177 | bool is_static = base_type.is_meta_type; |
2178 | while (base_type.is_set()) { |
2179 | switch (base_type.kind) { |
2180 | case GDScriptParser::DataType::CLASS: |
2181 | if (base_type.class_type->has_member(p_identifier)) { |
2182 | const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier); |
2183 | switch (member.type) { |
2184 | case GDScriptParser::ClassNode::Member::CONSTANT: |
2185 | r_type.type = member.constant->get_datatype(); |
2186 | if (member.constant->initializer && member.constant->initializer->is_constant) { |
2187 | r_type.value = member.constant->initializer->reduced_value; |
2188 | } |
2189 | return true; |
2190 | case GDScriptParser::ClassNode::Member::VARIABLE: |
2191 | if (!is_static) { |
2192 | if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { |
2193 | r_type.type = member.variable->get_datatype(); |
2194 | return true; |
2195 | } else if (member.variable->initializer) { |
2196 | const GDScriptParser::ExpressionNode *init = member.variable->initializer; |
2197 | if (init->is_constant) { |
2198 | r_type.value = init->reduced_value; |
2199 | r_type = _type_from_variant(init->reduced_value); |
2200 | return true; |
2201 | } else if (init->start_line == p_context.current_line) { |
2202 | return false; |
2203 | // Detects if variable is assigned to itself |
2204 | } else if (_is_expression_named_identifier(init, member.variable->identifier->name)) { |
2205 | if (member.variable->initializer->get_datatype().is_set()) { |
2206 | r_type.type = member.variable->initializer->get_datatype(); |
2207 | } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) { |
2208 | r_type.type = member.variable->get_datatype(); |
2209 | } |
2210 | return true; |
2211 | } else if (_guess_expression_type(p_context, init, r_type)) { |
2212 | return true; |
2213 | } else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) { |
2214 | r_type.type = init->get_datatype(); |
2215 | return true; |
2216 | } |
2217 | } |
2218 | } |
2219 | // TODO: Check assignments in constructor. |
2220 | return false; |
2221 | case GDScriptParser::ClassNode::Member::ENUM: |
2222 | r_type.type = member.m_enum->get_datatype(); |
2223 | r_type.enumeration = member.m_enum->identifier->name; |
2224 | return true; |
2225 | case GDScriptParser::ClassNode::Member::ENUM_VALUE: |
2226 | r_type = _type_from_variant(member.enum_value.value); |
2227 | return true; |
2228 | case GDScriptParser::ClassNode::Member::SIGNAL: |
2229 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2230 | r_type.type.kind = GDScriptParser::DataType::BUILTIN; |
2231 | r_type.type.builtin_type = Variant::SIGNAL; |
2232 | return true; |
2233 | case GDScriptParser::ClassNode::Member::FUNCTION: |
2234 | if (is_static && !member.function->is_static) { |
2235 | return false; |
2236 | } |
2237 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2238 | r_type.type.kind = GDScriptParser::DataType::BUILTIN; |
2239 | r_type.type.builtin_type = Variant::CALLABLE; |
2240 | return true; |
2241 | case GDScriptParser::ClassNode::Member::CLASS: |
2242 | r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2243 | r_type.type.kind = GDScriptParser::DataType::CLASS; |
2244 | r_type.type.class_type = member.m_class; |
2245 | r_type.type.is_meta_type = true; |
2246 | return true; |
2247 | case GDScriptParser::ClassNode::Member::GROUP: |
2248 | return false; // No-op, but silences warnings. |
2249 | case GDScriptParser::ClassNode::Member::UNDEFINED: |
2250 | return false; // Unreachable. |
2251 | } |
2252 | return false; |
2253 | } |
2254 | base_type = base_type.class_type->base_type; |
2255 | break; |
2256 | case GDScriptParser::DataType::SCRIPT: { |
2257 | Ref<Script> scr = base_type.script_type; |
2258 | if (scr.is_valid()) { |
2259 | HashMap<StringName, Variant> constants; |
2260 | scr->get_constants(&constants); |
2261 | if (constants.has(p_identifier)) { |
2262 | r_type = _type_from_variant(constants[p_identifier]); |
2263 | return true; |
2264 | } |
2265 | |
2266 | if (!is_static) { |
2267 | List<PropertyInfo> members; |
2268 | scr->get_script_property_list(&members); |
2269 | for (const PropertyInfo &prop : members) { |
2270 | if (prop.name == p_identifier) { |
2271 | r_type = _type_from_property(prop); |
2272 | return true; |
2273 | } |
2274 | } |
2275 | } |
2276 | Ref<Script> parent = scr->get_base_script(); |
2277 | if (parent.is_valid()) { |
2278 | base_type.script_type = parent; |
2279 | } else { |
2280 | base_type.kind = GDScriptParser::DataType::NATIVE; |
2281 | base_type.native_type = scr->get_instance_base_type(); |
2282 | } |
2283 | } else { |
2284 | return false; |
2285 | } |
2286 | } break; |
2287 | case GDScriptParser::DataType::NATIVE: { |
2288 | StringName class_name = base_type.native_type; |
2289 | if (!ClassDB::class_exists(class_name)) { |
2290 | return false; |
2291 | } |
2292 | |
2293 | // Skip constants since they're all integers. Type does not matter because int has no members. |
2294 | |
2295 | PropertyInfo prop; |
2296 | if (ClassDB::get_property_info(class_name, p_identifier, &prop)) { |
2297 | StringName getter = ClassDB::get_property_getter(class_name, p_identifier); |
2298 | if (getter != StringName()) { |
2299 | MethodBind *g = ClassDB::get_method(class_name, getter); |
2300 | if (g) { |
2301 | r_type = _type_from_property(g->get_return_info()); |
2302 | return true; |
2303 | } |
2304 | } else { |
2305 | r_type = _type_from_property(prop); |
2306 | return true; |
2307 | } |
2308 | } |
2309 | return false; |
2310 | } break; |
2311 | case GDScriptParser::DataType::BUILTIN: { |
2312 | Callable::CallError err; |
2313 | Variant tmp; |
2314 | Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err); |
2315 | |
2316 | if (err.error != Callable::CallError::CALL_OK) { |
2317 | return false; |
2318 | } |
2319 | bool valid = false; |
2320 | Variant res = tmp.get(p_identifier, &valid); |
2321 | if (valid) { |
2322 | r_type = _type_from_variant(res); |
2323 | r_type.value = Variant(); |
2324 | r_type.type.is_constant = false; |
2325 | return true; |
2326 | } |
2327 | return false; |
2328 | } break; |
2329 | default: { |
2330 | return false; |
2331 | } break; |
2332 | } |
2333 | } |
2334 | return false; |
2335 | } |
2336 | |
2337 | static void _find_last_return_in_block(GDScriptParser::CompletionContext &p_context, int &r_last_return_line, const GDScriptParser::ExpressionNode **r_last_returned_value) { |
2338 | if (!p_context.current_suite) { |
2339 | return; |
2340 | } |
2341 | |
2342 | for (int i = 0; i < p_context.current_suite->statements.size(); i++) { |
2343 | if (p_context.current_suite->statements[i]->start_line < r_last_return_line) { |
2344 | break; |
2345 | } |
2346 | |
2347 | GDScriptParser::CompletionContext c = p_context; |
2348 | switch (p_context.current_suite->statements[i]->type) { |
2349 | case GDScriptParser::Node::FOR: |
2350 | c.current_suite = static_cast<const GDScriptParser::ForNode *>(p_context.current_suite->statements[i])->loop; |
2351 | _find_last_return_in_block(c, r_last_return_line, r_last_returned_value); |
2352 | break; |
2353 | case GDScriptParser::Node::WHILE: |
2354 | c.current_suite = static_cast<const GDScriptParser::WhileNode *>(p_context.current_suite->statements[i])->loop; |
2355 | _find_last_return_in_block(c, r_last_return_line, r_last_returned_value); |
2356 | break; |
2357 | case GDScriptParser::Node::IF: { |
2358 | const GDScriptParser::IfNode *_if = static_cast<const GDScriptParser::IfNode *>(p_context.current_suite->statements[i]); |
2359 | c.current_suite = _if->true_block; |
2360 | _find_last_return_in_block(c, r_last_return_line, r_last_returned_value); |
2361 | if (_if->false_block) { |
2362 | c.current_suite = _if->false_block; |
2363 | _find_last_return_in_block(c, r_last_return_line, r_last_returned_value); |
2364 | } |
2365 | } break; |
2366 | case GDScriptParser::Node::MATCH: { |
2367 | const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(p_context.current_suite->statements[i]); |
2368 | for (int j = 0; j < match->branches.size(); j++) { |
2369 | c.current_suite = match->branches[j]->block; |
2370 | _find_last_return_in_block(c, r_last_return_line, r_last_returned_value); |
2371 | } |
2372 | } break; |
2373 | case GDScriptParser::Node::RETURN: { |
2374 | const GDScriptParser::ReturnNode *ret = static_cast<const GDScriptParser::ReturnNode *>(p_context.current_suite->statements[i]); |
2375 | if (ret->return_value) { |
2376 | if (ret->start_line > r_last_return_line) { |
2377 | r_last_return_line = ret->start_line; |
2378 | *r_last_returned_value = ret->return_value; |
2379 | } |
2380 | } |
2381 | } break; |
2382 | default: |
2383 | break; |
2384 | } |
2385 | } |
2386 | } |
2387 | |
2388 | static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) { |
2389 | static int recursion_depth = 0; |
2390 | RecursionCheck recursion(&recursion_depth); |
2391 | if (unlikely(recursion.check())) { |
2392 | ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type." ); |
2393 | } |
2394 | |
2395 | GDScriptParser::DataType base_type = p_base.type; |
2396 | bool is_static = base_type.is_meta_type; |
2397 | |
2398 | if (is_static && p_method == SNAME("new" )) { |
2399 | r_type.type = base_type; |
2400 | r_type.type.is_meta_type = false; |
2401 | r_type.type.is_constant = false; |
2402 | return true; |
2403 | } |
2404 | |
2405 | while (base_type.is_set() && !base_type.is_variant()) { |
2406 | switch (base_type.kind) { |
2407 | case GDScriptParser::DataType::CLASS: |
2408 | if (base_type.class_type->has_function(p_method)) { |
2409 | const GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function; |
2410 | if (!is_static || method->is_static) { |
2411 | if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) { |
2412 | r_type.type = method->get_datatype(); |
2413 | return true; |
2414 | } |
2415 | |
2416 | int last_return_line = -1; |
2417 | const GDScriptParser::ExpressionNode *last_returned_value = nullptr; |
2418 | GDScriptParser::CompletionContext c = p_context; |
2419 | c.current_class = base_type.class_type; |
2420 | c.current_function = const_cast<GDScriptParser::FunctionNode *>(method); |
2421 | c.current_suite = method->body; |
2422 | |
2423 | _find_last_return_in_block(c, last_return_line, &last_returned_value); |
2424 | if (last_returned_value) { |
2425 | c.current_line = c.current_suite->end_line; |
2426 | if (_guess_expression_type(c, last_returned_value, r_type)) { |
2427 | return true; |
2428 | } |
2429 | } |
2430 | } |
2431 | } |
2432 | base_type = base_type.class_type->base_type; |
2433 | break; |
2434 | case GDScriptParser::DataType::SCRIPT: { |
2435 | Ref<Script> scr = base_type.script_type; |
2436 | if (scr.is_valid()) { |
2437 | List<MethodInfo> methods; |
2438 | scr->get_script_method_list(&methods); |
2439 | for (const MethodInfo &mi : methods) { |
2440 | if (mi.name == p_method) { |
2441 | r_type = _type_from_property(mi.return_val); |
2442 | return true; |
2443 | } |
2444 | } |
2445 | Ref<Script> base_script = scr->get_base_script(); |
2446 | if (base_script.is_valid()) { |
2447 | base_type.script_type = base_script; |
2448 | } else { |
2449 | base_type.kind = GDScriptParser::DataType::NATIVE; |
2450 | base_type.native_type = scr->get_instance_base_type(); |
2451 | } |
2452 | } else { |
2453 | return false; |
2454 | } |
2455 | } break; |
2456 | case GDScriptParser::DataType::NATIVE: { |
2457 | if (!ClassDB::class_exists(base_type.native_type)) { |
2458 | return false; |
2459 | } |
2460 | MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method); |
2461 | if (mb) { |
2462 | r_type = _type_from_property(mb->get_return_info()); |
2463 | return true; |
2464 | } |
2465 | return false; |
2466 | } break; |
2467 | case GDScriptParser::DataType::BUILTIN: { |
2468 | Callable::CallError err; |
2469 | Variant tmp; |
2470 | Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err); |
2471 | if (err.error != Callable::CallError::CALL_OK) { |
2472 | return false; |
2473 | } |
2474 | |
2475 | List<MethodInfo> methods; |
2476 | tmp.get_method_list(&methods); |
2477 | |
2478 | for (const MethodInfo &mi : methods) { |
2479 | if (mi.name == p_method) { |
2480 | r_type = _type_from_property(mi.return_val); |
2481 | return true; |
2482 | } |
2483 | } |
2484 | return false; |
2485 | } break; |
2486 | default: { |
2487 | return false; |
2488 | } |
2489 | } |
2490 | } |
2491 | |
2492 | return false; |
2493 | } |
2494 | |
2495 | static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) { |
2496 | if (!p_enum_hint.contains("." )) { |
2497 | // Global constant or in the current class. |
2498 | StringName current_enum = p_enum_hint; |
2499 | if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) { |
2500 | const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum; |
2501 | for (int i = 0; i < _enum->values.size(); i++) { |
2502 | ScriptLanguage::CodeCompletionOption option(_enum->values[i].identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM); |
2503 | r_result.insert(option.display, option); |
2504 | } |
2505 | } else { |
2506 | for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { |
2507 | if (CoreConstants::get_global_constant_enum(i) == current_enum) { |
2508 | ScriptLanguage::CodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptLanguage::CODE_COMPLETION_KIND_ENUM); |
2509 | r_result.insert(option.display, option); |
2510 | } |
2511 | } |
2512 | } |
2513 | } else { |
2514 | String class_name = p_enum_hint.get_slice("." , 0); |
2515 | String enum_name = p_enum_hint.get_slice("." , 1); |
2516 | |
2517 | if (!ClassDB::class_exists(class_name)) { |
2518 | return; |
2519 | } |
2520 | |
2521 | List<StringName> enum_constants; |
2522 | ClassDB::get_enum_constants(class_name, enum_name, &enum_constants); |
2523 | for (const StringName &E : enum_constants) { |
2524 | String candidate = class_name + "." + E; |
2525 | int location = _get_enum_constant_location(class_name, E); |
2526 | ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location); |
2527 | r_result.insert(option.display, option); |
2528 | } |
2529 | } |
2530 | } |
2531 | |
2532 | static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) { |
2533 | Variant base = p_base.value; |
2534 | GDScriptParser::DataType base_type = p_base.type; |
2535 | |
2536 | const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes" ) ? "'" : "\"" ; |
2537 | |
2538 | while (base_type.is_set() && !base_type.is_variant()) { |
2539 | switch (base_type.kind) { |
2540 | case GDScriptParser::DataType::CLASS: { |
2541 | if (base_type.class_type->has_member(p_method)) { |
2542 | const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_method); |
2543 | |
2544 | if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { |
2545 | r_arghint = _make_arguments_hint(member.function, p_argidx); |
2546 | return; |
2547 | } |
2548 | } |
2549 | |
2550 | base_type = base_type.class_type->base_type; |
2551 | } break; |
2552 | case GDScriptParser::DataType::NATIVE: { |
2553 | StringName class_name = base_type.native_type; |
2554 | if (!ClassDB::class_exists(class_name)) { |
2555 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
2556 | break; |
2557 | } |
2558 | |
2559 | MethodInfo info; |
2560 | int method_args = 0; |
2561 | |
2562 | if (ClassDB::get_method_info(class_name, p_method, &info)) { |
2563 | method_args = info.arguments.size(); |
2564 | if (base.get_type() == Variant::OBJECT) { |
2565 | Object *obj = base.operator Object *(); |
2566 | if (obj) { |
2567 | List<String> options; |
2568 | obj->get_argument_options(p_method, p_argidx, &options); |
2569 | for (String &opt : options) { |
2570 | if (opt.is_quoted()) { |
2571 | opt = opt.unquote().quote(quote_style); // Handle user preference. |
2572 | } |
2573 | ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
2574 | r_result.insert(option.display, option); |
2575 | } |
2576 | } |
2577 | } |
2578 | |
2579 | if (p_argidx < method_args) { |
2580 | PropertyInfo arg_info = info.arguments[p_argidx]; |
2581 | if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { |
2582 | _find_enumeration_candidates(p_context, arg_info.class_name, r_result); |
2583 | } |
2584 | } |
2585 | |
2586 | r_arghint = _make_arguments_hint(info, p_argidx); |
2587 | } |
2588 | |
2589 | if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node" )) && (p_method == SNAME("get_node" ) || p_method == SNAME("has_node" ))) { |
2590 | // Get autoloads |
2591 | List<PropertyInfo> props; |
2592 | ProjectSettings::get_singleton()->get_property_list(&props); |
2593 | |
2594 | for (const PropertyInfo &E : props) { |
2595 | String s = E.name; |
2596 | if (!s.begins_with("autoload/" )) { |
2597 | continue; |
2598 | } |
2599 | String name = s.get_slice("/" , 1); |
2600 | ScriptLanguage::CodeCompletionOption option("/root/" + name, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); |
2601 | option.insert_text = option.display.quote(quote_style); |
2602 | r_result.insert(option.display, option); |
2603 | } |
2604 | } |
2605 | |
2606 | if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent" )) && p_method.operator String().contains("action" )) { |
2607 | // Get input actions |
2608 | List<PropertyInfo> props; |
2609 | ProjectSettings::get_singleton()->get_property_list(&props); |
2610 | for (const PropertyInfo &E : props) { |
2611 | String s = E.name; |
2612 | if (!s.begins_with("input/" )) { |
2613 | continue; |
2614 | } |
2615 | String name = s.get_slice("/" , 1); |
2616 | ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); |
2617 | option.insert_text = option.display.quote(quote_style); |
2618 | r_result.insert(option.display, option); |
2619 | } |
2620 | } |
2621 | |
2622 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
2623 | } break; |
2624 | case GDScriptParser::DataType::BUILTIN: { |
2625 | if (base.get_type() == Variant::NIL) { |
2626 | Callable::CallError err; |
2627 | Variant::construct(base_type.builtin_type, base, nullptr, 0, err); |
2628 | if (err.error != Callable::CallError::CALL_OK) { |
2629 | return; |
2630 | } |
2631 | } |
2632 | |
2633 | List<MethodInfo> methods; |
2634 | base.get_method_list(&methods); |
2635 | for (const MethodInfo &E : methods) { |
2636 | if (E.name == p_method) { |
2637 | r_arghint = _make_arguments_hint(E, p_argidx); |
2638 | return; |
2639 | } |
2640 | } |
2641 | |
2642 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
2643 | } break; |
2644 | default: { |
2645 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
2646 | } break; |
2647 | } |
2648 | } |
2649 | } |
2650 | |
2651 | static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) { |
2652 | if (p_context.base == nullptr) { |
2653 | return false; |
2654 | } |
2655 | const GDScriptParser::GetNodeNode *get_node = nullptr; |
2656 | |
2657 | switch (p_subscript->base->type) { |
2658 | case GDScriptParser::Node::GET_NODE: { |
2659 | get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base); |
2660 | } break; |
2661 | |
2662 | case GDScriptParser::Node::IDENTIFIER: { |
2663 | const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base); |
2664 | |
2665 | switch (identifier_node->source) { |
2666 | case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: { |
2667 | if (p_context.current_class != nullptr) { |
2668 | const StringName &member_name = identifier_node->name; |
2669 | const GDScriptParser::ClassNode *current_class = p_context.current_class; |
2670 | |
2671 | if (current_class->has_member(member_name)) { |
2672 | const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name); |
2673 | |
2674 | if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { |
2675 | const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable); |
2676 | |
2677 | if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) { |
2678 | get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer); |
2679 | } |
2680 | } |
2681 | } |
2682 | } |
2683 | } break; |
2684 | case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: { |
2685 | if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) { |
2686 | get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next); |
2687 | } |
2688 | } break; |
2689 | default: { |
2690 | } break; |
2691 | } |
2692 | } break; |
2693 | default: { |
2694 | } break; |
2695 | } |
2696 | |
2697 | if (get_node != nullptr) { |
2698 | const Object *node = p_context.base->call("get_node_or_null" , NodePath(get_node->full_path)); |
2699 | if (node != nullptr) { |
2700 | if (r_base != nullptr) { |
2701 | *r_base = node; |
2702 | } |
2703 | r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; |
2704 | r_base_type.kind = GDScriptParser::DataType::NATIVE; |
2705 | r_base_type.native_type = node->get_class_name(); |
2706 | r_base_type.builtin_type = Variant::OBJECT; |
2707 | return true; |
2708 | } |
2709 | } |
2710 | |
2711 | return false; |
2712 | } |
2713 | |
2714 | static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) { |
2715 | if (p_call->type == GDScriptParser::Node::PRELOAD) { |
2716 | if (p_argidx == 0 && bool(EDITOR_GET("text_editor/completion/complete_file_paths" ))) { |
2717 | _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result); |
2718 | } |
2719 | |
2720 | MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource" , PROPERTY_HINT_RESOURCE_TYPE, "Resource" ), "preload" , PropertyInfo(Variant::STRING, "path" )); |
2721 | r_arghint = _make_arguments_hint(mi, p_argidx); |
2722 | return; |
2723 | } else if (p_call->type != GDScriptParser::Node::CALL) { |
2724 | return; |
2725 | } |
2726 | |
2727 | Variant base; |
2728 | GDScriptParser::DataType base_type; |
2729 | bool _static = false; |
2730 | const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call); |
2731 | GDScriptParser::Node::Type callee_type = call->get_callee_type(); |
2732 | |
2733 | GDScriptCompletionIdentifier connect_base; |
2734 | |
2735 | if (callee_type == GDScriptParser::Node::SUBSCRIPT) { |
2736 | const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); |
2737 | |
2738 | if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) { |
2739 | const GDScriptParser::IdentifierNode *base_identifier = static_cast<const GDScriptParser::IdentifierNode *>(subscript->base); |
2740 | |
2741 | Variant::Type method_type = GDScriptParser::get_builtin_type(base_identifier->name); |
2742 | if (method_type < Variant::VARIANT_MAX) { |
2743 | Variant v; |
2744 | Callable::CallError err; |
2745 | Variant::construct(method_type, v, nullptr, 0, err); |
2746 | if (err.error != Callable::CallError::CALL_OK) { |
2747 | return; |
2748 | } |
2749 | List<MethodInfo> methods; |
2750 | v.get_method_list(&methods); |
2751 | |
2752 | for (MethodInfo &E : methods) { |
2753 | if (p_argidx >= E.arguments.size()) { |
2754 | continue; |
2755 | } |
2756 | if (E.name == call->function_name) { |
2757 | r_arghint += _make_arguments_hint(E, p_argidx); |
2758 | return; |
2759 | } |
2760 | } |
2761 | } |
2762 | } |
2763 | |
2764 | if (subscript->is_attribute) { |
2765 | bool found_type = _get_subscript_type(p_context, subscript, base_type, &base); |
2766 | |
2767 | if (!found_type) { |
2768 | GDScriptCompletionIdentifier ci; |
2769 | if (_guess_expression_type(p_context, subscript->base, ci)) { |
2770 | base_type = ci.type; |
2771 | base = ci.value; |
2772 | } else { |
2773 | return; |
2774 | } |
2775 | } |
2776 | |
2777 | _static = base_type.is_meta_type; |
2778 | } |
2779 | } else if (Variant::has_utility_function(call->function_name)) { |
2780 | MethodInfo info = Variant::get_utility_function_info(call->function_name); |
2781 | r_arghint = _make_arguments_hint(info, p_argidx); |
2782 | return; |
2783 | } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) { |
2784 | MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name); |
2785 | r_arghint = _make_arguments_hint(info, p_argidx); |
2786 | return; |
2787 | } else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { |
2788 | // Complete constructor. |
2789 | List<MethodInfo> constructors; |
2790 | Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors); |
2791 | |
2792 | int i = 0; |
2793 | for (const MethodInfo &E : constructors) { |
2794 | if (p_argidx >= E.arguments.size()) { |
2795 | continue; |
2796 | } |
2797 | if (i > 0) { |
2798 | r_arghint += "\n" ; |
2799 | } |
2800 | r_arghint += _make_arguments_hint(E, p_argidx); |
2801 | i++; |
2802 | } |
2803 | return; |
2804 | } else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) { |
2805 | base = p_context.base; |
2806 | |
2807 | if (p_context.current_class) { |
2808 | base_type = p_context.current_class->get_datatype(); |
2809 | _static = !p_context.current_function || p_context.current_function->is_static; |
2810 | } |
2811 | } else { |
2812 | return; |
2813 | } |
2814 | |
2815 | GDScriptCompletionIdentifier ci; |
2816 | ci.type = base_type; |
2817 | ci.value = base; |
2818 | _find_call_arguments(p_context, ci, call->function_name, p_argidx, _static, r_result, r_arghint); |
2819 | |
2820 | r_forced = r_result.size() > 0; |
2821 | } |
2822 | |
2823 | ::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { |
2824 | const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes" ) ? "'" : "\"" ; |
2825 | |
2826 | GDScriptParser parser; |
2827 | GDScriptAnalyzer analyzer(&parser); |
2828 | |
2829 | parser.parse(p_code, p_path, true); |
2830 | analyzer.analyze(); |
2831 | |
2832 | r_forced = false; |
2833 | HashMap<String, ScriptLanguage::CodeCompletionOption> options; |
2834 | |
2835 | GDScriptParser::CompletionContext completion_context = parser.get_completion_context(); |
2836 | completion_context.base = p_owner; |
2837 | bool is_function = false; |
2838 | |
2839 | switch (completion_context.type) { |
2840 | case GDScriptParser::COMPLETION_NONE: |
2841 | break; |
2842 | case GDScriptParser::COMPLETION_ANNOTATION: { |
2843 | List<MethodInfo> annotations; |
2844 | parser.get_annotation_list(&annotations); |
2845 | for (const MethodInfo &E : annotations) { |
2846 | ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2847 | if (E.arguments.size() > 0) { |
2848 | option.insert_text += "(" ; |
2849 | } |
2850 | options.insert(option.display, option); |
2851 | } |
2852 | r_forced = true; |
2853 | } break; |
2854 | case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: { |
2855 | if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) { |
2856 | break; |
2857 | } |
2858 | const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node); |
2859 | _find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options); |
2860 | r_forced = true; |
2861 | } break; |
2862 | case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: { |
2863 | // Constants. |
2864 | { |
2865 | List<StringName> constants; |
2866 | Variant::get_constants_for_type(completion_context.builtin_type, &constants); |
2867 | for (const StringName &E : constants) { |
2868 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT); |
2869 | bool valid = false; |
2870 | Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid); |
2871 | if (valid) { |
2872 | option.default_value = default_value; |
2873 | } |
2874 | options.insert(option.display, option); |
2875 | } |
2876 | } |
2877 | // Methods. |
2878 | { |
2879 | List<StringName> methods; |
2880 | Variant::get_builtin_method_list(completion_context.builtin_type, &methods); |
2881 | for (const StringName &E : methods) { |
2882 | if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) { |
2883 | ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
2884 | if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) { |
2885 | option.insert_text += "(" ; |
2886 | } else { |
2887 | option.insert_text += "()" ; |
2888 | } |
2889 | options.insert(option.display, option); |
2890 | } |
2891 | } |
2892 | } |
2893 | } break; |
2894 | case GDScriptParser::COMPLETION_INHERIT_TYPE: { |
2895 | _list_available_types(true, completion_context, options); |
2896 | r_forced = true; |
2897 | } break; |
2898 | case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: { |
2899 | ScriptLanguage::CodeCompletionOption option("void" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2900 | options.insert(option.display, option); |
2901 | } |
2902 | [[fallthrough]]; |
2903 | case GDScriptParser::COMPLETION_TYPE_NAME: { |
2904 | _list_available_types(false, completion_context, options); |
2905 | r_forced = true; |
2906 | } break; |
2907 | case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: { |
2908 | _list_available_types(false, completion_context, options); |
2909 | ScriptLanguage::CodeCompletionOption get("get" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2910 | options.insert(get.display, get); |
2911 | ScriptLanguage::CodeCompletionOption set("set" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2912 | options.insert(set.display, set); |
2913 | r_forced = true; |
2914 | } break; |
2915 | case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: { |
2916 | ScriptLanguage::CodeCompletionOption get("get" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2917 | options.insert(get.display, get); |
2918 | ScriptLanguage::CodeCompletionOption set("set" , ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); |
2919 | options.insert(set.display, set); |
2920 | r_forced = true; |
2921 | } break; |
2922 | case GDScriptParser::COMPLETION_PROPERTY_METHOD: { |
2923 | if (!completion_context.current_class) { |
2924 | break; |
2925 | } |
2926 | for (int i = 0; i < completion_context.current_class->members.size(); i++) { |
2927 | const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i]; |
2928 | if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) { |
2929 | continue; |
2930 | } |
2931 | if (member.function->is_static) { |
2932 | continue; |
2933 | } |
2934 | ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
2935 | options.insert(option.display, option); |
2936 | } |
2937 | r_forced = true; |
2938 | } break; |
2939 | case GDScriptParser::COMPLETION_ASSIGN: { |
2940 | GDScriptCompletionIdentifier type; |
2941 | if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) { |
2942 | break; |
2943 | } |
2944 | if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) { |
2945 | _find_identifiers(completion_context, false, options, 0); |
2946 | r_forced = true; |
2947 | break; |
2948 | } |
2949 | |
2950 | if (!type.enumeration.is_empty()) { |
2951 | _find_enumeration_candidates(completion_context, type.enumeration, options); |
2952 | r_forced = options.size() > 0; |
2953 | } else { |
2954 | _find_identifiers(completion_context, false, options, 0); |
2955 | r_forced = true; |
2956 | } |
2957 | } break; |
2958 | case GDScriptParser::COMPLETION_METHOD: |
2959 | is_function = true; |
2960 | [[fallthrough]]; |
2961 | case GDScriptParser::COMPLETION_IDENTIFIER: { |
2962 | _find_identifiers(completion_context, is_function, options, 0); |
2963 | } break; |
2964 | case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD: |
2965 | is_function = true; |
2966 | [[fallthrough]]; |
2967 | case GDScriptParser::COMPLETION_ATTRIBUTE: { |
2968 | r_forced = true; |
2969 | const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node); |
2970 | if (attr->base) { |
2971 | GDScriptCompletionIdentifier base; |
2972 | bool found_type = _get_subscript_type(completion_context, attr, base.type); |
2973 | if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) { |
2974 | break; |
2975 | } |
2976 | |
2977 | _find_identifiers_in_base(base, is_function, options, 0); |
2978 | } |
2979 | } break; |
2980 | case GDScriptParser::COMPLETION_SUBSCRIPT: { |
2981 | const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node); |
2982 | GDScriptCompletionIdentifier base; |
2983 | if (!_guess_expression_type(completion_context, subscript->base, base)) { |
2984 | break; |
2985 | } |
2986 | |
2987 | _find_identifiers_in_base(base, false, options, 0); |
2988 | } break; |
2989 | case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: { |
2990 | if (!completion_context.current_class) { |
2991 | break; |
2992 | } |
2993 | const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node); |
2994 | bool found = true; |
2995 | GDScriptCompletionIdentifier base; |
2996 | base.type.kind = GDScriptParser::DataType::CLASS; |
2997 | base.type.type_source = GDScriptParser::DataType::INFERRED; |
2998 | base.type.is_constant = true; |
2999 | base.type.class_type = completion_context.current_class; |
3000 | base.value = completion_context.base; |
3001 | |
3002 | for (int i = 0; i < completion_context.current_argument; i++) { |
3003 | GDScriptCompletionIdentifier ci; |
3004 | if (!_guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci)) { |
3005 | found = false; |
3006 | break; |
3007 | } |
3008 | base = ci; |
3009 | } |
3010 | |
3011 | // TODO: Improve this to only list types. |
3012 | if (found) { |
3013 | _find_identifiers_in_base(base, false, options, 0); |
3014 | } |
3015 | r_forced = true; |
3016 | } break; |
3017 | case GDScriptParser::COMPLETION_RESOURCE_PATH: { |
3018 | if (EDITOR_GET("text_editor/completion/complete_file_paths" )) { |
3019 | _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options); |
3020 | r_forced = true; |
3021 | } |
3022 | } break; |
3023 | case GDScriptParser::COMPLETION_CALL_ARGUMENTS: { |
3024 | if (!completion_context.node) { |
3025 | break; |
3026 | } |
3027 | _find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint); |
3028 | } break; |
3029 | case GDScriptParser::COMPLETION_OVERRIDE_METHOD: { |
3030 | GDScriptParser::DataType native_type = completion_context.current_class->base_type; |
3031 | while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) { |
3032 | switch (native_type.kind) { |
3033 | case GDScriptParser::DataType::CLASS: { |
3034 | native_type = native_type.class_type->base_type; |
3035 | } break; |
3036 | default: { |
3037 | native_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3038 | } break; |
3039 | } |
3040 | } |
3041 | |
3042 | if (!native_type.is_set()) { |
3043 | break; |
3044 | } |
3045 | |
3046 | StringName class_name = native_type.native_type; |
3047 | if (!ClassDB::class_exists(class_name)) { |
3048 | break; |
3049 | } |
3050 | |
3051 | bool use_type_hint = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints" ).operator bool(); |
3052 | |
3053 | List<MethodInfo> virtual_methods; |
3054 | ClassDB::get_virtual_methods(class_name, &virtual_methods); |
3055 | |
3056 | { |
3057 | // Not truly a virtual method, but can also be "overridden". |
3058 | MethodInfo static_init("_static_init" ); |
3059 | static_init.return_val.type = Variant::NIL; |
3060 | static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL; |
3061 | virtual_methods.push_back(static_init); |
3062 | } |
3063 | |
3064 | for (const MethodInfo &mi : virtual_methods) { |
3065 | String method_hint = mi.name; |
3066 | if (method_hint.contains(":" )) { |
3067 | method_hint = method_hint.get_slice(":" , 0); |
3068 | } |
3069 | method_hint += "(" ; |
3070 | |
3071 | if (mi.arguments.size()) { |
3072 | for (int i = 0; i < mi.arguments.size(); i++) { |
3073 | if (i > 0) { |
3074 | method_hint += ", " ; |
3075 | } |
3076 | String arg = mi.arguments[i].name; |
3077 | if (arg.contains(":" )) { |
3078 | arg = arg.substr(0, arg.find(":" )); |
3079 | } |
3080 | method_hint += arg; |
3081 | if (use_type_hint) { |
3082 | method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name); |
3083 | } |
3084 | } |
3085 | } |
3086 | method_hint += ")" ; |
3087 | if (use_type_hint) { |
3088 | method_hint += " -> " + _get_visual_datatype(mi.return_val, false, class_name); |
3089 | } |
3090 | method_hint += ":" ; |
3091 | |
3092 | ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION); |
3093 | options.insert(option.display, option); |
3094 | } |
3095 | } break; |
3096 | case GDScriptParser::COMPLETION_GET_NODE: { |
3097 | // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically. |
3098 | if (p_owner) { |
3099 | List<String> opts; |
3100 | p_owner->get_argument_options("get_node" , 0, &opts); |
3101 | |
3102 | for (const String &E : opts) { |
3103 | r_forced = true; |
3104 | String opt = E.strip_edges(); |
3105 | if (opt.is_quoted()) { |
3106 | // Remove quotes so that we can handle user preferred quote style, |
3107 | // or handle NodePaths which are valid identifiers and don't need quotes. |
3108 | opt = opt.unquote(); |
3109 | } |
3110 | // The path needs quotes if it's not a valid identifier (with an exception |
3111 | // for "/" as path separator, which also doesn't require quotes). |
3112 | if (!opt.replace("/" , "_" ).is_valid_identifier()) { |
3113 | // Ignore quote_style and just use double quotes for paths with apostrophes. |
3114 | // Double quotes don't need to be checked because they're not valid in node and property names. |
3115 | opt = opt.quote(opt.contains("'" ) ? "\"" : quote_style); // Handle user preference. |
3116 | } |
3117 | ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); |
3118 | options.insert(option.display, option); |
3119 | } |
3120 | |
3121 | // Get autoloads. |
3122 | for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) { |
3123 | String path = "/root/" + E.key; |
3124 | ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH); |
3125 | options.insert(option.display, option); |
3126 | } |
3127 | } |
3128 | } break; |
3129 | case GDScriptParser::COMPLETION_SUPER_METHOD: { |
3130 | if (!completion_context.current_class) { |
3131 | break; |
3132 | } |
3133 | _find_identifiers_in_class(completion_context.current_class, true, false, true, options, 0); |
3134 | } break; |
3135 | } |
3136 | |
3137 | for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) { |
3138 | r_options->push_back(E.value); |
3139 | } |
3140 | |
3141 | return OK; |
3142 | } |
3143 | |
3144 | #else |
3145 | |
3146 | Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) { |
3147 | return OK; |
3148 | } |
3149 | |
3150 | #endif |
3151 | |
3152 | //////// END COMPLETION ////////// |
3153 | |
3154 | String GDScriptLanguage::_get_indentation() const { |
3155 | #ifdef TOOLS_ENABLED |
3156 | if (Engine::get_singleton()->is_editor_hint()) { |
3157 | bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type" ); |
3158 | |
3159 | if (use_space_indentation) { |
3160 | int indent_size = EDITOR_GET("text_editor/behavior/indent/size" ); |
3161 | return String(" " ).repeat(indent_size); |
3162 | } |
3163 | } |
3164 | #endif |
3165 | return "\t" ; |
3166 | } |
3167 | |
3168 | void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const { |
3169 | String indent = _get_indentation(); |
3170 | |
3171 | Vector<String> lines = p_code.split("\n" ); |
3172 | List<int> indent_stack; |
3173 | |
3174 | for (int i = 0; i < lines.size(); i++) { |
3175 | String l = lines[i]; |
3176 | int tc = 0; |
3177 | for (int j = 0; j < l.length(); j++) { |
3178 | if (l[j] == ' ' || l[j] == '\t') { |
3179 | tc++; |
3180 | } else { |
3181 | break; |
3182 | } |
3183 | } |
3184 | |
3185 | String st = l.substr(tc, l.length()).strip_edges(); |
3186 | if (st.is_empty() || st.begins_with("#" )) { |
3187 | continue; //ignore! |
3188 | } |
3189 | |
3190 | int ilevel = 0; |
3191 | if (indent_stack.size()) { |
3192 | ilevel = indent_stack.back()->get(); |
3193 | } |
3194 | |
3195 | if (tc > ilevel) { |
3196 | indent_stack.push_back(tc); |
3197 | } else if (tc < ilevel) { |
3198 | while (indent_stack.size() && indent_stack.back()->get() > tc) { |
3199 | indent_stack.pop_back(); |
3200 | } |
3201 | |
3202 | if (indent_stack.size() && indent_stack.back()->get() != tc) { |
3203 | indent_stack.push_back(tc); // this is not right but gets the job done |
3204 | } |
3205 | } |
3206 | |
3207 | if (i >= p_from_line) { |
3208 | l = indent.repeat(indent_stack.size()) + st; |
3209 | } else if (i > p_to_line) { |
3210 | break; |
3211 | } |
3212 | |
3213 | lines.write[i] = l; |
3214 | } |
3215 | |
3216 | p_code = "" ; |
3217 | for (int i = 0; i < lines.size(); i++) { |
3218 | if (i > 0) { |
3219 | p_code += "\n" ; |
3220 | } |
3221 | p_code += lines[i]; |
3222 | } |
3223 | } |
3224 | |
3225 | #ifdef TOOLS_ENABLED |
3226 | |
3227 | static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, bool p_is_function, GDScriptLanguage::LookupResult &r_result) { |
3228 | GDScriptParser::DataType base_type = p_base; |
3229 | |
3230 | while (base_type.is_set()) { |
3231 | switch (base_type.kind) { |
3232 | case GDScriptParser::DataType::CLASS: { |
3233 | if (base_type.class_type) { |
3234 | if (base_type.class_type->has_member(p_symbol)) { |
3235 | r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; |
3236 | r_result.location = base_type.class_type->get_member(p_symbol).get_line(); |
3237 | r_result.class_path = base_type.script_path; |
3238 | Error err = OK; |
3239 | r_result.script = GDScriptCache::get_shallow_script(r_result.class_path, err); |
3240 | return err; |
3241 | } |
3242 | base_type = base_type.class_type->base_type; |
3243 | } |
3244 | } break; |
3245 | case GDScriptParser::DataType::SCRIPT: { |
3246 | Ref<Script> scr = base_type.script_type; |
3247 | if (scr.is_valid()) { |
3248 | int line = scr->get_member_line(p_symbol); |
3249 | if (line >= 0) { |
3250 | r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; |
3251 | r_result.location = line; |
3252 | r_result.script = scr; |
3253 | return OK; |
3254 | } |
3255 | Ref<Script> base_script = scr->get_base_script(); |
3256 | if (base_script.is_valid()) { |
3257 | base_type.script_type = base_script; |
3258 | } else { |
3259 | base_type.kind = GDScriptParser::DataType::NATIVE; |
3260 | base_type.native_type = scr->get_instance_base_type(); |
3261 | } |
3262 | } else { |
3263 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3264 | } |
3265 | } break; |
3266 | case GDScriptParser::DataType::NATIVE: { |
3267 | StringName class_name = base_type.native_type; |
3268 | if (!ClassDB::class_exists(class_name)) { |
3269 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3270 | break; |
3271 | } |
3272 | |
3273 | if (ClassDB::has_method(class_name, p_symbol, true)) { |
3274 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; |
3275 | r_result.class_name = base_type.native_type; |
3276 | r_result.class_member = p_symbol; |
3277 | return OK; |
3278 | } |
3279 | |
3280 | List<MethodInfo> virtual_methods; |
3281 | ClassDB::get_virtual_methods(class_name, &virtual_methods, true); |
3282 | for (const MethodInfo &E : virtual_methods) { |
3283 | if (E.name == p_symbol) { |
3284 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; |
3285 | r_result.class_name = base_type.native_type; |
3286 | r_result.class_member = p_symbol; |
3287 | return OK; |
3288 | } |
3289 | } |
3290 | |
3291 | if (ClassDB::has_signal(class_name, p_symbol, true)) { |
3292 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL; |
3293 | r_result.class_name = base_type.native_type; |
3294 | r_result.class_member = p_symbol; |
3295 | return OK; |
3296 | } |
3297 | |
3298 | StringName enum_name = ClassDB::get_integer_constant_enum(class_name, p_symbol, true); |
3299 | if (enum_name != StringName()) { |
3300 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; |
3301 | r_result.class_name = base_type.native_type; |
3302 | r_result.class_member = enum_name; |
3303 | return OK; |
3304 | } |
3305 | |
3306 | List<String> constants; |
3307 | ClassDB::get_integer_constant_list(class_name, &constants, true); |
3308 | for (const String &E : constants) { |
3309 | if (E == p_symbol) { |
3310 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3311 | r_result.class_name = base_type.native_type; |
3312 | r_result.class_member = p_symbol; |
3313 | return OK; |
3314 | } |
3315 | } |
3316 | |
3317 | if (ClassDB::has_property(class_name, p_symbol, true)) { |
3318 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY; |
3319 | r_result.class_name = base_type.native_type; |
3320 | r_result.class_member = p_symbol; |
3321 | return OK; |
3322 | } |
3323 | |
3324 | StringName parent = ClassDB::get_parent_class(class_name); |
3325 | if (parent != StringName()) { |
3326 | base_type.native_type = parent; |
3327 | } else { |
3328 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3329 | } |
3330 | } break; |
3331 | case GDScriptParser::DataType::BUILTIN: { |
3332 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3333 | |
3334 | if (Variant::has_constant(base_type.builtin_type, p_symbol)) { |
3335 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3336 | r_result.class_name = Variant::get_type_name(base_type.builtin_type); |
3337 | r_result.class_member = p_symbol; |
3338 | return OK; |
3339 | } |
3340 | |
3341 | Variant v; |
3342 | Ref<RefCounted> v_ref; |
3343 | if (base_type.builtin_type == Variant::OBJECT) { |
3344 | v_ref.instantiate(); |
3345 | v = v_ref; |
3346 | } else { |
3347 | Callable::CallError err; |
3348 | Variant::construct(base_type.builtin_type, v, nullptr, 0, err); |
3349 | if (err.error != Callable::CallError::CALL_OK) { |
3350 | break; |
3351 | } |
3352 | } |
3353 | |
3354 | if (v.has_method(p_symbol)) { |
3355 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; |
3356 | r_result.class_name = Variant::get_type_name(base_type.builtin_type); |
3357 | r_result.class_member = p_symbol; |
3358 | return OK; |
3359 | } |
3360 | |
3361 | bool valid = false; |
3362 | v.get(p_symbol, &valid); |
3363 | if (valid) { |
3364 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY; |
3365 | r_result.class_name = Variant::get_type_name(base_type.builtin_type); |
3366 | r_result.class_member = p_symbol; |
3367 | return OK; |
3368 | } |
3369 | } break; |
3370 | default: { |
3371 | base_type.kind = GDScriptParser::DataType::UNRESOLVED; |
3372 | } break; |
3373 | } |
3374 | } |
3375 | |
3376 | return ERR_CANT_RESOLVE; |
3377 | } |
3378 | |
3379 | ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) { |
3380 | // Before parsing, try the usual stuff. |
3381 | if (ClassDB::class_exists(p_symbol)) { |
3382 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; |
3383 | r_result.class_name = p_symbol; |
3384 | return OK; |
3385 | } |
3386 | |
3387 | for (int i = 0; i < Variant::VARIANT_MAX; i++) { |
3388 | Variant::Type t = Variant::Type(i); |
3389 | if (Variant::get_type_name(t) == p_symbol) { |
3390 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; |
3391 | r_result.class_name = Variant::get_type_name(t); |
3392 | return OK; |
3393 | } |
3394 | } |
3395 | |
3396 | if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) { |
3397 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3398 | r_result.class_name = "@GDScript" ; |
3399 | r_result.class_member = p_symbol; |
3400 | return OK; |
3401 | } |
3402 | |
3403 | GDScriptParser parser; |
3404 | parser.parse(p_code, p_path, true); |
3405 | |
3406 | GDScriptParser::CompletionContext context = parser.get_completion_context(); |
3407 | context.base = p_owner; |
3408 | |
3409 | // Allows class functions with the names like built-ins to be handled properly. |
3410 | if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) { |
3411 | // Need special checks for assert and preload as they are technically |
3412 | // keywords, so are not registered in GDScriptUtilityFunctions. |
3413 | if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) { |
3414 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD; |
3415 | r_result.class_name = "@GDScript" ; |
3416 | r_result.class_member = p_symbol; |
3417 | return OK; |
3418 | } |
3419 | } |
3420 | |
3421 | GDScriptAnalyzer analyzer(&parser); |
3422 | analyzer.analyze(); |
3423 | |
3424 | if (context.current_class && context.current_class->extends.size() > 0) { |
3425 | bool success = false; |
3426 | ClassDB::get_integer_constant(context.current_class->extends[0]->name, p_symbol, &success); |
3427 | if (success) { |
3428 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3429 | r_result.class_name = context.current_class->extends[0]->name; |
3430 | r_result.class_member = p_symbol; |
3431 | return OK; |
3432 | } |
3433 | } |
3434 | |
3435 | bool is_function = false; |
3436 | |
3437 | switch (context.type) { |
3438 | case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: { |
3439 | if (!Variant::has_builtin_method(context.builtin_type, StringName(p_symbol))) { |
3440 | // A constant. |
3441 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3442 | r_result.class_name = Variant::get_type_name(context.builtin_type); |
3443 | r_result.class_member = p_symbol; |
3444 | return OK; |
3445 | } |
3446 | // A method. |
3447 | GDScriptParser::DataType base_type; |
3448 | base_type.kind = GDScriptParser::DataType::BUILTIN; |
3449 | base_type.builtin_type = context.builtin_type; |
3450 | if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) { |
3451 | return OK; |
3452 | } |
3453 | } break; |
3454 | case GDScriptParser::COMPLETION_SUPER_METHOD: |
3455 | case GDScriptParser::COMPLETION_METHOD: { |
3456 | is_function = true; |
3457 | [[fallthrough]]; |
3458 | } |
3459 | case GDScriptParser::COMPLETION_ASSIGN: |
3460 | case GDScriptParser::COMPLETION_CALL_ARGUMENTS: |
3461 | case GDScriptParser::COMPLETION_IDENTIFIER: |
3462 | case GDScriptParser::COMPLETION_PROPERTY_METHOD: { |
3463 | GDScriptParser::DataType base_type; |
3464 | if (context.current_class) { |
3465 | if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) { |
3466 | base_type = context.current_class->get_datatype(); |
3467 | } else { |
3468 | base_type = context.current_class->base_type; |
3469 | } |
3470 | } else { |
3471 | break; |
3472 | } |
3473 | |
3474 | if (!is_function && context.current_suite) { |
3475 | // Lookup local variables. |
3476 | const GDScriptParser::SuiteNode *suite = context.current_suite; |
3477 | while (suite) { |
3478 | if (suite->has_local(p_symbol)) { |
3479 | r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; |
3480 | r_result.location = suite->get_local(p_symbol).start_line; |
3481 | return OK; |
3482 | } |
3483 | suite = suite->parent_block; |
3484 | } |
3485 | } |
3486 | |
3487 | if (_lookup_symbol_from_base(base_type, p_symbol, is_function, r_result) == OK) { |
3488 | return OK; |
3489 | } |
3490 | |
3491 | if (!is_function) { |
3492 | // Guess in autoloads as singletons. |
3493 | if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) { |
3494 | const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol); |
3495 | if (autoload.is_singleton) { |
3496 | String scr_path = autoload.path; |
3497 | if (!scr_path.ends_with(".gd" )) { |
3498 | // Not a script, try find the script anyway, |
3499 | // may have some success. |
3500 | scr_path = scr_path.get_basename() + ".gd" ; |
3501 | } |
3502 | |
3503 | if (FileAccess::exists(scr_path)) { |
3504 | r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION; |
3505 | r_result.location = 0; |
3506 | r_result.script = ResourceLoader::load(scr_path); |
3507 | return OK; |
3508 | } |
3509 | } |
3510 | } |
3511 | |
3512 | // Global. |
3513 | HashMap<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map(); |
3514 | if (classes.has(p_symbol)) { |
3515 | Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]]; |
3516 | if (value.get_type() == Variant::OBJECT) { |
3517 | Object *obj = value; |
3518 | if (obj) { |
3519 | if (Object::cast_to<GDScriptNativeClass>(obj)) { |
3520 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; |
3521 | r_result.class_name = Object::cast_to<GDScriptNativeClass>(obj)->get_name(); |
3522 | } else { |
3523 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS; |
3524 | r_result.class_name = obj->get_class(); |
3525 | } |
3526 | |
3527 | // proxy class remove the underscore. |
3528 | if (r_result.class_name.begins_with("_" )) { |
3529 | r_result.class_name = r_result.class_name.substr(1); |
3530 | } |
3531 | return OK; |
3532 | } |
3533 | } else { |
3534 | /* |
3535 | // Because get_integer_constant_enum and get_integer_constant don't work on @GlobalScope |
3536 | // We cannot determine the exact nature of the identifier here |
3537 | // Otherwise these codes would work |
3538 | StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true); |
3539 | if (enumName != nullptr) { |
3540 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM; |
3541 | r_result.class_name = "@GlobalScope"; |
3542 | r_result.class_member = enumName; |
3543 | return OK; |
3544 | } |
3545 | else { |
3546 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT; |
3547 | r_result.class_name = "@GlobalScope"; |
3548 | r_result.class_member = p_symbol; |
3549 | return OK; |
3550 | }*/ |
3551 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE; |
3552 | r_result.class_name = "@GlobalScope" ; |
3553 | r_result.class_member = p_symbol; |
3554 | return OK; |
3555 | } |
3556 | } else { |
3557 | List<StringName> utility_functions; |
3558 | Variant::get_utility_function_list(&utility_functions); |
3559 | if (utility_functions.find(p_symbol) != nullptr) { |
3560 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE; |
3561 | r_result.class_name = "@GlobalScope" ; |
3562 | r_result.class_member = p_symbol; |
3563 | return OK; |
3564 | } |
3565 | } |
3566 | } |
3567 | } break; |
3568 | case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD: { |
3569 | is_function = true; |
3570 | [[fallthrough]]; |
3571 | } |
3572 | case GDScriptParser::COMPLETION_ATTRIBUTE: { |
3573 | if (context.node->type != GDScriptParser::Node::SUBSCRIPT) { |
3574 | break; |
3575 | } |
3576 | const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(context.node); |
3577 | if (!subscript->is_attribute) { |
3578 | break; |
3579 | } |
3580 | GDScriptCompletionIdentifier base; |
3581 | |
3582 | bool found_type = _get_subscript_type(context, subscript, base.type); |
3583 | if (!found_type && !_guess_expression_type(context, subscript->base, base)) { |
3584 | break; |
3585 | } |
3586 | |
3587 | if (_lookup_symbol_from_base(base.type, p_symbol, is_function, r_result) == OK) { |
3588 | return OK; |
3589 | } |
3590 | } break; |
3591 | case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: { |
3592 | if (context.node == nullptr || context.node->type != GDScriptParser::Node::TYPE) { |
3593 | break; |
3594 | } |
3595 | const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(context.node); |
3596 | |
3597 | GDScriptParser::DataType base_type; |
3598 | const GDScriptParser::IdentifierNode *prev = nullptr; |
3599 | for (const GDScriptParser::IdentifierNode *E : type->type_chain) { |
3600 | if (E->name == p_symbol && prev != nullptr) { |
3601 | base_type = prev->get_datatype(); |
3602 | break; |
3603 | } |
3604 | prev = E; |
3605 | } |
3606 | if (base_type.kind != GDScriptParser::DataType::CLASS) { |
3607 | GDScriptCompletionIdentifier base; |
3608 | if (!_guess_expression_type(context, prev, base)) { |
3609 | break; |
3610 | } |
3611 | base_type = base.type; |
3612 | } |
3613 | |
3614 | if (_lookup_symbol_from_base(base_type, p_symbol, is_function, r_result) == OK) { |
3615 | return OK; |
3616 | } |
3617 | } break; |
3618 | case GDScriptParser::COMPLETION_OVERRIDE_METHOD: { |
3619 | GDScriptParser::DataType base_type = context.current_class->base_type; |
3620 | |
3621 | if (_lookup_symbol_from_base(base_type, p_symbol, true, r_result) == OK) { |
3622 | return OK; |
3623 | } |
3624 | } break; |
3625 | case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: |
3626 | case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: |
3627 | case GDScriptParser::COMPLETION_TYPE_NAME: { |
3628 | GDScriptParser::DataType base_type = context.current_class->get_datatype(); |
3629 | |
3630 | if (_lookup_symbol_from_base(base_type, p_symbol, false, r_result) == OK) { |
3631 | return OK; |
3632 | } |
3633 | } break; |
3634 | case GDScriptParser::COMPLETION_ANNOTATION: { |
3635 | const String annotation_symbol = "@" + p_symbol; |
3636 | if (parser.annotation_exists(annotation_symbol)) { |
3637 | r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION; |
3638 | r_result.class_name = "@GDScript" ; |
3639 | r_result.class_member = annotation_symbol; |
3640 | return OK; |
3641 | } |
3642 | } break; |
3643 | default: { |
3644 | } |
3645 | } |
3646 | |
3647 | return ERR_CANT_RESOLVE; |
3648 | } |
3649 | |
3650 | #endif |
3651 | |