summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp92
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h14
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp23
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h2
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp143
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp64
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h3
-rw-r--r--modules/gdscript/language_server/lsp.hpp593
9 files changed, 496 insertions, 442 deletions
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 16af7cb92f..a6fbfd3779 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -80,9 +80,18 @@ 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++) {
+ const lsp::DocumentSymbol &symbol = class_symbol.children[i];
+ members.set(symbol.name, &symbol);
+ }
}
}
@@ -448,87 +457,32 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int
}
const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name) const {
- const GDScriptParser::Node *head = get_parse_tree();
-
- if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
-
- if (const Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = gdclass->constant_expressions.find(p_name)) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(E->get().expression->line));
- }
- for (int i = 0; i < gdclass->subclasses.size(); i++) {
- const ClassNode *m = gdclass->subclasses[i];
- if (m && m->name == p_name) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line));
- }
- }
-
- for (int i = 0; i < gdclass->variables.size(); i++) {
- const GDScriptParser::ClassNode::Member &m = gdclass->variables[i];
- if (m.identifier == p_name) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line));
- }
- }
-
- for (int i = 0; i < gdclass->functions.size(); i++) {
- const GDScriptParser::FunctionNode *m = gdclass->functions[i];
- if (m->name == p_name) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line));
- }
- }
-
- for (int i = 0; i < gdclass->static_functions.size(); i++) {
- const GDScriptParser::FunctionNode *m = gdclass->static_functions[i];
- if (m->name == p_name) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line));
- }
- }
-
- for (int i = 0; i < gdclass->_signals.size(); i++) {
- const GDScriptParser::ClassNode::Signal &m = gdclass->_signals[i];
- if (m.name == p_name) {
- return get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line));
- }
- }
+ const lsp::DocumentSymbol *const *ptr = members.getptr(p_name);
+ if (ptr) {
+ return *ptr;
}
return NULL;
}
-void ExtendGDScriptParser::dump_member_symbols(Map<String, const lsp::DocumentSymbol *> &r_symbols) {
+const Array &ExtendGDScriptParser::get_member_completions() {
- const GDScriptParser::Node *head = get_parse_tree();
- if (const GDScriptParser::ClassNode *gdclass = dynamic_cast<const GDScriptParser::ClassNode *>(head)) {
+ if (member_completions.empty()) {
- for (const Map<StringName, GDScriptParser::ClassNode::Constant>::Element *E = gdclass->constant_expressions.front(); E; E = E->next()) {
- get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(E->get().expression->line));
- }
+ const String *name = members.next(NULL);
+ while (name) {
- for (int i = 0; i < gdclass->subclasses.size(); i++) {
- const ClassNode *m = gdclass->subclasses[i];
- r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
- }
-
- for (int i = 0; i < gdclass->variables.size(); i++) {
- const GDScriptParser::ClassNode::Member &m = gdclass->variables[i];
- r_symbols.insert(JOIN_SYMBOLS(path, m.identifier), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line)));
- }
-
- for (int i = 0; i < gdclass->functions.size(); i++) {
- const GDScriptParser::FunctionNode *m = gdclass->functions[i];
- r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
- }
-
- for (int i = 0; i < gdclass->static_functions.size(); i++) {
- const GDScriptParser::FunctionNode *m = gdclass->static_functions[i];
- r_symbols.insert(JOIN_SYMBOLS(path, m->name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m->line)));
- }
+ const lsp::DocumentSymbol *symbol = members.get(*name);
+ lsp::CompletionItem item = symbol->make_completion_item(false);
+ item.data = JOIN_SYMBOLS(path, *name);
+ member_completions.push_back(item.to_json());
- for (int i = 0; i < gdclass->_signals.size(); i++) {
- const GDScriptParser::ClassNode::Signal &m = gdclass->_signals[i];
- r_symbols.insert(JOIN_SYMBOLS(path, m.name), get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(m.line)));
+ name = members.next(name);
}
}
+
+ return member_completions;
}
Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index a7e5130e2c..d20dca59cf 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -43,29 +43,36 @@
#define JOIN_SYMBOLS(p_path, name) ((p_path) + "." + (name))
#endif
+typedef HashMap<String, const lsp::DocumentSymbol *> ClassMembers;
+
class ExtendGDScriptParser : public GDScriptParser {
+
String path;
String code;
Vector<String> lines;
lsp::DocumentSymbol class_symbol;
Vector<lsp::Diagnostic> diagnostics;
+ ClassMembers members;
void update_diagnostics();
- void update_symbols();
+ void update_symbols();
void parse_class_symbol(const GDScriptParser::ClassNode *p_class, lsp::DocumentSymbol &r_symbol);
void parse_function_symbol(const GDScriptParser::FunctionNode *p_func, lsp::DocumentSymbol &r_symbol);
- String parse_documentation(int p_line);
+ String parse_documentation(int p_line);
const lsp::DocumentSymbol *search_symbol_defined_at_line(int p_line, const lsp::DocumentSymbol &p_parent) const;
+ Array member_completions;
+
public:
_FORCE_INLINE_ const String &get_path() const { return path; }
_FORCE_INLINE_ const String &get_code() const { return code; }
_FORCE_INLINE_ const Vector<String> &get_lines() const { return lines; }
_FORCE_INLINE_ const lsp::DocumentSymbol &get_symbols() const { return class_symbol; }
_FORCE_INLINE_ const Vector<lsp::Diagnostic> &get_diagnostics() const { return diagnostics; }
+ _FORCE_INLINE_ const ClassMembers &get_members() const { return members; }
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;
@@ -74,7 +81,8 @@ public:
const lsp::DocumentSymbol *get_symbol_defined_at_line(int p_line) const;
const lsp::DocumentSymbol *get_member_symbol(const String &p_name) const;
- void dump_member_symbols(Map<String, const lsp::DocumentSymbol *> &r_symbols);
+
+ const Array &get_member_completions();
Error parse(const String &p_code, const String &p_path);
};
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 7c24efe450..7fb336cc58 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -91,7 +91,27 @@ void GDScriptLanguageProtocol::_bind_methods() {
Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
lsp::InitializeResult ret;
- workspace.initialize();
+
+ String root_uri = p_params["rootUri"];
+ String root = p_params["rootPath"];
+ bool is_same_workspace = root == workspace.root;
+ is_same_workspace = root.to_lower() == workspace.root.to_lower();
+#ifdef WINDOWS_ENABLED
+ is_same_workspace = root.replace("\\", "/").to_lower() == workspace.root.to_lower();
+#endif
+
+ if (root_uri.length() && is_same_workspace) {
+ workspace.root_uri = root_uri;
+ } else {
+ workspace.root_uri = "file://" + workspace.root;
+ }
+
+ if (!_initialized) {
+ workspace.initialize();
+ text_document.initialize();
+ _initialized = true;
+ }
+
return ret.to_json();
}
@@ -167,6 +187,7 @@ bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
server = NULL;
singleton = this;
+ _initialized = false;
set_scope("textDocument", &text_document);
set_scope("completionItem", &text_document);
set_scope("workspace", &workspace);
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index dbe073dd07..be4a7cd47c 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -62,6 +62,8 @@ class GDScriptLanguageProtocol : public JSONRPC {
String process_message(const String &p_text);
String format_output(const String &p_text);
+ bool _initialized;
+
protected:
static void _bind_methods();
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 177f13c04c..a5211fb0f1 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -30,6 +30,8 @@
#include "gdscript_text_document.h"
#include "../gdscript.h"
+#include "core/os/os.h"
+#include "editor/editor_settings.h"
#include "gdscript_extend_parser.h"
#include "gdscript_language_protocol.h"
@@ -71,6 +73,33 @@ lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_
return doc;
}
+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);
+ while (class_ptr) {
+
+ const ClassMembers &members = native_members.get(*class_ptr);
+
+ const String *name = members.next(NULL);
+ while (name) {
+
+ const lsp::DocumentSymbol *symbol = members.get(*name);
+ lsp::CompletionItem item = symbol->make_completion_item(false);
+ item.data = JOIN_SYMBOLS(String(*class_ptr), *name);
+ native_member_completions.push_back(item.to_json());
+
+ name = members.next(name);
+ }
+
+ class_ptr = native_members.next(class_ptr);
+ }
+ }
+}
+
Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
Dictionary params = p_params["textDocument"];
String uri = params["uri"];
@@ -87,6 +116,7 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
}
Array GDScriptTextDocument::completion(const Dictionary &p_params) {
+
Array arr;
lsp::CompletionParams params;
@@ -98,18 +128,16 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
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;
- item.insertText = option.insert_text;
item.data = request_data;
- if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "'" || params.context.triggerCharacter == "\"") && (option.insert_text.begins_with("'") || option.insert_text.begins_with("\""))) {
- item.insertText = option.insert_text.substr(1, option.insert_text.length() - 2);
- }
-
switch (option.kind) {
case ScriptCodeCompletionOption::KIND_ENUM:
item.kind = lsp::CompletionItemKind::Enum;
@@ -142,50 +170,24 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
item.kind = lsp::CompletionItemKind::Text;
break;
}
- arr.push_back(item.to_json());
- }
+ arr[i] = item.to_json(true);
+ i++;
+ }
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- for (Map<String, const lsp::DocumentSymbol *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().flat_symbols.front(); E; E = E->next()) {
- const lsp::DocumentSymbol *symbol = E->get();
- if (!symbol) continue;
+ arr = native_member_completions.duplicate();
- lsp::CompletionItem item;
- item.label = symbol->name;
- item.data = E->key();
+ for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.front(); E; E = E->next()) {
- switch (symbol->kind) {
- case lsp::SymbolKind::Enum:
- item.kind = lsp::CompletionItemKind::Enum;
- break;
- case lsp::SymbolKind::Class:
- item.kind = lsp::CompletionItemKind::Class;
- break;
- case lsp::SymbolKind::Property:
- item.kind = lsp::CompletionItemKind::Property;
- break;
- case lsp::SymbolKind::Method:
- case lsp::SymbolKind::Function:
- item.kind = lsp::CompletionItemKind::Method;
- break;
- case lsp::SymbolKind::Event:
- item.kind = lsp::CompletionItemKind::Event;
- break;
- case lsp::SymbolKind::Constant:
- item.kind = lsp::CompletionItemKind::Constant;
- break;
- case lsp::SymbolKind::Variable:
- item.kind = lsp::CompletionItemKind::Variable;
- break;
- case lsp::SymbolKind::File:
- item.kind = lsp::CompletionItemKind::File;
- break;
- default:
- item.kind = lsp::CompletionItemKind::Text;
- break;
+ ExtendGDScriptParser *script = E->get();
+ const Array &items = script->get_member_completions();
+
+ const int start_size = arr.size();
+ arr.resize(start_size + items.size());
+ for (int i = start_size; i < arr.size(); i++) {
+ arr[i] = items[i - start_size];
}
- arr.push_back(item.to_json());
}
}
return arr;
@@ -202,19 +204,50 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
const lsp::DocumentSymbol *symbol = NULL;
if (data.get_type() == Variant::DICTIONARY) {
+
params.load(p_params["data"]);
- GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function);
+ 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) {
- if (Map<String, const lsp::DocumentSymbol *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().flat_symbols.find(data)) {
- symbol = E->get();
+ String query = data;
+ int seperator_pos = query.find_last(".");
+ if (seperator_pos >= 0 && seperator_pos < query.length() - 1) {
+
+ String class_ = query.substr(0, seperator_pos);
+ StringName class_name = class_;
+ String member_name = query.substr(seperator_pos + 1, query.length());
+
+ if (const ClassMembers *members = GDScriptLanguageProtocol::get_singleton()->get_workspace().native_members.getptr(class_name)) {
+ if (const lsp::DocumentSymbol *const *member = members->getptr(member_name)) {
+ symbol = *member;
+ }
+ }
+
+ if (!symbol) {
+ if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace().scripts.find(class_name)) {
+ symbol = E->get()->get_member_symbol(member_name);
+ }
+ }
}
}
if (symbol) {
item.documentation = symbol->render();
}
+
+ if (item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) {
+ item.insertText = item.label + "(";
+ if (symbol && symbol->detail.find(",") == -1) {
+ item.insertText += ")";
+ }
+ } else if (item.kind == lsp::CompletionItemKind::Event) {
+ if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) {
+ const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
+ item.insertText = quote_style + item.label + quote_style;
+ }
+ }
+
return item.to_json();
}
@@ -247,17 +280,20 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
const lsp::DocumentSymbol *symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_symbol(params);
if (symbol) {
+
lsp::Hover hover;
hover.contents = symbol->render();
return hover.to_json();
+
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
+
Dictionary ret;
Array contents;
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()) {
- if (const lsp::DocumentSymbol *symbol = E->get()) {
- contents.push_back(symbol->render().value);
+ if (const lsp::DocumentSymbol *s = E->get()) {
+ contents.push_back(s->render().value);
}
}
ret["contents"] = contents;
@@ -289,16 +325,13 @@ Array GDScriptTextDocument::definition(const Dictionary &p_params) {
GDScriptLanguageProtocol::get_singleton()->get_workspace().resolve_related_symbols(params, list);
for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
- if (const lsp::DocumentSymbol *symbol = E->get()) {
+ if (const lsp::DocumentSymbol *s = E->get()) {
lsp::Location location;
- location.uri = symbol->uri;
- location.range = symbol->range;
+ location.uri = s->uri;
+ location.range = s->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());
- }
+ arr.push_back(location.to_json());
}
}
}
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index 68d89c7ba4..d1e11f684c 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -47,6 +47,8 @@ protected:
void sync_script_content(const String &p_path, const String &p_content);
+ Array native_member_completions;
+
private:
lsp::TextDocumentItem load_document_item(const Variant &p_param);
@@ -61,6 +63,8 @@ public:
Variant hover(const Dictionary &p_params);
Array definition(const Dictionary &p_params);
+ void initialize();
+
GDScriptTextDocument();
virtual ~GDScriptTextDocument();
};
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index d21f53652f..089c19e6a4 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -64,9 +64,9 @@ 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 end_pos;
+ StringName empty;
- while (class_name != end_pos) {
+ while (class_name != empty) {
if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
const lsp::DocumentSymbol &class_symbol = E->value();
@@ -159,22 +159,6 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path)
return NULL;
}
-void GDScriptWorkspace::strip_flat_symbols(const String &p_branch) {
-
- typedef Map<String, const lsp::DocumentSymbol *>::Element *Item;
-
- List<Item> removal_items;
- for (Item E = flat_symbols.front(); E; E = E->next()) {
- if (E->key().begins_with(p_branch)) {
- removal_items.push_back(E);
- }
- }
-
- for (List<Item>::Element *E = removal_items.front(); E; E = E->next()) {
- flat_symbols.erase(E->get());
- }
-}
-
String GDScriptWorkspace::marked_documentation(const String &p_bbcode) {
String markdown = p_bbcode.strip_edges();
@@ -327,13 +311,14 @@ Error GDScriptWorkspace::initialize() {
}
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- // expand symbol trees to the flat symbol pool
for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) {
+ ClassMembers members;
const lsp::DocumentSymbol &class_symbol = E->get();
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
- flat_symbols.insert(JOIN_SYMBOLS(class_symbol.name, symbol.name), &symbol);
+ members.set(symbol.name, &symbol);
}
+ native_members.set(E->key(), members);
}
}
@@ -355,12 +340,6 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
parse_results[p_path] = parser;
scripts[p_path] = parser;
- if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- // update flat symbol pool
- strip_flat_symbols(p_path);
- parser->dump_member_symbols(flat_symbols);
- }
-
} else {
if (last_parser && last_script && last_parser->get() != last_script->get()) {
memdelete(last_parser->get());
@@ -383,14 +362,16 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
}
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
- String path = p_uri.replace("file://", "").http_unescape();
- path = path.replace(root + "/", "res://");
- return ProjectSettings::get_singleton()->localize_path(path);
+ String path = p_uri;
+ path = path.replace(root_uri + "/", "res://");
+ path = path.http_unescape();
+ return path;
}
String GDScriptWorkspace::get_file_uri(const String &p_path) const {
- String path = ProjectSettings::get_singleton()->globalize_path(p_path);
- return "file://" + path;
+ String uri = p_path;
+ uri = uri.replace("res://", root_uri + "/");
+ return uri;
}
void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
@@ -486,14 +467,19 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
- for (Map<String, const lsp::DocumentSymbol *>::Element *E = flat_symbols.front(); E; E = E->next()) {
- String id = E->key();
- int idx = id.find_last(".");
- if (idx >= 0 && idx < id.length() - 1) {
- String name = id.substr(idx + 1, id.length());
- if (name == symbol_identifier) {
- r_list.push_back(E->get());
- }
+ const StringName *class_ptr = native_members.next(NULL);
+ while (class_ptr) {
+ const ClassMembers &members = native_members.get(*class_ptr);
+ if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
+ r_list.push_back(*symbol);
+ }
+ class_ptr = native_members.next(class_ptr);
+ }
+
+ for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
+ const ClassMembers &members = E->get()->get_members();
+ if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
+ r_list.push_back(*symbol);
}
}
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 2ae488b451..1ecaba6f1f 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -58,10 +58,11 @@ protected:
public:
String root;
+ String root_uri;
Map<String, ExtendGDScriptParser *> scripts;
Map<String, ExtendGDScriptParser *> parse_results;
- Map<String, const lsp::DocumentSymbol *> flat_symbols;
+ HashMap<StringName, ClassMembers> native_members;
public:
Array symbol(const Dictionary &p_params);
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 7e98bfa279..c208d5a198 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -46,11 +46,11 @@ struct TextDocumentIdentifier {
*/
DocumentUri uri;
- void load(const Dictionary &p_params) {
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
uri = p_params["uri"];
}
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["uri"] = uri;
return dict;
@@ -78,12 +78,12 @@ struct Position {
*/
int character = 0;
- void load(const Dictionary &p_params) {
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
line = p_params["line"];
character = p_params["character"];
}
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["line"] = line;
dict["character"] = character;
@@ -107,12 +107,12 @@ struct Range {
*/
Position end;
- void load(const Dictionary &p_params) {
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
start.load(p_params["start"]);
end.load(p_params["end"]);
}
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["start"] = start.to_json();
dict["end"] = end.to_json();
@@ -127,12 +127,12 @@ struct Location {
DocumentUri uri;
Range range;
- void load(const Dictionary &p_params) {
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
uri = p_params["uri"];
range.load(p_params["range"]);
}
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["uri"] = uri;
dict["range"] = range.to_json();
@@ -186,12 +186,12 @@ struct TextDocumentPositionParams {
*/
Position position;
- void load(const Dictionary &p_params) {
+ _FORCE_INLINE_ void load(const Dictionary &p_params) {
textDocument.load(p_params["textDocument"]);
position.load(p_params["position"]);
}
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["textDocument"] = textDocument.to_json();
dict["position"] = position.to_json();
@@ -199,6 +199,54 @@ struct TextDocumentPositionParams {
}
};
+/**
+ * A textual edit applicable to a text document.
+ */
+struct TextEdit {
+ /**
+ * The range of the text document to be manipulated. To insert
+ * text into a document create a range where start === end.
+ */
+ Range range;
+
+ /**
+ * The string to be inserted. For delete operations use an
+ * empty string.
+ */
+ String newText;
+};
+
+/**
+ * Represents a reference to a command.
+ * Provides a title which will be used to represent a command in the UI.
+ * Commands are identified by a string identifier.
+ * The recommended way to handle commands is to implement their execution on the server side if the client and server provides the corresponding capabilities.
+ * Alternatively the tool extension code could handle the command. The protocol currently doesn’t specify a set of well-known commands.
+ */
+struct Command {
+ /**
+ * Title of the command, like `save`.
+ */
+ String title;
+ /**
+ * The identifier of the actual command handler.
+ */
+ String command;
+ /**
+ * Arguments that the command handler should be
+ * invoked with.
+ */
+ Array arguments;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+ dict["title"] = title;
+ dict["command"] = command;
+ if (arguments.size()) dict["arguments"] = arguments;
+ return dict;
+ }
+};
+
namespace TextDocumentSyncKind {
/**
* Documents should not be synced at all.
@@ -674,256 +722,6 @@ struct MarkupContent {
};
/**
- * A symbol kind.
- */
-namespace SymbolKind {
-static const int File = 1;
-static const int Module = 2;
-static const int Namespace = 3;
-static const int Package = 4;
-static const int Class = 5;
-static const int Method = 6;
-static const int Property = 7;
-static const int Field = 8;
-static const int Constructor = 9;
-static const int Enum = 10;
-static const int Interface = 11;
-static const int Function = 12;
-static const int Variable = 13;
-static const int Constant = 14;
-static const int String = 15;
-static const int Number = 16;
-static const int Boolean = 17;
-static const int Array = 18;
-static const int Object = 19;
-static const int Key = 20;
-static const int Null = 21;
-static const int EnumMember = 22;
-static const int Struct = 23;
-static const int Event = 24;
-static const int Operator = 25;
-static const int TypeParameter = 26;
-}; // namespace SymbolKind
-
-/**
- * Represents information about programming constructs like variables, classes,
- * interfaces etc.
- */
-struct SymbolInformation {
- /**
- * The name of this symbol.
- */
- String name;
-
- /**
- * The kind of this symbol.
- */
- int kind = SymbolKind::File;
-
- /**
- * Indicates if this symbol is deprecated.
- */
- bool deprecated = false;
-
- /**
- * The location of this symbol. The location's range is used by a tool
- * to reveal the location in the editor. If the symbol is selected in the
- * tool the range's start information is used to position the cursor. So
- * the range usually spans more then the actual symbol's name and does
- * normally include things like visibility modifiers.
- *
- * The range doesn't have to denote a node range in the sense of a abstract
- * syntax tree. It can therefore not be used to re-construct a hierarchy of
- * the symbols.
- */
- Location location;
-
- /**
- * The name of the symbol containing this symbol. This information is for
- * user interface purposes (e.g. to render a qualifier in the user interface
- * if necessary). It can't be used to re-infer a hierarchy for the document
- * symbols.
- */
- String containerName;
-
- Dictionary to_json() const {
- Dictionary dict;
- dict["name"] = name;
- dict["kind"] = kind;
- dict["deprecated"] = deprecated;
- dict["location"] = location.to_json();
- dict["containerName"] = containerName;
- return dict;
- }
-};
-
-struct DocumentedSymbolInformation : public SymbolInformation {
- /**
- * A human-readable string with additional information
- */
- String detail;
-
- /**
- * A human-readable string that represents a doc-comment.
- */
- String documentation;
-};
-
-/**
- * Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be
- * hierarchical and they have two ranges: one that encloses its definition and one that points to its most interesting range,
- * 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.
- */
- String name;
-
- /**
- * More detail for this symbol, e.g the signature of a function.
- */
- String detail;
-
- /**
- * Documentation for this symbol
- */
- String documentation;
-
- /**
- * The kind of this symbol.
- */
- int kind = SymbolKind::File;
-
- /**
- * Indicates if this symbol is deprecated.
- */
- bool deprecated = false;
-
- /**
- * The range enclosing this symbol not including leading/trailing whitespace but everything else
- * like comments. This information is typically used to determine if the clients cursor is
- * inside the symbol to reveal in the symbol in the UI.
- */
- Range range;
-
- /**
- * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
- * Must be contained by the `range`.
- */
- Range selectionRange;
-
- DocumentUri uri;
- String script_path;
-
- /**
- * Children of this symbol, e.g. properties of a class.
- */
- Vector<DocumentSymbol> children;
-
- Dictionary to_json() const {
- Dictionary dict;
- dict["name"] = name;
- dict["detail"] = detail;
- dict["kind"] = kind;
- dict["deprecated"] = deprecated;
- dict["range"] = range.to_json();
- dict["selectionRange"] = selectionRange.to_json();
- Array arr;
- arr.resize(children.size());
- for (int i = 0; i < children.size(); i++) {
- arr[i] = children[i].to_json();
- }
- dict["children"] = arr;
- return dict;
- }
-
- void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {
- DocumentedSymbolInformation si;
- if (p_join_name && !p_container.empty()) {
- si.name = p_container + ">" + name;
- } else {
- si.name = name;
- }
- si.kind = kind;
- si.containerName = p_container;
- si.deprecated = deprecated;
- si.location.uri = p_uri;
- si.location.range = range;
- si.detail = detail;
- si.documentation = documentation;
- r_list.push_back(si);
- for (int i = 0; i < children.size(); i++) {
- children[i].symbol_tree_as_list(p_uri, r_list, si.name, p_join_name);
- }
- }
-
- MarkupContent render() const {
- MarkupContent markdown;
- if (detail.length()) {
- markdown.value = "\t" + detail + "\n\n";
- }
- if (documentation.length()) {
- markdown.value += documentation + "\n\n";
- }
- if (script_path.length()) {
- markdown.value += "Defined in [" + script_path + "](" + uri + ")";
- }
- return markdown;
- }
-};
-
-/**
- * A textual edit applicable to a text document.
- */
-struct TextEdit {
- /**
- * The range of the text document to be manipulated. To insert
- * text into a document create a range where start === end.
- */
- Range range;
-
- /**
- * The string to be inserted. For delete operations use an
- * empty string.
- */
- String newText;
-};
-
-/**
- * Represents a reference to a command.
- * Provides a title which will be used to represent a command in the UI.
- * Commands are identified by a string identifier.
- * The recommended way to handle commands is to implement their execution on the server side if the client and server provides the corresponding capabilities.
- * Alternatively the tool extension code could handle the command. The protocol currently doesn’t specify a set of well-known commands.
- */
-struct Command {
- /**
- * Title of the command, like `save`.
- */
- String title;
- /**
- * The identifier of the actual command handler.
- */
- String command;
- /**
- * Arguments that the command handler should be
- * invoked with.
- */
- Array arguments;
-
- Dictionary to_json() const {
- Dictionary dict;
- dict["title"] = title;
- dict["command"] = command;
- if (arguments.size()) dict["arguments"] = arguments;
- return dict;
- }
-};
-
-/**
* The kind of a completion entry.
*/
namespace CompletionItemKind {
@@ -1088,20 +886,22 @@ struct CompletionItem {
*/
Variant data;
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json(bool minimized = false) const {
Dictionary dict;
dict["label"] = label;
dict["kind"] = kind;
- dict["detail"] = detail;
- dict["documentation"] = documentation.to_json();
- dict["deprecated"] = deprecated;
- dict["preselect"] = preselect;
- dict["sortText"] = sortText;
- dict["filterText"] = filterText;
- dict["insertText"] = insertText;
- if (commitCharacters.size()) dict["commitCharacters"] = commitCharacters;
- dict["command"] = command.to_json();
dict["data"] = data;
+ if (!minimized) {
+ dict["insertText"] = insertText;
+ dict["detail"] = detail;
+ dict["documentation"] = documentation.to_json();
+ dict["deprecated"] = deprecated;
+ dict["preselect"] = preselect;
+ dict["sortText"] = sortText;
+ dict["filterText"] = filterText;
+ if (commitCharacters.size()) dict["commitCharacters"] = commitCharacters;
+ dict["command"] = command.to_json();
+ }
return dict;
}
@@ -1145,6 +945,251 @@ struct CompletionList {
};
/**
+ * A symbol kind.
+ */
+namespace SymbolKind {
+static const int File = 1;
+static const int Module = 2;
+static const int Namespace = 3;
+static const int Package = 4;
+static const int Class = 5;
+static const int Method = 6;
+static const int Property = 7;
+static const int Field = 8;
+static const int Constructor = 9;
+static const int Enum = 10;
+static const int Interface = 11;
+static const int Function = 12;
+static const int Variable = 13;
+static const int Constant = 14;
+static const int String = 15;
+static const int Number = 16;
+static const int Boolean = 17;
+static const int Array = 18;
+static const int Object = 19;
+static const int Key = 20;
+static const int Null = 21;
+static const int EnumMember = 22;
+static const int Struct = 23;
+static const int Event = 24;
+static const int Operator = 25;
+static const int TypeParameter = 26;
+}; // namespace SymbolKind
+
+/**
+ * Represents information about programming constructs like variables, classes,
+ * interfaces etc.
+ */
+struct SymbolInformation {
+ /**
+ * The name of this symbol.
+ */
+ String name;
+
+ /**
+ * The kind of this symbol.
+ */
+ int kind = SymbolKind::File;
+
+ /**
+ * Indicates if this symbol is deprecated.
+ */
+ bool deprecated = false;
+
+ /**
+ * The location of this symbol. The location's range is used by a tool
+ * to reveal the location in the editor. If the symbol is selected in the
+ * tool the range's start information is used to position the cursor. So
+ * the range usually spans more then the actual symbol's name and does
+ * normally include things like visibility modifiers.
+ *
+ * The range doesn't have to denote a node range in the sense of a abstract
+ * syntax tree. It can therefore not be used to re-construct a hierarchy of
+ * the symbols.
+ */
+ Location location;
+
+ /**
+ * The name of the symbol containing this symbol. This information is for
+ * user interface purposes (e.g. to render a qualifier in the user interface
+ * if necessary). It can't be used to re-infer a hierarchy for the document
+ * symbols.
+ */
+ String containerName;
+
+ _FORCE_INLINE_ Dictionary to_json() const {
+ Dictionary dict;
+ dict["name"] = name;
+ dict["kind"] = kind;
+ dict["deprecated"] = deprecated;
+ dict["location"] = location.to_json();
+ dict["containerName"] = containerName;
+ return dict;
+ }
+};
+
+struct DocumentedSymbolInformation : public SymbolInformation {
+ /**
+ * A human-readable string with additional information
+ */
+ String detail;
+
+ /**
+ * A human-readable string that represents a doc-comment.
+ */
+ String documentation;
+};
+
+/**
+ * Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be
+ * hierarchical and they have two ranges: one that encloses its definition and one that points to its most interesting range,
+ * 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.
+ */
+ String name;
+
+ /**
+ * More detail for this symbol, e.g the signature of a function.
+ */
+ String detail;
+
+ /**
+ * Documentation for this symbol
+ */
+ String documentation;
+
+ /**
+ * The kind of this symbol.
+ */
+ int kind = SymbolKind::File;
+
+ /**
+ * Indicates if this symbol is deprecated.
+ */
+ bool deprecated = false;
+
+ /**
+ * The range enclosing this symbol not including leading/trailing whitespace but everything else
+ * like comments. This information is typically used to determine if the clients cursor is
+ * inside the symbol to reveal in the symbol in the UI.
+ */
+ Range range;
+
+ /**
+ * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function.
+ * Must be contained by the `range`.
+ */
+ Range selectionRange;
+
+ DocumentUri uri;
+ String script_path;
+
+ /**
+ * Children of this symbol, e.g. properties of a class.
+ */
+ Vector<DocumentSymbol> children;
+
+ _FORCE_INLINE_ Dictionary to_json() const {
+ Dictionary dict;
+ dict["name"] = name;
+ dict["detail"] = detail;
+ dict["kind"] = kind;
+ dict["deprecated"] = deprecated;
+ dict["range"] = range.to_json();
+ dict["selectionRange"] = selectionRange.to_json();
+ Array arr;
+ arr.resize(children.size());
+ for (int i = 0; i < children.size(); i++) {
+ arr[i] = children[i].to_json();
+ }
+ dict["children"] = arr;
+ return dict;
+ }
+
+ void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {
+ DocumentedSymbolInformation si;
+ if (p_join_name && !p_container.empty()) {
+ si.name = p_container + ">" + name;
+ } else {
+ si.name = name;
+ }
+ si.kind = kind;
+ si.containerName = p_container;
+ si.deprecated = deprecated;
+ si.location.uri = p_uri;
+ si.location.range = range;
+ si.detail = detail;
+ si.documentation = documentation;
+ r_list.push_back(si);
+ for (int i = 0; i < children.size(); i++) {
+ children[i].symbol_tree_as_list(p_uri, r_list, si.name, p_join_name);
+ }
+ }
+
+ _FORCE_INLINE_ MarkupContent render() const {
+ MarkupContent markdown;
+ if (detail.length()) {
+ markdown.value = "\t" + detail + "\n\n";
+ }
+ if (documentation.length()) {
+ markdown.value += documentation + "\n\n";
+ }
+ if (script_path.length()) {
+ markdown.value += "Defined in [" + script_path + "](" + uri + ")";
+ }
+ return markdown;
+ }
+
+ _FORCE_INLINE_ CompletionItem make_completion_item(bool with_doc = false) const {
+
+ lsp::CompletionItem item;
+ item.label = name;
+
+ if (with_doc) {
+ item.documentation = render();
+ }
+
+ switch (kind) {
+ case lsp::SymbolKind::Enum:
+ item.kind = lsp::CompletionItemKind::Enum;
+ break;
+ case lsp::SymbolKind::Class:
+ item.kind = lsp::CompletionItemKind::Class;
+ break;
+ case lsp::SymbolKind::Property:
+ item.kind = lsp::CompletionItemKind::Property;
+ break;
+ case lsp::SymbolKind::Method:
+ case lsp::SymbolKind::Function:
+ item.kind = lsp::CompletionItemKind::Method;
+ break;
+ case lsp::SymbolKind::Event:
+ item.kind = lsp::CompletionItemKind::Event;
+ break;
+ case lsp::SymbolKind::Constant:
+ item.kind = lsp::CompletionItemKind::Constant;
+ break;
+ case lsp::SymbolKind::Variable:
+ item.kind = lsp::CompletionItemKind::Variable;
+ break;
+ case lsp::SymbolKind::File:
+ item.kind = lsp::CompletionItemKind::File;
+ break;
+ default:
+ item.kind = lsp::CompletionItemKind::Text;
+ break;
+ }
+
+ return item;
+ }
+};
+
+/**
* Enum of known range kinds
*/
namespace FoldingRangeKind {
@@ -1194,7 +1239,7 @@ struct FoldingRange {
*/
String kind = FoldingRangeKind::Region;
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["startLine"] = startLine;
dict["startCharacter"] = startCharacter;
@@ -1276,7 +1321,7 @@ struct Hover {
*/
Range range;
- Dictionary to_json() const {
+ _FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["range"] = range.to_json();
dict["contents"] = contents.to_json();
@@ -1410,7 +1455,7 @@ struct ServerCapabilities {
*/
ExecuteCommandOptions executeCommandProvider;
- Dictionary to_json() {
+ _FORCE_INLINE_ Dictionary to_json() {
Dictionary dict;
dict["textDocumentSync"] = (int)textDocumentSync.change;
dict["completionProvider"] = completionProvider.to_json();
@@ -1444,7 +1489,7 @@ struct InitializeResult {
*/
ServerCapabilities capabilities;
- Dictionary to_json() {
+ _FORCE_INLINE_ Dictionary to_json() {
Dictionary dict;
dict["capabilities"] = capabilities.to_json();
return dict;