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