diff options
author | Geequlim <geequlim@gmail.com> | 2019-06-25 12:12:41 +0800 |
---|---|---|
committer | geequlim <geequlim@gmail.com> | 2019-08-11 13:30:15 +0800 |
commit | b2f02317fabe284220c74c21229e4cad6ab74e93 (patch) | |
tree | 71817f93ffe84e92eed24accf2420bdfc41a2679 /modules/gdscript/language_server | |
parent | 76c9e4ceb73b02bd95ab0512e27229516208dc60 (diff) |
Improve symbol resolve for inner classes
Only level one inner classes would be resolved currently but it sould cover most real world use case
Improve documation parseing for const values
Improve documation format for native symbols
Diffstat (limited to 'modules/gdscript/language_server')
5 files changed, 156 insertions, 47 deletions
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index a6fbfd3779..16f4324da8 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -91,6 +91,16 @@ void ExtendGDScriptParser::update_symbols() { for (int i = 0; i < class_symbol.children.size(); i++) { const lsp::DocumentSymbol &symbol = class_symbol.children[i]; members.set(symbol.name, &symbol); + + // cache level one inner classes + if (symbol.kind == lsp::SymbolKind::Class) { + ClassMembers inner_class; + for (int j = 0; j < symbol.children.size(); j++) { + const lsp::DocumentSymbol &s = symbol.children[j]; + inner_class.set(s.name, &s); + } + inner_classes.set(symbol.name, inner_class); + } } } } @@ -112,7 +122,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_class->end_line); r_symbol.selectionRange.start.line = r_symbol.range.start.line; r_symbol.detail = "class " + r_symbol.name; - r_symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(p_class->line)); + 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); for (int i = 0; i < p_class->variables.size(); ++i) { @@ -192,7 +203,26 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p if (c.type.kind != GDScriptParser::DataType::UNRESOLVED) { symbol.detail += ": " + c.type.to_string(); } - symbol.detail += " = " + String(node->value); + + String value_text; + if (node->value.get_type() == Variant::OBJECT) { + RES res = node->value; + if (res.is_valid() && !res->get_path().empty()) { + value_text = "preload(\"" + res->get_path() + "\")"; + if (symbol.documentation.empty()) { + if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(res->get_path())) { + symbol.documentation = S->get()->class_symbol.documentation; + } + } + } else { + value_text = JSON::print(node->value); + } + } else { + value_text = JSON::print(node->value); + } + if (!value_text.empty()) { + symbol.detail += " = " + value_text; + } r_symbol.children.push_back(symbol); } @@ -296,29 +326,43 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN } } -String ExtendGDScriptParser::parse_documentation(int p_line) { +String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { ERR_FAIL_INDEX_V(p_line, lines.size(), String()); List<String> doc_lines; - // inline comment - 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()); - if (inline_comment.length() > 1) { - doc_lines.push_back(inline_comment.substr(1, inline_comment.length())); + if (!p_docs_down) { // inline comment + 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()); + if (inline_comment.length() > 1) { + doc_lines.push_back(inline_comment.substr(1, inline_comment.length())); + } } } - // upper line comments - for (int i = p_line - 1; i >= 0; --i) { + int step = p_docs_down ? 1 : -1; + int start_line = p_docs_down ? p_line : p_line - 1; + for (int i = start_line; true; i += step) { + + if (i < 0 || i >= lines.size()) break; + String line_comment = lines[i].strip_edges(true, false); if (line_comment.begins_with("#")) { if (line_comment.length() > 1) { - doc_lines.push_front(line_comment.substr(1, line_comment.length())); + 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); + } } else { - doc_lines.push_front(""); + if (p_docs_down) { + doc_lines.push_back(""); + } else { + doc_lines.push_front(""); + } } } else { break; @@ -456,11 +500,20 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int return search_symbol_defined_at_line(p_line, class_symbol); } -const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name) const { +const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const { - const lsp::DocumentSymbol *const *ptr = members.getptr(p_name); - if (ptr) { - return *ptr; + if (p_subclass.empty()) { + const lsp::DocumentSymbol *const *ptr = members.getptr(p_name); + if (ptr) { + return *ptr; + } + } else { + if (const ClassMembers *_class = inner_classes.getptr(p_subclass)) { + const lsp::DocumentSymbol *const *ptr = _class->getptr(p_name); + if (ptr) { + return *ptr; + } + } } return NULL; @@ -474,12 +527,29 @@ const Array &ExtendGDScriptParser::get_member_completions() { while (name) { const lsp::DocumentSymbol *symbol = members.get(*name); - lsp::CompletionItem item = symbol->make_completion_item(false); + lsp::CompletionItem item = symbol->make_completion_item(); item.data = JOIN_SYMBOLS(path, *name); member_completions.push_back(item.to_json()); name = members.next(name); } + + const String *_class = inner_classes.next(NULL); + while (_class) { + + const ClassMembers *inner_class = inner_classes.getptr(*_class); + const String *member_name = inner_class->next(NULL); + while (member_name) { + const lsp::DocumentSymbol *symbol = inner_class->get(*member_name); + lsp::CompletionItem item = symbol->make_completion_item(); + item.data = JOIN_SYMBOLS(path, JOIN_SYMBOLS(*_class, *member_name)); + member_completions.push_back(item.to_json()); + + member_name = inner_class->next(member_name); + } + + _class = inner_classes.next(_class); + } } return member_completions; diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index d20dca59cf..3710b92993 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -39,8 +39,12 @@ #define LINE_NUMBER_TO_INDEX(p_line) ((p_line)-1) #endif +#ifndef SYMBOL_SEPERATOR +#define SYMBOL_SEPERATOR "::" +#endif + #ifndef JOIN_SYMBOLS -#define JOIN_SYMBOLS(p_path, name) ((p_path) + "." + (name)) +#define JOIN_SYMBOLS(p_path, name) ((p_path) + SYMBOL_SEPERATOR + (name)) #endif typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers; @@ -54,6 +58,7 @@ class ExtendGDScriptParser : public GDScriptParser { lsp::DocumentSymbol class_symbol; Vector<lsp::Diagnostic> diagnostics; ClassMembers members; + HashMap<String, ClassMembers> inner_classes; void update_diagnostics(); @@ -61,7 +66,7 @@ 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); - String parse_documentation(int p_line); + 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; Array member_completions; @@ -73,6 +78,7 @@ public: _FORCE_INLINE_ const lsp::DocumentSymbol &get_symbols() const { return class_symbol; } _FORCE_INLINE_ const Vector<lsp::Diagnostic> &get_diagnostics() const { return diagnostics; } _FORCE_INLINE_ const ClassMembers &get_members() const { return members; } + _FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; } String get_text_for_completion(const lsp::Position &p_cursor) const; String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const; @@ -80,7 +86,7 @@ public: String get_uri() const; const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const; - const lsp::DocumentSymbol *get_member_symbol(const String &p_name) const; + const lsp::DocumentSymbol *get_member_symbol(const String &p_name, const String &p_subclass = "") const; const Array &get_member_completions(); diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index a5211fb0f1..cb42e31644 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -88,7 +88,7 @@ void GDScriptTextDocument::initialize() { while (name) { const lsp::DocumentSymbol *symbol = members.get(*name); - lsp::CompletionItem item = symbol->make_completion_item(false); + lsp::CompletionItem item = symbol->make_completion_item(); item.data = JOIN_SYMBOLS(String(*class_ptr), *name); native_member_completions.push_back(item.to_json()); @@ -171,7 +171,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { break; } - arr[i] = item.to_json(true); + arr[i] = item.to_json(); i++; } } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { @@ -211,12 +211,18 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { } else if (data.get_type() == Variant::STRING) { String query = data; - int seperator_pos = query.find_last("."); - if (seperator_pos >= 0 && seperator_pos < query.length() - 1) { - String class_ = query.substr(0, seperator_pos); + Vector<String> param_symbols = query.split(SYMBOL_SEPERATOR, false); + + if (param_symbols.size() >= 2) { + + String class_ = param_symbols[0]; StringName class_name = class_; - String member_name = query.substr(seperator_pos + 1, query.length()); + String member_name = param_symbols[param_symbols.size() - 1]; + String inner_class_name; + if (param_symbols.size() >= 3) { + inner_class_name = param_symbols[1]; + } if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members.getptr(class_name)) { if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) { @@ -226,7 +232,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { if (!symbol) { if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(class_name)) { - symbol = E->get()->get_member_symbol(member_name); + symbol = E->get()->get_member_symbol(member_name, inner_class_name); } } } @@ -248,7 +254,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { } } - return item.to_json(); + return item.to_json(true); } Array GDScriptTextDocument::foldingRange(const Dictionary &p_params) { diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 089c19e6a4..ec95ea5765 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -170,14 +170,6 @@ String GDScriptWorkspace::marked_documentation(const String &p_bbcode) { markdown = ""; for (int i = 0; i < lines.size(); i++) { String line = lines[i]; - 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]", "__"); int block_start = line.find("[codeblock]"); if (block_start != -1) { code_block_indent = block_start; @@ -186,14 +178,31 @@ String GDScriptWorkspace::marked_documentation(const String &p_bbcode) { line = "\n"; } else if (in_code_block) { line = "\t" + line.substr(code_block_indent, line.length()); - } else { - line = line.strip_edges(); } + 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"; } @@ -310,6 +319,8 @@ Error GDScriptWorkspace::initialize() { native_symbols.insert(class_name, class_symbol); } + reload_all_workspace_scripts(); + if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) { ClassMembers members; @@ -320,9 +331,12 @@ Error GDScriptWorkspace::initialize() { } native_members.set(E->key(), members); } - } - reload_all_workspace_scripts(); + // cache member completions + for (Map<String, ExtendGDScriptParser *>::Element *S = scripts.front(); S; S = S->next()) { + S->get()->get_member_completions(); + } + } return OK; } @@ -477,10 +491,23 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP } for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) { - const ClassMembers &members = E->get()->get_members(); + const ExtendGDScriptParser *script = E->get(); + const ClassMembers &members = script->get_members(); if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) { r_list.push_back(*symbol); } + + const HashMap<String, ClassMembers> &inner_classes = script->get_inner_classes(); + const String *_class = inner_classes.next(NULL); + while (_class) { + + const ClassMembers *inner_class = inner_classes.getptr(*_class); + if (const lsp::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) { + r_list.push_back(*symbol); + } + + _class = inner_classes.next(_class); + } } } } diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index c208d5a198..065b8b65e8 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -886,12 +886,12 @@ struct CompletionItem { */ Variant data; - _FORCE_INLINE_ Dictionary to_json(bool minimized = false) const { + _FORCE_INLINE_ Dictionary to_json(bool resolved = false) const { Dictionary dict; dict["label"] = label; dict["kind"] = kind; dict["data"] = data; - if (!minimized) { + if (resolved) { dict["insertText"] = insertText; dict["detail"] = detail; dict["documentation"] = documentation.to_json(); @@ -1145,12 +1145,12 @@ struct DocumentSymbol { return markdown; } - _FORCE_INLINE_ CompletionItem make_completion_item(bool with_doc = false) const { + _FORCE_INLINE_ CompletionItem make_completion_item(bool resolved = false) const { lsp::CompletionItem item; item.label = name; - if (with_doc) { + if (resolved) { item.documentation = render(); } |