LanguageClient.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "LanguageClient.h"
  27. #include "HackStudio.h"
  28. #include "Locator.h"
  29. #include <AK/String.h>
  30. #include <AK/Vector.h>
  31. #include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h>
  32. #include <LibGUI/Notification.h>
  33. namespace HackStudio {
  34. void ServerConnection::handle(const Messages::LanguageClient::AutoCompleteSuggestions& message)
  35. {
  36. if (!m_language_client) {
  37. dbgln("Language Server connection has no attached language client");
  38. return;
  39. }
  40. m_language_client->provide_autocomplete_suggestions(message.suggestions());
  41. }
  42. void ServerConnection::handle(const Messages::LanguageClient::DeclarationLocation& message)
  43. {
  44. if (!m_language_client) {
  45. dbgln("Language Server connection has no attached language client");
  46. return;
  47. }
  48. m_language_client->declaration_found(message.location().file, message.location().line, message.location().column);
  49. }
  50. void ServerConnection::die()
  51. {
  52. dbgln("ServerConnection::die()");
  53. if (!m_language_client)
  54. return;
  55. m_language_client->on_server_crash();
  56. }
  57. void LanguageClient::open_file(const String& path, int fd)
  58. {
  59. if (!m_server_connection)
  60. return;
  61. m_server_connection->post_message(Messages::LanguageServer::FileOpened(path, fd));
  62. }
  63. void LanguageClient::set_file_content(const String& path, const String& content)
  64. {
  65. if (!m_server_connection)
  66. return;
  67. m_server_connection->post_message(Messages::LanguageServer::SetFileContent(path, content));
  68. }
  69. void LanguageClient::insert_text(const String& path, const String& text, size_t line, size_t column)
  70. {
  71. if (!m_server_connection)
  72. return;
  73. m_server_connection->post_message(Messages::LanguageServer::FileEditInsertText(path, text, line, column));
  74. }
  75. void LanguageClient::remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column)
  76. {
  77. if (!m_server_connection)
  78. return;
  79. m_server_connection->post_message(Messages::LanguageServer::FileEditRemoveText(path, from_line, from_column, to_line, to_column));
  80. }
  81. void LanguageClient::request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column)
  82. {
  83. if (!m_server_connection)
  84. return;
  85. set_active_client();
  86. m_server_connection->post_message(Messages::LanguageServer::AutoCompleteSuggestions(GUI::AutocompleteProvider::ProjectLocation { path, cursor_line, cursor_column }));
  87. }
  88. void LanguageClient::provide_autocomplete_suggestions(const Vector<GUI::AutocompleteProvider::Entry>& suggestions)
  89. {
  90. if (on_autocomplete_suggestions)
  91. on_autocomplete_suggestions(suggestions);
  92. // Otherwise, drop it on the floor :shrug:
  93. }
  94. void LanguageClient::set_autocomplete_mode(const String& mode)
  95. {
  96. if (!m_server_connection)
  97. return;
  98. m_server_connection->post_message(Messages::LanguageServer::SetAutoCompleteMode(mode));
  99. }
  100. void LanguageClient::set_active_client()
  101. {
  102. if (!m_server_connection)
  103. return;
  104. m_server_connection->attach(*this);
  105. }
  106. void LanguageClient::on_server_crash()
  107. {
  108. VERIFY(m_server_connection);
  109. auto project_path = m_server_connection->project_path();
  110. ServerConnection::remove_instance_for_project(project_path);
  111. m_server_connection = nullptr;
  112. auto notification = GUI::Notification::construct();
  113. notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-hack-studio.png"));
  114. notification->set_title("Oops!");
  115. notification->set_text(String::formatted("LanguageServer for {} crashed", project_path));
  116. notification->show();
  117. }
  118. HashMap<String, NonnullRefPtr<ServerConnection>> ServerConnection::s_instances_for_projects;
  119. RefPtr<ServerConnection> ServerConnection::instance_for_project(const String& project_path)
  120. {
  121. auto key = LexicalPath { project_path }.string();
  122. auto value = s_instances_for_projects.get(key);
  123. if (!value.has_value())
  124. return nullptr;
  125. return *value.value();
  126. }
  127. void ServerConnection::set_instance_for_project(const String& project_path, NonnullRefPtr<ServerConnection>&& instance)
  128. {
  129. auto key = LexicalPath { project_path }.string();
  130. s_instances_for_projects.set(key, move(instance));
  131. }
  132. void ServerConnection::remove_instance_for_project(const String& project_path)
  133. {
  134. auto key = LexicalPath { project_path }.string();
  135. s_instances_for_projects.remove(key);
  136. }
  137. void ServerConnection::handle(const Messages::LanguageClient::DeclarationsInDocument& message)
  138. {
  139. locator().set_declared_symbols(message.filename(), message.declarations());
  140. }
  141. void LanguageClient::search_declaration(const String& path, size_t line, size_t column)
  142. {
  143. if (!m_server_connection)
  144. return;
  145. set_active_client();
  146. m_server_connection->post_message(Messages::LanguageServer::FindDeclaration(GUI::AutocompleteProvider::ProjectLocation { path, line, column }));
  147. }
  148. void LanguageClient::declaration_found(const String& file, size_t line, size_t column)
  149. {
  150. if (!on_declaration_found) {
  151. dbgln("on_declaration_found callback is not set");
  152. return;
  153. }
  154. on_declaration_found(file, line, column);
  155. }
  156. }