Files
clice/bin/clice.cc
2025-10-10 22:23:09 +08:00

176 lines
5.1 KiB
C++

#include "Server/Version.h"
#include "Server/Server.h"
#include "Support/Logging.h"
#include "Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Process.h"
#include "llvm/ADT/StringSwitch.h"
namespace cl = llvm::cl;
using namespace clice;
namespace {
static cl::OptionCategory category("clice options");
enum class Mode {
Pipe,
Socket,
Indexer,
};
cl::opt<Mode> mode{
"mode",
cl::cat(category),
cl::value_desc("string"),
cl::init(Mode::Pipe),
cl::values(clEnumValN(Mode::Pipe, "pipe", "pipe mode, clice will listen on stdio"),
clEnumValN(Mode::Socket, "socket", "socket mode, clice will listen on host:port")),
/// clEnumValN(Mode::Indexer, "indexer", "indexer mode, to implement")
cl::desc("The mode of clice, default is pipe, socket is usually used for debugging"),
};
cl::opt<std::string> host{
"host",
cl::cat(category),
cl::value_desc("string"),
cl::init("127.0.0.1"),
cl::desc("The host to connect to (default: 127.0.0.1)"),
};
cl::opt<unsigned int> port{
"port",
cl::cat(category),
cl::value_desc("unsigned int"),
cl::init(50051),
cl::desc("The port to connect to"),
};
cl::opt<std::string> resource_dir{
"resource-dir",
cl::cat(category),
cl::value_desc("path"),
cl::desc(R"(The path of the clang resource directory, default is "../../lib/clang/version")"),
};
cl::opt<logging::ColorMode> log_color{
"log-color",
cl::cat(category),
cl::value_desc("always|auto|never"),
cl::init(logging::ColorMode::automatic),
cl::values(clEnumValN(logging::ColorMode::automatic, "auto", ""),
clEnumValN(logging::ColorMode::always, "always", ""),
clEnumValN(logging::ColorMode::never, "never", "")),
cl::desc("When to use terminal colors, default is auto"),
};
cl::opt<logging::Level> log_level{
"log-level",
cl::cat(category),
cl::value_desc("trace|debug|info|warn|fatal"),
cl::init(logging::Level::info),
cl::values(clEnumValN(logging::Level::trace, "trace", ""),
clEnumValN(logging::Level::debug, "debug", ""),
clEnumValN(logging::Level::info, "info", ""),
clEnumValN(logging::Level::warn, "warn", ""),
clEnumValN(logging::Level::err, "fatal", "")),
cl::desc("The log level, default is info"),
};
void init_log() {
using namespace logging;
options.color = log_color;
options.level = log_level;
logging::create_stderr_logger("clice", logging::options);
}
/// Check the command line arguments and initialize the clice.
bool check_arguments(int argc, const char** argv) {
/// Hide unrelated options.
cl::HideUnrelatedOptions(category);
// Set version printer and parse command line options
cl::SetVersionPrinter([](llvm::raw_ostream& os) {
os << std::format("clice version: {}\nllvm version: {}\n",
clice::config::version,
clice::config::llvm_version);
});
cl::ParseCommandLineOptions(argc,
argv,
"clice is a new generation of language server for C/C++");
init_log();
for(int i = 0; i < argc; ++i) {
logging::info("argv[{}] = {}", i, argv[i]);
}
// Initialize resource directory
if(resource_dir.empty()) {
logging::info("No resource directory specified, using default resource directory");
// Try to initialize default resource directory
if(auto result = fs::init_resource_dir(argv[0]); !result) {
logging::warn("Cannot find default resource directory, because {}", result.error());
return false;
}
} else {
// Set and check the specified resource directory
fs::resource_dir = resource_dir.getValue();
if(fs::exists(fs::resource_dir)) {
logging::info("Resource directory found: {}", fs::resource_dir);
} else {
logging::warn("Resource directory not found: {}", fs::resource_dir);
return false;
}
}
return true;
}
} // namespace
int main(int argc, const char** argv) {
llvm::InitLLVM guard(argc, argv);
llvm::setBugReportMsg(
"Please report bugs to https://github.com/clice-io/clice/issues and include the crash backtrace");
if(!check_arguments(argc, argv)) {
return 1;
}
async::init();
/// The global server instance.
static Server instance;
auto loop = [&](json::Value value) -> async::Task<> {
co_await instance.on_receive(value);
};
switch(mode) {
case Mode::Pipe: {
async::net::listen(loop);
logging::info("Server starts listening on stdin/stdout");
break;
}
case Mode::Socket: {
async::net::listen(host.c_str(), port, loop);
logging::info("Server starts listening on {}:{}", host.getValue(), port.getValue());
break;
}
case Mode::Indexer: {
/// TODO:
break;
}
}
async::run();
logging::info("clice exit normally!");
return 0;
}