diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 1f140e14cdf9..45f9ec9c6a15 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -123,7 +123,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p r_symbol.selectionRange.start.line = r_symbol.range.start.line; r_symbol.detail = "class " + r_symbol.name; bool is_root_class = &r_symbol == &class_symbol; - r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class); + r_symbol.documentation = parse_documentation_as_markdown(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->line), is_root_class); for (int i = 0; i < p_class->variables.size(); ++i) { @@ -150,7 +150,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.detail += " = " + JSON::print(m.default_value); } - symbol.documentation = parse_documentation(line); + symbol.documentation = parse_documentation_as_markdown(line); symbol.uri = uri; symbol.script_path = path; @@ -170,7 +170,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.range.end.line = symbol.range.start.line; symbol.range.end.character = lines[line].length(); symbol.selectionRange.start.line = symbol.range.start.line; - symbol.documentation = parse_documentation(line); + symbol.documentation = parse_documentation_as_markdown(line); symbol.uri = uri; symbol.script_path = path; symbol.detail = "signal " + signal.name + "("; @@ -198,7 +198,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p symbol.range.end.line = symbol.range.start.line; symbol.range.end.character = lines[line].length(); symbol.selectionRange.start.line = symbol.range.start.line; - symbol.documentation = parse_documentation(line); + symbol.documentation = parse_documentation_as_markdown(line); symbol.uri = uri; symbol.script_path = path; @@ -266,7 +266,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN r_symbol.range.end.line = MAX(p_func->body->end_line - 2, p_func->body->line); r_symbol.range.end.character = lines[r_symbol.range.end.line].length(); r_symbol.selectionRange.start.line = r_symbol.range.start.line; - r_symbol.documentation = GDScriptWorkspace::marked_documentation(parse_documentation(line)); + r_symbol.documentation = parse_documentation_as_markdown(line); r_symbol.uri = uri; r_symbol.script_path = path; @@ -324,11 +324,67 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) { symbol.detail += ": " + var->datatype.to_string(); } - symbol.documentation = GDScriptWorkspace::marked_documentation(parse_documentation(line)); + symbol.documentation = parse_documentation_as_markdown(line); r_symbol.children.push_back(symbol); } } +String ExtendGDScriptParser::marked_documentation(const String &p_bbcode) { + + String markdown = p_bbcode.strip_edges(); + + Vector lines = markdown.split("\n"); + bool in_code_block = false; + int code_block_indent = -1; + + markdown = ""; + for (int i = 0; i < lines.size(); i++) { + String line = lines[i]; + int block_start = line.find("[codeblock]"); + if (block_start != -1) { + code_block_indent = block_start; + in_code_block = true; + line = "'''gdscript"; + line = "\n"; + } else if (in_code_block) { + line = "\t" + line.substr(code_block_indent, line.length()); + } + + if (in_code_block && line.find("[/codeblock]") != -1) { + line = "'''\n"; + line = "\n"; + in_code_block = false; + } + + if (!in_code_block) { + line = line.strip_edges(); + line = line.replace("[code]", "`"); + line = line.replace("[/code]", "`"); + line = line.replace("[i]", "*"); + line = line.replace("[/i]", "*"); + line = line.replace("[b]", "**"); + line = line.replace("[/b]", "**"); + line = line.replace("[u]", "__"); + line = line.replace("[/u]", "__"); + line = line.replace("[method ", "`"); + line = line.replace("[member ", "`"); + line = line.replace("[signal ", "`"); + line = line.replace("[enum ", "`"); + line = line.replace("[constant ", "`"); + line = line.replace("[", "`"); + line = line.replace("]", "`"); + } + + if (!in_code_block && i < lines.size() - 1) { + line += "\n\n"; + } else if (i < lines.size() - 1) { + line += "\n"; + } + markdown += line; + } + return markdown; +} + String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { ERR_FAIL_INDEX_V(p_line, lines.size(), String()); @@ -353,19 +409,11 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { String line_comment = lines[i].strip_edges(true, false); if (line_comment.begins_with("#")) { - if (line_comment.length() > 1) { - line_comment = line_comment.substr(1, line_comment.length()); - if (p_docs_down) { - doc_lines.push_back(line_comment); - } else { - doc_lines.push_front(line_comment); - } + line_comment = line_comment.substr(1, line_comment.length()); + if (p_docs_down) { + doc_lines.push_back(line_comment); } else { - if (p_docs_down) { - doc_lines.push_back(""); - } else { - doc_lines.push_front(""); - } + doc_lines.push_front(line_comment); } } else { break; @@ -374,8 +422,7 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { String doc; for (List::Element *E = doc_lines.front(); E; E = E->next()) { - String content = E->get(); - doc += content + "\n"; + doc += E->get() + "\n"; } return doc; } @@ -496,6 +543,10 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i return ret; } +String ExtendGDScriptParser::parse_documentation_as_markdown(int p_line, bool p_docs_down) { + return marked_documentation(parse_documentation(p_line, p_docs_down)); +} + const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const { if (p_line <= 0) { return &class_symbol; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index 397951aa1c59..dd0453d3ffa3 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -73,6 +73,11 @@ class ExtendGDScriptParser : public GDScriptParser { Array member_completions; + String parse_documentation_as_markdown(int p_line, bool p_docs_down = false); + +public: + static String marked_documentation(const String &p_bbcode); + public: _FORCE_INLINE_ const String &get_path() const { return path; } _FORCE_INLINE_ const Vector &get_lines() const { return lines; } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 98b5c2813060..afe461b68ec6 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -189,6 +189,10 @@ bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const { return bool(_EDITOR_GET("network/language_server/enable_smart_resolve")); } +bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const { + return bool(_EDITOR_GET("network/language_server/show_native_symbols_in_editor")); +} + GDScriptLanguageProtocol::GDScriptLanguageProtocol() { server = NULL; singleton = this; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index 1d7ed70fb05c..136b45fd7806 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -84,6 +84,7 @@ public: void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client = -1); bool is_smart_resolve_enabled() const; + bool is_goto_native_symbols_enabled() const; GDScriptLanguageProtocol(); ~GDScriptLanguageProtocol(); diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 893bfd5f9830..9bea4557acd2 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -38,6 +38,7 @@ GDScriptLanguageServer::GDScriptLanguageServer() { thread_exit = false; _EDITOR_DEF("network/language_server/remote_port", 6008); _EDITOR_DEF("network/language_server/enable_smart_resolve", false); + _EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false); } void GDScriptLanguageServer::_notification(int p_what) { diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 424cad5a42b7..a79c082141db 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -32,6 +32,7 @@ #include "../gdscript.h" #include "core/os/os.h" #include "editor/editor_settings.h" +#include "editor/plugins/script_text_editor.h" #include "gdscript_extend_parser.h" #include "gdscript_language_protocol.h" @@ -47,6 +48,7 @@ void GDScriptTextDocument::_bind_methods() { ClassDB::bind_method(D_METHOD("colorPresentation"), &GDScriptTextDocument::colorPresentation); ClassDB::bind_method(D_METHOD("hover"), &GDScriptTextDocument::hover); ClassDB::bind_method(D_METHOD("definition"), &GDScriptTextDocument::definition); + ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor"), &GDScriptTextDocument::show_native_symbol_in_editor); } void GDScriptTextDocument::didOpen(const Variant &p_param) { @@ -324,6 +326,31 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) { 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 (!symbol->native_class.empty() && GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) { + String id; + switch (symbol->kind) { + case lsp::SymbolKind::Class: + id = "class_name:" + symbol->name; + break; + case lsp::SymbolKind::Constant: + id = "class_constant:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Property: + case lsp::SymbolKind::Variable: + id = "class_property:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Enum: + id = "class_enum:" + symbol->native_class + ":" + symbol->name; + break; + case lsp::SymbolKind::Method: + case lsp::SymbolKind::Function: + id = "class_method:" + symbol->native_class + ":" + symbol->name; + break; + default: + id = "class_global:" + symbol->native_class + ":" + symbol->name; + break; + } + call_deferred("show_native_symbol_in_editor", id); } } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { @@ -332,12 +359,12 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) { for (List::Element *E = list.front(); E; E = E->next()) { if (const lsp::DocumentSymbol *s = E->get()) { - - lsp::Location location; - location.uri = s->uri; - location.range = s->range; - - arr.push_back(location.to_json()); + if (!s->uri.empty()) { + lsp::Location location; + location.uri = s->uri; + location.range = s->range; + arr.push_back(location.to_json()); + } } } } @@ -357,3 +384,8 @@ void GDScriptTextDocument::sync_script_content(const String &p_uri, const String String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_uri); GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content); } + +void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) { + ScriptEditor::get_singleton()->call_deferred("_help_class_goto", p_symbol_id); + OS::get_singleton()->move_window_to_foreground(); +} diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h index d1e11f684c05..d15022d2c48b 100644 --- a/modules/gdscript/language_server/gdscript_text_document.h +++ b/modules/gdscript/language_server/gdscript_text_document.h @@ -46,6 +46,7 @@ protected: void didChange(const Variant &p_param); void sync_script_content(const String &p_path, const String &p_content); + void show_native_symbol_in_editor(const String &p_symbol_id); Array native_member_completions; diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 237c44cc9251..6de02671a459 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -165,58 +165,6 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) return NULL; } -String GDScriptWorkspace::marked_documentation(const String &p_bbcode) { - - String markdown = p_bbcode.strip_edges(); - - Vector lines = markdown.split("\n"); - bool in_code_block = false; - int code_block_indent = -1; - - markdown = ""; - for (int i = 0; i < lines.size(); i++) { - String line = lines[i]; - int block_start = line.find("[codeblock]"); - if (block_start != -1) { - code_block_indent = block_start; - in_code_block = true; - line = "'''gdscript"; - line = "\n"; - } else if (in_code_block) { - line = "\t" + line.substr(code_block_indent, line.length()); - } - - if (in_code_block && line.find("[/codeblock]") != -1) { - line = "'''\n"; - line = "\n"; - in_code_block = false; - } - - if (!in_code_block) { - line = line.strip_edges(); - line = line.replace("[code]", "`"); - line = line.replace("[/code]", "`"); - line = line.replace("[i]", "*"); - line = line.replace("[/i]", "*"); - line = line.replace("[b]", "**"); - line = line.replace("[/b]", "**"); - line = line.replace("[u]", "__"); - line = line.replace("[/u]", "__"); - line = line.replace("[method ", "`"); - line = line.replace("[member ", "`"); - line = line.replace("[signal ", "`"); - line = line.replace("[", "`"); - line = line.replace("]", "`"); - } - - if (!in_code_block && i < lines.size() - 1) { - line += "\n"; - } - markdown += line + "\n"; - } - return markdown; -} - Array GDScriptWorkspace::symbol(const Dictionary &p_params) { String query = p_params["query"]; Array arr; @@ -244,24 +192,26 @@ Error GDScriptWorkspace::initialize() { lsp::DocumentSymbol class_symbol; String class_name = E->key(); class_symbol.name = class_name; + class_symbol.native_class = class_name; class_symbol.kind = lsp::SymbolKind::Class; class_symbol.detail = String(" class ") + class_name; if (!class_data.inherits.empty()) { class_symbol.detail += " extends " + class_data.inherits; } - class_symbol.documentation = marked_documentation(class_data.brief_description) + "\n" + marked_documentation(class_data.description); + class_symbol.documentation = ExtendGDScriptParser::marked_documentation(class_data.brief_description) + "\n" + ExtendGDScriptParser::marked_documentation(class_data.description); for (int i = 0; i < class_data.constants.size(); i++) { const DocData::ConstantDoc &const_data = class_data.constants[i]; lsp::DocumentSymbol symbol; symbol.name = const_data.name; + symbol.native_class = class_name; symbol.kind = lsp::SymbolKind::Constant; symbol.detail = "const " + class_name + "." + const_data.name; if (const_data.enumeration.length()) { symbol.detail += ": " + const_data.enumeration; } symbol.detail += " = " + const_data.value; - symbol.documentation = marked_documentation(const_data.description); + symbol.documentation = ExtendGDScriptParser::marked_documentation(const_data.description); class_symbol.children.push_back(symbol); } @@ -274,6 +224,7 @@ Error GDScriptWorkspace::initialize() { const DocData::PropertyDoc &data = class_data.properties[i]; lsp::DocumentSymbol symbol; symbol.name = data.name; + symbol.native_class = class_name; symbol.kind = lsp::SymbolKind::Property; symbol.detail = String(i >= theme_prop_start_idx ? " var" : "var") + " " + class_name + "." + data.name; if (data.enumeration.length()) { @@ -281,7 +232,7 @@ Error GDScriptWorkspace::initialize() { } else { symbol.detail += ": " + data.type; } - symbol.documentation = marked_documentation(data.description); + symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description); class_symbol.children.push_back(symbol); } @@ -295,6 +246,7 @@ Error GDScriptWorkspace::initialize() { lsp::DocumentSymbol symbol; symbol.name = data.name; + symbol.native_class = class_name; symbol.kind = i >= signal_start_idx ? lsp::SymbolKind::Event : lsp::SymbolKind::Method; String params = ""; @@ -318,7 +270,7 @@ Error GDScriptWorkspace::initialize() { } symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + data.return_type; - symbol.documentation = marked_documentation(data.description); + symbol.documentation = ExtendGDScriptParser::marked_documentation(data.description); class_symbol.children.push_back(symbol); } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 8c82c04c34a8..adce169d4beb 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -84,8 +84,6 @@ public: Dictionary generate_script_api(const String &p_path); - static String marked_documentation(const String &p_bbcode); - GDScriptWorkspace(); ~GDScriptWorkspace(); }; diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 065b8b65e83f..3e57b6ee7e69 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -1063,6 +1063,11 @@ struct DocumentSymbol { */ String documentation; + /** + * Class name for the native symbols + */ + String native_class; + /** * The kind of this symbol. */