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.cpp106
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h7
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp219
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h46
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp62
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h9
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp171
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h7
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp153
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h10
-rw-r--r--modules/gdscript/language_server/lsp.hpp191
11 files changed, 679 insertions, 302 deletions
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 6b5c26ec81..f87e8687e5 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -35,7 +35,6 @@
#include "gdscript_workspace.h"
void ExtendGDScriptParser::update_diagnostics() {
-
diagnostics.clear();
if (has_error()) {
@@ -80,12 +79,10 @@ void ExtendGDScriptParser::update_diagnostics() {
}
void ExtendGDScriptParser::update_symbols() {
-
members.clear();
const GDScriptParser::Node *head = get_parse_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++) {
@@ -112,10 +109,11 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) {
FileAccessRef fs = FileAccess::create(FileAccess::ACCESS_RESOURCES);
tokenizer.set_code(p_code);
while (true) {
- if (tokenizer.get_token() == GDScriptTokenizer::TK_EOF) {
+ GDScriptTokenizerText::Token token = tokenizer.get_token();
+ if (token == GDScriptTokenizer::TK_EOF || token == GDScriptTokenizer::TK_ERROR) {
break;
- } else if (tokenizer.get_token() == GDScriptTokenizer::TK_CONSTANT) {
- Variant const_val = tokenizer.get_token_constant();
+ } else if (token == GDScriptTokenizer::TK_CONSTANT) {
+ const Variant &const_val = tokenizer.get_token_constant();
if (const_val.get_type() == Variant::STRING) {
String path = const_val;
bool exists = fs->file_exists(path);
@@ -140,15 +138,15 @@ void ExtendGDScriptParser::update_document_links(const String &p_code) {
}
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())
+ 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);
@@ -160,7 +158,6 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
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;
@@ -288,7 +285,6 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
}
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;
@@ -326,7 +322,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
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) {
+ if (const_node == nullptr) {
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);
@@ -383,8 +379,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("#")) {
@@ -407,22 +404,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;
@@ -432,7 +427,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);
@@ -456,19 +450,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(), "");
@@ -506,7 +499,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) {
@@ -522,6 +515,50 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::search_symbol_defined_at_line(i
return ret;
}
+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;
+ int index = 0;
+
+ bool found = false;
+ for (int l = p_position.line; l >= 0; --l) {
+ String line = lines[l];
+ int c = line.length() - 1;
+ if (l == p_position.line) {
+ c = MIN(c, p_position.character - 1);
+ }
+
+ while (c >= 0) {
+ const CharType &character = line[c];
+ if (character == ')') {
+ ++bracket_stack;
+ } else if (character == '(') {
+ --bracket_stack;
+ if (bracket_stack < 0) {
+ found = true;
+ }
+ }
+ if (bracket_stack <= 0 && character == ',') {
+ ++index;
+ }
+ --c;
+ if (found) {
+ r_func_pos.character = c;
+ break;
+ }
+ }
+
+ if (found) {
+ r_func_pos.line = l;
+ r_arg_index = index;
+ return OK;
+ }
+ }
+
+ return ERR_METHOD_NOT_FOUND;
+}
+
const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int p_line) const {
if (p_line <= 0) {
return &class_symbol;
@@ -530,7 +567,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) {
@@ -545,7 +581,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String
}
}
- return NULL;
+ return nullptr;
}
const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const {
@@ -553,12 +589,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);
@@ -567,11 +600,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();
@@ -602,7 +634,7 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
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) {
+ if (const_node == nullptr) {
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);
@@ -650,7 +682,6 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
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);
@@ -711,7 +742,7 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
Array static_functions;
for (int i = 0; i < p_class->static_functions.size(); ++i) {
- static_functions.append(dump_function_api(p_class->functions[i]));
+ static_functions.append(dump_function_api(p_class->static_functions[i]));
}
class_api["static_functions"] = static_functions;
@@ -719,7 +750,6 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
}
Dictionary ExtendGDScriptParser::generate_api() const {
-
Dictionary api;
const GDScriptParser::Node *head = get_parse_tree();
if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
@@ -732,7 +762,7 @@ 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.get_base_dir(), false, p_path, false, nullptr, false);
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 a6e0ca5534..0c031d7883 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -50,7 +50,6 @@
typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers;
class ExtendGDScriptParser : public GDScriptParser {
-
String path;
Vector<String> lines;
@@ -83,6 +82,8 @@ public:
_FORCE_INLINE_ const ClassMembers &get_members() const { return members; }
_FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; }
+ Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const;
+
String get_text_for_completion(const lsp::Position &p_cursor) const;
String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const;
String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 90646f73ba..35bf4287b8 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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,32 +32,109 @@
#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;
}
-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.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_disconnected(int p_id, bool p_was_clean_close) {
- clients.erase(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(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 +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", "p_method", "p_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 +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,17 @@ 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)) {
+
+ 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 +207,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,60 +224,59 @@ 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) {
- 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");
- }
- return server->listen(p_port);
+Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &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) {
+ p_client_id = latest_client_id;
}
-
- Ref<WebSocketPeer> *peer = clients.getptr(p_client);
- ERR_FAIL_COND(peer == NULL);
+ 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 {
@@ -218,7 +288,7 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
}
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
- server = NULL;
+ server.instance();
singleton = this;
_initialized = false;
workspace.instance();
@@ -227,9 +297,6 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
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;
+ latest_client_id = 0;
+ next_client_id = 0;
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 136b45fd78..d929fd255d 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 : 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,16 +68,16 @@ 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;
+ int next_client_id;
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);
@@ -77,17 +97,15 @@ public:
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll();
- Error start(int p_port);
+ 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 8d58b99e02..d53914814f 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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,18 +31,23 @@
#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_exit = false;
- _EDITOR_DEF("network/language_server/remote_port", 6008);
- _EDITOR_DEF("network/language_server/enable_smart_resolve", false);
+ thread = nullptr;
+ 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);
+ _EDITOR_DEF("network/language_server/use_thread", use_thread);
}
void GDScriptLanguageServer::_notification(int p_what) {
-
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
start();
@@ -50,12 +55,25 @@ void GDScriptLanguageServer::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE:
stop();
break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (started && !use_thread) {
+ protocol.poll();
+ }
+ } break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ int port = (int)_EDITOR_GET("network/language_server/remote_port");
+ bool use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
+ if (port != this->port || use_thread != this->use_thread) {
+ this->stop();
+ this->start();
+ }
+ } break;
}
}
void GDScriptLanguageServer::thread_main(void *p_userdata) {
GDScriptLanguageServer *self = static_cast<GDScriptLanguageServer *>(p_userdata);
- while (!self->thread_exit) {
+ while (self->thread_running) {
// Poll 20 times per second
self->protocol.poll();
OS::get_singleton()->delay_usec(50000);
@@ -63,22 +81,30 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
}
void GDScriptLanguageServer::start() {
- int port = (int)_EDITOR_GET("network/language_server/remote_port");
- if (protocol.start(port) == OK) {
+ 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) {
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
- ERR_FAIL_COND(thread != NULL || thread_exit);
- thread_exit = false;
- thread = Thread::create(GDScriptLanguageServer::thread_main, this);
+ if (use_thread) {
+ ERR_FAIL_COND(thread != nullptr);
+ thread_running = true;
+ thread = Thread::create(GDScriptLanguageServer::thread_main, this);
+ }
+ set_process_internal(!use_thread);
+ started = true;
}
}
void GDScriptLanguageServer::stop() {
- ERR_FAIL_COND(NULL == thread || thread_exit);
- thread_exit = true;
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = NULL;
+ if (use_thread) {
+ ERR_FAIL_COND(nullptr == thread);
+ thread_running = false;
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ thread = nullptr;
+ }
protocol.stop();
+ started = false;
EditorNode::get_log()->add_message("--- GDScript language server stopped ---", EditorLog::MSG_TYPE_EDITOR);
}
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 83c2320d98..228d29bf42 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -41,7 +41,10 @@ class GDScriptLanguageServer : public EditorPlugin {
GDScriptLanguageProtocol protocol;
Thread *thread;
- bool thread_exit;
+ bool thread_running;
+ bool started;
+ bool use_thread;
+ int port;
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 b83db718b8..778cb4d254 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -35,6 +35,7 @@
#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);
@@ -49,6 +50,8 @@ void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("colorPresentation"), &GDScriptTextDocument::colorPresentation);
ClassDB::bind_method(D_METHOD("hover"), &GDScriptTextDocument::hover);
ClassDB::bind_method(D_METHOD("definition"), &GDScriptTextDocument::definition);
+ ClassDB::bind_method(D_METHOD("declaration"), &GDScriptTextDocument::declaration);
+ ClassDB::bind_method(D_METHOD("signatureHelp"), &GDScriptTextDocument::signatureHelp);
ClassDB::bind_method(D_METHOD("show_native_symbol_in_editor"), &GDScriptTextDocument::show_native_symbol_in_editor);
}
@@ -82,19 +85,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);
@@ -109,7 +108,6 @@ void GDScriptTextDocument::initialize() {
}
Variant GDScriptTextDocument::nativeSymbol(const Dictionary &p_params) {
-
Variant ret;
lsp::NativeSymbolInspectParams params;
@@ -139,7 +137,6 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
}
Array GDScriptTextDocument::completion(const Dictionary &p_params) {
-
Array arr;
lsp::CompletionParams params;
@@ -150,12 +147,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;
@@ -198,11 +193,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();
@@ -217,28 +210,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];
@@ -267,7 +256,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->detail.find(",") == -1) {
+ if (symbol && symbol->children.empty()) {
item.insertText += ")";
}
} else if (item.kind == lsp::CompletionItemKind::Event) {
@@ -281,8 +270,6 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
}
Array GDScriptTextDocument::foldingRange(const Dictionary &p_params) {
- Dictionary params = p_params["textDocument"];
- String path = params["uri"];
Array arr;
return arr;
}
@@ -312,19 +299,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;
@@ -342,68 +328,64 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
}
Array GDScriptTextDocument::definition(const Dictionary &p_params) {
- Array arr;
-
lsp::TextDocumentPositionParams params;
params.load(p_params);
+ List<const lsp::DocumentSymbol *> symbols;
+ Array arr = this->find_symbols(params, symbols);
+ return arr;
+}
- const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params);
- if (symbol) {
- lsp::Location location;
- location.uri = symbol->uri;
- location.range = symbol->range;
-
- const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri);
- if (file_checker->file_exists(path)) {
- arr.push_back(location.to_json());
- } else if (!symbol->native_class.empty()) {
- if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
- String id;
- switch (symbol->kind) {
- case lsp::SymbolKind::Class:
- id = "class_name:" + symbol->name;
- break;
- case lsp::SymbolKind::Constant:
- id = "class_constant:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Property:
- case lsp::SymbolKind::Variable:
- id = "class_property:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Enum:
- id = "class_enum:" + symbol->native_class + ":" + symbol->name;
- break;
- case lsp::SymbolKind::Method:
- case lsp::SymbolKind::Function:
- id = "class_method:" + symbol->native_class + ":" + symbol->name;
- break;
- default:
- id = "class_global:" + symbol->native_class + ":" + symbol->name;
- break;
- }
- call_deferred("show_native_symbol_in_editor", id);
- } else {
- notify_client_show_symbol(symbol);
+Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
+ lsp::TextDocumentPositionParams 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
+ const lsp::DocumentSymbol *symbol = symbols.front()->get();
+ if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
+ String id;
+ switch (symbol->kind) {
+ case lsp::SymbolKind::Class:
+ id = "class_name:" + symbol->name;
+ break;
+ case lsp::SymbolKind::Constant:
+ id = "class_constant:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Property:
+ case lsp::SymbolKind::Variable:
+ id = "class_property:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Enum:
+ id = "class_enum:" + symbol->native_class + ":" + symbol->name;
+ break;
+ case lsp::SymbolKind::Method:
+ case lsp::SymbolKind::Function:
+ id = "class_method:" + symbol->native_class + ":" + symbol->name;
+ break;
+ default:
+ id = "class_global:" + symbol->native_class + ":" + symbol->name;
+ break;
}
+ call_deferred("show_native_symbol_in_editor", id);
+ } else {
+ notify_client_show_symbol(symbol);
}
- } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+ }
+ return arr;
+}
- List<const lsp::DocumentSymbol *> list;
- GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
- for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
+Variant GDScriptTextDocument::signatureHelp(const Dictionary &p_params) {
+ Variant ret;
- if (const lsp::DocumentSymbol *s = E->get()) {
- if (!s->uri.empty()) {
- lsp::Location location;
- location.uri = s->uri;
- location.range = s->range;
- arr.push_back(location.to_json());
- }
- }
- }
+ lsp::TextDocumentPositionParams params;
+ params.load(p_params);
+
+ lsp::SignatureHelp s;
+ if (OK == GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_signature(params, s)) {
+ ret = s.to_json();
}
- return arr;
+ return ret;
}
GDScriptTextDocument::GDScriptTextDocument() {
@@ -421,5 +403,36 @@ 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) {
+ Array arr;
+ const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(p_location);
+ if (symbol) {
+ lsp::Location location;
+ location.uri = symbol->uri;
+ location.range = symbol->range;
+ const String &path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(symbol->uri);
+ if (file_checker->file_exists(path)) {
+ arr.push_back(location.to_json());
+ }
+ r_list.push_back(symbol);
+ } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+ List<const lsp::DocumentSymbol *> list;
+ 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()) {
+ lsp::Location location;
+ location.uri = s->uri;
+ location.range = s->range;
+ arr.push_back(location.to_json());
+ r_list.push_back(s);
+ }
+ }
+ }
+ }
+ return arr;
}
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 235e2c3f6e..b2fd0c31f9 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -51,6 +51,7 @@ protected:
Array native_member_completions;
private:
+ Array find_symbols(const lsp::TextDocumentPositionParams &p_location, List<const lsp::DocumentSymbol *> &r_list);
lsp::TextDocumentItem load_document_item(const Variant &p_param);
void notify_client_show_symbol(const lsp::DocumentSymbol *symbol);
@@ -65,6 +66,8 @@ public:
Array colorPresentation(const Dictionary &p_params);
Variant hover(const Dictionary &p_params);
Array definition(const Dictionary &p_params);
+ Variant declaration(const Dictionary &p_params);
+ Variant signatureHelp(const Dictionary &p_params);
void initialize();
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index f2c0e7035b..9285d88157 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -33,8 +33,11 @@
#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);
@@ -50,7 +53,7 @@ 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 +71,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 +92,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,13 +100,13 @@ 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() {
- List<String> pathes;
- list_script_files("res://", pathes);
- for (List<String>::Element *E = pathes.front(); E; E = E->next()) {
+ List<String> paths;
+ list_script_files("res://", paths);
+ for (List<String>::Element *E = paths.front(); E; E = E->next()) {
const String &path = E->get();
Error err;
String content = FileAccess::get_file_as_string(path, &err);
@@ -149,7 +151,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 +163,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 +184,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();
@@ -252,6 +255,12 @@ Error GDScriptWorkspace::initialize() {
bool arg_default_value_started = false;
for (int j = 0; j < data.arguments.size(); j++) {
const DocData::ArgumentDoc &arg = data.arguments[j];
+
+ lsp::DocumentSymbol symbol_arg;
+ symbol_arg.name = arg.name;
+ symbol_arg.kind = lsp::SymbolKind::Variable;
+ symbol_arg.detail = arg.type;
+
if (!arg_default_value_started && !arg.default_value.empty()) {
arg_default_value_started = true;
}
@@ -263,6 +272,8 @@ Error GDScriptWorkspace::initialize() {
arg_str += ", ";
}
params += arg_str;
+
+ symbol.children.push_back(symbol_arg);
}
if (data.qualifiers.find("vararg") != -1) {
params += params.empty() ? "..." : ", ...";
@@ -303,14 +314,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;
@@ -365,25 +374,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()) {
@@ -398,19 +453,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();
@@ -421,7 +471,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;
@@ -439,15 +488,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)) {
@@ -464,9 +511,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);
@@ -479,7 +525,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) {
@@ -493,7 +538,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) {
@@ -513,6 +558,46 @@ Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) {
return api;
}
+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)) {
+ symbols.push_back(symbol);
+ } else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+ GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols);
+ }
+
+ 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();
+
+ for (int i = 0; i < symbol->children.size(); i++) {
+ const lsp::DocumentSymbol &arg = symbol->children[i];
+ lsp::ParameterInformation arg_info;
+ arg_info.label = arg.name;
+ signature_info.parameters.push_back(arg_info);
+ }
+ r_signature.signatures.push_back(signature_info);
+ break;
+ }
+ }
+
+ if (r_signature.signatures.size()) {
+ return OK;
+ }
+ }
+ }
+ return ERR_METHOD_NOT_FOUND;
+}
+
GDScriptWorkspace::GDScriptWorkspace() {
ProjectSettings::get_singleton()->get_resource_path();
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index a416ae1075..e45b06747d 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -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);
@@ -83,6 +88,7 @@ public:
const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params);
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);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index a048af88bb..cf27a1578c 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-2019 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 */
@@ -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;
}
};
@@ -330,8 +330,6 @@ struct CompletionOptions {
triggerCharacters.push_back("$");
triggerCharacters.push_back("'");
triggerCharacters.push_back("\"");
- triggerCharacters.push_back("(");
- triggerCharacters.push_back(",");
}
Dictionary to_json() const {
@@ -488,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;
@@ -496,7 +494,7 @@ struct TextDocumentSyncOptions {
dict["willSave"] = willSave;
dict["openClose"] = openClose;
dict["change"] = change;
- dict["save"] = save.to_json();
+ dict["save"] = save;
return dict;
}
};
@@ -948,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) {
@@ -967,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"];
+ }
}
};
@@ -1098,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.
@@ -1207,7 +1224,6 @@ struct DocumentSymbol {
}
_FORCE_INLINE_ CompletionItem make_completion_item(bool resolved = false) const {
-
lsp::CompletionItem item;
item.label = name;
@@ -1251,7 +1267,6 @@ struct DocumentSymbol {
};
struct NativeSymbolInspectParams {
-
String native_class;
String symbol_name;
@@ -1283,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.
*/
@@ -1366,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`
@@ -1402,6 +1415,119 @@ struct Hover {
}
};
+/**
+ * Represents a parameter of a callable-signature. A parameter can
+ * have a label and a doc-comment.
+ */
+struct ParameterInformation {
+ /**
+ * The label of this parameter information.
+ *
+ * Either a string or an inclusive start and exclusive end offsets within its containing
+ * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16
+ * string representation as `Position` and `Range` does.
+ *
+ * *Note*: a label of type string should be a substring of its containing signature label.
+ * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.
+ */
+ String label;
+
+ /**
+ * The human-readable doc-comment of this parameter. Will be shown
+ * in the UI but can be omitted.
+ */
+ MarkupContent documentation;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+ dict["label"] = label;
+ dict["documentation"] = documentation.to_json();
+ return dict;
+ }
+};
+
+/**
+ * Represents the signature of something callable. A signature
+ * can have a label, like a function-name, a doc-comment, and
+ * a set of parameters.
+ */
+struct SignatureInformation {
+ /**
+ * The label of this signature. Will be shown in
+ * the UI.
+ */
+ String label;
+
+ /**
+ * The human-readable doc-comment of this signature. Will be shown
+ * in the UI but can be omitted.
+ */
+ MarkupContent documentation;
+
+ /**
+ * The parameters of this signature.
+ */
+ Vector<ParameterInformation> parameters;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+ dict["label"] = label;
+ dict["documentation"] = documentation.to_json();
+ Array args;
+ for (int i = 0; i < parameters.size(); i++) {
+ args.push_back(parameters[i].to_json());
+ }
+ dict["parameters"] = args;
+ return dict;
+ }
+};
+
+/**
+ * Signature help represents the signature of something
+ * callable. There can be multiple signature but only one
+ * active and only one active parameter.
+ */
+struct SignatureHelp {
+ /**
+ * One or more signatures.
+ */
+ Vector<SignatureInformation> signatures;
+
+ /**
+ * The active signature. If omitted or the value lies outside the
+ * range of `signatures` the value defaults to zero or is ignored if
+ * `signatures.length === 0`. Whenever possible implementors should
+ * make an active decision about the active signature and shouldn't
+ * rely on a default value.
+ * In future version of the protocol this property might become
+ * mandatory to better express this.
+ */
+ int activeSignature = 0;
+
+ /**
+ * The active parameter of the active signature. If omitted or the value
+ * lies outside the range of `signatures[activeSignature].parameters`
+ * defaults to 0 if the active signature has parameters. If
+ * the active signature has no parameters it is ignored.
+ * In future version of the protocol this property might become
+ * mandatory to better express the active parameter if the
+ * active signature does have any.
+ */
+ int activeParameter = 0;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+ Array sigs;
+ for (int i = 0; i < signatures.size(); i++) {
+ sigs.push_back(signatures[i].to_json());
+ }
+ dict["signatures"] = sigs;
+ dict["activeSignature"] = activeSignature;
+ dict["activeParameter"] = activeParameter;
+ return dict;
+ }
+};
+
struct ServerCapabilities {
/**
* Defines how text documents are synced. Is either a detailed structure defining each notification or
@@ -1530,8 +1656,10 @@ 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["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
@@ -1570,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;
@@ -1583,9 +1710,8 @@ struct GodotNativeClassInfo {
}
};
-/** Features not included in the standart lsp specifications */
+/** Features not included in the standard lsp specifications */
struct GodotCapabilities {
-
/**
* Native class list
*/
@@ -1604,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");