Index: clangd/CodeComplete.h =================================================================== --- clangd/CodeComplete.h +++ clangd/CodeComplete.h @@ -145,7 +145,8 @@ // include is used. // If we've bundled together overloads that have different sets of includes, // thse includes may not be accurate for all of them. - llvm::SmallVector Includes; + using IncludeCandidates = llvm::SmallVector; + IncludeCandidates Includes; /// Holds information about small corrections that needs to be done. Like /// converting '->' to '.' on member access. Index: clangd/CodeComplete.cpp =================================================================== --- clangd/CodeComplete.cpp +++ clangd/CodeComplete.cpp @@ -320,6 +320,25 @@ } }; +// Moves includes that do not need edits (i.e. already exist) to the front. +CodeCompletion::IncludeCandidates +moveNonInsertingIncludesToFront(CodeCompletion::IncludeCandidates Includes) { + if (Includes.size() <= 1) + return Includes; + // stable_partition on objects is slow. Do it on pointers instead. + llvm::SmallVector Pointers; + for (auto &Inc : Includes) + Pointers.push_back(&Inc); + std::stable_partition(Pointers.begin(), Pointers.end(), + [](const CodeCompletion::IncludeCandidate *I) { + return !I->Insertion.hasValue(); + }); + CodeCompletion::IncludeCandidates Results; + for (auto *Inc : Pointers) + Results.push_back(std::move(*Inc)); + return Results; +} + // Assembles a code completion out of a bundle of >=1 completion candidates. // Many of the expensive strings are only computed at this point, once we know // the candidate bundle is going to be returned. @@ -393,24 +412,21 @@ }; bool ShouldInsert = C.headerToInsertIfAllowed().hasValue(); // Calculate include paths and edits for all possible headers. + llvm::SmallVector IncludeCandidates; for (const auto &Inc : C.RankedIncludeHeaders) { if (auto ToInclude = Inserted(Inc)) { CodeCompletion::IncludeCandidate Include; Include.Header = ToInclude->first; if (ToInclude->second && ShouldInsert) Include.Insertion = Includes.insert(ToInclude->first); - Completion.Includes.push_back(std::move(Include)); + IncludeCandidates.push_back(std::move(Include)); } else log("Failed to generate include insertion edits for adding header " "(FileURI='{0}', IncludeHeader='{1}') into {2}", C.IndexResult->CanonicalDeclaration.FileURI, Inc, FileName); } - // Prefer includes that do not need edits (i.e. already exist). - std::stable_partition(Completion.Includes.begin(), - Completion.Includes.end(), - [](const CodeCompletion::IncludeCandidate &I) { - return !I.Insertion.hasValue(); - }); + Completion.Includes = + moveNonInsertingIncludesToFront(std::move(IncludeCandidates)); } void add(const CompletionCandidate &C, CodeCompletionString *SemaCCS) {