| 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 |  | 
|---|