2
0

gdscript_text_document.cpp 18 KB

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