You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-12-31 18:41:20 +00:00
LSP: Rework management of client owned files
This commit is contained in:
@@ -52,6 +52,7 @@ for name, path in env.module_list.items():
|
||||
|
||||
# Generate header to be included in `tests/test_main.cpp` to run module-specific tests.
|
||||
if env["tests"]:
|
||||
env.Append(CPPDEFINES=["TESTS_ENABLED"])
|
||||
env.CommandNoCache("modules_tests.gen.h", test_headers, env.Run(modules_builders.modules_tests_builder))
|
||||
|
||||
# libmodules.a with only register_module_types.
|
||||
|
||||
@@ -344,8 +344,9 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
|
||||
if (res.is_valid() && !res->get_path().is_empty()) {
|
||||
value_text = "preload(\"" + res->get_path() + "\")";
|
||||
if (symbol.documentation.is_empty()) {
|
||||
if (HashMap<String, ExtendGDScriptParser *>::Iterator S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
|
||||
symbol.documentation = S->value->class_symbol.documentation;
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(res->get_path());
|
||||
if (parser) {
|
||||
symbol.documentation = parser->class_symbol.documentation;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1038,18 +1039,17 @@ Dictionary ExtendGDScriptParser::generate_api() const {
|
||||
return api;
|
||||
}
|
||||
|
||||
Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
|
||||
void 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, false);
|
||||
parse_result = GDScriptParser::parse(p_code, p_path, false);
|
||||
GDScriptAnalyzer analyzer(this);
|
||||
|
||||
if (err == OK) {
|
||||
err = analyzer.analyze();
|
||||
if (parse_result == OK) {
|
||||
parse_result = analyzer.analyze();
|
||||
}
|
||||
update_diagnostics();
|
||||
update_symbols();
|
||||
update_document_links(p_code);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,7 @@ public:
|
||||
_FORCE_INLINE_ const Vector<LSP::Diagnostic> &get_diagnostics() const { return diagnostics; }
|
||||
_FORCE_INLINE_ const ClassMembers &get_members() const { return members; }
|
||||
_FORCE_INLINE_ const HashMap<String, ClassMembers> &get_inner_classes() const { return inner_classes; }
|
||||
Error parse_result;
|
||||
|
||||
Error get_left_function_call(const LSP::Position &p_position, LSP::Position &r_func_pos, int &r_arg_index) const;
|
||||
|
||||
@@ -166,5 +167,5 @@ public:
|
||||
const Array &get_member_completions();
|
||||
Dictionary generate_api() const;
|
||||
|
||||
Error parse(const String &p_code, const String &p_path);
|
||||
void parse(const String &p_code, const String &p_path);
|
||||
};
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
#include "editor/editor_log.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
#include "modules/gdscript/language_server/godot_lsp.h"
|
||||
|
||||
#define LSP_CLIENT_V(m_ret_val) \
|
||||
ERR_FAIL_COND_V(latest_client_id == LSP_NO_CLIENT, m_ret_val); \
|
||||
ERR_FAIL_COND_V(!clients.has(latest_client_id), m_ret_val); \
|
||||
Ref<LSPeer> client = clients.get(latest_client_id); \
|
||||
ERR_FAIL_COND_V(!client.is_valid(), m_ret_val);
|
||||
|
||||
#define LSP_CLIENT \
|
||||
ERR_FAIL_COND(latest_client_id == LSP_NO_CLIENT); \
|
||||
ERR_FAIL_COND(!clients.has(latest_client_id)); \
|
||||
Ref<LSPeer> client = clients.get(latest_client_id); \
|
||||
ERR_FAIL_COND(!client.is_valid());
|
||||
|
||||
GDScriptLanguageProtocol *GDScriptLanguageProtocol::singleton = nullptr;
|
||||
|
||||
@@ -312,8 +325,7 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
|
||||
}
|
||||
#endif
|
||||
if (p_client_id == -1) {
|
||||
ERR_FAIL_COND_MSG(latest_client_id == -1,
|
||||
"GDScript LSP: Can't notify client as none was connected.");
|
||||
ERR_FAIL_COND_MSG(latest_client_id == LSP_NO_CLIENT, "GDScript LSP: Can't notify client as none was connected.");
|
||||
p_client_id = latest_client_id;
|
||||
}
|
||||
ERR_FAIL_COND(!clients.has(p_client_id));
|
||||
@@ -333,8 +345,7 @@ void GDScriptLanguageProtocol::request_client(const String &p_method, const Vari
|
||||
}
|
||||
#endif
|
||||
if (p_client_id == -1) {
|
||||
ERR_FAIL_COND_MSG(latest_client_id == -1,
|
||||
"GDScript LSP: Can't notify client as none was connected.");
|
||||
ERR_FAIL_COND_MSG(latest_client_id == LSP_NO_CLIENT, "GDScript LSP: Can't notify client as none was connected.");
|
||||
p_client_id = latest_client_id;
|
||||
}
|
||||
ERR_FAIL_COND(!clients.has(p_client_id));
|
||||
@@ -356,6 +367,174 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
|
||||
return bool(_EDITOR_GET("network/language_server/show_native_symbols_in_editor"));
|
||||
}
|
||||
|
||||
ExtendGDScriptParser *GDScriptLanguageProtocol::LSPeer::parse_script(const String &p_path) {
|
||||
remove_cached_parser(p_path);
|
||||
|
||||
String content;
|
||||
const LSP::TextDocumentItem *document = managed_files.getptr(p_path);
|
||||
if (document == nullptr) {
|
||||
if (!p_path.has_extension("gd")) {
|
||||
return nullptr;
|
||||
}
|
||||
Error err;
|
||||
content = FileAccess::get_file_as_string(p_path, &err);
|
||||
if (err != OK) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
if (document->languageId != LSP::LanguageId::GDSCRIPT) {
|
||||
return nullptr;
|
||||
}
|
||||
content = document->text;
|
||||
}
|
||||
|
||||
ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
|
||||
parser->parse(content, p_path);
|
||||
|
||||
if (document != nullptr) {
|
||||
parse_results[p_path] = parser;
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->publish_diagnostics(p_path);
|
||||
} else {
|
||||
stale_parsers[p_path] = parser;
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
|
||||
void GDScriptLanguageProtocol::LSPeer::remove_cached_parser(const String &p_path) {
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator cached = parse_results.find(p_path);
|
||||
if (cached) {
|
||||
memdelete(cached->value);
|
||||
parse_results.remove(cached);
|
||||
}
|
||||
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator stale = stale_parsers.find(p_path);
|
||||
if (stale) {
|
||||
memdelete(stale->value);
|
||||
stale_parsers.remove(stale);
|
||||
}
|
||||
}
|
||||
|
||||
ExtendGDScriptParser *GDScriptLanguageProtocol::get_parse_result(const String &p_path) {
|
||||
LSP_CLIENT_V(nullptr);
|
||||
|
||||
ExtendGDScriptParser **cached_parser = client->parse_results.getptr(p_path);
|
||||
if (cached_parser == nullptr) {
|
||||
return client->parse_script(p_path);
|
||||
}
|
||||
return *cached_parser;
|
||||
}
|
||||
|
||||
void GDScriptLanguageProtocol::lsp_did_open(const Dictionary &p_params) {
|
||||
LSP_CLIENT;
|
||||
|
||||
LSP::TextDocumentItem document;
|
||||
document.load(p_params["textDocument"]);
|
||||
|
||||
// We keep track of non GDScript files that the client owns, but we are not interested in the content.
|
||||
if (document.languageId != LSP::LanguageId::GDSCRIPT) {
|
||||
document.text = "";
|
||||
}
|
||||
|
||||
String path = get_workspace()->get_file_path(document.uri);
|
||||
|
||||
/// An open notification must not be sent more than once without a corresponding close notification send before.
|
||||
ERR_FAIL_COND_MSG(client->managed_files.has(path), "LSP: Client is opening already opened file.");
|
||||
|
||||
client->managed_files[path] = document;
|
||||
client->parse_script(path);
|
||||
}
|
||||
|
||||
void GDScriptLanguageProtocol::lsp_did_change(const Dictionary &p_params) {
|
||||
LSP_CLIENT;
|
||||
|
||||
LSP::TextDocumentIdentifier identifier;
|
||||
identifier.load(p_params["textDocument"]);
|
||||
|
||||
String path = get_workspace()->get_file_path(identifier.uri);
|
||||
LSP::TextDocumentItem *document = client->managed_files.getptr(path);
|
||||
|
||||
/// Before a client can change a text document it must claim ownership of its content using the textDocument/didOpen notification.
|
||||
ERR_FAIL_COND_MSG(document == nullptr, "LSP: Client is changing file without opening it.");
|
||||
|
||||
if (document->languageId != LSP::LanguageId::GDSCRIPT) {
|
||||
return;
|
||||
}
|
||||
|
||||
Array contentChanges = p_params["contentChanges"];
|
||||
|
||||
if (contentChanges.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We only support TextDocumentSyncKind::Full. So only the last full text is relevant.
|
||||
LSP::TextDocumentContentChangeEvent event;
|
||||
event.load(contentChanges.back());
|
||||
document->text = event.text;
|
||||
|
||||
client->parse_script(path);
|
||||
}
|
||||
|
||||
void GDScriptLanguageProtocol::lsp_did_close(const Dictionary &p_params) {
|
||||
LSP_CLIENT;
|
||||
|
||||
LSP::TextDocumentIdentifier identifier;
|
||||
identifier.load(p_params["textDocument"]);
|
||||
|
||||
String path = get_workspace()->get_file_path(identifier.uri);
|
||||
bool was_opened = client->managed_files.erase(path);
|
||||
|
||||
client->remove_cached_parser(path);
|
||||
|
||||
/// A close notification requires a previous open notification to be sent.
|
||||
ERR_FAIL_COND_MSG(!was_opened, "LSP: Client is closing file without opening it.");
|
||||
}
|
||||
|
||||
void GDScriptLanguageProtocol::resolve_related_symbols(const LSP::TextDocumentPositionParams &p_doc_pos, List<const LSP::DocumentSymbol *> &r_list) {
|
||||
LSP_CLIENT;
|
||||
|
||||
String path = workspace->get_file_path(p_doc_pos.textDocument.uri);
|
||||
|
||||
const ExtendGDScriptParser *parser = get_parse_result(path);
|
||||
if (!parser) {
|
||||
return;
|
||||
}
|
||||
|
||||
String symbol_identifier;
|
||||
LSP::Range range;
|
||||
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, range);
|
||||
|
||||
for (const KeyValue<StringName, ClassMembers> &E : workspace->native_members) {
|
||||
if (const LSP::DocumentSymbol *const *symbol = E.value.getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
}
|
||||
|
||||
for (const KeyValue<String, ExtendGDScriptParser *> &E : client->parse_results) {
|
||||
const ExtendGDScriptParser *scr = E.value;
|
||||
const ClassMembers &members = scr->get_members();
|
||||
if (const LSP::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
|
||||
for (const KeyValue<String, ClassMembers> &F : scr->get_inner_classes()) {
|
||||
const ClassMembers *inner_class = &F.value;
|
||||
if (const LSP::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GDScriptLanguageProtocol::LSPeer::~LSPeer() {
|
||||
while (!parse_results.is_empty()) {
|
||||
remove_cached_parser(parse_results.begin()->key);
|
||||
}
|
||||
while (!stale_parsers.is_empty()) {
|
||||
remove_cached_parser(stale_parsers.begin()->key);
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define SET_DOCUMENT_METHOD(m_method) set_method(_STR(textDocument/m_method), callable_mp(text_document.ptr(), &GDScriptTextDocument::m_method))
|
||||
#define SET_COMPLETION_METHOD(m_method) set_method(_STR(completionItem/m_method), callable_mp(text_document.ptr(), &GDScriptTextDocument::m_method))
|
||||
@@ -392,8 +571,6 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
|
||||
|
||||
SET_COMPLETION_METHOD(resolve);
|
||||
|
||||
SET_WORKSPACE_METHOD(didDeleteFiles);
|
||||
|
||||
set_method("initialize", callable_mp(this, &GDScriptLanguageProtocol::initialize));
|
||||
set_method("initialized", callable_mp(this, &GDScriptLanguageProtocol::initialized));
|
||||
|
||||
@@ -403,3 +580,6 @@ GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
|
||||
#undef SET_DOCUMENT_METHOD
|
||||
#undef SET_COMPLETION_METHOD
|
||||
#undef SET_WORKSPACE_METHOD
|
||||
|
||||
#undef LSP_CLIENT
|
||||
#undef LSP_CLIENT_V
|
||||
|
||||
@@ -41,9 +41,15 @@
|
||||
#define LSP_MAX_BUFFER_SIZE 4194304
|
||||
#define LSP_MAX_CLIENTS 8
|
||||
|
||||
#define LSP_NO_CLIENT -1
|
||||
|
||||
class GDScriptLanguageProtocol : public JSONRPC {
|
||||
GDCLASS(GDScriptLanguageProtocol, JSONRPC)
|
||||
|
||||
#ifdef TESTS_ENABLED
|
||||
friend class TestGDScriptLanguageProtocolInitializer;
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct LSPeer : RefCounted {
|
||||
Ref<StreamPeerTCP> connection;
|
||||
@@ -58,6 +64,24 @@ private:
|
||||
|
||||
Error handle_data();
|
||||
Error send_data();
|
||||
|
||||
/**
|
||||
* Tracks all files that the client claimed, however for files deemed not relevant
|
||||
* to the server the `text` might not be persisted.
|
||||
*/
|
||||
HashMap<String, LSP::TextDocumentItem> managed_files;
|
||||
HashMap<String, ExtendGDScriptParser *> parse_results;
|
||||
|
||||
void remove_cached_parser(const String &p_path);
|
||||
ExtendGDScriptParser *parse_script(const String &p_path);
|
||||
|
||||
~LSPeer();
|
||||
|
||||
private:
|
||||
// We can't cache parsers for scripts not managed by the editor since we have
|
||||
// no way to invalidate the cache. We still need to keep track of those parsers
|
||||
// to clean them up properly.
|
||||
HashMap<String, ExtendGDScriptParser *> stale_parsers;
|
||||
};
|
||||
|
||||
enum LSPErrorCode {
|
||||
@@ -69,7 +93,7 @@ private:
|
||||
|
||||
HashMap<int, Ref<LSPeer>> clients;
|
||||
Ref<TCPServer> server;
|
||||
int latest_client_id = 0;
|
||||
int latest_client_id = LSP_NO_CLIENT;
|
||||
int next_client_id = 0;
|
||||
|
||||
int next_server_id = 0;
|
||||
@@ -107,5 +131,27 @@ public:
|
||||
bool is_smart_resolve_enabled() const;
|
||||
bool is_goto_native_symbols_enabled() const;
|
||||
|
||||
// Text Document Synchronization
|
||||
void lsp_did_open(const Dictionary &p_params);
|
||||
void lsp_did_change(const Dictionary &p_params);
|
||||
void lsp_did_close(const Dictionary &p_params);
|
||||
|
||||
/**
|
||||
* Returns a list of symbols that might be related to the document position.
|
||||
*
|
||||
* The result fulfills no semantic guarantees, nor is it guaranteed to be complete.
|
||||
* Should only be used for "smart resolve".
|
||||
*/
|
||||
void resolve_related_symbols(const LSP::TextDocumentPositionParams &p_doc_pos, List<const LSP::DocumentSymbol *> &r_list);
|
||||
|
||||
/**
|
||||
* Returns parse results for the given path, using the cache if available.
|
||||
* If no such file exists, or the file is not a GDScript file a `nullptr` is returned.
|
||||
*/
|
||||
ExtendGDScriptParser *get_parse_result(const String &p_path);
|
||||
|
||||
GDScriptLanguageProtocol();
|
||||
~GDScriptLanguageProtocol() {
|
||||
clients.clear();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -63,29 +63,21 @@ void GDScriptTextDocument::_bind_methods() {
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::didOpen(const Variant &p_param) {
|
||||
LSP::TextDocumentItem doc = load_document_item(p_param);
|
||||
sync_script_content(doc.uri, doc.text);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::didClose(const Variant &p_param) {
|
||||
// Left empty on purpose. Godot does nothing special on closing a document,
|
||||
// but it satisfies LSP clients that require didClose be implemented.
|
||||
GDScriptLanguageProtocol::get_singleton()->lsp_did_open(p_param);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::didChange(const Variant &p_param) {
|
||||
LSP::TextDocumentItem doc = load_document_item(p_param);
|
||||
Dictionary dict = p_param;
|
||||
Array contentChanges = dict["contentChanges"];
|
||||
for (int i = 0; i < contentChanges.size(); ++i) {
|
||||
LSP::TextDocumentContentChangeEvent evt;
|
||||
evt.load(contentChanges[i]);
|
||||
doc.text = evt.text;
|
||||
}
|
||||
sync_script_content(doc.uri, doc.text);
|
||||
GDScriptLanguageProtocol::get_singleton()->lsp_did_change(p_param);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::didClose(const Variant &p_param) {
|
||||
GDScriptLanguageProtocol::get_singleton()->lsp_did_close(p_param);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::willSaveWaitUntil(const Variant &p_param) {
|
||||
LSP::TextDocumentItem doc = load_document_item(p_param);
|
||||
Dictionary dict = p_param;
|
||||
LSP::TextDocumentIdentifier doc;
|
||||
doc.load(dict["textDocument"]);
|
||||
|
||||
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
|
||||
Ref<Script> scr = ResourceLoader::load(path);
|
||||
@@ -95,12 +87,11 @@ void GDScriptTextDocument::willSaveWaitUntil(const Variant &p_param) {
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::didSave(const Variant &p_param) {
|
||||
LSP::TextDocumentItem doc = load_document_item(p_param);
|
||||
Dictionary dict = p_param;
|
||||
LSP::TextDocumentIdentifier doc;
|
||||
doc.load(dict["textDocument"]);
|
||||
String text = dict["text"];
|
||||
|
||||
sync_script_content(doc.uri, text);
|
||||
|
||||
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
|
||||
Ref<GDScript> scr = ResourceLoader::load(path);
|
||||
if (scr.is_valid() && (scr->load_source_code(path) == OK)) {
|
||||
@@ -126,13 +117,6 @@ void GDScriptTextDocument::reload_script(Ref<GDScript> p_to_reload_script) {
|
||||
ScriptEditor::get_singleton()->trigger_live_script_reload(p_to_reload_script->get_path());
|
||||
}
|
||||
|
||||
LSP::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
|
||||
LSP::TextDocumentItem doc;
|
||||
Dictionary params = p_param;
|
||||
doc.load(params["textDocument"]);
|
||||
return doc;
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::notify_client_show_symbol(const LSP::DocumentSymbol *symbol) {
|
||||
ERR_FAIL_NULL(symbol);
|
||||
GDScriptLanguageProtocol::get_singleton()->notify_client("gdscript/show_native_symbol", symbol->to_json(true));
|
||||
@@ -172,8 +156,10 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
|
||||
String uri = params["uri"];
|
||||
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri);
|
||||
Array arr;
|
||||
if (HashMap<String, ExtendGDScriptParser *>::ConstIterator parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
|
||||
LSP::DocumentSymbol symbol = parser->value->get_symbols();
|
||||
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
if (parser) {
|
||||
LSP::DocumentSymbol symbol = parser->get_symbols();
|
||||
arr.push_back(symbol.to_json(true));
|
||||
}
|
||||
return arr;
|
||||
@@ -324,8 +310,9 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
|
||||
}
|
||||
|
||||
if (!symbol) {
|
||||
if (HashMap<String, ExtendGDScriptParser *>::ConstIterator E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
|
||||
symbol = E->value->get_member_symbol(member_name, inner_class_name);
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(class_name);
|
||||
if (parser) {
|
||||
symbol = parser->get_member_symbol(member_name, inner_class_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -396,7 +383,7 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
|
||||
Dictionary ret;
|
||||
Array contents;
|
||||
List<const LSP::DocumentSymbol *> list;
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
|
||||
GDScriptLanguageProtocol::get_singleton()->resolve_related_symbols(params, list);
|
||||
for (const LSP::DocumentSymbol *&E : list) {
|
||||
if (const LSP::DocumentSymbol *s = E) {
|
||||
contents.push_back(s->render().value);
|
||||
@@ -473,11 +460,6 @@ GDScriptTextDocument::GDScriptTextDocument() {
|
||||
file_checker = FileAccess::create(FileAccess::ACCESS_RESOURCES);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
|
||||
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
|
||||
}
|
||||
|
||||
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
|
||||
callable_mp(ScriptEditor::get_singleton(), &ScriptEditor::goto_help).call_deferred(p_symbol_id);
|
||||
|
||||
@@ -500,7 +482,7 @@ Array GDScriptTextDocument::find_symbols(const LSP::TextDocumentPositionParams &
|
||||
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);
|
||||
GDScriptLanguageProtocol::get_singleton()->resolve_related_symbols(p_location, list);
|
||||
for (const LSP::DocumentSymbol *&E : list) {
|
||||
if (const LSP::DocumentSymbol *s = E) {
|
||||
if (!s->uri.is_empty()) {
|
||||
|
||||
@@ -48,7 +48,6 @@ protected:
|
||||
|
||||
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);
|
||||
|
||||
public:
|
||||
@@ -59,7 +58,6 @@ public:
|
||||
void didSave(const Variant &p_param);
|
||||
|
||||
void reload_script(Ref<GDScript> p_to_reload_script);
|
||||
void sync_script_content(const String &p_path, const String &p_content);
|
||||
void show_native_symbol_in_editor(const String &p_symbol_id);
|
||||
|
||||
Variant nativeSymbol(const Dictionary &p_params);
|
||||
|
||||
@@ -45,13 +45,16 @@
|
||||
|
||||
void GDScriptWorkspace::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal);
|
||||
ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::didDeleteFiles);
|
||||
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
|
||||
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
|
||||
ClassDB::bind_method(D_METHOD("get_file_path", "uri"), &GDScriptWorkspace::get_file_path);
|
||||
ClassDB::bind_method(D_METHOD("get_file_uri", "path"), &GDScriptWorkspace::get_file_uri);
|
||||
ClassDB::bind_method(D_METHOD("publish_diagnostics", "path"), &GDScriptWorkspace::publish_diagnostics);
|
||||
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::didDeleteFiles);
|
||||
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
|
||||
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_script);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) {
|
||||
@@ -106,37 +109,6 @@ void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStr
|
||||
GDScriptLanguageProtocol::get_singleton()->request_client("workspace/applyEdit", params.to_json());
|
||||
}
|
||||
|
||||
void GDScriptWorkspace::didDeleteFiles(const Dictionary &p_params) {
|
||||
Array files = p_params["files"];
|
||||
for (int i = 0; i < files.size(); ++i) {
|
||||
Dictionary file = files[i];
|
||||
String uri = file["uri"];
|
||||
String path = get_file_path(uri);
|
||||
parse_script(path, "");
|
||||
}
|
||||
}
|
||||
|
||||
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator parser = parse_results.find(p_path);
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator scr = scripts.find(p_path);
|
||||
if (parser && scr) {
|
||||
if (scr->value && scr->value == parser->value) {
|
||||
memdelete(scr->value);
|
||||
} else {
|
||||
memdelete(scr->value);
|
||||
memdelete(parser->value);
|
||||
}
|
||||
parse_results.erase(p_path);
|
||||
scripts.erase(p_path);
|
||||
} else if (parser) {
|
||||
memdelete(parser->value);
|
||||
parse_results.erase(p_path);
|
||||
} else if (scr) {
|
||||
memdelete(scr->value);
|
||||
scripts.erase(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;
|
||||
@@ -168,9 +140,9 @@ const LSP::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
|
||||
}
|
||||
|
||||
const LSP::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const {
|
||||
HashMap<String, ExtendGDScriptParser *>::ConstIterator S = scripts.find(p_path);
|
||||
if (S) {
|
||||
return &(S->value->get_symbols());
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(p_path);
|
||||
if (parser) {
|
||||
return &(parser->get_symbols());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -219,18 +191,13 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
|
||||
List<String> paths;
|
||||
list_script_files("res://", paths);
|
||||
for (const String &path : paths) {
|
||||
Error err;
|
||||
String content = FileAccess::get_file_as_string(path, &err);
|
||||
ERR_CONTINUE(err != OK);
|
||||
err = parse_script(path, content);
|
||||
|
||||
if (err != OK) {
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(path);
|
||||
String err_msg = "Failed parse script " + path;
|
||||
if (S) {
|
||||
err_msg += "\n" + S->value->get_errors().front()->get().message;
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
if (parser == nullptr || parser->parse_result != OK) {
|
||||
String err_msg = "LSP: Failed to parse script: " + path;
|
||||
if (parser) {
|
||||
err_msg += "\n" + parser->get_errors().front()->get().message;
|
||||
}
|
||||
ERR_CONTINUE_MSG(err != OK, err_msg);
|
||||
ERR_PRINT(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,30 +227,6 @@ void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String>
|
||||
}
|
||||
}
|
||||
|
||||
ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String &p_path) {
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator S = scripts.find(p_path);
|
||||
if (!S) {
|
||||
parse_local_script(p_path);
|
||||
S = scripts.find(p_path);
|
||||
}
|
||||
if (S) {
|
||||
return S->value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) {
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(p_path);
|
||||
if (!S) {
|
||||
parse_local_script(p_path);
|
||||
S = parse_results.find(p_path);
|
||||
}
|
||||
if (S) {
|
||||
return S->value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define HANDLE_DOC(m_string) ((is_native ? DTR(m_string) : (m_string)).strip_edges())
|
||||
|
||||
Error GDScriptWorkspace::initialize() {
|
||||
@@ -427,11 +370,6 @@ Error GDScriptWorkspace::initialize() {
|
||||
}
|
||||
native_members.insert(E.key, members);
|
||||
}
|
||||
|
||||
// Cache member completions.
|
||||
for (const KeyValue<String, ExtendGDScriptParser *> &S : scripts) {
|
||||
S.value->get_member_completions();
|
||||
}
|
||||
}
|
||||
|
||||
EditorNode *editor_node = EditorNode::get_singleton();
|
||||
@@ -440,29 +378,6 @@ Error GDScriptWorkspace::initialize() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) {
|
||||
ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
|
||||
Error err = parser->parse(p_content, p_path);
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator last_parser = parse_results.find(p_path);
|
||||
HashMap<String, ExtendGDScriptParser *>::Iterator last_script = scripts.find(p_path);
|
||||
|
||||
if (err == OK) {
|
||||
remove_cache_parser(p_path);
|
||||
parse_results[p_path] = parser;
|
||||
scripts[p_path] = parser;
|
||||
|
||||
} else {
|
||||
if (last_parser && last_script && last_parser->value != last_script->value) {
|
||||
memdelete(last_parser->value);
|
||||
}
|
||||
parse_results[p_path] = parser;
|
||||
}
|
||||
|
||||
publish_diagnostics(p_path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool is_valid_rename_target(const LSP::DocumentSymbol *p_symbol) {
|
||||
// Must be valid symbol.
|
||||
if (!p_symbol) {
|
||||
@@ -505,8 +420,8 @@ bool GDScriptWorkspace::can_rename(const LSP::TextDocumentPositionParams &p_doc_
|
||||
}
|
||||
|
||||
String path = get_file_path(p_doc_pos.textDocument.uri);
|
||||
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
|
||||
// We only care about the range.
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
if (parser) {
|
||||
_ALLOW_DISCARD_ parser->get_identifier_under_position(p_doc_pos.position, r_range);
|
||||
r_symbol = *reference_symbol;
|
||||
return true;
|
||||
@@ -519,7 +434,8 @@ Vector<LSP::Location> GDScriptWorkspace::find_usages_in_file(const LSP::Document
|
||||
Vector<LSP::Location> usages;
|
||||
|
||||
String identifier = p_symbol.name;
|
||||
if (const ExtendGDScriptParser *parser = get_parse_result(p_file_path)) {
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(p_file_path);
|
||||
if (parser) {
|
||||
const PackedStringArray &content = parser->get_lines();
|
||||
for (int i = 0; i < content.size(); ++i) {
|
||||
String line = content[i];
|
||||
@@ -570,15 +486,6 @@ Vector<LSP::Location> GDScriptWorkspace::find_all_usages(const LSP::DocumentSymb
|
||||
return usages;
|
||||
}
|
||||
|
||||
Error GDScriptWorkspace::parse_local_script(const String &p_path) {
|
||||
Error err;
|
||||
String content = FileAccess::get_file_as_string(p_path, &err);
|
||||
if (err == OK) {
|
||||
err = parse_script(p_path, content);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
String GDScriptWorkspace::get_file_path(const String &p_uri) {
|
||||
int port;
|
||||
String scheme;
|
||||
@@ -671,9 +578,10 @@ String GDScriptWorkspace::get_file_uri(const String &p_path) const {
|
||||
void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
|
||||
Dictionary params;
|
||||
Array errors;
|
||||
HashMap<String, ExtendGDScriptParser *>::ConstIterator ele = parse_results.find(p_path);
|
||||
if (ele) {
|
||||
const Vector<LSP::Diagnostic> &list = ele->value->get_diagnostics();
|
||||
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(p_path);
|
||||
if (parser) {
|
||||
const Vector<LSP::Diagnostic> &list = parser->get_diagnostics();
|
||||
errors.resize(list.size());
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
errors[i] = list[i].to_json();
|
||||
@@ -734,7 +642,8 @@ void GDScriptWorkspace::completion(const LSP::CompletionParams &p_params, List<S
|
||||
String call_hint;
|
||||
bool forced = false;
|
||||
|
||||
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
if (parser) {
|
||||
Node *owner_scene_node = _get_owner_scene_node(path);
|
||||
|
||||
Array stack;
|
||||
@@ -771,7 +680,9 @@ const LSP::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const LSP::TextDocu
|
||||
const LSP::DocumentSymbol *symbol = nullptr;
|
||||
|
||||
String path = get_file_path(p_doc_pos.textDocument.uri);
|
||||
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
|
||||
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
if (parser) {
|
||||
String symbol_identifier = p_symbol_name;
|
||||
if (symbol_identifier.get_slice_count("(") > 0) {
|
||||
symbol_identifier = symbol_identifier.get_slicec('(', 0);
|
||||
@@ -803,7 +714,8 @@ const LSP::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const LSP::TextDocu
|
||||
target_script_path = ret.script_path;
|
||||
}
|
||||
|
||||
if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {
|
||||
const ExtendGDScriptParser *target_parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(target_script_path);
|
||||
if (target_parser) {
|
||||
symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location), symbol_identifier);
|
||||
|
||||
if (symbol) {
|
||||
@@ -836,37 +748,6 @@ const LSP::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const LSP::TextDocu
|
||||
return symbol;
|
||||
}
|
||||
|
||||
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;
|
||||
LSP::Range range;
|
||||
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, range);
|
||||
|
||||
for (const KeyValue<StringName, ClassMembers> &E : native_members) {
|
||||
const ClassMembers &members = native_members.get(E.key);
|
||||
if (const LSP::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
}
|
||||
|
||||
for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
|
||||
const ExtendGDScriptParser *scr = E.value;
|
||||
const ClassMembers &members = scr->get_members();
|
||||
if (const LSP::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
|
||||
for (const KeyValue<String, ClassMembers> &F : scr->get_inner_classes()) {
|
||||
const ClassMembers *inner_class = &F.value;
|
||||
if (const LSP::DocumentSymbol *const *symbol = inner_class->getptr(symbol_identifier)) {
|
||||
r_list.push_back(*symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LSP::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const LSP::NativeSymbolInspectParams &p_params) {
|
||||
if (HashMap<StringName, LSP::DocumentSymbol>::Iterator E = native_symbols.find(p_params.native_class)) {
|
||||
const LSP::DocumentSymbol &symbol = E->value;
|
||||
@@ -885,7 +766,8 @@ const LSP::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const LSP::N
|
||||
}
|
||||
|
||||
void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<LSP::DocumentLink> &r_list) {
|
||||
if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) {
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(get_file_path(p_uri));
|
||||
if (parser && parser->parse_result == Error::OK) {
|
||||
const List<LSP::DocumentLink> &links = parser->get_document_links();
|
||||
for (const LSP::DocumentLink &E : links) {
|
||||
r_list.push_back(E);
|
||||
@@ -895,14 +777,17 @@ void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<LSP::Do
|
||||
|
||||
Dictionary GDScriptWorkspace::generate_script_api(const String &p_path) {
|
||||
Dictionary api;
|
||||
if (const ExtendGDScriptParser *parser = get_parse_successed_script(p_path)) {
|
||||
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(p_path);
|
||||
if (parser) {
|
||||
api = parser->generate_api();
|
||||
}
|
||||
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))) {
|
||||
const ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(get_file_path(p_doc_pos.textDocument.uri));
|
||||
if (parser) {
|
||||
LSP::TextDocumentPositionParams text_pos;
|
||||
text_pos.textDocument = p_doc_pos.textDocument;
|
||||
|
||||
@@ -912,7 +797,7 @@ Error GDScriptWorkspace::resolve_signature(const LSP::TextDocumentPositionParams
|
||||
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);
|
||||
GDScriptLanguageProtocol::get_singleton()->resolve_related_symbols(text_pos, symbols);
|
||||
}
|
||||
|
||||
for (const LSP::DocumentSymbol *const &symbol : symbols) {
|
||||
@@ -942,18 +827,4 @@ Error GDScriptWorkspace::resolve_signature(const LSP::TextDocumentPositionParams
|
||||
|
||||
GDScriptWorkspace::GDScriptWorkspace() {}
|
||||
|
||||
GDScriptWorkspace::~GDScriptWorkspace() {
|
||||
HashSet<String> cached_parsers;
|
||||
|
||||
for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) {
|
||||
cached_parsers.insert(E.key);
|
||||
}
|
||||
|
||||
for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
|
||||
cached_parsers.insert(E.key);
|
||||
}
|
||||
|
||||
for (const String &E : cached_parsers) {
|
||||
remove_cache_parser(E);
|
||||
}
|
||||
}
|
||||
GDScriptWorkspace::~GDScriptWorkspace() {}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../gdscript_parser.h"
|
||||
#include "core/error/error_macros.h"
|
||||
#include "gdscript_extend_parser.h"
|
||||
#include "godot_lsp.h"
|
||||
|
||||
@@ -44,9 +44,20 @@ private:
|
||||
void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners);
|
||||
Node *_get_owner_scene_node(String p_path);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void didDeleteFiles() {}
|
||||
Error parse_script(const String &p_path, const String &p_content) {
|
||||
WARN_DEPRECATED;
|
||||
return Error::FAILED;
|
||||
}
|
||||
Error parse_local_script(const String &p_path) {
|
||||
WARN_DEPRECATED;
|
||||
return Error::FAILED;
|
||||
}
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void remove_cache_parser(const String &p_path);
|
||||
bool initialized = false;
|
||||
HashMap<StringName, LSP::DocumentSymbol> native_symbols;
|
||||
|
||||
@@ -60,9 +71,6 @@ protected:
|
||||
|
||||
void reload_all_workspace_scripts();
|
||||
|
||||
ExtendGDScriptParser *get_parse_successed_script(const String &p_path);
|
||||
ExtendGDScriptParser *get_parse_result(const String &p_path);
|
||||
|
||||
void list_script_files(const String &p_root_dir, List<String> &r_files);
|
||||
|
||||
void apply_new_signal(Object *obj, String function, PackedStringArray args);
|
||||
@@ -71,16 +79,11 @@ public:
|
||||
String root;
|
||||
String root_uri;
|
||||
|
||||
HashMap<String, ExtendGDScriptParser *> scripts;
|
||||
HashMap<String, ExtendGDScriptParser *> parse_results;
|
||||
HashMap<StringName, ClassMembers> native_members;
|
||||
|
||||
public:
|
||||
Error initialize();
|
||||
|
||||
Error parse_script(const String &p_path, const String &p_content);
|
||||
Error parse_local_script(const String &p_path);
|
||||
|
||||
String get_file_path(const String &p_uri);
|
||||
String get_file_uri(const String &p_path) const;
|
||||
|
||||
@@ -88,12 +91,11 @@ public:
|
||||
void completion(const LSP::CompletionParams &p_params, List<ScriptLanguage::CodeCompletionOption> *r_options);
|
||||
|
||||
const LSP::DocumentSymbol *resolve_symbol(const LSP::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_required = false);
|
||||
void resolve_related_symbols(const LSP::TextDocumentPositionParams &p_doc_pos, List<const LSP::DocumentSymbol *> &r_list);
|
||||
|
||||
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);
|
||||
void didDeleteFiles(const Dictionary &p_params);
|
||||
Dictionary rename(const LSP::TextDocumentPositionParams &p_doc_pos, const String &new_name);
|
||||
bool can_rename(const LSP::TextDocumentPositionParams &p_doc_pos, LSP::DocumentSymbol &r_symbol, LSP::Range &r_range);
|
||||
Vector<LSP::Location> find_usages_in_file(const LSP::DocumentSymbol &p_symbol, const String &p_file_path);
|
||||
|
||||
@@ -663,6 +663,11 @@ struct DocumentOnTypeFormattingOptions {
|
||||
}
|
||||
};
|
||||
|
||||
enum class LanguageId {
|
||||
GDSCRIPT,
|
||||
OTHER,
|
||||
};
|
||||
|
||||
struct TextDocumentItem {
|
||||
/**
|
||||
* The text document's URI.
|
||||
@@ -672,7 +677,7 @@ struct TextDocumentItem {
|
||||
/**
|
||||
* The text document's language identifier.
|
||||
*/
|
||||
String languageId;
|
||||
LanguageId languageId;
|
||||
|
||||
/**
|
||||
* The version number of this document (it will increase after each
|
||||
@@ -687,18 +692,17 @@ struct TextDocumentItem {
|
||||
|
||||
void load(const Dictionary &p_dict) {
|
||||
uri = p_dict["uri"];
|
||||
languageId = p_dict.get("languageId", "");
|
||||
version = p_dict.get("version", 0);
|
||||
text = p_dict.get("text", "");
|
||||
}
|
||||
version = p_dict["version"];
|
||||
text = p_dict["text"];
|
||||
|
||||
Dictionary to_json() const {
|
||||
Dictionary dict;
|
||||
dict["uri"] = uri;
|
||||
dict["languageId"] = languageId;
|
||||
dict["version"] = version;
|
||||
dict["text"] = text;
|
||||
return dict;
|
||||
// Clients should use "gdscript" as language id, but we can't enforce it. The Rider integration
|
||||
// in particular uses "gd" at the time of writing. We normalize the id to make it easier to work with.
|
||||
String rawLanguageId = p_dict["languageId"];
|
||||
if (rawLanguageId == "gdscript" || rawLanguageId == "gd") {
|
||||
languageId = LanguageId::GDSCRIPT;
|
||||
} else {
|
||||
languageId = LanguageId::OTHER;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1702,16 +1706,8 @@ struct FileOperations {
|
||||
* Workspace specific server capabilities
|
||||
*/
|
||||
struct Workspace {
|
||||
/**
|
||||
* The server is interested in file notifications/requests.
|
||||
*/
|
||||
FileOperations fileOperations;
|
||||
|
||||
Dictionary to_json() const {
|
||||
Dictionary dict;
|
||||
|
||||
dict["fileOperations"] = fileOperations.to_json();
|
||||
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,6 +54,17 @@
|
||||
|
||||
#include "thirdparty/doctest/doctest.h"
|
||||
|
||||
class TestGDScriptLanguageProtocolInitializer {
|
||||
public:
|
||||
static void setup_client() {
|
||||
GDScriptLanguageProtocol *proto = GDScriptLanguageProtocol::get_singleton();
|
||||
Ref<GDScriptLanguageProtocol::LSPeer> peer = memnew(GDScriptLanguageProtocol::LSPeer);
|
||||
proto->clients.insert(proto->next_client_id, peer);
|
||||
proto->latest_client_id = proto->next_client_id;
|
||||
proto->next_client_id++;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct doctest::StringMaker<LSP::Position> {
|
||||
static doctest::String convert(const LSP::Position &p_val) {
|
||||
@@ -96,6 +107,7 @@ GDScriptLanguageProtocol *initialize(const String &p_root) {
|
||||
init_language(absolute_root);
|
||||
|
||||
GDScriptLanguageProtocol *proto = memnew(GDScriptLanguageProtocol);
|
||||
TestGDScriptLanguageProtocolInitializer::setup_client();
|
||||
|
||||
Ref<GDScriptWorkspace> workspace = GDScriptLanguageProtocol::get_singleton()->get_workspace();
|
||||
workspace->root = absolute_root;
|
||||
@@ -502,8 +514,7 @@ func f():
|
||||
|
||||
for (const String &path : paths) {
|
||||
assert_no_errors_in(path);
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_local_script(path);
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_results[path];
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
REQUIRE(parser);
|
||||
LSP::DocumentSymbol cls = parser->get_symbols();
|
||||
|
||||
@@ -515,8 +526,7 @@ func f():
|
||||
SUBCASE("Documentation is correctly set") {
|
||||
String path = "res://lsp/doc_comments.gd";
|
||||
assert_no_errors_in(path);
|
||||
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_local_script(path);
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_results[path];
|
||||
ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_parse_result(path);
|
||||
REQUIRE(parser);
|
||||
LSP::DocumentSymbol cls = parser->get_symbols();
|
||||
REQUIRE(cls.documentation.contains("brief"));
|
||||
|
||||
Reference in New Issue
Block a user