18 #include "llvm/ADT/SmallVector.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/ADT/StringSwitch.h" 21 #include "llvm/LineEditor/LineEditor.h" 22 #include "llvm/Support/CommandLine.h" 23 #include "llvm/Support/Signals.h" 29 llvm::cl::opt<std::string> IndexPath(
"index-path",
30 llvm::cl::desc(
"Path to the index"),
31 llvm::cl::Positional, llvm::cl::Required);
33 static const std::string Overview = R
"( 34 This is an **experimental** interactive tool to process user-provided search 35 queries over given symbol collection obtained via clangd-indexer. The 36 tool can be used to evaluate search quality of existing index implementations 37 and manually construct non-trivial test cases. 39 Type use "help" request to get information about the details. 42 void reportTime(llvm::StringRef Name, llvm::function_ref<
void()> F) {
43 const auto TimerStart = std::chrono::high_resolution_clock::now();
45 const auto TimerStop = std::chrono::high_resolution_clock::now();
46 const auto Duration = std::chrono::duration_cast<std::chrono::milliseconds>(
47 TimerStop - TimerStart);
48 llvm::outs() << llvm::formatv(
"{0} took {1:ms+n}.\n", Name, Duration);
51 std::vector<SymbolID> getSymbolIDsFromIndex(llvm::StringRef QualifiedName,
52 const SymbolIndex *
Index) {
53 FuzzyFindRequest Request;
56 bool IsGlobalScope = QualifiedName.consume_front(
"::");
58 if (IsGlobalScope || !Names.first.empty())
59 Request.Scopes = {Names.first};
63 Request.Scopes = {
""};
65 Request.Query = Names.second;
66 std::vector<SymbolID> SymIDs;
67 Index->fuzzyFind(Request, [&](
const Symbol &Sym) {
68 std::string SymQualifiedName = (Sym.Scope + Sym.Name).str();
69 if (QualifiedName == SymQualifiedName)
70 SymIDs.push_back(Sym.ID);
79 llvm::cl::opt<bool, false, llvm::cl::parser<bool>> Help{
80 "help", llvm::cl::desc(
"Display available options"),
81 llvm::cl::ValueDisallowed, llvm::cl::cat(llvm::cl::GeneralCategory)};
82 virtual void run() = 0;
89 virtual void parseAndRun(llvm::ArrayRef<const char *> Argv,
90 const char *Overview,
const SymbolIndex &Index) {
91 std::string ParseErrs;
92 llvm::raw_string_ostream OS(ParseErrs);
93 bool Ok = llvm::cl::ParseCommandLineOptions(Argv.size(), Argv.data(),
95 if (Help.getNumOccurrences() > 0) {
98 llvm::cl::PrintHelpMessage();
100 llvm::outs() << OS.str();
102 this->Index = &
Index;
103 reportTime(Argv[0], [&] { run(); });
106 llvm::cl::ResetCommandLineParser();
120 class FuzzyFind :
public Command {
121 llvm::cl::opt<std::string> Query{
123 llvm::cl::Positional,
125 llvm::cl::desc(
"Query string to be fuzzy-matched"),
127 llvm::cl::opt<std::string> Scopes{
129 llvm::cl::desc(
"Allowed symbol scopes (comma-separated list)"),
131 llvm::cl::opt<unsigned> Limit{
134 llvm::cl::desc(
"Max results to display"),
137 void run()
override {
138 FuzzyFindRequest Request;
139 Request.Limit = Limit;
140 Request.Query = Query;
141 if (Scopes.getNumOccurrences() > 0) {
142 llvm::SmallVector<llvm::StringRef, 8> Scopes;
143 llvm::StringRef(this->Scopes).split(Scopes,
',');
144 Request.Scopes = {Scopes.begin(), Scopes.end()};
146 Request.AnyScope = Request.Scopes.empty();
148 static const auto OutputFormat =
"{0,-4} | {1,-40} | {2,-25}\n";
149 llvm::outs() << llvm::formatv(OutputFormat,
"Rank",
"Symbol ID",
152 Index->fuzzyFind(Request, [&](
const Symbol &Sym) {
153 llvm::outs() << llvm::formatv(OutputFormat, Rank++, Sym.ID.str(),
154 Sym.Scope + Sym.Name);
159 class Lookup :
public Command {
160 llvm::cl::opt<std::string> ID{
162 llvm::cl::Positional,
163 llvm::cl::desc(
"Symbol ID to look up (hex)"),
165 llvm::cl::opt<std::string> Name{
167 llvm::cl::desc(
"Qualified name to look up."),
170 void run()
override {
171 if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) {
173 <<
"Missing required argument: please provide id or -name.\n";
176 std::vector<SymbolID> IDs;
177 if (ID.getNumOccurrences()) {
185 IDs = getSymbolIDsFromIndex(Name, Index);
188 LookupRequest Request;
189 Request.IDs.insert(IDs.begin(), IDs.end());
190 bool FoundSymbol =
false;
191 Index->lookup(Request, [&](
const Symbol &Sym) {
193 llvm::outs() <<
toYAML(Sym);
196 llvm::outs() <<
"not found\n";
201 llvm::cl::opt<std::string> ID{
203 llvm::cl::Positional,
204 llvm::cl::desc(
"Symbol ID of the symbol being queried (hex)."),
206 llvm::cl::opt<std::string> Name{
208 llvm::cl::desc(
"Qualified name of the symbol being queried."),
210 llvm::cl::opt<std::string> Filter{
212 llvm::cl::init(
".*"),
214 "Print all results from files matching this regular expression."),
217 void run()
override {
218 if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) {
220 <<
"Missing required argument: please provide id or -name.\n";
223 std::vector<SymbolID> IDs;
224 if (ID.getNumOccurrences()) {
232 IDs = getSymbolIDsFromIndex(Name, Index);
233 if (IDs.size() > 1) {
234 llvm::outs() << llvm::formatv(
235 "The name {0} is ambiguous, found {1} different " 236 "symbols. Please use id flag to disambiguate.\n",
241 RefsRequest RefRequest;
242 RefRequest.IDs.insert(IDs.begin(), IDs.end());
243 llvm::Regex RegexFilter(Filter);
244 Index->refs(RefRequest, [&RegexFilter](
const Ref &R) {
247 llvm::outs() << U.takeError();
250 if (RegexFilter.match(U->body()))
251 llvm::outs() << R <<
"\n";
261 {
"find",
"Search for symbols with fuzzyFind", llvm::make_unique<FuzzyFind>},
262 {
"lookup",
"Dump symbol details by ID or qualified name",
263 llvm::make_unique<Lookup>},
264 {
"refs",
"Find references by ID or qualified name",
265 llvm::make_unique<Refs>},
268 std::unique_ptr<SymbolIndex> openIndex(llvm::StringRef Index) {
276 int main(
int argc,
const char *argv[]) {
279 llvm::cl::ParseCommandLineOptions(argc, argv, Overview);
280 llvm::cl::ResetCommandLineParser();
281 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
283 std::unique_ptr<SymbolIndex>
Index;
284 reportTime(
"Dex build", [&]() {
285 Index = openIndex(IndexPath);
289 llvm::outs() <<
"Failed to open the index.\n";
293 llvm::LineEditor LE(
"dexp");
295 while (llvm::Optional<std::string> Request = LE.readLine()) {
297 std::replace(Request->begin(), Request->end(),
' ',
'\0');
298 llvm::SmallVector<llvm::StringRef, 8> Args;
299 llvm::StringRef(*Request).split(Args,
'\0', -1,
303 if (Args.front() ==
"help") {
304 llvm::outs() <<
"dexp - Index explorer\nCommands:\n";
305 for (
const auto &C : CommandInfo)
306 llvm::outs() << llvm::formatv(
"{0,16} - {1}\n", C.Name, C.Description);
307 llvm::outs() <<
"Get detailed command help with e.g. `find -help`.\n";
310 llvm::SmallVector<const char *, 8> FakeArgv;
311 for (llvm::StringRef S : Args)
312 FakeArgv.push_back(S.data());
314 bool Recognized =
false;
315 for (
const auto &Cmd : CommandInfo) {
316 if (Cmd.Name == Args.front()) {
318 Cmd.Implementation()->parseAndRun(FakeArgv, Cmd.Description, *Index);
323 llvm::outs() <<
"Unknown command. Try 'help'.\n";
const tooling::CompileCommand & Command
std::function< std::unique_ptr< Command >)> Implementation
std::unique_ptr< SymbolIndex > loadIndex(llvm::StringRef SymbolFilename, bool UseDex)
This defines Dex - a symbol index implementation based on query iterators over symbol tokens...
static llvm::Expected< SymbolID > fromStr(llvm::StringRef)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::string toYAML(const Symbol &)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::pair< llvm::StringRef, llvm::StringRef > splitQualifiedName(llvm::StringRef QName)
From "a::b::c", return {"a::b::", "c"}.
static llvm::Expected< URI > parse(llvm::StringRef Uri)
Parse a URI string "<scheme>:[//<authority>/]<path>".
int main(int argc, const char *argv[])
const SymbolIndex * Index