diff options
author | Itamar <itamar8910@gmail.com> | 2021-02-13 09:47:08 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-02-13 19:50:09 +0100 |
commit | 715933ce8bac46abbd4f27c6ec6f869aafb04db5 (patch) | |
tree | 5a72e17e8e70fd55ac9dcedc7a56c13c586ad700 /Userland/DevTools | |
parent | 8ace2cfa1848ad4402b0c8ffa1875d6d9bffec10 (diff) | |
download | serenity-715933ce8bac46abbd4f27c6ec6f869aafb04db5.zip |
HackStudio: Handle crash of the LanguageServer gracefully
Previously, HackStudio exited whenever a LanguageServer crashed.
Now, we disconnect all clients from that language server instance and
show a nice notification.
Diffstat (limited to 'Userland/DevTools')
-rw-r--r-- | Userland/DevTools/HackStudio/LanguageClient.cpp | 77 | ||||
-rw-r--r-- | Userland/DevTools/HackStudio/LanguageClient.h | 28 |
2 files changed, 88 insertions, 17 deletions
diff --git a/Userland/DevTools/HackStudio/LanguageClient.cpp b/Userland/DevTools/HackStudio/LanguageClient.cpp index f320bf08e4..7c16305dc3 100644 --- a/Userland/DevTools/HackStudio/LanguageClient.cpp +++ b/Userland/DevTools/HackStudio/LanguageClient.cpp @@ -28,6 +28,7 @@ #include <AK/String.h> #include <AK/Vector.h> #include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h> +#include <LibGUI/Notification.h> namespace HackStudio { @@ -40,34 +41,54 @@ void ServerConnection::handle(const Messages::LanguageClient::AutoCompleteSugges m_language_client->provide_autocomplete_suggestions(message.suggestions()); } +void ServerConnection::die() +{ + dbgln("ServerConnection::die()"); + if (!m_language_client) + return; + m_language_client->on_server_crash(); +} + void LanguageClient::open_file(const String& path, int fd) { - m_connection.post_message(Messages::LanguageServer::FileOpened(path, fd)); + if (!m_server_connection) + return; + m_server_connection->post_message(Messages::LanguageServer::FileOpened(path, fd)); } void LanguageClient::set_file_content(const String& path, const String& content) { - m_connection.post_message(Messages::LanguageServer::SetFileContent(path, content)); + if (!m_server_connection) + return; + m_server_connection->post_message(Messages::LanguageServer::SetFileContent(path, content)); } void LanguageClient::insert_text(const String& path, const String& text, size_t line, size_t column) { - m_connection.post_message(Messages::LanguageServer::FileEditInsertText(path, text, line, column)); + if (!m_server_connection) + return; + m_server_connection->post_message(Messages::LanguageServer::FileEditInsertText(path, text, line, column)); } void LanguageClient::remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column) { - m_connection.post_message(Messages::LanguageServer::FileEditRemoveText(path, from_line, from_column, to_line, to_column)); + if (!m_server_connection) + return; + m_server_connection->post_message(Messages::LanguageServer::FileEditRemoveText(path, from_line, from_column, to_line, to_column)); } void LanguageClient::request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column) { + if (!m_server_connection) + return; set_active_client(); - m_connection.post_message(Messages::LanguageServer::AutoCompleteSuggestions(path, cursor_line, cursor_column)); + m_server_connection->post_message(Messages::LanguageServer::AutoCompleteSuggestions(path, cursor_line, cursor_column)); } void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>& suggestions) { + if (!m_server_connection) + return; if (on_autocomplete_suggestions) on_autocomplete_suggestions(suggestions); @@ -76,12 +97,54 @@ void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::Autocomp void LanguageClient::set_autocomplete_mode(const String& mode) { - m_connection.post_message(Messages::LanguageServer::SetAutoCompleteMode(mode)); + if (!m_server_connection) + return; + m_server_connection->post_message(Messages::LanguageServer::SetAutoCompleteMode(mode)); } void LanguageClient::set_active_client() { - m_connection.attach(*this); + if (!m_server_connection) + return; + m_server_connection->attach(*this); +} + +void LanguageClient::on_server_crash() +{ + ASSERT(m_server_connection); + auto project_path = m_server_connection->projcet_path(); + ServerConnection::remove_instance_for_project(project_path); + m_server_connection = nullptr; + + auto notification = GUI::Notification::construct(); + + notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-hack-studio.png")); + notification->set_title("Oops!"); + notification->set_text(String::formatted("LanguageServer for {} crashed", project_path)); + notification->show(); +} + +HashMap<String, NonnullRefPtr<ServerConnection>> ServerConnection::s_instances_for_projects; + +RefPtr<ServerConnection> ServerConnection::instance_for_project(const String& project_path) +{ + auto key = LexicalPath { project_path }.string(); + auto value = s_instances_for_projects.get(key); + if (!value.has_value()) + return nullptr; + return *value.value(); +} + +void ServerConnection::set_instance_for_project(const String& project_path, NonnullRefPtr<ServerConnection>&& instance) +{ + auto key = LexicalPath { project_path }.string(); + s_instances_for_projects.set(key, move(instance)); +} + +void ServerConnection::remove_instance_for_project(const String& project_path) +{ + auto key = LexicalPath { project_path }.string(); + s_instances_for_projects.remove(key); } } diff --git a/Userland/DevTools/HackStudio/LanguageClient.h b/Userland/DevTools/HackStudio/LanguageClient.h index b5a4085bf5..2125219c9a 100644 --- a/Userland/DevTools/HackStudio/LanguageClient.h +++ b/Userland/DevTools/HackStudio/LanguageClient.h @@ -67,45 +67,53 @@ public: } WeakPtr<LanguageClient> language_client() { return m_language_client; } + const String& projcet_path() const { return m_project_path; } template<typename ConcreteType> static NonnullRefPtr<ServerConnection> get_or_create(const String& project_path) { - static HashMap<String, NonnullRefPtr<ConcreteType>> s_instances_for_projects; auto key = LexicalPath { project_path }.string(); if (auto instance = s_instances_for_projects.get(key); instance.has_value()) return *instance.value(); auto connection = ConcreteType::construct(project_path); connection->handshake(); - s_instances_for_projects.set(key, *connection); + set_instance_for_project(project_path, *connection); return *connection; } + static RefPtr<ServerConnection> instance_for_project(const String& project_path); + static void set_instance_for_project(const String& project_path, NonnullRefPtr<ServerConnection>&&); + static void remove_instance_for_project(const String& project_path); + + virtual void die(); + protected: virtual void handle(const Messages::LanguageClient::AutoCompleteSuggestions&) override; String m_project_path; WeakPtr<LanguageClient> m_language_client; + +private: + static HashMap<String, NonnullRefPtr<ServerConnection>> s_instances_for_projects; }; class LanguageClient : public Weakable<LanguageClient> { public: explicit LanguageClient(NonnullRefPtr<ServerConnection>&& connection) - : m_connection(*connection) - , m_server_connection(move(connection)) + : m_server_connection(move(connection)) { - m_previous_client = m_connection.language_client(); + m_previous_client = m_server_connection->language_client(); ASSERT(m_previous_client.ptr() != this); - m_connection.attach(*this); + m_server_connection->attach(*this); } virtual ~LanguageClient() { - m_connection.detach(); + m_server_connection->detach(); ASSERT(m_previous_client.ptr() != this); if (m_previous_client) - m_connection.attach(*m_previous_client); + m_server_connection->attach(*m_previous_client); } void set_active_client(); @@ -117,12 +125,12 @@ public: virtual void set_autocomplete_mode(const String& mode); void provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>&); + void on_server_crash(); Function<void(Vector<GUI::AutocompleteProvider::Entry>)> on_autocomplete_suggestions; private: - ServerConnection& m_connection; - NonnullRefPtr<ServerConnection> m_server_connection; + WeakPtr<ServerConnection> m_server_connection; WeakPtr<LanguageClient> m_previous_client; }; |