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