Simplify; improve $ccls/inheritanceHierarchy

This commit is contained in:
Fangrui Song 2018-05-25 22:48:58 -07:00
parent f5ce45fd55
commit a6094ef714
9 changed files with 141 additions and 173 deletions

View File

@ -2,6 +2,8 @@
#include "query_utils.h" #include "query_utils.h"
#include "queue_manager.h" #include "queue_manager.h"
#include <unordered_set>
namespace { namespace {
MethodType kMethodType = "$ccls/inheritanceHierarchy"; MethodType kMethodType = "$ccls/inheritanceHierarchy";
@ -79,19 +81,22 @@ bool ExpandHelper(MessageHandler* m,
int levels, int levels,
Q& entity) { Q& entity) {
const auto* def = entity.AnyDef(); const auto* def = entity.AnyDef();
if (!def) { if (def) {
entry->name = def->Name(qualified);
if (def->spell) {
if (auto loc = GetLsLocation(m->db, m->working_files, *def->spell))
entry->location = *loc;
}
} else if (!derived) {
entry->numChildren = 0; entry->numChildren = 0;
return false; return false;
} }
entry->name = def->Name(qualified); std::unordered_set<Usr> seen;
if (def->spell) {
if (std::optional<lsLocation> loc =
GetLsLocation(m->db, m->working_files, *def->spell))
entry->location = *loc;
}
if (derived) { if (derived) {
if (levels > 0) { if (levels > 0) {
for (auto usr : entity.derived) { for (auto usr : entity.derived) {
if (seen.insert(usr).second)
continue;
Out_CclsInheritanceHierarchy::Entry entry1; Out_CclsInheritanceHierarchy::Entry entry1;
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;
@ -105,6 +110,8 @@ bool ExpandHelper(MessageHandler* m,
} else { } else {
if (levels > 0) { if (levels > 0) {
for (auto usr : def->bases) { for (auto usr : def->bases) {
if (seen.insert(usr).second)
continue;
Out_CclsInheritanceHierarchy::Entry entry1; Out_CclsInheritanceHierarchy::Entry entry1;
entry1.id = std::to_string(usr); entry1.id = std::to_string(usr);
entry1.usr = usr; entry1.usr = usr;

View File

@ -267,12 +267,8 @@ struct lsTextDocumentClientCapabilities {
// and `${3:foo}`. `$0` defines the final tab stop, it defaults to // and `${3:foo}`. `$0` defines the final tab stop, it defaults to
// the end of the snippet. Placeholders with equal identifiers are linked, // the end of the snippet. Placeholders with equal identifiers are linked,
// that is typing in one will update others too. // that is typing in one will update others too.
std::optional<bool> snippetSupport; bool snippetSupport = false;
}; } completionItem;
// The client supports the following `CompletionItem` specific
// capabilities.
std::optional<lsCompletionItem> completionItem;
} completion; } completion;
struct lsGenericDynamicReg { struct lsGenericDynamicReg {
@ -426,114 +422,110 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
void Run(In_InitializeRequest* request) override { void Run(In_InitializeRequest* request) override {
// Log initialization parameters. auto& params = request->params;
rapidjson::StringBuffer output; if (!params.rootUri)
rapidjson::Writer<rapidjson::StringBuffer> writer(output); return;
JsonWriter json_writer(&writer); {
Reflect(json_writer, request->params.initializationOptions); rapidjson::StringBuffer output;
LOG_S(INFO) << "Init parameters: " << output.GetString(); rapidjson::Writer<rapidjson::StringBuffer> writer(output);
std::unique_ptr<Config> config; JsonWriter json_writer(&writer);
Reflect(json_writer, params.initializationOptions);
if (request->params.rootUri) { LOG_S(INFO) << "initializationOptions: " << output.GetString();
std::string project_path =
NormalizePath(request->params.rootUri->GetPath());
LOG_S(INFO) << "[querydb] Initialize in directory " << project_path
<< " with uri " << request->params.rootUri->raw_uri;
{
if (request->params.initializationOptions)
config = std::make_unique<Config>(*request->params.initializationOptions);
else
config = std::make_unique<Config>();
rapidjson::Document reader;
reader.Parse(g_init_options.c_str());
if (!reader.HasParseError()) {
JsonReader json_reader{&reader};
try {
Reflect(json_reader, *config);
} catch (std::invalid_argument&) {
// This will not trigger because parse error is handled in
// MessageRegistry::Parse in lsp.cc
}
}
if (config->cacheDirectory.empty()) {
LOG_S(ERROR) << "cacheDirectory cannot be empty.";
exit(1);
} else {
config->cacheDirectory = NormalizePath(config->cacheDirectory);
EnsureEndsInSlash(config->cacheDirectory);
}
}
// Client capabilities
{
const auto& cap = request->params.capabilities.textDocument;
if (cap.completion.completionItem)
config->client.snippetSupport =
cap.completion.completionItem->snippetSupport.value_or(false);
}
// Ensure there is a resource directory.
if (config->clang.resourceDir.empty())
config->clang.resourceDir = GetDefaultResourceDirectory();
LOG_S(INFO) << "Using -resource-dir=" << config->clang.resourceDir;
// Send initialization before starting indexers, so we don't send a
// status update too early.
// TODO: query request->params.capabilities.textDocument and support
// only things the client supports.
Out_InitializeResponse out;
out.id = request->id;
QueueManager::WriteStdout(kMethodType, out);
// Set project root.
EnsureEndsInSlash(project_path);
config->projectRoot = project_path;
// Create two cache directories for files inside and outside of the
// project.
sys::fs::create_directories(config->cacheDirectory +
EscapeFileName(config->projectRoot));
sys::fs::create_directories(config->cacheDirectory + '@' +
EscapeFileName(config->projectRoot));
g_config = std::move(config);
Timer time;
diag_engine->Init();
semantic_cache->Init();
// Open up / load the project.
project->Load(project_path);
time.ResetAndPrint("[perf] Loaded compilation entries (" +
std::to_string(project->entries.size()) + " files)");
// Start indexer threads. Start this after loading the project, as that
// may take a long time. Indexer threads will emit status/progress
// reports.
if (g_config->index.threads == 0)
g_config->index.threads = std::thread::hardware_concurrency();
LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; i++) {
std::thread([=]() {
g_thread_id = i + 1;
std::string name = "indexer" + std::to_string(i);
SetThreadName(name.c_str());
Indexer_Main(diag_engine, vfs, project, working_files, waiter);
}).detach();
}
// Start scanning include directories before dispatching project
// files, because that takes a long time.
include_complete->Rescan();
time.Reset();
project->Index(QueueManager::instance(), working_files, request->id);
// We need to support multiple concurrent index processes.
time.ResetAndPrint("[perf] Dispatched initial index requests");
} }
std::string project_path = NormalizePath(params.rootUri->GetPath());
LOG_S(INFO) << "initialize in directory " << project_path << " with uri "
<< params.rootUri->raw_uri;
{
if (params.initializationOptions)
g_config = std::make_unique<Config>(*params.initializationOptions);
else
g_config = std::make_unique<Config>();
rapidjson::Document reader;
reader.Parse(g_init_options.c_str());
if (!reader.HasParseError()) {
JsonReader json_reader{&reader};
try {
Reflect(json_reader, *g_config);
} catch (std::invalid_argument&) {
// This will not trigger because parse error is handled in
// MessageRegistry::Parse in lsp.cc
}
}
if (g_config->cacheDirectory.empty()) {
LOG_S(ERROR) << "cacheDirectory cannot be empty.";
exit(1);
} else {
g_config->cacheDirectory = NormalizePath(g_config->cacheDirectory);
EnsureEndsInSlash(g_config->cacheDirectory);
}
}
// Client capabilities
const auto& capabilities = params.capabilities;
g_config->client.snippetSupport =
capabilities.textDocument.completion.completionItem.snippetSupport;
// Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty())
g_config->clang.resourceDir = GetDefaultResourceDirectory();
LOG_S(INFO) << "Using -resource-dir=" << g_config->clang.resourceDir;
// Send initialization before starting indexers, so we don't send a
// status update too early.
// TODO: query request->params.capabilities.textDocument and support
// only things the client supports.
Out_InitializeResponse out;
out.id = request->id;
QueueManager::WriteStdout(kMethodType, out);
// Set project root.
EnsureEndsInSlash(project_path);
g_config->projectRoot = project_path;
// Create two cache directories for files inside and outside of the
// project.
sys::fs::create_directories(g_config->cacheDirectory +
EscapeFileName(g_config->projectRoot));
sys::fs::create_directories(g_config->cacheDirectory + '@' +
EscapeFileName(g_config->projectRoot));
Timer time;
diag_engine->Init();
semantic_cache->Init();
// Open up / load the project.
project->Load(project_path);
time.ResetAndPrint("[perf] Loaded compilation entries (" +
std::to_string(project->entries.size()) + " files)");
// Start indexer threads. Start this after loading the project, as that
// may take a long time. Indexer threads will emit status/progress
// reports.
if (g_config->index.threads == 0)
g_config->index.threads = std::thread::hardware_concurrency();
LOG_S(INFO) << "Starting " << g_config->index.threads << " indexers";
for (int i = 0; i < g_config->index.threads; i++) {
std::thread([=]() {
g_thread_id = i + 1;
std::string name = "indexer" + std::to_string(i);
SetThreadName(name.c_str());
Indexer_Main(diag_engine, vfs, project, working_files, waiter);
}).detach();
}
// Start scanning include directories before dispatching project
// files, because that takes a long time.
include_complete->Rescan();
time.Reset();
project->Index(QueueManager::instance(), working_files, request->id);
// We need to support multiple concurrent index processes.
time.ResetAndPrint("[perf] Dispatched initial index requests");
} }
}; };
REGISTER_MESSAGE_HANDLER(Handler_Initialize); REGISTER_MESSAGE_HANDLER(Handler_Initialize);

View File

@ -26,16 +26,10 @@ struct Handler_TextDocumentDidChange
std::string path = request->params.textDocument.uri.GetPath(); std::string path = request->params.textDocument.uri.GetPath();
working_files->OnChange(request->params); working_files->OnChange(request->params);
if (g_config->index.onDidChange) { if (g_config->index.onDidChange) {
std::optional<std::string> content = ReadContent(path); Project::Entry entry = project->FindCompilationEntryForFile(path);
if (!content) { QueueManager::instance()->index_request.PushBack(
LOG_S(ERROR) << "Unable to read file content after saving " << path; Index_Request(entry.filename, entry.args, true /*is_interactive*/),
} else { true);
Project::Entry entry = project->FindCompilationEntryForFile(path);
QueueManager::instance()->index_request.PushBack(
Index_Request(entry.filename, entry.args, true /*is_interactive*/,
*content),
true);
}
} }
clang_complete->NotifyEdit(path); clang_complete->NotifyEdit(path);
clang_complete->DiagnosticsUpdate( clang_complete->DiagnosticsUpdate(

View File

@ -65,7 +65,7 @@ struct Handler_TextDocumentDidOpen
QueueManager::instance()->index_request.PushBack( QueueManager::instance()->index_request.PushBack(
Index_Request(entry.filename, Index_Request(entry.filename,
params.args.size() ? params.args : entry.args, params.args.size() ? params.args : entry.args,
true /*is_interactive*/, params.textDocument.text), true /*is_interactive*/),
true /* priority */); true /* priority */);
clang_complete->FlushSession(entry.filename); clang_complete->FlushSession(entry.filename);

View File

@ -48,16 +48,10 @@ struct Handler_TextDocumentDidSave
// if so, ignore that index response. // if so, ignore that index response.
// TODO: send as priority request // TODO: send as priority request
if (!g_config->index.onDidChange) { if (!g_config->index.onDidChange) {
std::optional<std::string> content = ReadContent(path); Project::Entry entry = project->FindCompilationEntryForFile(path);
if (!content) { QueueManager::instance()->index_request.PushBack(
LOG_S(ERROR) << "Unable to read file content after saving " << path; Index_Request(entry.filename, entry.args, true /*is_interactive*/),
} else { true);
Project::Entry entry = project->FindCompilationEntryForFile(path);
QueueManager::instance()->index_request.PushBack(
Index_Request(entry.filename, entry.args, true /*is_interactive*/,
*content),
true);
}
} }
clang_complete->NotifySave(path); clang_complete->NotifySave(path);

View File

@ -54,20 +54,15 @@ struct Handler_WorkspaceDidChangeWatchedFiles
switch (event.type) { switch (event.type) {
case lsFileChangeType::Created: case lsFileChangeType::Created:
case lsFileChangeType::Changed: { case lsFileChangeType::Changed: {
std::optional<std::string> content = ReadContent(path); QueueManager::instance()->index_request.PushBack(
if (!content) Index_Request(path, entry.args, is_interactive));
LOG_S(ERROR) << "Unable to read file content after saving " << path; if (is_interactive)
else { clang_complete->NotifySave(path);
QueueManager::instance()->index_request.PushBack(
Index_Request(path, entry.args, is_interactive, *content));
if (is_interactive)
clang_complete->NotifySave(path);
}
break; break;
} }
case lsFileChangeType::Deleted: case lsFileChangeType::Deleted:
QueueManager::instance()->index_request.PushBack( QueueManager::instance()->index_request.PushBack(
Index_Request(path, entry.args, is_interactive, std::string())); Index_Request(path, entry.args, is_interactive));
break; break;
} }
} }

View File

@ -458,19 +458,13 @@ void Project::Index(QueueManager* queue,
WorkingFiles* wfiles, WorkingFiles* wfiles,
lsRequestId id) { lsRequestId id) {
ForAllFilteredFiles([&](int i, const Project::Entry& entry) { ForAllFilteredFiles([&](int i, const Project::Entry& entry) {
std::optional<std::string> content = ReadContent(entry.filename);
if (!content) {
LOG_S(ERROR) << "When loading project, canont read file "
<< entry.filename;
return;
}
bool is_interactive = wfiles->GetFileByFilename(entry.filename) != nullptr; bool is_interactive = wfiles->GetFileByFilename(entry.filename) != nullptr;
queue->index_request.PushBack(Index_Request(entry.filename, entry.args, queue->index_request.PushBack(
is_interactive, *content, id)); Index_Request(entry.filename, entry.args, is_interactive, id));
}); });
// Dummy request to indicate that project is loaded and // Dummy request to indicate that project is loaded and
// trigger refreshing semantic highlight for all working files. // trigger refreshing semantic highlight for all working files.
queue->index_request.PushBack(Index_Request("", {}, false, "")); queue->index_request.PushBack(Index_Request("", {}, false));
} }
TEST_SUITE("Project") { TEST_SUITE("Project") {

View File

@ -6,17 +6,11 @@
#include <sstream> #include <sstream>
Index_Request::Index_Request( Index_Request::Index_Request(const std::string& path,
const std::string& path, const std::vector<std::string>& args,
const std::vector<std::string>& args, bool is_interactive,
bool is_interactive, lsRequestId id)
const std::string& contents, : path(path), args(args), is_interactive(is_interactive), id(id) {}
lsRequestId id)
: path(path),
args(args),
is_interactive(is_interactive),
contents(contents),
id(id) {}
Index_OnIndexed::Index_OnIndexed(IndexUpdate&& update, Index_OnIndexed::Index_OnIndexed(IndexUpdate&& update,
PerformanceImportFile perf) PerformanceImportFile perf)

View File

@ -19,13 +19,11 @@ struct Index_Request {
// TODO: make |args| a string that is parsed lazily. // TODO: make |args| a string that is parsed lazily.
std::vector<std::string> args; std::vector<std::string> args;
bool is_interactive; bool is_interactive;
std::string contents; // Preloaded contents.
lsRequestId id; lsRequestId id;
Index_Request(const std::string& path, Index_Request(const std::string& path,
const std::vector<std::string>& args, const std::vector<std::string>& args,
bool is_interactive, bool is_interactive,
const std::string& contents,
lsRequestId id = {}); lsRequestId id = {});
}; };