ccls/src/main.cc

142 lines
4.0 KiB
C++
Raw Normal View History

2018-05-27 19:24:56 +00:00
#include "log.hh"
#include "pipeline.hh"
#include "platform.h"
#include "serializer.h"
#include "serializers/json.h"
#include "test.h"
#include "working_files.h"
2018-05-28 00:50:02 +00:00
using namespace ccls;
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/Process.h>
2018-05-27 19:24:56 +00:00
#include <llvm/Support/Signals.h>
using namespace llvm;
using namespace llvm::cl;
#include <doctest/doctest.h>
#include <rapidjson/error/en.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <unordered_map>
#include <vector>
std::string g_init_options;
namespace {
opt<bool> opt_help("h", desc("Alias for -help"));
opt<int> opt_verbose("v", desc("verbosity"), init(0));
opt<bool> opt_test_index("test-index", desc("run index tests"));
opt<bool> opt_test_unit("test-unit", desc("run unit tests"));
opt<std::string> opt_init("init", desc("extra initialization options"));
opt<std::string> opt_log_file("log-file", desc("log"), value_desc("filename"));
opt<std::string> opt_log_file_append("log-file-append", desc("log"), value_desc("filename"));
list<std::string> opt_extra(Positional, ZeroOrMore, desc("extra"));
2018-05-27 19:24:56 +00:00
void CloseLog() {
fclose(ccls::log::file);
}
} // namespace
int main(int argc, char** argv) {
TraceMe();
2018-05-27 19:24:56 +00:00
sys::PrintStackTraceOnErrorSignal(argv[0]);
ParseCommandLineOptions(argc, argv,
"C/C++/Objective-C language server\n\n"
"See more on https://github.com/MaskRay/ccls/wiki");
if (opt_help) {
PrintHelpMessage();
// Also emit doctest help if --test-unit is passed.
if (!opt_test_unit)
return 0;
}
2018-05-28 00:50:02 +00:00
pipeline::Init();
#ifdef _WIN32
// We need to write to stdout in binary mode because in Windows, writing
// \n will implicitly write \r\n. Language server API will ignore a
// \r\r\n split request.
_setmode(_fileno(stdout), O_BINARY);
_setmode(_fileno(stdin), O_BINARY);
#endif
IndexInit();
bool language_server = true;
2018-05-27 19:24:56 +00:00
if (opt_log_file.size() || opt_log_file_append.size()) {
ccls::log::file = opt_log_file.size()
? fopen(opt_log_file.c_str(), "wb")
: fopen(opt_log_file_append.c_str(), "ab");
if (!ccls::log::file) {
fprintf(
stderr, "failed to open %s\n",
(opt_log_file.size() ? opt_log_file : opt_log_file_append).c_str());
return 2;
}
setbuf(ccls::log::file, NULL);
atexit(CloseLog);
}
if (opt_test_unit) {
language_server = false;
doctest::Context context;
std::vector<const char*> args{argv[0]};
if (opt_help)
args.push_back("-h");
for (auto& arg : opt_extra)
args.push_back(arg.c_str());
context.applyCommandLine(args.size(), args.data());
int res = context.run();
if (res != 0 || context.shouldExit())
return res;
}
if (opt_test_index) {
language_server = false;
if (!RunIndexTests("", sys::Process::StandardInIsUserInput()))
return 1;
}
if (language_server) {
if (!opt_init.empty()) {
// We check syntax error here but override client-side
// initializationOptions in messages/initialize.cc
g_init_options = opt_init;
rapidjson::Document reader;
rapidjson::ParseResult ok = reader.Parse(g_init_options.c_str());
if (!ok) {
fprintf(stderr, "Failed to parse --init as JSON: %s (%zd)\n",
rapidjson::GetParseError_En(ok.Code()), ok.Offset());
return 1;
}
JsonReader json_reader{&reader};
try {
Config config;
Reflect(json_reader, config);
} catch (std::invalid_argument& e) {
fprintf(stderr, "Failed to parse --init %s, expected %s\n",
static_cast<JsonReader&>(json_reader).GetPath().c_str(),
e.what());
return 1;
}
}
// The thread that reads from stdin and dispatchs commands to the main thread.
pipeline::LaunchStdin();
// The thread that writes responses from the main thread to stdout.
pipeline::LaunchStdout();
// Main thread which also spawns indexer threads upon the "initialize" request.
2018-05-28 00:50:02 +00:00
pipeline::MainLoop();
}
return 0;
}