1818#include " clang/Index/IndexDataConsumer.h"
1919#include " clang/Index/IndexSymbol.h"
2020#include " clang/Index/IndexingAction.h"
21+ #include " llvm/ADT/ArrayRef.h"
22+ #include " llvm/ADT/STLExtras.h"
23+ #include " llvm/ADT/SmallVector.h"
2124#include " llvm/ADT/StringRef.h"
2225#include " llvm/Support/FormatVariadic.h"
2326#include " llvm/Support/Path.h"
2427#include " llvm/Support/ScopedPrinter.h"
28+ #include < tuple>
2529
2630#define DEBUG_TYPE " FindSymbols"
2731
@@ -38,6 +42,21 @@ struct ScoredSymbolGreater {
3842 }
3943};
4044
45+ // Returns true if \p Query can be found as a sub-sequence inside \p Scope.
46+ bool approximateScopeMatch (llvm::StringRef Scope, llvm::StringRef Query) {
47+ assert (Scope.empty () || Scope.endswith (" ::" ));
48+ assert (Query.empty () || Query.endswith (" ::" ));
49+ while (!Scope.empty () && !Query.empty ()) {
50+ auto Colons = Scope.find (" ::" );
51+ assert (Colons != llvm::StringRef::npos);
52+
53+ llvm::StringRef LeadingSpecifier = Scope.slice (0 , Colons + 2 );
54+ Scope = Scope.slice (Colons + 2 , llvm::StringRef::npos);
55+ Query.consume_front (LeadingSpecifier);
56+ }
57+ return Query.empty ();
58+ }
59+
4160} // namespace
4261
4362llvm::Expected<Location> indexToLSPLocation (const SymbolLocation &Loc,
@@ -71,44 +90,54 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
7190 if (Query.empty () || !Index)
7291 return Result;
7392
93+ // Lookup for qualified names are performed as:
94+ // - Exact namespaces are boosted by the index.
95+ // - Approximate matches are (sub-scope match) included via AnyScope logic.
96+ // - Non-matching namespaces (no sub-scope match) are post-filtered.
7497 auto Names = splitQualifiedName (Query);
7598
7699 FuzzyFindRequest Req;
77100 Req.Query = std::string (Names.second );
78101
79- // FuzzyFind doesn't want leading :: qualifier
80- bool IsGlobalQuery = Names.first .consume_front (" ::" );
81- // Restrict results to the scope in the query string if present (global or
82- // not).
83- if (IsGlobalQuery || !Names.first .empty ())
102+ // FuzzyFind doesn't want leading :: qualifier.
103+ auto HasLeadingColons = Names.first .consume_front (" ::" );
104+ // Limit the query to specific namespace if it is fully-qualified.
105+ Req.AnyScope = !HasLeadingColons;
106+ // Boost symbols from desired namespace.
107+ if (HasLeadingColons || !Names.first .empty ())
84108 Req.Scopes = {std::string (Names.first )};
85- else
86- Req.AnyScope = true ;
87- if (Limit)
109+ if (Limit) {
88110 Req.Limit = Limit;
111+ // If we are boosting a specific scope allow more results to be retrieved,
112+ // since some symbols from preferred namespaces might not make the cut.
113+ if (Req.AnyScope && !Req.Scopes .empty ())
114+ *Req.Limit *= 5 ;
115+ }
89116 TopN<ScoredSymbolInfo, ScoredSymbolGreater> Top (
90117 Req.Limit ? *Req.Limit : std::numeric_limits<size_t >::max ());
91118 FuzzyMatcher Filter (Req.Query );
92- Index->fuzzyFind (Req, [HintPath, &Top, &Filter](const Symbol &Sym) {
119+
120+ Index->fuzzyFind (Req, [HintPath, &Top, &Filter, AnyScope = Req.AnyScope ,
121+ ReqScope = Names.first ](const Symbol &Sym) {
122+ llvm::StringRef Scope = Sym.Scope ;
123+ // Fuzzyfind might return symbols from irrelevant namespaces if query was
124+ // not fully-qualified, drop those.
125+ if (AnyScope && !approximateScopeMatch (Scope, ReqScope))
126+ return ;
127+
93128 auto Loc = symbolToLocation (Sym, HintPath);
94129 if (!Loc) {
95130 log (" Workspace symbols: {0}" , Loc.takeError ());
96131 return ;
97132 }
98133
99- llvm::StringRef Scope = Sym.Scope ;
100- Scope.consume_back (" ::" );
101- SymbolInformation Info;
102- Info.name = (Sym.Name + Sym.TemplateSpecializationArgs ).str ();
103- Info.kind = indexSymbolKindToSymbolKind (Sym.SymInfo .Kind );
104- Info.location = *Loc;
105- Info.containerName = Scope.str ();
106-
107134 SymbolQualitySignals Quality;
108135 Quality.merge (Sym);
109136 SymbolRelevanceSignals Relevance;
110137 Relevance.Name = Sym.Name ;
111138 Relevance.Query = SymbolRelevanceSignals::Generic;
139+ // If symbol and request scopes do not match exactly, apply a penalty.
140+ Relevance.InBaseClass = AnyScope && Scope != ReqScope;
112141 if (auto NameMatch = Filter.match (Sym.Name ))
113142 Relevance.NameMatch = *NameMatch;
114143 else {
@@ -122,6 +151,13 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit,
122151 dlog (" FindSymbols: {0}{1} = {2}\n {3}{4}\n " , Sym.Scope , Sym.Name , Score,
123152 Quality, Relevance);
124153
154+ SymbolInformation Info;
155+ Info.name = (Sym.Name + Sym.TemplateSpecializationArgs ).str ();
156+ Info.kind = indexSymbolKindToSymbolKind (Sym.SymInfo .Kind );
157+ Info.location = *Loc;
158+ Scope.consume_back (" ::" );
159+ Info.containerName = Scope.str ();
160+
125161 // Exposed score excludes fuzzy-match component, for client-side re-ranking.
126162 Info.score = Score / Relevance.NameMatch ;
127163 Top.push ({Score, std::move (Info)});
0 commit comments