mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 19:15:09 +00:00
HackStudio: Send an open file to language servers
Language servers will now receive an open file instead of just its path. This means the language servers no longer need to access the filesystem to open the file themselves. The C++ language server now has no filesystem access whatsoever (although we might need to relax this in the future if it learns to complete #include paths), while the Shell language server can read /etc/passwd (it wants that in order to get the user's home directory) and browse (but not read!) the whole file system tree for completing paths.
This commit is contained in:
parent
098070b767
commit
e7e179212c
12 changed files with 60 additions and 56 deletions
|
@ -48,6 +48,7 @@
|
||||||
#include <LibWeb/DOM/Text.h>
|
#include <LibWeb/DOM/Text.h>
|
||||||
#include <LibWeb/HTML/HTMLHeadElement.h>
|
#include <LibWeb/HTML/HTMLHeadElement.h>
|
||||||
#include <LibWeb/OutOfProcessWebView.h>
|
#include <LibWeb/OutOfProcessWebView.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
// #define EDITOR_DEBUG
|
// #define EDITOR_DEBUG
|
||||||
|
|
||||||
|
@ -480,8 +481,17 @@ void Editor::set_document(GUI::TextDocument& doc)
|
||||||
set_syntax_highlighter(nullptr);
|
set_syntax_highlighter(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_language_client)
|
if (m_language_client) {
|
||||||
m_language_client->open_file(code_document.file_path().string());
|
auto full_file_path = String::formatted("{}/{}", project().root_directory(), code_document.file_path());
|
||||||
|
dbg() << "Opening " << full_file_path;
|
||||||
|
int fd = open(full_file_path.characters(), O_RDONLY | O_NOCTTY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_language_client->open_file(code_document.file_path().string(), fd);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data()
|
Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data()
|
||||||
|
|
|
@ -36,9 +36,9 @@ void ServerConnection::handle(const Messages::LanguageClient::AutoCompleteSugges
|
||||||
m_language_client->provide_autocomplete_suggestions(message.suggestions());
|
m_language_client->provide_autocomplete_suggestions(message.suggestions());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClient::open_file(const String& path)
|
void LanguageClient::open_file(const String& path, int fd)
|
||||||
{
|
{
|
||||||
m_connection.post_message(Messages::LanguageServer::FileOpened(path));
|
m_connection.post_message(Messages::LanguageServer::FileOpened(path, fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClient::set_file_content(const String& path, const String& content)
|
void LanguageClient::set_file_content(const String& path, const String& content)
|
||||||
|
|
|
@ -43,9 +43,8 @@ class ServerConnection
|
||||||
: public IPC::ServerConnection<LanguageClientEndpoint, LanguageServerEndpoint>
|
: public IPC::ServerConnection<LanguageClientEndpoint, LanguageServerEndpoint>
|
||||||
, public LanguageClientEndpoint {
|
, public LanguageClientEndpoint {
|
||||||
public:
|
public:
|
||||||
ServerConnection(const StringView& socket, const StringView& project_path)
|
ServerConnection(const StringView& socket)
|
||||||
: IPC::ServerConnection<LanguageClientEndpoint, LanguageServerEndpoint>(*this, socket)
|
: IPC::ServerConnection<LanguageClientEndpoint, LanguageServerEndpoint>(*this, socket)
|
||||||
, m_project_path(project_path)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ public:
|
||||||
|
|
||||||
virtual void handshake() override
|
virtual void handshake() override
|
||||||
{
|
{
|
||||||
auto response = send_sync<Messages::LanguageServer::Greet>(m_project_path.string());
|
auto response = send_sync<Messages::LanguageServer::Greet>();
|
||||||
set_my_client_id(response->client_id());
|
set_my_client_id(response->client_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +72,7 @@ public:
|
||||||
if (auto instance = s_instances_for_projects.get(key); instance.has_value())
|
if (auto instance = s_instances_for_projects.get(key); instance.has_value())
|
||||||
return *instance.value();
|
return *instance.value();
|
||||||
|
|
||||||
auto connection = ConcreteType::construct(project_path);
|
auto connection = ConcreteType::construct();
|
||||||
connection->handshake();
|
connection->handshake();
|
||||||
s_instances_for_projects.set(key, *connection);
|
s_instances_for_projects.set(key, *connection);
|
||||||
return *connection;
|
return *connection;
|
||||||
|
@ -83,7 +82,6 @@ protected:
|
||||||
virtual void handle(const Messages::LanguageClient::AutoCompleteSuggestions&) override;
|
virtual void handle(const Messages::LanguageClient::AutoCompleteSuggestions&) override;
|
||||||
|
|
||||||
LanguageClient* m_language_client { nullptr };
|
LanguageClient* m_language_client { nullptr };
|
||||||
LexicalPath m_project_path;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LanguageClient {
|
class LanguageClient {
|
||||||
|
@ -100,7 +98,7 @@ public:
|
||||||
m_connection.detach();
|
m_connection.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void open_file(const String& path);
|
virtual void open_file(const String& path, int fd);
|
||||||
virtual void set_file_content(const String& path, const String& content);
|
virtual void set_file_content(const String& path, const String& content);
|
||||||
virtual void insert_text(const String& path, const String& text, size_t line, size_t column);
|
virtual void insert_text(const String& path, const String& text, size_t line, size_t column);
|
||||||
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column);
|
||||||
|
|
|
@ -32,16 +32,16 @@
|
||||||
#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h>
|
#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h>
|
||||||
#include <LibIPC/ServerConnection.h>
|
#include <LibIPC/ServerConnection.h>
|
||||||
|
|
||||||
#define LANGUAGE_CLIENT(namespace_, socket_name) \
|
#define LANGUAGE_CLIENT(namespace_, socket_name) \
|
||||||
namespace namespace_ { \
|
namespace namespace_ { \
|
||||||
class ServerConnection : public HackStudio::ServerConnection { \
|
class ServerConnection : public HackStudio::ServerConnection { \
|
||||||
C_OBJECT(ServerConnection) \
|
C_OBJECT(ServerConnection) \
|
||||||
private: \
|
private: \
|
||||||
ServerConnection(const String& project_path) \
|
ServerConnection() \
|
||||||
: HackStudio::ServerConnection("/tmp/portal/language/" #socket_name, project_path) \
|
: HackStudio::ServerConnection("/tmp/portal/language/" #socket_name) \
|
||||||
{ \
|
{ \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace LanguageClients {
|
namespace LanguageClients {
|
||||||
|
|
|
@ -53,12 +53,8 @@ void ClientConnection::die()
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnPtr<Messages::LanguageServer::GreetResponse> ClientConnection::handle(const Messages::LanguageServer::Greet& message)
|
OwnPtr<Messages::LanguageServer::GreetResponse> ClientConnection::handle(const Messages::LanguageServer::Greet&)
|
||||||
{
|
{
|
||||||
m_project_root = LexicalPath(message.project_root());
|
|
||||||
#ifdef DEBUG_CPP_LANGUAGE_SERVER
|
|
||||||
dbgln("project_root: {}", m_project_root);
|
|
||||||
#endif
|
|
||||||
return make<Messages::LanguageServer::GreetResponse>(client_id());
|
return make<Messages::LanguageServer::GreetResponse>(client_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,16 +77,11 @@ static DefaultDocumentClient s_default_document_client;
|
||||||
|
|
||||||
void ClientConnection::handle(const Messages::LanguageServer::FileOpened& message)
|
void ClientConnection::handle(const Messages::LanguageServer::FileOpened& message)
|
||||||
{
|
{
|
||||||
LexicalPath file_path(String::formatted("{}/{}", m_project_root, message.file_name()));
|
auto file = Core::File::construct(this);
|
||||||
#ifdef DEBUG_CPP_LANGUAGE_SERVER
|
if (!file->open(message.file().fd(), Core::IODevice::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes)) {
|
||||||
dbgln("FileOpened: {}", file_path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto file = Core::File::construct(file_path.string());
|
|
||||||
if (!file->open(Core::IODevice::ReadOnly)) {
|
|
||||||
errno = file->error();
|
errno = file->error();
|
||||||
perror("open");
|
perror("open");
|
||||||
dbgln("Failed to open project file: {}", file_path);
|
dbgln("Failed to open project file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto content = file->read_all();
|
auto content = file->read_all();
|
||||||
|
|
|
@ -58,7 +58,6 @@ private:
|
||||||
|
|
||||||
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
||||||
|
|
||||||
LexicalPath m_project_root;
|
|
||||||
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -36,16 +36,20 @@
|
||||||
int main(int, char**)
|
int main(int, char**)
|
||||||
{
|
{
|
||||||
Core::EventLoop event_loop;
|
Core::EventLoop event_loop;
|
||||||
if (pledge("stdio unix rpath", nullptr) < 0) {
|
if (pledge("stdio unix recvfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
||||||
IPC::new_client_connection<LanguageServers::Cpp::ClientConnection>(socket.release_nonnull(), 1);
|
IPC::new_client_connection<LanguageServers::Cpp::ClientConnection>(socket.release_nonnull(), 1);
|
||||||
if (pledge("stdio rpath", nullptr) < 0) {
|
if (pledge("stdio recvfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (unveil(nullptr, nullptr) < 0) {
|
||||||
|
perror("unveil");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return event_loop.exec();
|
return event_loop.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
endpoint LanguageServer = 8001
|
endpoint LanguageServer = 8001
|
||||||
{
|
{
|
||||||
Greet(String project_root) => (i32 client_id)
|
Greet() => (i32 client_id)
|
||||||
|
|
||||||
FileOpened(String file_name) =|
|
FileOpened(String file_name, IPC::File file) =|
|
||||||
FileEditInsertText(String file_name, String text, i32 start_line, i32 start_column) =|
|
FileEditInsertText(String file_name, String text, i32 start_line, i32 start_column) =|
|
||||||
FileEditRemoveText(String file_name, i32 start_line, i32 start_column, i32 end_line, i32 end_column) =|
|
FileEditRemoveText(String file_name, i32 start_line, i32 start_column, i32 end_line, i32 end_column) =|
|
||||||
SetFileContent(String file_name, String content) =|
|
SetFileContent(String file_name, String content) =|
|
||||||
|
|
|
@ -53,12 +53,8 @@ void ClientConnection::die()
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnPtr<Messages::LanguageServer::GreetResponse> ClientConnection::handle(const Messages::LanguageServer::Greet& message)
|
OwnPtr<Messages::LanguageServer::GreetResponse> ClientConnection::handle(const Messages::LanguageServer::Greet&)
|
||||||
{
|
{
|
||||||
m_project_root = LexicalPath(message.project_root());
|
|
||||||
#ifdef DEBUG_SH_LANGUAGE_SERVER
|
|
||||||
dbgln("project_root: {}", m_project_root);
|
|
||||||
#endif
|
|
||||||
return make<Messages::LanguageServer::GreetResponse>(client_id());
|
return make<Messages::LanguageServer::GreetResponse>(client_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,16 +77,11 @@ static DefaultDocumentClient s_default_document_client;
|
||||||
|
|
||||||
void ClientConnection::handle(const Messages::LanguageServer::FileOpened& message)
|
void ClientConnection::handle(const Messages::LanguageServer::FileOpened& message)
|
||||||
{
|
{
|
||||||
LexicalPath file_path(String::formatted("{}/{}", m_project_root, message.file_name()));
|
auto file = Core::File::construct(this);
|
||||||
#ifdef DEBUG_SH_LANGUAGE_SERVER
|
if (!file->open(message.file().fd(), Core::IODevice::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes)) {
|
||||||
dbgln("FileOpened: {}", file_path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto file = Core::File::construct(file_path.string());
|
|
||||||
if (!file->open(Core::IODevice::ReadOnly)) {
|
|
||||||
errno = file->error();
|
errno = file->error();
|
||||||
perror("open");
|
perror("open");
|
||||||
dbgln("Failed to open project file: {}", file_path);
|
dbgln("Failed to open project file");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto content = file->read_all();
|
auto content = file->read_all();
|
||||||
|
|
|
@ -59,7 +59,6 @@ private:
|
||||||
|
|
||||||
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
RefPtr<GUI::TextDocument> document_for(const String& file_name);
|
||||||
|
|
||||||
LexicalPath m_project_root;
|
|
||||||
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
|
||||||
|
|
||||||
AutoComplete m_autocomplete;
|
AutoComplete m_autocomplete;
|
||||||
|
|
|
@ -36,16 +36,28 @@
|
||||||
int main(int, char**)
|
int main(int, char**)
|
||||||
{
|
{
|
||||||
Core::EventLoop event_loop;
|
Core::EventLoop event_loop;
|
||||||
if (pledge("stdio unix rpath", nullptr) < 0) {
|
if (pledge("stdio unix rpath recvfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
||||||
IPC::new_client_connection<LanguageServers::Shell::ClientConnection>(socket.release_nonnull(), 1);
|
IPC::new_client_connection<LanguageServers::Shell::ClientConnection>(socket.release_nonnull(), 1);
|
||||||
if (pledge("stdio rpath", nullptr) < 0) {
|
if (pledge("stdio rpath recvfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (unveil("/etc/passwd", "r") < 0) {
|
||||||
|
perror("unveil");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (unveil("/", "b") < 0) {
|
||||||
|
perror("unveil");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (unveil(nullptr, nullptr) < 0) {
|
||||||
|
perror("unveil");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return event_loop.exec();
|
return event_loop.exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,14 +59,14 @@ static void open_default_project_file(const String& project_path);
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread unix", nullptr) < 0) {
|
if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread unix sendfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto app = GUI::Application::construct(argc, argv);
|
auto app = GUI::Application::construct(argc, argv);
|
||||||
|
|
||||||
if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec fattr thread unix", nullptr) < 0) {
|
if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec fattr thread unix sendfd", nullptr) < 0) {
|
||||||
perror("pledge");
|
perror("pledge");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue