mirror of
https://github.com/MaskRay/ccls.git
synced 2025-02-20 15:40:57 +00:00
wip
This commit is contained in:
parent
8fb0fb816c
commit
bf98dc56fb
872
command_line.cc
872
command_line.cc
@ -2,6 +2,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "compilation_database_loader.h"
|
||||
#include "indexer.h"
|
||||
@ -9,6 +10,8 @@
|
||||
#include "query.h"
|
||||
#include "language_server_api.h"
|
||||
|
||||
#include "third_party/tiny-process-library/process.hpp"
|
||||
|
||||
#include <rapidjson/istreamwrapper.h>
|
||||
#include <rapidjson/ostreamwrapper.h>
|
||||
|
||||
@ -17,40 +20,6 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
bool ParsePreferredSymbolLocation(const std::string& content, PreferredSymbolLocation* obj) {
|
||||
#define PARSE_AS(name, string) \
|
||||
if (content == #string) { \
|
||||
*obj = name; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
PARSE_AS(PreferredSymbolLocation::Declaration, "declaration");
|
||||
PARSE_AS(PreferredSymbolLocation::Definition, "definition");
|
||||
|
||||
return false;
|
||||
#undef PARSE_AS
|
||||
}
|
||||
|
||||
bool ParseCommand(const std::string& content, Command* obj) {
|
||||
#define PARSE_AS(name, string) \
|
||||
if (content == #string) { \
|
||||
*obj = name; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
PARSE_AS(Command::Callees, "callees");
|
||||
PARSE_AS(Command::Callers, "callers");
|
||||
PARSE_AS(Command::FindAllUsages, "find-all-usages");
|
||||
PARSE_AS(Command::FindInterestingUsages, "find-interesting-usages");
|
||||
PARSE_AS(Command::GotoReferenced, "goto-referenced");
|
||||
PARSE_AS(Command::Hierarchy, "hierarchy");
|
||||
PARSE_AS(Command::Outline, "outline");
|
||||
PARSE_AS(Command::Search, "search");
|
||||
|
||||
return false;
|
||||
#undef PARSE_AS
|
||||
}
|
||||
|
||||
|
||||
std::unordered_map<std::string, std::string> ParseOptions(int argc, char** argv) {
|
||||
std::unordered_map<std::string, std::string> output;
|
||||
@ -82,118 +51,7 @@ bool HasOption(const std::unordered_map<std::string, std::string>& options, cons
|
||||
return options.find(option) != options.end();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// Connects to a running --project-directory instance. Forks
|
||||
// and creates it if not running.
|
||||
//
|
||||
// Implements language server spec.
|
||||
indexer.exe --language-server
|
||||
|
||||
// Holds the runtime db that the --language-server instance
|
||||
// runs queries against.
|
||||
indexer.exe --project-directory /work2/chrome/src
|
||||
|
||||
// Created from the --project-directory (server) instance
|
||||
indexer.exe --index-file /work2/chrome/src/chrome/foo.cc
|
||||
|
||||
// Configuration data is read from a JSON file.
|
||||
{
|
||||
"max_threads": 40,
|
||||
"cache_directory": "/work/indexer_cache/"
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
struct IpcMessage_IsAlive : public BaseIpcMessage<IpcMessage_IsAlive> {
|
||||
static IpcMessageId id;
|
||||
};
|
||||
|
||||
IpcMessageId IpcMessage_IsAlive::id = "IsAlive";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct IpcMessage_DocumentSymbolsRequest : public BaseIpcMessage<IpcMessage_DocumentSymbolsRequest> {
|
||||
std::string document;
|
||||
|
||||
// BaseIpcMessage:
|
||||
static IpcMessageId id;
|
||||
void Serialize(Writer& writer) override {
|
||||
writer.String(document.c_str());
|
||||
}
|
||||
void Deserialize(Reader& reader) override {
|
||||
document = reader.GetString();
|
||||
}
|
||||
};
|
||||
IpcMessageId IpcMessage_DocumentSymbolsRequest::id = "IpcMessage_DocumentSymbolsRequest";
|
||||
|
||||
struct IpcMessage_DocumentSymbolsResponse : public BaseIpcMessage<IpcMessage_DocumentSymbolsResponse> {
|
||||
std::vector<language_server_api::SymbolInformation> symbols;
|
||||
|
||||
// BaseIpcMessage:
|
||||
static IpcMessageId id;
|
||||
};
|
||||
IpcMessageId IpcMessage_DocumentSymbolsResponse::id = "IpcMessage_DocumentSymbolsResponse";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void QueryDbMain() {
|
||||
IpcServer ipc("languageserver");
|
||||
|
||||
while (true) {
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> messages = ipc.TakeMessages();
|
||||
|
||||
for (auto& message : messages) {
|
||||
std::cout << "Processing message " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
|
||||
if (message->runtime_id() == IpcMessage_IsAlive::id) {
|
||||
IpcMessage_IsAlive response;
|
||||
ipc.SendToClient(0, &response); // todo: make non-blocking
|
||||
break;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Unhandled IPC message with kind " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
|
||||
void EmitReferences(IpcClient& ipc) {
|
||||
|
||||
}
|
||||
|
||||
// Separate thread whose only job is to read from stdin and
|
||||
// dispatch read commands to the actual indexer program. This
|
||||
// cannot be done on the main thread because reading from std::cin
|
||||
// blocks.
|
||||
void LanguageServerStdinToServerDispatcher(IpcClient& ipc) {
|
||||
while (true) {
|
||||
std::string input;
|
||||
std::cin >> input;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParseRpc(const std::string& method, const rapidjson::GenericValue<rapidjson::UTF8<>>& params) {
|
||||
}
|
||||
|
||||
std::unique_ptr<language_server_api::InMessage> ParseMessage() {
|
||||
int content_length = -1;
|
||||
@ -216,7 +74,11 @@ std::unique_ptr<language_server_api::InMessage> ParseMessage() {
|
||||
break;
|
||||
}
|
||||
|
||||
assert(content_length >= 0);
|
||||
// bad input that is not a message.
|
||||
if (content_length < 0) {
|
||||
std::cerr << "parsing command failed (no Content-Length header)" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string content;
|
||||
content.reserve(content_length);
|
||||
@ -231,41 +93,322 @@ std::unique_ptr<language_server_api::InMessage> ParseMessage() {
|
||||
assert(!document.HasParseError());
|
||||
|
||||
return language_server_api::MessageRegistry::instance()->Parse(document);
|
||||
|
||||
/*
|
||||
std::string id;
|
||||
if (document["id"].IsString())
|
||||
id = document["id"].GetString();
|
||||
else
|
||||
id = std::to_string(document["id"].GetInt());
|
||||
std::string method = document["method"].GetString();
|
||||
auto& params = document["params"];
|
||||
|
||||
|
||||
// Send initialize response.
|
||||
{
|
||||
std::string content =
|
||||
R"foo({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"result": {
|
||||
"capabilities": {
|
||||
"documentSymbolProvider": true
|
||||
}
|
||||
}
|
||||
})foo";
|
||||
std::cout << "Content-Length: " << content.size();
|
||||
std::cout << (char)13 << char(10) << char(13) << char(10);
|
||||
std::cout << content;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Main loop for the language server. |ipc| is connected to
|
||||
// a server.
|
||||
void LanguageServerLoop(IpcClient* ipc) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct IpcMessage_Quit : public BaseIpcMessage<IpcMessage_Quit> {
|
||||
static IpcMessageId kId;
|
||||
};
|
||||
IpcMessageId IpcMessage_Quit::kId = "Quit";
|
||||
|
||||
|
||||
struct IpcMessage_IsAlive : public BaseIpcMessage<IpcMessage_IsAlive> {
|
||||
static IpcMessageId kId;
|
||||
};
|
||||
IpcMessageId IpcMessage_IsAlive::kId = "IsAlive";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct IpcMessage_OpenProject : public BaseIpcMessage<IpcMessage_OpenProject> {
|
||||
static IpcMessageId kId;
|
||||
|
||||
std::string project_path;
|
||||
|
||||
// BaseIpcMessage:
|
||||
void Serialize(Writer& writer) override {
|
||||
writer.String(project_path.c_str(), project_path.size());
|
||||
}
|
||||
void Deserialize(Reader& reader) override {
|
||||
project_path = reader.GetString();
|
||||
}
|
||||
};
|
||||
IpcMessageId IpcMessage_OpenProject::kId = "OpenProject";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct IpcMessage_DocumentSymbolsRequest : public BaseIpcMessage<IpcMessage_DocumentSymbolsRequest> {
|
||||
language_server_api::RequestId id;
|
||||
std::string document;
|
||||
|
||||
// BaseIpcMessage:
|
||||
static IpcMessageId kId;
|
||||
void Serialize(Writer& writer) override {
|
||||
using namespace language_server_api;
|
||||
auto& value = *this;
|
||||
|
||||
writer.StartObject();
|
||||
SERIALIZE_MEMBER(id);
|
||||
SERIALIZE_MEMBER(document);
|
||||
writer.EndObject();
|
||||
}
|
||||
void Deserialize(Reader& reader) override {
|
||||
using namespace language_server_api;
|
||||
auto& value = *this;
|
||||
|
||||
DESERIALIZE_MEMBER(id);
|
||||
DESERIALIZE_MEMBER(document);
|
||||
}
|
||||
};
|
||||
IpcMessageId IpcMessage_DocumentSymbolsRequest::kId = "IpcMessage_DocumentSymbolsRequest";
|
||||
|
||||
struct IpcMessage_DocumentSymbolsResponse : public BaseIpcMessage<IpcMessage_DocumentSymbolsResponse> {
|
||||
language_server_api::RequestId id;
|
||||
std::vector<language_server_api::SymbolInformation> symbols;
|
||||
|
||||
// BaseIpcMessage:
|
||||
static IpcMessageId kId;
|
||||
void Serialize(Writer& writer) override {
|
||||
using namespace language_server_api;
|
||||
auto& value = *this;
|
||||
|
||||
writer.StartObject();
|
||||
SERIALIZE_MEMBER(id);
|
||||
SERIALIZE_MEMBER(symbols);
|
||||
writer.EndObject();
|
||||
}
|
||||
void Deserialize(Reader& reader) override {
|
||||
using namespace language_server_api;
|
||||
auto& value = *this;
|
||||
|
||||
DESERIALIZE_MEMBER(id);
|
||||
DESERIALIZE_MEMBER(symbols);
|
||||
}
|
||||
};
|
||||
IpcMessageId IpcMessage_DocumentSymbolsResponse::kId = "IpcMessage_DocumentSymbolsResponse";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void QueryDbMainLoop(IpcServer* ipc, QueryableDatabase* db) {
|
||||
using namespace language_server_api;
|
||||
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> messages = ipc->TakeMessages();
|
||||
|
||||
for (auto& message : messages) {
|
||||
std::cerr << "Processing message " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
|
||||
if (IpcMessage_Quit::kId == message->runtime_id()) {
|
||||
break;
|
||||
}
|
||||
|
||||
else if (IpcMessage_IsAlive::kId == message->runtime_id()) {
|
||||
IpcMessage_IsAlive response;
|
||||
ipc->SendToClient(0, &response); // todo: make non-blocking
|
||||
}
|
||||
|
||||
else if (IpcMessage_OpenProject::kId == message->runtime_id()) {
|
||||
IpcMessage_OpenProject* msg = static_cast<IpcMessage_OpenProject*>(message.get());
|
||||
std::string path = msg->project_path;
|
||||
|
||||
|
||||
std::vector<CompilationEntry> entries = LoadCompilationEntriesFromDirectory(path);
|
||||
for (int i = 0; i < entries.size(); ++i) {
|
||||
const CompilationEntry& entry = entries[i];
|
||||
std::string filepath = path + "/" + entry.filename;
|
||||
std::cerr << "[" << i << "/" << (entries.size() - 1) << "] Parsing file " << filepath << std::endl;
|
||||
IndexedFile file = Parse(filepath, entry.args);
|
||||
IndexUpdate update(file);
|
||||
db->ApplyIndexUpdate(&update);
|
||||
}
|
||||
std::cerr << "Done" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
else if (IpcMessage_DocumentSymbolsRequest::kId == message->runtime_id()) {
|
||||
auto msg = static_cast<IpcMessage_DocumentSymbolsRequest*>(message.get());
|
||||
|
||||
IpcMessage_DocumentSymbolsResponse response;
|
||||
response.id = msg->id;
|
||||
|
||||
std::cerr << "Wanted file " << msg->document << std::endl;
|
||||
for (auto& file : db->files) {
|
||||
std::cerr << " - Have file " << file.file_id << std::endl;
|
||||
|
||||
// TODO: make sure we normalize ids!
|
||||
// TODO: hashmap lookup.
|
||||
if (file.file_id == msg->document) {
|
||||
std::cerr << "Found file" << std::endl;
|
||||
|
||||
|
||||
for (UsrRef ref : file.outline) {
|
||||
SymbolIdx symbol = db->usr_to_symbol[ref.usr];
|
||||
|
||||
SymbolInformation info;
|
||||
info.location.range.start.line = ref.loc.line - 1; // TODO: cleanup indexer to negate by 1.
|
||||
info.location.range.start.character = ref.loc.column - 1; // TODO: cleanup indexer to negate by 1.
|
||||
// TODO: store range information.
|
||||
info.location.range.end.line = info.location.range.start.line;
|
||||
info.location.range.end.character = info.location.range.start.character;
|
||||
|
||||
// TODO: cleanup namespace/naming so there is only one SymbolKind.
|
||||
switch (symbol.kind) {
|
||||
case ::SymbolKind::Type:
|
||||
{
|
||||
QueryableTypeDef& def = db->types[symbol.idx];
|
||||
info.name = def.def.qualified_name;
|
||||
info.kind = language_server_api::SymbolKind::Class;
|
||||
break;
|
||||
}
|
||||
case ::SymbolKind::Func:
|
||||
{
|
||||
QueryableFuncDef& def = db->funcs[symbol.idx];
|
||||
info.name = def.def.qualified_name;
|
||||
if (def.def.declaring_type.has_value()) {
|
||||
info.kind = language_server_api::SymbolKind::Method;
|
||||
Usr declaring = def.def.declaring_type.value();
|
||||
info.containerName = db->types[db->usr_to_symbol[declaring].idx].def.qualified_name;
|
||||
}
|
||||
else {
|
||||
info.kind = language_server_api::SymbolKind::Function;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ::SymbolKind::Var:
|
||||
{
|
||||
QueryableVarDef& def = db->vars[symbol.idx];
|
||||
info.name = def.def.qualified_name;
|
||||
info.kind = language_server_api::SymbolKind::Variable;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO
|
||||
//info.containerName = "fooey";
|
||||
|
||||
response.symbols.push_back(info);
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ipc->SendToClient(0, &response);
|
||||
}
|
||||
|
||||
|
||||
else {
|
||||
std::cerr << "Unhandled IPC message with kind " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO: global lock on stderr output.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Separate thread whose only job is to read from stdin and
|
||||
// dispatch read commands to the actual indexer program. This
|
||||
// cannot be done on the main thread because reading from std::cin
|
||||
// blocks.
|
||||
//
|
||||
// |ipc| is connected to a server.
|
||||
void LanguageServerStdinLoop(IpcClient* ipc) {
|
||||
using namespace language_server_api;
|
||||
|
||||
while (true) {
|
||||
@ -279,8 +422,15 @@ void LanguageServerLoop(IpcClient* ipc) {
|
||||
switch (message->method_id) {
|
||||
case MethodId::Initialize:
|
||||
{
|
||||
// TODO: response should take id as input.
|
||||
// TODO: message should not have top-level id.
|
||||
auto request = static_cast<In_InitializeRequest*>(message.get());
|
||||
if (request->params.rootUri) {
|
||||
std::string project_path = request->params.rootUri->GetPath();
|
||||
std::cerr << "Initialize in directory " << project_path << std::endl;
|
||||
IpcMessage_OpenProject open_project;
|
||||
open_project.project_path = project_path;
|
||||
ipc->SendToServer(&open_project);
|
||||
}
|
||||
|
||||
auto response = Out_InitializeResponse();
|
||||
response.id = message->id.value();
|
||||
response.result.capabilities.documentSymbolProvider = true;
|
||||
@ -290,157 +440,169 @@ void LanguageServerLoop(IpcClient* ipc) {
|
||||
|
||||
case MethodId::TextDocumentDocumentSymbol:
|
||||
{
|
||||
auto response = Out_DocumentSymbolResponse();
|
||||
response.id = message->id.value();
|
||||
// TODO: response should take id as input.
|
||||
// TODO: message should not have top-level id.
|
||||
auto request = static_cast<In_DocumentSymbolRequest*>(message.get());
|
||||
|
||||
for (int i = 0; i < 2500; ++i) {
|
||||
SymbolInformation info;
|
||||
info.containerName = "fooContainer";
|
||||
info.kind = language_server_api::SymbolKind::Field;
|
||||
info.location.range.start.line = 5;
|
||||
info.location.range.end.character = 20;
|
||||
info.location.range.end.line = 5;
|
||||
info.location.range.end.character = 25;
|
||||
info.name = "Foobar";
|
||||
response.result.push_back(info);
|
||||
}
|
||||
|
||||
response.Send();
|
||||
IpcMessage_DocumentSymbolsRequest ipc_request;
|
||||
ipc_request.id = request->id.value();
|
||||
ipc_request.document = request->params.textDocument.uri.GetPath();
|
||||
std::cerr << "Request textDocument=" << ipc_request.document << std::endl;
|
||||
ipc->SendToServer(&ipc_request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LanguageServerMainLoop(IpcClient* ipc) {
|
||||
using namespace language_server_api;
|
||||
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> messages = ipc->TakeMessages();
|
||||
for (auto& message : messages) {
|
||||
if (IpcMessage_Quit::kId == message->runtime_id()) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
else if (IpcMessage_DocumentSymbolsResponse::kId == message->runtime_id()) {
|
||||
auto msg = static_cast<IpcMessage_DocumentSymbolsResponse*>(message.get());
|
||||
|
||||
void LanguageServerMain() {
|
||||
IpcClient ipc("languageserver", 0);
|
||||
auto response = Out_DocumentSymbolResponse();
|
||||
response.id = msg->id;
|
||||
response.result = msg->symbols;
|
||||
response.Send();
|
||||
std::cerr << "Send symbol response to client (" << response.result.size() << " symbols)" << std::endl;
|
||||
}
|
||||
|
||||
else {
|
||||
std::cerr << "Unhandled IPC message with kind " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LanguageServerMain(std::string process_name) {
|
||||
IpcClient client_ipc("languageserver", 0);
|
||||
|
||||
// Discard any left-over messages from previous runs.
|
||||
ipc.TakeMessages();
|
||||
client_ipc.TakeMessages();
|
||||
|
||||
// Emit an alive check. Sleep so the server has time to respond.
|
||||
IpcMessage_IsAlive check_alive;
|
||||
ipc.SendToServer(&check_alive);
|
||||
client_ipc.SendToServer(&check_alive);
|
||||
|
||||
// TODO: Tune this value or make it configurable.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
|
||||
// Check if we got an IsAlive message back.
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> messages = ipc.TakeMessages();
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> messages = client_ipc.TakeMessages();
|
||||
bool has_server = false;
|
||||
for (auto& message : messages) {
|
||||
if (message->runtime_id() == IpcMessage_IsAlive::id) {
|
||||
if (message->runtime_id() == IpcMessage_IsAlive::kId) {
|
||||
has_server = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No server is running. Start it.
|
||||
//if (!has_server) {
|
||||
// std::cerr << "Unable to detect running indexer server" << std::endl;
|
||||
// exit(1);
|
||||
//}
|
||||
#if false
|
||||
if (!has_server) {
|
||||
if (process_name.empty())
|
||||
return;
|
||||
|
||||
std::thread stdio_reader(&LanguageServerLoop, &ipc);
|
||||
Process p(process_name + " --querydb", "",
|
||||
/*stdout*/[](const char* bytes, size_t n) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
std::cerr << bytes[i];
|
||||
},
|
||||
/*stderr*/[](const char* bytes, size_t n) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
std::cerr << bytes[i];
|
||||
},
|
||||
/*open_stdin*/false);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// Pass empty process name so we only try to start the querydb once.
|
||||
LanguageServerMain("");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//std::cout << "Found indexer server" << std::endl;
|
||||
//LanguageServerLoop(ipc);
|
||||
|
||||
// TODO: This is used for debugging, so we can attach to the client.
|
||||
|
||||
|
||||
//std::cout << "garbagelkadklasldk" << std::endl;
|
||||
|
||||
bool should_break = true;
|
||||
while (should_break)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
// for debugging attach
|
||||
//std::this_thread::sleep_for(std::chrono::seconds(4));
|
||||
|
||||
|
||||
//std::cout.flush();
|
||||
/*
|
||||
language_server_api::ShowMessageOutNotification show;
|
||||
show.type = language_server_api::MessageType::Info;
|
||||
show.message = "hello";
|
||||
show.Send();
|
||||
*/
|
||||
std::thread stdio_reader(&LanguageServerStdinLoop, &client_ipc);
|
||||
|
||||
|
||||
// No server. Run it in-process.
|
||||
if (!has_server) {
|
||||
|
||||
QueryableDatabase db;
|
||||
IpcServer server_ipc("languageserver");
|
||||
|
||||
while (true) {
|
||||
QueryDbMainLoop(&server_ipc, &db);
|
||||
LanguageServerMainLoop(&client_ipc);
|
||||
// TODO: use a condition variable.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
while (true) {
|
||||
LanguageServerMainLoop(&client_ipc);
|
||||
// TODO: use a condition variable.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if false
|
||||
|
||||
struct IpcMessage_IsAlive : public BaseIpcMessage {
|
||||
IpcMessage_IsAlive();
|
||||
|
||||
// BaseIpcMessage:
|
||||
void Serialize(Writer& writer) override;
|
||||
void Deserialize(Reader& reader) override;
|
||||
};
|
||||
|
||||
struct IpcMessage_ImportIndex : public BaseIpcMessage {
|
||||
std::string path;
|
||||
|
||||
IpcMessage_ImportIndex();
|
||||
|
||||
// BaseMessage:
|
||||
void Serialize(Writer& writer) override;
|
||||
void Deserialize(Reader& reader) override;
|
||||
};
|
||||
|
||||
struct IpcMessage_CreateIndex : public BaseIpcMessage {
|
||||
std::string path;
|
||||
std::vector<std::string> args;
|
||||
|
||||
IpcMessage_CreateIndex();
|
||||
|
||||
// BaseMessage:
|
||||
void Serialize(Writer& writer) override;
|
||||
void Deserialize(Reader& reader) override;
|
||||
};
|
||||
|
||||
|
||||
IpcMessage_IsAlive::IpcMessage_IsAlive() {
|
||||
kind = JsonMessage::Kind::IsAlive;
|
||||
}
|
||||
|
||||
void IpcMessage_IsAlive::Serialize(Writer& writer) {}
|
||||
|
||||
void IpcMessage_IsAlive::Deserialize(Reader& reader) {}
|
||||
|
||||
IpcMessage_ImportIndex::IpcMessage_ImportIndex() {
|
||||
kind = JsonMessage::Kind::ImportIndex;
|
||||
}
|
||||
|
||||
void IpcMessage_ImportIndex::Serialize(Writer& writer) {
|
||||
writer.StartObject();
|
||||
::Serialize(writer, "path", path);
|
||||
writer.EndObject();
|
||||
}
|
||||
void IpcMessage_ImportIndex::Deserialize(Reader& reader) {
|
||||
::Deserialize(reader, "path", path);
|
||||
}
|
||||
|
||||
IpcMessage_CreateIndex::IpcMessage_CreateIndex() {
|
||||
kind = JsonMessage::Kind::CreateIndex;
|
||||
}
|
||||
|
||||
void IpcMessage_CreateIndex::Serialize(Writer& writer) {
|
||||
writer.StartObject();
|
||||
::Serialize(writer, "path", path);
|
||||
::Serialize(writer, "args", args);
|
||||
writer.EndObject();
|
||||
}
|
||||
void IpcMessage_CreateIndex::Deserialize(Reader& reader) {
|
||||
::Deserialize(reader, "path", path);
|
||||
::Deserialize(reader, "args", args);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// We need to write to stdout in binary mode because in Windows, writing
|
||||
@ -451,9 +613,13 @@ int main(int argc, char** argv) {
|
||||
_setmode(_fileno(stdin), O_BINARY);
|
||||
#endif
|
||||
|
||||
std::cerr << "Starting language server" << std::endl;
|
||||
|
||||
|
||||
IpcRegistry::instance()->Register<IpcMessage_Quit>();
|
||||
|
||||
IpcRegistry::instance()->Register<IpcMessage_IsAlive>();
|
||||
IpcRegistry::instance()->Register<IpcMessage_OpenProject>();
|
||||
|
||||
IpcRegistry::instance()->Register<IpcMessage_DocumentSymbolsRequest>();
|
||||
IpcRegistry::instance()->Register<IpcMessage_DocumentSymbolsResponse>();
|
||||
|
||||
@ -462,128 +628,34 @@ int main(int argc, char** argv) {
|
||||
language_server_api::MessageRegistry::instance()->Register<language_server_api::In_InitializedNotification>();
|
||||
language_server_api::MessageRegistry::instance()->Register<language_server_api::In_DocumentSymbolRequest>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::unordered_map<std::string, std::string> options = ParseOptions(argc, argv);
|
||||
|
||||
if (HasOption(options, "--language-server")) {
|
||||
LanguageServerMain();
|
||||
std::cerr << "Running language server" << std::endl;
|
||||
LanguageServerMain(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (HasOption(options, "--querydb")) {
|
||||
QueryDbMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LanguageServerMain();
|
||||
return 0;
|
||||
|
||||
if (argc == 1 || options.find("--help") != options.end()) {
|
||||
std::cout << R"help(clang-indexer help:
|
||||
|
||||
General:
|
||||
--help Print this help information.
|
||||
--help-commands
|
||||
Print all available query commands.
|
||||
--project Path to compile_commands.json. Needed for the server, and
|
||||
optionally by clients if there are multiple servers running.
|
||||
--print-config
|
||||
Emit all configuration data this executable is using.
|
||||
|
||||
|
||||
Server:
|
||||
--server If present, this binary will run in server mode. The binary
|
||||
will not return until killed or an exit is requested. The
|
||||
server computes and caches an index of the entire program
|
||||
which is then queried by short-lived client processes. A
|
||||
client is created by running this binary with a --command
|
||||
flag.
|
||||
--cache-dir Directory to cache the index and other useful information. If
|
||||
a previous cache is present, the database will try to reuse
|
||||
it. If this flag is not present, the database will be
|
||||
in-memory only.
|
||||
--threads Number of threads to use for indexing and querying tasks.
|
||||
This value is optional; a good estimate is computed by
|
||||
default.
|
||||
|
||||
|
||||
Client:
|
||||
--command Execute a query command against the index. See
|
||||
--command-help for a listing of valid commands and a
|
||||
description of what they do. Presence of this flag indicates
|
||||
that the indexer is in client mode; this flag is mutually
|
||||
exclusive with --server.
|
||||
--location Location of the query. Some commands require only a file,
|
||||
other require a line and column as well. Format is
|
||||
filename[:line:column]. For example, "foobar.cc" and
|
||||
"foobar.cc:1:10" are valid inputs.
|
||||
--preferred-symbol-location
|
||||
When looking up symbols, try to return either the
|
||||
'declaration' or the 'definition'. Defaults to 'definition'.
|
||||
)help";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (HasOption(options, "--help-commands")) {
|
||||
std::cout << R"(Available commands:
|
||||
|
||||
callees:
|
||||
callers:
|
||||
Emit all functions (with location) that this function calls ("callees") or
|
||||
that call this function ("callers"). Requires a location.
|
||||
|
||||
find-all-usages:
|
||||
Emit every usage of the given symbol. This is intended to support a rename
|
||||
refactoring. This output contains many uninteresting usages of symbols;
|
||||
prefer find-interesting-usges. Requires a location.
|
||||
|
||||
find-interesting-usages:
|
||||
Emit only usages of the given symbol which are semantically interesting.
|
||||
Requires a location.
|
||||
|
||||
goto-referenced:
|
||||
Find an associated reference (either definition or declaration) for the
|
||||
given symbol. Requires a location.
|
||||
|
||||
hierarchy:
|
||||
List the type hierarchy (ie, inherited and derived members) for the given
|
||||
method or type. Requires a location.
|
||||
|
||||
outline:
|
||||
Emit a file outline, listing all of the symbols in the file.
|
||||
|
||||
search:
|
||||
Search for a symbol by name.
|
||||
)";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (HasOption(options, "--project")) {
|
||||
std::vector<CompilationEntry> entries = LoadCompilationEntriesFromDirectory(options["--project"]);
|
||||
|
||||
|
||||
for (const CompilationEntry& entry : entries) {
|
||||
std::cout << "Parsing " << entry.filename << std::endl;
|
||||
QueryableDatabase db;
|
||||
IndexedFile file = Parse(entry.filename, entry.args);
|
||||
|
||||
IndexUpdate update(file);
|
||||
db.ApplyIndexUpdate(&update);
|
||||
//std::cout << db.ToString() << std::endl << std::endl;
|
||||
else if (HasOption(options, "--querydb")) {
|
||||
std::cerr << "Running querydb" << std::endl;
|
||||
QueryableDatabase db;
|
||||
IpcServer ipc("languageserver");
|
||||
while (true) {
|
||||
QueryDbMainLoop(&ipc, &db);
|
||||
// TODO: use a condition variable.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
|
||||
std::cin.get();
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
std::cerr << "Running language server" << std::endl;
|
||||
LanguageServerMain(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (HasOption(options, "--command")) {
|
||||
Command command;
|
||||
if (!ParseCommand(options["--command"], &command))
|
||||
Fail("Unknown command \"" + options["--command"] + "\"; see --help-commands");
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::cout << "Invalid arguments. Try --help.";
|
||||
exit(1);
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
@ -5,12 +5,42 @@
|
||||
|
||||
#include "libclangmm/Utility.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
// See http://stackoverflow.com/a/2072890
|
||||
bool EndsWith(const std::string& value, const std::string& ending) {
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
std::vector<CompilationEntry> LoadFromDirectoryListing(const std::string& project_directory) {
|
||||
std::vector<CompilationEntry> result;
|
||||
|
||||
std::vector<std::string> files = GetFilesInFolder(project_directory, false /*add_folder_to_path*/);
|
||||
|
||||
for (const std::string& file : files) {
|
||||
if (EndsWith(file, ".cc") || EndsWith(file, ".cpp") ||
|
||||
EndsWith(file, ".c") || EndsWith(file, ".h") ||
|
||||
EndsWith(file, ".hpp")) {
|
||||
|
||||
CompilationEntry entry;
|
||||
entry.directory = ".";
|
||||
entry.filename = file;
|
||||
entry.args = {};
|
||||
result.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<CompilationEntry> LoadCompilationEntriesFromDirectory(const std::string& project_directory) {
|
||||
CXCompilationDatabase_Error cx_db_load_error;
|
||||
CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(project_directory.c_str(), &cx_db_load_error);
|
||||
if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) {
|
||||
std::cerr << "[FATAL]: Unable to load compile_commands.json located at \"" << project_directory << "\"";
|
||||
exit(1);
|
||||
std::cerr << "Unable to load compile_commands.json located at \"" << project_directory << "\"; using directory listing instead." << std::endl;
|
||||
return LoadFromDirectoryListing(project_directory);
|
||||
}
|
||||
|
||||
CXCompileCommands cx_commands = clang_CompilationDatabase_getAllCompileCommands(cx_db);
|
||||
|
36
indexer.cpp
36
indexer.cpp
@ -87,7 +87,7 @@ std::string IndexedFile::ToString() {
|
||||
|
||||
IndexedTypeDef::IndexedTypeDef(TypeId id, const std::string& usr) : id(id), def(usr) {
|
||||
assert(usr.size() > 0);
|
||||
//std::cout << "Creating type with usr " << usr << std::endl;
|
||||
//std::cerr << "Creating type with usr " << usr << std::endl;
|
||||
}
|
||||
|
||||
void IndexedTypeDef::AddUsage(Location loc, bool insert_if_not_present) {
|
||||
@ -196,8 +196,8 @@ CXIdxClientContainer startedTranslationUnit(CXClientData client_data, void *rese
|
||||
|
||||
clang::VisiterResult DumpVisitor(clang::Cursor cursor, clang::Cursor parent, int* level) {
|
||||
for (int i = 0; i < *level; ++i)
|
||||
std::cout << " ";
|
||||
std::cout << clang::ToString(cursor.get_kind()) << " " << cursor.get_spelling() << std::endl;
|
||||
std::cerr << " ";
|
||||
std::cerr << clang::ToString(cursor.get_kind()) << " " << cursor.get_spelling() << std::endl;
|
||||
|
||||
*level += 1;
|
||||
cursor.VisitChildren(&DumpVisitor, level);
|
||||
@ -432,6 +432,8 @@ optional<TypeId> ResolveDeclToType(IndexedFile* db, clang::Cursor decl_cursor,
|
||||
// The second TypeRef is an uninteresting usage.
|
||||
bool process_last_type_ref = true;
|
||||
if (IsTypeDefinition(semantic_container) && !IsTypeDefinition(lexical_container)) {
|
||||
//if (!decl_cursor.is_definition())
|
||||
// decl_cursor = decl_cursor.get_definition();
|
||||
assert(decl_cursor.is_definition());
|
||||
process_last_type_ref = false;
|
||||
}
|
||||
@ -713,15 +715,15 @@ void indexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) {
|
||||
}
|
||||
|
||||
default:
|
||||
std::cout << "!! Unhandled indexDeclaration: " << clang::Cursor(decl->cursor).ToString() << " at " << db->id_cache.Resolve(decl->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cout << " entityInfo->kind = " << decl->entityInfo->kind << std::endl;
|
||||
std::cout << " entityInfo->USR = " << decl->entityInfo->USR << std::endl;
|
||||
std::cerr << "!! Unhandled indexDeclaration: " << clang::Cursor(decl->cursor).ToString() << " at " << db->id_cache.Resolve(decl->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cerr << " entityInfo->kind = " << decl->entityInfo->kind << std::endl;
|
||||
std::cerr << " entityInfo->USR = " << decl->entityInfo->USR << std::endl;
|
||||
if (decl->declAsContainer)
|
||||
std::cout << " declAsContainer = " << clang::Cursor(decl->declAsContainer->cursor).ToString() << std::endl;
|
||||
std::cerr << " declAsContainer = " << clang::Cursor(decl->declAsContainer->cursor).ToString() << std::endl;
|
||||
if (decl->semanticContainer)
|
||||
std::cout << " semanticContainer = " << clang::Cursor(decl->semanticContainer->cursor).ToString() << std::endl;
|
||||
std::cerr << " semanticContainer = " << clang::Cursor(decl->semanticContainer->cursor).ToString() << std::endl;
|
||||
if (decl->lexicalContainer)
|
||||
std::cout << " lexicalContainer = " << clang::Cursor(decl->lexicalContainer->cursor).get_usr() << std::endl;
|
||||
std::cerr << " lexicalContainer = " << clang::Cursor(decl->lexicalContainer->cursor).get_usr() << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -849,18 +851,18 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re
|
||||
}
|
||||
|
||||
default:
|
||||
std::cout << "!! Unhandled indexEntityReference: " << cursor.ToString() << " at " << db->id_cache.Resolve(ref->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cout << " ref->referencedEntity->kind = " << ref->referencedEntity->kind << std::endl;
|
||||
std::cerr << "!! Unhandled indexEntityReference: " << cursor.ToString() << " at " << db->id_cache.Resolve(ref->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cerr << " ref->referencedEntity->kind = " << ref->referencedEntity->kind << std::endl;
|
||||
if (ref->parentEntity)
|
||||
std::cout << " ref->parentEntity->kind = " << ref->parentEntity->kind << std::endl;
|
||||
std::cout << " ref->loc = " << db->id_cache.Resolve(ref->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cout << " ref->kind = " << ref->kind << std::endl;
|
||||
std::cerr << " ref->parentEntity->kind = " << ref->parentEntity->kind << std::endl;
|
||||
std::cerr << " ref->loc = " << db->id_cache.Resolve(ref->loc, false /*interesting*/).ToString() << std::endl;
|
||||
std::cerr << " ref->kind = " << ref->kind << std::endl;
|
||||
if (ref->parentEntity)
|
||||
std::cout << " parentEntity = " << clang::Cursor(ref->parentEntity->cursor).ToString() << std::endl;
|
||||
std::cerr << " parentEntity = " << clang::Cursor(ref->parentEntity->cursor).ToString() << std::endl;
|
||||
if (ref->referencedEntity)
|
||||
std::cout << " referencedEntity = " << clang::Cursor(ref->referencedEntity->cursor).ToString() << std::endl;
|
||||
std::cerr << " referencedEntity = " << clang::Cursor(ref->referencedEntity->cursor).ToString() << std::endl;
|
||||
if (ref->container)
|
||||
std::cout << " container = " << clang::Cursor(ref->container->cursor).ToString() << std::endl;
|
||||
std::cerr << " container = " << clang::Cursor(ref->container->cursor).ToString() << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
4
ipc.cc
4
ipc.cc
@ -52,7 +52,7 @@ void IpcDirectionalChannel::PushMessage(BaseIpcMessageElided* message) {
|
||||
writer.SetIndent(' ', 2);
|
||||
message->Serialize(writer);
|
||||
|
||||
//std::cout << "Sending message with id " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
//std::cerr << "Sending message with id " << message->runtime_id() << " (hash " << message->hashed_runtime_id() << ")" << std::endl;
|
||||
|
||||
size_t payload_size = strlen(output.GetString());
|
||||
assert(payload_size < shmem_size && "Increase shared memory size, payload will never fit");
|
||||
@ -62,7 +62,7 @@ void IpcDirectionalChannel::PushMessage(BaseIpcMessageElided* message) {
|
||||
while (true) {
|
||||
if (!first) {
|
||||
if (!did_log) {
|
||||
std::cout << "[info]: shmem full, waiting" << std::endl; // TODO: remove
|
||||
std::cerr << "[info]: shmem full, waiting" << std::endl; // TODO: remove
|
||||
did_log = true;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||
|
9
ipc.h
9
ipc.h
@ -15,6 +15,7 @@
|
||||
using Writer = rapidjson::PrettyWriter<rapidjson::StringBuffer>;
|
||||
using Reader = rapidjson::Document;
|
||||
|
||||
// TODO: We need to add support for payloads larger than the maximum shared memory buffer size.
|
||||
|
||||
// Messages are funky objects. They contain potentially variable amounts of
|
||||
// data and are passed between processes. This means that they need to be
|
||||
@ -42,12 +43,12 @@ struct BaseIpcMessageElided {
|
||||
// Usage:
|
||||
//
|
||||
// class IpcMessage_Foo : public BaseIpcMessage<IpcMessage_Foo> {
|
||||
// static IpcMessageId id;
|
||||
// static IpcMessageId kId;
|
||||
//
|
||||
// // BaseIpcMessage:
|
||||
// ...
|
||||
// }
|
||||
// IpcMessageId IpcMessage_Foo::id = "Foo";
|
||||
// IpcMessageId IpcMessage_Foo::kId = "Foo";
|
||||
//
|
||||
//
|
||||
// main() {
|
||||
@ -102,7 +103,7 @@ void IpcRegistry::Register() {
|
||||
hash_to_id = MakeUnique<std::unordered_map<int, std::string>>();
|
||||
}
|
||||
|
||||
IpcMessageId id = T::id;
|
||||
IpcMessageId id = T::kId;
|
||||
|
||||
int hash = std::hash<IpcMessageId>()(id);
|
||||
auto it = allocators->find(hash);
|
||||
@ -163,6 +164,8 @@ struct IpcClient {
|
||||
void SendToServer(BaseIpcMessageElided* message);
|
||||
std::vector<std::unique_ptr<BaseIpcMessageElided>> TakeMessages();
|
||||
|
||||
IpcDirectionalChannel* client() { return &client_; }
|
||||
|
||||
private:
|
||||
IpcDirectionalChannel server_;
|
||||
IpcDirectionalChannel client_;
|
||||
|
@ -512,15 +512,33 @@ namespace language_server_api {
|
||||
// Keep all types in the language_server_api namespace in sync with language server spec.
|
||||
// TODO
|
||||
struct DocumentUri {
|
||||
std::string path;
|
||||
std::string raw_uri;
|
||||
|
||||
std::string GetPath() {
|
||||
// TODO: make this not a hack.
|
||||
std::string result = raw_uri;
|
||||
|
||||
size_t index = result.find("%3A");
|
||||
if (index != -1) {
|
||||
result.replace(result.begin() + index, result.begin() + index + 3, ":");
|
||||
}
|
||||
|
||||
index = result.find("file://");
|
||||
if (index != -1) {
|
||||
result.replace(result.begin() + index, result.begin() + index + 8, "");
|
||||
}
|
||||
|
||||
std::replace(result.begin(), result.end(), '\\', '/');
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
void Serialize(Writer& writer, const DocumentUri& value) {
|
||||
Serialize(writer, value.path);
|
||||
Serialize(writer, value.raw_uri);
|
||||
}
|
||||
|
||||
void Deserialize(const Reader& reader, DocumentUri& value) {
|
||||
Deserialize(reader, value.path);
|
||||
Deserialize(reader, value.raw_uri);
|
||||
}
|
||||
|
||||
|
||||
@ -1344,7 +1362,7 @@ namespace language_server_api {
|
||||
|
||||
struct In_DocumentSymbolRequest : public InRequestMessage {
|
||||
const static MethodId kMethod = MethodId::TextDocumentDocumentSymbol;
|
||||
|
||||
|
||||
DocumentSymbolParams params;
|
||||
|
||||
In_DocumentSymbolRequest(optional<RequestId> id, const Reader& reader)
|
||||
@ -1449,9 +1467,9 @@ namespace language_server_api {
|
||||
};
|
||||
|
||||
|
||||
#undef SERIALIZE_MEMBER
|
||||
#undef SERIALIZE_MEMBER2
|
||||
#undef DESERIALIZE_MEMBER
|
||||
//#undef SERIALIZE_MEMBER
|
||||
//#undef SERIALIZE_MEMBER2
|
||||
//#undef DESERIALIZE_MEMBER
|
||||
|
||||
|
||||
|
||||
|
175
old.cc
Normal file
175
old.cc
Normal file
@ -0,0 +1,175 @@
|
||||
#if false
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// Connects to a running --project-directory instance. Forks
|
||||
// and creates it if not running.
|
||||
//
|
||||
// Implements language server spec.
|
||||
indexer.exe --language-server
|
||||
|
||||
// Holds the runtime db that the --language-server instance
|
||||
// runs queries against.
|
||||
indexer.exe --project-directory /work2/chrome/src
|
||||
|
||||
// Created from the --project-directory (server) instance
|
||||
indexer.exe --index-file /work2/chrome/src/chrome/foo.cc
|
||||
|
||||
// Configuration data is read from a JSON file.
|
||||
{
|
||||
"max_threads": 40,
|
||||
"cache_directory": "/work/indexer_cache/"
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
bool ParsePreferredSymbolLocation(const std::string& content, PreferredSymbolLocation* obj) {
|
||||
#define PARSE_AS(name, string) \
|
||||
if (content == #string) { \
|
||||
*obj = name; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
PARSE_AS(PreferredSymbolLocation::Declaration, "declaration");
|
||||
PARSE_AS(PreferredSymbolLocation::Definition, "definition");
|
||||
|
||||
return false;
|
||||
#undef PARSE_AS
|
||||
}
|
||||
|
||||
bool ParseCommand(const std::string& content, Command* obj) {
|
||||
#define PARSE_AS(name, string) \
|
||||
if (content == #string) { \
|
||||
*obj = name; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
PARSE_AS(Command::Callees, "callees");
|
||||
PARSE_AS(Command::Callers, "callers");
|
||||
PARSE_AS(Command::FindAllUsages, "find-all-usages");
|
||||
PARSE_AS(Command::FindInterestingUsages, "find-interesting-usages");
|
||||
PARSE_AS(Command::GotoReferenced, "goto-referenced");
|
||||
PARSE_AS(Command::Hierarchy, "hierarchy");
|
||||
PARSE_AS(Command::Outline, "outline");
|
||||
PARSE_AS(Command::Search, "search");
|
||||
|
||||
return false;
|
||||
#undef PARSE_AS
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 1 || options.find("--help") != options.end()) {
|
||||
std::cout << R"help(clang-indexer help:
|
||||
|
||||
General:
|
||||
--help Print this help information.
|
||||
--help-commands
|
||||
Print all available query commands.
|
||||
--project Path to compile_commands.json. Needed for the server, and
|
||||
optionally by clients if there are multiple servers running.
|
||||
--print-config
|
||||
Emit all configuration data this executable is using.
|
||||
|
||||
|
||||
Server:
|
||||
--server If present, this binary will run in server mode. The binary
|
||||
will not return until killed or an exit is requested. The
|
||||
server computes and caches an index of the entire program
|
||||
which is then queried by short-lived client processes. A
|
||||
client is created by running this binary with a --command
|
||||
flag.
|
||||
--cache-dir Directory to cache the index and other useful information. If
|
||||
a previous cache is present, the database will try to reuse
|
||||
it. If this flag is not present, the database will be
|
||||
in-memory only.
|
||||
--threads Number of threads to use for indexing and querying tasks.
|
||||
This value is optional; a good estimate is computed by
|
||||
default.
|
||||
|
||||
|
||||
Client:
|
||||
--command Execute a query command against the index. See
|
||||
--command-help for a listing of valid commands and a
|
||||
description of what they do. Presence of this flag indicates
|
||||
that the indexer is in client mode; this flag is mutually
|
||||
exclusive with --server.
|
||||
--location Location of the query. Some commands require only a file,
|
||||
other require a line and column as well. Format is
|
||||
filename[:line:column]. For example, "foobar.cc" and
|
||||
"foobar.cc:1:10" are valid inputs.
|
||||
--preferred-symbol-location
|
||||
When looking up symbols, try to return either the
|
||||
'declaration' or the 'definition'. Defaults to 'definition'.
|
||||
)help";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (HasOption(options, "--help-commands")) {
|
||||
std::cout << R"(Available commands:
|
||||
|
||||
callees:
|
||||
callers:
|
||||
Emit all functions (with location) that this function calls ("callees") or
|
||||
that call this function ("callers"). Requires a location.
|
||||
|
||||
find-all-usages:
|
||||
Emit every usage of the given symbol. This is intended to support a rename
|
||||
refactoring. This output contains many uninteresting usages of symbols;
|
||||
prefer find-interesting-usges. Requires a location.
|
||||
|
||||
find-interesting-usages:
|
||||
Emit only usages of the given symbol which are semantically interesting.
|
||||
Requires a location.
|
||||
|
||||
goto-referenced:
|
||||
Find an associated reference (either definition or declaration) for the
|
||||
given symbol. Requires a location.
|
||||
|
||||
hierarchy:
|
||||
List the type hierarchy (ie, inherited and derived members) for the given
|
||||
method or type. Requires a location.
|
||||
|
||||
outline:
|
||||
Emit a file outline, listing all of the symbols in the file.
|
||||
|
||||
search:
|
||||
Search for a symbol by name.
|
||||
)";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (HasOption(options, "--project")) {
|
||||
std::vector<CompilationEntry> entries = LoadCompilationEntriesFromDirectory(options["--project"]);
|
||||
|
||||
|
||||
for (const CompilationEntry& entry : entries) {
|
||||
std::cout << "Parsing " << entry.filename << std::endl;
|
||||
QueryableDatabase db;
|
||||
IndexedFile file = Parse(entry.filename, entry.args);
|
||||
|
||||
IndexUpdate update(file);
|
||||
db.ApplyIndexUpdate(&update);
|
||||
//std::cout << db.ToString() << std::endl << std::endl;
|
||||
}
|
||||
|
||||
std::cin.get();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (HasOption(options, "--command")) {
|
||||
Command command;
|
||||
if (!ParseCommand(options["--command"], &command))
|
||||
Fail("Unknown command \"" + options["--command"] + "\"; see --help-commands");
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::cout << "Invalid arguments. Try --help.";
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -16,8 +16,10 @@ struct PlatformSharedMemory {
|
||||
char* shared_start;
|
||||
};
|
||||
|
||||
const int shmem_size = 1024; // number of chars/bytes (256kb)
|
||||
const int shmem_size = 1024 * 256; // number of chars/bytes (256kb)
|
||||
|
||||
std::unique_ptr<PlatformMutex> CreatePlatformMutex(const std::string& name);
|
||||
std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(PlatformMutex* mutex);
|
||||
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(const std::string& name);
|
||||
|
||||
std::string GetWorkingDirectory();
|
@ -71,3 +71,22 @@ std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(PlatformM
|
||||
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(const std::string& name) {
|
||||
return MakeUnique<PlatformSharedMemoryWin>(name);
|
||||
}
|
||||
|
||||
// See http://stackoverflow.com/a/19535628
|
||||
std::string GetWorkingDirectory() {
|
||||
char result[MAX_PATH];
|
||||
return std::string(result, GetModuleFileName(NULL, result, MAX_PATH));
|
||||
}
|
||||
|
||||
/*
|
||||
// linux
|
||||
#include <string>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
std::string getexepath() {
|
||||
char result[ PATH_MAX ];
|
||||
ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
|
||||
return std::string( result, (count > 0) ? count : 0 );
|
||||
}
|
||||
*/
|
40
query.cc
40
query.cc
@ -74,9 +74,11 @@ std::vector<QueryableLocation> MapIdToUsr(const IdCache& id_cache, const std::ve
|
||||
}
|
||||
QueryableTypeDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const TypeDefDefinitionData<>& def) {
|
||||
QueryableTypeDef::DefUpdate result(def.usr);
|
||||
if (result.definition)
|
||||
result.short_name = def.short_name;
|
||||
result.qualified_name = def.qualified_name;
|
||||
if (def.definition)
|
||||
result.definition = MapIdToUsr(id_cache, def.definition.value());
|
||||
if (result.alias_of)
|
||||
if (def.alias_of)
|
||||
result.alias_of = MapIdToUsr(id_cache, def.alias_of.value());
|
||||
result.parents = MapIdToUsr(id_cache, def.parents);
|
||||
result.types = MapIdToUsr(id_cache, def.types);
|
||||
@ -86,11 +88,13 @@ QueryableTypeDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const TypeDefDef
|
||||
}
|
||||
QueryableFuncDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const FuncDefDefinitionData<>& def) {
|
||||
QueryableFuncDef::DefUpdate result(def.usr);
|
||||
if (result.definition)
|
||||
result.short_name = def.short_name;
|
||||
result.qualified_name = def.qualified_name;
|
||||
if (def.definition)
|
||||
result.definition = MapIdToUsr(id_cache, def.definition.value());
|
||||
if (result.declaring_type)
|
||||
if (def.declaring_type)
|
||||
result.declaring_type = MapIdToUsr(id_cache, def.declaring_type.value());
|
||||
if (result.base)
|
||||
if (def.base)
|
||||
result.base = MapIdToUsr(id_cache, def.base.value());
|
||||
result.locals = MapIdToUsr(id_cache, def.locals);
|
||||
result.callees = MapIdToUsr(id_cache, def.callees);
|
||||
@ -98,13 +102,15 @@ QueryableFuncDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const FuncDefDef
|
||||
}
|
||||
QueryableVarDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const VarDefDefinitionData<>& def) {
|
||||
QueryableVarDef::DefUpdate result(def.usr);
|
||||
if (result.declaration)
|
||||
result.short_name = def.short_name;
|
||||
result.qualified_name = def.qualified_name;
|
||||
if (def.declaration)
|
||||
result.declaration = MapIdToUsr(id_cache, def.declaration.value());
|
||||
if (result.definition)
|
||||
if (def.definition)
|
||||
result.definition = MapIdToUsr(id_cache, def.definition.value());
|
||||
if (result.variable_type)
|
||||
if (def.variable_type)
|
||||
result.variable_type = MapIdToUsr(id_cache, def.variable_type.value());
|
||||
if (result.declaring_type)
|
||||
if (def.declaring_type)
|
||||
result.declaring_type = MapIdToUsr(id_cache, def.declaring_type.value());
|
||||
return result;
|
||||
}
|
||||
@ -112,8 +118,10 @@ QueryableVarDef::DefUpdate MapIdToUsr(const IdCache& id_cache, const VarDefDefin
|
||||
QueryableFile::QueryableFile(const IndexedFile& indexed)
|
||||
: file_id(indexed.path) {
|
||||
|
||||
auto add_outline = [this, &indexed](Usr usr, Location location) {
|
||||
outline.push_back(UsrRef(usr, MapIdToUsr(indexed.id_cache, location)));
|
||||
FileId local_file_id = indexed.id_cache.file_path_to_file_id.find(indexed.path)->second;
|
||||
auto add_outline = [this, &indexed, local_file_id](Usr usr, Location location) {
|
||||
if (location.file_id() == local_file_id)
|
||||
outline.push_back(UsrRef(usr, MapIdToUsr(indexed.id_cache, location)));
|
||||
};
|
||||
|
||||
for (const IndexedTypeDef& def : indexed.types) {
|
||||
@ -357,7 +365,7 @@ IndexUpdate::IndexUpdate(IndexedFile& previous_file, IndexedFile& current_file)
|
||||
previous, current, \
|
||||
&removed, &added); \
|
||||
if (did_add) {\
|
||||
std::cout << "Adding mergeable update on " << current_def->def.short_name << " (" << current_def->def.usr << ") for field " << #index_name << std::endl; \
|
||||
std::cerr << "Adding mergeable update on " << current_def->def.short_name << " (" << current_def->def.usr << ") for field " << #index_name << std::endl; \
|
||||
query_name.push_back(MergeableUpdate<type>(current_def->def.usr, removed, added)); \
|
||||
} \
|
||||
}
|
||||
@ -376,7 +384,7 @@ IndexUpdate::IndexUpdate(IndexedFile& previous_file, IndexedFile& current_file)
|
||||
current_queryable_file.outline,
|
||||
&removed, &added);
|
||||
if (did_add) {
|
||||
std::cout << "Adding mergeable update on outline (" << current_file.path << ")" << std::endl;
|
||||
std::cerr << "Adding mergeable update on outline (" << current_file.path << ")" << std::endl;
|
||||
files_outline.push_back(MergeableUpdate<UsrRef>(current_file.path, removed, added));
|
||||
}
|
||||
} while (false); // do while false instead of just {} to appease Visual Studio code formatter.
|
||||
@ -617,11 +625,11 @@ void QueryableDatabase::ApplyIndexUpdate(IndexUpdate* update) {
|
||||
|
||||
int main233(int argc, char** argv) {
|
||||
IndexedFile indexed_file_a = Parse("full_tests/index_delta/a_v0.cc", {});
|
||||
std::cout << indexed_file_a.ToString() << std::endl;
|
||||
std::cerr << indexed_file_a.ToString() << std::endl;
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cerr << std::endl;
|
||||
IndexedFile indexed_file_b = Parse("full_tests/index_delta/a_v1.cc", {});
|
||||
std::cout << indexed_file_b.ToString() << std::endl;
|
||||
std::cerr << indexed_file_b.ToString() << std::endl;
|
||||
// TODO: We don't need to do ID remapping when computting a diff. Well, we need to do it for the IndexUpdate.
|
||||
IndexUpdate import(indexed_file_a);
|
||||
/*
|
||||
|
4
task.cc
4
task.cc
@ -183,11 +183,11 @@ static void ThreadMain(int id, Config* config, TaskManager* tm) {
|
||||
assert(false);
|
||||
break;
|
||||
case Task::Kind::Exit:
|
||||
std::cout << id << ": Exiting" << std::endl;
|
||||
std::cerr << id << ": Exiting" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << id << ": waking" << std::endl;
|
||||
std::cerr << id << ": waking" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
6
test.cc
6
test.cc
@ -1,6 +1,7 @@
|
||||
#include "indexer.h"
|
||||
#include "serializer.h"
|
||||
#include "utils.h"
|
||||
#include "platform.h"
|
||||
|
||||
void Write(const std::vector<std::string>& strs) {
|
||||
for (const std::string& str : strs)
|
||||
@ -81,7 +82,7 @@ void VerifySerializeToFrom(IndexedFile* file) {
|
||||
}
|
||||
}
|
||||
|
||||
int main333(int argc, char** argv) {
|
||||
int main222(int argc, char** argv) {
|
||||
// TODO: Assert that we need to be on clang >= 3.9.1
|
||||
|
||||
/*
|
||||
@ -92,8 +93,9 @@ int main333(int argc, char** argv) {
|
||||
return 0;
|
||||
*/
|
||||
|
||||
for (std::string path : GetFilesInFolder("tests")) {
|
||||
for (std::string path : GetFilesInFolder("tests", true /*add_folder_to_path*/)) {
|
||||
//if (path != "tests/usage/type_usage_declare_field.cc") continue;
|
||||
path = "C:/Users/jacob/Desktop/superindex/indexer/" + path;
|
||||
|
||||
// Parse expected output from the test, parse it into JSON document.
|
||||
std::string expected_output;
|
||||
|
14
utils.cc
14
utils.cc
@ -5,7 +5,7 @@
|
||||
|
||||
#include "tinydir.h"
|
||||
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder) {
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder, bool add_folder_to_path) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
tinydir_dir dir;
|
||||
@ -21,10 +21,16 @@ std::vector<std::string> GetFilesInFolder(std::string folder) {
|
||||
goto bail;
|
||||
}
|
||||
|
||||
std::string full_path = folder + "/" + file.name;
|
||||
std::string full_path;
|
||||
if (add_folder_to_path)
|
||||
full_path = folder + "/";
|
||||
full_path += file.name;
|
||||
if (file.is_dir) {
|
||||
if (strcmp(file.name, ".") != 0 && strcmp(file.name, "..") != 0) {
|
||||
for (std::string nested_file : GetFilesInFolder(full_path))
|
||||
// Ignore all dot directories.
|
||||
// Note that we must always ignore the '.' and '..' directories, otherwise
|
||||
// this will loop infinitely.
|
||||
if (file.name[0] != '.') {
|
||||
for (std::string nested_file : GetFilesInFolder(full_path, true /*add_folder_to_path*/))
|
||||
result.push_back(nested_file);
|
||||
}
|
||||
}
|
||||
|
3
utils.h
3
utils.h
@ -4,7 +4,8 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder);
|
||||
// Finds all files in the given folder. This is recursive.
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder, bool add_folder_to_path);
|
||||
std::vector<std::string> ReadLines(std::string filename);
|
||||
void ParseTestExpectation(std::string filename, std::string* expected_output);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user