diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -156,6 +156,10 @@ /// Enable preview of FoldingRanges feature. bool FoldingRanges = false; + // Automatically parse the STL to enable code completion and include + // insertion + bool IndexSTL = false; + explicit operator TUScheduler::Options() const; }; // Sensible default options for use in tests. diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -30,6 +30,7 @@ #include "support/Logger.h" #include "support/Markup.h" #include "support/MemoryTree.h" +#include "support/Path.h" #include "support/ThreadsafeFS.h" #include "support/Trace.h" #include "clang/Format/Format.h" @@ -53,9 +54,7 @@ #include #include #include -#include #include -#include #include namespace clang { @@ -179,6 +178,7 @@ if (Opts.BackgroundIndex) { BackgroundIndex::Options BGOpts; BGOpts.ThreadPoolSize = std::max(Opts.AsyncThreadsCount, 1u); + BGOpts.IndexSTL = Opts.IndexSTL; BGOpts.OnProgress = [Callbacks](BackgroundQueue::Stats S) { if (Callbacks) Callbacks->onBackgroundIndexProgress(S); @@ -219,6 +219,8 @@ // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. if (NewFile && BackgroundIdx) BackgroundIdx->boostRelated(File); + if (BackgroundIdx) + BackgroundIdx->indexSTLHeaders({File.str()}); } void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } diff --git a/clang-tools-extra/clangd/index/Background.h b/clang-tools-extra/clangd/index/Background.h --- a/clang-tools-extra/clangd/index/Background.h +++ b/clang-tools-extra/clangd/index/Background.h @@ -141,6 +141,7 @@ std::function ContextProvider = nullptr; // Whether to collect references to main-file-only symbols. bool CollectMainFileRefs = false; + bool IndexSTL = false; }; /// Creates a new background index and starts its threads. @@ -155,6 +156,8 @@ // available sometime later. void enqueue(const std::vector &ChangedFiles) { Queue.push(changedFilesTask(ChangedFiles)); + // TODO(kuhnel): Only index if enqueueing C++ file. + indexSTLHeaders(ChangedFiles); } /// Boosts priority of indexing related to Path. @@ -176,6 +179,8 @@ void profile(MemoryTree &MT) const; + void indexSTLHeaders(const std::vector &ChangedFiles); + private: /// Represents the state of a single file when indexing was performed. struct ShardVersion { @@ -220,6 +225,8 @@ BackgroundQueue Queue; AsyncTaskRunner ThreadPool; GlobalCompilationDatabase::CommandChanged::Subscription CommandsChanged; + // Whether the STL header files need to be indexed + bool NeedToIndexSTLHeaders = true; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/index/Background.cpp b/clang-tools-extra/clangd/index/Background.cpp --- a/clang-tools-extra/clangd/index/Background.cpp +++ b/clang-tools-extra/clangd/index/Background.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -103,7 +104,8 @@ CommandsChanged( CDB.watch([&](const std::vector &ChangedFiles) { enqueue(ChangedFiles); - })) { + })), + NeedToIndexSTLHeaders(Opts.IndexSTL) { assert(Opts.ThreadPoolSize > 0 && "Thread pool size can't be zero."); assert(this->IndexStorageFactory && "Storage factory can not be null!"); for (unsigned I = 0; I < Opts.ThreadPoolSize; ++I) { @@ -420,5 +422,56 @@ // We don't want to mix memory used by index and symbols, so call base class. MT.child("index").addUsage(SwapIndex::estimateMemoryUsage()); } + +void BackgroundIndex::indexSTLHeaders( + const std::vector &ChangedFiles) { + // Only index if we need to. + if (!NeedToIndexSTLHeaders) + return; + + vlog("Trying to find place for STL header include."); + std::string SourceRoot; + for (auto ChangedFile : ChangedFiles) { + if (CDB.getProjectInfo(ChangedFile)) { + SourceRoot = CDB.getProjectInfo(ChangedFile)->SourceRoot; + break; + } + } + // fallback if we can't find a Compilations Database + if (SourceRoot.empty()) { + if (ChangedFiles.empty()) { + vlog("Could not find place for STL header include."); + return; + } + SourceRoot = llvm::sys::path::parent_path(ChangedFiles[0]).str(); + } + // TODO(kuhnel): is the a better place to store this file? + // TODO(kuhnel): do we need a file at all, can we just pass a string to the + // indexer? + const Path STLHeaderPath = SourceRoot + "/.stl_header.h"; + vlog("Indexing STL headers from {0}", STLHeaderPath); + std::ofstream STLIndexFile; + std::set Headers; + +// gather all the known STL headers, without duplicates +#define SYMBOL(Name, NameSpace, Header) Headers.insert(#Header); +#include "StdSymbolMap.inc" +#undef SYMBOL + + // TODO(kuhnel): figure out if there are other ways to write to files in llvm. + STLIndexFile.open(STLHeaderPath); + STLIndexFile << "/* This is a temporary file created by clangd. It is " + "used by the background index to automatically index the " + "standard template library. */" + << std::endl + << std::endl; + for (auto Header : Headers) { + STLIndexFile << "#include " << Header << std::endl; + } + STLIndexFile.close(); + Queue.push(changedFilesTask({STLHeaderPath})); + NeedToIndexSTLHeaders = false; +}; + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -524,6 +524,15 @@ std::function getMemoryCleanupFunction() { return nullptr; } #endif +opt IndexSTL{ + "index-stl", + cat(Features), + desc("Background index should always include the STL headers even if \n" + "not used at the moment. This will enable code completion and \n" + "include fixer."), + init(true), +}; + #if CLANGD_ENABLE_REMOTE opt RemoteIndexAddress{ "remote-index-address", @@ -897,6 +906,7 @@ // Shall we allow to customize the file limit? Opts.Rename.AllowCrossFile = CrossFileRename; + Opts.IndexSTL = IndexSTL; if (CheckFile.getNumOccurrences()) { llvm::SmallString<256> Path;