Skip to content

Commit 95a7368

Browse files
committed
chore: Add a simple thread pool to the LSP server
1 parent 4fa8acc commit 95a7368

File tree

12 files changed

+721
-394
lines changed

12 files changed

+721
-394
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ option(CXX_LIBCXX_WITH_CLANG "Link with libc++" OFF)
4444
option(CXX_BUILD_TESTS "Build tests" ON)
4545
option(CXX_INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimization" OFF)
4646

47+
find_package(Threads)
48+
4749
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CXX_LIBCXX_WITH_CLANG)
4850
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
4951
endif()

CMakePresets.json

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@
7676
{
7777
"name": "install",
7878
"configurePreset": "default",
79-
"targets": [
80-
"install"
81-
]
79+
"targets": ["install"]
8280
},
8381
{
8482
"name": "build-emscripten",
@@ -87,9 +85,7 @@
8785
{
8886
"name": "install-emscripten",
8987
"configurePreset": "emscripten",
90-
"targets": [
91-
"install"
92-
]
88+
"targets": ["install"]
9389
},
9490
{
9591
"name": "build-wasi",
@@ -98,9 +94,7 @@
9894
{
9995
"name": "install-wasi",
10096
"configurePreset": "wasi",
101-
"targets": [
102-
"install"
103-
]
97+
"targets": ["install"]
10498
}
10599
],
106100
"testPresets": [
@@ -174,4 +168,4 @@
174168
]
175169
}
176170
]
177-
}
171+
}

src/frontend/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,17 @@ target_compile_definitions(cxx PRIVATE
2727
CXX_VERSION="${CMAKE_PROJECT_VERSION}"
2828
)
2929

