summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/language_server
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/language_server')
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp718
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h7
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp246
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h48
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp25
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h14
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp43
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h12
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp161
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h16
-rw-r--r--modules/gdscript/language_server/lsp.hpp216
11 files changed, 912 insertions, 594 deletions
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 0f6f13944b..f817964a3c 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,25 +29,28 @@
/*************************************************************************/
#include "gdscript_extend_parser.h"
+
#include "../gdscript.h"
-#include "core/io/json.h"
+#include "../gdscript_analyzer.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());
- const String &line_text = get_lines()[line];
+ const PackedStringArray lines = get_lines();
+ int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1);
+ const String &line_text = lines[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
@@ -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,240 +129,304 @@ 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.is_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 += " = " + m.variable->initializer->reduced_value.to_json_string();
+ }
- 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().is_empty()) {
+ value_text = "preload(\"" + res->get_path() + "\")";
+ if (symbol.documentation.is_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 = default_value.to_json_string();
+ }
+ } else {
+ value_text = default_value.to_json_string();
+ }
+ if (!value_text.is_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) {
+ parameters += " = " + parameter->default_value->reduced_value.to_json_string();
}
}
- 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()) {
- 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.uri = uri;
- symbol.script_path = path;
- symbol.detail = "var " + symbol.name;
- if (var->datatype.kind != GDScriptParser::DataType::UNRESOLVED) {
- symbol.detail += ": " + var->datatype.to_string();
+ List<GDScriptParser::SuiteNode *> function_nodes;
+
+ List<GDScriptParser::Node *> node_stack;
+ node_stack.push_back(p_func->body);
+
+ while (!node_stack.is_empty()) {
+ GDScriptParser::Node *node = node_stack[0];
+ node_stack.pop_front();
+
+ switch (node->type) {
+ case GDScriptParser::TypeNode::IF: {
+ GDScriptParser::IfNode *if_node = (GDScriptParser::IfNode *)node;
+ node_stack.push_back(if_node->true_block);
+ if (if_node->false_block) {
+ node_stack.push_back(if_node->false_block);
+ }
+ } break;
+
+ case GDScriptParser::TypeNode::FOR: {
+ GDScriptParser::ForNode *for_node = (GDScriptParser::ForNode *)node;
+ node_stack.push_back(for_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::WHILE: {
+ GDScriptParser::WhileNode *while_node = (GDScriptParser::WhileNode *)node;
+ node_stack.push_back(while_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::MATCH_BRANCH: {
+ GDScriptParser::MatchBranchNode *match_node = (GDScriptParser::MatchBranchNode *)node;
+ node_stack.push_back(match_node->block);
+ } break;
+
+ case GDScriptParser::TypeNode::SUITE: {
+ GDScriptParser::SuiteNode *suite_node = (GDScriptParser::SuiteNode *)node;
+ function_nodes.push_back(suite_node);
+ for (int i = 0; i < suite_node->statements.size(); ++i) {
+ node_stack.push_back(suite_node->statements[i]);
+ }
+ } break;
+
+ default:
+ continue;
+ }
+ }
+
+ for (List<GDScriptParser::SuiteNode *>::Element *N = function_nodes.front(); N; N = N->next()) {
+ const GDScriptParser::SuiteNode *suite_node = N->get();
+ for (int i = 0; i < suite_node->locals.size(); i++) {
+ const SuiteNode::Local &local = suite_node->locals[i];
+ lsp::DocumentSymbol symbol;
+ 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 = local.type == 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_NUMBER_TO_INDEX(local.start_line));
+ r_symbol.children.push_back(symbol);
}
- symbol.documentation = parse_documentation(line);
- r_symbol.children.push_back(symbol);
}
}
@@ -384,8 +449,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 +474,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,12 +497,11 @@ 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);
String last_part = line.substr(p_cursor.character + 1, lines[i].length());
- if (!p_symbol.empty()) {
+ if (!p_symbol.is_empty()) {
String left_cursor_text;
for (int c = p_cursor.character - 1; c >= 0; c--) {
left_cursor_text = line.substr(c, p_cursor.character - c);
@@ -457,19 +520,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 +539,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 +548,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 +569,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 +586,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 +600,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,8 +637,7 @@ 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()) {
+ if (p_subclass.is_empty()) {
const lsp::DocumentSymbol *const *ptr = members.getptr(p_name);
if (ptr) {
return *ptr;
@@ -591,7 +651,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 +659,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);
+ if (member_completions.is_empty()) {
+ 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 +670,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 +693,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 +719,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(); j++) {
+ enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].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 +848,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..28b9b3c82a 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,7 @@
#define GDSCRIPT_EXTEND_PARSER_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "lsp.hpp"
#ifndef LINE_NUMBER_TO_INDEX
@@ -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..0d1f98778e 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,40 +29,117 @@
/*************************************************************************/
#include "gdscript_language_protocol.h"
-#include "core/io/json.h"
-#include "core/os/copymem.h"
-#include "core/project_settings.h"
+
+#include "core/config/project_settings.h"
+#include "editor/doc_tools.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);
- if (!output.empty()) {
- CharString charstr = output.utf8();
- peer->put_packet((const uint8_t *)charstr.ptr(), charstr.length());
+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.is_empty()) {
+ res_queue.push_back(output.utf8());
+ }
+ }
+ 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::LSPeer::send_data() {
+ int sent = 0;
+ if (!res_queue.is_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;
+}
+
+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) {
String ret = process_string(p_text);
- if (ret.empty()) {
+ if (ret.is_empty()) {
return ret;
} else {
return format_output(ret);
@@ -70,7 +147,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 +159,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", "client_id"), &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 +169,6 @@ void GDScriptLanguageProtocol::_bind_methods() {
}
Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
-
lsp::InitializeResult ret;
String root_uri = p_params["rootUri"];
@@ -110,17 +183,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)) {
- String msg = JSON::print(request);
+ 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 = Variant(request).to_json_string();
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 +209,10 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
}
void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
-
lsp::GodotCapabilities capabilities;
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *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 +226,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);
+Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
+ 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);
+ String msg = Variant(message).to_json_string();
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,18 +293,12 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
}
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
- server = NULL;
+ server.instantiate();
singleton = this;
- _initialized = false;
- workspace.instance();
- text_document.instance();
+ workspace.instantiate();
+ text_document.instantiate();
set_scope("textDocument", text_document.ptr());
set_scope("completionItem", text_document.ptr());
set_scope("workspace", workspace.ptr());
workspace->root = ProjectSettings::get_singleton()->get_resource_path();
}
-
-GDScriptLanguageProtocol::~GDScriptLanguageProtocol() {
- 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..969c38eab6 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -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 : RefCounted {
+ 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<TCPServer> 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();
@@ -77,17 +97,15 @@ public:
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll();
- Error start(int p_port, const IP_Address &p_bind_ip);
+ Error start(int p_port, const IPAddress &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..33597c286f 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,18 +29,13 @@
/*************************************************************************/
#include "gdscript_language_server.h"
-#include "core/os/file_access.h"
+
+#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
GDScriptLanguageServer::GDScriptLanguageServer() {
- thread = NULL;
- thread_running = false;
- started = false;
-
- use_thread = false;
- port = 6008;
_EDITOR_DEF("network/language_server/remote_port", port);
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
_EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false);
@@ -48,7 +43,6 @@ GDScriptLanguageServer::GDScriptLanguageServer() {
}
void GDScriptLanguageServer::_notification(int p_what) {
-
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
start();
@@ -84,12 +78,11 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
void GDScriptLanguageServer::start() {
port = (int)_EDITOR_GET("network/language_server/remote_port");
use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
+ if (protocol.start(port, IPAddress("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);
thread_running = true;
- thread = Thread::create(GDScriptLanguageServer::thread_main, this);
+ thread.start(GDScriptLanguageServer::thread_main, this);
}
set_process_internal(!use_thread);
started = true;
@@ -98,11 +91,9 @@ void GDScriptLanguageServer::start() {
void GDScriptLanguageServer::stop() {
if (use_thread) {
- ERR_FAIL_COND(NULL == thread);
+ ERR_FAIL_COND(!thread.is_started());
thread_running = false;
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = NULL;
+ thread.wait_to_finish();
}
protocol.stop();
started = false;
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 228d29bf42..29c5ddd70e 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -40,11 +40,11 @@ class GDScriptLanguageServer : public EditorPlugin {
GDScriptLanguageProtocol protocol;
- Thread *thread;
- bool thread_running;
- bool started;
- bool use_thread;
- int port;
+ Thread thread;
+ bool thread_running = false;
+ bool started = false;
+ bool use_thread = false;
+ int port = 6008;
static void thread_main(void *p_userdata);
private:
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 0572c5f746..030633274c 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -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;
@@ -151,13 +147,11 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
List<ScriptCodeCompletionOption> options;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options);
- if (!options.empty()) {
-
+ if (!options.is_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];
@@ -269,7 +257,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
item.insertText = item.label + "(";
- if (symbol && symbol->children.empty()) {
+ if (symbol && symbol->children.is_empty()) {
item.insertText += ")";
}
} else if (item.kind == lsp::CompletionItemKind::Event) {
@@ -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;
@@ -354,7 +341,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
params.load(p_params);
List<const lsp::DocumentSymbol *> symbols;
Array arr = this->find_symbols(params, symbols);
- if (arr.empty() && !symbols.empty() && !symbols.front()->get()->native_class.empty()) { // Find a native symbol
+ if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol
const lsp::DocumentSymbol *symbol = symbols.front()->get();
if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
String id;
@@ -413,11 +400,13 @@ GDScriptTextDocument::~GDScriptTextDocument() {
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
+ EditorFileSystem::get_singleton()->update_file(path);
}
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) {
@@ -437,7 +426,7 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list);
for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
if (const lsp::DocumentSymbol *s = E->get()) {
- if (!s->uri.empty()) {
+ if (!s->uri.is_empty()) {
lsp::Location location;
location.uri = s->uri;
location.range = s->range;
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index b2fd0c31f9..17f1d5d5e3 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,12 +31,12 @@
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
-#include "core/os/file_access.h"
-#include "core/reference.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
#include "lsp.hpp"
-class GDScriptTextDocument : public Reference {
- GDCLASS(GDScriptTextDocument, Reference)
+class GDScriptTextDocument : public RefCounted {
+ GDCLASS(GDScriptTextDocument, RefCounted)
protected:
static void _bind_methods();
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 1c0590cff1..1915c92cbf 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -29,28 +29,44 @@
/*************************************************************************/
#include "gdscript_workspace.h"
+
#include "../gdscript.h"
#include "../gdscript_parser.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/config/project_settings.h"
+#include "core/object/script_language.h"
+#include "editor/doc_tools.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("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);
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::did_delete_files(const Dictionary &p_params) {
+ Array files = p_params["files"];
+ for (int i = 0; i < files.size(); ++i) {
+ Dictionary file = files[i];
+ String uri = file["uri"];
+ String path = get_file_path(uri);
+ parse_script(path, "");
+ }
}
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 +84,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;
@@ -76,7 +91,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
const lsp::DocumentSymbol &class_symbol = E->value();
- if (p_member.empty()) {
+ if (p_member.is_empty()) {
return &class_symbol;
} else {
for (int i = 0; i < class_symbol.children.size(); i++) {
@@ -90,7 +105,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 +113,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 +130,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 +164,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,19 +176,21 @@ 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) {
String query = p_params["query"];
Array arr;
- if (!query.empty()) {
+ if (!query.is_empty()) {
for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
if (query.is_subsequence_ofi(script_symbols[i].name)) {
- arr.push_back(script_symbols[i].to_json());
+ lsp::DocumentedSymbolInformation symbol = script_symbols[i];
+ symbol.location.uri = get_file_uri(symbol.location.uri);
+ arr.push_back(symbol.to_json());
}
}
}
@@ -182,11 +199,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();
+ DocTools *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();
@@ -194,7 +212,7 @@ Error GDScriptWorkspace::initialize() {
class_symbol.native_class = class_name;
class_symbol.kind = lsp::SymbolKind::Class;
class_symbol.detail = String("<Native> class ") + class_name;
- if (!class_data.inherits.empty()) {
+ if (!class_data.inherits.is_empty()) {
class_symbol.detail += " extends " + class_data.inherits;
}
class_symbol.documentation = class_data.brief_description + "\n" + class_data.description;
@@ -258,7 +276,7 @@ Error GDScriptWorkspace::initialize() {
symbol_arg.kind = lsp::SymbolKind::Variable;
symbol_arg.detail = arg.type;
- if (!arg_default_value_started && !arg.default_value.empty()) {
+ if (!arg_default_value_started && !arg.default_value.is_empty()) {
arg_default_value_started = true;
}
String arg_str = arg.name + ": " + arg.type;
@@ -273,11 +291,11 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
if (data.qualifiers.find("vararg") != -1) {
- params += params.empty() ? "..." : ", ...";
+ params += params.is_empty() ? "..." : ", ...";
}
String return_type = data.return_type;
- if (return_type.empty()) {
+ if (return_type.is_empty()) {
return_type = "void";
}
symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type;
@@ -311,14 +329,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;
@@ -347,7 +363,7 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
String path = p_uri;
path = path.replace(root_uri + "/", "res://");
- path = path.http_unescape();
+ path = path.uri_decode();
return path;
}
@@ -373,25 +389,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->instantiate();
+ 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()) {
@@ -399,26 +461,21 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
lsp::Position pos = p_doc_pos.position;
- if (symbol_identifier.empty()) {
+ if (symbol_identifier.is_empty()) {
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
pos.character += offset.y;
}
- if (!symbol_identifier.empty()) {
-
+ if (!symbol_identifier.is_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,9 +486,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
} else {
-
String member = ret.class_member;
- if (member.empty() && symbol_identifier != ret.class_name) {
+ if (member.is_empty() && symbol_identifier != ret.class_name) {
member = symbol_identifier;
}
symbol = get_native_symbol(ret.class_name, member);
@@ -447,15 +503,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 +526,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,10 +540,9 @@ 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) {
+ if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {
return &symbol;
}
@@ -501,7 +553,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 +575,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 +590,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..8b166a873c 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,12 +32,17 @@
#define GDSCRIPT_WORKSPACE_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
+#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
-class GDScriptWorkspace : public Reference {
- GDCLASS(GDScriptWorkspace, Reference);
+class GDScriptWorkspace : public RefCounted {
+ GDCLASS(GDScriptWorkspace, RefCounted);
+
+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();
@@ -84,6 +89,7 @@ public:
void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
Dictionary generate_script_api(const String &p_path);
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
+ void did_delete_files(const Dictionary &p_params);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index a2dcc48820..a7dcfdb22d 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,9 +31,9 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
-#include "core/class_db.h"
-#include "core/list.h"
-#include "editor/doc/doc_data.h"
+#include "core/doc_data.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.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;
}
};
@@ -547,7 +547,7 @@ struct TextDocumentItem {
* The version number of this document (it will increase after each
* change, including undo/redo).
*/
- int version;
+ int version = 0;
/**
* The content of the opened text document.
@@ -584,7 +584,7 @@ struct TextDocumentContentChangeEvent {
/**
* The length of the range that got replaced.
*/
- int rangeLength;
+ int rangeLength = 0;
/**
* The new text of the range/document.
@@ -656,12 +656,12 @@ struct Diagnostic {
* The diagnostic's severity. Can be omitted. If omitted it is up to the
* client to interpret diagnostics as error, warning, info or hint.
*/
- int severity;
+ int severity = 0;
/**
* The diagnostic's code, which might appear in the user interface.
*/
- int code;
+ int code = 0;
/**
* A human-readable string describing the source of this
@@ -687,7 +687,7 @@ struct Diagnostic {
dict["severity"] = severity;
dict["message"] = message;
dict["source"] = source;
- if (!relatedInformation.empty()) {
+ if (!relatedInformation.is_empty()) {
Array arr;
arr.resize(relatedInformation.size());
for (int i = 0; i < relatedInformation.size(); i++) {
@@ -766,7 +766,7 @@ struct MarkupContent {
// Use namespace instead of enumeration to follow the LSP specifications
// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
-// And here C++ compilers are unhappy with our enumeration name like Color, File, Reference etc.
+// And here C++ compilers are unhappy with our enumeration name like Color, File, RefCounted etc.
/**
* The kind of a completion entry.
*/
@@ -788,7 +788,7 @@ static const int Keyword = 14;
static const int Snippet = 15;
static const int Color = 16;
static const int File = 17;
-static const int Reference = 18;
+static const int RefCounted = 18;
static const int Folder = 19;
static const int EnumMember = 20;
static const int Constant = 21;
@@ -833,7 +833,7 @@ struct CompletionItem {
* an icon is chosen by the editor. The standardized set
* of available values is defined in `CompletionItemKind`.
*/
- int kind;
+ int kind = 0;
/**
* A human-readable string with additional information
@@ -891,7 +891,7 @@ struct CompletionItem {
* The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`.
*/
- int insertTextFormat;
+ int insertTextFormat = 0;
/**
* An edit which is applied to a document when selecting this completion. When an edit is provided the value of
@@ -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"];
+ }
}
};
@@ -983,7 +1003,7 @@ struct CompletionList {
* This list it not complete. Further typing should result in recomputing
* this list.
*/
- bool isIncomplete;
+ bool isIncomplete = false;
/**
* The completion items.
@@ -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.
@@ -1172,7 +1191,7 @@ struct DocumentSymbol {
void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {
DocumentedSymbolInformation si;
- if (p_join_name && !p_container.empty()) {
+ if (p_join_name && !p_container.is_empty()) {
si.name = p_container + ">" + name;
} else {
si.name = name;
@@ -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.
*
@@ -1514,6 +1528,114 @@ struct SignatureHelp {
}
};
+/**
+ * A pattern to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationPattern {
+ /**
+ * The glob pattern to match.
+ */
+ String glob = "**/*.gd";
+
+ /**
+ * Whether to match `file`s or `folder`s with this pattern.
+ *
+ * Matches both if undefined.
+ */
+ String matches = "file";
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["glob"] = glob;
+ dict["matches"] = matches;
+
+ return dict;
+ }
+};
+
+/**
+ * A filter to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationFilter {
+ /**
+ * The actual file operation pattern.
+ */
+ FileOperationPattern pattern;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["pattern"] = pattern.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * The options to register for file operations.
+ */
+struct FileOperationRegistrationOptions {
+ /**
+ * The actual filters.
+ */
+ Vector<FileOperationFilter> filters;
+
+ FileOperationRegistrationOptions() {
+ filters.push_back(FileOperationFilter());
+ }
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ Array filts;
+ for (int i = 0; i < filters.size(); i++) {
+ filts.push_back(filters[i].to_json());
+ }
+ dict["filters"] = filts;
+
+ return dict;
+ }
+};
+
+/**
+ * The server is interested in file notifications/requests.
+ */
+struct FileOperations {
+ /**
+ * The server is interested in receiving didDeleteFiles file notifications.
+ */
+ FileOperationRegistrationOptions didDelete;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["didDelete"] = didDelete.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * Workspace specific server capabilities
+ */
+struct Workspace {
+ /**
+ * The server is interested in file notifications/requests.
+ */
+ FileOperations fileOperations;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["fileOperations"] = fileOperations.to_json();
+
+ return dict;
+ }
+};
+
struct ServerCapabilities {
/**
* Defines how text documents are synced. Is either a detailed structure defining each notification or
@@ -1576,6 +1698,11 @@ struct ServerCapabilities {
bool workspaceSymbolProvider = true;
/**
+ * The server supports workspace folder.
+ */
+ Workspace workspace;
+
+ /**
* The server provides code actions. The `CodeActionOptions` return type is only
* valid if the client signals code action literal support via the property
* `textDocument.codeAction.codeActionLiteralSupport`.
@@ -1642,12 +1769,12 @@ 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("(");
dict["signatureHelpProvider"] = signatureHelpProvider.to_json();
- dict["codeLensProvider"] = false; // codeLensProvider.to_json();
+ //dict["codeLensProvider"] = codeLensProvider.to_json();
dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
dict["renameProvider"] = renameProvider.to_json();
dict["documentLinkProvider"] = documentLinkProvider.to_json();
@@ -1662,6 +1789,7 @@ struct ServerCapabilities {
dict["documentHighlightProvider"] = documentHighlightProvider;
dict["documentSymbolProvider"] = documentSymbolProvider;
dict["workspaceSymbolProvider"] = workspaceSymbolProvider;
+ dict["workspace"] = workspace.to_json();
dict["codeActionProvider"] = codeActionProvider;
dict["documentFormattingProvider"] = documentFormattingProvider;
dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider;
@@ -1684,10 +1812,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 +1826,6 @@ struct GodotNativeClassInfo {
/** Features not included in the standard lsp specifications */
struct GodotCapabilities {
-
/**
* Native class list
*/
@@ -1718,7 +1844,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");
@@ -1770,7 +1895,6 @@ static String marked_documentation(const String &p_bbcode) {
}
return markdown;
}
-
} // namespace lsp
#endif