2018-05-27 19:24:56 +00:00
|
|
|
#include "pipeline.hh"
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-13 16:52:19 +00:00
|
|
|
#include "clang_complete.h"
|
2017-12-29 16:29:47 +00:00
|
|
|
#include "config.h"
|
2018-05-13 16:52:19 +00:00
|
|
|
#include "include_complete.h"
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "log.hh"
|
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"
|
2018-05-28 00:50:02 +00:00
|
|
|
#include "pipeline.hh"
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/ADT/Twine.h>
|
|
|
|
#include <llvm/Support/Threading.h>
|
2018-06-01 03:06:09 +00:00
|
|
|
#include <llvm/Support/Timer.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
using namespace llvm;
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-06-08 04:53:41 +00:00
|
|
|
#include <chrono>
|
2018-05-13 16:52:19 +00:00
|
|
|
#include <thread>
|
2018-07-03 22:47:43 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-06-08 04:53:41 +00:00
|
|
|
void DiagnosticsPublisher::Init() {
|
|
|
|
frequencyMs_ = g_config->diagnostics.frequencyMs;
|
|
|
|
match_ = std::make_unique<GroupMatch>(g_config->diagnostics.whitelist,
|
|
|
|
g_config->diagnostics.blacklist);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiagnosticsPublisher::Publish(WorkingFiles* working_files,
|
|
|
|
std::string path,
|
|
|
|
std::vector<lsDiagnostic> diagnostics) {
|
2018-07-03 22:47:43 +00:00
|
|
|
bool good = true;
|
2018-06-08 04:53:41 +00:00
|
|
|
// Cache diagnostics so we can show fixits.
|
|
|
|
working_files->DoActionOnFile(path, [&](WorkingFile* working_file) {
|
2018-07-03 22:47:43 +00:00
|
|
|
if (working_file) {
|
|
|
|
good = working_file->diagnostics_.empty();
|
2018-06-08 04:53:41 +00:00
|
|
|
working_file->diagnostics_ = diagnostics;
|
2018-07-03 22:47:43 +00:00
|
|
|
}
|
2018-06-08 04:53:41 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
int64_t now =
|
|
|
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
|
|
std::chrono::high_resolution_clock::now().time_since_epoch())
|
|
|
|
.count();
|
2018-07-03 22:47:43 +00:00
|
|
|
if (frequencyMs_ >= 0 &&
|
|
|
|
(nextPublish_ <= now || (!good && diagnostics.empty())) &&
|
2018-06-08 04:53:41 +00:00
|
|
|
match_->IsMatch(path)) {
|
|
|
|
nextPublish_ = now + frequencyMs_;
|
|
|
|
|
|
|
|
Out_TextDocumentPublishDiagnostics out;
|
|
|
|
out.params.uri = lsDocumentUri::FromPath(path);
|
|
|
|
out.params.diagnostics = diagnostics;
|
|
|
|
ccls::pipeline::WriteStdout(kMethodType_TextDocumentPublishDiagnostics, out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace ccls::pipeline {
|
|
|
|
namespace {
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
struct Index_Request {
|
|
|
|
std::string path;
|
|
|
|
std::vector<std::string> args;
|
|
|
|
bool is_interactive;
|
|
|
|
lsRequestId id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Stdout_Request {
|
|
|
|
MethodType method;
|
|
|
|
std::string content;
|
|
|
|
};
|
|
|
|
|
|
|
|
MultiQueueWaiter* main_waiter;
|
|
|
|
MultiQueueWaiter* indexer_waiter;
|
|
|
|
MultiQueueWaiter* stdout_waiter;
|
|
|
|
ThreadedQueue<std::unique_ptr<InMessage>>* on_request;
|
|
|
|
ThreadedQueue<Index_Request>* index_request;
|
2018-06-01 04:21:34 +00:00
|
|
|
ThreadedQueue<IndexUpdate>* on_indexed;
|
2018-05-28 00:50:02 +00:00
|
|
|
ThreadedQueue<Stdout_Request>* for_stdout;
|
|
|
|
|
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
|
|
|
if (opt_previous_index) {
|
|
|
|
auto& prev_args = opt_previous_index->args;
|
2018-06-09 01:20:51 +00:00
|
|
|
if (prev_args != args) {
|
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-28 00:50:02 +00:00
|
|
|
std::string AppendSerializationFormat(const std::string& base) {
|
|
|
|
switch (g_config->cacheFormat) {
|
|
|
|
case SerializeFormat::Binary:
|
|
|
|
return base + ".blob";
|
|
|
|
case SerializeFormat::Json:
|
|
|
|
return base + ".json";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetCachePath(const std::string& source_file) {
|
|
|
|
std::string cache_file;
|
|
|
|
size_t len = g_config->projectRoot.size();
|
|
|
|
if (StartsWith(source_file, g_config->projectRoot)) {
|
|
|
|
cache_file = EscapeFileName(g_config->projectRoot) +
|
|
|
|
EscapeFileName(source_file.substr(len));
|
|
|
|
} else {
|
|
|
|
cache_file = '@' + EscapeFileName(g_config->projectRoot) +
|
|
|
|
EscapeFileName(source_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
return g_config->cacheDirectory + cache_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<IndexFile> RawCacheLoad(
|
|
|
|
const std::string& path) {
|
|
|
|
std::string cache_path = GetCachePath(path);
|
|
|
|
std::optional<std::string> file_content = ReadContent(cache_path);
|
|
|
|
std::optional<std::string> serialized_indexed_content =
|
|
|
|
ReadContent(AppendSerializationFormat(cache_path));
|
|
|
|
if (!file_content || !serialized_indexed_content)
|
|
|
|
return nullptr;
|
|
|
|
|
2018-06-08 04:53:41 +00:00
|
|
|
return ccls::Deserialize(g_config->cacheFormat, path,
|
|
|
|
*serialized_indexed_content, *file_content,
|
|
|
|
IndexFile::kMajorVersion);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
2018-06-03 07:29:33 +00:00
|
|
|
bool Indexer_Parse(DiagnosticsPublisher* diag_pub,
|
2018-05-05 22:29:17 +00:00
|
|
|
WorkingFiles* working_files,
|
|
|
|
Project* project,
|
|
|
|
VFS* vfs,
|
2018-05-09 05:01:58 +00:00
|
|
|
ClangIndexer* indexer) {
|
2018-05-28 00:50:02 +00:00
|
|
|
std::optional<Index_Request> opt_request = index_request->TryPopFront();
|
2018-05-05 22:29:17 +00:00
|
|
|
if (!opt_request)
|
|
|
|
return false;
|
|
|
|
auto& request = *opt_request;
|
2018-01-18 07:59:48 +00:00
|
|
|
|
2018-05-08 15:56:20 +00:00
|
|
|
// Dummy one to trigger refresh semantic highlight.
|
2018-05-08 07:35:32 +00:00
|
|
|
if (request.path.empty()) {
|
2018-05-08 15:56:20 +00:00
|
|
|
IndexUpdate dummy;
|
|
|
|
dummy.refresh = true;
|
2018-06-01 04:21:34 +00:00
|
|
|
on_indexed->PushBack(std::move(dummy), false);
|
2018-05-08 07:35:32 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
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;
|
2018-05-28 00:50:02 +00:00
|
|
|
prev = RawCacheLoad(path_to_index);
|
2018-05-05 22:29:17 +00:00
|
|
|
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;
|
2018-06-02 07:33:12 +00:00
|
|
|
int reparseForDep = g_config->index.reparseForDependency;
|
|
|
|
if (reparseForDep > 1 || (reparseForDep == 1 && !Project::loaded))
|
|
|
|
for (const auto& dep : prev->dependencies) {
|
|
|
|
if (auto write_time1 = LastWriteTime(dep.first().str())) {
|
|
|
|
if (dep.second < *write_time1) {
|
|
|
|
reparse = 2;
|
|
|
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
|
|
|
vfs->state[dep.first().str()].stage = 0;
|
|
|
|
}
|
|
|
|
} else
|
2018-05-05 22:29:17 +00:00
|
|
|
reparse = 2;
|
2018-06-02 07:33:12 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
}
|
2018-01-18 07:59:48 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
if (reparse < 2) {
|
2018-06-02 07:33:12 +00:00
|
|
|
LOG_S(INFO) << "load cache for " << path_to_index;
|
2018-05-05 22:29:17 +00:00
|
|
|
auto dependencies = prev->dependencies;
|
|
|
|
if (reparse) {
|
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
2018-06-01 04:21:34 +00:00
|
|
|
on_indexed->PushBack(std::move(update), request.is_interactive);
|
2018-02-01 05:20:38 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
for (const auto& dep : dependencies)
|
2018-06-06 07:36:39 +00:00
|
|
|
if (vfs->Mark(dep.first().str(), 0, 2) &&
|
|
|
|
(prev = RawCacheLoad(dep.first().str()))) {
|
2018-05-05 22:29:17 +00:00
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
2018-06-01 04:21:34 +00:00
|
|
|
on_indexed->PushBack(std::move(update), request.is_interactive);
|
2018-05-05 22:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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-06-01 04:21:34 +00:00
|
|
|
auto indexes = indexer->Index(vfs, path_to_index, entry.args, {});
|
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-05-28 00:50:02 +00:00
|
|
|
pipeline::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-06-03 07:29:33 +00:00
|
|
|
diag_pub->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;
|
2018-05-28 00:50:02 +00:00
|
|
|
prev = 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;
|
2018-05-28 00:50:02 +00:00
|
|
|
{
|
2018-07-03 22:47:43 +00:00
|
|
|
static Timer timer("write", "store index");
|
2018-06-01 03:06:09 +00:00
|
|
|
timer.startTimer();
|
2018-05-28 00:50:02 +00:00
|
|
|
std::string cache_path = GetCachePath(path);
|
|
|
|
WriteToFile(cache_path, curr->file_contents);
|
|
|
|
WriteToFile(AppendSerializationFormat(cache_path),
|
|
|
|
Serialize(g_config->cacheFormat, *curr));
|
2018-06-01 03:06:09 +00:00
|
|
|
timer.stopTimer();
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
|
|
|
|
vfs->Reset(path_to_index);
|
|
|
|
if (entry.id >= 0) {
|
|
|
|
std::lock_guard<std::mutex> lock(project->mutex_);
|
|
|
|
for (auto& dep : curr->dependencies)
|
2018-05-15 05:13:18 +00:00
|
|
|
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());
|
|
|
|
LOG_S(INFO) << "built index for " << path << " (is_delta=" << !!prev << ")";
|
2017-12-29 16:45:10 +00:00
|
|
|
|
2018-06-01 04:21:34 +00:00
|
|
|
on_indexed->PushBack(std::move(update), 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-05-28 00:50:02 +00:00
|
|
|
void Init() {
|
|
|
|
main_waiter = new MultiQueueWaiter;
|
|
|
|
on_request = new ThreadedQueue<std::unique_ptr<InMessage>>(main_waiter);
|
2018-06-01 04:21:34 +00:00
|
|
|
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
2018-05-28 00:50:02 +00:00
|
|
|
|
|
|
|
indexer_waiter = new MultiQueueWaiter;
|
|
|
|
index_request = new ThreadedQueue<Index_Request>(indexer_waiter);
|
|
|
|
|
|
|
|
stdout_waiter = new MultiQueueWaiter;
|
|
|
|
for_stdout = new ThreadedQueue<Stdout_Request>(stdout_waiter);
|
|
|
|
}
|
|
|
|
|
2018-06-03 07:29:33 +00:00
|
|
|
void Indexer_Main(DiagnosticsPublisher* diag_pub,
|
2018-05-05 22:29:17 +00:00
|
|
|
VFS* vfs,
|
2017-12-29 15:56:34 +00:00
|
|
|
Project* project,
|
2018-05-28 00:50:02 +00:00
|
|
|
WorkingFiles* working_files) {
|
2017-12-24 01:30:52 +00:00
|
|
|
// Build one index per-indexer, as building the index acquires a global lock.
|
2018-05-09 05:01:58 +00:00
|
|
|
ClangIndexer indexer;
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-05-05 22:29:17 +00:00
|
|
|
while (true)
|
2018-06-03 07:29:33 +00:00
|
|
|
if (!Indexer_Parse(diag_pub, working_files, project, vfs, &indexer))
|
2018-05-28 00:50:02 +00:00
|
|
|
indexer_waiter->Wait(index_request);
|
2018-02-05 03:38:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-30 06:56:14 +00:00
|
|
|
void Main_OnIndexed(DB* db,
|
2018-05-28 00:50:02 +00:00
|
|
|
SemanticHighlightSymbolCache* semantic_cache,
|
|
|
|
WorkingFiles* working_files,
|
2018-06-01 04:21:34 +00:00
|
|
|
IndexUpdate* update) {
|
|
|
|
if (update->refresh) {
|
2018-06-02 07:33:12 +00:00
|
|
|
Project::loaded = true;
|
|
|
|
LOG_S(INFO) << "loaded project. Refresh semantic highlight for all working file.";
|
2018-05-08 07:35:32 +00:00
|
|
|
std::lock_guard<std::mutex> lock(working_files->files_mutex);
|
|
|
|
for (auto& f : working_files->files) {
|
2018-05-08 15:56:20 +00:00
|
|
|
std::string filename = LowerPathIfInsensitive(f->filename);
|
|
|
|
if (db->name2file_id.find(filename) == db->name2file_id.end())
|
|
|
|
continue;
|
|
|
|
QueryFile* file = &db->files[db->name2file_id[filename]];
|
2018-05-08 07:35:32 +00:00
|
|
|
EmitSemanticHighlighting(db, semantic_cache, f.get(), file);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-03 22:47:43 +00:00
|
|
|
static Timer timer("apply", "apply index");
|
2018-06-01 03:06:09 +00:00
|
|
|
timer.startTimer();
|
2018-06-01 04:21:34 +00:00
|
|
|
db->ApplyIndexUpdate(update);
|
2018-06-01 03:06:09 +00:00
|
|
|
timer.stopTimer();
|
2018-02-05 03:38:57 +00:00
|
|
|
|
|
|
|
// Update indexed content, inactive lines, and semantic highlighting.
|
2018-06-01 04:21:34 +00:00
|
|
|
if (update->files_def_update) {
|
|
|
|
auto& def_u = *update->files_def_update;
|
|
|
|
LOG_S(INFO) << "apply index for " << def_u.first.path;
|
2018-05-28 00:50:02 +00:00
|
|
|
if (WorkingFile* working_file =
|
2018-06-01 04:21:34 +00:00
|
|
|
working_files->GetFileByFilename(def_u.first.path)) {
|
|
|
|
working_file->SetIndexContent(def_u.second);
|
|
|
|
EmitInactiveLines(working_file, def_u.first.inactive_regions);
|
|
|
|
EmitSemanticHighlighting(db, semantic_cache, working_file,
|
|
|
|
&db->files[update->file_id]);
|
2018-02-05 03:38:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-03-20 03:01:23 +00:00
|
|
|
|
2018-06-01 03:06:09 +00:00
|
|
|
void LaunchStdin() {
|
|
|
|
std::thread([]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("stdin");
|
2018-05-13 16:52:19 +00:00
|
|
|
while (true) {
|
|
|
|
std::unique_ptr<InMessage> message;
|
|
|
|
std::optional<std::string> err =
|
|
|
|
MessageRegistry::instance()->ReadMessageFromStdin(&message);
|
|
|
|
|
|
|
|
// Message parsing can fail if we don't recognize the method.
|
|
|
|
if (err) {
|
|
|
|
// The message may be partially deserialized.
|
|
|
|
// Emit an error ResponseMessage if |id| is available.
|
|
|
|
if (message) {
|
|
|
|
lsRequestId id = message->GetRequestId();
|
|
|
|
if (id.Valid()) {
|
|
|
|
Out_Error out;
|
|
|
|
out.id = id;
|
|
|
|
out.error.code = lsErrorCodes::InvalidParams;
|
|
|
|
out.error.message = std::move(*err);
|
2018-05-28 00:50:02 +00:00
|
|
|
WriteStdout(kMethodType_Unknown, out);
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2018-02-05 03:38:57 +00:00
|
|
|
|
2018-05-13 16:52:19 +00:00
|
|
|
// Cache |method_id| so we can access it after moving |message|.
|
|
|
|
MethodType method_type = message->GetMethodType();
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
on_request->PushBack(std::move(message));
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
// If the message was to exit then querydb will take care of the actual
|
|
|
|
// exit. Stop reading from stdin since it might be detached.
|
|
|
|
if (method_type == kMethodType_Exit)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}).detach();
|
|
|
|
}
|
|
|
|
|
2018-06-01 03:06:09 +00:00
|
|
|
void LaunchStdout() {
|
2018-05-13 16:52:19 +00:00
|
|
|
std::thread([=]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("stdout");
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
while (true) {
|
2018-05-28 00:50:02 +00:00
|
|
|
std::vector<Stdout_Request> messages = for_stdout->DequeueAll();
|
2018-05-13 16:52:19 +00:00
|
|
|
if (messages.empty()) {
|
2018-05-28 00:50:02 +00:00
|
|
|
stdout_waiter->Wait(for_stdout);
|
2018-05-13 16:52:19 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& message : messages) {
|
2018-07-03 22:47:43 +00:00
|
|
|
#ifdef _WIN32
|
2018-05-13 16:52:19 +00:00
|
|
|
fwrite(message.content.c_str(), message.content.size(), 1, stdout);
|
|
|
|
fflush(stdout);
|
2018-07-03 22:47:43 +00:00
|
|
|
#else
|
|
|
|
write(1, message.content.c_str(), message.content.size());
|
|
|
|
#endif
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}).detach();
|
|
|
|
}
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
void MainLoop() {
|
2018-05-13 16:52:19 +00:00
|
|
|
Project project;
|
|
|
|
SemanticHighlightSymbolCache semantic_cache;
|
|
|
|
WorkingFiles working_files;
|
|
|
|
VFS vfs;
|
2018-06-03 07:29:33 +00:00
|
|
|
DiagnosticsPublisher diag_pub;
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
ClangCompleteManager clang_complete(
|
|
|
|
&project, &working_files,
|
|
|
|
[&](std::string path, std::vector<lsDiagnostic> diagnostics) {
|
2018-06-03 07:29:33 +00:00
|
|
|
diag_pub.Publish(&working_files, path, diagnostics);
|
2018-05-13 16:52:19 +00:00
|
|
|
},
|
|
|
|
[](lsRequestId id) {
|
|
|
|
if (id.Valid()) {
|
|
|
|
Out_Error out;
|
|
|
|
out.id = id;
|
|
|
|
out.error.code = lsErrorCodes::InternalError;
|
|
|
|
out.error.message =
|
|
|
|
"Dropping completion request; a newer request "
|
|
|
|
"has come in that will be serviced instead.";
|
2018-05-28 00:50:02 +00:00
|
|
|
pipeline::WriteStdout(kMethodType_Unknown, out);
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
IncludeComplete include_complete(&project);
|
|
|
|
auto global_code_complete_cache = std::make_unique<CodeCompleteCache>();
|
|
|
|
auto non_global_code_complete_cache = std::make_unique<CodeCompleteCache>();
|
|
|
|
auto signature_cache = std::make_unique<CodeCompleteCache>();
|
2018-05-30 06:56:14 +00:00
|
|
|
DB db;
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
// Setup shared references.
|
|
|
|
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
|
|
|
handler->db = &db;
|
|
|
|
handler->waiter = indexer_waiter;
|
|
|
|
handler->project = &project;
|
2018-06-03 07:29:33 +00:00
|
|
|
handler->diag_pub = &diag_pub;
|
2018-05-13 16:52:19 +00:00
|
|
|
handler->vfs = &vfs;
|
|
|
|
handler->semantic_cache = &semantic_cache;
|
|
|
|
handler->working_files = &working_files;
|
|
|
|
handler->clang_complete = &clang_complete;
|
|
|
|
handler->include_complete = &include_complete;
|
|
|
|
handler->global_code_complete_cache = global_code_complete_cache.get();
|
|
|
|
handler->non_global_code_complete_cache =
|
|
|
|
non_global_code_complete_cache.get();
|
|
|
|
handler->signature_cache = signature_cache.get();
|
2017-12-24 01:30:52 +00:00
|
|
|
}
|
|
|
|
|
2018-05-13 16:52:19 +00:00
|
|
|
while (true) {
|
2018-05-28 00:50:02 +00:00
|
|
|
std::vector<std::unique_ptr<InMessage>> messages = on_request->DequeueAll();
|
2018-05-13 16:52:19 +00:00
|
|
|
bool did_work = messages.size();
|
|
|
|
for (auto& message : messages) {
|
|
|
|
// TODO: Consider using std::unordered_map to lookup the handler
|
|
|
|
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
|
|
|
if (handler->GetMethodType() == message->GetMethodType()) {
|
|
|
|
handler->Run(std::move(message));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message)
|
|
|
|
LOG_S(ERROR) << "No handler for " << message->GetMethodType();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 80; i--;) {
|
2018-06-01 04:21:34 +00:00
|
|
|
std::optional<IndexUpdate> update = on_indexed->TryPopFront();
|
|
|
|
if (!update)
|
2018-05-13 16:52:19 +00:00
|
|
|
break;
|
|
|
|
did_work = true;
|
2018-06-01 04:21:34 +00:00
|
|
|
Main_OnIndexed(&db, &semantic_cache, &working_files, &*update);
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
|
2018-06-09 01:20:51 +00:00
|
|
|
if (!did_work) {
|
|
|
|
FreeUnusedMemory();
|
2018-05-28 00:50:02 +00:00
|
|
|
main_waiter->Wait(on_indexed, on_request);
|
2018-06-09 01:20:51 +00:00
|
|
|
}
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
2017-12-24 01:30:52 +00:00
|
|
|
}
|
2018-05-28 00:50:02 +00:00
|
|
|
|
|
|
|
void Index(const std::string& path,
|
|
|
|
const std::vector<std::string>& args,
|
|
|
|
bool interactive,
|
|
|
|
lsRequestId id) {
|
|
|
|
index_request->PushBack({path, args, interactive, id}, interactive);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> LoadCachedFileContents(const std::string& path) {
|
|
|
|
return ReadContent(GetCachePath(path));
|
|
|
|
}
|
|
|
|
|
|
|
|
void WriteStdout(MethodType method, lsBaseOutMessage& response) {
|
|
|
|
std::ostringstream sstream;
|
|
|
|
response.Write(sstream);
|
|
|
|
|
|
|
|
Stdout_Request out;
|
|
|
|
out.content = sstream.str();
|
|
|
|
out.method = method;
|
|
|
|
for_stdout->PushBack(std::move(out));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|