Serenity Operating System
1/*
2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include "ConnectionFromClient.h"
9#include <AK/Debug.h>
10#include <AK/HashMap.h>
11#include <LibGUI/TextDocument.h>
12
13namespace LanguageServers {
14
15static HashMap<int, RefPtr<ConnectionFromClient>> s_connections;
16
17ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket)
18 : IPC::ConnectionFromClient<LanguageClientEndpoint, LanguageServerEndpoint>(*this, move(socket), 1)
19{
20 s_connections.set(1, *this);
21}
22
23void ConnectionFromClient::die()
24{
25 s_connections.remove(client_id());
26 exit(0);
27}
28
29void ConnectionFromClient::greet(DeprecatedString const& project_root)
30{
31 m_filedb.set_project_root(project_root);
32 if (unveil(project_root.characters(), "r") < 0) {
33 perror("unveil");
34 exit(1);
35 }
36 if (unveil(nullptr, nullptr) < 0) {
37 perror("unveil");
38 exit(1);
39 }
40}
41
42void ConnectionFromClient::file_opened(DeprecatedString const& filename, IPC::File const& file)
43{
44 if (m_filedb.is_open(filename)) {
45 return;
46 }
47 m_filedb.add(filename, file.take_fd());
48 m_autocomplete_engine->file_opened(filename);
49}
50
51void ConnectionFromClient::file_edit_insert_text(DeprecatedString const& filename, DeprecatedString const& text, i32 start_line, i32 start_column)
52{
53 dbgln_if(LANGUAGE_SERVER_DEBUG, "InsertText for file: {}", filename);
54 dbgln_if(LANGUAGE_SERVER_DEBUG, "Text: {}", text);
55 dbgln_if(LANGUAGE_SERVER_DEBUG, "[{}:{}]", start_line, start_column);
56 m_filedb.on_file_edit_insert_text(filename, text, start_line, start_column);
57 m_autocomplete_engine->on_edit(filename);
58}
59
60void ConnectionFromClient::file_edit_remove_text(DeprecatedString const& filename, i32 start_line, i32 start_column, i32 end_line, i32 end_column)
61{
62 dbgln_if(LANGUAGE_SERVER_DEBUG, "RemoveText for file: {}", filename);
63 dbgln_if(LANGUAGE_SERVER_DEBUG, "[{}:{} - {}:{}]", start_line, start_column, end_line, end_column);
64 m_filedb.on_file_edit_remove_text(filename, start_line, start_column, end_line, end_column);
65 m_autocomplete_engine->on_edit(filename);
66}
67
68void ConnectionFromClient::auto_complete_suggestions(CodeComprehension::ProjectLocation const& location)
69{
70 dbgln_if(LANGUAGE_SERVER_DEBUG, "AutoCompleteSuggestions for: {} {}:{}", location.file, location.line, location.column);
71
72 auto document = m_filedb.get_document(location.file);
73 if (!document) {
74 dbgln("file {} has not been opened", location.file);
75 return;
76 }
77
78 GUI::TextPosition autocomplete_position = { (size_t)location.line, (size_t)max(location.column, location.column - 1) };
79 Vector<CodeComprehension::AutocompleteResultEntry> suggestions = m_autocomplete_engine->get_suggestions(location.file, autocomplete_position);
80 async_auto_complete_suggestions(move(suggestions));
81}
82
83void ConnectionFromClient::set_file_content(DeprecatedString const& filename, DeprecatedString const& content)
84{
85 dbgln_if(LANGUAGE_SERVER_DEBUG, "SetFileContent: {}", filename);
86 auto document = m_filedb.get_document(filename);
87 if (!document) {
88 m_filedb.add(filename, content);
89 VERIFY(m_filedb.is_open(filename));
90 } else {
91 document->set_text(content.view());
92 }
93 VERIFY(m_filedb.is_open(filename));
94 m_autocomplete_engine->on_edit(filename);
95}
96
97void ConnectionFromClient::find_declaration(CodeComprehension::ProjectLocation const& location)
98{
99 dbgln_if(LANGUAGE_SERVER_DEBUG, "FindDeclaration: {} {}:{}", location.file, location.line, location.column);
100 auto document = m_filedb.get_document(location.file);
101 if (!document) {
102 dbgln("file {} has not been opened", location.file);
103 return;
104 }
105
106 GUI::TextPosition identifier_position = { (size_t)location.line, (size_t)location.column };
107 auto decl_location = m_autocomplete_engine->find_declaration_of(location.file, identifier_position);
108 if (!decl_location.has_value()) {
109 dbgln("could not find declaration");
110 return;
111 }
112
113 dbgln_if(LANGUAGE_SERVER_DEBUG, "declaration location: {} {}:{}", decl_location.value().file, decl_location.value().line, decl_location.value().column);
114 async_declaration_location(CodeComprehension::ProjectLocation { decl_location.value().file, decl_location.value().line, decl_location.value().column });
115}
116
117void ConnectionFromClient::get_parameters_hint(CodeComprehension::ProjectLocation const& location)
118{
119 dbgln_if(LANGUAGE_SERVER_DEBUG, "GetParametersHint: {} {}:{}", location.file, location.line, location.column);
120 auto document = m_filedb.get_document(location.file);
121 if (!document) {
122 dbgln("file {} has not been opened", location.file);
123 return;
124 }
125
126 GUI::TextPosition identifier_position = { (size_t)location.line, (size_t)location.column };
127 auto params = m_autocomplete_engine->get_function_params_hint(location.file, identifier_position);
128 if (!params.has_value()) {
129 dbgln("could not get parameters hint");
130 return;
131 }
132
133 dbgln_if(LANGUAGE_SERVER_DEBUG, "parameters hint:");
134 for (auto& param : params->params) {
135 dbgln_if(LANGUAGE_SERVER_DEBUG, "{}", param);
136 }
137 dbgln_if(LANGUAGE_SERVER_DEBUG, "Parameter index: {}", params->current_index);
138
139 async_parameters_hint_result(params->params, params->current_index);
140}
141
142void ConnectionFromClient::get_tokens_info(DeprecatedString const& filename)
143{
144 dbgln_if(LANGUAGE_SERVER_DEBUG, "GetTokenInfo: {}", filename);
145 auto document = m_filedb.get_document(filename);
146 if (!document) {
147 dbgln("file {} has not been opened", filename);
148 return;
149 }
150
151 auto tokens_info = m_autocomplete_engine->get_tokens_info(filename);
152 async_tokens_info_result(move(tokens_info));
153}
154
155}