diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 4d90c4eec380..1f140e14cdf9 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -139,7 +139,10 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.range.end.line = line; symbol.range.end.character = lines[line].length(); symbol.selectionRange.start.line = symbol.range.start.line; - symbol.detail = "var " + m.identifier; + if (m._export.type != Variant::NIL) { + symbol.detail += "export "; + } + symbol.detail += "var " + m.identifier; if (m.data_type.kind != GDScriptParser::DataType::UNRESOLVED) { symbol.detail += ": " + m.data_type.to_string(); } @@ -210,7 +213,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p if (res.is_valid() && !res->get_path().empty()) { value_text = "preload(\"" + res->get_path() + "\")"; if (symbol.documentation.empty()) { - if (Map::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(res->get_path())) { + if (Map::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) { symbol.documentation = S->get()->class_symbol.documentation; } } @@ -335,7 +338,7 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { String inline_comment = lines[p_line]; int comment_start = inline_comment.find("#"); if (comment_start != -1) { - inline_comment = inline_comment.substr(comment_start, inline_comment.length()); + inline_comment = inline_comment.substr(comment_start, inline_comment.length()).strip_edges(); if (inline_comment.length() > 1) { doc_lines.push_back(inline_comment.substr(1, inline_comment.length())); } @@ -407,7 +410,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c if (i == p_cursor.line) { String line = lines[i]; String first_part = line.substr(0, p_cursor.character); - String last_part = line.substr(p_cursor.character, lines[i].size()); + String last_part = line.substr(p_cursor.character + 1, lines[i].length()); if (!p_symbol.empty()) { String left_cursor_text; for (int c = p_cursor.character - 1; c >= 0; c--) { @@ -473,7 +476,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & } String ExtendGDScriptParser::get_uri() const { - return GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_uri(path); + return GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path); } const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const { @@ -555,6 +558,145 @@ const Array &ExtendGDScriptParser::get_member_completions() { return member_completions; } +Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::FunctionNode *p_func) const { + Dictionary func; + ERR_FAIL_NULL_V(p_func, func); + func["name"] = p_func->name; + func["return_type"] = p_func->return_type.to_string(); + func["rpc_mode"] = p_func->rpc_mode; + Array arguments; + for (int i = 0; i < p_func->arguments.size(); i++) { + Dictionary arg; + arg["name"] = p_func->arguments[i]; + arg["type"] = p_func->argument_types[i].to_string(); + int default_value_idx = i - (p_func->arguments.size() - p_func->default_values.size()); + if (default_value_idx >= 0) { + const GDScriptParser::ConstantNode *const_node = dynamic_cast(p_func->default_values[default_value_idx]); + if (const_node == NULL) { + const GDScriptParser::OperatorNode *operator_node = dynamic_cast(p_func->default_values[default_value_idx]); + if (operator_node) { + const_node = dynamic_cast(operator_node->next); + } + } + if (const_node) { + arg["default_value"] = const_node->value; + } + } + arguments.push_back(arg); + } + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->line))) { + func["signature"] = symbol->detail; + func["description"] = symbol->documentation; + } + func["arguments"] = arguments; + return func; +} + +Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode *p_class) const { + Dictionary class_api; + + ERR_FAIL_NULL_V(p_class, class_api); + + class_api["name"] = String(p_class->name); + class_api["path"] = path; + Array extends_class; + for (int i = 0; i < p_class->extends_class.size(); i++) { + extends_class.append(String(p_class->extends_class[i])); + } + class_api["extends_class"] = extends_class; + class_api["extends_file"] = String(p_class->extends_file); + class_api["icon"] = String(p_class->icon_path); + + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_class->line))) { + class_api["signature"] = symbol->detail; + class_api["description"] = symbol->documentation; + } + + Array subclasses; + for (int i = 0; i < p_class->subclasses.size(); i++) { + subclasses.push_back(dump_class_api(p_class->subclasses[i])); + } + class_api["sub_classes"] = subclasses; + + Array constants; + for (Map::Element *E = p_class->constant_expressions.front(); E; E = E->next()) { + + const GDScriptParser::ClassNode::Constant &c = E->value(); + const GDScriptParser::ConstantNode *node = dynamic_cast(c.expression); + + Dictionary api; + api["name"] = E->key(); + api["value"] = node->value; + api["data_type"] = node->datatype.to_string(); + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(node->line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + constants.push_back(api); + } + class_api["constants"] = constants; + + Array members; + for (int i = 0; i < p_class->variables.size(); ++i) { + const GDScriptParser::ClassNode::Member &m = p_class->variables[i]; + Dictionary api; + api["name"] = m.identifier; + api["data_type"] = m.data_type.to_string(); + api["default_value"] = m.default_value; + api["setter"] = String(m.setter); + api["getter"] = String(m.getter); + api["export"] = m._export.type != Variant::NIL; + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + members.push_back(api); + } + class_api["members"] = members; + + Array signals; + for (int i = 0; i < p_class->_signals.size(); ++i) { + const GDScriptParser::ClassNode::Signal &signal = p_class->_signals[i]; + Dictionary api; + api["name"] = signal.name; + Array args; + for (int j = 0; j < signal.arguments.size(); j++) { + args.append(signal.arguments[j]); + } + api["arguments"] = args; + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(signal.line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + signals.push_back(api); + } + class_api["signals"] = signals; + + Array methods; + for (int i = 0; i < p_class->functions.size(); ++i) { + methods.append(dump_function_api(p_class->functions[i])); + } + class_api["methods"] = methods; + + Array static_functions; + for (int i = 0; i < p_class->static_functions.size(); ++i) { + static_functions.append(dump_function_api(p_class->functions[i])); + } + class_api["static_functions"] = static_functions; + + return class_api; +} + +Dictionary ExtendGDScriptParser::generate_api() const { + + Dictionary api; + const GDScriptParser::Node *head = get_parse_tree(); + if (const GDScriptParser::ClassNode *gdclass = dynamic_cast(head)) { + api = dump_class_api(gdclass); + } + return api; +} + Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) { path = p_path; lines = p_code.split("\n"); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index e2da500d0db5..397951aa1c59 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -65,6 +65,9 @@ class ExtendGDScriptParser : public GDScriptParser { void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol); void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol); + Dictionary dump_function_api(const GDScriptParser::FunctionNode *p_func) const; + Dictionary dump_class_api(const GDScriptParser::ClassNode *p_class) const; + String parse_documentation(int p_line, bool p_docs_down = false); const lsp::DocumentSymbol *search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const; @@ -87,6 +90,7 @@ public: const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const; const Array &get_member_completions(); + Dictionary generate_api() const; Error parse(const String &p_code, const String &p_path); }; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 9ebabc276e8d..98b5c2813060 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -86,6 +86,12 @@ void GDScriptLanguageProtocol::_bind_methods() { ClassDB::bind_method(D_METHOD("on_data_received"), &GDScriptLanguageProtocol::on_data_received); ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected); ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected); + ClassDB::bind_method(D_METHOD("notify_all_clients", "p_method", "p_params"), &GDScriptLanguageProtocol::notify_all_clients, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("notify_client", "p_method", "p_params", "p_client"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled); + ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document); + ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace); + ClassDB::bind_method(D_METHOD("is_initialized"), &GDScriptLanguageProtocol::is_initialized); } Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { @@ -94,20 +100,20 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { String root_uri = p_params["rootUri"]; String root = p_params["rootPath"]; - bool is_same_workspace = root == workspace.root; - is_same_workspace = root.to_lower() == workspace.root.to_lower(); + bool is_same_workspace = root == workspace->root; + is_same_workspace = root.to_lower() == workspace->root.to_lower(); #ifdef WINDOWS_ENABLED - is_same_workspace = root.replace("\\", "/").to_lower() == workspace.root.to_lower(); + is_same_workspace = root.replace("\\", "/").to_lower() == workspace->root.to_lower(); #endif if (root_uri.length() && is_same_workspace) { - workspace.root_uri = root_uri; + workspace->root_uri = root_uri; } else { - workspace.root_uri = "file://" + workspace.root; + workspace->root_uri = "file://" + workspace->root; Dictionary params; - params["path"] = workspace.root; + params["path"] = workspace->root; Dictionary request = make_notification("gdscrip_client/changeWorkspace", params); if (Ref *peer = clients.getptr(lastest_client_id)) { String msg = JSON::print(request); @@ -118,8 +124,8 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { } if (!_initialized) { - workspace.initialize(); - text_document.initialize(); + workspace->initialize(); + text_document->initialize(); _initialized = true; } @@ -187,10 +193,12 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() { server = NULL; singleton = this; _initialized = false; - set_scope("textDocument", &text_document); - set_scope("completionItem", &text_document); - set_scope("workspace", &workspace); - workspace.root = ProjectSettings::get_singleton()->get_resource_path(); + workspace.instance(); + text_document.instance(); + set_scope("textDocument", text_document.ptr()); + set_scope("completionItem", text_document.ptr()); + set_scope("workspace", workspace.ptr()); + workspace->root = ProjectSettings::get_singleton()->get_resource_path(); } GDScriptLanguageProtocol::~GDScriptLanguageProtocol() { diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index be4a7cd47cea..1d7ed70fb05c 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -52,8 +52,8 @@ class GDScriptLanguageProtocol : public JSONRPC { WebSocketServer *server; int lastest_client_id; - GDScriptTextDocument text_document; - GDScriptWorkspace workspace; + Ref text_document; + Ref workspace; void on_data_received(int p_id); void on_client_connected(int p_id, const String &p_protocal); @@ -72,7 +72,9 @@ protected: public: _FORCE_INLINE_ static GDScriptLanguageProtocol *get_singleton() { return singleton; } - _FORCE_INLINE_ GDScriptWorkspace &get_workspace() { return workspace; } + _FORCE_INLINE_ Ref get_workspace() { return workspace; } + _FORCE_INLINE_ Ref get_text_document() { return text_document; } + _FORCE_INLINE_ bool is_initialized() const { return _initialized; } void poll(); Error start(int p_port); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index cb42e3164426..424cad5a42b7 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -77,7 +77,7 @@ void GDScriptTextDocument::initialize() { if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { - const HashMap &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members; + const HashMap &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members; const StringName *class_ptr = native_members.next(NULL); while (class_ptr) { @@ -103,9 +103,9 @@ void GDScriptTextDocument::initialize() { Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) { Dictionary params = p_params["textDocument"]; String uri = params["uri"]; - String path = GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_path(uri); + String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri); Array arr; - if (const Map::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(path)) { + if (const Map::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) { Vector list; parser->get()->get_symbols().symbol_tree_as_list(uri, list); for (int i = 0; i < list.size(); i++) { @@ -124,7 +124,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { Dictionary request_data = params.to_json(); List options; - GDScriptLanguageProtocol::get_singleton()->get_workspace().completion(params, &options); + GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); if (!options.empty()) { @@ -178,7 +178,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { arr = native_member_completions.duplicate(); - for (Map::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.front(); E; E = E->next()) { + for (Map::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) { ExtendGDScriptParser *script = E->get(); const Array &items = script->get_member_completions(); @@ -206,7 +206,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { if (data.get_type() == Variant::DICTIONARY) { params.load(p_params["data"]); - symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function); + symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function); } else if (data.get_type() == Variant::STRING) { @@ -224,14 +224,14 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { inner_class_name = param_symbols[1]; } - if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members.getptr(class_name)) { + if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members.getptr(class_name)) { if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) { symbol = *member; } } if (!symbol) { - if (const Map::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(class_name)) { + if (const Map::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) { symbol = E->get()->get_member_symbol(member_name, inner_class_name); } } @@ -284,7 +284,7 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) { lsp::TextDocumentPositionParams params; params.load(p_params); - const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params); + const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params); if (symbol) { lsp::Hover hover; @@ -296,7 +296,7 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) { Dictionary ret; Array contents; List list; - GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list); + GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list); for (List::Element *E = list.front(); E; E = E->next()) { if (const lsp::DocumentSymbol *s = E->get()) { contents.push_back(s->render().value); @@ -315,20 +315,20 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) { lsp::TextDocumentPositionParams params; params.load(p_params); - const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params); + const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params); if (symbol) { lsp::Location location; location.uri = symbol->uri; location.range = symbol->range; - const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_path(symbol->uri); + const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri); if (file_checker->file_exists(path)) { arr.push_back(location.to_json()); } } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { List list; - GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list); + GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list); for (List::Element *E = list.front(); E; E = E->next()) { if (const lsp::DocumentSymbol *s = E->get()) { @@ -354,6 +354,6 @@ GDScriptTextDocument::~GDScriptTextDocument() { } void GDScriptTextDocument::sync_script_content(const String &p_uri, const String &p_content) { - String path = GDScriptLanguageProtocol::get_singleton()->get_workspace().get_file_path(p_uri); - GDScriptLanguageProtocol::get_singleton()->get_workspace().parse_script(path, p_content); + String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_uri); + GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); } diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index ec95ea57650f..237c44cc9251 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -38,6 +38,12 @@ void GDScriptWorkspace::_bind_methods() { ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol); + ClassDB::bind_method(D_METHOD("parse_script", "p_path", "p_content"), &GDScriptWorkspace::parse_script); + ClassDB::bind_method(D_METHOD("parse_local_script", "p_path"), &GDScriptWorkspace::parse_local_script); + ClassDB::bind_method(D_METHOD("get_file_path", "p_uri"), &GDScriptWorkspace::get_file_path); + ClassDB::bind_method(D_METHOD("get_file_uri", "p_path"), &GDScriptWorkspace::get_file_uri); + ClassDB::bind_method(D_METHOD("publish_diagnostics", "p_path"), &GDScriptWorkspace::publish_diagnostics); + ClassDB::bind_method(D_METHOD("generate_script_api", "p_path"), &GDScriptWorkspace::generate_script_api); } void GDScriptWorkspace::remove_cache_parser(const String &p_path) { @@ -512,6 +518,14 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP } } +Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) { + Dictionary api; + if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) { + api = parser->generate_api(); + } + return api; +} + GDScriptWorkspace::GDScriptWorkspace() { ProjectSettings::get_singleton()->get_resource_path(); } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 1ecaba6f1f32..8c82c04c34a8 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -82,6 +82,8 @@ public: const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false); void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List &r_list); + Dictionary generate_script_api(const String &p_path); + static String marked_documentation(const String &p_bbcode); GDScriptWorkspace(); diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index edf877b9ac11..cabbbc485c65 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -44,6 +44,7 @@ Ref resource_saver_gd; #ifdef TOOLS_ENABLED +#include "core/engine.h" #include "editor/editor_export.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" @@ -131,8 +132,11 @@ static void _editor_init() { Ref gd_export; gd_export.instance(); EditorExport::get_singleton()->add_export_plugin(gd_export); - EditorNode::get_singleton()->add_editor_plugin(memnew(GDScriptLanguageServer)); + register_lsp_types(); + GDScriptLanguageServer *lsp_plugin = memnew(GDScriptLanguageServer); + EditorNode::get_singleton()->add_editor_plugin(lsp_plugin); + Engine::get_singleton()->add_singleton(Engine::Singleton("GDScriptLanguageProtocol", GDScriptLanguageProtocol::get_singleton())); } #endif