diff --git a/src/indexer.cc b/src/indexer.cc index eba13560..d33299dc 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -70,8 +70,7 @@ struct IndexParam { if (!vfs.Stamp(path, it->second.mtime, 1)) return; - it->second.db = std::make_unique(File.getUniqueID(), path, - it->second.content); + it->second.db = std::make_unique(path, it->second.content); } } @@ -1173,9 +1172,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); diff --git a/src/indexer.hh b/src/indexer.hh index e1c604c3..cc127b1d 100644 --- a/src/indexer.hh +++ b/src/indexer.hh @@ -278,7 +278,6 @@ struct IndexFile { // files accepted by newer ccls. static const int kMinorVersion; - llvm::sys::fs::UniqueID UniqueID; std::string path; std::vector args; // 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. 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); diff --git a/src/message_handler.cc b/src/message_handler.cc index 478267c9..08d5d1b2 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -169,6 +169,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); diff --git a/src/message_handler.hh b/src/message_handler.hh index 7063fe1c..b0a3747c 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -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 &); diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index 63b7803c..f87264ee 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -30,6 +30,8 @@ namespace { enum class TextDocumentSyncKind { None = 0, Full = 1, Incremental = 2 }; REFLECT_UNDERLYING(TextDocumentSyncKind) +bool didChangeWatchedFiles; + struct ServerCap { struct SaveOptions { bool includeText = false; @@ -228,6 +230,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 watchers = {{}}; + } registerOptions; +}; +struct RegistrationParam { + std::vector 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; @@ -287,6 +307,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()) @@ -359,6 +381,13 @@ void StandaloneInitialize(MessageHandler &handler, const std::string &root) { Initialize(&handler, param, reply); } +void MessageHandler::initialized(EmptyParam &) { + if (didChangeWatchedFiles) { + RegistrationParam param; + pipeline::Request("client/registerCapability", param); + } +} + void MessageHandler::shutdown(EmptyParam &, ReplyOnce &reply) { reply(JsonNull{}); } diff --git a/src/messages/textDocument_did.cc b/src/messages/textDocument_did.cc index 2dc91629..b00b3c7d 100644 --- a/src/messages/textDocument_did.cc +++ b/src/messages/textDocument_did.cc @@ -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 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, false); manager->OnSave(path); } } // namespace ccls diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index 5b049958..5435d98f 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -23,11 +23,13 @@ limitations under the License. #include #include +#include #include #include #include #include +using namespace llvm; namespace ccls { REFLECT_STRUCT(SymbolInformation, name, kind, location, containerName); @@ -44,20 +46,30 @@ void MessageHandler::workspace_didChangeWatchedFiles( DidChangeWatchedFilesParam ¶m) { for (auto &event : param.changes) { 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 = wfiles->GetFile(path) ? IndexMode::Normal : IndexMode::NonInteractive; switch (event.type) { case FileChangeType::Created: case FileChangeType::Changed: { - pipeline::Index(path, {}, mode); - if (mode == IndexMode::Normal) - manager->OnSave(path); - else - manager->OnClose(path); + pipeline::Index(path, {}, mode, true); + if (event.type == FileChangeType::Changed) { + if (mode == IndexMode::Normal) + manager->OnSave(path); + else + manager->OnClose(path); + } break; } case FileChangeType::Deleted: - pipeline::Index(path, {}, mode); + pipeline::Index(path, {}, mode, false); manager->OnClose(path); break; } diff --git a/src/pipeline.cc b/src/pipeline.cc index 0e5e031c..c7b8e204 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -65,16 +65,16 @@ void StandaloneInitialize(MessageHandler &, const std::string &root); namespace pipeline { std::atomic quit; -std::atomic loaded_ts = ATOMIC_VAR_INIT(0), - pending_index_requests = ATOMIC_VAR_INIT(0); +std::atomic loaded_ts{0}, pending_index_requests{0}, request_id{0}; int64_t tick = 0; namespace { -struct Index_Request { +struct IndexRequest { std::string path; std::vector args; IndexMode mode; + bool must_exist = false; RequestId id; int64_t ts = tick++; }; @@ -87,7 +87,7 @@ MultiQueueWaiter *main_waiter; MultiQueueWaiter *indexer_waiter; MultiQueueWaiter *stdout_waiter; ThreadedQueue *on_request; -ThreadedQueue *index_request; +ThreadedQueue *index_request; ThreadedQueue *on_indexed; ThreadedQueue *for_stdout; @@ -172,7 +172,7 @@ std::mutex &GetFileMutex(const std::string &path) { bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, Project *project, VFS *vfs, const GroupMatch &matcher) { - std::optional opt_request = index_request->TryPopFront(); + std::optional opt_request = index_request->TryPopFront(); if (!opt_request) return false; auto &request = *opt_request; @@ -194,23 +194,33 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, 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()) entry.args = request.args; std::string path_to_index = entry.filename; std::unique_ptr prev; + bool deleted = false; + int reparse = 0; std::optional 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 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 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); @@ -294,27 +304,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> 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> indexes; + if (deleted) { + indexes.push_back(std::make_unique(request.path, "")); + if (request.path != path_to_index) + indexes.push_back(std::make_unique(path_to_index, "")); + } else { + std::vector> 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 &curr : indexes) { @@ -324,8 +341,9 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, 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)); if (vfs->Loaded(path)) @@ -339,9 +357,14 @@ bool Indexer_Parse(SemaManager *completion, WorkingFiles *wfiles, std::string().swap(it.first->second.index.file_contents); } else { std::string cache_path = GetCachePath(path); - WriteToFile(cache_path, curr->file_contents); - WriteToFile(AppendSerializationFormat(cache_path), - Serialize(g_config->cacheFormat, *curr)); + if (deleted) { + (void)sys::fs::remove(cache_path); + (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()), request.mode != IndexMode::NonInteractive); @@ -392,7 +415,7 @@ void Init() { on_indexed = new ThreadedQueue(main_waiter); indexer_waiter = new MultiQueueWaiter; - index_request = new ThreadedQueue(indexer_waiter); + index_request = new ThreadedQueue(indexer_waiter); stdout_waiter = new MultiQueueWaiter; for_stdout = new ThreadedQueue(stdout_waiter); @@ -625,9 +648,10 @@ void Standalone(const std::string &root) { } void Index(const std::string &path, const std::vector &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 LoadIndexedContent(const std::string &path) { @@ -641,7 +665,8 @@ std::optional LoadIndexedContent(const std::string &path) { return ReadContent(GetCachePath(path)); } -void Notify(const char *method, const std::function &fn) { +void NotifyOrRequest(const char *method, bool request, + const std::function &fn) { rapidjson::StringBuffer output; rapidjson::Writer w(output); w.StartObject(); @@ -649,6 +674,10 @@ void Notify(const char *method, const std::function &fn) { w.String("2.0"); w.Key("method"); w.String(method); + if (request) { + w.Key("id"); + w.Int64(request_id.fetch_add(1, std::memory_order_relaxed)); + } w.Key("params"); JsonWriter writer(&w); fn(writer); diff --git a/src/pipeline.hh b/src/pipeline.hh index b63c17f0..0ec48090 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -54,13 +54,17 @@ void MainLoop(); void Standalone(const std::string &root); void Index(const std::string &path, const std::vector &args, - IndexMode mode, RequestId id = {}); + IndexMode mode, bool must_exist, RequestId id = {}); std::optional LoadIndexedContent(const std::string& path); -void Notify(const char *method, const std::function &fn); +void NotifyOrRequest(const char *method, bool request, + const std::function &fn); template void Notify(const char *method, T &result) { - Notify(method, [&](JsonWriter &w) { Reflect(w, result); }); + NotifyOrRequest(method, false, [&](JsonWriter &w) { Reflect(w, result); }); +} +template void Request(const char *method, T &result) { + NotifyOrRequest(method, true, [&](JsonWriter &w) { Reflect(w, result); }); } void Reply(RequestId id, const std::function &fn); diff --git a/src/project.cc b/src/project.cc index 7f6c5c61..b4347174 100644 --- a/src/project.cc +++ b/src/project.cc @@ -412,7 +412,7 @@ void Project::Load(const std::string &root) { } Project::Entry Project::FindEntry(const std::string &path, - bool can_be_inferred) { + bool must_exist) { Project::Folder *best_folder = nullptr; const Entry *best = nullptr; std::lock_guard lock(mtx); @@ -420,11 +420,12 @@ Project::Entry Project::FindEntry(const std::string &path, 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 (!must_exist || entry.filename == path) return entry; } } + bool exists = false; std::string dir; const std::vector *extra = nullptr; Project::Entry ret; @@ -432,11 +433,12 @@ Project::Entry Project::FindEntry(const std::string &path, if (StringRef(path).startswith(root)) for (auto &[dir1, args] : folder.dot_ccls) if (StringRef(path).startswith(dir1)) { - if (AppendToCDB(args)) { - dir = dir1; - extra = &args; + dir = dir1; + extra = &args; + if (AppendToCDB(args)) goto out; - } + exists = true; + ret.root = ret.directory = root; ret.filename = path; if (args.empty()) { @@ -452,6 +454,8 @@ Project::Entry Project::FindEntry(const std::string &path, return ret; } out: + if (must_exist && !exists) + return ret; if (!best) { int best_score = INT_MIN; @@ -514,9 +518,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; @@ -529,6 +534,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 diff --git a/src/query.cc b/src/query.cc index 6bc3ae11..3b54e691 100644 --- a/src/query.cc +++ b/src/query.cc @@ -71,8 +71,7 @@ bool TryReplaceDef(llvm::SmallVectorImpl &def_list, Q &&def) { IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) { IndexUpdate r; - static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path, - ""); + static IndexFile empty(current->path, ""); if (previous) r.prev_lid2path = std::move(previous->lid2path); else diff --git a/src/sema_manager.cc b/src/sema_manager.cc index 0696414d..006c82be 100644 --- a/src/sema_manager.cc +++ b/src/sema_manager.cc @@ -290,7 +290,7 @@ public: std::unique_ptr BuildCompilerInstance( Session &session, std::unique_ptr CI, IntrusiveRefCntPtr FS, DiagnosticConsumer &DC, - const PreambleData *preamble, const std::string &path, + const PreambleData *preamble, const std::string &main, std::unique_ptr &Buf) { if (preamble) { #if LLVM_VERSION_MAJOR >= 7 @@ -299,7 +299,7 @@ std::unique_ptr BuildCompilerInstance( preamble->Preamble.AddImplicitPreamble(*CI, FS, Buf.get()); #endif } else { - CI->getPreprocessorOpts().addRemappedFile(path, Buf.get()); + CI->getPreprocessorOpts().addRemappedFile(main, Buf.get()); } auto Clang = std::make_unique(session.PCH); @@ -316,6 +316,11 @@ std::unique_ptr BuildCompilerInstance( Clang->createFileManager(); Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(), 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; } @@ -691,7 +696,7 @@ SemaManager::EnsureSession(const std::string &path, bool *created) { std::shared_ptr session = sessions.Get(path); if (!session) { session = std::make_shared( - project_->FindEntry(path, false), wfiles, PCH); + project_->FindEntry(path, true), wfiles, PCH); std::string line; if (LOG_V_ENABLED(1)) { line = "\n "; diff --git a/src/serializer.cc b/src/serializer.cc index fc2faef9..aba9342c 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -470,8 +470,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(sys::fs::UniqueID(0, 0), path, - file_content); + file = std::make_unique(path, file_content); ReflectFile(reader, *file); } catch (std::invalid_argument &e) { LOG_S(INFO) << "failed to deserialize '" << path << "': " << e.what(); @@ -494,8 +493,7 @@ Deserialize(SerializeFormat format, const std::string &path, if (reader.HasParseError()) return nullptr; - file = std::make_unique(sys::fs::UniqueID(0, 0), path, - file_content); + file = std::make_unique(path, file_content); JsonReader json_reader{&reader}; try { ReflectFile(json_reader, *file);