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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user