2018-08-21 05:27:52 +00:00
|
|
|
// Copyright 2017-2018 ccls Authors
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "pipeline.hh"
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "config.hh"
|
|
|
|
#include "include_complete.hh"
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "log.hh"
|
2018-10-28 17:49:31 +00:00
|
|
|
#include "lsp.hh"
|
|
|
|
#include "message_handler.hh"
|
2018-08-09 17:08:14 +00:00
|
|
|
#include "pipeline.hh"
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "platform.hh"
|
2018-10-28 17:49:31 +00:00
|
|
|
#include "project.hh"
|
2018-11-27 06:29:28 +00:00
|
|
|
#include "query.hh"
|
2018-12-01 06:44:52 +00:00
|
|
|
#include "sema_manager.hh"
|
2019-01-09 07:19:17 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
#include <rapidjson/document.h>
|
2019-01-09 07:19:17 +00:00
|
|
|
#include <rapidjson/writer.h>
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-12-24 05:22:51 +00:00
|
|
|
#include <llvm/Support/Path.h>
|
2018-10-23 05:01:10 +00:00
|
|
|
#include <llvm/Support/Process.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/Support/Threading.h>
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-06-08 04:53:41 +00:00
|
|
|
#include <chrono>
|
2018-09-10 06:46:13 +00:00
|
|
|
#include <mutex>
|
|
|
|
#include <shared_mutex>
|
2018-05-13 16:52:19 +00:00
|
|
|
#include <thread>
|
2018-07-03 22:47:43 +00:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
2018-12-25 06:20:00 +00:00
|
|
|
using namespace llvm;
|
|
|
|
namespace chrono = std::chrono;
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
namespace ccls {
|
2018-11-03 20:52:43 +00:00
|
|
|
namespace {
|
|
|
|
struct PublishDiagnosticParam {
|
|
|
|
DocumentUri uri;
|
|
|
|
std::vector<Diagnostic> diagnostics;
|
|
|
|
};
|
2018-12-02 23:53:33 +00:00
|
|
|
REFLECT_STRUCT(PublishDiagnosticParam, uri, diagnostics);
|
2018-11-03 20:52:43 +00:00
|
|
|
} // namespace
|
|
|
|
|
2018-09-21 01:04:55 +00:00
|
|
|
void VFS::Clear() {
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
state.clear();
|
|
|
|
}
|
|
|
|
|
2019-02-21 15:46:20 +00:00
|
|
|
int VFS::Loaded(const std::string &path) {
|
2018-09-21 01:04:55 +00:00
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
return state[path].loaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VFS::Stamp(const std::string &path, int64_t ts, int step) {
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
State &st = state[path];
|
|
|
|
if (st.timestamp < ts || (st.timestamp == ts && st.step < step)) {
|
|
|
|
st.timestamp = ts;
|
|
|
|
st.step = step;
|
|
|
|
return true;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
struct MessageHandler;
|
|
|
|
void StandaloneInitialize(MessageHandler &, const std::string &root);
|
|
|
|
|
|
|
|
namespace pipeline {
|
2018-09-18 01:03:59 +00:00
|
|
|
|
2018-12-14 04:13:35 +00:00
|
|
|
std::atomic<bool> quit;
|
2018-12-21 04:53:50 +00:00
|
|
|
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
|
2018-09-24 06:02:19 +00:00
|
|
|
int64_t tick = 0;
|
2018-09-18 01:03:59 +00:00
|
|
|
|
2018-06-08 04:53:41 +00:00
|
|
|
namespace {
|
|
|
|
|
2018-12-21 04:53:50 +00:00
|
|
|
struct IndexRequest {
|
2018-05-28 00:50:02 +00:00
|
|
|
std::string path;
|
2018-09-19 16:31:45 +00:00
|
|
|
std::vector<const char *> args;
|
2018-09-08 06:40:22 +00:00
|
|
|
IndexMode mode;
|
2018-12-21 04:53:50 +00:00
|
|
|
bool must_exist = false;
|
2018-11-03 20:52:43 +00:00
|
|
|
RequestId id;
|
2018-09-18 01:03:59 +00:00
|
|
|
int64_t ts = tick++;
|
2018-05-28 00:50:02 +00:00
|
|
|
};
|
|
|
|
|
2018-12-14 04:13:35 +00:00
|
|
|
std::mutex thread_mtx;
|
|
|
|
std::condition_variable no_active_threads;
|
|
|
|
int active_threads;
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
MultiQueueWaiter *main_waiter;
|
|
|
|
MultiQueueWaiter *indexer_waiter;
|
|
|
|
MultiQueueWaiter *stdout_waiter;
|
2018-10-28 17:49:31 +00:00
|
|
|
ThreadedQueue<InMessage> *on_request;
|
2018-12-21 04:53:50 +00:00
|
|
|
ThreadedQueue<IndexRequest> *index_request;
|
2018-08-09 17:08:14 +00:00
|
|
|
ThreadedQueue<IndexUpdate> *on_indexed;
|
2019-01-09 07:19:17 +00:00
|
|
|
ThreadedQueue<std::string> *for_stdout;
|
2018-05-28 00:50:02 +00:00
|
|
|
|
2018-09-08 17:37:48 +00:00
|
|
|
struct InMemoryIndexFile {
|
|
|
|
std::string content;
|
|
|
|
IndexFile index;
|
|
|
|
};
|
2018-09-10 06:46:13 +00:00
|
|
|
std::shared_mutex g_index_mutex;
|
2018-09-08 17:37:48 +00:00
|
|
|
std::unordered_map<std::string, InMemoryIndexFile> g_index;
|
|
|
|
|
2018-07-08 18:51:07 +00:00
|
|
|
bool CacheInvalid(VFS *vfs, IndexFile *prev, const std::string &path,
|
2018-09-19 16:31:45 +00:00
|
|
|
const std::vector<const char *> &args,
|
2018-07-08 18:51:07 +00:00
|
|
|
const std::optional<std::string> &from) {
|
2018-05-05 22:29:17 +00:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(vfs->mutex);
|
2018-09-08 17:37:48 +00:00
|
|
|
if (prev->mtime < vfs->state[path].timestamp) {
|
2019-01-29 07:47:03 +00:00
|
|
|
LOG_V(1) << "timestamp changed for " << path
|
|
|
|
<< (from ? " (via " + *from + ")" : std::string());
|
2018-05-05 22:29:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
2018-01-18 05:53:03 +00:00
|
|
|
}
|
|
|
|
|
2018-12-24 05:22:51 +00:00
|
|
|
// For inferred files, allow -o a a.cc -> -o b b.cc
|
|
|
|
std::string stem = sys::path::stem(path);
|
2018-09-19 16:31:45 +00:00
|
|
|
bool changed = prev->args.size() != args.size();
|
|
|
|
for (size_t i = 0; !changed && i < args.size(); i++)
|
2018-12-24 05:22:51 +00:00
|
|
|
if (strcmp(prev->args[i], args[i]) && sys::path::stem(args[i]) != stem)
|
2018-09-19 16:31:45 +00:00
|
|
|
changed = true;
|
|
|
|
if (changed)
|
2019-01-29 07:47:03 +00:00
|
|
|
LOG_V(1) << "args changed for " << path
|
|
|
|
<< (from ? " (via " + *from + ")" : std::string());
|
2018-09-19 16:31:45 +00:00
|
|
|
return changed;
|
2018-01-18 05:53:03 +00:00
|
|
|
};
|
2018-01-18 05:48:09 +00:00
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
std::string AppendSerializationFormat(const std::string &base) {
|
2019-02-21 15:46:20 +00:00
|
|
|
switch (g_config->cache.format) {
|
2018-08-09 17:08:14 +00:00
|
|
|
case SerializeFormat::Binary:
|
|
|
|
return base + ".blob";
|
|
|
|
case SerializeFormat::Json:
|
|
|
|
return base + ".json";
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 15:46:20 +00:00
|
|
|
std::string GetCachePath(const std::string &src) {
|
|
|
|
if (g_config->cache.hierarchicalPath) {
|
|
|
|
std::string ret = src[0] == '/' ? src.substr(1) : src;
|
|
|
|
#ifdef _WIN32
|
|
|
|
std::replace(ret.begin(), ret.end(), ':', '@');
|
|
|
|
#endif
|
|
|
|
return g_config->cache.directory + ret;
|
|
|
|
}
|
2018-10-08 05:02:28 +00:00
|
|
|
for (auto &root : g_config->workspaceFolders)
|
2019-02-21 15:46:20 +00:00
|
|
|
if (StringRef(src).startswith(root)) {
|
2018-10-08 05:02:28 +00:00
|
|
|
auto len = root.size();
|
2019-02-21 15:46:20 +00:00
|
|
|
return g_config->cache.directory +
|
2018-10-08 05:02:28 +00:00
|
|
|
EscapeFileName(root.substr(0, len - 1)) + '/' +
|
2019-02-21 15:46:20 +00:00
|
|
|
EscapeFileName(src.substr(len));
|
2018-10-08 05:02:28 +00:00
|
|
|
}
|
2019-02-21 15:46:20 +00:00
|
|
|
return g_config->cache.directory + '@' +
|
2018-10-08 05:02:28 +00:00
|
|
|
EscapeFileName(g_config->fallbackFolder.substr(
|
|
|
|
0, g_config->fallbackFolder.size() - 1)) +
|
2019-02-21 15:46:20 +00:00
|
|
|
'/' + EscapeFileName(src);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
std::unique_ptr<IndexFile> RawCacheLoad(const std::string &path) {
|
2019-02-21 15:46:20 +00:00
|
|
|
if (g_config->cache.retainInMemory) {
|
2018-09-10 06:46:13 +00:00
|
|
|
std::shared_lock lock(g_index_mutex);
|
2018-09-08 17:37:48 +00:00
|
|
|
auto it = g_index.find(path);
|
2019-02-21 15:46:20 +00:00
|
|
|
if (it != g_index.end())
|
|
|
|
return std::make_unique<IndexFile>(it->second.index);
|
|
|
|
if (g_config->cache.directory.empty())
|
2018-09-08 17:37:48 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
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;
|
|
|
|
|
2019-02-21 15:46:20 +00:00
|
|
|
return ccls::Deserialize(g_config->cache.format, path,
|
2018-06-08 04:53:41 +00:00
|
|
|
*serialized_indexed_content, *file_content,
|
|
|
|
IndexFile::kMajorVersion);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
2018-09-28 20:41:50 +00:00
|
|
|
std::mutex &GetFileMutex(const std::string &path) {
|
2018-09-18 01:03:59 +00:00
|
|
|
const int N_MUTEXES = 256;
|
|
|
|
static std::mutex mutexes[N_MUTEXES];
|
2018-09-28 20:41:50 +00:00
|
|
|
return mutexes[std::hash<std::string>()(path) % N_MUTEXES];
|
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
2018-09-28 20:41:50 +00:00
|
|
|
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
2018-12-21 04:53:50 +00:00
|
|
|
std::optional<IndexRequest> opt_request = index_request->TryPopFront();
|
2018-05-05 22:29:17 +00:00
|
|
|
if (!opt_request)
|
|
|
|
return false;
|
2018-08-09 17:08:14 +00:00
|
|
|
auto &request = *opt_request;
|
2018-09-08 06:40:22 +00:00
|
|
|
bool loud = request.mode != IndexMode::OnChange;
|
2018-10-01 05:54:48 +00:00
|
|
|
struct RAII {
|
|
|
|
~RAII() { pending_index_requests--; }
|
|
|
|
} raii;
|
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-11-27 06:22:27 +00:00
|
|
|
if (!matcher.Matches(request.path)) {
|
2018-09-18 01:03:59 +00:00
|
|
|
LOG_IF_S(INFO, loud) << "skip " << request.path;
|
2018-08-30 15:47:48 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-21 04:53:50 +00:00
|
|
|
// must_exist is currently unused.
|
2018-12-24 05:22:51 +00:00
|
|
|
Project::Entry entry =
|
|
|
|
project->FindEntry(request.path, true, request.must_exist);
|
2018-12-21 04:53:50 +00:00
|
|
|
if (request.must_exist && entry.filename.empty())
|
|
|
|
return true;
|
2018-09-12 18:12:00 +00:00
|
|
|
if (request.args.size())
|
|
|
|
entry.args = request.args;
|
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-12-21 04:53:50 +00:00
|
|
|
bool deleted = false;
|
|
|
|
int reparse = 0;
|
2018-05-05 22:29:17 +00:00
|
|
|
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
2018-12-21 04:53:50 +00:00
|
|
|
if (!write_time) {
|
|
|
|
deleted = true;
|
|
|
|
} else {
|
|
|
|
reparse = vfs->Stamp(path_to_index, *write_time, 0);
|
|
|
|
if (request.path != path_to_index) {
|
|
|
|
std::optional<int64_t> mtime1 = LastWriteTime(request.path);
|
|
|
|
if (!mtime1)
|
|
|
|
deleted = true;
|
|
|
|
else if (vfs->Stamp(request.path, *mtime1, 0))
|
|
|
|
reparse = 2;
|
|
|
|
}
|
2018-09-12 18:12:00 +00:00
|
|
|
}
|
2018-12-21 04:53:50 +00:00
|
|
|
if (deleted)
|
|
|
|
reparse = 2;
|
|
|
|
|
2018-09-20 07:31:23 +00:00
|
|
|
if (g_config->index.onChange) {
|
2018-09-08 06:40:22 +00:00
|
|
|
reparse = 2;
|
2018-09-20 07:31:23 +00:00
|
|
|
std::lock_guard lock(vfs->mutex);
|
|
|
|
vfs->state[path_to_index].step = 0;
|
|
|
|
if (request.path != path_to_index)
|
|
|
|
vfs->state[request.path].step = 0;
|
|
|
|
}
|
2018-09-24 06:02:19 +00:00
|
|
|
bool track = g_config->index.trackDependency > 1 ||
|
|
|
|
(g_config->index.trackDependency == 1 && request.ts < loaded_ts);
|
|
|
|
if (!reparse && !track)
|
2018-05-05 22:29:17 +00:00
|
|
|
return true;
|
|
|
|
|
2018-09-24 06:02:19 +00:00
|
|
|
if (reparse < 2)
|
|
|
|
do {
|
2018-09-28 20:41:50 +00:00
|
|
|
std::unique_lock lock(GetFileMutex(path_to_index));
|
2018-09-24 06:02:19 +00:00
|
|
|
prev = RawCacheLoad(path_to_index);
|
|
|
|
if (!prev || CacheInvalid(vfs, prev.get(), path_to_index, entry.args,
|
|
|
|
std::nullopt))
|
|
|
|
break;
|
|
|
|
if (track)
|
|
|
|
for (const auto &dep : prev->dependencies) {
|
|
|
|
if (auto mtime1 = LastWriteTime(dep.first.val().str())) {
|
|
|
|
if (dep.second < *mtime1) {
|
|
|
|
reparse = 2;
|
2019-01-29 07:47:03 +00:00
|
|
|
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
|
|
|
<< dep.first.val().str();
|
2018-09-24 06:02:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reparse = 2;
|
2019-01-29 07:47:03 +00:00
|
|
|
LOG_V(1) << "timestamp changed for " << path_to_index << " via "
|
|
|
|
<< dep.first.val().str();
|
2018-09-24 06:02:19 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (reparse == 0)
|
|
|
|
return true;
|
|
|
|
if (reparse == 2)
|
|
|
|
break;
|
2018-09-18 01:03:59 +00:00
|
|
|
|
2018-09-24 06:02:19 +00:00
|
|
|
if (vfs->Loaded(path_to_index))
|
|
|
|
return true;
|
2018-09-18 01:03:59 +00:00
|
|
|
LOG_S(INFO) << "load cache for " << path_to_index;
|
|
|
|
auto dependencies = prev->dependencies;
|
2018-09-24 06:02:19 +00:00
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
|
|
|
on_indexed->PushBack(std::move(update),
|
|
|
|
request.mode != IndexMode::NonInteractive);
|
|
|
|
{
|
2018-09-18 01:03:59 +00:00
|
|
|
std::lock_guard lock1(vfs->mutex);
|
2019-02-21 15:46:20 +00:00
|
|
|
vfs->state[path_to_index].loaded++;
|
2018-09-18 01:03:59 +00:00
|
|
|
}
|
|
|
|
lock.unlock();
|
2018-09-24 06:02:19 +00:00
|
|
|
|
2018-09-18 01:03:59 +00:00
|
|
|
for (const auto &dep : dependencies) {
|
|
|
|
std::string path = dep.first.val().str();
|
2018-09-24 06:02:19 +00:00
|
|
|
if (!vfs->Stamp(path, dep.second, 1))
|
|
|
|
continue;
|
2018-09-28 20:41:50 +00:00
|
|
|
std::lock_guard lock1(GetFileMutex(path));
|
2018-09-18 01:03:59 +00:00
|
|
|
prev = RawCacheLoad(path);
|
|
|
|
if (!prev)
|
|
|
|
continue;
|
2018-09-12 18:12:00 +00:00
|
|
|
{
|
2018-09-18 01:03:59 +00:00
|
|
|
std::lock_guard lock2(vfs->mutex);
|
|
|
|
VFS::State &st = vfs->state[path];
|
|
|
|
if (st.loaded)
|
|
|
|
continue;
|
2019-02-21 15:46:20 +00:00
|
|
|
st.loaded++;
|
2018-09-18 01:03:59 +00:00
|
|
|
st.timestamp = prev->mtime;
|
2018-09-12 18:12:00 +00:00
|
|
|
}
|
2018-09-18 01:03:59 +00:00
|
|
|
IndexUpdate update = IndexUpdate::CreateDelta(nullptr, prev.get());
|
|
|
|
on_indexed->PushBack(std::move(update),
|
2018-09-24 06:02:19 +00:00
|
|
|
request.mode != IndexMode::NonInteractive);
|
2018-09-12 18:12:00 +00:00
|
|
|
if (entry.id >= 0) {
|
2018-12-21 09:05:23 +00:00
|
|
|
std::lock_guard lock2(project->mtx);
|
2018-10-08 05:02:28 +00:00
|
|
|
project->root2folder[entry.root].path2entry_index[path] = entry.id;
|
2018-09-12 18:12:00 +00:00
|
|
|
}
|
2018-05-05 22:29:17 +00:00
|
|
|
}
|
2018-09-18 01:03:59 +00:00
|
|
|
return true;
|
2018-09-24 06:02:19 +00:00
|
|
|
} while (0);
|
2018-01-18 08:21:39 +00:00
|
|
|
|
2018-12-22 02:23:35 +00:00
|
|
|
if (loud) {
|
|
|
|
std::string line;
|
|
|
|
if (LOG_V_ENABLED(1)) {
|
|
|
|
line = "\n ";
|
|
|
|
for (auto &arg : entry.args)
|
|
|
|
(line += ' ') += arg;
|
|
|
|
}
|
2018-12-21 04:53:50 +00:00
|
|
|
LOG_S(INFO) << (deleted ? "delete " : "parse ") << path_to_index << line;
|
2018-12-22 02:23:35 +00:00
|
|
|
}
|
2018-01-18 08:21:39 +00:00
|
|
|
|
2018-12-21 04:53:50 +00:00
|
|
|
std::vector<std::unique_ptr<IndexFile>> indexes;
|
|
|
|
if (deleted) {
|
|
|
|
indexes.push_back(std::make_unique<IndexFile>(request.path, ""));
|
|
|
|
if (request.path != path_to_index)
|
|
|
|
indexes.push_back(std::make_unique<IndexFile>(path_to_index, ""));
|
|
|
|
} else {
|
|
|
|
std::vector<std::pair<std::string, std::string>> remapped;
|
|
|
|
if (g_config->index.onChange) {
|
|
|
|
std::string content = wfiles->GetContent(path_to_index);
|
|
|
|
if (content.size())
|
|
|
|
remapped.emplace_back(path_to_index, content);
|
|
|
|
}
|
|
|
|
bool ok;
|
|
|
|
indexes = idx::Index(completion, wfiles, vfs, entry.directory,
|
|
|
|
path_to_index, entry.args, remapped, ok);
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
if (request.id.Valid()) {
|
|
|
|
ResponseError err;
|
|
|
|
err.code = ErrorCode::InternalError;
|
|
|
|
err.message = "failed to index " + path_to_index;
|
|
|
|
pipeline::ReplyError(request.id, err);
|
|
|
|
}
|
|
|
|
return true;
|
2018-01-20 07:56:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
2018-05-05 22:29:17 +00:00
|
|
|
std::string path = curr->path;
|
2018-11-27 06:22:27 +00:00
|
|
|
if (!matcher.Matches(path)) {
|
2018-09-18 01:03:59 +00:00
|
|
|
LOG_IF_S(INFO, loud) << "skip index for " << path;
|
2018-08-30 15:47:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-12-21 04:53:50 +00:00
|
|
|
if (!deleted)
|
|
|
|
LOG_IF_S(INFO, loud) << "store index for " << path
|
|
|
|
<< " (delta: " << !!prev << ")";
|
2018-09-18 01:03:59 +00:00
|
|
|
{
|
2018-09-28 20:41:50 +00:00
|
|
|
std::lock_guard lock(GetFileMutex(path));
|
2019-02-21 15:46:20 +00:00
|
|
|
int loaded = vfs->Loaded(path), retain = g_config->cache.retainInMemory;
|
|
|
|
if (loaded)
|
2018-09-18 01:03:59 +00:00
|
|
|
prev = RawCacheLoad(path);
|
|
|
|
else
|
|
|
|
prev.reset();
|
2019-02-21 15:46:20 +00:00
|
|
|
if (retain > 0 && retain <= loaded + 1) {
|
2018-09-18 01:03:59 +00:00
|
|
|
std::lock_guard lock(g_index_mutex);
|
|
|
|
auto it = g_index.insert_or_assign(
|
|
|
|
path, InMemoryIndexFile{curr->file_contents, *curr});
|
|
|
|
std::string().swap(it.first->second.index.file_contents);
|
2019-02-21 15:46:20 +00:00
|
|
|
}
|
|
|
|
if (g_config->cache.directory.size()) {
|
2018-09-18 01:03:59 +00:00
|
|
|
std::string cache_path = GetCachePath(path);
|
2018-12-21 04:53:50 +00:00
|
|
|
if (deleted) {
|
|
|
|
(void)sys::fs::remove(cache_path);
|
|
|
|
(void)sys::fs::remove(AppendSerializationFormat(cache_path));
|
|
|
|
} else {
|
2019-02-21 15:46:20 +00:00
|
|
|
if (g_config->cache.hierarchicalPath)
|
|
|
|
sys::fs::create_directories(
|
|
|
|
sys::path::parent_path(cache_path, sys::path::Style::posix),
|
|
|
|
true);
|
2018-12-21 04:53:50 +00:00
|
|
|
WriteToFile(cache_path, curr->file_contents);
|
|
|
|
WriteToFile(AppendSerializationFormat(cache_path),
|
2019-02-21 15:46:20 +00:00
|
|
|
Serialize(g_config->cache.format, *curr));
|
2018-12-21 04:53:50 +00:00
|
|
|
}
|
2018-09-18 01:03:59 +00:00
|
|
|
}
|
|
|
|
on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()),
|
|
|
|
request.mode != IndexMode::NonInteractive);
|
|
|
|
{
|
|
|
|
std::lock_guard lock1(vfs->mutex);
|
2019-02-21 15:46:20 +00:00
|
|
|
vfs->state[path].loaded++;
|
2018-09-18 01:03:59 +00:00
|
|
|
}
|
|
|
|
if (entry.id >= 0) {
|
2018-12-21 09:05:23 +00:00
|
|
|
std::lock_guard lock(project->mtx);
|
2018-10-08 05:02:28 +00:00
|
|
|
auto &folder = project->root2folder[entry.root];
|
2018-09-18 01:03:59 +00:00
|
|
|
for (auto &dep : curr->dependencies)
|
2018-10-08 05:02:28 +00:00
|
|
|
folder.path2entry_index[dep.first.val().str()] = entry.id;
|
2018-09-18 01:03:59 +00:00
|
|
|
}
|
2018-03-20 18:55:40 +00:00
|
|
|
}
|
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-12-14 04:13:35 +00:00
|
|
|
void Quit(SemaManager &manager) {
|
|
|
|
quit.store(true, std::memory_order_relaxed);
|
|
|
|
manager.Quit();
|
|
|
|
|
|
|
|
{ std::lock_guard lock(index_request->mutex_); }
|
|
|
|
indexer_waiter->cv.notify_all();
|
|
|
|
{ std::lock_guard lock(for_stdout->mutex_); }
|
|
|
|
stdout_waiter->cv.notify_one();
|
|
|
|
std::unique_lock lock(thread_mtx);
|
|
|
|
no_active_threads.wait(lock, [] { return !active_threads; });
|
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
} // namespace
|
2017-12-29 16:45:10 +00:00
|
|
|
|
2018-12-14 04:13:35 +00:00
|
|
|
void ThreadEnter() {
|
|
|
|
std::lock_guard lock(thread_mtx);
|
|
|
|
active_threads++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadLeave() {
|
|
|
|
std::lock_guard lock(thread_mtx);
|
|
|
|
if (!--active_threads)
|
|
|
|
no_active_threads.notify_one();
|
|
|
|
}
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
void Init() {
|
|
|
|
main_waiter = new MultiQueueWaiter;
|
2018-10-28 17:49:31 +00:00
|
|
|
on_request = new ThreadedQueue<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;
|
2018-12-21 04:53:50 +00:00
|
|
|
index_request = new ThreadedQueue<IndexRequest>(indexer_waiter);
|
2018-05-28 00:50:02 +00:00
|
|
|
|
|
|
|
stdout_waiter = new MultiQueueWaiter;
|
2019-01-09 07:19:17 +00:00
|
|
|
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void Indexer_Main(SemaManager *manager, VFS *vfs, Project *project,
|
2018-09-08 23:00:14 +00:00
|
|
|
WorkingFiles *wfiles) {
|
2018-08-30 15:47:48 +00:00
|
|
|
GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist);
|
2018-05-05 22:29:17 +00:00
|
|
|
while (true)
|
2018-12-01 06:44:52 +00:00
|
|
|
if (!Indexer_Parse(manager, wfiles, project, vfs, matcher))
|
2018-12-14 04:13:35 +00:00
|
|
|
if (indexer_waiter->Wait(quit, index_request))
|
|
|
|
break;
|
2018-02-05 03:38:57 +00:00
|
|
|
}
|
|
|
|
|
2018-10-29 04:21:21 +00:00
|
|
|
void Main_OnIndexed(DB *db, WorkingFiles *wfiles, IndexUpdate *update) {
|
2018-06-01 04:21:34 +00:00
|
|
|
if (update->refresh) {
|
2018-08-09 17:08:14 +00:00
|
|
|
LOG_S(INFO)
|
|
|
|
<< "loaded project. Refresh semantic highlight for all working file.";
|
2018-12-01 06:44:52 +00:00
|
|
|
std::lock_guard lock(wfiles->mutex);
|
|
|
|
for (auto &[f, wf] : wfiles->files) {
|
|
|
|
std::string path = LowerPathIfInsensitive(f);
|
|
|
|
if (db->name2file_id.find(path) == db->name2file_id.end())
|
2018-05-08 15:56:20 +00:00
|
|
|
continue;
|
2018-12-01 06:44:52 +00:00
|
|
|
QueryFile &file = db->files[db->name2file_id[path]];
|
|
|
|
EmitSemanticHighlight(db, wf.get(), file);
|
2018-05-08 07:35:32 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-01 04:21:34 +00:00
|
|
|
db->ApplyIndexUpdate(update);
|
2018-02-05 03:38:57 +00:00
|
|
|
|
2018-07-08 19:49:27 +00:00
|
|
|
// Update indexed content, skipped ranges, and semantic highlighting.
|
2018-06-01 04:21:34 +00:00
|
|
|
if (update->files_def_update) {
|
2018-08-09 17:08:14 +00:00
|
|
|
auto &def_u = *update->files_def_update;
|
2018-12-01 06:44:52 +00:00
|
|
|
if (WorkingFile *wfile = wfiles->GetFile(def_u.first.path)) {
|
2018-09-08 06:40:22 +00:00
|
|
|
// FIXME With index.onChange: true, use buffer_content only for
|
|
|
|
// request.path
|
|
|
|
wfile->SetIndexContent(g_config->index.onChange ? wfile->buffer_content
|
|
|
|
: def_u.second);
|
2019-01-09 07:19:17 +00:00
|
|
|
QueryFile &file = db->files[update->file_id];
|
|
|
|
EmitSkippedRanges(wfile, file);
|
|
|
|
EmitSemanticHighlight(db, wfile, file);
|
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() {
|
2018-12-14 04:13:35 +00:00
|
|
|
ThreadEnter();
|
2018-06-01 03:06:09 +00:00
|
|
|
std::thread([]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("stdin");
|
2018-10-28 17:49:31 +00:00
|
|
|
std::string str;
|
2018-11-26 00:05:00 +00:00
|
|
|
const std::string_view kContentLength("Content-Length: ");
|
2019-03-04 14:52:07 +00:00
|
|
|
bool received_exit = false;
|
2018-05-13 16:52:19 +00:00
|
|
|
while (true) {
|
2018-10-28 17:49:31 +00:00
|
|
|
int len = 0;
|
|
|
|
str.clear();
|
|
|
|
while (true) {
|
|
|
|
int c = getchar();
|
|
|
|
if (c == EOF)
|
2019-03-04 14:52:07 +00:00
|
|
|
goto quit;
|
2018-10-28 17:49:31 +00:00
|
|
|
if (c == '\n') {
|
|
|
|
if (str.empty())
|
|
|
|
break;
|
|
|
|
if (!str.compare(0, kContentLength.size(), kContentLength))
|
|
|
|
len = atoi(str.c_str() + kContentLength.size());
|
|
|
|
str.clear();
|
|
|
|
} else if (c != '\r') {
|
|
|
|
str += c;
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
}
|
2018-02-05 03:38:57 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
str.resize(len);
|
|
|
|
for (int i = 0; i < len; ++i) {
|
|
|
|
int c = getchar();
|
|
|
|
if (c == EOF)
|
2019-03-04 14:52:07 +00:00
|
|
|
goto quit;
|
2018-10-28 17:49:31 +00:00
|
|
|
str[i] = c;
|
|
|
|
}
|
2018-05-13 16:52:19 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
auto message = std::make_unique<char[]>(len);
|
|
|
|
std::copy(str.begin(), str.end(), message.get());
|
|
|
|
auto document = std::make_unique<rapidjson::Document>();
|
|
|
|
document->Parse(message.get(), len);
|
|
|
|
assert(!document->HasParseError());
|
|
|
|
|
|
|
|
JsonReader reader{document.get()};
|
2018-12-02 23:53:33 +00:00
|
|
|
if (!reader.m->HasMember("jsonrpc") ||
|
|
|
|
std::string((*reader.m)["jsonrpc"].GetString()) != "2.0")
|
2018-12-14 04:13:35 +00:00
|
|
|
break;
|
2018-11-03 20:52:43 +00:00
|
|
|
RequestId id;
|
2018-10-28 17:49:31 +00:00
|
|
|
std::string method;
|
|
|
|
ReflectMember(reader, "id", id);
|
|
|
|
ReflectMember(reader, "method", method);
|
2019-01-29 07:47:03 +00:00
|
|
|
if (id.Valid())
|
|
|
|
LOG_V(2) << "receive RequestMessage: " << id.value << " " << method;
|
|
|
|
else
|
|
|
|
LOG_V(2) << "receive NotificationMessage " << method;
|
2018-12-14 04:13:35 +00:00
|
|
|
if (method.empty())
|
|
|
|
continue;
|
2019-03-04 14:52:07 +00:00
|
|
|
received_exit = method == "exit";
|
2018-12-25 06:20:00 +00:00
|
|
|
// g_config is not available before "initialize". Use 0 in that case.
|
2018-10-28 17:49:31 +00:00
|
|
|
on_request->PushBack(
|
2018-12-25 06:20:00 +00:00
|
|
|
{id, std::move(method), std::move(message), std::move(document),
|
|
|
|
chrono::steady_clock::now() +
|
|
|
|
chrono::milliseconds(g_config ? g_config->request.timeout : 0)});
|
2018-10-28 17:49:31 +00:00
|
|
|
|
2019-03-04 14:52:07 +00:00
|
|
|
if (received_exit)
|
2018-05-13 16:52:19 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-03-04 14:52:07 +00:00
|
|
|
|
|
|
|
quit:
|
|
|
|
if (!received_exit) {
|
|
|
|
const std::string_view str("{\"jsonrpc\":\"2.0\",\"method\":\"exit\"}");
|
|
|
|
auto message = std::make_unique<char[]>(str.size());
|
|
|
|
std::copy(str.begin(), str.end(), message.get());
|
|
|
|
auto document = std::make_unique<rapidjson::Document>();
|
|
|
|
document->Parse(message.get(), str.size());
|
|
|
|
on_request->PushBack({RequestId(), std::string("exit"),
|
|
|
|
std::move(message), std::move(document),
|
|
|
|
chrono::steady_clock::now()});
|
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
ThreadLeave();
|
|
|
|
}).detach();
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
|
2018-06-01 03:06:09 +00:00
|
|
|
void LaunchStdout() {
|
2018-12-14 04:13:35 +00:00
|
|
|
ThreadEnter();
|
|
|
|
std::thread([]() {
|
2018-05-27 19:24:56 +00:00
|
|
|
set_thread_name("stdout");
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
while (true) {
|
2019-01-09 07:19:17 +00:00
|
|
|
std::vector<std::string> messages = for_stdout->DequeueAll();
|
|
|
|
for (auto &s : messages) {
|
|
|
|
llvm::outs() << "Content-Length: " << s.size() << "\r\n\r\n" << s;
|
|
|
|
llvm::outs().flush();
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
if (stdout_waiter->Wait(quit, for_stdout))
|
|
|
|
break;
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
ThreadLeave();
|
|
|
|
}).detach();
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
|
2018-05-28 00:50:02 +00:00
|
|
|
void MainLoop() {
|
2018-05-13 16:52:19 +00:00
|
|
|
Project project;
|
2018-10-29 04:21:21 +00:00
|
|
|
WorkingFiles wfiles;
|
2018-05-13 16:52:19 +00:00
|
|
|
VFS vfs;
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
SemaManager manager(
|
2018-10-29 04:21:21 +00:00
|
|
|
&project, &wfiles,
|
2018-11-03 20:52:43 +00:00
|
|
|
[&](std::string path, std::vector<Diagnostic> diagnostics) {
|
|
|
|
PublishDiagnosticParam params;
|
|
|
|
params.uri = DocumentUri::FromPath(path);
|
2019-01-09 07:19:17 +00:00
|
|
|
params.diagnostics = diagnostics;
|
|
|
|
Notify("textDocument/publishDiagnostics", params);
|
2018-05-13 16:52:19 +00:00
|
|
|
},
|
2018-11-03 20:52:43 +00:00
|
|
|
[](RequestId id) {
|
2018-05-13 16:52:19 +00:00
|
|
|
if (id.Valid()) {
|
2018-11-03 20:52:43 +00:00
|
|
|
ResponseError err;
|
|
|
|
err.code = ErrorCode::InternalError;
|
2019-01-09 07:19:17 +00:00
|
|
|
err.message = "drop older completion request";
|
|
|
|
ReplyError(id, err);
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
IncludeComplete include_complete(&project);
|
2018-05-30 06:56:14 +00:00
|
|
|
DB db;
|
2018-05-13 16:52:19 +00:00
|
|
|
|
|
|
|
// Setup shared references.
|
2018-10-28 17:49:31 +00:00
|
|
|
MessageHandler handler;
|
|
|
|
handler.db = &db;
|
|
|
|
handler.project = &project;
|
|
|
|
handler.vfs = &vfs;
|
2018-10-29 04:21:21 +00:00
|
|
|
handler.wfiles = &wfiles;
|
2018-12-01 06:44:52 +00:00
|
|
|
handler.manager = &manager;
|
2018-10-28 17:49:31 +00:00
|
|
|
handler.include_complete = &include_complete;
|
2017-12-24 01:30:52 +00:00
|
|
|
|
2018-09-24 17:56:29 +00:00
|
|
|
bool has_indexed = false;
|
2018-12-25 06:20:00 +00:00
|
|
|
std::deque<InMessage> backlog;
|
|
|
|
StringMap<std::deque<InMessage *>> path2backlog;
|
2018-05-13 16:52:19 +00:00
|
|
|
while (true) {
|
2018-12-25 06:20:00 +00:00
|
|
|
if (backlog.size()) {
|
|
|
|
auto now = chrono::steady_clock::now();
|
|
|
|
handler.overdue = true;
|
|
|
|
while (backlog.size()) {
|
|
|
|
if (backlog[0].backlog_path.size()) {
|
|
|
|
if (now < backlog[0].deadline)
|
|
|
|
break;
|
|
|
|
handler.Run(backlog[0]);
|
|
|
|
path2backlog[backlog[0].backlog_path].pop_front();
|
|
|
|
}
|
|
|
|
backlog.pop_front();
|
|
|
|
}
|
|
|
|
handler.overdue = false;
|
|
|
|
}
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
std::vector<InMessage> messages = on_request->DequeueAll();
|
2018-05-13 16:52:19 +00:00
|
|
|
bool did_work = messages.size();
|
2018-10-28 17:49:31 +00:00
|
|
|
for (InMessage &message : messages)
|
2018-12-25 06:20:00 +00:00
|
|
|
try {
|
|
|
|
handler.Run(message);
|
|
|
|
} catch (NotIndexed &ex) {
|
|
|
|
backlog.push_back(std::move(message));
|
|
|
|
backlog.back().backlog_path = ex.path;
|
|
|
|
path2backlog[ex.path].push_back(&backlog.back());
|
|
|
|
}
|
2018-05-13 16:52:19 +00:00
|
|
|
|
2018-09-23 19:10:40 +00:00
|
|
|
bool indexed = false;
|
|
|
|
for (int i = 20; 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-09-23 19:10:40 +00:00
|
|
|
indexed = true;
|
2018-10-29 04:21:21 +00:00
|
|
|
Main_OnIndexed(&db, &wfiles, &*update);
|
2018-12-25 06:20:00 +00:00
|
|
|
if (update->files_def_update) {
|
|
|
|
auto it = path2backlog.find(update->files_def_update->first.path);
|
|
|
|
if (it != path2backlog.end()) {
|
|
|
|
for (auto &message : it->second) {
|
|
|
|
handler.Run(*message);
|
|
|
|
message->backlog_path.clear();
|
|
|
|
}
|
|
|
|
path2backlog.erase(it);
|
|
|
|
}
|
|
|
|
}
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-14 04:13:35 +00:00
|
|
|
if (did_work) {
|
2018-09-24 17:56:29 +00:00
|
|
|
has_indexed |= indexed;
|
2018-12-14 04:13:35 +00:00
|
|
|
if (quit.load(std::memory_order_relaxed))
|
|
|
|
break;
|
|
|
|
} else {
|
2018-09-24 17:56:29 +00:00
|
|
|
if (has_indexed) {
|
2018-09-23 19:10:40 +00:00
|
|
|
FreeUnusedMemory();
|
2018-09-24 17:56:29 +00:00
|
|
|
has_indexed = false;
|
|
|
|
}
|
2018-12-25 06:20:00 +00:00
|
|
|
if (backlog.empty())
|
|
|
|
main_waiter->Wait(quit, on_indexed, on_request);
|
|
|
|
else
|
|
|
|
main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request);
|
2018-06-09 01:20:51 +00:00
|
|
|
}
|
2018-05-13 16:52:19 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
|
|
|
|
Quit(manager);
|
2017-12-24 01:30:52 +00:00
|
|
|
}
|
2018-05-28 00:50:02 +00:00
|
|
|
|
2018-10-23 05:01:10 +00:00
|
|
|
void Standalone(const std::string &root) {
|
|
|
|
Project project;
|
|
|
|
WorkingFiles wfiles;
|
|
|
|
VFS vfs;
|
2018-12-01 06:44:52 +00:00
|
|
|
SemaManager manager(
|
|
|
|
nullptr, nullptr, [&](std::string, std::vector<Diagnostic>) {},
|
|
|
|
[](RequestId id) {});
|
2018-10-23 05:01:10 +00:00
|
|
|
IncludeComplete complete(&project);
|
2018-10-28 17:49:31 +00:00
|
|
|
|
|
|
|
MessageHandler handler;
|
|
|
|
handler.project = &project;
|
2018-10-29 04:21:21 +00:00
|
|
|
handler.wfiles = &wfiles;
|
2018-10-28 17:49:31 +00:00
|
|
|
handler.vfs = &vfs;
|
2018-12-01 06:44:52 +00:00
|
|
|
handler.manager = &manager;
|
2018-10-28 17:49:31 +00:00
|
|
|
handler.include_complete = &complete;
|
|
|
|
|
|
|
|
StandaloneInitialize(handler, root);
|
2018-10-23 05:01:10 +00:00
|
|
|
bool tty = sys::Process::StandardOutIsDisplayed();
|
|
|
|
|
|
|
|
if (tty) {
|
|
|
|
int entries = 0;
|
|
|
|
for (auto &[_, folder] : project.root2folder)
|
|
|
|
entries += folder.entries.size();
|
|
|
|
printf("entries: %5d\n", entries);
|
|
|
|
}
|
|
|
|
while (1) {
|
|
|
|
(void)on_indexed->DequeueAll();
|
|
|
|
int pending = pending_index_requests;
|
|
|
|
if (tty) {
|
|
|
|
printf("\rpending: %5d", pending);
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
if (!pending)
|
|
|
|
break;
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
if (tty)
|
|
|
|
puts("");
|
2018-12-14 04:13:35 +00:00
|
|
|
Quit(manager);
|
2018-10-23 05:01:10 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 16:31:45 +00:00
|
|
|
void Index(const std::string &path, const std::vector<const char *> &args,
|
2018-12-21 04:53:50 +00:00
|
|
|
IndexMode mode, bool must_exist, RequestId id) {
|
2018-10-01 05:54:48 +00:00
|
|
|
pending_index_requests++;
|
2018-12-21 04:53:50 +00:00
|
|
|
index_request->PushBack({path, args, mode, must_exist, id},
|
|
|
|
mode != IndexMode::NonInteractive);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 15:46:20 +00:00
|
|
|
void RemoveCache(const std::string &path) {
|
|
|
|
if (g_config->cache.directory.size()) {
|
|
|
|
std::lock_guard lock(g_index_mutex);
|
|
|
|
g_index.erase(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-08 17:37:48 +00:00
|
|
|
std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
2019-02-21 15:46:20 +00:00
|
|
|
if (g_config->cache.directory.empty()) {
|
2018-09-10 06:46:13 +00:00
|
|
|
std::shared_lock lock(g_index_mutex);
|
2018-09-08 17:37:48 +00:00
|
|
|
auto it = g_index.find(path);
|
|
|
|
if (it == g_index.end())
|
|
|
|
return {};
|
|
|
|
return it->second.content;
|
|
|
|
}
|
2018-05-28 00:50:02 +00:00
|
|
|
return ReadContent(GetCachePath(path));
|
|
|
|
}
|
|
|
|
|
2018-12-21 04:53:50 +00:00
|
|
|
void NotifyOrRequest(const char *method, bool request,
|
|
|
|
const std::function<void(JsonWriter &)> &fn) {
|
2019-01-09 07:19:17 +00:00
|
|
|
rapidjson::StringBuffer output;
|
|
|
|
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
|
|
|
w.StartObject();
|
|
|
|
w.Key("jsonrpc");
|
|
|
|
w.String("2.0");
|
|
|
|
w.Key("method");
|
|
|
|
w.String(method);
|
2018-12-21 04:53:50 +00:00
|
|
|
if (request) {
|
|
|
|
w.Key("id");
|
|
|
|
w.Int64(request_id.fetch_add(1, std::memory_order_relaxed));
|
|
|
|
}
|
2019-01-09 07:19:17 +00:00
|
|
|
w.Key("params");
|
|
|
|
JsonWriter writer(&w);
|
|
|
|
fn(writer);
|
|
|
|
w.EndObject();
|
2019-01-29 07:47:03 +00:00
|
|
|
LOG_V(2) << (request ? "RequestMessage: " : "NotificationMessage: ") << method;
|
2019-01-09 07:19:17 +00:00
|
|
|
for_stdout->PushBack(output.GetString());
|
|
|
|
}
|
|
|
|
|
2018-11-03 20:52:43 +00:00
|
|
|
static void Reply(RequestId id, const char *key,
|
2018-12-02 23:53:33 +00:00
|
|
|
const std::function<void(JsonWriter &)> &fn) {
|
2019-01-09 07:19:17 +00:00
|
|
|
rapidjson::StringBuffer output;
|
|
|
|
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
|
|
|
w.StartObject();
|
|
|
|
w.Key("jsonrpc");
|
|
|
|
w.String("2.0");
|
|
|
|
w.Key("id");
|
|
|
|
switch (id.type) {
|
2018-11-03 20:52:43 +00:00
|
|
|
case RequestId::kNone:
|
2019-01-09 07:19:17 +00:00
|
|
|
w.Null();
|
|
|
|
break;
|
2018-11-03 20:52:43 +00:00
|
|
|
case RequestId::kInt:
|
2019-01-09 07:19:17 +00:00
|
|
|
w.Int(id.value);
|
|
|
|
break;
|
2018-11-03 20:52:43 +00:00
|
|
|
case RequestId::kString:
|
2019-01-09 07:19:17 +00:00
|
|
|
auto s = std::to_string(id.value);
|
|
|
|
w.String(s.c_str(), s.length());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
w.Key(key);
|
|
|
|
JsonWriter writer(&w);
|
|
|
|
fn(writer);
|
|
|
|
w.EndObject();
|
2019-01-29 07:47:03 +00:00
|
|
|
if (id.Valid())
|
|
|
|
LOG_V(2) << "respond to RequestMessage: " << id.value;
|
2019-01-09 07:19:17 +00:00
|
|
|
for_stdout->PushBack(output.GetString());
|
|
|
|
}
|
|
|
|
|
2018-12-02 23:53:33 +00:00
|
|
|
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
2019-01-09 07:19:17 +00:00
|
|
|
Reply(id, "result", fn);
|
|
|
|
}
|
2018-05-28 00:50:02 +00:00
|
|
|
|
2018-12-02 23:53:33 +00:00
|
|
|
void ReplyError(RequestId id, const std::function<void(JsonWriter &)> &fn) {
|
2019-01-09 07:19:17 +00:00
|
|
|
Reply(id, "error", fn);
|
2018-05-28 00:50:02 +00:00
|
|
|
}
|
2018-10-28 17:49:31 +00:00
|
|
|
} // namespace pipeline
|
|
|
|
} // namespace ccls
|