mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-27 10:02:03 +00:00
Handle file deletion and register workspace/didChangeWatchedFiles
* In the "initialized" callback, send client/registerCapability with DidChangeWatchedFilesRegistrationOptions * In workspace/didChangeWatchedFiles callback, call pipeline::Index * In pipeline::Index, add a `deleted` status
This commit is contained in:
parent
7977bf3edb
commit
d430cb8ff9
@ -70,8 +70,7 @@ struct IndexParam {
|
|||||||
|
|
||||||
if (!vfs.Stamp(path, it->second.mtime, 1))
|
if (!vfs.Stamp(path, it->second.mtime, 1))
|
||||||
return;
|
return;
|
||||||
it->second.db = std::make_unique<IndexFile>(File.getUniqueID(), path,
|
it->second.db = std::make_unique<IndexFile>(path, it->second.content);
|
||||||
it->second.content);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1173,9 +1172,8 @@ public:
|
|||||||
const int IndexFile::kMajorVersion = 19;
|
const int IndexFile::kMajorVersion = 19;
|
||||||
const int IndexFile::kMinorVersion = 1;
|
const int IndexFile::kMinorVersion = 1;
|
||||||
|
|
||||||
IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
|
IndexFile::IndexFile(const std::string &path, const std::string &contents)
|
||||||
const std::string &contents)
|
: path(path), file_contents(contents) {}
|
||||||
: UniqueID(UniqueID), path(path), file_contents(contents) {}
|
|
||||||
|
|
||||||
IndexFunc &IndexFile::ToFunc(Usr usr) {
|
IndexFunc &IndexFile::ToFunc(Usr usr) {
|
||||||
auto [it, inserted] = usr2func.try_emplace(usr);
|
auto [it, inserted] = usr2func.try_emplace(usr);
|
||||||
|
@ -278,7 +278,6 @@ struct IndexFile {
|
|||||||
// files accepted by newer ccls.
|
// files accepted by newer ccls.
|
||||||
static const int kMinorVersion;
|
static const int kMinorVersion;
|
||||||
|
|
||||||
llvm::sys::fs::UniqueID UniqueID;
|
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<const char *> args;
|
std::vector<const char *> args;
|
||||||
// This is unfortunately time_t as used by clang::FileEntry
|
// This is unfortunately time_t as used by clang::FileEntry
|
||||||
@ -308,8 +307,7 @@ struct IndexFile {
|
|||||||
// File contents at the time of index. Not serialized.
|
// File contents at the time of index. Not serialized.
|
||||||
std::string file_contents;
|
std::string file_contents;
|
||||||
|
|
||||||
IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
|
IndexFile(const std::string &path, const std::string &contents);
|
||||||
const std::string &contents);
|
|
||||||
|
|
||||||
IndexFunc &ToFunc(Usr usr);
|
IndexFunc &ToFunc(Usr usr);
|
||||||
IndexType &ToType(Usr usr);
|
IndexType &ToType(Usr usr);
|
||||||
|
@ -169,6 +169,7 @@ MessageHandler::MessageHandler() {
|
|||||||
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||||
Bind("exit", &MessageHandler::exit);
|
Bind("exit", &MessageHandler::exit);
|
||||||
Bind("initialize", &MessageHandler::initialize);
|
Bind("initialize", &MessageHandler::initialize);
|
||||||
|
Bind("initialized", &MessageHandler::initialized);
|
||||||
Bind("shutdown", &MessageHandler::shutdown);
|
Bind("shutdown", &MessageHandler::shutdown);
|
||||||
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
||||||
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
||||||
|
@ -250,6 +250,7 @@ private:
|
|||||||
void ccls_vars(JsonReader &, ReplyOnce &);
|
void ccls_vars(JsonReader &, ReplyOnce &);
|
||||||
void exit(EmptyParam &);
|
void exit(EmptyParam &);
|
||||||
void initialize(JsonReader &, ReplyOnce &);
|
void initialize(JsonReader &, ReplyOnce &);
|
||||||
|
void initialized(EmptyParam &);
|
||||||
void shutdown(EmptyParam &, ReplyOnce &);
|
void shutdown(EmptyParam &, ReplyOnce &);
|
||||||
void textDocument_codeAction(CodeActionParam &, ReplyOnce &);
|
void textDocument_codeAction(CodeActionParam &, ReplyOnce &);
|
||||||
void textDocument_codeLens(TextDocumentParam &, ReplyOnce &);
|
void textDocument_codeLens(TextDocumentParam &, ReplyOnce &);
|
||||||
|
@ -30,6 +30,8 @@ namespace {
|
|||||||
enum class TextDocumentSyncKind { None = 0, Full = 1, Incremental = 2 };
|
enum class TextDocumentSyncKind { None = 0, Full = 1, Incremental = 2 };
|
||||||
REFLECT_UNDERLYING(TextDocumentSyncKind)
|
REFLECT_UNDERLYING(TextDocumentSyncKind)
|
||||||
|
|
||||||
|
bool didChangeWatchedFiles;
|
||||||
|
|
||||||
struct ServerCap {
|
struct ServerCap {
|
||||||
struct SaveOptions {
|
struct SaveOptions {
|
||||||
bool includeText = false;
|
bool includeText = false;
|
||||||
@ -228,6 +230,24 @@ struct InitializeResult {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(InitializeResult, capabilities);
|
REFLECT_STRUCT(InitializeResult, capabilities);
|
||||||
|
|
||||||
|
struct FileSystemWatcher {
|
||||||
|
std::string globPattern = "**/*";
|
||||||
|
};
|
||||||
|
struct DidChangeWatchedFilesRegistration {
|
||||||
|
std::string id = "didChangeWatchedFiles";
|
||||||
|
std::string method = "workspace/didChangeWatchedFiles";
|
||||||
|
struct Option {
|
||||||
|
std::vector<FileSystemWatcher> watchers = {{}};
|
||||||
|
} registerOptions;
|
||||||
|
};
|
||||||
|
struct RegistrationParam {
|
||||||
|
std::vector<DidChangeWatchedFilesRegistration> registrations = {{}};
|
||||||
|
};
|
||||||
|
REFLECT_STRUCT(FileSystemWatcher, globPattern);
|
||||||
|
REFLECT_STRUCT(DidChangeWatchedFilesRegistration::Option, watchers);
|
||||||
|
REFLECT_STRUCT(DidChangeWatchedFilesRegistration, id, method, registerOptions);
|
||||||
|
REFLECT_STRUCT(RegistrationParam, registrations);
|
||||||
|
|
||||||
void *Indexer(void *arg_) {
|
void *Indexer(void *arg_) {
|
||||||
MessageHandler *h;
|
MessageHandler *h;
|
||||||
int idx;
|
int idx;
|
||||||
@ -287,6 +307,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
|||||||
capabilities.textDocument.definition.linkSupport;
|
capabilities.textDocument.definition.linkSupport;
|
||||||
g_config->client.snippetSupport &=
|
g_config->client.snippetSupport &=
|
||||||
capabilities.textDocument.completion.completionItem.snippetSupport;
|
capabilities.textDocument.completion.completionItem.snippetSupport;
|
||||||
|
didChangeWatchedFiles =
|
||||||
|
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration;
|
||||||
|
|
||||||
// Ensure there is a resource directory.
|
// Ensure there is a resource directory.
|
||||||
if (g_config->clang.resourceDir.empty())
|
if (g_config->clang.resourceDir.empty())
|
||||||
@ -359,6 +381,13 @@ void StandaloneInitialize(MessageHandler &handler, const std::string &root) {
|
|||||||
Initialize(&handler, param, reply);
|
Initialize(&handler, param, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageHandler::initialized(EmptyParam &) {
|
||||||
|
if (didChangeWatchedFiles) {
|
||||||
|
RegistrationParam param;
|
||||||
|
pipeline::Request("client/registerCapability", param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
||||||
reply(JsonNull{});
|
reply(JsonNull{});
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
|||||||
std::string path = param.textDocument.uri.GetPath();
|
std::string path = param.textDocument.uri.GetPath();
|
||||||
wfiles->OnChange(param);
|
wfiles->OnChange(param);
|
||||||
if (g_config->index.onChange)
|
if (g_config->index.onChange)
|
||||||
pipeline::Index(path, {}, IndexMode::OnChange);
|
pipeline::Index(path, {}, IndexMode::OnChange, true);
|
||||||
manager->OnView(path);
|
manager->OnView(path);
|
||||||
if (g_config->diagnostics.onChange >= 0)
|
if (g_config->diagnostics.onChange >= 0)
|
||||||
manager->ScheduleDiag(path, g_config->diagnostics.onChange);
|
manager->ScheduleDiag(path, g_config->diagnostics.onChange);
|
||||||
@ -56,14 +56,14 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
|||||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||||
if ((lang.first != LanguageId::Unknown && !lang.second) ||
|
if ((lang.first != LanguageId::Unknown && !lang.second) ||
|
||||||
!pipeline::pending_index_requests)
|
!pipeline::pending_index_requests)
|
||||||
pipeline::Index(path, {}, IndexMode::Normal);
|
pipeline::Index(path, {}, IndexMode::Normal, false);
|
||||||
|
|
||||||
manager->OnView(path);
|
manager->OnView(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||||
const std::string &path = param.textDocument.uri.GetPath();
|
const std::string &path = param.textDocument.uri.GetPath();
|
||||||
pipeline::Index(path, {}, IndexMode::Normal);
|
pipeline::Index(path, {}, IndexMode::Normal, false);
|
||||||
manager->OnSave(path);
|
manager->OnSave(path);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -23,11 +23,13 @@ limitations under the License.
|
|||||||
|
|
||||||
#include <llvm/ADT/STLExtras.h>
|
#include <llvm/ADT/STLExtras.h>
|
||||||
#include <llvm/ADT/StringRef.h>
|
#include <llvm/ADT/StringRef.h>
|
||||||
|
#include <llvm/Support/Path.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName);
|
||||||
@ -44,20 +46,30 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
|||||||
DidChangeWatchedFilesParam ¶m) {
|
DidChangeWatchedFilesParam ¶m) {
|
||||||
for (auto &event : param.changes) {
|
for (auto &event : param.changes) {
|
||||||
std::string path = event.uri.GetPath();
|
std::string path = event.uri.GetPath();
|
||||||
|
if ((g_config->cacheDirectory.size() &&
|
||||||
|
StringRef(path).startswith(g_config->cacheDirectory)) ||
|
||||||
|
lookupExtension(path).first == LanguageId::Unknown)
|
||||||
|
return;
|
||||||
|
for (std::string cur = path; cur.size(); cur = sys::path::parent_path(cur))
|
||||||
|
if (cur[0] == '.')
|
||||||
|
return;
|
||||||
|
|
||||||
IndexMode mode =
|
IndexMode mode =
|
||||||
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive;
|
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive;
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case FileChangeType::Created:
|
case FileChangeType::Created:
|
||||||
case FileChangeType::Changed: {
|
case FileChangeType::Changed: {
|
||||||
pipeline::Index(path, {}, mode);
|
pipeline::Index(path, {}, mode, true);
|
||||||
if (mode == IndexMode::Normal)
|
if (event.type == FileChangeType::Changed) {
|
||||||
manager->OnSave(path);
|
if (mode == IndexMode::Normal)
|
||||||
else
|
manager->OnSave(path);
|
||||||
manager->OnClose(path);
|
else
|
||||||
|
manager->OnClose(path);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FileChangeType::Deleted:
|
case FileChangeType::Deleted:
|
||||||
pipeline::Index(path, {}, mode);
|
pipeline::Index(path, {}, mode, false);
|
||||||
manager->OnClose(path);
|
manager->OnClose(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
113
src/pipeline.cc
113
src/pipeline.cc
@ -65,16 +65,16 @@ void StandaloneInitialize(MessageHandler &, const std::string &root);
|
|||||||
namespace pipeline {
|
namespace pipeline {
|
||||||
|
|
||||||
std::atomic<bool> quit;
|
std::atomic<bool> quit;
|
||||||
std::atomic<int64_t> loaded_ts = ATOMIC_VAR_INIT(0),
|
std::atomic<int64_t> loaded_ts{0}, pending_index_requests{0}, request_id{0};
|
||||||
pending_index_requests = ATOMIC_VAR_INIT(0);
|
|
||||||
int64_t tick = 0;
|
int64_t tick = 0;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct Index_Request {
|
struct IndexRequest {
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<const char *> args;
|
std::vector<const char *> args;
|
||||||
IndexMode mode;
|
IndexMode mode;
|
||||||
|
bool must_exist = false;
|
||||||
RequestId id;
|
RequestId id;
|
||||||
int64_t ts = tick++;
|
int64_t ts = tick++;
|
||||||
};
|
};
|
||||||
@ -87,7 +87,7 @@ MultiQueueWaiter *main_waiter;
|
|||||||
MultiQueueWaiter *indexer_waiter;
|
MultiQueueWaiter *indexer_waiter;
|
||||||
MultiQueueWaiter *stdout_waiter;
|
MultiQueueWaiter *stdout_waiter;
|
||||||
ThreadedQueue<InMessage> *on_request;
|
ThreadedQueue<InMessage> *on_request;
|
||||||
ThreadedQueue<Index_Request> *index_request;
|
ThreadedQueue<IndexRequest> *index_request;
|
||||||
ThreadedQueue<IndexUpdate> *on_indexed;
|
ThreadedQueue<IndexUpdate> *on_indexed;
|
||||||
ThreadedQueue<std::string> *for_stdout;
|
ThreadedQueue<std::string> *for_stdout;
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ std::mutex &GetFileMutex(const std::string &path) {
|
|||||||
|
|
||||||
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||||
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
Project *project, VFS *vfs, const GroupMatch &matcher) {
|
||||||
std::optional<Index_Request> opt_request = index_request->TryPopFront();
|
std::optional<IndexRequest> opt_request = index_request->TryPopFront();
|
||||||
if (!opt_request)
|
if (!opt_request)
|
||||||
return false;
|
return false;
|
||||||
auto &request = *opt_request;
|
auto &request = *opt_request;
|
||||||
@ -194,23 +194,33 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry entry = project->FindEntry(request.path, true);
|
// must_exist is currently unused.
|
||||||
|
Project::Entry entry = project->FindEntry(request.path, false);
|
||||||
|
if (request.must_exist && entry.filename.empty())
|
||||||
|
return true;
|
||||||
if (request.args.size())
|
if (request.args.size())
|
||||||
entry.args = request.args;
|
entry.args = request.args;
|
||||||
std::string path_to_index = entry.filename;
|
std::string path_to_index = entry.filename;
|
||||||
std::unique_ptr<IndexFile> prev;
|
std::unique_ptr<IndexFile> prev;
|
||||||
|
|
||||||
|
bool deleted = false;
|
||||||
|
int reparse = 0;
|
||||||
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
||||||
if (!write_time)
|
if (!write_time) {
|
||||||
return true;
|
deleted = true;
|
||||||
int reparse = vfs->Stamp(path_to_index, *write_time, 0);
|
} else {
|
||||||
if (request.path != path_to_index) {
|
reparse = vfs->Stamp(path_to_index, *write_time, 0);
|
||||||
std::optional<int64_t> mtime1 = LastWriteTime(request.path);
|
if (request.path != path_to_index) {
|
||||||
if (!mtime1)
|
std::optional<int64_t> mtime1 = LastWriteTime(request.path);
|
||||||
return true;
|
if (!mtime1)
|
||||||
if (vfs->Stamp(request.path, *mtime1, 0))
|
deleted = true;
|
||||||
reparse = 2;
|
else if (vfs->Stamp(request.path, *mtime1, 0))
|
||||||
|
reparse = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (deleted)
|
||||||
|
reparse = 2;
|
||||||
|
|
||||||
if (g_config->index.onChange) {
|
if (g_config->index.onChange) {
|
||||||
reparse = 2;
|
reparse = 2;
|
||||||
std::lock_guard lock(vfs->mutex);
|
std::lock_guard lock(vfs->mutex);
|
||||||
@ -294,27 +304,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
for (auto &arg : entry.args)
|
for (auto &arg : entry.args)
|
||||||
(line += ' ') += arg;
|
(line += ' ') += arg;
|
||||||
}
|
}
|
||||||
LOG_S(INFO) << "parse " << path_to_index << line;
|
LOG_S(INFO) << (deleted ? "delete " : "parse ") << path_to_index << line;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> remapped;
|
std::vector<std::unique_ptr<IndexFile>> indexes;
|
||||||
if (g_config->index.onChange) {
|
if (deleted) {
|
||||||
std::string content = wfiles->GetContent(path_to_index);
|
indexes.push_back(std::make_unique<IndexFile>(request.path, ""));
|
||||||
if (content.size())
|
if (request.path != path_to_index)
|
||||||
remapped.emplace_back(path_to_index, content);
|
indexes.push_back(std::make_unique<IndexFile>(path_to_index, ""));
|
||||||
}
|
} else {
|
||||||
bool ok;
|
std::vector<std::pair<std::string, std::string>> remapped;
|
||||||
auto indexes = idx::Index(completion, wfiles, vfs, entry.directory,
|
if (g_config->index.onChange) {
|
||||||
path_to_index, entry.args, remapped, ok);
|
std::string content = wfiles->GetContent(path_to_index);
|
||||||
|
if (content.size())
|
||||||
if (!ok) {
|
remapped.emplace_back(path_to_index, content);
|
||||||
if (request.id.Valid()) {
|
}
|
||||||
ResponseError err;
|
bool ok;
|
||||||
err.code = ErrorCode::InternalError;
|
indexes = idx::Index(completion, wfiles, vfs, entry.directory,
|
||||||
err.message = "failed to index " + path_to_index;
|
path_to_index, entry.args, remapped, ok);
|
||||||
pipeline::ReplyError(request.id, err);
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
||||||
@ -324,8 +341,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_IF_S(INFO, loud) << "store index for " << path << " (delta: " << !!prev
|
if (!deleted)
|
||||||
<< ")";
|
LOG_IF_S(INFO, loud) << "store index for " << path
|
||||||
|
<< " (delta: " << !!prev << ")";
|
||||||
{
|
{
|
||||||
std::lock_guard lock(GetFileMutex(path));
|
std::lock_guard lock(GetFileMutex(path));
|
||||||
if (vfs->Loaded(path))
|
if (vfs->Loaded(path))
|
||||||
@ -339,9 +357,14 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
|||||||
std::string().swap(it.first->second.index.file_contents);
|
std::string().swap(it.first->second.index.file_contents);
|
||||||
} else {
|
} else {
|
||||||
std::string cache_path = GetCachePath(path);
|
std::string cache_path = GetCachePath(path);
|
||||||
WriteToFile(cache_path, curr->file_contents);
|
if (deleted) {
|
||||||
WriteToFile(AppendSerializationFormat(cache_path),
|
(void)sys::fs::remove(cache_path);
|
||||||
Serialize(g_config->cacheFormat, *curr));
|
(void)sys::fs::remove(AppendSerializationFormat(cache_path));
|
||||||
|
} else {
|
||||||
|
WriteToFile(cache_path, curr->file_contents);
|
||||||
|
WriteToFile(AppendSerializationFormat(cache_path),
|
||||||
|
Serialize(g_config->cacheFormat, *curr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()),
|
on_indexed->PushBack(IndexUpdate::CreateDelta(prev.get(), curr.get()),
|
||||||
request.mode != IndexMode::NonInteractive);
|
request.mode != IndexMode::NonInteractive);
|
||||||
@ -392,7 +415,7 @@ void Init() {
|
|||||||
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
||||||
|
|
||||||
indexer_waiter = new MultiQueueWaiter;
|
indexer_waiter = new MultiQueueWaiter;
|
||||||
index_request = new ThreadedQueue<Index_Request>(indexer_waiter);
|
index_request = new ThreadedQueue<IndexRequest>(indexer_waiter);
|
||||||
|
|
||||||
stdout_waiter = new MultiQueueWaiter;
|
stdout_waiter = new MultiQueueWaiter;
|
||||||
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
||||||
@ -625,9 +648,10 @@ void Standalone(const std::string &root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Index(const std::string &path, const std::vector<const char *> &args,
|
void Index(const std::string &path, const std::vector<const char *> &args,
|
||||||
IndexMode mode, RequestId id) {
|
IndexMode mode, bool must_exist, RequestId id) {
|
||||||
pending_index_requests++;
|
pending_index_requests++;
|
||||||
index_request->PushBack({path, args, mode, id}, mode != IndexMode::NonInteractive);
|
index_request->PushBack({path, args, mode, must_exist, id},
|
||||||
|
mode != IndexMode::NonInteractive);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
||||||
@ -641,7 +665,8 @@ std::optional<std::string> LoadIndexedContent(const std::string &path) {
|
|||||||
return ReadContent(GetCachePath(path));
|
return ReadContent(GetCachePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notify(const char *method, const std::function<void(JsonWriter &)> &fn) {
|
void NotifyOrRequest(const char *method, bool request,
|
||||||
|
const std::function<void(JsonWriter &)> &fn) {
|
||||||
rapidjson::StringBuffer output;
|
rapidjson::StringBuffer output;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
rapidjson::Writer<rapidjson::StringBuffer> w(output);
|
||||||
w.StartObject();
|
w.StartObject();
|
||||||
@ -649,6 +674,10 @@ void Notify(const char *method, const std::function<void(JsonWriter &)> &fn) {
|
|||||||
w.String("2.0");
|
w.String("2.0");
|
||||||
w.Key("method");
|
w.Key("method");
|
||||||
w.String(method);
|
w.String(method);
|
||||||
|
if (request) {
|
||||||
|
w.Key("id");
|
||||||
|
w.Int64(request_id.fetch_add(1, std::memory_order_relaxed));
|
||||||
|
}
|
||||||
w.Key("params");
|
w.Key("params");
|
||||||
JsonWriter writer(&w);
|
JsonWriter writer(&w);
|
||||||
fn(writer);
|
fn(writer);
|
||||||
|
@ -54,13 +54,17 @@ void MainLoop();
|
|||||||
void Standalone(const std::string &root);
|
void Standalone(const std::string &root);
|
||||||
|
|
||||||
void Index(const std::string &path, const std::vector<const char *> &args,
|
void Index(const std::string &path, const std::vector<const char *> &args,
|
||||||
IndexMode mode, RequestId id = {});
|
IndexMode mode, bool must_exist, RequestId id = {});
|
||||||
|
|
||||||
std::optional<std::string> LoadIndexedContent(const std::string& path);
|
std::optional<std::string> LoadIndexedContent(const std::string& path);
|
||||||
|
|
||||||
void Notify(const char *method, const std::function<void(JsonWriter &)> &fn);
|
void NotifyOrRequest(const char *method, bool request,
|
||||||
|
const std::function<void(JsonWriter &)> &fn);
|
||||||
template <typename T> void Notify(const char *method, T &result) {
|
template <typename T> void Notify(const char *method, T &result) {
|
||||||
Notify(method, [&](JsonWriter &w) { Reflect(w, result); });
|
NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); });
|
||||||
|
}
|
||||||
|
template <typename T> void Request(const char *method, T &result) {
|
||||||
|
NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
void Reply(RequestId id, const std::function<void(JsonWriter &)> &fn);
|
||||||
|
@ -412,7 +412,7 @@ void Project::Load(const std::string &root) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Project::Entry Project::FindEntry(const std::string &path,
|
Project::Entry Project::FindEntry(const std::string &path,
|
||||||
bool can_be_inferred) {
|
bool must_exist) {
|
||||||
Project::Folder *best_folder = nullptr;
|
Project::Folder *best_folder = nullptr;
|
||||||
const Entry *best = nullptr;
|
const Entry *best = nullptr;
|
||||||
std::lock_guard lock(mtx);
|
std::lock_guard lock(mtx);
|
||||||
@ -420,11 +420,12 @@ Project::Entry Project::FindEntry(const std::string &path,
|
|||||||
auto it = folder.path2entry_index.find(path);
|
auto it = folder.path2entry_index.find(path);
|
||||||
if (it != folder.path2entry_index.end()) {
|
if (it != folder.path2entry_index.end()) {
|
||||||
Project::Entry &entry = folder.entries[it->second];
|
Project::Entry &entry = folder.entries[it->second];
|
||||||
if (can_be_inferred || entry.filename == path)
|
if (!must_exist || entry.filename == path)
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool exists = false;
|
||||||
std::string dir;
|
std::string dir;
|
||||||
const std::vector<const char *> *extra = nullptr;
|
const std::vector<const char *> *extra = nullptr;
|
||||||
Project::Entry ret;
|
Project::Entry ret;
|
||||||
@ -432,11 +433,12 @@ Project::Entry Project::FindEntry(const std::string &path,
|
|||||||
if (StringRef(path).startswith(root))
|
if (StringRef(path).startswith(root))
|
||||||
for (auto &[dir1, args] : folder.dot_ccls)
|
for (auto &[dir1, args] : folder.dot_ccls)
|
||||||
if (StringRef(path).startswith(dir1)) {
|
if (StringRef(path).startswith(dir1)) {
|
||||||
if (AppendToCDB(args)) {
|
dir = dir1;
|
||||||
dir = dir1;
|
extra = &args;
|
||||||
extra = &args;
|
if (AppendToCDB(args))
|
||||||
goto out;
|
goto out;
|
||||||
}
|
exists = true;
|
||||||
|
|
||||||
ret.root = ret.directory = root;
|
ret.root = ret.directory = root;
|
||||||
ret.filename = path;
|
ret.filename = path;
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
@ -452,6 +454,8 @@ Project::Entry Project::FindEntry(const std::string &path,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (must_exist && !exists)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (!best) {
|
if (!best) {
|
||||||
int best_score = INT_MIN;
|
int best_score = INT_MIN;
|
||||||
@ -514,9 +518,10 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
|||||||
if (match.Matches(entry.filename, &reason) &&
|
if (match.Matches(entry.filename, &reason) &&
|
||||||
match_i.Matches(entry.filename, &reason)) {
|
match_i.Matches(entry.filename, &reason)) {
|
||||||
bool interactive = wfiles->GetFile(entry.filename) != nullptr;
|
bool interactive = wfiles->GetFile(entry.filename) != nullptr;
|
||||||
pipeline::Index(
|
pipeline::Index(entry.filename, entry.args,
|
||||||
entry.filename, entry.args,
|
interactive ? IndexMode::Normal
|
||||||
interactive ? IndexMode::Normal : IndexMode::NonInteractive, id);
|
: IndexMode::NonInteractive,
|
||||||
|
false, id);
|
||||||
} else {
|
} else {
|
||||||
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
|
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
|
||||||
<< "; skip " << entry.filename;
|
<< "; skip " << entry.filename;
|
||||||
@ -529,6 +534,6 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
|||||||
pipeline::loaded_ts = pipeline::tick;
|
pipeline::loaded_ts = pipeline::tick;
|
||||||
// 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.
|
||||||
pipeline::Index("", {}, IndexMode::NonInteractive);
|
pipeline::Index("", {}, IndexMode::NonInteractive, false);
|
||||||
}
|
}
|
||||||
} // namespace ccls
|
} // namespace ccls
|
||||||
|
@ -71,8 +71,7 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
|||||||
|
|
||||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||||
IndexUpdate r;
|
IndexUpdate r;
|
||||||
static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path,
|
static IndexFile empty(current->path, "<empty>");
|
||||||
"<empty>");
|
|
||||||
if (previous)
|
if (previous)
|
||||||
r.prev_lid2path = std::move(previous->lid2path);
|
r.prev_lid2path = std::move(previous->lid2path);
|
||||||
else
|
else
|
||||||
|
@ -290,7 +290,7 @@ public:
|
|||||||
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
||||||
Session &session, std::unique_ptr<CompilerInvocation> CI,
|
Session &session, std::unique_ptr<CompilerInvocation> CI,
|
||||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
|
||||||
const PreambleData *preamble, const std::string &path,
|
const PreambleData *preamble, const std::string &main,
|
||||||
std::unique_ptr<llvm::MemoryBuffer> &Buf) {
|
std::unique_ptr<llvm::MemoryBuffer> &Buf) {
|
||||||
if (preamble) {
|
if (preamble) {
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
#if LLVM_VERSION_MAJOR >= 7
|
||||||
@ -299,7 +299,7 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
|||||||
preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get());
|
preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get());
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
CI->getPreprocessorOpts().addRemappedFile(path, Buf.get());
|
CI->getPreprocessorOpts().addRemappedFile(main, Buf.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
||||||
@ -316,6 +316,11 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
|||||||
Clang->createFileManager();
|
Clang->createFileManager();
|
||||||
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
|
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
|
||||||
Clang->getFileManager(), true));
|
Clang->getFileManager(), true));
|
||||||
|
auto &IS = Clang->getFrontendOpts().Inputs;
|
||||||
|
if (IS.size()) {
|
||||||
|
assert(IS[0].isFile());
|
||||||
|
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
||||||
|
}
|
||||||
return Clang;
|
return Clang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +696,7 @@ SemaManager::EnsureSession(const std::string &path, bool *created) {
|
|||||||
std::shared_ptr<ccls::Session> session = sessions.Get(path);
|
std::shared_ptr<ccls::Session> session = sessions.Get(path);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
session = std::make_shared<ccls::Session>(
|
session = std::make_shared<ccls::Session>(
|
||||||
project_->FindEntry(path, false), wfiles, PCH);
|
project_->FindEntry(path, true), wfiles, PCH);
|
||||||
std::string line;
|
std::string line;
|
||||||
if (LOG_V_ENABLED(1)) {
|
if (LOG_V_ENABLED(1)) {
|
||||||
line = "\n ";
|
line = "\n ";
|
||||||
|
@ -470,8 +470,7 @@ Deserialize(SerializeFormat format, const std::string &path,
|
|||||||
if (major != IndexFile::kMajorVersion ||
|
if (major != IndexFile::kMajorVersion ||
|
||||||
minor != IndexFile::kMinorVersion)
|
minor != IndexFile::kMinorVersion)
|
||||||
throw std::invalid_argument("Invalid version");
|
throw std::invalid_argument("Invalid version");
|
||||||
file = std::make_unique<IndexFile>(sys::fs::UniqueID(0, 0), path,
|
file = std::make_unique<IndexFile>(path, file_content);
|
||||||
file_content);
|
|
||||||
ReflectFile(reader, *file);
|
ReflectFile(reader, *file);
|
||||||
} catch (std::invalid_argument &e) {
|
} catch (std::invalid_argument &e) {
|
||||||
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
||||||
@ -494,8 +493,7 @@ Deserialize(SerializeFormat format, const std::string &path,
|
|||||||
if (reader.HasParseError())
|
if (reader.HasParseError())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
file = std::make_unique<IndexFile>(sys::fs::UniqueID(0, 0), path,
|
file = std::make_unique<IndexFile>(path, file_content);
|
||||||
file_content);
|
|
||||||
JsonReader json_reader{&reader};
|
JsonReader json_reader{&reader};
|
||||||
try {
|
try {
|
||||||
ReflectFile(json_reader, *file);
|
ReflectFile(json_reader, *file);
|
||||||
|
Loading…
Reference in New Issue
Block a user