Serenity Operating System
1/*
2 * Copyright (c) 2020, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include "AutoCompleteResponse.h"
10#include <AK/Forward.h>
11#include <AK/LexicalPath.h>
12#include <AK/Types.h>
13#include <AK/WeakPtr.h>
14#include <AK/Weakable.h>
15#include <LibCore/ElapsedTimer.h>
16#include <LibCpp/Preprocessor.h>
17#include <LibIPC/ConnectionToServer.h>
18#include <LibSyntax/Language.h>
19
20#include <DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h>
21#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h>
22
23namespace HackStudio {
24
25class LanguageClient;
26class ConnectionToServerWrapper;
27
28class ConnectionToServer
29 : public IPC::ConnectionToServer<LanguageClientEndpoint, LanguageServerEndpoint>
30 , public LanguageClientEndpoint {
31 friend class ConnectionToServerWrapper;
32
33public:
34 ConnectionToServer(NonnullOwnPtr<Core::LocalSocket> socket, DeprecatedString const& project_path)
35 : IPC::ConnectionToServer<LanguageClientEndpoint, LanguageServerEndpoint>(*this, move(socket))
36 {
37 m_project_path = project_path;
38 async_greet(m_project_path);
39 }
40
41 WeakPtr<LanguageClient> language_client() { return m_current_language_client; }
42 DeprecatedString const& project_path() const { return m_project_path; }
43
44 virtual void die() override;
45
46 LanguageClient const* active_client() const { return !m_current_language_client ? nullptr : m_current_language_client.ptr(); }
47
48protected:
49 virtual void auto_complete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const&) override;
50 virtual void declaration_location(CodeComprehension::ProjectLocation const&) override;
51 virtual void declarations_in_document(DeprecatedString const&, Vector<CodeComprehension::Declaration> const&) override;
52 virtual void todo_entries_in_document(DeprecatedString const&, Vector<CodeComprehension::TodoEntry> const&) override;
53 virtual void parameters_hint_result(Vector<DeprecatedString> const&, int index) override;
54 virtual void tokens_info_result(Vector<CodeComprehension::TokenInfo> const&) override;
55 void set_wrapper(ConnectionToServerWrapper& wrapper) { m_wrapper = &wrapper; }
56
57 DeprecatedString m_project_path;
58 WeakPtr<LanguageClient> m_current_language_client;
59 ConnectionToServerWrapper* m_wrapper { nullptr };
60};
61
62class ConnectionToServerWrapper {
63 AK_MAKE_NONCOPYABLE(ConnectionToServerWrapper);
64
65public:
66 explicit ConnectionToServerWrapper(DeprecatedString const& language_name, Function<NonnullRefPtr<ConnectionToServer>()> connection_creator);
67 ~ConnectionToServerWrapper() = default;
68
69 template<typename LanguageServerType>
70 static ConnectionToServerWrapper& get_or_create(DeprecatedString const& project_path);
71
72 Syntax::Language language() const { return m_language; }
73 ConnectionToServer* connection();
74 void on_crash();
75 void try_respawn_connection();
76
77 void attach(LanguageClient& client);
78 void detach();
79 void set_active_client(LanguageClient& client);
80
81private:
82 void create_connection();
83 void show_crash_notification() const;
84 void show_frequent_crashes_notification() const;
85
86 Syntax::Language m_language;
87 Function<NonnullRefPtr<ConnectionToServer>()> m_connection_creator;
88 RefPtr<ConnectionToServer> m_connection;
89
90 Core::ElapsedTimer m_last_crash_timer;
91 bool m_respawn_allowed { true };
92};
93
94class ConnectionToServerInstances {
95public:
96 static void set_instance_for_language(DeprecatedString const& language_name, NonnullOwnPtr<ConnectionToServerWrapper>&& connection_wrapper);
97 static void remove_instance_for_language(DeprecatedString const& language_name);
98
99 static ConnectionToServerWrapper* get_instance_wrapper(DeprecatedString const& language_name);
100
101private:
102 static HashMap<DeprecatedString, NonnullOwnPtr<ConnectionToServerWrapper>> s_instance_for_language;
103};
104
105class LanguageClient : public Weakable<LanguageClient> {
106public:
107 explicit LanguageClient(ConnectionToServerWrapper& connection_wrapper)
108 : m_connection_wrapper(connection_wrapper)
109 {
110 if (m_connection_wrapper.connection()) {
111 m_previous_client = m_connection_wrapper.connection()->language_client();
112 VERIFY(m_previous_client.ptr() != this);
113 m_connection_wrapper.attach(*this);
114 }
115 }
116
117 virtual ~LanguageClient()
118 {
119 // m_connection_wrapper is nullified if the server crashes
120 if (m_connection_wrapper.connection())
121 m_connection_wrapper.detach();
122
123 VERIFY(m_previous_client.ptr() != this);
124 if (m_previous_client && m_connection_wrapper.connection())
125 m_connection_wrapper.set_active_client(*m_previous_client);
126 }
127
128 Syntax::Language language() const { return m_connection_wrapper.language(); }
129 void set_active_client();
130 bool is_active_client() const;
131 virtual void open_file(DeprecatedString const& path, int fd);
132 virtual void set_file_content(DeprecatedString const& path, DeprecatedString const& content);
133 virtual void insert_text(DeprecatedString const& path, DeprecatedString const& text, size_t line, size_t column);
134 virtual void remove_text(DeprecatedString const& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
135 virtual void request_autocomplete(DeprecatedString const& path, size_t cursor_line, size_t cursor_column);
136 virtual void search_declaration(DeprecatedString const& path, size_t line, size_t column);
137 virtual void get_parameters_hint(DeprecatedString const& path, size_t line, size_t column);
138 virtual void get_tokens_info(DeprecatedString const& filename);
139
140 void provide_autocomplete_suggestions(Vector<CodeComprehension::AutocompleteResultEntry> const&) const;
141 void declaration_found(DeprecatedString const& file, size_t line, size_t column) const;
142 void parameters_hint_result(Vector<DeprecatedString> const& params, size_t argument_index) const;
143
144 // Callbacks that get called when the result of a language server query is ready
145 Function<void(Vector<CodeComprehension::AutocompleteResultEntry>)> on_autocomplete_suggestions;
146 Function<void(DeprecatedString const&, size_t, size_t)> on_declaration_found;
147 Function<void(Vector<DeprecatedString> const&, size_t)> on_function_parameters_hint_result;
148 Function<void(Vector<CodeComprehension::TokenInfo> const&)> on_tokens_info_result;
149
150private:
151 ConnectionToServerWrapper& m_connection_wrapper;
152 WeakPtr<LanguageClient> m_previous_client;
153};
154
155template<typename ConnectionToServerT>
156static inline NonnullOwnPtr<LanguageClient> get_language_client(DeprecatedString const& project_path)
157{
158 return make<LanguageClient>(ConnectionToServerWrapper::get_or_create<ConnectionToServerT>(project_path));
159}
160
161template<typename LanguageServerType>
162ConnectionToServerWrapper& ConnectionToServerWrapper::get_or_create(DeprecatedString const& project_path)
163{
164 auto* wrapper = ConnectionToServerInstances::get_instance_wrapper(LanguageServerType::language_name());
165 if (wrapper)
166 return *wrapper;
167
168 auto connection_wrapper_ptr = make<ConnectionToServerWrapper>(LanguageServerType::language_name(), [project_path]() { return LanguageServerType::try_create(project_path).release_value_but_fixme_should_propagate_errors(); });
169 auto& connection_wrapper = *connection_wrapper_ptr;
170 ConnectionToServerInstances::set_instance_for_language(LanguageServerType::language_name(), move(connection_wrapper_ptr));
171 return connection_wrapper;
172}
173
174}