30+
# if cmake founds the Threads package, link with it
31+
if(Threads_FOUND)
32+
target_link_libraries(cxx Threads::Threads)
33+
target_compile_options(cxx PRIVATE -pthread)
34+
else()
35+
target_compile_definitions(cxx PRIVATE CXX_NO_THREADS)
36+
endif()
37+
3038
if(EMSCRIPTEN)
31-
target_link_options(cxx PUBLIC
39+
target_link_options(cxx PRIVATE -pthread)
40+
target_link_options(cxx PRIVATE
3241
"SHELL:-s EXIT_RUNTIME=1"
3342
"SHELL:-s WASM_BIGINT=1"
3443
"SHELL:-s NODERAWFS=1"

src/frontend/cxx/cli.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ std::vector<CLIOptionDescr> options{
198198

199199
{"-lsp", "Start Language Server", &CLI::opt_lsp},
200200

201+
{"-lsp-test", "Start Language Server in testing mode", &CLI::opt_lsp_test},
202+
203+
{"-j", "<n>", "Run <n> jobs in parallel.", CLIOptionDescrKind::kSeparated},
204+
201205
{"-v", "Show commands to run and use verbose output", &CLI::opt_v},
202206

203207
};

src/frontend/cxx/cli.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class CLI {
7474
bool opt_v = false;
7575
bool opt_emit_ast = false;
7676
bool opt_lsp = false;
77+
bool opt_lsp_test = false;
7778

7879
void parse(int& argc, char**& argv);
7980

src/frontend/cxx/cxx_document.cc

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// Copyright (c) 2024 Roberto Raggi <[email protected]>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
// SOFTWARE.
20+
21+
#include "cxx_document.h"
22+
23+
#include <cxx/ast.h>
24+
#include <cxx/ast_visitor.h>
25+
#include <cxx/control.h>
26+
#include <cxx/gcc_linux_toolchain.h>
27+
#include <cxx/lexer.h>
28+
#include <cxx/lsp/enums.h>
29+
#include <cxx/lsp/requests.h>
30+
#include <cxx/lsp/types.h>
31+
#include <cxx/macos_toolchain.h>
32+
#include <cxx/preprocessor.h>
33+
#include <cxx/private/path.h>
34+
#include <cxx/scope.h>
35+
#include <cxx/symbol_printer.h>
36+
#include <cxx/symbols.h>
37+
#include <cxx/translation_unit.h>
38+
#include <cxx/wasm32_wasi_toolchain.h>
39+
#include <cxx/windows_toolchain.h>
40+
41+
namespace cxx::lsp {
42+
43+
namespace {
44+
45+
struct Diagnostics final : cxx::DiagnosticsClient {
46+
json messages = json::array();
47+
Vector<lsp::Diagnostic> diagnostics{messages};
48+
49+
void report(const cxx::Diagnostic& diag) override {
50+
std::string_view fileName;
51+
std::uint32_t line = 0;
52+
std::uint32_t column = 0;
53+
54+
preprocessor()->getTokenStartPosition(diag.token(), &line, &column,
55+
&fileName);
56+
57+
std::uint32_t endLine = 0;
58+
std::uint32_t endColumn = 0;
59+
60+
preprocessor()->getTokenEndPosition(diag.token(), &endLine, &endColumn,
61+
nullptr);
62+
63+
auto tmp = json::object();
64+
65+
auto d = diagnostics.emplace_back();
66+
67+
int s = std::max(int(line) - 1, 0);
68+
int sc = std::max(int(column) - 1, 0);
69+
int e = std::max(int(endLine) - 1, 0);
70+
int ec = std::max(int(endColumn) - 1, 0);
71+
72+
d.message(diag.message());
73+
d.range().start(lsp::Position(tmp).line(s).character(sc));
74+
d.range().end(lsp::Position(tmp).line(e).character(ec));
75+
}
76+
};
77+
78+
} // namespace
79+
80+
struct CxxDocument::Private {
81+
const CLI& cli;
82+
long version;
83+
Control control;
84+
Diagnostics diagnosticsClient;
85+
TranslationUnit unit{&control, &diagnosticsClient};
86+
std::shared_ptr<Toolchain> toolchain;
87+
88+
Private(const CLI& cli, long version) : cli(cli), version(version) {}
89+
90+
void configure();
91+
};
92+
93+
void CxxDocument::Private::configure() {
94+
auto preprocesor = unit.preprocessor();
95+
96+
auto toolchainId = cli.getSingle("-toolchain");
97+
98+
if (!toolchainId) {
99+
toolchainId = "wasm32";
100+
}
101+
102+
if (toolchainId == "darwin" || toolchainId == "macos") {
103+
toolchain = std::make_unique<MacOSToolchain>(preprocesor);
104+
} else if (toolchainId == "wasm32") {
105+
auto wasmToolchain = std::make_unique<Wasm32WasiToolchain>(preprocesor);
106+
107+
fs::path app_dir;
108+
109+
#if __wasi__
110+
app_dir = fs::path("/usr/bin/");
111+
#elif !defined(CXX_NO_FILESYSTEM)
112+
app_dir = std::filesystem::canonical(
113+
std::filesystem::path(cli.app_name).remove_filename());
114+
#elif __unix__ || __APPLE__
115+
char* app_name = realpath(cli.app_name.c_str(), nullptr);
116+
app_dir = fs::path(app_name).remove_filename().string();
117+
std::free(app_name);
118+
#endif
119+
120+
wasmToolchain->setAppdir(app_dir.string());
121+
122+
if (auto paths = cli.get("--sysroot"); !paths.empty()) {
123+
wasmToolchain->setSysroot(paths.back());
124+
} else {
125+
auto sysroot_dir = app_dir / std::string("../lib/wasi-sysroot");
126+
wasmToolchain->setSysroot(sysroot_dir.string());
127+
}
128+
129+
toolchain = std::move(wasmToolchain);
130+
} else if (toolchainId == "linux") {
131+
std::string host;
132+
#ifdef __aarch64__
133+
host = "aarch64";
134+
#elif __x86_64__
135+
host = "x86_64";
136+
#endif
137+
138+
std::string arch = cli.getSingle("-arch").value_or(host);
139+
toolchain = std::make_unique<GCCLinuxToolchain>(preprocesor, arch);
140+
} else if (toolchainId == "windows") {
141+
auto windowsToolchain = std::make_unique<WindowsToolchain>(preprocesor);
142+
143+
if (auto paths = cli.get("-vctoolsdir"); !paths.empty()) {
144+
windowsToolchain->setVctoolsdir(paths.back());
145+
}
146+
147+
if (auto paths = cli.get("-winsdkdir"); !paths.empty()) {
148+
windowsToolchain->setWinsdkdir(paths.back());
149+
}
150+
151+
if (auto versions = cli.get("-winsdkversion"); !versions.empty()) {
152+
windowsToolchain->setWinsdkversion(versions.back());
153+
}
154+
155+
toolchain = std::move(windowsToolchain);
156+
}
157+
158+
if (toolchain) {
159+
control.setMemoryLayout(toolchain->memoryLayout());
160+
161+
if (!cli.opt_nostdinc) toolchain->addSystemIncludePaths();
162+
163+
if (!cli.opt_nostdincpp) toolchain->addSystemCppIncludePaths();
164+
165+
toolchain->addPredefinedMacros();
166+
}
167+
168+
for (const auto& path : cli.get("-I")) {
169+
preprocesor->addSystemIncludePath(path);
170+
}
171+
172+
for (const auto& macro : cli.get("-D")) {
173+
auto sep = macro.find_first_of("=");
174+
175+
if (sep == std::string::npos) {
176+
preprocesor->defineMacro(macro, "1");
177+
} else {
178+
preprocesor->defineMacro(macro.substr(0, sep), macro.substr(sep + 1));
179+
}
180+
}
181+
182+
for (const auto& macro : cli.get("-U")) {
183+
preprocesor->undefMacro(macro);
184+
}
185+
}
186+
187+
CxxDocument::CxxDocument(const CLI& cli, long version)
188+
: d(std::make_unique<Private>(cli, version)) {}
189+
190+
void CxxDocument::parse(std::string source, std::string fileName) {
191+
d->configure();
192+
193+
auto& unit = d->unit;
194+
auto& cli = d->cli;
195+
196+
unit.setSource(std::move(source), fileName);
197+
198+
auto preprocessor = unit.preprocessor();
199+
preprocessor->squeeze();
200+
201+
unit.parse(ParserConfiguration{
202+
.checkTypes = cli.opt_fcheck,
203+
.fuzzyTemplateResolution = true,
204+
.staticAssert = cli.opt_fstatic_assert || cli.opt_fcheck,
205+
.reflect = !cli.opt_fno_reflect,
206+
.templates = cli.opt_ftemplates,
207+
});
208+
}
209+
210+
CxxDocument::~CxxDocument() {}
211+
212+
auto CxxDocument::version() const -> long { return d->version; }
213+
214+
auto CxxDocument::diagnostics() const -> Vector<Diagnostic> {
215+
return Vector<Diagnostic>(d->diagnosticsClient.messages);
216+
}
217+
218+
} // namespace cxx::lsp

src/frontend/cxx/cxx_document.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) 2024 Roberto Raggi <[email protected]>
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
// SOFTWARE.
20+
21+
#pragma once
22+
23+
#include <cxx/lsp/fwd.h>
24+
25+
#include <memory>
26+
#include <string>
27+
28+
#include "cli.h"
29+
30+
namespace cxx::lsp {
31+
32+
class CxxDocument {
33+
public:
34+
explicit CxxDocument(const CLI& cli, long version);
35+
~CxxDocument();
36+
37+
void parse(std::string source, std::string fileName);
38+
39+
[[nodiscard]] auto version() const -> long;
40+
[[nodiscard]] auto diagnostics() const -> Vector<Diagnostic>;
41+
42+
private:
43+
struct Private;
44+
std::unique_ptr<Private> d;
45+
};
46+
47+
} // namespace cxx::lsp

0 commit comments

Comments
 (0)