mirror of
https://github.com/MaskRay/ccls.git
synced 2025-06-10 02:02:12 +00:00
Handle workspace/didChangeWatchedFiles with deleted files
* In the "initialize" 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
b1492fedef
commit
ad113151dc
@ -82,8 +82,7 @@ struct IndexParam {
|
||||
|
||||
if (!vfs.Stamp(path, it->second.mtime, 1))
|
||||
return;
|
||||
it->second.db = std::make_unique<IndexFile>(File.getUniqueID(), path,
|
||||
it->second.content);
|
||||
it->second.db = std::make_unique<IndexFile>(path, it->second.content);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1186,9 +1185,8 @@ public:
|
||||
const int IndexFile::kMajorVersion = 19;
|
||||
const int IndexFile::kMinorVersion = 1;
|
||||
|
||||
IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
|
||||
const std::string &contents)
|
||||
: UniqueID(UniqueID), path(path), file_contents(contents) {}
|
||||
IndexFile::IndexFile(const std::string &path, const std::string &contents)
|
||||
: path(path), file_contents(contents) {}
|
||||
|
||||
IndexFunc &IndexFile::ToFunc(Usr usr) {
|
||||
auto [it, inserted] = usr2func.try_emplace(usr);
|
||||
|
@ -290,7 +290,6 @@ struct IndexFile {
|
||||
// files accepted by newer ccls.
|
||||
static const int kMinorVersion;
|
||||
|
||||
llvm::sys::fs::UniqueID UniqueID;
|
||||
std::string path;
|
||||
std::vector<const char *> args;
|
||||
// This is unfortunately time_t as used by clang::FileEntry
|
||||
@ -320,8 +319,7 @@ struct IndexFile {
|
||||
// File contents at the time of index. Not serialized.
|
||||
std::string file_contents;
|
||||
|
||||
IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
|
||||
const std::string &contents);
|
||||
IndexFile(const std::string &path, const std::string &contents);
|
||||
|
||||
IndexFunc &ToFunc(Usr usr);
|
||||
IndexType &ToType(Usr usr);
|
||||
|
@ -181,6 +181,7 @@ MessageHandler::MessageHandler() {
|
||||
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||
Bind("exit", &MessageHandler::exit);
|
||||
Bind("initialize", &MessageHandler::initialize);
|
||||
Bind("initialized", &MessageHandler::initialized);
|
||||
Bind("shutdown", &MessageHandler::shutdown);
|
||||
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
||||
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
||||
|
@ -250,6 +250,7 @@ private:
|
||||
void ccls_vars(JsonReader &, ReplyOnce &);
|
||||
void exit(EmptyParam &);
|
||||
void initialize(JsonReader &, ReplyOnce &);
|
||||
void initialized(EmptyParam &);
|
||||
void shutdown(EmptyParam &, ReplyOnce &);
|
||||
void textDocument_codeAction(CodeActionParam &, ReplyOnce &);
|
||||
void textDocument_codeLens(TextDocumentParam &, ReplyOnce &);
|
||||
|
@ -42,6 +42,8 @@ namespace {
|
||||
enum class TextDocumentSyncKind { None = 0, Full = 1, Incremental = 2 };
|
||||
REFLECT_UNDERLYING(TextDocumentSyncKind)
|
||||
|
||||
bool didChangeWatchedFiles;
|
||||
|
||||
struct ServerCap {
|
||||
struct SaveOptions {
|
||||
bool includeText = false;
|
||||
@ -239,6 +241,24 @@ struct InitializeResult {
|
||||
};
|
||||
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_) {
|
||||
MessageHandler *h;
|
||||
int idx;
|
||||
@ -297,6 +317,8 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) {
|
||||
capabilities.textDocument.definition.linkSupport;
|
||||
g_config->client.snippetSupport &=
|
||||
capabilities.textDocument.completion.completionItem.snippetSupport;
|
||||
didChangeWatchedFiles =
|
||||
capabilities.workspace.didChangeWatchedFiles.dynamicRegistration;
|
||||
|
||||
// Ensure there is a resource directory.
|
||||
if (g_config->clang.resourceDir.empty())
|
||||
@ -369,6 +391,13 @@ void StandaloneInitialize(MessageHandler &handler, const std::string &root) {
|
||||
Initialize(&handler, param, reply);
|
||||
}
|
||||
|
||||
void MessageHandler::initialized(EmptyParam &) {
|
||||
if (didChangeWatchedFiles) {
|
||||
RegistrationParam param;
|
||||
pipeline::Notify("client/registerCapability", param);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) {
|
||||
reply(JsonNull{});
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ void MessageHandler::textDocument_didChange(TextDocumentDidChangeParam ¶m) {
|
||||
std::string path = param.textDocument.uri.GetPath();
|
||||
wfiles->OnChange(param);
|
||||
if (g_config->index.onChange)
|
||||
pipeline::Index(path, {}, IndexMode::OnChange);
|
||||
pipeline::Index(path, {}, IndexMode::OnChange, true);
|
||||
manager->OnView(path);
|
||||
if (g_config->diagnostics.onChange >= 0)
|
||||
manager->ScheduleDiag(path, g_config->diagnostics.onChange);
|
||||
@ -56,14 +56,14 @@ void MessageHandler::textDocument_didOpen(DidOpenTextDocumentParam ¶m) {
|
||||
std::pair<LanguageId, bool> lang = lookupExtension(path);
|
||||
if ((lang.first != LanguageId::Unknown && !lang.second) ||
|
||||
!pipeline::pending_index_requests)
|
||||
pipeline::Index(path, {}, IndexMode::Normal);
|
||||
pipeline::Index(path, {}, IndexMode::Normal, false);
|
||||
|
||||
manager->OnView(path);
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_didSave(TextDocumentParam ¶m) {
|
||||
const std::string &path = param.textDocument.uri.GetPath();
|
||||
pipeline::Index(path, {}, IndexMode::Normal);
|
||||
pipeline::Index(path, {}, IndexMode::Normal, true);
|
||||
manager->OnSave(path);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -48,8 +48,10 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
||||
wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive;
|
||||
switch (event.type) {
|
||||
case FileChangeType::Created:
|
||||
pipeline::Index(path, {}, mode, false);
|
||||
break;
|
||||
case FileChangeType::Changed: {
|
||||
pipeline::Index(path, {}, mode);
|
||||
pipeline::Index(path, {}, mode, false);
|
||||
if (mode == IndexMode::Normal)
|
||||
manager->OnSave(path);
|
||||
else
|
||||
@ -57,7 +59,7 @@ void MessageHandler::workspace_didChangeWatchedFiles(
|
||||
break;
|
||||
}
|
||||
case FileChangeType::Deleted:
|
||||
pipeline::Index(path, {}, mode);
|
||||
pipeline::Index(path, {}, mode, true);
|
||||
manager->OnClose(path);
|
||||
break;
|
||||
}
|
||||
|
@ -84,10 +84,11 @@ int64_t tick = 0;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Index_Request {
|
||||
struct IndexRequest {
|
||||
std::string path;
|
||||
std::vector<const char *> args;
|
||||
IndexMode mode;
|
||||
bool must_exist = false;
|
||||
RequestId id;
|
||||
int64_t ts = tick++;
|
||||
};
|
||||
@ -100,7 +101,7 @@ MultiQueueWaiter *main_waiter;
|
||||
MultiQueueWaiter *indexer_waiter;
|
||||
MultiQueueWaiter *stdout_waiter;
|
||||
ThreadedQueue<InMessage> *on_request;
|
||||
ThreadedQueue<Index_Request> *index_request;
|
||||
ThreadedQueue<IndexRequest> *index_request;
|
||||
ThreadedQueue<IndexUpdate> *on_indexed;
|
||||
ThreadedQueue<std::string> *for_stdout;
|
||||
|
||||
@ -185,7 +186,7 @@ std::mutex &GetFileMutex(const std::string &path) {
|
||||
|
||||
bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
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)
|
||||
return false;
|
||||
auto &request = *opt_request;
|
||||
@ -207,23 +208,32 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
return false;
|
||||
}
|
||||
|
||||
Project::Entry entry = project->FindEntry(request.path, true);
|
||||
Project::Entry entry = project->FindEntry(request.path, request.must_exist);
|
||||
if (request.must_exist && entry.filename.empty())
|
||||
return true;
|
||||
if (request.args.size())
|
||||
entry.args = request.args;
|
||||
std::string path_to_index = entry.filename;
|
||||
std::unique_ptr<IndexFile> prev;
|
||||
|
||||
bool deleted = false;
|
||||
int reparse = 0;
|
||||
std::optional<int64_t> write_time = LastWriteTime(path_to_index);
|
||||
if (!write_time)
|
||||
return true;
|
||||
int 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)
|
||||
return true;
|
||||
if (vfs->Stamp(request.path, *mtime1, 0))
|
||||
reparse = 2;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (deleted)
|
||||
reparse = 2;
|
||||
|
||||
if (g_config->index.onChange) {
|
||||
reparse = 2;
|
||||
std::lock_guard lock(vfs->mutex);
|
||||
@ -307,27 +317,34 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles,
|
||||
for (auto &arg : entry.args)
|
||||
(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;
|
||||
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;
|
||||
auto 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);
|
||||
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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (std::unique_ptr<IndexFile> &curr : indexes) {
|
||||
@ -405,7 +422,7 @@ void Init() {
|
||||
on_indexed = new ThreadedQueue<IndexUpdate>(main_waiter);
|
||||
|
||||
indexer_waiter = new MultiQueueWaiter;
|
||||
index_request = new ThreadedQueue<Index_Request>(indexer_waiter);
|
||||
index_request = new ThreadedQueue<IndexRequest>(indexer_waiter);
|
||||
|
||||
stdout_waiter = new MultiQueueWaiter;
|
||||
for_stdout = new ThreadedQueue<std::string>(stdout_waiter);
|
||||
@ -638,9 +655,10 @@ void Standalone(const std::string &root) {
|
||||
}
|
||||
|
||||
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++;
|
||||
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) {
|
||||
|
@ -51,7 +51,7 @@ void MainLoop();
|
||||
void Standalone(const std::string &root);
|
||||
|
||||
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);
|
||||
|
||||
|
@ -413,13 +413,13 @@ void Project::Load(const std::string &root) {
|
||||
}
|
||||
|
||||
Project::Entry Project::FindEntry(const std::string &path,
|
||||
bool can_be_inferred) {
|
||||
bool must_exist) {
|
||||
std::shared_lock lock(mtx);
|
||||
for (auto &[root, folder] : root2folder) {
|
||||
auto it = folder.path2entry_index.find(path);
|
||||
if (it != folder.path2entry_index.end()) {
|
||||
Project::Entry &entry = folder.entries[it->second];
|
||||
if (can_be_inferred || entry.filename == path)
|
||||
if (entry.filename == path)
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@ -427,6 +427,8 @@ Project::Entry Project::FindEntry(const std::string &path,
|
||||
std::string dir;
|
||||
const std::vector<const char *> *extra = nullptr;
|
||||
Project::Entry ret;
|
||||
if (must_exist)
|
||||
return ret;
|
||||
for (auto &[root, folder] : root2folder)
|
||||
if (StringRef(path).startswith(root))
|
||||
for (auto &[dir1, args] : folder.dot_ccls)
|
||||
@ -512,9 +514,10 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
||||
if (match.Matches(entry.filename, &reason) &&
|
||||
match_i.Matches(entry.filename, &reason)) {
|
||||
bool interactive = wfiles->GetFile(entry.filename) != nullptr;
|
||||
pipeline::Index(
|
||||
entry.filename, entry.args,
|
||||
interactive ? IndexMode::Normal : IndexMode::NonInteractive, id);
|
||||
pipeline::Index(entry.filename, entry.args,
|
||||
interactive ? IndexMode::Normal
|
||||
: IndexMode::NonInteractive,
|
||||
false, id);
|
||||
} else {
|
||||
LOG_V(1) << "[" << i << "/" << folder.entries.size() << "]: " << reason
|
||||
<< "; skip " << entry.filename;
|
||||
@ -527,6 +530,6 @@ void Project::Index(WorkingFiles *wfiles, RequestId id) {
|
||||
pipeline::loaded_ts = pipeline::tick;
|
||||
// Dummy request to indicate that project is loaded and
|
||||
// trigger refreshing semantic highlight for all working files.
|
||||
pipeline::Index("", {}, IndexMode::NonInteractive);
|
||||
pipeline::Index("", {}, IndexMode::NonInteractive, false);
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -83,8 +83,7 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q> &def_list, Q &&def) {
|
||||
|
||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||
IndexUpdate r;
|
||||
static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path,
|
||||
"<empty>");
|
||||
static IndexFile empty(current->path, "<empty>");
|
||||
if (previous)
|
||||
r.prev_lid2path = std::move(previous->lid2path);
|
||||
else
|
||||
|
@ -482,8 +482,7 @@ Deserialize(SerializeFormat format, const std::string &path,
|
||||
if (major != IndexFile::kMajorVersion ||
|
||||
minor != IndexFile::kMinorVersion)
|
||||
throw std::invalid_argument("Invalid version");
|
||||
file = std::make_unique<IndexFile>(sys::fs::UniqueID(0, 0), path,
|
||||
file_content);
|
||||
file = std::make_unique<IndexFile>(path, file_content);
|
||||
ReflectFile(reader, *file);
|
||||
} catch (std::invalid_argument &e) {
|
||||
LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what();
|
||||
@ -506,8 +505,7 @@ Deserialize(SerializeFormat format, const std::string &path,
|
||||
if (reader.HasParseError())
|
||||
return nullptr;
|
||||
|
||||
file = std::make_unique<IndexFile>(sys::fs::UniqueID(0, 0), path,
|
||||
file_content);
|
||||
file = std::make_unique<IndexFile>(path, file_content);
|
||||
JsonReader json_reader{&reader};
|
||||
try {
|
||||
ReflectFile(json_reader, *file);
|
||||
|
Loading…
Reference in New Issue
Block a user