| 1 | /**************************************************************************/ | 
|---|
| 2 | /*  gdscript_workspace.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_workspace.h" | 
|---|
| 32 |  | 
|---|
| 33 | #include "../gdscript.h" | 
|---|
| 34 | #include "../gdscript_parser.h" | 
|---|
| 35 | #include "gdscript_language_protocol.h" | 
|---|
| 36 |  | 
|---|
| 37 | #include "core/config/project_settings.h" | 
|---|
| 38 | #include "core/object/script_language.h" | 
|---|
| 39 | #include "editor/doc_tools.h" | 
|---|
| 40 | #include "editor/editor_file_system.h" | 
|---|
| 41 | #include "editor/editor_help.h" | 
|---|
| 42 | #include "editor/editor_node.h" | 
|---|
| 43 | #include "editor/editor_settings.h" | 
|---|
| 44 | #include "scene/resources/packed_scene.h" | 
|---|
| 45 |  | 
|---|
| 46 | void GDScriptWorkspace::_bind_methods() { | 
|---|
| 47 | ClassDB::bind_method(D_METHOD( "apply_new_signal"), &GDScriptWorkspace::apply_new_signal); | 
|---|
| 48 | ClassDB::bind_method(D_METHOD( "didDeleteFiles"), &GDScriptWorkspace::did_delete_files); | 
|---|
| 49 | ClassDB::bind_method(D_METHOD( "parse_script", "path", "content"), &GDScriptWorkspace::parse_script); | 
|---|
| 50 | ClassDB::bind_method(D_METHOD( "parse_local_script", "path"), &GDScriptWorkspace::parse_local_script); | 
|---|
| 51 | ClassDB::bind_method(D_METHOD( "get_file_path", "uri"), &GDScriptWorkspace::get_file_path); | 
|---|
| 52 | ClassDB::bind_method(D_METHOD( "get_file_uri", "path"), &GDScriptWorkspace::get_file_uri); | 
|---|
| 53 | ClassDB::bind_method(D_METHOD( "publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics); | 
|---|
| 54 | ClassDB::bind_method(D_METHOD( "generate_script_api", "path"), &GDScriptWorkspace::generate_script_api); | 
|---|
| 55 | } | 
|---|
| 56 |  | 
|---|
| 57 | void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) { | 
|---|
| 58 | Ref<Script> scr = obj->get_script(); | 
|---|
| 59 |  | 
|---|
| 60 | if (scr->get_language()->get_name() != "GDScript") { | 
|---|
| 61 | return; | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | String function_signature = "func "+ function; | 
|---|
| 65 | String source = scr->get_source_code(); | 
|---|
| 66 |  | 
|---|
| 67 | if (source.contains(function_signature)) { | 
|---|
| 68 | return; | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | int first_class = source.find( "\nclass "); | 
|---|
| 72 | int start_line = 0; | 
|---|
| 73 | if (first_class != -1) { | 
|---|
| 74 | start_line = source.substr(0, first_class).split( "\n").size(); | 
|---|
| 75 | } else { | 
|---|
| 76 | start_line = source.split( "\n").size(); | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | String function_body = "\n\n"+ function_signature + "("; | 
|---|
| 80 | for (int i = 0; i < args.size(); ++i) { | 
|---|
| 81 | function_body += args[i]; | 
|---|
| 82 | if (i < args.size() - 1) { | 
|---|
| 83 | function_body += ", "; | 
|---|
| 84 | } | 
|---|
| 85 | } | 
|---|
| 86 | function_body += ")"; | 
|---|
| 87 | if (EditorSettings::get_singleton()->get_setting( "text_editor/completion/add_type_hints")) { | 
|---|
| 88 | function_body += " -> void"; | 
|---|
| 89 | } | 
|---|
| 90 | function_body += ":\n\tpass # Replace with function body.\n"; | 
|---|
| 91 |  | 
|---|
| 92 | lsp::TextEdit text_edit; | 
|---|
| 93 |  | 
|---|
| 94 | if (first_class != -1) { | 
|---|
| 95 | function_body += "\n\n"; | 
|---|
| 96 | } | 
|---|
| 97 | text_edit.range.end.line = text_edit.range.start.line = start_line; | 
|---|
| 98 |  | 
|---|
| 99 | text_edit.newText = function_body; | 
|---|
| 100 |  | 
|---|
| 101 | String uri = get_file_uri(scr->get_path()); | 
|---|
| 102 |  | 
|---|
| 103 | lsp::ApplyWorkspaceEditParams params; | 
|---|
| 104 | params.edit.add_edit(uri, text_edit); | 
|---|
| 105 |  | 
|---|
| 106 | GDScriptLanguageProtocol::get_singleton()->request_client( "workspace/applyEdit", params.to_json()); | 
|---|
| 107 | } | 
|---|
| 108 |  | 
|---|
| 109 | void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) { | 
|---|
| 110 | Array files = p_params[ "files"]; | 
|---|
| 111 | for (int i = 0; i < files.size(); ++i) { | 
|---|
| 112 | Dictionary file = files[i]; | 
|---|
| 113 | String uri = file[ "uri"]; | 
|---|
| 114 | String path = get_file_path(uri); | 
|---|
| 115 | parse_script(path, ""); | 
|---|
| 116 | } | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | void GDScriptWorkspace::remove_cache_parser(const String &p_path) { | 
|---|
| 120 | HashMap<String, ExtendGDScriptParser *>::Iterator parser = parse_results.find(p_path); | 
|---|
| 121 | HashMap<String, ExtendGDScriptParser *>::Iterator scr = scripts.find(p_path); | 
|---|
| 122 | if (parser && scr) { | 
|---|
| 123 | if (scr->value && scr->value == parser->value) { | 
|---|
| 124 | memdelete(scr->value); | 
|---|
| 125 | } else { | 
|---|
| 126 | memdelete(scr->value); | 
|---|
| 127 | memdelete(parser->value); | 
|---|
| 128 | } | 
|---|
| 129 | parse_results.erase(p_path); | 
|---|
| 130 | scripts.erase(p_path); | 
|---|
| 131 | } else if (parser) { | 
|---|
| 132 | memdelete(parser->value); | 
|---|
| 133 | parse_results.erase(p_path); | 
|---|
| 134 | } else if (scr) { | 
|---|
| 135 | memdelete(scr->value); | 
|---|
| 136 | scripts.erase(p_path); | 
|---|
| 137 | } | 
|---|
| 138 | } | 
|---|
| 139 |  | 
|---|
| 140 | const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_class, const String &p_member) const { | 
|---|
| 141 | StringName class_name = p_class; | 
|---|
| 142 | StringName empty; | 
|---|
| 143 |  | 
|---|
| 144 | while (class_name != empty) { | 
|---|
| 145 | if (HashMap<StringName, lsp::DocumentSymbol>::ConstIterator E = native_symbols.find(class_name)) { | 
|---|
| 146 | const lsp::DocumentSymbol &class_symbol = E->value; | 
|---|
| 147 |  | 
|---|
| 148 | if (p_member.is_empty()) { | 
|---|
| 149 | return &class_symbol; | 
|---|
| 150 | } else { | 
|---|
| 151 | for (int i = 0; i < class_symbol.children.size(); i++) { | 
|---|
| 152 | const lsp::DocumentSymbol &symbol = class_symbol.children[i]; | 
|---|
| 153 | if (symbol.name == p_member) { | 
|---|
| 154 | return &symbol; | 
|---|
| 155 | } | 
|---|
| 156 | } | 
|---|
| 157 | } | 
|---|
| 158 | } | 
|---|
| 159 | class_name = ClassDB::get_parent_class(class_name); | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|
| 162 | return nullptr; | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const { | 
|---|
| 166 | HashMap<String, ExtendGDScriptParser *>::ConstIterator S = scripts.find(p_path); | 
|---|
| 167 | if (S) { | 
|---|
| 168 | return &(S->value->get_symbols()); | 
|---|
| 169 | } | 
|---|
| 170 | return nullptr; | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 | const lsp::DocumentSymbol *GDScriptWorkspace::get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier) { | 
|---|
| 174 | for (int i = 0; i < p_parent->children.size(); ++i) { | 
|---|
| 175 | const lsp::DocumentSymbol *parameter_symbol = &p_parent->children[i]; | 
|---|
| 176 | if (!parameter_symbol->detail.is_empty() && parameter_symbol->name == symbol_identifier) { | 
|---|
| 177 | return parameter_symbol; | 
|---|
| 178 | } | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | return nullptr; | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | const lsp::DocumentSymbol *GDScriptWorkspace::get_local_symbol_at(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier, const lsp::Position p_position) { | 
|---|
| 185 | // Go down and pick closest `DocumentSymbol` with `p_symbol_identifier`. | 
|---|
| 186 |  | 
|---|
| 187 | const lsp::DocumentSymbol *current = &p_parser->get_symbols(); | 
|---|
| 188 | const lsp::DocumentSymbol *best_match = nullptr; | 
|---|
| 189 |  | 
|---|
| 190 | while (current) { | 
|---|
| 191 | if (current->name == p_symbol_identifier) { | 
|---|
| 192 | if (current->selectionRange.contains(p_position)) { | 
|---|
| 193 | // Exact match: pos is ON symbol decl identifier. | 
|---|
| 194 | return current; | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | best_match = current; | 
|---|
| 198 | } | 
|---|
| 199 |  | 
|---|
| 200 | const lsp::DocumentSymbol *parent = current; | 
|---|
| 201 | current = nullptr; | 
|---|
| 202 | for (const lsp::DocumentSymbol &child : parent->children) { | 
|---|
| 203 | if (child.range.contains(p_position)) { | 
|---|
| 204 | current = &child; | 
|---|
| 205 | break; | 
|---|
| 206 | } | 
|---|
| 207 | } | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | return best_match; | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | void GDScriptWorkspace::reload_all_workspace_scripts() { | 
|---|
| 214 | List<String> paths; | 
|---|
| 215 | list_script_files( "res://", paths); | 
|---|
| 216 | for (const String &path : paths) { | 
|---|
| 217 | Error err; | 
|---|
| 218 | String content = FileAccess::get_file_as_string(path, &err); | 
|---|
| 219 | ERR_CONTINUE(err != OK); | 
|---|
| 220 | err = parse_script(path, content); | 
|---|
| 221 |  | 
|---|
| 222 | if (err != OK) { | 
|---|
| 223 | HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(path); | 
|---|
| 224 | String err_msg = "Failed parse script "+ path; | 
|---|
| 225 | if (S) { | 
|---|
| 226 | err_msg += "\n"+ S->value->get_errors()[0].message; | 
|---|
| 227 | } | 
|---|
| 228 | ERR_CONTINUE_MSG(err != OK, err_msg); | 
|---|
| 229 | } | 
|---|
| 230 | } | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String> &r_files) { | 
|---|
| 234 | Error err; | 
|---|
| 235 | Ref<DirAccess> dir = DirAccess::open(p_root_dir, &err); | 
|---|
| 236 | if (OK == err) { | 
|---|
| 237 | dir->list_dir_begin(); | 
|---|
| 238 | String file_name = dir->get_next(); | 
|---|
| 239 | while (file_name.length()) { | 
|---|
| 240 | if (dir->current_is_dir() && file_name != "."&& file_name != ".."&& file_name != "./") { | 
|---|
| 241 | list_script_files(p_root_dir.path_join(file_name), r_files); | 
|---|
| 242 | } else if (file_name.ends_with( ".gd")) { | 
|---|
| 243 | String script_file = p_root_dir.path_join(file_name); | 
|---|
| 244 | r_files.push_back(script_file); | 
|---|
| 245 | } | 
|---|
| 246 | file_name = dir->get_next(); | 
|---|
| 247 | } | 
|---|
| 248 | } | 
|---|
| 249 | } | 
|---|
| 250 |  | 
|---|
| 251 | ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String &p_path) { | 
|---|
| 252 | HashMap<String, ExtendGDScriptParser *>::Iterator S = scripts.find(p_path); | 
|---|
| 253 | if (!S) { | 
|---|
| 254 | parse_local_script(p_path); | 
|---|
| 255 | S = scripts.find(p_path); | 
|---|
| 256 | } | 
|---|
| 257 | if (S) { | 
|---|
| 258 | return S->value; | 
|---|
| 259 | } | 
|---|
| 260 | return nullptr; | 
|---|
| 261 | } | 
|---|
| 262 |  | 
|---|
| 263 | ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) { | 
|---|
| 264 | HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(p_path); | 
|---|
| 265 | if (!S) { | 
|---|
| 266 | parse_local_script(p_path); | 
|---|
| 267 | S = parse_results.find(p_path); | 
|---|
| 268 | } | 
|---|
| 269 | if (S) { | 
|---|
| 270 | return S->value; | 
|---|
| 271 | } | 
|---|
| 272 | return nullptr; | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | Error GDScriptWorkspace::initialize() { | 
|---|
| 276 | if (initialized) { | 
|---|
| 277 | return OK; | 
|---|
| 278 | } | 
|---|
| 279 |  | 
|---|
| 280 | DocTools *doc = EditorHelp::get_doc_data(); | 
|---|
| 281 | for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) { | 
|---|
| 282 | const DocData::ClassDoc &class_data = E.value; | 
|---|
| 283 | lsp::DocumentSymbol class_symbol; | 
|---|
| 284 | String class_name = E.key; | 
|---|
| 285 | class_symbol.name = class_name; | 
|---|
| 286 | class_symbol.native_class = class_name; | 
|---|
| 287 | class_symbol.kind = lsp::SymbolKind::Class; | 
|---|
| 288 | class_symbol.detail = String( "<Native> class ") + class_name; | 
|---|
| 289 | if (!class_data.inherits.is_empty()) { | 
|---|
| 290 | class_symbol.detail += " extends "+ class_data.inherits; | 
|---|
| 291 | } | 
|---|
| 292 | class_symbol.documentation = class_data.brief_description + "\n"+ class_data.description; | 
|---|
| 293 |  | 
|---|
| 294 | for (int i = 0; i < class_data.constants.size(); i++) { | 
|---|
| 295 | const DocData::ConstantDoc &const_data = class_data.constants[i]; | 
|---|
| 296 | lsp::DocumentSymbol symbol; | 
|---|
| 297 | symbol.name = const_data.name; | 
|---|
| 298 | symbol.native_class = class_name; | 
|---|
| 299 | symbol.kind = lsp::SymbolKind::Constant; | 
|---|
| 300 | symbol.detail = "const "+ class_name + "."+ const_data.name; | 
|---|
| 301 | if (const_data.enumeration.length()) { | 
|---|
| 302 | symbol.detail += ": "+ const_data.enumeration; | 
|---|
| 303 | } | 
|---|
| 304 | symbol.detail += " = "+ const_data.value; | 
|---|
| 305 | symbol.documentation = const_data.description; | 
|---|
| 306 | class_symbol.children.push_back(symbol); | 
|---|
| 307 | } | 
|---|
| 308 |  | 
|---|
| 309 | for (int i = 0; i < class_data.properties.size(); i++) { | 
|---|
| 310 | const DocData::PropertyDoc &data = class_data.properties[i]; | 
|---|
| 311 | lsp::DocumentSymbol symbol; | 
|---|
| 312 | symbol.name = data.name; | 
|---|
| 313 | symbol.native_class = class_name; | 
|---|
| 314 | symbol.kind = lsp::SymbolKind::Property; | 
|---|
| 315 | symbol.detail = "var "+ class_name + "."+ data.name; | 
|---|
| 316 | if (data.enumeration.length()) { | 
|---|
| 317 | symbol.detail += ": "+ data.enumeration; | 
|---|
| 318 | } else { | 
|---|
| 319 | symbol.detail += ": "+ data.type; | 
|---|
| 320 | } | 
|---|
| 321 | symbol.documentation = data.description; | 
|---|
| 322 | class_symbol.children.push_back(symbol); | 
|---|
| 323 | } | 
|---|
| 324 |  | 
|---|
| 325 | for (int i = 0; i < class_data.theme_properties.size(); i++) { | 
|---|
| 326 | const DocData::ThemeItemDoc &data = class_data.theme_properties[i]; | 
|---|
| 327 | lsp::DocumentSymbol symbol; | 
|---|
| 328 | symbol.name = data.name; | 
|---|
| 329 | symbol.native_class = class_name; | 
|---|
| 330 | symbol.kind = lsp::SymbolKind::Property; | 
|---|
| 331 | symbol.detail = "<Theme> var "+ class_name + "."+ data.name + ": "+ data.type; | 
|---|
| 332 | symbol.documentation = data.description; | 
|---|
| 333 | class_symbol.children.push_back(symbol); | 
|---|
| 334 | } | 
|---|
| 335 |  | 
|---|
| 336 | Vector<DocData::MethodDoc> methods_signals; | 
|---|
| 337 | methods_signals.append_array(class_data.constructors); | 
|---|
| 338 | methods_signals.append_array(class_data.methods); | 
|---|
| 339 | methods_signals.append_array(class_data.operators); | 
|---|
| 340 | const int signal_start_idx = methods_signals.size(); | 
|---|
| 341 | methods_signals.append_array(class_data.signals); | 
|---|
| 342 |  | 
|---|
| 343 | for (int i = 0; i < methods_signals.size(); i++) { | 
|---|
| 344 | const DocData::MethodDoc &data = methods_signals[i]; | 
|---|
| 345 |  | 
|---|
| 346 | lsp::DocumentSymbol symbol; | 
|---|
| 347 | symbol.name = data.name; | 
|---|
| 348 | symbol.native_class = class_name; | 
|---|
| 349 | symbol.kind = i >= signal_start_idx ? lsp::SymbolKind::Event : lsp::SymbolKind::Method; | 
|---|
| 350 |  | 
|---|
| 351 | String params = ""; | 
|---|
| 352 | bool arg_default_value_started = false; | 
|---|
| 353 | for (int j = 0; j < data.arguments.size(); j++) { | 
|---|
| 354 | const DocData::ArgumentDoc &arg = data.arguments[j]; | 
|---|
| 355 |  | 
|---|
| 356 | lsp::DocumentSymbol symbol_arg; | 
|---|
| 357 | symbol_arg.name = arg.name; | 
|---|
| 358 | symbol_arg.kind = lsp::SymbolKind::Variable; | 
|---|
| 359 | symbol_arg.detail = arg.type; | 
|---|
| 360 |  | 
|---|
| 361 | if (!arg_default_value_started && !arg.default_value.is_empty()) { | 
|---|
| 362 | arg_default_value_started = true; | 
|---|
| 363 | } | 
|---|
| 364 | String arg_str = arg.name + ": "+ arg.type; | 
|---|
| 365 | if (arg_default_value_started) { | 
|---|
| 366 | arg_str += " = "+ arg.default_value; | 
|---|
| 367 | } | 
|---|
| 368 | if (j < data.arguments.size() - 1) { | 
|---|
| 369 | arg_str += ", "; | 
|---|
| 370 | } | 
|---|
| 371 | params += arg_str; | 
|---|
| 372 |  | 
|---|
| 373 | symbol.children.push_back(symbol_arg); | 
|---|
| 374 | } | 
|---|
| 375 | if (data.qualifiers.contains( "vararg")) { | 
|---|
| 376 | params += params.is_empty() ? "...": ", ..."; | 
|---|
| 377 | } | 
|---|
| 378 |  | 
|---|
| 379 | String return_type = data.return_type; | 
|---|
| 380 | if (return_type.is_empty()) { | 
|---|
| 381 | return_type = "void"; | 
|---|
| 382 | } | 
|---|
| 383 | symbol.detail = "func "+ class_name + "."+ data.name + "("+ params + ") -> "+ return_type; | 
|---|
| 384 | symbol.documentation = data.description; | 
|---|
| 385 | class_symbol.children.push_back(symbol); | 
|---|
| 386 | } | 
|---|
| 387 |  | 
|---|
| 388 | native_symbols.insert(class_name, class_symbol); | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | reload_all_workspace_scripts(); | 
|---|
| 392 |  | 
|---|
| 393 | if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { | 
|---|
| 394 | for (const KeyValue<StringName, lsp::DocumentSymbol> &E : native_symbols) { | 
|---|
| 395 | ClassMembers members; | 
|---|
| 396 | const lsp::DocumentSymbol &class_symbol = E.value; | 
|---|
| 397 | for (int i = 0; i < class_symbol.children.size(); i++) { | 
|---|
| 398 | const lsp::DocumentSymbol &symbol = class_symbol.children[i]; | 
|---|
| 399 | members.insert(symbol.name, &symbol); | 
|---|
| 400 | } | 
|---|
| 401 | native_members.insert(E.key, members); | 
|---|
| 402 | } | 
|---|
| 403 |  | 
|---|
| 404 | // Cache member completions. | 
|---|
| 405 | for (const KeyValue<String, ExtendGDScriptParser *> &S : scripts) { | 
|---|
| 406 | S.value->get_member_completions(); | 
|---|
| 407 | } | 
|---|
| 408 | } | 
|---|
| 409 |  | 
|---|
| 410 | EditorNode *editor_node = EditorNode::get_singleton(); | 
|---|
| 411 | editor_node->connect( "script_add_function_request", callable_mp(this, &GDScriptWorkspace::apply_new_signal)); | 
|---|
| 412 |  | 
|---|
| 413 | return OK; | 
|---|
| 414 | } | 
|---|
| 415 |  | 
|---|
| 416 | Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) { | 
|---|
| 417 | ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser); | 
|---|
| 418 | Error err = parser->parse(p_content, p_path); | 
|---|
| 419 | HashMap<String, ExtendGDScriptParser *>::Iterator last_parser = parse_results.find(p_path); | 
|---|
| 420 | HashMap<String, ExtendGDScriptParser *>::Iterator last_script = scripts.find(p_path); | 
|---|
| 421 |  | 
|---|
| 422 | if (err == OK) { | 
|---|
| 423 | remove_cache_parser(p_path); | 
|---|
| 424 | parse_results[p_path] = parser; | 
|---|
| 425 | scripts[p_path] = parser; | 
|---|
| 426 |  | 
|---|
| 427 | } else { | 
|---|
| 428 | if (last_parser && last_script && last_parser->value != last_script->value) { | 
|---|
| 429 | memdelete(last_parser->value); | 
|---|
| 430 | } | 
|---|
| 431 | parse_results[p_path] = parser; | 
|---|
| 432 | } | 
|---|
| 433 |  | 
|---|
| 434 | publish_diagnostics(p_path); | 
|---|
| 435 |  | 
|---|
| 436 | return err; | 
|---|
| 437 | } | 
|---|
| 438 |  | 
|---|
| 439 | static bool is_valid_rename_target(const lsp::DocumentSymbol *p_symbol) { | 
|---|
| 440 | // Must be valid symbol. | 
|---|
| 441 | if (!p_symbol) { | 
|---|
| 442 | return false; | 
|---|
| 443 | } | 
|---|
| 444 |  | 
|---|
| 445 | // Cannot rename builtin. | 
|---|
| 446 | if (!p_symbol->native_class.is_empty()) { | 
|---|
| 447 | return false; | 
|---|
| 448 | } | 
|---|
| 449 |  | 
|---|
| 450 | // Source must be available. | 
|---|
| 451 | if (p_symbol->script_path.is_empty()) { | 
|---|
| 452 | return false; | 
|---|
| 453 | } | 
|---|
| 454 |  | 
|---|
| 455 | return true; | 
|---|
| 456 | } | 
|---|
| 457 |  | 
|---|
| 458 | Dictionary GDScriptWorkspace::rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name) { | 
|---|
| 459 | lsp::WorkspaceEdit edit; | 
|---|
| 460 |  | 
|---|
| 461 | const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos); | 
|---|
| 462 | if (is_valid_rename_target(reference_symbol)) { | 
|---|
| 463 | Vector<lsp::Location> usages = find_all_usages(*reference_symbol); | 
|---|
| 464 | for (int i = 0; i < usages.size(); ++i) { | 
|---|
| 465 | lsp::Location loc = usages[i]; | 
|---|
| 466 |  | 
|---|
| 467 | edit.add_change(loc.uri, loc.range.start.line, loc.range.start.character, loc.range.end.character, new_name); | 
|---|
| 468 | } | 
|---|
| 469 | } | 
|---|
| 470 |  | 
|---|
| 471 | return edit.to_json(); | 
|---|
| 472 | } | 
|---|
| 473 |  | 
|---|
| 474 | bool GDScriptWorkspace::can_rename(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::DocumentSymbol &r_symbol, lsp::Range &r_range) { | 
|---|
| 475 | const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos); | 
|---|
| 476 | if (!is_valid_rename_target(reference_symbol)) { | 
|---|
| 477 | return false; | 
|---|
| 478 | } | 
|---|
| 479 |  | 
|---|
| 480 | String path = get_file_path(p_doc_pos.textDocument.uri); | 
|---|
| 481 | if (const ExtendGDScriptParser *parser = get_parse_result(path)) { | 
|---|
| 482 | parser->get_identifier_under_position(p_doc_pos.position, r_range); | 
|---|
| 483 | r_symbol = *reference_symbol; | 
|---|
| 484 | return true; | 
|---|
| 485 | } | 
|---|
| 486 |  | 
|---|
| 487 | return false; | 
|---|
| 488 | } | 
|---|
| 489 |  | 
|---|
| 490 | Vector<lsp::Location> GDScriptWorkspace::find_usages_in_file(const lsp::DocumentSymbol &p_symbol, const String &p_file_path) { | 
|---|
| 491 | Vector<lsp::Location> usages; | 
|---|
| 492 |  | 
|---|
| 493 | String identifier = p_symbol.name; | 
|---|
| 494 | if (const ExtendGDScriptParser *parser = get_parse_result(p_file_path)) { | 
|---|
| 495 | const PackedStringArray &content = parser->get_lines(); | 
|---|
| 496 | for (int i = 0; i < content.size(); ++i) { | 
|---|
| 497 | String line = content[i]; | 
|---|
| 498 |  | 
|---|
| 499 | int character = line.find(identifier); | 
|---|
| 500 | while (character > -1) { | 
|---|
| 501 | lsp::TextDocumentPositionParams params; | 
|---|
| 502 |  | 
|---|
| 503 | lsp::TextDocumentIdentifier text_doc; | 
|---|
| 504 | text_doc.uri = get_file_uri(p_file_path); | 
|---|
| 505 |  | 
|---|
| 506 | params.textDocument = text_doc; | 
|---|
| 507 | params.position.line = i; | 
|---|
| 508 | params.position.character = character; | 
|---|
| 509 |  | 
|---|
| 510 | const lsp::DocumentSymbol *other_symbol = resolve_symbol(params); | 
|---|
| 511 |  | 
|---|
| 512 | if (other_symbol == &p_symbol) { | 
|---|
| 513 | lsp::Location loc; | 
|---|
| 514 | loc.uri = text_doc.uri; | 
|---|
| 515 | loc.range.start = params.position; | 
|---|
| 516 | loc.range.end.line = params.position.line; | 
|---|
| 517 | loc.range.end.character = params.position.character + identifier.length(); | 
|---|
| 518 | usages.append(loc); | 
|---|
| 519 | } | 
|---|
| 520 |  | 
|---|
| 521 | character = line.find(identifier, character + 1); | 
|---|
| 522 | } | 
|---|
| 523 | } | 
|---|
| 524 | } | 
|---|
| 525 |  | 
|---|
| 526 | return usages; | 
|---|
| 527 | } | 
|---|
| 528 |  | 
|---|
| 529 | Vector<lsp::Location> GDScriptWorkspace::find_all_usages(const lsp::DocumentSymbol &p_symbol) { | 
|---|
| 530 | if (p_symbol.local) { | 
|---|
| 531 | // Only search in current document. | 
|---|
| 532 | return find_usages_in_file(p_symbol, p_symbol.script_path); | 
|---|
| 533 | } | 
|---|
| 534 | // Search in all documents. | 
|---|
| 535 | List<String> paths; | 
|---|
| 536 | list_script_files( "res://", paths); | 
|---|
| 537 |  | 
|---|
| 538 | Vector<lsp::Location> usages; | 
|---|
| 539 | for (List<String>::Element *PE = paths.front(); PE; PE = PE->next()) { | 
|---|
| 540 | usages.append_array(find_usages_in_file(p_symbol, PE->get())); | 
|---|
| 541 | } | 
|---|
| 542 | return usages; | 
|---|
| 543 | } | 
|---|
| 544 |  | 
|---|
| 545 | Error GDScriptWorkspace::parse_local_script(const String &p_path) { | 
|---|
| 546 | Error err; | 
|---|
| 547 | String content = FileAccess::get_file_as_string(p_path, &err); | 
|---|
| 548 | if (err == OK) { | 
|---|
| 549 | err = parse_script(p_path, content); | 
|---|
| 550 | } | 
|---|
| 551 | return err; | 
|---|
| 552 | } | 
|---|
| 553 |  | 
|---|
| 554 | String GDScriptWorkspace::get_file_path(const String &p_uri) const { | 
|---|
| 555 | String path = p_uri.uri_decode(); | 
|---|
| 556 | String base_uri = root_uri.uri_decode(); | 
|---|
| 557 | path = path.replacen(base_uri + "/", "res://"); | 
|---|
| 558 | return path; | 
|---|
| 559 | } | 
|---|
| 560 |  | 
|---|
| 561 | String GDScriptWorkspace::get_file_uri(const String &p_path) const { | 
|---|
| 562 | String uri = p_path; | 
|---|
| 563 | uri = uri.replace( "res://", root_uri + "/"); | 
|---|
| 564 | return uri; | 
|---|
| 565 | } | 
|---|
| 566 |  | 
|---|
| 567 | void GDScriptWorkspace::publish_diagnostics(const String &p_path) { | 
|---|
| 568 | Dictionary params; | 
|---|
| 569 | Array errors; | 
|---|
| 570 | HashMap<String, ExtendGDScriptParser *>::ConstIterator ele = parse_results.find(p_path); | 
|---|
| 571 | if (ele) { | 
|---|
| 572 | const Vector<lsp::Diagnostic> &list = ele->value->get_diagnostics(); | 
|---|
| 573 | errors.resize(list.size()); | 
|---|
| 574 | for (int i = 0; i < list.size(); ++i) { | 
|---|
| 575 | errors[i] = list[i].to_json(); | 
|---|
| 576 | } | 
|---|
| 577 | } | 
|---|
| 578 | params[ "diagnostics"] = errors; | 
|---|
| 579 | params[ "uri"] = get_file_uri(p_path); | 
|---|
| 580 | GDScriptLanguageProtocol::get_singleton()->notify_client( "textDocument/publishDiagnostics", params); | 
|---|
| 581 | } | 
|---|
| 582 |  | 
|---|
| 583 | void GDScriptWorkspace::_get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners) { | 
|---|
| 584 | if (!efsd) { | 
|---|
| 585 | return; | 
|---|
| 586 | } | 
|---|
| 587 |  | 
|---|
| 588 | for (int i = 0; i < efsd->get_subdir_count(); i++) { | 
|---|
| 589 | _get_owners(efsd->get_subdir(i), p_path, owners); | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | for (int i = 0; i < efsd->get_file_count(); i++) { | 
|---|
| 593 | Vector<String> deps = efsd->get_file_deps(i); | 
|---|
| 594 | bool found = false; | 
|---|
| 595 | for (int j = 0; j < deps.size(); j++) { | 
|---|
| 596 | if (deps[j] == p_path) { | 
|---|
| 597 | found = true; | 
|---|
| 598 | break; | 
|---|
| 599 | } | 
|---|
| 600 | } | 
|---|
| 601 | if (!found) { | 
|---|
| 602 | continue; | 
|---|
| 603 | } | 
|---|
| 604 |  | 
|---|
| 605 | owners.push_back(efsd->get_file_path(i)); | 
|---|
| 606 | } | 
|---|
| 607 | } | 
|---|
| 608 |  | 
|---|
| 609 | Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) { | 
|---|
| 610 | Node *owner_scene_node = nullptr; | 
|---|
| 611 | List<String> owners; | 
|---|
| 612 |  | 
|---|
| 613 | _get_owners(EditorFileSystem::get_singleton()->get_filesystem(), p_path, owners); | 
|---|
| 614 |  | 
|---|
| 615 | for (int i = 0; i < owners.size(); i++) { | 
|---|
| 616 | NodePath owner_path = owners[i]; | 
|---|
| 617 | Ref<Resource> owner_res = ResourceLoader::load(owner_path); | 
|---|
| 618 | if (Object::cast_to<PackedScene>(owner_res.ptr())) { | 
|---|
| 619 | Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res)); | 
|---|
| 620 | owner_scene_node = owner_packed_scene->instantiate(); | 
|---|
| 621 | break; | 
|---|
| 622 | } | 
|---|
| 623 | } | 
|---|
| 624 |  | 
|---|
| 625 | return owner_scene_node; | 
|---|
| 626 | } | 
|---|
| 627 |  | 
|---|
| 628 | void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptLanguage::CodeCompletionOption> *r_options) { | 
|---|
| 629 | String path = get_file_path(p_params.textDocument.uri); | 
|---|
| 630 | String call_hint; | 
|---|
| 631 | bool forced = false; | 
|---|
| 632 |  | 
|---|
| 633 | if (const ExtendGDScriptParser *parser = get_parse_result(path)) { | 
|---|
| 634 | Node *owner_scene_node = _get_owner_scene_node(path); | 
|---|
| 635 |  | 
|---|
| 636 | Array stack; | 
|---|
| 637 | Node *current = nullptr; | 
|---|
| 638 | if (owner_scene_node != nullptr) { | 
|---|
| 639 | stack.push_back(owner_scene_node); | 
|---|
| 640 |  | 
|---|
| 641 | while (!stack.is_empty()) { | 
|---|
| 642 | current = Object::cast_to<Node>(stack.pop_back()); | 
|---|
| 643 | Ref<GDScript> scr = current->get_script(); | 
|---|
| 644 | if (scr.is_valid() && scr->get_path() == path) { | 
|---|
| 645 | break; | 
|---|
| 646 | } | 
|---|
| 647 | for (int i = 0; i < current->get_child_count(); ++i) { | 
|---|
| 648 | stack.push_back(current->get_child(i)); | 
|---|
| 649 | } | 
|---|
| 650 | } | 
|---|
| 651 |  | 
|---|
| 652 | Ref<GDScript> scr = current->get_script(); | 
|---|
| 653 | if (!scr.is_valid() || scr->get_path() != path) { | 
|---|
| 654 | current = owner_scene_node; | 
|---|
| 655 | } | 
|---|
| 656 | } | 
|---|
| 657 |  | 
|---|
| 658 | String code = parser->get_text_for_completion(p_params.position); | 
|---|
| 659 | GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint); | 
|---|
| 660 | if (owner_scene_node) { | 
|---|
| 661 | memdelete(owner_scene_node); | 
|---|
| 662 | } | 
|---|
| 663 | } | 
|---|
| 664 | } | 
|---|
| 665 |  | 
|---|
| 666 | const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_required) { | 
|---|
| 667 | const lsp::DocumentSymbol *symbol = nullptr; | 
|---|
| 668 |  | 
|---|
| 669 | String path = get_file_path(p_doc_pos.textDocument.uri); | 
|---|
| 670 | if (const ExtendGDScriptParser *parser = get_parse_result(path)) { | 
|---|
| 671 | String symbol_identifier = p_symbol_name; | 
|---|
| 672 | Vector<String> identifier_parts = symbol_identifier.split( "("); | 
|---|
| 673 | if (identifier_parts.size()) { | 
|---|
| 674 | symbol_identifier = identifier_parts[0]; | 
|---|
| 675 | } | 
|---|
| 676 |  | 
|---|
| 677 | lsp::Position pos = p_doc_pos.position; | 
|---|
| 678 | if (symbol_identifier.is_empty()) { | 
|---|
| 679 | lsp::Range range; | 
|---|
| 680 | symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, range); | 
|---|
| 681 | pos.character = range.end.character; | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | if (!symbol_identifier.is_empty()) { | 
|---|
| 685 | if (ScriptServer::is_global_class(symbol_identifier)) { | 
|---|
| 686 | String class_path = ScriptServer::get_global_class_path(symbol_identifier); | 
|---|
| 687 | symbol = get_script_symbol(class_path); | 
|---|
| 688 |  | 
|---|
| 689 | } else { | 
|---|
| 690 | ScriptLanguage::LookupResult ret; | 
|---|
| 691 | if (symbol_identifier == "new"&& parser->get_lines()[p_doc_pos.position.line].replace( " ", "").replace( "\t", "").find( "new(") > -1) { | 
|---|
| 692 | symbol_identifier = "_init"; | 
|---|
| 693 | } | 
|---|
| 694 | if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) { | 
|---|
| 695 | if (ret.type == ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION) { | 
|---|
| 696 | String target_script_path = path; | 
|---|
| 697 | if (!ret.script.is_null()) { | 
|---|
| 698 | target_script_path = ret.script->get_path(); | 
|---|
| 699 | } else if (!ret.class_path.is_empty()) { | 
|---|
| 700 | target_script_path = ret.class_path; | 
|---|
| 701 | } | 
|---|
| 702 |  | 
|---|
| 703 | if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) { | 
|---|
| 704 | symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location), symbol_identifier); | 
|---|
| 705 |  | 
|---|
| 706 | if (symbol) { | 
|---|
| 707 | switch (symbol->kind) { | 
|---|
| 708 | case lsp::SymbolKind::Function: { | 
|---|
| 709 | if (symbol->name != symbol_identifier) { | 
|---|
| 710 | symbol = get_parameter_symbol(symbol, symbol_identifier); | 
|---|
| 711 | } | 
|---|
| 712 | } break; | 
|---|
| 713 | } | 
|---|
| 714 | } | 
|---|
| 715 | } | 
|---|
| 716 |  | 
|---|
| 717 | } else { | 
|---|
| 718 | String member = ret.class_member; | 
|---|
| 719 | if (member.is_empty() && symbol_identifier != ret.class_name) { | 
|---|
| 720 | member = symbol_identifier; | 
|---|
| 721 | } | 
|---|
| 722 | symbol = get_native_symbol(ret.class_name, member); | 
|---|
| 723 | } | 
|---|
| 724 | } else { | 
|---|
| 725 | symbol = get_local_symbol_at(parser, symbol_identifier, p_doc_pos.position); | 
|---|
| 726 | if (!symbol) { | 
|---|
| 727 | symbol = parser->get_member_symbol(symbol_identifier); | 
|---|
| 728 | } | 
|---|
| 729 | } | 
|---|
| 730 | } | 
|---|
| 731 | } | 
|---|
| 732 | } | 
|---|
| 733 |  | 
|---|
| 734 | return symbol; | 
|---|
| 735 | } | 
|---|
| 736 |  | 
|---|
| 737 | void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list) { | 
|---|
| 738 | String path = get_file_path(p_doc_pos.textDocument.uri); | 
|---|
| 739 | if (const ExtendGDScriptParser *parser = get_parse_result(path)) { | 
|---|
| 740 | String symbol_identifier; | 
|---|
| 741 | lsp::Range range; | 
|---|
| 742 | symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, range); | 
|---|
| 743 |  | 
|---|
| 744 | for (const KeyValue<StringName, ClassMembers> &E : native_members) { | 
|---|
| 745 | const ClassMembers &members = native_members.get(E.key); | 
|---|
| 746 | if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) { | 
|---|
| 747 | r_list.push_back(*symbol); | 
|---|
| 748 | } | 
|---|
| 749 | } | 
|---|
| 750 |  | 
|---|
| 751 | for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) { | 
|---|
| 752 | const ExtendGDScriptParser *scr = E.value; | 
|---|
| 753 | const ClassMembers &members = scr->get_members(); | 
|---|
| 754 | if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) { | 
|---|
| 755 | r_list.push_back(*symbol); | 
|---|
| 756 | } | 
|---|
| 757 |  | 
|---|
| 758 | for (const KeyValue<String, ClassMembers> &F : scr->get_inner_classes()) { | 
|---|
| 759 | const ClassMembers *inner_class = &F.value; | 
|---|
| 760 | if (const lsp::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) { | 
|---|
| 761 | r_list.push_back(*symbol); | 
|---|
| 762 | } | 
|---|
| 763 | } | 
|---|
| 764 | } | 
|---|
| 765 | } | 
|---|
| 766 | } | 
|---|
| 767 |  | 
|---|
| 768 | const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) { | 
|---|
| 769 | if (HashMap<StringName, lsp::DocumentSymbol>::Iterator E = native_symbols.find(p_params.native_class)) { | 
|---|
| 770 | const lsp::DocumentSymbol &symbol = E->value; | 
|---|
| 771 | if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) { | 
|---|
| 772 | return &symbol; | 
|---|
| 773 | } | 
|---|
| 774 |  | 
|---|
| 775 | for (int i = 0; i < symbol.children.size(); ++i) { | 
|---|
| 776 | if (symbol.children[i].name == p_params.symbol_name) { | 
|---|
| 777 | return &(symbol.children[i]); | 
|---|
| 778 | } | 
|---|
| 779 | } | 
|---|
| 780 | } | 
|---|
| 781 |  | 
|---|
| 782 | return nullptr; | 
|---|
| 783 | } | 
|---|
| 784 |  | 
|---|
| 785 | void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) { | 
|---|
| 786 | if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) { | 
|---|
| 787 | const List<lsp::DocumentLink> &links = parser->get_document_links(); | 
|---|
| 788 | for (const lsp::DocumentLink &E : links) { | 
|---|
| 789 | r_list.push_back(E); | 
|---|
| 790 | } | 
|---|
| 791 | } | 
|---|
| 792 | } | 
|---|
| 793 |  | 
|---|
| 794 | Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) { | 
|---|
| 795 | Dictionary api; | 
|---|
| 796 | if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) { | 
|---|
| 797 | api = parser->generate_api(); | 
|---|
| 798 | } | 
|---|
| 799 | return api; | 
|---|
| 800 | } | 
|---|
| 801 |  | 
|---|
| 802 | Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature) { | 
|---|
| 803 | if (const ExtendGDScriptParser *parser = get_parse_result(get_file_path(p_doc_pos.textDocument.uri))) { | 
|---|
| 804 | lsp::TextDocumentPositionParams text_pos; | 
|---|
| 805 | text_pos.textDocument = p_doc_pos.textDocument; | 
|---|
| 806 |  | 
|---|
| 807 | if (parser->get_left_function_call(p_doc_pos.position, text_pos.position, r_signature.activeParameter) == OK) { | 
|---|
| 808 | List<const lsp::DocumentSymbol *> symbols; | 
|---|
| 809 |  | 
|---|
| 810 | if (const lsp::DocumentSymbol *symbol = resolve_symbol(text_pos)) { | 
|---|
| 811 | symbols.push_back(symbol); | 
|---|
| 812 | } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { | 
|---|
| 813 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols); | 
|---|
| 814 | } | 
|---|
| 815 |  | 
|---|
| 816 | for (const lsp::DocumentSymbol *const &symbol : symbols) { | 
|---|
| 817 | if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) { | 
|---|
| 818 | lsp::SignatureInformation signature_info; | 
|---|
| 819 | signature_info.label = symbol->detail; | 
|---|
| 820 | signature_info.documentation = symbol->render(); | 
|---|
| 821 |  | 
|---|
| 822 | for (int i = 0; i < symbol->children.size(); i++) { | 
|---|
| 823 | const lsp::DocumentSymbol &arg = symbol->children[i]; | 
|---|
| 824 | lsp::ParameterInformation arg_info; | 
|---|
| 825 | arg_info.label = arg.name; | 
|---|
| 826 | signature_info.parameters.push_back(arg_info); | 
|---|
| 827 | } | 
|---|
| 828 | r_signature.signatures.push_back(signature_info); | 
|---|
| 829 | break; | 
|---|
| 830 | } | 
|---|
| 831 | } | 
|---|
| 832 |  | 
|---|
| 833 | if (r_signature.signatures.size()) { | 
|---|
| 834 | return OK; | 
|---|
| 835 | } | 
|---|
| 836 | } | 
|---|
| 837 | } | 
|---|
| 838 | return ERR_METHOD_NOT_FOUND; | 
|---|
| 839 | } | 
|---|
| 840 |  | 
|---|
| 841 | GDScriptWorkspace::GDScriptWorkspace() { | 
|---|
| 842 | ProjectSettings::get_singleton()->get_resource_path(); | 
|---|
| 843 | } | 
|---|
| 844 |  | 
|---|
| 845 | GDScriptWorkspace::~GDScriptWorkspace() { | 
|---|
| 846 | HashSet<String> cached_parsers; | 
|---|
| 847 |  | 
|---|
| 848 | for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) { | 
|---|
| 849 | cached_parsers.insert(E.key); | 
|---|
| 850 | } | 
|---|
| 851 |  | 
|---|
| 852 | for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) { | 
|---|
| 853 | cached_parsers.insert(E.key); | 
|---|
| 854 | } | 
|---|
| 855 |  | 
|---|
| 856 | for (const String &E : cached_parsers) { | 
|---|
| 857 | remove_cache_parser(E); | 
|---|
| 858 | } | 
|---|
| 859 | } | 
|---|
| 860 |  | 
|---|