sammccall created this revision.
Herald added subscribers: cfe-commits, jkorous, MaskRay, ioeric, ilya-biryukov.
This folds together overloads items into a single CompletionItem.
It's full of hacks and breaks all the tests.
We may want to experiment with unfolding them sometimes, but this doesn't do
that yet.
Repository:
rCTE Clang Tools Extra
https://reviews.llvm.org/D47957
Files:
clangd/CodeComplete.cpp
Index: clangd/CodeComplete.cpp
===================================================================
--- clangd/CodeComplete.cpp
+++ clangd/CodeComplete.cpp
@@ -220,20 +220,56 @@
return HeaderFile{std::move(*Resolved), /*Verbatim=*/false};
}
+size_t overloadToken(const CodeCompletionResult &R) {
+ if (!R.Declaration || !R.Declaration->isFunctionOrFunctionTemplate())
+ return 0;
+ // XXX avoid string copies.
+ if (R.Declaration->isCXXClassMember())
+ return hash_combine('M',
+ StringRef(R.Declaration->getDeclName().getAsString()));
+ return hash_combine('F',
+ StringRef(R.Declaration->getQualifiedNameAsString()));
+}
+
+size_t overloadToken(const Symbol &S) {
+ switch (S.SymInfo.Kind) {
+ case index::SymbolKind::ClassMethod:
+ case index::SymbolKind::InstanceMethod:
+ case index::SymbolKind::StaticMethod:
+ return hash_combine('M', S.Name);
+ case index::SymbolKind::Function:
+ return hash_combine('F', StringRef((S.Scope + S.Name).str()));
+ default:
+ return 0;
+ }
+}
+
/// A code completion result, in clang-native form.
/// It may be promoted to a CompletionItem if it's among the top-ranked results.
struct CompletionCandidate {
llvm::StringRef Name; // Used for filtering and sorting.
// We may have a result from Sema, from the index, or both.
const CodeCompletionResult *SemaResult = nullptr;
const Symbol *IndexResult = nullptr;
+ // Returns a token identifying the overload set this is part of.
+ // 0 indicates it's not part of any overload set.
+ size_t overload() const {
+ if (SemaResult && IndexResult)
+ assert(overloadToken(*SemaResult) == overloadToken(*IndexResult));
+ if (IndexResult)
+ return overloadToken(*IndexResult);
+ if (SemaResult)
+ return overloadToken(*SemaResult);
+ return 0;
+ }
+
// Builds an LSP completion item.
CompletionItem build(StringRef FileName, const CompletionItemScores &Scores,
const CodeCompleteOptions &Opts,
CodeCompletionString *SemaCCS,
const IncludeInserter *Includes,
- llvm::StringRef SemaDocComment) const {
+ llvm::StringRef SemaDocComment, unsigned Bundle) const {
assert(bool(SemaResult) == bool(SemaCCS));
CompletionItem I;
bool ShouldInsertInclude = true;
@@ -309,10 +345,27 @@
I.sortText = sortText(Scores.finalScore, Name);
I.insertTextFormat = Opts.EnableSnippets ? InsertTextFormat::Snippet
: InsertTextFormat::PlainText;
+ if (Bundle) {
+ I.detail = "[overloaded]";
+ I.insertText = Opts.EnableSnippets ? (Name + "(${0})").str() : Name.str();
+ I.label = (Name + "(...)").str();
+ }
return I;
}
};
-using ScoredCandidate = std::pair<CompletionCandidate, CompletionItemScores>;
+struct CandidateBundle {
+ SmallVector<CompletionCandidate, 4> Candidates;
+ // Builds an LSP completion item.
+ CompletionItem build(StringRef FileName, const CompletionItemScores &Scores,
+ const CodeCompleteOptions &Opts,
+ CodeCompletionString *SemaCCS,
+ const IncludeInserter *Includes,
+ llvm::StringRef SemaDocComment) const {
+ return Candidates.front().build(FileName, Scores, Opts, SemaCCS, Includes,
+ SemaDocComment, Candidates.size() > 1);
+ }
+};
+using ScoredBundle = std::pair<CandidateBundle, CompletionItemScores>;
// Determine the symbol ID for a Sema code completion result, if possible.
llvm::Optional<SymbolID> getSymbolID(const CodeCompletionResult &R) {
@@ -589,10 +642,10 @@
};
struct ScoredCandidateGreater {
- bool operator()(const ScoredCandidate &L, const ScoredCandidate &R) {
+ bool operator()(const ScoredBundle &L, const ScoredBundle &R) {
if (L.second.finalScore != R.second.finalScore)
return L.second.finalScore > R.second.finalScore;
- return L.first.Name < R.first.Name; // Earlier name is better.
+ return L.first.Candidates.front().Name < R.first.Candidates.front().Name; // Earlier name is better.
}
};
@@ -984,13 +1037,30 @@
// Merges the Sema and Index results where possible, scores them, and
// returns the top results from best to worst.
- std::vector<std::pair<CompletionCandidate, CompletionItemScores>>
+ std::vector<std::pair<CandidateBundle, CompletionItemScores>>
mergeResults(const std::vector<CodeCompletionResult> &SemaResults,
const SymbolSlab &IndexResults) {
trace::Span Tracer("Merge and score results");
- // We only keep the best N results at any time, in "native" format.
- TopN<ScoredCandidate, ScoredCandidateGreater> Top(
- Opts.Limit == 0 ? std::numeric_limits<size_t>::max() : Opts.Limit);
+ // Candidates are grouped into overload bundles.
+ std::vector<CandidateBundle> Bundles;
+ llvm::DenseMap<size_t, size_t> BundleLookup;
+ auto AddToBundles = [&](const CodeCompletionResult *SemaResult,
+ const Symbol *IndexResult) {
+ CompletionCandidate C;
+ C.SemaResult = SemaResult;
+ C.IndexResult = IndexResult;
+ C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult);
+ auto Overload = C.overload();
+ if (Overload) {
+ auto Ret = BundleLookup.try_emplace(C.overload(), Bundles.size());
+ if (Ret.second)
+ Bundles.emplace_back();
+ Bundles[Ret.first->second].Candidates.push_back(std::move(C));
+ } else {
+ Bundles.emplace_back();
+ Bundles.back().Candidates.push_back(std::move(C));
+ }
+ };
llvm::DenseSet<const Symbol *> UsedIndexResults;
auto CorrespondingIndexResult =
[&](const CodeCompletionResult &SemaResult) -> const Symbol * {
@@ -1005,13 +1075,18 @@
};
// Emit all Sema results, merging them with Index results if possible.
for (auto &SemaResult : Recorder->Results)
- addCandidate(Top, &SemaResult, CorrespondingIndexResult(SemaResult));
+ AddToBundles(&SemaResult, CorrespondingIndexResult(SemaResult));
// Now emit any Index-only results.
for (const auto &IndexResult : IndexResults) {
if (UsedIndexResults.count(&IndexResult))
continue;
- addCandidate(Top, /*SemaResult=*/nullptr, &IndexResult);
+ AddToBundles(/*SemaResult=*/nullptr, &IndexResult);
}
+ // We only keep the best N results at any time, in "native" format.
+ TopN<ScoredBundle, ScoredCandidateGreater> Top(
+ Opts.Limit == 0 ? std::numeric_limits<size_t>::max() : Opts.Limit);
+ for (auto &Bundle : Bundles)
+ addCandidate(Top, std::move(Bundle));
return std::move(Top).items();
}
@@ -1025,28 +1100,28 @@
}
// Scores a candidate and adds it to the TopN structure.
- void addCandidate(TopN<ScoredCandidate, ScoredCandidateGreater> &Candidates,
- const CodeCompletionResult *SemaResult,
- const Symbol *IndexResult) {
- CompletionCandidate C;
- C.SemaResult = SemaResult;
- C.IndexResult = IndexResult;
- C.Name = IndexResult ? IndexResult->Name : Recorder->getName(*SemaResult);
-
+ void addCandidate(TopN<ScoredBundle, ScoredCandidateGreater> &Candidates,
+ CandidateBundle Bundle) {
SymbolQualitySignals Quality;
SymbolRelevanceSignals Relevance;
Relevance.Query = SymbolRelevanceSignals::CodeComplete;
- if (auto FuzzyScore = fuzzyScore(C))
+ auto First = Bundle.Candidates.front();
+ if (auto FuzzyScore = fuzzyScore(First))
Relevance.NameMatch = *FuzzyScore;
else
return;
- if (IndexResult) {
- Quality.merge(*IndexResult);
- Relevance.merge(*IndexResult);
- }
- if (SemaResult) {
- Quality.merge(*SemaResult);
- Relevance.merge(*SemaResult);
+ unsigned SemaResult = 0, IndexResult = 0;
+ for (const auto &Candidate : Bundle.Candidates) {
+ if (Candidate.IndexResult) {
+ Quality.merge(*Candidate.IndexResult);
+ Relevance.merge(*Candidate.IndexResult);
+ ++IndexResult;
+ }
+ if (Candidate.SemaResult) {
+ Quality.merge(*Candidate.SemaResult);
+ Relevance.merge(*Candidate.SemaResult);
+ ++SemaResult;
+ }
}
float QualScore = Quality.evaluate(), RelScore = Relevance.evaluate();
@@ -1059,24 +1134,24 @@
Scores.symbolScore =
Scores.filterScore ? Scores.finalScore / Scores.filterScore : QualScore;
- LLVM_DEBUG(llvm::dbgs()
- << "CodeComplete: " << C.Name << (IndexResult ? " (index)" : "")
- << (SemaResult ? " (sema)" : "") << " = " << Scores.finalScore
- << "\n"
- << Quality << Relevance << "\n");
+ LLVM_DEBUG(llvm::dbgs() << "CodeComplete: " << First.Name << "("
+ << IndexResult << " index) "
+ << "(" << SemaResult << " sema)"
+ << " = " << Scores.finalScore << "\n"
+ << Quality << Relevance << "\n");
NSema += bool(SemaResult);
NIndex += bool(IndexResult);
NBoth += SemaResult && IndexResult;
- if (Candidates.push({C, Scores}))
+ if (Candidates.push({std::move(Bundle), Scores}))
Incomplete = true;
}
- CompletionItem toCompletionItem(const CompletionCandidate &Candidate,
+ CompletionItem toCompletionItem(const CandidateBundle &Candidate,
const CompletionItemScores &Scores) {
CodeCompletionString *SemaCCS = nullptr;
std::string DocComment;
- if (auto *SR = Candidate.SemaResult) {
+ if (auto *SR = Candidate.Candidates.front().SemaResult) {
SemaCCS = Recorder->codeCompletionString(*SR);
if (Opts.IncludeComments) {
assert(Recorder->CCSema);
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits