Skip to content

Commit

Permalink
Replace main function with a symbol indexer
Browse files Browse the repository at this point in the history
This program runs in two modes, either a single-file mode (which outputs
on stdout), or batch mode, (which outputs to a file based on the first
argument and an environment variable).

In either case, the program reads sepecified .blob files, iterates over
all symbols contained in the index, and outputs TSV data for those
symbols which are defined by the file being indexed.

The resulting output can be used as an index to search for the
definition of any symbol, without having an LSP-enabled editor open at
the time. A commit in my dotfiles repo (brenns10/dotfiles@79ade3b758a4
"tagsearch: add a system for searching indexed symbols") demonstrates
the use of this system.
  • Loading branch information
brenns10 committed Oct 13, 2020
1 parent 1dc69f4 commit 2657894
Showing 1 changed file with 86 additions and 93 deletions.
179 changes: 86 additions & 93 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <filesystem>
#include <iostream>
#include <fstream>

using namespace ccls;
using namespace llvm;
Expand Down Expand Up @@ -51,101 +54,91 @@ opt<std::string> opt_log_file("log-file", desc("stderr or log file"),
value_desc("file"), init("stderr"), cat(C));
opt<bool> opt_log_file_append("log-file-append", desc("append to log file"),
cat(C));
}

void closeLog() { fclose(ccls::log::file); }
std::string unescape(const std::string &path)
{
std::filesystem::path p(path);
std::string loc = p.parent_path().filename();
std::string orig = p.filename();
/*
* Boy this is not true, see escapeFileName().
* - if the original contains '@', this is wrong
* - if the original contains ':', this is wrong
* Thankfully, those are rare enough that I don't care it's wrong.
*/
std::replace(orig.begin(), orig.end(), '@', '/');
std::replace(loc.begin(), loc.end(), '@', '/');
std::string result = loc + "/" + orig;
return result.substr(0, result.length() - 5); // remove .blob
}

} // namespace
void index_file(const std::string &fn, std::ostream *output)
{
std::string content = *readContent(fn);
auto file = ccls::deserialize(SerializeFormat::Binary, fn, content, "", {});
std::string orig = unescape(file->path);
for (auto &[usr, v] : file->usr2var) {
Maybe<DeclRef> spell = v.def.spell;
if (spell && !v.def.is_local() && v.def.kind != SymbolKind::Field) {
std::string basicname(v.def.name(false));
Pos start = spell->range.start;
*output << basicname << "\t" << v.def.detailed_name <<
"\t" << orig << "\t" << start.line << "\t" <<
(int)v.def.kind << std::endl;
}
}
for (auto &[usr, v] : file->usr2func) {
Maybe<DeclRef> spell = v.def.spell;
if (spell) {
std::string basicname(v.def.name(false));
Pos start = spell->range.start;
*output << basicname << "\t" << v.def.detailed_name <<
"\t" << orig << "\t" << start.line << "\t" <<
(int)v.def.kind << std::endl;
}
}
for (auto &[usr, v] : file->usr2type) {
Maybe<DeclRef> spell = v.def.spell;
if (spell) {
std::string basicname(v.def.name(false));
Pos start = spell->range.start;
*output << basicname << "\t" << v.def.detailed_name <<
"\t" << orig << "\t" << start.line << "\t" <<
(int)v.def.kind << std::endl;
}
}
}

int main(int argc, char **argv) {
traceMe();
sys::PrintStackTraceOnErrorSignal(argv[0]);
cl::SetVersionPrinter([](raw_ostream &os) {
os << clang::getClangToolFullVersion("ccls version " CCLS_VERSION "\nclang")
<< "\n";
});

cl::HideUnrelatedOptions(C);

ParseCommandLineOptions(argc, argv,
"C/C++/Objective-C language server\n\n"
"See more on https://github.com/MaskRay/ccls/wiki");

if (opt_help) {
PrintHelpMessage();
return 0;
}
ccls::log::verbosity = ccls::log::Verbosity(opt_verbose.getValue());

pipeline::init();
const char *env = getenv("CCLS_CRASH_RECOVERY");
if (!env || strcmp(env, "0") != 0)
CrashRecoveryContext::Enable();

bool language_server = true;

if (opt_log_file.size()) {
ccls::log::file =
opt_log_file == "stderr"
? stderr
: fopen(opt_log_file.c_str(), opt_log_file_append ? "ab" : "wb");
if (!ccls::log::file) {
fprintf(stderr, "failed to open %s\n", opt_log_file.c_str());
return 2;
}
setbuf(ccls::log::file, NULL);
atexit(closeLog);
}

if (opt_test_index != "!") {
language_server = false;
if (!ccls::runIndexTests(opt_test_index,
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;
for (const std::string &str : g_init_options) {
rapidjson::ParseResult ok = reader.Parse(str.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;
}
}
}

sys::ChangeStdinToBinary();
sys::ChangeStdoutToBinary();
if (opt_index.size()) {
SmallString<256> root(opt_index);
sys::fs::make_absolute(root);
pipeline::standalone(std::string(root.data(), root.size()));
} else {
// 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.
pipeline::mainLoop();
}
}

return 0;
std::ostream *output = &std::cout;
std::ofstream outfile;
int i = 1;

/* prevent segfault */
ccls::Config config;
g_config = &config;

if (argc <= 1) {
std::cerr << "Expected at least one argument" << std::endl;
std::cerr << "usage: " << argv[0] << " SINGLE_BLOB" << std::endl;
std::cerr << "or: " << argv[0] << " OUTFILE BLOB [BLOB ...]" << std::endl;
return 1;
}
if (argc > 2) {
std::string fn(argv[1]);
fn += "/output-";
fn += std::getenv("PROCESS_ID");
outfile.open(fn, std::ios_base::app | std::ios_base::out);
output = &outfile;
i = 2;
}
for ( ; i < argc; i++) {
std::string filename(argv[i]);
index_file(filename, output);
}
if (outfile.is_open()) {
outfile.close();
}
return 0;
}

0 comments on commit 2657894

Please sign in to comment.