diff --git a/src/clang_complete.cc b/src/clang_complete.cc index 9f9b671f..f6a84820 100644 --- a/src/clang_complete.cc +++ b/src/clang_complete.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "clang_utils.h" #include "filesystem.hh" @@ -525,7 +525,7 @@ bool Parse(CompilerInstance &Clang) { return true; } -void CompletionPreloadMain(ClangCompleteManager *completion_manager) { +void CompletionPreloadMain(CompletionManager *completion_manager) { while (true) { // Fetching the completion request blocks until we have a request. auto request = completion_manager->preload_requests_.Dequeue(); @@ -550,10 +550,10 @@ void CompletionPreloadMain(ClangCompleteManager *completion_manager) { } } -void CompletionMain(ClangCompleteManager *completion_manager) { +void CompletionMain(CompletionManager *completion_manager) { while (true) { // Fetching the completion request blocks until we have a request. - std::unique_ptr request = + std::unique_ptr request = completion_manager->completion_request_.Dequeue(); // Drop older requests if we're not buffering. @@ -606,10 +606,10 @@ void CompletionMain(ClangCompleteManager *completion_manager) { } } -void DiagnosticMain(ClangCompleteManager *manager) { +void DiagnosticMain(CompletionManager *manager) { while (true) { // Fetching the completion request blocks until we have a request. - ClangCompleteManager::DiagnosticRequest request = + CompletionManager::DiagnosticRequest request = manager->diagnostic_request_.Dequeue(); std::string path = request.document.uri.GetPath(); @@ -699,10 +699,10 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) { } // namespace ccls -ClangCompleteManager::ClangCompleteManager(Project *project, - WorkingFiles *working_files, - OnDiagnostic on_diagnostic, - OnDropped on_dropped) +CompletionManager::CompletionManager(Project *project, + WorkingFiles *working_files, + OnDiagnostic on_diagnostic, + OnDropped on_dropped) : project_(project), working_files_(working_files), on_diagnostic_(on_diagnostic), on_dropped_(on_dropped), preloaded_sessions_(kMaxPreloadedSessions), @@ -725,7 +725,7 @@ ClangCompleteManager::ClangCompleteManager(Project *project, .detach(); } -void ClangCompleteManager::CodeComplete( +void CompletionManager::CodeComplete( const lsRequestId &id, const lsTextDocumentPositionParams &completion_location, const OnComplete &on_complete) { @@ -734,7 +734,7 @@ void ClangCompleteManager::CodeComplete( on_complete)); } -void ClangCompleteManager::DiagnosticsUpdate( +void CompletionManager::DiagnosticsUpdate( const lsTextDocumentIdentifier &document) { bool has = false; diagnostic_request_.Iterate([&](const DiagnosticRequest &request) { @@ -746,29 +746,13 @@ void ClangCompleteManager::DiagnosticsUpdate( true /*priority*/); } -void ClangCompleteManager::NotifyView(const std::string &filename) { - // - // On view, we reparse only if the file has not been parsed. The existence of - // a CompletionSession instance implies the file is already parsed or will be - // parsed soon. - // - +void CompletionManager::NotifyView(const std::string &path) { // Only reparse the file if we create a new CompletionSession. - if (EnsureCompletionOrCreatePreloadSession(filename)) - preload_requests_.PushBack(PreloadRequest(filename), true); + if (EnsureCompletionOrCreatePreloadSession(path)) + preload_requests_.PushBack(PreloadRequest(path), true); } -void ClangCompleteManager::NotifyEdit(const std::string &filename) { - // - // We treat an edit like a view, because the completion logic will handle - // moving the CompletionSession instance from preloaded to completion - // storage. - // - - NotifyView(filename); -} - -void ClangCompleteManager::NotifySave(const std::string &filename) { +void CompletionManager::NotifySave(const std::string &filename) { // // On save, always reparse. // @@ -777,7 +761,7 @@ void ClangCompleteManager::NotifySave(const std::string &filename) { preload_requests_.PushBack(PreloadRequest(filename), true); } -void ClangCompleteManager::NotifyClose(const std::string &filename) { +void CompletionManager::NotifyClose(const std::string &filename) { // // On close, we clear any existing CompletionSession instance. // @@ -797,63 +781,63 @@ void ClangCompleteManager::NotifyClose(const std::string &filename) { assert((preloaded_ptr && completion_ptr) == false); } -bool ClangCompleteManager::EnsureCompletionOrCreatePreloadSession( - const std::string &filename) { +bool CompletionManager::EnsureCompletionOrCreatePreloadSession( + const std::string &path) { std::lock_guard lock(sessions_lock_); // Check for an existing CompletionSession. - if (preloaded_sessions_.TryGet(filename) || - completion_sessions_.TryGet(filename)) { + if (preloaded_sessions_.TryGet(path) || + completion_sessions_.TryGet(path)) { return false; } // No CompletionSession, create new one. auto session = std::make_shared( - project_->FindCompilationEntryForFile(filename), working_files_, PCH); + project_->FindCompilationEntryForFile(path), working_files_, PCH); preloaded_sessions_.Insert(session->file.filename, session); return true; } std::shared_ptr -ClangCompleteManager::TryGetSession(const std::string &filename, - bool mark_as_completion, - bool create_if_needed) { +CompletionManager::TryGetSession(const std::string &path, + bool mark_as_completion, + bool create_if_needed) { std::lock_guard lock(sessions_lock_); // Try to find a preloaded session. std::shared_ptr preloaded = - preloaded_sessions_.TryGet(filename); + preloaded_sessions_.TryGet(path); if (preloaded) { // If this request is for a completion, we should move it to // |completion_sessions|. if (mark_as_completion) { - preloaded_sessions_.TryTake(filename); - completion_sessions_.Insert(filename, preloaded); + preloaded_sessions_.TryTake(path); + completion_sessions_.Insert(path, preloaded); } return preloaded; } // Try to find a completion session. If none create one. std::shared_ptr session = - completion_sessions_.TryGet(filename); + completion_sessions_.TryGet(path); if (!session && create_if_needed) { session = std::make_shared( - project_->FindCompilationEntryForFile(filename), working_files_, PCH); - completion_sessions_.Insert(filename, session); + project_->FindCompilationEntryForFile(path), working_files_, PCH); + completion_sessions_.Insert(path, session); } return session; } -void ClangCompleteManager::FlushSession(const std::string &filename) { +void CompletionManager::FlushSession(const std::string &path) { std::lock_guard lock(sessions_lock_); - preloaded_sessions_.TryTake(filename); - completion_sessions_.TryTake(filename); + preloaded_sessions_.TryTake(path); + completion_sessions_.TryTake(path); } -void ClangCompleteManager::FlushAllSessions() { +void CompletionManager::FlushAllSessions() { LOG_S(INFO) << "flush all clang complete sessions"; std::lock_guard lock(sessions_lock_); diff --git a/src/clang_complete.h b/src/clang_complete.hh similarity index 91% rename from src/clang_complete.h rename to src/clang_complete.hh index 92716e41..94e9fc36 100644 --- a/src/clang_complete.h +++ b/src/clang_complete.hh @@ -76,7 +76,7 @@ struct CompletionSession }; } -struct ClangCompleteManager { +struct CompletionManager { using OnDiagnostic = std::function diagnostics)>; using OnComplete = std::function - TryGetSession(const std::string &filename, bool mark_as_completion, + TryGetSession(const std::string &path, bool mark_as_completion, bool create_if_needed); // Flushes all saved sessions with the supplied filename - void FlushSession(const std::string &filename); + void FlushSession(const std::string &path); // Flushes all saved sessions void FlushAllSessions(void); diff --git a/src/config.h b/src/config.h index ae234f72..565c671c 100644 --- a/src/config.h +++ b/src/config.h @@ -154,6 +154,8 @@ struct Config { // If true, diagnostics will be reported for textDocument/didOpen. bool onOpen = true; + bool spellChecking = true; + std::vector whitelist; } diagnostics; @@ -238,7 +240,7 @@ MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, dropOldRequests, includeMaxPathSize, includeSuffixWhitelist, includeWhitelist); MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange, - onOpen, whitelist) + onOpen, spellChecking, whitelist) MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist) MAKE_REFLECT_STRUCT(Config::Index, blacklist, comments, enabled, multiVersion, multiVersionBlacklist, multiVersionWhitelist, onChange, diff --git a/src/indexer.cc b/src/indexer.cc index 567dcf50..55586691 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -15,12 +15,13 @@ limitations under the License. #include "indexer.h" +#include "clang_complete.hh" #include "clang_tu.h" #include "log.hh" #include "match.h" #include "platform.h" #include "serializer.h" -using ccls::Intern; +using namespace ccls; #include #include @@ -1195,7 +1196,8 @@ void Init() { } std::vector> -Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, +Index(CompletionManager *completion, WorkingFiles *wfiles, VFS *vfs, + const std::string &opt_wdir, const std::string &file, const std::vector &args, const std::vector> &remapped) { if (!g_config->index.enabled) @@ -1221,13 +1223,32 @@ Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, // HSOpts.UseBuiltinIncludes) // HSOpts.ResourceDir = g_config->clang.resourceDir; } + std::string buf = wfiles->GetContent(file); std::vector> Bufs; - for (auto &[filename, content] : remapped) { - Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); - CI->getPreprocessorOpts().addRemappedFile( - filename == file ? CI->getFrontendOpts().Inputs[0].getFile() - : StringRef(filename), - Bufs.back().get()); + if (buf.size()) { + // If there is a completion session, reuse its preamble if exists. + bool done_remap = false; + std::shared_ptr session = + completion->TryGetSession(file, false, false); + if (session) + if (auto preamble = session->GetPreamble()) { + Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(buf)); + auto Bounds = ComputePreambleBounds(*CI->getLangOpts(), Bufs.back().get(), 0); + if (preamble->Preamble.CanReuse(*CI, Bufs.back().get(), Bounds, + FS.get())) { + preamble->Preamble.AddImplicitPreamble(*CI, FS, Bufs.back().get()); + done_remap = true; + } + } + for (auto &[filename, content] : remapped) { + if (filename == file && done_remap) + continue; + Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); + CI->getPreprocessorOpts().addRemappedFile( + filename == file ? CI->getFrontendOpts().Inputs[0].getFile() + : StringRef(filename), + Bufs.back().get()); + } } DiagnosticConsumer DC; diff --git a/src/indexer.h b/src/indexer.h index 797b0096..ab20ba30 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -284,10 +284,14 @@ struct IndexFile { std::string ToString(); }; +struct CompletionManager; +struct WorkingFiles; + namespace ccls::idx { void Init(); std::vector> -Index(VFS *vfs, const std::string &opt_wdir, const std::string &file, +Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, + const std::string &opt_wdir, const std::string &file, const std::vector &args, - const std::vector>& remapped); -} // namespace ccls::idx + const std::vector> &remapped); +} diff --git a/src/message_handler.h b/src/message_handler.h index 99da9d30..3d314222 100644 --- a/src/message_handler.h +++ b/src/message_handler.h @@ -26,7 +26,7 @@ limitations under the License. #include #include -struct ClangCompleteManager; +struct CompletionManager; struct CodeCompleteCache; struct Config; class DiagnosticsPublisher; @@ -119,7 +119,7 @@ struct MessageHandler { ImportManager *import_manager = nullptr; SemanticHighlightSymbolCache *semantic_cache = nullptr; WorkingFiles *working_files = nullptr; - ClangCompleteManager *clang_complete = nullptr; + CompletionManager *clang_complete = nullptr; IncludeComplete *include_complete = nullptr; CodeCompleteCache *global_code_complete_cache = nullptr; CodeCompleteCache *non_global_code_complete_cache = nullptr; diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index fdbfa349..332f8c5b 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +#include "clang_complete.hh" #include "filesystem.hh" #include "include_complete.h" #include "log.hh" @@ -495,7 +496,8 @@ struct Handler_Initialize : BaseMessageHandler { g_thread_id = i + 1; std::string name = "indexer" + std::to_string(i); set_thread_name(name.c_str()); - pipeline::Indexer_Main(diag_pub, vfs, project, working_files); + pipeline::Indexer_Main(clang_complete, diag_pub, vfs, project, + working_files); }) .detach(); } diff --git a/src/messages/textDocument_codeLens.cc b/src/messages/textDocument_codeLens.cc index 730441e7..ccf235fd 100644 --- a/src/messages/textDocument_codeLens.cc +++ b/src/messages/textDocument_codeLens.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "lsp_code_action.h" #include "message_handler.h" #include "pipeline.hh" diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 9582b578..78383969 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "fuzzy_match.h" #include "include_complete.h" #include "message_handler.h" @@ -380,7 +380,7 @@ struct Handler_TextDocumentCompletion : MessageHandler { pipeline::WriteStdout(kMethodType, out); } else { - ClangCompleteManager::OnComplete callback = std::bind( + CompletionManager::OnComplete callback = std::bind( [this, request, params, is_global_completion, existing_completion, has_open_paren](const std::vector &results, bool is_cached_result) { @@ -420,7 +420,7 @@ struct Handler_TextDocumentCompletion : MessageHandler { !global_code_complete_cache->cached_results_.empty(); }); if (is_cache_match) { - ClangCompleteManager::OnComplete freshen_global = + CompletionManager::OnComplete freshen_global = [this](std::vector results, bool is_cached_result) { assert(!is_cached_result); diff --git a/src/messages/textDocument_didChange.cc b/src/messages/textDocument_didChange.cc index f010ae04..5b50714c 100644 --- a/src/messages/textDocument_didChange.cc +++ b/src/messages/textDocument_didChange.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" #include "project.h" diff --git a/src/messages/textDocument_didClose.cc b/src/messages/textDocument_didClose.cc index 8187ee7d..8e3f504f 100644 --- a/src/messages/textDocument_didClose.cc +++ b/src/messages/textDocument_didClose.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" #include "working_files.h" diff --git a/src/messages/textDocument_didOpen.cc b/src/messages/textDocument_didOpen.cc index 2dc25c01..146c22c0 100644 --- a/src/messages/textDocument_didOpen.cc +++ b/src/messages/textDocument_didOpen.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "include_complete.h" #include "message_handler.h" #include "pipeline.hh" diff --git a/src/messages/textDocument_didSave.cc b/src/messages/textDocument_didSave.cc index 023d1bb3..0badb21c 100644 --- a/src/messages/textDocument_didSave.cc +++ b/src/messages/textDocument_didSave.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" #include "project.h" diff --git a/src/messages/textDocument_signatureHelp.cc b/src/messages/textDocument_signatureHelp.cc index 8799a542..1a763e21 100644 --- a/src/messages/textDocument_signatureHelp.cc +++ b/src/messages/textDocument_signatureHelp.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" using namespace ccls; @@ -114,7 +114,7 @@ struct Handler_TextDocumentSignatureHelp : MessageHandler { if (search.empty()) return; - ClangCompleteManager::OnComplete callback = std::bind( + CompletionManager::OnComplete callback = std::bind( [this](InMessage *message, std::string search, int active_param, const std::vector &results, bool is_cached_result) { diff --git a/src/messages/workspace_didChangeConfiguration.cc b/src/messages/workspace_didChangeConfiguration.cc index eb5b2d88..9b9ba474 100644 --- a/src/messages/workspace_didChangeConfiguration.cc +++ b/src/messages/workspace_didChangeConfiguration.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" #include "project.h" diff --git a/src/messages/workspace_didChangeWatchedFiles.cc b/src/messages/workspace_didChangeWatchedFiles.cc index b2c3eef5..5d872859 100644 --- a/src/messages/workspace_didChangeWatchedFiles.cc +++ b/src/messages/workspace_didChangeWatchedFiles.cc @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ -#include "clang_complete.h" +#include "clang_complete.hh" #include "message_handler.h" #include "pipeline.hh" #include "project.h" diff --git a/src/pipeline.cc b/src/pipeline.cc index c973f564..ffab9e29 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -15,7 +15,7 @@ limitations under the License. #include "pipeline.hh" -#include "clang_complete.h" +#include "clang_complete.hh" #include "config.h" #include "include_complete.h" #include "log.hh" @@ -165,7 +165,8 @@ std::unique_ptr RawCacheLoad(const std::string &path) { IndexFile::kMajorVersion); } -bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, +bool Indexer_Parse(CompletionManager *completion, + DiagnosticsPublisher *diag_pub, WorkingFiles *wfiles, Project *project, VFS *vfs, const GroupMatch &matcher) { std::optional opt_request = index_request->TryPopFront(); if (!opt_request) @@ -259,12 +260,12 @@ bool Indexer_Parse(DiagnosticsPublisher *diag_pub, WorkingFiles *working_files, std::vector> remapped; if (g_config->index.onChange) { - std::string content = working_files->GetContent(request.path); + std::string content = wfiles->GetContent(request.path); if (content.size()) remapped.emplace_back(request.path, content); } - auto indexes = - idx::Index(vfs, entry.directory, path_to_index, entry.args, remapped); + auto indexes = idx::Index(completion, wfiles, vfs, entry.directory, + path_to_index, entry.args, remapped); if (indexes.empty()) { if (g_config->index.enabled && request.id.Valid()) { @@ -348,11 +349,13 @@ void Init() { for_stdout = new ThreadedQueue(stdout_waiter); } -void Indexer_Main(DiagnosticsPublisher *diag_pub, VFS *vfs, Project *project, +void Indexer_Main(CompletionManager *completion, + DiagnosticsPublisher *diag_pub, VFS *vfs, Project *project, WorkingFiles *working_files) { GroupMatch matcher(g_config->index.whitelist, g_config->index.blacklist); while (true) - if (!Indexer_Parse(diag_pub, working_files, project, vfs, matcher)) + if (!Indexer_Parse(completion, diag_pub, working_files, project, vfs, + matcher)) indexer_waiter->Wait(index_request); } @@ -464,7 +467,7 @@ void MainLoop() { VFS vfs; DiagnosticsPublisher diag_pub; - ClangCompleteManager clang_complete( + CompletionManager clang_complete( &project, &working_files, [&](std::string path, std::vector diagnostics) { diag_pub.Publish(&working_files, path, diagnostics); diff --git a/src/pipeline.hh b/src/pipeline.hh index 3c49285c..da9bfc9e 100644 --- a/src/pipeline.hh +++ b/src/pipeline.hh @@ -8,6 +8,7 @@ #include #include +struct CompletionManager; struct GroupMatch; struct VFS; struct Project; @@ -37,10 +38,9 @@ namespace pipeline { void Init(); void LaunchStdin(); void LaunchStdout(); -void Indexer_Main(DiagnosticsPublisher* diag_pub, - VFS* vfs, - Project* project, - WorkingFiles* working_files); +void Indexer_Main(CompletionManager *complete, + DiagnosticsPublisher *diag_pub, VFS *vfs, Project *project, + WorkingFiles *working_files); void MainLoop(); void Index(const std::string &path, const std::vector &args, diff --git a/src/test.cc b/src/test.cc index b93a21b9..19d52a9e 100644 --- a/src/test.cc +++ b/src/test.cc @@ -15,6 +15,7 @@ limitations under the License. #include "test.h" +#include "clang_complete.hh" #include "filesystem.hh" #include "indexer.h" #include "platform.h" @@ -301,7 +302,11 @@ bool RunIndexTests(const std::string &filter_path, bool enable_update) { // Run test. g_config = new Config; VFS vfs; - auto dbs = ccls::idx::Index(&vfs, "", path, flags, {}); + CompletionManager completion( + nullptr, nullptr, [&](std::string, std::vector) {}, + [](lsRequestId id) {}); + WorkingFiles wfiles; + auto dbs = ccls::idx::Index(&completion, &wfiles, &vfs, "", path, flags, {}); for (const auto &entry : all_expected_output) { const std::string &expected_path = entry.first;