2017-12-24 01:30:52 +00:00
|
|
|
#include "import_pipeline.h"
|
|
|
|
|
2017-12-29 17:27:56 +00:00
|
|
|
#include "cache_manager.h"
|
2017-12-29 16:29:47 +00:00
|
|
|
#include "config.h"
|
2018-03-06 01:18:33 +00:00
|
|
|
#include "diagnostics_engine.h"
|
2018-02-24 00:12:39 +00:00
|
|
|
#include "lsp.h"
|
2017-12-24 01:30:52 +00:00
|
|
|
#include "message_handler.h"
|
|
|
|
#include "platform.h"
|
|
|
|
#include "project.h"
|
|
|
|
#include "query_utils.h"
|
|
|
|
#include "queue_manager.h"
|
|
|
|
#include "timer.h"
|
|
|
|
|
|
|
|
#include <doctest/doctest.h>
|
|
|
|
#include <loguru.hpp>
|
|
|
|
|
2018-01-07 21:06:18 +00:00
|
|
|
#include <chrono>
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2017-12-29 16:45:10 +00:00
|
|
|
namespace {
|
2017-12-29 17:27:56 +00:00
|
|
|
|
2018-01-18 05:53:03 +00:00
|
|
|
// Checks if |path| needs to be reparsed. This will modify cached state
|
|
|
|
// such that calling this function twice with the same path may return true
|
|
|
|
// the first time but will return false the second.
|
2018-01-18 07:19:08 +00:00
|
|
|
//
|
|
|
|
// |from|: The file which generated the parse request for this file.
|
2018-05-05 22:29:17 +00:00
|
|
|
bool FileNeedsParse(int64_t write_time,
|
|
|
|
VFS* vfs,
|
|
|
|
bool is_interactive,
|
|
|
|
IndexFile* opt_previous_index,
|
|
|
|
const std::string& path,
|
|
|
|
const std::vector<std::string>& args,
|
|
|
|
const std::optional<std::string>& from) {
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
|
|
|
if (vfs->state[path].timestamp < write_time) {
|
|
|
|
LOG_S(INFO) << "timestamp changed for " << path
|
|
|
|
<< (from ? " (via " + *from + ")" : std::string());
|
|
|
|
return true;
|
|
|
|
}
|
2018-01-18 05:53:03 +00:00
|
|
|
}
|
|
|
|
|
2018-01-18 06:04:07 +00:00
|
|
|
// Command-line arguments changed.
|
2018-01-18 12:15:15 +00:00
|
|
|
auto is_file = [](const std::string& arg) {
|
|
|
|
return EndsWithAny(arg, {".h", ".c", ".cc", ".cpp", ".hpp", ".m", ".mm"});
|
|
|
|
};
|
|
|
|
if (opt_previous_index) {
|
|
|
|
auto& prev_args = opt_previous_index->args;
|
|
|
|
bool same = prev_args.size() == args.size();
|
|
|
|
for (size_t i = 0; i < args.size() && same; ++i) {
|
|
|
|
same = prev_args[i] == args[i] ||
|
|
|
|
(is_file(prev_args[i]) && is_file(args[i]));
|
|
|
|
}
|
|
|
|
if (!same) {
|
2018-05-05 22:29:17 +00:00
|
|
|
LOG_S(INFO) << "args changed for " << path << (from ? " (via " + *from + ")" : std::string());
|
|
|
|
return true;
|
2018-01-18 12:15:15 +00:00
|
|
|
}
|
2018-01-18 07:19:08 +00:00
|
|
|
}
|
2018-01-18 06:04:07 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
return false;
|
2018-01-18 05:53:03 +00:00
|
|
|
};
|
2018-01-18 05:48:09 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
bool Indexer_Parse(DiagnosticsEngine* diag_engine,
|
|
|
|
WorkingFiles* working_files,
|
|
|
|
Project* project,
|
|
|
|
VFS* vfs,
|
|
|
|
IIndexer* indexer) {
|
|
|
|
auto* queue = QueueManager::instance();
|
|
|
|
std::optional<Index_Request> opt_request = queue->index_request.TryPopFront();
|
|
|
|
if (!opt_request)
|
|
|
|
return false;
|
|
|
|
auto& request = *opt_request;
|
|
|
|
ICacheManager cache;
|
2018-01-18 07:59:48 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
Project::Entry entry;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(project->mutex_);
|
|
|
|
auto it = project->absolute_path_to_entry_index_.find(request.path);
|
|
|
|
if (it != project->absolute_path_to_entry_index_.end())
|
|
|
|
entry = project->entries[it->second];
|
|
|
|
else {
|
|
|
|
entry.filename = request.path;
|
|
|
|
entry.args = request.args;
|
2018-04-30 04:49:03 +00:00
|
|
|
}
|
2018-01-18 07:59:48 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
std::string path_to_index = entry.filename;
|
|
|
|
std::unique_ptr<IndexFile> prev;
|
2018-01-18 07:59:48 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
// Try to load the file from cache.
|
|
|
|
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
|
|
|
if (!write_time)
|
|
|
|
return true;
|
|
|
|
// FIXME Don't drop
|
|
|
|
if (!vfs->Mark(path_to_index, g_thread_id, 1))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
int reparse; // request.is_interactive;
|
|
|
|
prev = cache.RawCacheLoad(path_to_index);
|
|
|
|
if (!prev)
|
|
|
|
reparse = 2;
|
|
|
|
else {
|
|
|
|
reparse = vfs->Stamp(path_to_index, prev->last_write_time);
|
|
|
|
if (FileNeedsParse(*write_time, vfs, request.is_interactive, &*prev,
|
|
|
|
path_to_index, entry.args, std::nullopt))
|
|
|
|
reparse = 2;
|
|
|
|
for (const auto& dep : prev->dependencies)
|
|
|
|
if (auto write_time1 = LastWriteTime(dep.first)) {
|
|
|
|
if (dep.second < *write_time1) {
|
|
|
|
reparse = 2;
|
|
|
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
|
|
|
vfs->state[dep.first].stage = 0;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
reparse = 2;
|
|
|
|
}
|
2018-01-18 07:59:48 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
if (reparse < 2) {
|
|
|
|
PerformanceImportFile perf;
|
|
|
|
auto dependencies = prev->dependencies;
|
|
|
|
if (reparse) {
|
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
|
|
|
queue->on_indexed.PushBack(Index_OnIndexed(std::move(update), perf),
|
|
|
|
request.is_interactive);
|
2018-02-01 05:20:38 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
for (const auto& dep : dependencies)
|
|
|
|
if (vfs->Mark(dep.first, 0, 2)) {
|
|
|
|
prev = cache.RawCacheLoad(dep.first);
|
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
|
|
|
queue->on_indexed.PushBack(Index_OnIndexed(std::move(update), perf),
|
|
|
|
request.is_interactive);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
|
|
|
VFS::State& state = vfs->state[path_to_index];
|
|
|
|
if (state.owner == g_thread_id)
|
|
|
|
state.stage = 0;
|
|
|
|
return true;
|
2018-01-18 08:21:39 +00:00
|
|
|
}
|
|
|
|
|
2018-05-04 04:20:10 +00:00
|
|
|
LOG_S(INFO) << "parse " << path_to_index;
|
2018-01-18 08:21:39 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
std::vector<Index_OnIdMapped> result;
|
2017-12-24 01:30:52 +00:00
|
|
|
PerformanceImportFile perf;
|
2018-05-05 22:29:17 +00:00
|
|
|
auto indexes = indexer->Index(vfs, path_to_index, entry.args, {}, &perf);
|
2018-01-20 07:56:49 +00:00
|
|
|
|
2018-04-02 07:22:12 +00:00
|
|
|
if (indexes.empty()) {
|
2018-04-16 19:36:02 +00:00
|
|
|
if (g_config->index.enabled && request.id.Valid()) {
|
2018-01-20 07:56:49 +00:00
|
|
|
Out_Error out;
|
|
|
|
out.id = request.id;
|
|
|
|
out.error.code = lsErrorCodes::InternalError;
|
|
|
|
out.error.message = "Failed to index " + path_to_index;
|
2018-03-22 04:05:25 +00:00
|
|
|
QueueManager::WriteStdout(kMethodType_Unknown, out);
|
2018-01-20 07:56:49 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
vfs->Reset(path_to_index);
|
|
|
|
return true;
|
2018-01-20 07:56:49 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
for (std::unique_ptr<IndexFile>& curr : indexes) {
|
2017-12-24 01:30:52 +00:00
|
|
|
// Only emit diagnostics for non-interactive sessions, which makes it easier
|
|
|
|
// to identify indexing problems. For interactive sessions, diagnostics are
|
|
|
|
// handled by code completion.
|
2018-01-20 07:56:49 +00:00
|
|
|
if (!request.is_interactive)
|
2018-05-05 22:29:17 +00:00
|
|
|
diag_engine->Publish(working_files, curr->path, curr->diagnostics_);
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
std::string path = curr->path;
|
|
|
|
if (!(vfs->Stamp(path, curr->last_write_time) || path == path_to_index))
|
|
|
|
continue;
|
|
|
|
LOG_S(INFO) << "emit index for " << path;
|
|
|
|
prev = cache.RawCacheLoad(path);
|
2018-03-20 18:55:40 +00:00
|
|
|
|
|
|
|
// Write current index to disk if requested.
|
2018-05-05 22:29:17 +00:00
|
|
|
LOG_S(INFO) << "store index for " << path;
|
|
|
|
Timer time;
|
|
|
|
cache.WriteToCache(*curr);
|
|
|
|
perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
|
|
|
|
|
|
|
vfs->Reset(path_to_index);
|
|
|
|
if (entry.id >= 0) {
|
|
|
|
std::lock_guard<std::mutex> lock(project->mutex_);
|
|
|
|
for (auto& dep : curr->dependencies)
|
|
|
|
project->absolute_path_to_entry_index_[dep.first] = entry.id;
|
2018-03-20 18:55:40 +00:00
|
|
|
}
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-05 03:40:52 +00:00
|
|
|
// Build delta update.
|
2018-05-05 22:29:17 +00:00
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(prev.get(), curr.get());
|
|
|
|
perf.index_make_delta = time.ElapsedMicrosecondsAndReset();
|
|
|
|
LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")";
|
2017-12-29 16:45:10 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
Index_OnIndexed reply(std::move(update), perf);
|
|
|
|
queue->on_indexed.PushBack(std::move(reply), request.is_interactive);
|
2018-04-22 17:01:44 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
return true;
|
2018-04-22 17:01:44 +00:00
|
|
|
}
|
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
} // namespace
|
2017-12-29 16:45:10 +00:00
|
|
|
|
2018-04-04 06:05:41 +00:00
|
|
|
void Indexer_Main(DiagnosticsEngine* diag_engine,
|
2018-05-05 22:29:17 +00:00
|
|
|
VFS* vfs,
|
2017-12-29 15:56:34 +00:00
|
|
|
ImportPipelineStatus* status,
|
|
|
|
Project* project,
|
|
|
|
WorkingFiles* working_files,
|
|
|
|
MultiQueueWaiter* waiter) {
|
2017-12-28 18:21:30 +00:00
|
|
|
auto* queue = QueueManager::instance();
|
2017-12-24 01:30:52 +00:00
|
|
|
// Build one index per-indexer, as building the index acquires a global lock.
|
2018-04-22 17:01:44 +00:00
|
|
|
auto indexer = std::make_unique<ClangIndexer>();
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
while (true)
|
|
|
|
if (!Indexer_Parse(diag_engine, working_files, project, vfs, indexer.get()))
|
|
|
|
waiter->Wait(&queue->index_request);
|
2018-02-05 03:38:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
void QueryDb_OnIndexed(QueueManager* queue,
|
|
|
|
QueryDatabase* db,
|
|
|
|
ImportPipelineStatus* status,
|
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
|
|
|
WorkingFiles* working_files,
|
|
|
|
Index_OnIndexed* response) {
|
|
|
|
Timer time;
|
|
|
|
db->ApplyIndexUpdate(&response->update);
|
|
|
|
|
|
|
|
// Update indexed content, inactive lines, and semantic highlighting.
|
2018-04-30 04:49:03 +00:00
|
|
|
if (response->update.files_def_update) {
|
|
|
|
auto& update = *response->update.files_def_update;
|
|
|
|
time.ResetAndPrint("apply index for " + update.value.path);
|
2018-02-05 03:38:57 +00:00
|
|
|
WorkingFile* working_file =
|
2018-04-30 04:49:03 +00:00
|
|
|
working_files->GetFileByFilename(update.value.path);
|
2018-02-05 03:38:57 +00:00
|
|
|
if (working_file) {
|
|
|
|
// Update indexed content.
|
2018-04-30 04:49:03 +00:00
|
|
|
working_file->SetIndexContent(update.file_content);
|
2018-02-05 03:38:57 +00:00
|
|
|
|
|
|
|
// Inactive lines.
|
2018-04-30 04:49:03 +00:00
|
|
|
EmitInactiveLines(working_file, update.value.inactive_regions);
|
2018-02-05 03:38:57 +00:00
|
|
|
|
|
|
|
// Semantic highlighting.
|
2018-04-30 04:49:03 +00:00
|
|
|
int file_id =
|
|
|
|
db->name2file_id[LowerPathIfInsensitive(working_file->filename)];
|
|
|
|
QueryFile* file = &db->files[file_id];
|
2018-02-05 03:38:57 +00:00
|
|
|
EmitSemanticHighlighting(db, semantic_cache, working_file, file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-20 03:01:23 +00:00
|
|
|
|
2018-02-05 03:38:57 +00:00
|
|
|
} // namespace
|
|
|
|
|
2018-04-04 06:05:41 +00:00
|
|
|
bool QueryDb_ImportMain(QueryDatabase* db,
|
2018-01-07 21:06:18 +00:00
|
|
|
ImportPipelineStatus* status,
|
2017-12-24 01:30:52 +00:00
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
|
|
|
WorkingFiles* working_files) {
|
|
|
|
auto* queue = QueueManager::instance();
|
|
|
|
bool did_work = false;
|
|
|
|
|
2018-05-05 03:40:52 +00:00
|
|
|
for (int i = 80; i--; ) {
|
2018-03-31 03:16:33 +00:00
|
|
|
std::optional<Index_OnIndexed> response = queue->on_indexed.TryPopFront();
|
2017-12-24 01:30:52 +00:00
|
|
|
if (!response)
|
|
|
|
break;
|
|
|
|
did_work = true;
|
2018-04-30 04:49:03 +00:00
|
|
|
QueryDb_OnIndexed(queue, db, status, semantic_cache, working_files,
|
|
|
|
&*response);
|
2017-12-24 01:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return did_work;
|
|
|
|
}
|