From 72d11cd17355585bd3f2b6d467ebb45ad55b6759 Mon Sep 17 00:00:00 2001 From: geequlim Date: Sun, 30 Jun 2019 16:10:13 +0800 Subject: [PATCH] Add optional goto definition support for native symbols This action will show help for target symbol in godot editor and bring the godot editor window to foreground Improved markdown documentation for symbols. --- .../gdscript_extend_parser.cpp | 91 +++++++++++++++---- .../language_server/gdscript_extend_parser.h | 5 + .../gdscript_language_protocol.cpp | 4 + .../gdscript_language_protocol.h | 1 + .../gdscript_language_server.cpp | 1 + .../gdscript_text_document.cpp | 44 +++++++-- .../language_server/gdscript_text_document.h | 1 + .../language_server/gdscript_workspace.cpp | 64 ++----------- .../language_server/gdscript_workspace.h | 2 - modules/gdscript/language_server/lsp.hpp | 5 + 10 files changed, 134 insertions(+), 84 deletions(-) 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. */