| 1 | /**************************************************************************/ |
| 2 | /* gdscript_text_document.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_text_document.h" |
| 32 | |
| 33 | #include "../gdscript.h" |
| 34 | #include "gdscript_extend_parser.h" |
| 35 | #include "gdscript_language_protocol.h" |
| 36 | |
| 37 | #include "core/os/os.h" |
| 38 | #include "editor/editor_settings.h" |
| 39 | #include "editor/plugins/script_text_editor.h" |
| 40 | #include "servers/display_server.h" |
| 41 | |
| 42 | void GDScriptTextDocument::_bind_methods() { |
| 43 | ClassDB::bind_method(D_METHOD("didOpen" ), &GDScriptTextDocument::didOpen); |
| 44 | ClassDB::bind_method(D_METHOD("didClose" ), &GDScriptTextDocument::didClose); |
| 45 | ClassDB::bind_method(D_METHOD("didChange" ), &GDScriptTextDocument::didChange); |
| 46 | ClassDB::bind_method(D_METHOD("willSaveWaitUntil" ), &GDScriptTextDocument::willSaveWaitUntil); |
| 47 | ClassDB::bind_method(D_METHOD("didSave" ), &GDScriptTextDocument::didSave); |
| 48 | ClassDB::bind_method(D_METHOD("nativeSymbol" ), &GDScriptTextDocument::nativeSymbol); |
| 49 | ClassDB::bind_method(D_METHOD("documentSymbol" ), &GDScriptTextDocument::documentSymbol); |
| 50 | ClassDB::bind_method(D_METHOD("completion" ), &GDScriptTextDocument::completion); |
| 51 | ClassDB::bind_method(D_METHOD("resolve" ), &GDScriptTextDocument::resolve); |
| 52 | ClassDB::bind_method(D_METHOD("rename" ), &GDScriptTextDocument::rename); |
| 53 | ClassDB::bind_method(D_METHOD("prepareRename" ), &GDScriptTextDocument::prepareRename); |
| 54 | ClassDB::bind_method(D_METHOD("references" ), &GDScriptTextDocument::references); |
| 55 | ClassDB::bind_method(D_METHOD("foldingRange" ), &GDScriptTextDocument::foldingRange); |
| 56 | ClassDB::bind_method(D_METHOD("codeLens" ), &GDScriptTextDocument::codeLens); |
| 57 | ClassDB::bind_method(D_METHOD("documentLink" ), &GDScriptTextDocument::documentLink); |
| 58 | ClassDB::bind_method(D_METHOD("colorPresentation" ), &GDScriptTextDocument::colorPresentation); |
| 59 | ClassDB::bind_method(D_METHOD("hover" ), &GDScriptTextDocument::hover); |
| 60 | ClassDB::bind_method(D_METHOD("definition" ), &GDScriptTextDocument::definition); |
| 61 | ClassDB::bind_method(D_METHOD("declaration" ), &GDScriptTextDocument::declaration); |
| 62 | ClassDB::bind_method(D_METHOD("signatureHelp" ), &GDScriptTextDocument::signatureHelp); |
| 63 | ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor" ), &GDScriptTextDocument::show_native_symbol_in_editor); |
| 64 | } |
| 65 | |
| 66 | void GDScriptTextDocument::didOpen(const Variant &p_param) { |
| 67 | lsp::TextDocumentItem doc = load_document_item(p_param); |
| 68 | sync_script_content(doc.uri, doc.text); |
| 69 | } |
| 70 | |
| 71 | void GDScriptTextDocument::didClose(const Variant &p_param) { |
| 72 | // Left empty on purpose. Godot does nothing special on closing a document, |
| 73 | // but it satisfies LSP clients that require didClose be implemented. |
| 74 | } |
| 75 | |
| 76 | void GDScriptTextDocument::didChange(const Variant &p_param) { |
| 77 | lsp::TextDocumentItem doc = load_document_item(p_param); |
| 78 | Dictionary dict = p_param; |
| 79 | Array contentChanges = dict["contentChanges" ]; |
| 80 | for (int i = 0; i < contentChanges.size(); ++i) { |
| 81 | lsp::TextDocumentContentChangeEvent evt; |
| 82 | evt.load(contentChanges[i]); |
| 83 | doc.text = evt.text; |
| 84 | } |
| 85 | sync_script_content(doc.uri, doc.text); |
| 86 | } |
| 87 | |
| 88 | void GDScriptTextDocument::willSaveWaitUntil(const Variant &p_param) { |
| 89 | lsp::TextDocumentItem doc = load_document_item(p_param); |
| 90 | |
| 91 | String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri); |
| 92 | Ref<Script> scr = ResourceLoader::load(path); |
| 93 | if (scr.is_valid()) { |
| 94 | ScriptEditor::get_singleton()->clear_docs_from_script(scr); |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | void GDScriptTextDocument::didSave(const Variant &p_param) { |
| 99 | lsp::TextDocumentItem doc = load_document_item(p_param); |
| 100 | Dictionary dict = p_param; |
| 101 | String text = dict["text" ]; |
| 102 | |
| 103 | sync_script_content(doc.uri, text); |
| 104 | |
| 105 | String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri); |
| 106 | Ref<GDScript> scr = ResourceLoader::load(path); |
| 107 | if (scr.is_valid() && (scr->load_source_code(path) == OK)) { |
| 108 | if (scr->is_tool()) { |
| 109 | scr->get_language()->reload_tool_script(scr, true); |
| 110 | } else { |
| 111 | scr->reload(true); |
| 112 | } |
| 113 | scr->update_exports(); |
| 114 | ScriptEditor::get_singleton()->reload_scripts(true); |
| 115 | ScriptEditor::get_singleton()->update_docs_from_script(scr); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) { |
| 120 | lsp::TextDocumentItem doc; |
| 121 | Dictionary params = p_param; |
| 122 | doc.load(params["textDocument" ]); |
| 123 | return doc; |
| 124 | } |
| 125 | |
| 126 | void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol *symbol) { |
| 127 | ERR_FAIL_NULL(symbol); |
| 128 | GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol" , symbol->to_json(true)); |
| 129 | } |
| 130 | |
| 131 | void GDScriptTextDocument::initialize() { |
| 132 | if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { |
| 133 | for (const KeyValue<StringName, ClassMembers> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members) { |
| 134 | const ClassMembers &members = E.value; |
| 135 | |
| 136 | for (const KeyValue<String, const lsp::DocumentSymbol *> &F : members) { |
| 137 | const lsp::DocumentSymbol *symbol = members.get(F.key); |
| 138 | lsp::CompletionItem item = symbol->make_completion_item(); |
| 139 | item.data = JOIN_SYMBOLS(String(E.key), F.key); |
| 140 | native_member_completions.push_back(item.to_json()); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) { |
| 147 | Variant ret; |
| 148 | |
| 149 | lsp::NativeSymbolInspectParams params; |
| 150 | params.load(p_params); |
| 151 | |
| 152 | if (const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_native_symbol(params)) { |
| 153 | ret = symbol->to_json(true); |
| 154 | notify_client_show_symbol(symbol); |
| 155 | } |
| 156 | |
| 157 | return ret; |
| 158 | } |
| 159 | |
| 160 | Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) { |
| 161 | Dictionary params = p_params["textDocument" ]; |
| 162 | String uri = params["uri" ]; |
| 163 | String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri); |
| 164 | Array arr; |
| 165 | if (HashMap<String, ExtendGDScriptParser *>::ConstIterator parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) { |
| 166 | lsp::DocumentSymbol symbol = parser->value->get_symbols(); |
| 167 | arr.push_back(symbol.to_json(true)); |
| 168 | } |
| 169 | return arr; |
| 170 | } |
| 171 | |
| 172 | Array GDScriptTextDocument::completion(const Dictionary &p_params) { |
| 173 | Array arr; |
| 174 | |
| 175 | lsp::CompletionParams params; |
| 176 | params.load(p_params); |
| 177 | Dictionary request_data = params.to_json(); |
| 178 | |
| 179 | List<ScriptLanguage::CodeCompletionOption> options; |
| 180 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); |
| 181 | |
| 182 | if (!options.is_empty()) { |
| 183 | int i = 0; |
| 184 | arr.resize(options.size()); |
| 185 | |
| 186 | for (const ScriptLanguage::CodeCompletionOption &option : options) { |
| 187 | lsp::CompletionItem item; |
| 188 | item.label = option.display; |
| 189 | item.data = request_data; |
| 190 | item.insertText = option.insert_text; |
| 191 | |
| 192 | switch (option.kind) { |
| 193 | case ScriptLanguage::CODE_COMPLETION_KIND_ENUM: |
| 194 | item.kind = lsp::CompletionItemKind::Enum; |
| 195 | break; |
| 196 | case ScriptLanguage::CODE_COMPLETION_KIND_CLASS: |
| 197 | item.kind = lsp::CompletionItemKind::Class; |
| 198 | break; |
| 199 | case ScriptLanguage::CODE_COMPLETION_KIND_MEMBER: |
| 200 | item.kind = lsp::CompletionItemKind::Property; |
| 201 | break; |
| 202 | case ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION: |
| 203 | item.kind = lsp::CompletionItemKind::Method; |
| 204 | break; |
| 205 | case ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL: |
| 206 | item.kind = lsp::CompletionItemKind::Event; |
| 207 | break; |
| 208 | case ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT: |
| 209 | item.kind = lsp::CompletionItemKind::Constant; |
| 210 | break; |
| 211 | case ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE: |
| 212 | item.kind = lsp::CompletionItemKind::Variable; |
| 213 | break; |
| 214 | case ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH: |
| 215 | item.kind = lsp::CompletionItemKind::File; |
| 216 | break; |
| 217 | case ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH: |
| 218 | item.kind = lsp::CompletionItemKind::Snippet; |
| 219 | break; |
| 220 | case ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT: |
| 221 | item.kind = lsp::CompletionItemKind::Text; |
| 222 | break; |
| 223 | default: { |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | arr[i] = item.to_json(); |
| 228 | i++; |
| 229 | } |
| 230 | } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { |
| 231 | arr = native_member_completions.duplicate(); |
| 232 | |
| 233 | for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) { |
| 234 | ExtendGDScriptParser *scr = E.value; |
| 235 | const Array &items = scr->get_member_completions(); |
| 236 | |
| 237 | const int start_size = arr.size(); |
| 238 | arr.resize(start_size + items.size()); |
| 239 | for (int i = start_size; i < arr.size(); i++) { |
| 240 | arr[i] = items[i - start_size]; |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | return arr; |
| 245 | } |
| 246 | |
| 247 | Dictionary GDScriptTextDocument::rename(const Dictionary &p_params) { |
| 248 | lsp::TextDocumentPositionParams params; |
| 249 | params.load(p_params); |
| 250 | String new_name = p_params["newName" ]; |
| 251 | |
| 252 | return GDScriptLanguageProtocol::get_singleton()->get_workspace()->rename(params, new_name); |
| 253 | } |
| 254 | |
| 255 | Variant GDScriptTextDocument::prepareRename(const Dictionary &p_params) { |
| 256 | lsp::TextDocumentPositionParams params; |
| 257 | params.load(p_params); |
| 258 | |
| 259 | lsp::DocumentSymbol symbol; |
| 260 | lsp::Range range; |
| 261 | if (GDScriptLanguageProtocol::get_singleton()->get_workspace()->can_rename(params, symbol, range)) { |
| 262 | return Variant(range.to_json()); |
| 263 | } |
| 264 | |
| 265 | // `null` -> rename not valid at current location. |
| 266 | return Variant(); |
| 267 | } |
| 268 | |
| 269 | Array GDScriptTextDocument::references(const Dictionary &p_params) { |
| 270 | Array res; |
| 271 | |
| 272 | lsp::ReferenceParams params; |
| 273 | params.load(p_params); |
| 274 | |
| 275 | const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params); |
| 276 | if (symbol) { |
| 277 | Vector<lsp::Location> usages = GDScriptLanguageProtocol::get_singleton()->get_workspace()->find_all_usages(*symbol); |
| 278 | res.resize(usages.size()); |
| 279 | int declaration_adjustment = 0; |
| 280 | for (int i = 0; i < usages.size(); i++) { |
| 281 | lsp::Location usage = usages[i]; |
| 282 | if (!params.context.includeDeclaration && usage.range == symbol->range) { |
| 283 | declaration_adjustment++; |
| 284 | continue; |
| 285 | } |
| 286 | res[i - declaration_adjustment] = usages[i].to_json(); |
| 287 | } |
| 288 | |
| 289 | if (declaration_adjustment > 0) { |
| 290 | res.resize(res.size() - declaration_adjustment); |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | return res; |
| 295 | } |
| 296 | |
| 297 | Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { |
| 298 | lsp::CompletionItem item; |
| 299 | item.load(p_params); |
| 300 | |
| 301 | lsp::CompletionParams params; |
| 302 | Variant data = p_params["data" ]; |
| 303 | |
| 304 | const lsp::DocumentSymbol *symbol = nullptr; |
| 305 | |
| 306 | if (data.get_type() == Variant::DICTIONARY) { |
| 307 | params.load(p_params["data" ]); |
| 308 | symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function); |
| 309 | |
| 310 | } else if (data.get_type() == Variant::STRING) { |
| 311 | String query = data; |
| 312 | |
| 313 | Vector<String> param_symbols = query.split(SYMBOL_SEPERATOR, false); |
| 314 | |
| 315 | if (param_symbols.size() >= 2) { |
| 316 | String class_ = param_symbols[0]; |
| 317 | StringName class_name = class_; |
| 318 | String member_name = param_symbols[param_symbols.size() - 1]; |
| 319 | String inner_class_name; |
| 320 | if (param_symbols.size() >= 3) { |
| 321 | inner_class_name = param_symbols[1]; |
| 322 | } |
| 323 | |
| 324 | if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members.getptr(class_name)) { |
| 325 | if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) { |
| 326 | symbol = *member; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | if (!symbol) { |
| 331 | if (HashMap<String, ExtendGDScriptParser *>::ConstIterator E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) { |
| 332 | symbol = E->value->get_member_symbol(member_name, inner_class_name); |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | if (symbol) { |
| 339 | item.documentation = symbol->render(); |
| 340 | } |
| 341 | |
| 342 | if (item.kind == lsp::CompletionItemKind::Event) { |
| 343 | if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(" )) { |
| 344 | const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes" ) ? "'" : "\"" ; |
| 345 | item.insertText = item.label.quote(quote_style); |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | return item.to_json(true); |
| 350 | } |
| 351 | |
| 352 | Array GDScriptTextDocument::foldingRange(const Dictionary &p_params) { |
| 353 | Array arr; |
| 354 | return arr; |
| 355 | } |
| 356 | |
| 357 | Array GDScriptTextDocument::codeLens(const Dictionary &p_params) { |
| 358 | Array arr; |
| 359 | return arr; |
| 360 | } |
| 361 | |
| 362 | Array GDScriptTextDocument::documentLink(const Dictionary &p_params) { |
| 363 | Array ret; |
| 364 | |
| 365 | lsp::DocumentLinkParams params; |
| 366 | params.load(p_params); |
| 367 | |
| 368 | List<lsp::DocumentLink> links; |
| 369 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_document_links(params.textDocument.uri, links); |
| 370 | for (const lsp::DocumentLink &E : links) { |
| 371 | ret.push_back(E.to_json()); |
| 372 | } |
| 373 | return ret; |
| 374 | } |
| 375 | |
| 376 | Array GDScriptTextDocument::colorPresentation(const Dictionary &p_params) { |
| 377 | Array arr; |
| 378 | return arr; |
| 379 | } |
| 380 | |
| 381 | Variant GDScriptTextDocument::hover(const Dictionary &p_params) { |
| 382 | lsp::TextDocumentPositionParams params; |
| 383 | params.load(p_params); |
| 384 | |
| 385 | const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params); |
| 386 | if (symbol) { |
| 387 | lsp::Hover hover; |
| 388 | hover.contents = symbol->render(); |
| 389 | hover.range.start = params.position; |
| 390 | hover.range.end = params.position; |
| 391 | return hover.to_json(); |
| 392 | |
| 393 | } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { |
| 394 | Dictionary ret; |
| 395 | Array contents; |
| 396 | List<const lsp::DocumentSymbol *> list; |
| 397 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list); |
| 398 | for (const lsp::DocumentSymbol *&E : list) { |
| 399 | if (const lsp::DocumentSymbol *s = E) { |
| 400 | contents.push_back(s->render().value); |
| 401 | } |
| 402 | } |
| 403 | ret["contents" ] = contents; |
| 404 | return ret; |
| 405 | } |
| 406 | |
| 407 | return Variant(); |
| 408 | } |
| 409 | |
| 410 | Array GDScriptTextDocument::definition(const Dictionary &p_params) { |
| 411 | lsp::TextDocumentPositionParams params; |
| 412 | params.load(p_params); |
| 413 | List<const lsp::DocumentSymbol *> symbols; |
| 414 | Array arr = this->find_symbols(params, symbols); |
| 415 | return arr; |
| 416 | } |
| 417 | |
| 418 | Variant GDScriptTextDocument::declaration(const Dictionary &p_params) { |
| 419 | lsp::TextDocumentPositionParams params; |
| 420 | params.load(p_params); |
| 421 | List<const lsp::DocumentSymbol *> symbols; |
| 422 | Array arr = this->find_symbols(params, symbols); |
| 423 | if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol |
| 424 | const lsp::DocumentSymbol *symbol = symbols.front()->get(); |
| 425 | if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) { |
| 426 | String id; |
| 427 | switch (symbol->kind) { |
| 428 | case lsp::SymbolKind::Class: |
| 429 | id = "class_name:" + symbol->name; |
| 430 | break; |
| 431 | case lsp::SymbolKind::Constant: |
| 432 | id = "class_constant:" + symbol->native_class + ":" + symbol->name; |
| 433 | break; |
| 434 | case lsp::SymbolKind::Property: |
| 435 | case lsp::SymbolKind::Variable: |
| 436 | id = "class_property:" + symbol->native_class + ":" + symbol->name; |
| 437 | break; |
| 438 | case lsp::SymbolKind::Enum: |
| 439 | id = "class_enum:" + symbol->native_class + ":" + symbol->name; |
| 440 | break; |
| 441 | case lsp::SymbolKind::Method: |
| 442 | case lsp::SymbolKind::Function: |
| 443 | id = "class_method:" + symbol->native_class + ":" + symbol->name; |
| 444 | break; |
| 445 | default: |
| 446 | id = "class_global:" + symbol->native_class + ":" + symbol->name; |
| 447 | break; |
| 448 | } |
| 449 | call_deferred(SNAME("show_native_symbol_in_editor" ), id); |
| 450 | } else { |
| 451 | notify_client_show_symbol(symbol); |
| 452 | } |
| 453 | } |
| 454 | return arr; |
| 455 | } |
| 456 | |
| 457 | Variant GDScriptTextDocument::signatureHelp(const Dictionary &p_params) { |
| 458 | Variant ret; |
| 459 | |
| 460 | lsp::TextDocumentPositionParams params; |
| 461 | params.load(p_params); |
| 462 | |
| 463 | lsp::SignatureHelp s; |
| 464 | if (OK == GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_signature(params, s)) { |
| 465 | ret = s.to_json(); |
| 466 | } |
| 467 | |
| 468 | return ret; |
| 469 | } |
| 470 | |
| 471 | GDScriptTextDocument::GDScriptTextDocument() { |
| 472 | file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES); |
| 473 | } |
| 474 | |
| 475 | void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) { |
| 476 | String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path); |
| 477 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); |
| 478 | |
| 479 | EditorFileSystem::get_singleton()->update_file(path); |
| 480 | } |
| 481 | |
| 482 | void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) { |
| 483 | ScriptEditor::get_singleton()->call_deferred(SNAME("_help_class_goto" ), p_symbol_id); |
| 484 | |
| 485 | DisplayServer::get_singleton()->window_move_to_foreground(); |
| 486 | } |
| 487 | |
| 488 | Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &p_location, List<const lsp::DocumentSymbol *> &r_list) { |
| 489 | Array arr; |
| 490 | const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(p_location); |
| 491 | if (symbol) { |
| 492 | lsp::Location location; |
| 493 | location.uri = symbol->uri; |
| 494 | location.range = symbol->selectionRange; |
| 495 | const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri); |
| 496 | if (file_checker->file_exists(path)) { |
| 497 | arr.push_back(location.to_json()); |
| 498 | } |
| 499 | r_list.push_back(symbol); |
| 500 | } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { |
| 501 | List<const lsp::DocumentSymbol *> list; |
| 502 | GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list); |
| 503 | for (const lsp::DocumentSymbol *&E : list) { |
| 504 | if (const lsp::DocumentSymbol *s = E) { |
| 505 | if (!s->uri.is_empty()) { |
| 506 | lsp::Location location; |
| 507 | location.uri = s->uri; |
| 508 | location.range = s->selectionRange; |
| 509 | arr.push_back(location.to_json()); |
| 510 | r_list.push_back(s); |
| 511 | } |
| 512 | } |
| 513 | } |
| 514 | } |
| 515 | return arr; |
| 516 | } |
| 517 | |