2022-07-18 18:26:57 -04:00
|
|
|
// clang-format off
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <io.h>
|
|
|
|
#define NOMINMAX
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
#include <vector>
|
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
#include "common/log/log.h"
|
2022-07-23 10:30:23 -04:00
|
|
|
#include "common/util/unicode_util.h"
|
2023-08-08 12:59:37 -04:00
|
|
|
#include "common/util/term_util.h"
|
2022-07-18 18:26:57 -04:00
|
|
|
|
|
|
|
#include "lsp/handlers/lsp_router.h"
|
|
|
|
#include "lsp/state/workspace.h"
|
|
|
|
#include "lsp/transport/stdio.h"
|
|
|
|
#include "lsp/state/app.h"
|
|
|
|
|
|
|
|
#include "third-party/CLI11.hpp"
|
|
|
|
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
// NOTE - if we ever add HTTP support to the LSP
|
|
|
|
/*
|
|
|
|
What needs to be understood is that for connection timing issues the server is actually a client
|
|
|
|
and the client is the server in terms of opening the ports.
|
|
|
|
|
|
|
|
When you specify a socket transport the client is listening on that port for a connection. The
|
|
|
|
socket port number is passed as --socket=${port} to the server process started.
|
|
|
|
*/
|
|
|
|
|
2023-08-08 12:59:37 -04:00
|
|
|
void setup_logging(bool verbose, std::string log_file, bool disable_ansi_colors) {
|
2022-10-29 18:27:31 -04:00
|
|
|
if (!log_file.empty()) {
|
2023-11-11 17:51:55 -05:00
|
|
|
lg::set_file(fs::path(log_file).filename().string(), false, true,
|
|
|
|
fs::path(log_file).parent_path().string());
|
2022-10-29 18:27:31 -04:00
|
|
|
}
|
2022-07-18 18:26:57 -04:00
|
|
|
if (verbose) {
|
|
|
|
lg::set_file_level(lg::level::debug);
|
|
|
|
lg::set_flush_level(lg::level::debug);
|
|
|
|
} else {
|
|
|
|
lg::set_file_level(lg::level::info);
|
|
|
|
lg::set_flush_level(lg::level::info);
|
|
|
|
}
|
2023-08-08 12:59:37 -04:00
|
|
|
if (disable_ansi_colors) {
|
|
|
|
lg::disable_ansi_colors();
|
|
|
|
}
|
2022-07-18 18:26:57 -04:00
|
|
|
|
|
|
|
// We use stdout to communicate with the client, so don't use it at all!
|
|
|
|
lg::set_stdout_level(lg::level::off);
|
|
|
|
lg::initialize();
|
|
|
|
}
|
|
|
|
|
2024-03-30 19:49:07 -04:00
|
|
|
std::string temp_url_encode(const std::string& value) {
|
|
|
|
std::ostringstream escaped;
|
|
|
|
escaped.fill('0');
|
|
|
|
escaped << std::hex;
|
|
|
|
|
|
|
|
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
|
|
|
|
std::string::value_type c = (*i);
|
|
|
|
|
|
|
|
// Keep alphanumeric and other accepted characters intact
|
|
|
|
if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~' || c == '/') {
|
|
|
|
escaped << c;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any other characters are percent-encoded
|
|
|
|
escaped << std::uppercase;
|
|
|
|
escaped << '%' << std::setw(2) << int((unsigned char)c);
|
|
|
|
escaped << std::nouppercase;
|
|
|
|
}
|
|
|
|
|
|
|
|
return escaped.str();
|
|
|
|
}
|
|
|
|
|
2022-07-18 18:26:57 -04:00
|
|
|
int main(int argc, char** argv) {
|
2022-07-23 10:30:23 -04:00
|
|
|
ArgumentGuard u8_guard(argc, argv);
|
2022-07-18 18:26:57 -04:00
|
|
|
|
|
|
|
CLI::App app{"OpenGOAL Language Server"};
|
|
|
|
|
|
|
|
bool use_stdin = true;
|
|
|
|
bool verbose = false;
|
|
|
|
std::string logfile;
|
|
|
|
app.add_flag("--stdio", use_stdin,
|
|
|
|
"Don't launch an HTTP server and instead accept input on stdin");
|
|
|
|
app.add_flag("-v,--verbose", verbose, "Enable verbose logging");
|
|
|
|
app.add_option("-l,--log", logfile, "Log file path");
|
2023-08-08 12:59:37 -04:00
|
|
|
define_common_cli_arguments(app);
|
2022-07-18 18:26:57 -04:00
|
|
|
app.validate_positionals();
|
|
|
|
CLI11_PARSE(app, argc, argv);
|
|
|
|
|
|
|
|
AppState appstate;
|
2024-03-30 19:49:07 -04:00
|
|
|
|
2022-07-18 18:26:57 -04:00
|
|
|
LSPRouter lsp_router;
|
|
|
|
appstate.verbose = verbose;
|
2023-03-01 17:52:33 -05:00
|
|
|
try {
|
2023-08-08 12:59:37 -04:00
|
|
|
setup_logging(appstate.verbose, logfile, _cli_flag_disable_ansi);
|
2023-03-01 17:52:33 -05:00
|
|
|
} catch (const std::exception& e) {
|
|
|
|
lg::error("Failed to setup logging: {}", e.what());
|
|
|
|
return 1;
|
|
|
|
}
|
2022-07-18 18:26:57 -04:00
|
|
|
lsp_router.init_routes();
|
|
|
|
|
|
|
|
lg::info("OpenGOAL LSP Initialized, ready for requests");
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
|
|
#endif
|
|
|
|
|
2024-03-30 19:49:07 -04:00
|
|
|
// TODO - make the server check for the process id of the extension host and exit itself if that
|
|
|
|
// process goes away (the process id comes on the command line as an argument and in the
|
|
|
|
// initialize request). This is what we do in all our servers since the extension host could die
|
|
|
|
// unexpected as well.
|
|
|
|
|
2023-04-01 19:40:21 -04:00
|
|
|
try {
|
|
|
|
char c;
|
|
|
|
MessageBuffer message_buffer;
|
|
|
|
while (std::cin.get(c)) {
|
|
|
|
message_buffer.handle_char(c);
|
|
|
|
|
|
|
|
if (message_buffer.message_completed()) {
|
|
|
|
json body = message_buffer.body();
|
2023-05-21 17:24:23 -04:00
|
|
|
// If the request doesn't have a 'method', then it's not a request
|
|
|
|
// skip it, but log it. We don't depend on any requests from the client yet
|
|
|
|
// currently they are mostly just notifications
|
|
|
|
if (!body.contains("method")) {
|
|
|
|
lg::warn("Response received from client - {}", body.dump());
|
|
|
|
message_buffer.clear();
|
|
|
|
continue;
|
|
|
|
}
|
2023-04-01 19:40:21 -04:00
|
|
|
auto method_name = body["method"].get<std::string>();
|
|
|
|
lg::info(">>> Received message of method '{}'", method_name);
|
|
|
|
auto responses = lsp_router.route_message(message_buffer, appstate);
|
|
|
|
if (responses) {
|
|
|
|
for (const auto& response : responses.value()) {
|
|
|
|
std::cout << response.c_str() << std::flush;
|
|
|
|
if (appstate.verbose) {
|
|
|
|
lg::debug("<<< Sending message: {}", response);
|
|
|
|
} else {
|
|
|
|
lg::info("<<< Sending message of method '{}'", method_name);
|
|
|
|
}
|
2022-07-18 18:26:57 -04:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 19:40:21 -04:00
|
|
|
message_buffer.clear();
|
2022-07-18 18:26:57 -04:00
|
|
|
}
|
|
|
|
}
|
2023-04-01 19:40:21 -04:00
|
|
|
} catch (std::exception& e) {
|
|
|
|
lg::error("Unexpected LSP Exception occured - {}", e.what());
|
2022-07-18 18:26:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|