diff options
Diffstat (limited to 'modules/gdscript/language_server')
9 files changed, 645 insertions, 494 deletions
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 0f6f13944b..668dfd4835 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -29,24 +29,27 @@ /*************************************************************************/ #include "gdscript_extend_parser.h" + #include "../gdscript.h" +#include "../gdscript_analyzer.h" #include "core/io/json.h" #include "gdscript_language_protocol.h" #include "gdscript_workspace.h" void ExtendGDScriptParser::update_diagnostics() { - diagnostics.clear(); - if (has_error()) { + const List<ParserError> &errors = get_errors(); + for (const List<ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) { + const ParserError &error = E->get(); lsp::Diagnostic diagnostic; diagnostic.severity = lsp::DiagnosticSeverity::Error; - diagnostic.message = get_error(); + diagnostic.message = error.message; diagnostic.source = "gdscript"; diagnostic.code = -1; lsp::Range range; lsp::Position pos; - int line = LINE_NUMBER_TO_INDEX(get_error_line()); + int line = LINE_NUMBER_TO_INDEX(error.line); const String &line_text = get_lines()[line]; pos.line = line; pos.character = line_text.length() - line_text.strip_edges(true, false).length(); @@ -62,12 +65,12 @@ void ExtendGDScriptParser::update_diagnostics() { const GDScriptWarning &warning = E->get(); lsp::Diagnostic diagnostic; diagnostic.severity = lsp::DiagnosticSeverity::Warning; - diagnostic.message = warning.get_message(); + diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message(); diagnostic.source = "gdscript"; diagnostic.code = warning.code; lsp::Range range; lsp::Position pos; - int line = LINE_NUMBER_TO_INDEX(warning.line); + int line = LINE_NUMBER_TO_INDEX(warning.start_line); const String &line_text = get_lines()[line]; pos.line = line; pos.character = line_text.length() - line_text.strip_edges(true, false).length(); @@ -80,12 +83,10 @@ void ExtendGDScriptParser::update_diagnostics() { } void ExtendGDScriptParser::update_symbols() { - members.clear(); - const GDScriptParser::Node *head = get_parse_tree(); + const GDScriptParser::Node *head = get_tree(); if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) { - parse_class_symbol(gdclass, class_symbol); for (int i = 0; i < class_symbol.children.size(); i++) { @@ -108,15 +109,15 @@ void ExtendGDScriptParser::update_symbols() { void ExtendGDScriptParser::update_document_links(const String &p_code) { document_links.clear(); - GDScriptTokenizerText tokenizer; + GDScriptTokenizer tokenizer; FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES); - tokenizer.set_code(p_code); + tokenizer.set_source_code(p_code); while (true) { - GDScriptTokenizerText::Token token = tokenizer.get_token(); - if (token == GDScriptTokenizer::TK_EOF || token == GDScriptTokenizer::TK_ERROR) { + GDScriptTokenizer::Token token = tokenizer.scan(); + if (token.type == GDScriptTokenizer::Token::TK_EOF) { break; - } else if (token == GDScriptTokenizer::TK_CONSTANT) { - const Variant &const_val = tokenizer.get_token_constant(); + } else if (token.type == GDScriptTokenizer::Token::LITERAL) { + const Variant &const_val = token.literal; if (const_val.get_type() == Variant::STRING) { String path = const_val; bool exists = fs->file_exists(path); @@ -128,239 +129,255 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) { String value = const_val; lsp::DocumentLink link; link.target = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_uri(path); - link.range.start.line = LINE_NUMBER_TO_INDEX(tokenizer.get_token_line()); - link.range.end.line = link.range.start.line; - link.range.end.character = LINE_NUMBER_TO_INDEX(tokenizer.get_token_column()); - link.range.start.character = link.range.end.character - value.length(); + link.range.start.line = LINE_NUMBER_TO_INDEX(token.start_line); + link.range.end.line = LINE_NUMBER_TO_INDEX(token.end_line); + link.range.start.character = LINE_NUMBER_TO_INDEX(token.start_column); + link.range.end.character = LINE_NUMBER_TO_INDEX(token.end_column); document_links.push_back(link); } } } - tokenizer.advance(); } } void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol) { - const String uri = get_uri(); r_symbol.uri = uri; r_symbol.script_path = path; r_symbol.children.clear(); - r_symbol.name = p_class->name; - if (r_symbol.name.empty()) + r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String(); + if (r_symbol.name.empty()) { r_symbol.name = path.get_file(); + } r_symbol.kind = lsp::SymbolKind::Class; r_symbol.deprecated = false; - r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->line); - r_symbol.range.start.character = p_class->column; + r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->start_line); + r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_class->start_column); 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; 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) { - - const GDScriptParser::ClassNode::Member &m = p_class->variables[i]; - - lsp::DocumentSymbol symbol; - symbol.name = m.identifier; - symbol.kind = lsp::SymbolKind::Variable; - symbol.deprecated = false; - const int line = LINE_NUMBER_TO_INDEX(m.line); - symbol.range.start.line = line; - symbol.range.start.character = lines[line].length() - lines[line].strip_edges(true, false).length(); - symbol.range.end.line = line; - symbol.range.end.character = lines[line].length(); - symbol.selectionRange.start.line = symbol.range.start.line; - 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(); - } - if (m.default_value.get_type() != Variant::NIL) { - symbol.detail += " = " + JSON::print(m.default_value); - } - - symbol.documentation = parse_documentation(line); - symbol.uri = uri; - symbol.script_path = path; - - r_symbol.children.push_back(symbol); - } - - for (int i = 0; i < p_class->_signals.size(); ++i) { - const GDScriptParser::ClassNode::Signal &signal = p_class->_signals[i]; - - lsp::DocumentSymbol symbol; - symbol.name = signal.name; - symbol.kind = lsp::SymbolKind::Event; - symbol.deprecated = false; - const int line = LINE_NUMBER_TO_INDEX(signal.line); - symbol.range.start.line = line; - symbol.range.start.character = lines[line].length() - lines[line].strip_edges(true, false).length(); - 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.uri = uri; - symbol.script_path = path; - symbol.detail = "signal " + signal.name + "("; - for (int j = 0; j < signal.arguments.size(); j++) { - if (j > 0) { - symbol.detail += ", "; - } - symbol.detail += signal.arguments[j]; - } - symbol.detail += ")"; - - r_symbol.children.push_back(symbol); - } + r_symbol.documentation = parse_documentation(is_root_class ? 0 : LINE_NUMBER_TO_INDEX(p_class->start_line), is_root_class); + + for (int i = 0; i < p_class->members.size(); i++) { + const ClassNode::Member &m = p_class->members[i]; + + switch (m.type) { + case ClassNode::Member::VARIABLE: { + lsp::DocumentSymbol symbol; + symbol.name = m.variable->identifier->name; + symbol.kind = lsp::SymbolKind::Variable; + symbol.deprecated = false; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.variable->end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.variable->end_column); + symbol.selectionRange.start.line = symbol.range.start.line; + if (m.variable->exported) { + symbol.detail += "@export "; + } + symbol.detail += "var " + m.variable->identifier->name; + if (m.get_datatype().is_hard_type()) { + symbol.detail += ": " + m.get_datatype().to_string(); + } + if (m.variable->initializer != nullptr && m.variable->initializer->is_constant) { + symbol.detail += " = " + JSON::print(m.variable->initializer->reduced_value); + } - for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) { - lsp::DocumentSymbol symbol; - const GDScriptParser::ClassNode::Constant &c = E->value(); - const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); - ERR_FAIL_COND(!node); - symbol.name = E->key(); - symbol.kind = lsp::SymbolKind::Constant; - symbol.deprecated = false; - const int line = LINE_NUMBER_TO_INDEX(E->get().expression->line); - symbol.range.start.line = line; - symbol.range.start.character = E->get().expression->column; - 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.uri = uri; - symbol.script_path = path; + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.variable->start_line)); + symbol.uri = uri; + symbol.script_path = path; + + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::CONSTANT: { + lsp::DocumentSymbol symbol; + + symbol.name = m.constant->identifier->name; + symbol.kind = lsp::SymbolKind::Constant; + symbol.deprecated = false; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.constant->start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.constant->start_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.constant->end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.constant->start_column); + symbol.selectionRange.start.line = LINE_NUMBER_TO_INDEX(m.constant->start_line); + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.constant->start_line)); + symbol.uri = uri; + symbol.script_path = path; + + symbol.detail = "const " + symbol.name; + if (m.constant->get_datatype().is_hard_type()) { + symbol.detail += ": " + m.constant->get_datatype().to_string(); + } - symbol.detail = "const " + symbol.name; - if (c.type.kind != GDScriptParser::DataType::UNRESOLVED) { - symbol.detail += ": " + c.type.to_string(); - } + const Variant &default_value = m.constant->initializer->reduced_value; + String value_text; + if (default_value.get_type() == Variant::OBJECT) { + RES res = default_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(default_value); + } + } else { + value_text = JSON::print(default_value); + } + if (!value_text.empty()) { + symbol.detail += " = " + value_text; + } - 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; + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::ENUM_VALUE: { + lsp::DocumentSymbol symbol; + + symbol.name = m.enum_value.identifier->name; + symbol.kind = lsp::SymbolKind::EnumMember; + symbol.deprecated = false; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.enum_value.leftmost_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.enum_value.line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.enum_value.rightmost_column); + symbol.selectionRange.start.line = LINE_NUMBER_TO_INDEX(m.enum_value.line); + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.enum_value.line)); + symbol.uri = uri; + symbol.script_path = path; + + symbol.detail = symbol.name + " = " + itos(m.enum_value.value); + + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::SIGNAL: { + lsp::DocumentSymbol symbol; + symbol.name = m.signal->identifier->name; + symbol.kind = lsp::SymbolKind::Event; + symbol.deprecated = false; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.signal->start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.signal->start_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.signal->end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.signal->end_column); + symbol.selectionRange.start.line = symbol.range.start.line; + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.signal->start_line)); + symbol.uri = uri; + symbol.script_path = path; + symbol.detail = "signal " + String(m.signal->identifier->name) + "("; + for (int j = 0; j < m.signal->parameters.size(); j++) { + if (j > 0) { + symbol.detail += ", "; } + symbol.detail += m.signal->parameters[i]->identifier->name; } - } else { - value_text = JSON::print(node->value); - } - } else { - value_text = JSON::print(node->value); - } - if (!value_text.empty()) { - symbol.detail += " = " + value_text; + symbol.detail += ")"; + + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::ENUM: { + lsp::DocumentSymbol symbol; + symbol.kind = lsp::SymbolKind::Enum; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.m_enum->start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.m_enum->start_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(m.m_enum->end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(m.m_enum->end_column); + symbol.selectionRange.start.line = symbol.range.start.line; + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.m_enum->start_line)); + symbol.uri = uri; + symbol.script_path = path; + + symbol.detail = "enum " + String(m.m_enum->identifier->name) + "{"; + for (int j = 0; j < m.m_enum->values.size(); j++) { + if (j > 0) { + symbol.detail += ", "; + } + symbol.detail += String(m.m_enum->values[j].identifier->name) + " = " + itos(m.m_enum->values[j].value); + } + symbol.detail += "}"; + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::FUNCTION: { + lsp::DocumentSymbol symbol; + parse_function_symbol(m.function, symbol); + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::CLASS: { + lsp::DocumentSymbol symbol; + parse_class_symbol(m.m_class, symbol); + r_symbol.children.push_back(symbol); + } break; + case ClassNode::Member::UNDEFINED: + break; // Unreachable. } - - r_symbol.children.push_back(symbol); - } - - for (int i = 0; i < p_class->functions.size(); ++i) { - const GDScriptParser::FunctionNode *func = p_class->functions[i]; - lsp::DocumentSymbol symbol; - parse_function_symbol(func, symbol); - r_symbol.children.push_back(symbol); - } - - for (int i = 0; i < p_class->static_functions.size(); ++i) { - const GDScriptParser::FunctionNode *func = p_class->static_functions[i]; - lsp::DocumentSymbol symbol; - parse_function_symbol(func, symbol); - r_symbol.children.push_back(symbol); - } - - for (int i = 0; i < p_class->subclasses.size(); ++i) { - const GDScriptParser::ClassNode *subclass = p_class->subclasses[i]; - lsp::DocumentSymbol symbol; - parse_class_symbol(subclass, symbol); - r_symbol.children.push_back(symbol); } } void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol) { - const String uri = get_uri(); - r_symbol.name = p_func->name; + r_symbol.name = p_func->identifier->name; r_symbol.kind = lsp::SymbolKind::Function; - r_symbol.detail = "func " + p_func->name + "("; + r_symbol.detail = "func " + String(p_func->identifier->name) + "("; r_symbol.deprecated = false; - const int line = LINE_NUMBER_TO_INDEX(p_func->line); - r_symbol.range.start.line = line; - r_symbol.range.start.character = p_func->column; - r_symbol.range.end.line = MAX(p_func->body->end_line - 2, r_symbol.range.start.line); - r_symbol.range.end.character = lines[r_symbol.range.end.line].length(); + r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->start_line); + r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_func->start_column); + r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_func->start_line); + r_symbol.range.end.character = LINE_NUMBER_TO_INDEX(p_func->end_column); r_symbol.selectionRange.start.line = r_symbol.range.start.line; - r_symbol.documentation = parse_documentation(line); + r_symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(p_func->start_line)); r_symbol.uri = uri; r_symbol.script_path = path; - String arguments; - for (int i = 0; i < p_func->arguments.size(); i++) { + String parameters; + for (int i = 0; i < p_func->parameters.size(); i++) { + const ParameterNode *parameter = p_func->parameters[i]; lsp::DocumentSymbol symbol; symbol.kind = lsp::SymbolKind::Variable; - symbol.name = p_func->arguments[i]; - symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->body->line); - symbol.range.start.character = p_func->body->column; - symbol.range.end = symbol.range.start; + symbol.name = parameter->identifier->name; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(parameter->start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(parameter->start_line); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(parameter->end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(parameter->end_column); symbol.uri = uri; symbol.script_path = path; r_symbol.children.push_back(symbol); if (i > 0) { - arguments += ", "; + parameters += ", "; } - arguments += String(p_func->arguments[i]); - if (p_func->argument_types[i].kind != GDScriptParser::DataType::UNRESOLVED) { - arguments += ": " + p_func->argument_types[i].to_string(); + parameters += String(parameter->identifier->name); + if (parameter->get_datatype().is_hard_type()) { + parameters += ": " + parameter->get_datatype().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<const GDScriptParser::ConstantNode *>(p_func->default_values[default_value_idx]); - if (const_node == NULL) { - const GDScriptParser::OperatorNode *operator_node = dynamic_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[default_value_idx]); - if (operator_node) { - const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(operator_node->next); - } - } - - if (const_node) { - String value = JSON::print(const_node->value); - arguments += " = " + value; - } + if (parameter->default_value != nullptr) { + String value = JSON::print(parameter->default_value->reduced_value); + parameters += " = " + value; } } - r_symbol.detail += arguments + ")"; - if (p_func->return_type.kind != GDScriptParser::DataType::UNRESOLVED) { - r_symbol.detail += " -> " + p_func->return_type.to_string(); + r_symbol.detail += parameters + ")"; + if (p_func->get_datatype().is_hard_type()) { + r_symbol.detail += " -> " + p_func->get_datatype().to_string(); } - for (const Map<StringName, LocalVarNode *>::Element *E = p_func->body->variables.front(); E; E = E->next()) { + for (int i = 0; i < p_func->body->locals.size(); i++) { + const SuiteNode::Local &local = p_func->body->locals[i]; lsp::DocumentSymbol symbol; - const GDScriptParser::LocalVarNode *var = E->value(); - symbol.name = E->key(); - symbol.kind = lsp::SymbolKind::Variable; - symbol.range.start.line = LINE_NUMBER_TO_INDEX(E->get()->line); - symbol.range.start.character = E->get()->column; - symbol.range.end.line = symbol.range.start.line; - symbol.range.end.character = lines[symbol.range.end.line].length(); + symbol.name = local.name; + symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable; + symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line); + symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column); + symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line); + symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column); symbol.uri = uri; symbol.script_path = path; - symbol.detail = "var " + symbol.name; - if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) { - symbol.detail += ": " + var->datatype.to_string(); + symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var "; + symbol.detail += symbol.name; + if (local.get_datatype().is_hard_type()) { + symbol.detail += ": " + local.get_datatype().to_string(); } - symbol.documentation = parse_documentation(line); + symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line)); r_symbol.children.push_back(symbol); } } @@ -384,8 +401,9 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { 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; + if (i < 0 || i >= lines.size()) { + break; + } String line_comment = lines[i].strip_edges(true, false); if (line_comment.begins_with("#")) { @@ -408,22 +426,20 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) { } String ExtendGDScriptParser::get_text_for_completion(const lsp::Position &p_cursor) const { - String longthing; int len = lines.size(); for (int i = 0; i < len; i++) { - if (i == p_cursor.line) { longthing += lines[i].substr(0, p_cursor.character); longthing += String::chr(0xFFFF); //not unicode, represents the cursor longthing += lines[i].substr(p_cursor.character, lines[i].size()); } else { - longthing += lines[i]; } - if (i != len - 1) + if (i != len - 1) { longthing += "\n"; + } } return longthing; @@ -433,7 +449,6 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c String longthing; int len = lines.size(); for (int i = 0; i < len; i++) { - if (i == p_cursor.line) { String line = lines[i]; String first_part = line.substr(0, p_cursor.character); @@ -457,19 +472,18 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c } longthing += last_part; } else { - longthing += lines[i]; } - if (i != len - 1) + if (i != len - 1) { longthing += "\n"; + } } return longthing; } String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const { - ERR_FAIL_INDEX_V(p_position.line, lines.size(), ""); String line = lines[p_position.line]; ERR_FAIL_INDEX_V(p_position.character, line.size(), ""); @@ -477,7 +491,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int start_pos = p_position.character; for (int c = p_position.character; c >= 0; c--) { start_pos = c; - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -486,7 +500,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position & int end_pos = p_position.character; for (int c = p_position.character; c < line.length(); c++) { - CharType ch = line[c]; + char32_t ch = line[c]; bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'; if (!valid_char) { break; @@ -507,7 +521,7 @@ String ExtendGDScriptParser::get_uri() const { } const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const { - const lsp::DocumentSymbol *ret = NULL; + const lsp::DocumentSymbol *ret = nullptr; if (p_line < p_parent.range.start.line) { return ret; } else if (p_parent.range.start.line == p_line) { @@ -524,7 +538,6 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i } Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const { - ERR_FAIL_INDEX_V(p_position.line, lines.size(), ERR_INVALID_PARAMETER); int bracket_stack = 0; @@ -539,7 +552,7 @@ Error ExtendGDScriptParser::get_left_function_call(const lsp::Position &p_positi } while (c >= 0) { - const CharType &character = line[c]; + const char32_t &character = line[c]; if (character == ')') { ++bracket_stack; } else if (character == '(') { @@ -576,7 +589,6 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int } const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const { - if (p_subclass.empty()) { const lsp::DocumentSymbol *const *ptr = members.getptr(p_name); if (ptr) { @@ -591,7 +603,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String } } - return NULL; + return nullptr; } const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const { @@ -599,12 +611,9 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const } const Array &ExtendGDScriptParser::get_member_completions() { - if (member_completions.empty()) { - - const String *name = members.next(NULL); + const String *name = members.next(nullptr); while (name) { - const lsp::DocumentSymbol *symbol = members.get(*name); lsp::CompletionItem item = symbol->make_completion_item(); item.data = JOIN_SYMBOLS(path, *name); @@ -613,11 +622,10 @@ const Array &ExtendGDScriptParser::get_member_completions() { name = members.next(name); } - const String *_class = inner_classes.next(NULL); + const String *_class = inner_classes.next(nullptr); while (_class) { - const ClassMembers *inner_class = inner_classes.getptr(*_class); - const String *member_name = inner_class->next(NULL); + const String *member_name = inner_class->next(nullptr); while (member_name) { const lsp::DocumentSymbol *symbol = inner_class->get(*member_name); lsp::CompletionItem item = symbol->make_completion_item(); @@ -637,34 +645,24 @@ const Array &ExtendGDScriptParser::get_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["name"] = p_func->identifier->name; + func["return_type"] = p_func->get_datatype().to_string(); func["rpc_mode"] = p_func->rpc_mode; - Array arguments; - for (int i = 0; i < p_func->arguments.size(); i++) { + Array parameters; + for (int i = 0; i < p_func->parameters.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<const GDScriptParser::ConstantNode *>(p_func->default_values[default_value_idx]); - if (const_node == NULL) { - const GDScriptParser::OperatorNode *operator_node = dynamic_cast<const GDScriptParser::OperatorNode *>(p_func->default_values[default_value_idx]); - if (operator_node) { - const_node = dynamic_cast<const GDScriptParser::ConstantNode *>(operator_node->next); - } - } - if (const_node) { - arg["default_value"] = const_node->value; - } + arg["name"] = p_func->parameters[i]->identifier->name; + arg["type"] = p_func->parameters[i]->get_datatype().to_string(); + if (p_func->parameters[i]->default_value != nullptr) { + arg["default_value"] = p_func->parameters[i]->default_value->reduced_value; } - arguments.push_back(arg); + parameters.push_back(arg); } - if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->line))) { + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_func->start_line))) { func["signature"] = symbol->detail; func["description"] = symbol->documentation; } - func["arguments"] = arguments; + func["arguments"] = parameters; return func; } @@ -673,101 +671,125 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode ERR_FAIL_NULL_V(p_class, class_api); - class_api["name"] = String(p_class->name); + class_api["name"] = p_class->identifier != nullptr ? String(p_class->identifier->name) : String(); 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])); + for (int i = 0; i < p_class->extends.size(); i++) { + extends_class.append(String(p_class->extends[i])); } class_api["extends_class"] = extends_class; - class_api["extends_file"] = String(p_class->extends_file); + class_api["extends_file"] = String(p_class->extends_path); 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))) { + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(p_class->start_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 nested_classes; Array constants; - for (Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) { - - const GDScriptParser::ClassNode::Constant &c = E->value(); - const GDScriptParser::ConstantNode *node = dynamic_cast<const GDScriptParser::ConstantNode *>(c.expression); - ERR_FAIL_COND_V(!node, class_api); - - 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; + Array methods; + Array static_functions; + + for (int i = 0; i < p_class->members.size(); i++) { + const ClassNode::Member &m = p_class->members[i]; + switch (m.type) { + case ClassNode::Member::CLASS: + nested_classes.push_back(dump_class_api(m.m_class)); + break; + case ClassNode::Member::CONSTANT: { + Dictionary api; + api["name"] = m.constant->identifier->name; + api["value"] = m.constant->initializer->reduced_value; + api["data_type"] = m.constant->get_datatype().to_string(); + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.constant->start_line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + constants.push_back(api); + } break; + case ClassNode::Member::ENUM_VALUE: { + Dictionary api; + api["name"] = m.enum_value.identifier->name; + api["value"] = m.enum_value.value; + api["data_type"] = m.get_datatype().to_string(); + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.enum_value.line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + constants.push_back(api); + } break; + case ClassNode::Member::ENUM: { + Dictionary enum_dict; + for (int j = 0; j < m.m_enum->values.size(); i++) { + enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value; + } + + Dictionary api; + api["name"] = m.m_enum->identifier->name; + api["value"] = enum_dict; + api["data_type"] = m.get_datatype().to_string(); + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.m_enum->start_line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + constants.push_back(api); + } break; + case ClassNode::Member::VARIABLE: { + Dictionary api; + api["name"] = m.variable->identifier->name; + api["data_type"] = m.variable->get_datatype().to_string(); + api["default_value"] = m.variable->initializer != nullptr ? m.variable->initializer->reduced_value : Variant(); + api["setter"] = m.variable->setter ? ("@" + String(m.variable->identifier->name) + "_setter") : (m.variable->setter_pointer != nullptr ? String(m.variable->setter_pointer->name) : String()); + api["getter"] = m.variable->getter ? ("@" + String(m.variable->identifier->name) + "_getter") : (m.variable->getter_pointer != nullptr ? String(m.variable->getter_pointer->name) : String()); + api["export"] = m.variable->exported; + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.variable->start_line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + members.push_back(api); + } break; + case ClassNode::Member::SIGNAL: { + Dictionary api; + api["name"] = m.signal->identifier->name; + Array pars; + for (int j = 0; j < m.signal->parameters.size(); j++) { + pars.append(String(m.signal->parameters[i]->identifier->name)); + } + api["arguments"] = pars; + if (const lsp::DocumentSymbol *symbol = get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.signal->start_line))) { + api["signature"] = symbol->detail; + api["description"] = symbol->documentation; + } + signals.push_back(api); + } break; + case ClassNode::Member::FUNCTION: { + if (m.function->is_static) { + static_functions.append(dump_function_api(m.function)); + } else { + methods.append(dump_function_api(m.function)); + } + } break; + case ClassNode::Member::UNDEFINED: + break; // Unreachable. } - 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["sub_classes"] = nested_classes; + class_api["constants"] = constants; + class_api["members"] = members; + class_api["signals"] = signals; 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->static_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(); + const GDScriptParser::Node *head = get_tree(); if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) { api = dump_class_api(gdclass); } @@ -778,7 +800,11 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) { path = p_path; lines = p_code.split("\n"); - Error err = GDScriptParser::parse(p_code, p_path.get_base_dir(), false, p_path, false, NULL, false); + Error err = GDScriptParser::parse(p_code, p_path, false); + if (err == OK) { + GDScriptAnalyzer analyzer(this); + err = analyzer.analyze(); + } update_diagnostics(); update_symbols(); update_document_links(p_code); diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h index 43dfce994b..0c031d7883 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.h +++ b/modules/gdscript/language_server/gdscript_extend_parser.h @@ -50,7 +50,6 @@ typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers; class ExtendGDScriptParser : public GDScriptParser { - String path; Vector<String> lines; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 7133c6b4be..2a67d2ff4f 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -29,35 +29,113 @@ /*************************************************************************/ #include "gdscript_language_protocol.h" + #include "core/io/json.h" #include "core/os/copymem.h" #include "core/project_settings.h" +#include "editor/editor_log.h" #include "editor/editor_node.h" -GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = NULL; - -void GDScriptLanguageProtocol::on_data_received(int p_id) { - lastest_client_id = p_id; - Ref<WebSocketPeer> peer = server->get_peer(p_id); - PoolByteArray data; - if (OK == peer->get_packet_buffer(data)) { - String message; - message.parse_utf8((const char *)data.read().ptr(), data.size()); - if (message.begins_with("Content-Length:")) return; - String output = process_message(message); +GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = nullptr; + +Error GDScriptLanguageProtocol::LSPeer::handle_data() { + int read = 0; + // Read headers + if (!has_header) { + while (true) { + if (req_pos >= LSP_MAX_BUFFER_SIZE) { + req_pos = 0; + ERR_FAIL_COND_V_MSG(true, ERR_OUT_OF_MEMORY, "Response header too big"); + } + Error err = connection->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) { + return FAILED; + } else if (read != 1) { // Busy, wait until next poll + return ERR_BUSY; + } + char *r = (char *)req_buf; + int l = req_pos; + + // End of headers + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + r[l - 3] = '\0'; // Null terminate to read string + String header; + header.parse_utf8(r); + content_length = header.substr(16).to_int(); + has_header = true; + req_pos = 0; + break; + } + req_pos++; + } + } + if (has_header) { + while (req_pos < content_length) { + if (req_pos >= LSP_MAX_BUFFER_SIZE) { + req_pos = 0; + has_header = false; + ERR_FAIL_COND_V_MSG(req_pos >= LSP_MAX_BUFFER_SIZE, ERR_OUT_OF_MEMORY, "Response content too big"); + } + Error err = connection->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) { + return FAILED; + } else if (read != 1) { + return ERR_BUSY; + } + req_pos++; + } + + // Parse data + String msg; + msg.parse_utf8((const char *)req_buf, req_pos); + + // Reset to read again + req_pos = 0; + has_header = false; + + // Response + String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg); if (!output.empty()) { - CharString charstr = output.utf8(); - peer->put_packet((const uint8_t *)charstr.ptr(), charstr.length()); + res_queue.push_back(output.utf8()); + } + } + return OK; +} + +Error GDScriptLanguageProtocol::LSPeer::send_data() { + int sent = 0; + if (!res_queue.empty()) { + CharString c_res = res_queue[0]; + if (res_sent < c_res.size()) { + Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent); + if (err != OK) { + return err; + } + res_sent += sent; + } + // Response sent + if (res_sent >= c_res.size() - 1) { + res_sent = 0; + res_queue.remove(0); } } + return OK; } -void GDScriptLanguageProtocol::on_client_connected(int p_id, const String &p_protocal) { - clients.set(p_id, server->get_peer(p_id)); +Error GDScriptLanguageProtocol::on_client_connected() { + Ref<StreamPeerTCP> tcp_peer = server->take_connection(); + ERR_FAIL_COND_V_MSG(clients.size() >= LSP_MAX_CLIENTS, FAILED, "Max client limits reached"); + Ref<LSPeer> peer = memnew(LSPeer); + peer->connection = tcp_peer; + clients.set(next_client_id, peer); + next_client_id++; + EditorNode::get_log()->add_message("Connection Taken", EditorLog::MSG_TYPE_EDITOR); + return OK; } -void GDScriptLanguageProtocol::on_client_disconnected(int p_id, bool p_was_clean_close) { - clients.erase(p_id); +void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) { + clients.erase(p_client_id); + EditorNode::get_log()->add_message("Disconnected", EditorLog::MSG_TYPE_EDITOR); } String GDScriptLanguageProtocol::process_message(const String &p_text) { @@ -70,7 +148,6 @@ String GDScriptLanguageProtocol::process_message(const String &p_text) { } String GDScriptLanguageProtocol::format_output(const String &p_text) { - String header = "Content-Length: "; CharString charstr = p_text.utf8(); size_t len = charstr.length(); @@ -83,11 +160,9 @@ String GDScriptLanguageProtocol::format_output(const String &p_text) { void GDScriptLanguageProtocol::_bind_methods() { ClassDB::bind_method(D_METHOD("initialize", "params"), &GDScriptLanguageProtocol::initialize); ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized); - 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("notify_client", "method", "params"), &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); @@ -95,7 +170,6 @@ void GDScriptLanguageProtocol::_bind_methods() { } Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { - lsp::InitializeResult ret; String root_uri = p_params["rootUri"]; @@ -110,17 +184,19 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { if (root_uri.length() && is_same_workspace) { workspace->root_uri = root_uri; } else { - workspace->root_uri = "file://" + workspace->root; Dictionary params; params["path"] = workspace->root; - Dictionary request = make_notification("gdscrip_client/changeWorkspace", params); - if (Ref<WebSocketPeer> *peer = clients.getptr(lastest_client_id)) { + Dictionary request = make_notification("gdscript_client/changeWorkspace", params); + + ERR_FAIL_COND_V_MSG(!clients.has(latest_client_id), ret.to_json(), + vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id)); + Ref<LSPeer> peer = clients.get(latest_client_id); + if (peer != nullptr) { String msg = JSON::print(request); msg = format_output(msg); - CharString charstr = msg.utf8(); - (*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length()); + (*peer)->res_queue.push_back(msg.utf8()); } } @@ -134,12 +210,10 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { } void GDScriptLanguageProtocol::initialized(const Variant &p_params) { - lsp::GodotCapabilities capabilities; DocData *doc = EditorHelp::get_doc_data(); for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { - lsp::GodotNativeClassInfo gdclass; gdclass.name = E->get().name; gdclass.class_doc = &(E->get()); @@ -153,61 +227,62 @@ void GDScriptLanguageProtocol::initialized(const Variant &p_params) { } void GDScriptLanguageProtocol::poll() { - server->poll(); + if (server->is_connection_available()) { + on_client_connected(); + } + const int *id = nullptr; + while ((id = clients.next(id))) { + Ref<LSPeer> peer = clients.get(*id); + StreamPeerTCP::Status status = peer->connection->get_status(); + if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) { + on_client_disconnected(*id); + id = nullptr; + } else { + if (peer->connection->get_available_bytes() > 0) { + latest_client_id = *id; + Error err = peer->handle_data(); + if (err != OK && err != ERR_BUSY) { + on_client_disconnected(*id); + id = nullptr; + } + } + Error err = peer->send_data(); + if (err != OK && err != ERR_BUSY) { + on_client_disconnected(*id); + id = nullptr; + } + } + } } Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) { - if (server == NULL) { - server = dynamic_cast<WebSocketServer *>(ClassDB::instance("WebSocketServer")); - ERR_FAIL_COND_V(!server, FAILED); - server->set_buffers(8192, 1024, 8192, 1024); // 8mb should be way more than enough - server->connect("data_received", this, "on_data_received"); - server->connect("client_connected", this, "on_client_connected"); - server->connect("client_disconnected", this, "on_client_disconnected"); - } - server->set_bind_ip(p_bind_ip); - return server->listen(p_port); + return server->listen(p_port, p_bind_ip); } void GDScriptLanguageProtocol::stop() { - const int *ptr = clients.next(NULL); - while (ptr) { - clients.get(*ptr)->close(); - ptr = clients.next(ptr); + const int *id = nullptr; + while ((id = clients.next(id))) { + Ref<LSPeer> peer = clients.get(*id); + peer->connection->disconnect_from_host(); } - server->stop(); - clients.clear(); -} - -void GDScriptLanguageProtocol::notify_all_clients(const String &p_method, const Variant &p_params) { - Dictionary message = make_notification(p_method, p_params); - String msg = JSON::print(message); - msg = format_output(msg); - CharString charstr = msg.utf8(); - const int *p_id = clients.next(NULL); - while (p_id != NULL) { - Ref<WebSocketPeer> peer = clients.get(*p_id); - (*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length()); - p_id = clients.next(p_id); - } + server->stop(); } -void GDScriptLanguageProtocol::notify_client(const String &p_method, const Variant &p_params, int p_client) { - - if (p_client == -1) { - p_client = lastest_client_id; +void GDScriptLanguageProtocol::notify_client(const String &p_method, const Variant &p_params, int p_client_id) { + if (p_client_id == -1) { + ERR_FAIL_COND_MSG(latest_client_id == -1, + "GDScript LSP: Can't notify client as none was connected."); + p_client_id = latest_client_id; } - - Ref<WebSocketPeer> *peer = clients.getptr(p_client); - ERR_FAIL_COND(peer == NULL); + ERR_FAIL_COND(!clients.has(p_client_id)); + Ref<LSPeer> peer = clients.get(p_client_id); + ERR_FAIL_COND(peer == nullptr); Dictionary message = make_notification(p_method, p_params); String msg = JSON::print(message); msg = format_output(msg); - CharString charstr = msg.utf8(); - - (*peer)->put_packet((const uint8_t *)charstr.ptr(), charstr.length()); + peer->res_queue.push_back(msg.utf8()); } bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const { @@ -219,9 +294,8 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const { } GDScriptLanguageProtocol::GDScriptLanguageProtocol() { - server = NULL; + server.instance(); singleton = this; - _initialized = false; workspace.instance(); text_document.instance(); set_scope("textDocument", text_document.ptr()); @@ -229,8 +303,3 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() { set_scope("workspace", workspace.ptr()); workspace->root = ProjectSettings::get_singleton()->get_resource_path(); } - -GDScriptLanguageProtocol::~GDScriptLanguageProtocol() { - memdelete(server); - server = NULL; -} diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index 52c680ab19..cf5242e8c5 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -31,16 +31,36 @@ #ifndef GDSCRIPT_PROTOCAL_SERVER_H #define GDSCRIPT_PROTOCAL_SERVER_H +#include "core/io/stream_peer.h" +#include "core/io/stream_peer_tcp.h" +#include "core/io/tcp_server.h" #include "gdscript_text_document.h" #include "gdscript_workspace.h" #include "lsp.hpp" #include "modules/jsonrpc/jsonrpc.h" -#include "modules/websocket/websocket_peer.h" -#include "modules/websocket/websocket_server.h" + +#define LSP_MAX_BUFFER_SIZE 4194304 +#define LSP_MAX_CLIENTS 8 class GDScriptLanguageProtocol : public JSONRPC { GDCLASS(GDScriptLanguageProtocol, JSONRPC) +private: + struct LSPeer : Reference { + Ref<StreamPeerTCP> connection; + + uint8_t req_buf[LSP_MAX_BUFFER_SIZE]; + int req_pos = 0; + bool has_header = false; + bool has_content = false; + int content_length = 0; + Vector<CharString> res_queue; + int res_sent = 0; + + Error handle_data(); + Error send_data(); + }; + enum LSPErrorCode { RequestCancelled = -32800, ContentModified = -32801, @@ -48,21 +68,21 @@ class GDScriptLanguageProtocol : public JSONRPC { static GDScriptLanguageProtocol *singleton; - HashMap<int, Ref<WebSocketPeer> > clients; - WebSocketServer *server; - int lastest_client_id; + HashMap<int, Ref<LSPeer>> clients; + Ref<TCP_Server> server; + int latest_client_id = 0; + int next_client_id = 0; Ref<GDScriptTextDocument> text_document; Ref<GDScriptWorkspace> workspace; - void on_data_received(int p_id); - void on_client_connected(int p_id, const String &p_protocal); - void on_client_disconnected(int p_id, bool p_was_clean_close); + Error on_client_connected(); + void on_client_disconnected(const int &p_client_id); String process_message(const String &p_text); String format_output(const String &p_text); - bool _initialized; + bool _initialized = false; protected: static void _bind_methods(); @@ -80,14 +100,12 @@ public: Error start(int p_port, const IP_Address &p_bind_ip); void stop(); - void notify_all_clients(const String &p_method, const Variant &p_params = Variant()); - void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client = -1); + void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1); bool is_smart_resolve_enabled() const; bool is_goto_native_symbols_enabled() const; GDScriptLanguageProtocol(); - ~GDScriptLanguageProtocol(); }; #endif diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp index 7170c63058..3387d262f8 100644 --- a/modules/gdscript/language_server/gdscript_language_server.cpp +++ b/modules/gdscript/language_server/gdscript_language_server.cpp @@ -29,13 +29,14 @@ /*************************************************************************/ #include "gdscript_language_server.h" + #include "core/os/file_access.h" #include "core/os/os.h" #include "editor/editor_log.h" #include "editor/editor_node.h" GDScriptLanguageServer::GDScriptLanguageServer() { - thread = NULL; + thread = nullptr; thread_running = false; started = false; @@ -48,7 +49,6 @@ GDScriptLanguageServer::GDScriptLanguageServer() { } void GDScriptLanguageServer::_notification(int p_what) { - switch (p_what) { case NOTIFICATION_ENTER_TREE: start(); @@ -87,7 +87,7 @@ void GDScriptLanguageServer::start() { if (protocol.start(port, IP_Address("127.0.0.1")) == OK) { EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR); if (use_thread) { - ERR_FAIL_COND(thread != NULL); + ERR_FAIL_COND(thread != nullptr); thread_running = true; thread = Thread::create(GDScriptLanguageServer::thread_main, this); } @@ -98,11 +98,11 @@ void GDScriptLanguageServer::start() { void GDScriptLanguageServer::stop() { if (use_thread) { - ERR_FAIL_COND(NULL == thread); + ERR_FAIL_COND(nullptr == thread); thread_running = false; Thread::wait_to_finish(thread); memdelete(thread); - thread = NULL; + thread = nullptr; } protocol.stop(); started = false; diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 0572c5f746..c6fe3169dc 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -29,12 +29,14 @@ /*************************************************************************/ #include "gdscript_text_document.h" + #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" +#include "servers/display_server.h" void GDScriptTextDocument::_bind_methods() { ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen); @@ -84,19 +86,15 @@ void GDScriptTextDocument::notify_client_show_symbol(const lsp::DocumentSymbol * } void GDScriptTextDocument::initialize() { - if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { - const HashMap<StringName, ClassMembers> &native_members = GDScriptLanguageProtocol::get_singleton()->get_workspace()->native_members; - const StringName *class_ptr = native_members.next(NULL); + const StringName *class_ptr = native_members.next(nullptr); while (class_ptr) { - const ClassMembers &members = native_members.get(*class_ptr); - const String *name = members.next(NULL); + const String *name = members.next(nullptr); while (name) { - const lsp::DocumentSymbol *symbol = members.get(*name); lsp::CompletionItem item = symbol->make_completion_item(); item.data = JOIN_SYMBOLS(String(*class_ptr), *name); @@ -111,7 +109,6 @@ void GDScriptTextDocument::initialize() { } Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) { - Variant ret; lsp::NativeSymbolInspectParams params; @@ -141,7 +138,6 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) { } Array GDScriptTextDocument::completion(const Dictionary &p_params) { - Array arr; lsp::CompletionParams params; @@ -152,12 +148,10 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options); if (!options.empty()) { - int i = 0; arr.resize(options.size()); for (const List<ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) { - const ScriptCodeCompletionOption &option = E->get(); lsp::CompletionItem item; item.label = option.display; @@ -200,11 +194,9 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { i++; } } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { - arr = native_member_completions.duplicate(); for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) { - ExtendGDScriptParser *script = E->get(); const Array &items = script->get_member_completions(); @@ -219,28 +211,24 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) { } Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { - lsp::CompletionItem item; item.load(p_params); lsp::CompletionParams params; Variant data = p_params["data"]; - const lsp::DocumentSymbol *symbol = NULL; + const lsp::DocumentSymbol *symbol = nullptr; 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); } else if (data.get_type() == Variant::STRING) { - String query = data; 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 = param_symbols[param_symbols.size() - 1]; @@ -312,19 +300,18 @@ Array GDScriptTextDocument::colorPresentation(const Dictionary &p_params) { } 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); if (symbol) { - lsp::Hover hover; hover.contents = symbol->render(); + hover.range.start = params.position; + hover.range.end = params.position; return hover.to_json(); } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) { - Dictionary ret; Array contents; List<const lsp::DocumentSymbol *> list; @@ -417,7 +404,8 @@ void GDScriptTextDocument::sync_script_content(const String &p_path, const Strin 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(); + + DisplayServer::get_singleton()->window_move_to_foreground(); } Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &p_location, List<const lsp::DocumentSymbol *> &r_list) { diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 1c0590cff1..776193e37c 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -29,28 +29,32 @@ /*************************************************************************/ #include "gdscript_workspace.h" + #include "../gdscript.h" #include "../gdscript_parser.h" #include "core/project_settings.h" #include "core/script_language.h" +#include "editor/editor_file_system.h" #include "editor/editor_help.h" +#include "editor/editor_node.h" #include "gdscript_language_protocol.h" +#include "scene/resources/packed_scene.h" 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); + ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script); + ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script); + ClassDB::bind_method(D_METHOD("get_file_path", "uri"), &GDScriptWorkspace::get_file_path); + ClassDB::bind_method(D_METHOD("get_file_uri", "path"), &GDScriptWorkspace::get_file_uri); + ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics); + ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api); } void GDScriptWorkspace::remove_cache_parser(const String &p_path) { Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path); Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path); if (parser && script) { - if (script->get() && script->get() == script->get()) { + if (script->get() && script->get() == parser->get()) { memdelete(script->get()); } else { memdelete(script->get()); @@ -68,7 +72,6 @@ void GDScriptWorkspace::remove_cache_parser(const String &p_path) { } const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_class, const String &p_member) const { - StringName class_name = p_class; StringName empty; @@ -90,7 +93,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_ class_name = ClassDB::get_parent_class(class_name); } - return NULL; + return nullptr; } const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const { @@ -98,7 +101,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_ if (S) { return &(S->get()->get_symbols()); } - return NULL; + return nullptr; } void GDScriptWorkspace::reload_all_workspace_scripts() { @@ -115,7 +118,7 @@ void GDScriptWorkspace::reload_all_workspace_scripts() { Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(path); String err_msg = "Failed parse script " + path; if (S) { - err_msg += "\n" + S->get()->get_error(); + err_msg += "\n" + S->get()->get_errors()[0].message; } ERR_CONTINUE_MSG(err != OK, err_msg); } @@ -149,7 +152,7 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String if (S) { return S->get(); } - return NULL; + return nullptr; } ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) { @@ -161,7 +164,7 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) if (S) { return S->get(); } - return NULL; + return nullptr; } Array GDScriptWorkspace::symbol(const Dictionary &p_params) { @@ -182,11 +185,12 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) { } Error GDScriptWorkspace::initialize() { - if (initialized) return OK; + if (initialized) { + return OK; + } DocData *doc = EditorHelp::get_doc_data(); for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) { - const DocData::ClassDoc &class_data = E->value(); lsp::DocumentSymbol class_symbol; String class_name = E->key(); @@ -311,14 +315,12 @@ Error GDScriptWorkspace::initialize() { } Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) { - ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser); Error err = parser->parse(p_content, p_path); Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path); Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path); if (err == OK) { - remove_cache_parser(p_path); parse_results[p_path] = parser; scripts[p_path] = parser; @@ -373,25 +375,71 @@ void GDScriptWorkspace::publish_diagnostics(const String &p_path) { GDScriptLanguageProtocol::get_singleton()->notify_client("textDocument/publishDiagnostics", params); } -void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) { +void GDScriptWorkspace::_get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners) { + if (!efsd) { + return; + } + for (int i = 0; i < efsd->get_subdir_count(); i++) { + _get_owners(efsd->get_subdir(i), p_path, owners); + } + + for (int i = 0; i < efsd->get_file_count(); i++) { + Vector<String> deps = efsd->get_file_deps(i); + bool found = false; + for (int j = 0; j < deps.size(); j++) { + if (deps[j] == p_path) { + found = true; + break; + } + } + if (!found) { + continue; + } + + owners.push_back(efsd->get_file_path(i)); + } +} + +Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) { + Node *owner_scene_node = nullptr; + List<String> owners; + + _get_owners(EditorFileSystem::get_singleton()->get_filesystem(), p_path, owners); + + for (int i = 0; i < owners.size(); i++) { + NodePath owner_path = owners[i]; + RES owner_res = ResourceLoader::load(owner_path); + if (Object::cast_to<PackedScene>(owner_res.ptr())) { + Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res)); + owner_scene_node = owner_packed_scene->instance(); + break; + } + } + + return owner_scene_node; +} + +void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options) { String path = get_file_path(p_params.textDocument.uri); String call_hint; bool forced = false; if (const ExtendGDScriptParser *parser = get_parse_result(path)) { + Node *owner_scene_node = _get_owner_scene_node(path); String code = parser->get_text_for_completion(p_params.position); - GDScriptLanguage::get_singleton()->complete_code(code, path, NULL, r_options, forced, call_hint); + GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint); + if (owner_scene_node) { + memdelete(owner_scene_node); + } } } const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_requred) { - - const lsp::DocumentSymbol *symbol = NULL; + const lsp::DocumentSymbol *symbol = nullptr; String path = get_file_path(p_doc_pos.textDocument.uri); if (const ExtendGDScriptParser *parser = get_parse_result(path)) { - String symbol_identifier = p_symbol_name; Vector<String> identifier_parts = symbol_identifier.split("("); if (identifier_parts.size()) { @@ -406,19 +454,14 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } if (!symbol_identifier.empty()) { - if (ScriptServer::is_global_class(symbol_identifier)) { - String class_path = ScriptServer::get_global_class_path(symbol_identifier); symbol = get_script_symbol(class_path); } else { - ScriptLanguage::LookupResult ret; - if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_requred), symbol_identifier, path, NULL, ret)) { - + if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_requred), symbol_identifier, path, nullptr, ret)) { if (ret.type == ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION) { - String target_script_path = path; if (!ret.script.is_null()) { target_script_path = ret.script->get_path(); @@ -429,7 +472,6 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } } else { - String member = ret.class_member; if (member.empty() && symbol_identifier != ret.class_name) { member = symbol_identifier; @@ -447,15 +489,13 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu } void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list) { - String path = get_file_path(p_doc_pos.textDocument.uri); if (const ExtendGDScriptParser *parser = get_parse_result(path)) { - String symbol_identifier; Vector2i offset; symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset); - const StringName *class_ptr = native_members.next(NULL); + const StringName *class_ptr = native_members.next(nullptr); while (class_ptr) { const ClassMembers &members = native_members.get(*class_ptr); if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) { @@ -472,9 +512,8 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP } const HashMap<String, ClassMembers> &inner_classes = script->get_inner_classes(); - const String *_class = inner_classes.next(NULL); + const String *_class = inner_classes.next(nullptr); 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); @@ -487,7 +526,6 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP } const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) { - if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) { const lsp::DocumentSymbol &symbol = E->get(); if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) { @@ -501,7 +539,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::N } } - return NULL; + return nullptr; } void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) { @@ -523,12 +561,10 @@ Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) { Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature) { if (const ExtendGDScriptParser *parser = get_parse_result(get_file_path(p_doc_pos.textDocument.uri))) { - lsp::TextDocumentPositionParams text_pos; text_pos.textDocument = p_doc_pos.textDocument; if (parser->get_left_function_call(p_doc_pos.position, text_pos.position, r_signature.activeParameter) == OK) { - List<const lsp::DocumentSymbol *> symbols; if (const lsp::DocumentSymbol *symbol = resolve_symbol(text_pos)) { @@ -540,7 +576,6 @@ Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams for (List<const lsp::DocumentSymbol *>::Element *E = symbols.front(); E; E = E->next()) { const lsp::DocumentSymbol *symbol = E->get(); if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) { - lsp::SignatureInformation signature_info; signature_info.label = symbol->detail; signature_info.documentation = symbol->render(); diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 146a5cb7c9..e45b06747d 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -33,12 +33,17 @@ #include "../gdscript_parser.h" #include "core/variant.h" +#include "editor/editor_file_system.h" #include "gdscript_extend_parser.h" #include "lsp.hpp" class GDScriptWorkspace : public Reference { GDCLASS(GDScriptWorkspace, Reference); +private: + void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners); + Node *_get_owner_scene_node(String p_path); + protected: static void _bind_methods(); void remove_cache_parser(const String &p_path); diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index a2dcc48820..cf27a1578c 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -33,7 +33,7 @@ #include "core/class_db.h" #include "core/list.h" -#include "editor/doc/doc_data.h" +#include "editor/doc_data.h" namespace lsp { @@ -149,14 +149,13 @@ struct Location { * Represents a link between a source and a target location. */ struct LocationLink { - /** * Span of the origin of this link. * * Used as the underlined span for mouse interaction. Defaults to the word range at * the mouse position. */ - Range *originSelectionRange = NULL; + Range *originSelectionRange = nullptr; /** * The target resource identifier of this link. @@ -220,7 +219,6 @@ struct DocumentLinkParams { * text document or a web site. */ struct DocumentLink { - /** * The range this link applies to. */ @@ -282,7 +280,9 @@ struct Command { Dictionary dict; dict["title"] = title; dict["command"] = command; - if (arguments.size()) dict["arguments"] = arguments; + if (arguments.size()) { + dict["arguments"] = arguments; + } return dict; } }; @@ -486,7 +486,7 @@ struct TextDocumentSyncOptions { * If present save notifications are sent to the server. If omitted the notification should not be * sent. */ - SaveOptions save; + bool save = false; Dictionary to_json() { Dictionary dict; @@ -494,7 +494,7 @@ struct TextDocumentSyncOptions { dict["willSave"] = willSave; dict["openClose"] = openClose; dict["change"] = change; - dict["save"] = save.to_json(); + dict["save"] = save; return dict; } }; @@ -946,16 +946,24 @@ struct CompletionItem { dict["preselect"] = preselect; dict["sortText"] = sortText; dict["filterText"] = filterText; - if (commitCharacters.size()) dict["commitCharacters"] = commitCharacters; + if (commitCharacters.size()) { + dict["commitCharacters"] = commitCharacters; + } dict["command"] = command.to_json(); } return dict; } void load(const Dictionary &p_dict) { - if (p_dict.has("label")) label = p_dict["label"]; - if (p_dict.has("kind")) kind = p_dict["kind"]; - if (p_dict.has("detail")) detail = p_dict["detail"]; + if (p_dict.has("label")) { + label = p_dict["label"]; + } + if (p_dict.has("kind")) { + kind = p_dict["kind"]; + } + if (p_dict.has("detail")) { + detail = p_dict["detail"]; + } if (p_dict.has("documentation")) { Variant doc = p_dict["documentation"]; if (doc.get_type() == Variant::STRING) { @@ -965,12 +973,24 @@ struct CompletionItem { documentation.value = v["value"]; } } - if (p_dict.has("deprecated")) deprecated = p_dict["deprecated"]; - if (p_dict.has("preselect")) preselect = p_dict["preselect"]; - if (p_dict.has("sortText")) sortText = p_dict["sortText"]; - if (p_dict.has("filterText")) filterText = p_dict["filterText"]; - if (p_dict.has("insertText")) insertText = p_dict["insertText"]; - if (p_dict.has("data")) data = p_dict["data"]; + if (p_dict.has("deprecated")) { + deprecated = p_dict["deprecated"]; + } + if (p_dict.has("preselect")) { + preselect = p_dict["preselect"]; + } + if (p_dict.has("sortText")) { + sortText = p_dict["sortText"]; + } + if (p_dict.has("filterText")) { + filterText = p_dict["filterText"]; + } + if (p_dict.has("insertText")) { + insertText = p_dict["insertText"]; + } + if (p_dict.has("data")) { + data = p_dict["data"]; + } } }; @@ -1096,7 +1116,6 @@ struct DocumentedSymbolInformation : public SymbolInformation { * e.g. the range of an identifier. */ struct DocumentSymbol { - /** * The name of this symbol. Will be displayed in the user interface and therefore must not be * an empty string or a string only consisting of white spaces. @@ -1205,7 +1224,6 @@ struct DocumentSymbol { } _FORCE_INLINE_ CompletionItem make_completion_item(bool resolved = false) const { - lsp::CompletionItem item; item.label = name; @@ -1249,7 +1267,6 @@ struct DocumentSymbol { }; struct NativeSymbolInspectParams { - String native_class; String symbol_name; @@ -1281,7 +1298,6 @@ static const String Region = "region"; * Represents a folding range. */ struct FoldingRange { - /** * The zero-based line number from where the folded range starts. */ @@ -1364,7 +1380,6 @@ struct CompletionContext { }; struct CompletionParams : public TextDocumentPositionParams { - /** * The completion context. This is only available if the client specifies * to send this using `ClientCapabilities.textDocument.completion.contextSupport === true` @@ -1405,7 +1420,6 @@ struct Hover { * have a label and a doc-comment. */ struct ParameterInformation { - /** * The label of this parameter information. * @@ -1642,7 +1656,7 @@ struct ServerCapabilities { _FORCE_INLINE_ Dictionary to_json() { Dictionary dict; - dict["textDocumentSync"] = (int)textDocumentSync.change; + dict["textDocumentSync"] = textDocumentSync.to_json(); dict["completionProvider"] = completionProvider.to_json(); signatureHelpProvider.triggerCharacters.push_back(","); signatureHelpProvider.triggerCharacters.push_back("("); @@ -1684,10 +1698,9 @@ struct InitializeResult { }; struct GodotNativeClassInfo { - String name; - const DocData::ClassDoc *class_doc = NULL; - const ClassDB::ClassInfo *class_info = NULL; + const DocData::ClassDoc *class_doc = nullptr; + const ClassDB::ClassInfo *class_info = nullptr; Dictionary to_json() { Dictionary dict; @@ -1699,7 +1712,6 @@ struct GodotNativeClassInfo { /** Features not included in the standard lsp specifications */ struct GodotCapabilities { - /** * Native class list */ @@ -1718,7 +1730,6 @@ struct GodotCapabilities { /** Format BBCode documentation from DocData to markdown */ static String marked_documentation(const String &p_bbcode) { - String markdown = p_bbcode.strip_edges(); Vector<String> lines = markdown.split("\n"); |