17 #include "llvm/ADT/SmallVector.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/ADT/StringSwitch.h" 20 #include "llvm/LineEditor/LineEditor.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/Signals.h" 28 llvm::cl::opt<std::string> IndexPath(
"index-path",
29 llvm::cl::desc(
"Path to the index"),
30 llvm::cl::Positional, llvm::cl::Required);
32 static const std::string Overview = R
"( 33 This is an **experimental** interactive tool to process user-provided search 34 queries over given symbol collection obtained via clangd-indexer. The 35 tool can be used to evaluate search quality of existing index implementations 36 and manually construct non-trivial test cases. 38 Type use "help" request to get information about the details. 41 void reportTime(llvm::StringRef Name, llvm::function_ref<
void()> F) {
42 const auto TimerStart = std::chrono::high_resolution_clock::now();
44 const auto TimerStop = std::chrono::high_resolution_clock::now();
45 const auto Duration = std::chrono::duration_cast<std::chrono::milliseconds>(
46 TimerStop - TimerStart);
47 llvm::outs() << llvm::formatv(
"{0} took {1:ms+n}.\n", Name, Duration);
50 std::vector<SymbolID> getSymbolIDsFromIndex(llvm::StringRef QualifiedName,
51 const SymbolIndex *
Index) {
52 FuzzyFindRequest Request;
55 bool IsGlobalScope = QualifiedName.consume_front(
"::");
57 if (IsGlobalScope || !Names.first.empty())
58 Request.Scopes = {Names.first};
62 Request.Scopes = {
""};
64 Request.Query = Names.second;
65 std::vector<SymbolID> SymIDs;
66 Index->fuzzyFind(Request, [&](
const Symbol &Sym) {
67 std::string SymQualifiedName = (Sym.Scope + Sym.Name).str();
68 if (QualifiedName == SymQualifiedName)
69 SymIDs.push_back(Sym.ID);
78 llvm::cl::opt<bool, false, llvm::cl::parser<bool>> Help{
79 "help", llvm::cl::desc(
"Display available options"),
80 llvm::cl::ValueDisallowed, llvm::cl::cat(llvm::cl::GeneralCategory)};
81 virtual void run() = 0;
88 virtual void parseAndRun(llvm::ArrayRef<const char *> Argv,
89 const char *Overview,
const SymbolIndex &Index) {
90 std::string ParseErrs;
91 llvm::raw_string_ostream OS(ParseErrs);
92 bool Ok = llvm::cl::ParseCommandLineOptions(Argv.size(), Argv.data(),
94 if (Help.getNumOccurrences() > 0) {
97 llvm::cl::PrintHelpMessage();
99 llvm::outs() << OS.str();
101 this->Index = &
Index;
102 reportTime(Argv[0], [&] { run(); });
105 llvm::cl::ResetCommandLineParser();
119 class FuzzyFind :
public Command {
120 llvm::cl::opt<std::string> Query{
122 llvm::cl::Positional,
124 llvm::cl::desc(
"Query string to be fuzzy-matched"),
126 llvm::cl::opt<std::string> Scopes{
128 llvm::cl::desc(
"Allowed symbol scopes (comma-separated list)"),
130 llvm::cl::opt<unsigned>
Limit{
133 llvm::cl::desc(
"Max results to display"),
136 void run()
override {
137 FuzzyFindRequest Request;
138 Request.Limit =
Limit;
139 Request.Query = Query;
140 if (Scopes.getNumOccurrences() > 0) {
141 llvm::SmallVector<llvm::StringRef, 8> Scopes;
142 llvm::StringRef(this->Scopes).split(Scopes,
',');
143 Request.Scopes = {Scopes.begin(), Scopes.end()};
145 Request.AnyScope = Request.Scopes.empty();
147 static const auto OutputFormat =
"{0,-4} | {1,-40} | {2,-25}\n";
148 llvm::outs() << llvm::formatv(OutputFormat,
"Rank",
"Symbol ID",
151 Index->fuzzyFind(Request, [&](
const Symbol &Sym) {
152 llvm::outs() << llvm::formatv(OutputFormat, Rank++, Sym.ID.str(),
153 Sym.Scope + Sym.Name);
158 class Lookup :
public Command {
159 llvm::cl::opt<std::string> ID{
161 llvm::cl::Positional,
162 llvm::cl::desc(
"Symbol ID to look up (hex)"),
164 llvm::cl::opt<std::string> Name{
166 llvm::cl::desc(
"Qualified name to look up."),
169 void run()
override {
170 if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) {
172 <<
"Missing required argument: please provide id or -name.\n";
175 std::vector<SymbolID> IDs;
176 if (ID.getNumOccurrences()) {
184 IDs = getSymbolIDsFromIndex(Name, Index);
187 LookupRequest Request;
188 Request.IDs.insert(IDs.begin(), IDs.end());
189 bool FoundSymbol =
false;
190 Index->lookup(Request, [&](
const Symbol &Sym) {
192 llvm::outs() <<
toYAML(Sym);
195 llvm::outs() <<
"not found\n";
200 llvm::cl::opt<std::string> ID{
202 llvm::cl::Positional,
203 llvm::cl::desc(
"Symbol ID of the symbol being queried (hex)."),
205 llvm::cl::opt<std::string> Name{
207 llvm::cl::desc(
"Qualified name of the symbol being queried."),
209 llvm::cl::opt<std::string> Filter{
211 llvm::cl::init(
".*"),
213 "Print all results from files matching this regular expression."),
216 void run()
override {
217 if (ID.getNumOccurrences() == 0 && Name.getNumOccurrences() == 0) {
219 <<
"Missing required argument: please provide id or -name.\n";
222 std::vector<SymbolID> IDs;
223 if (ID.getNumOccurrences()) {
231 IDs = getSymbolIDsFromIndex(Name, Index);
232 if (IDs.size() > 1) {
233 llvm::outs() << llvm::formatv(
234 "The name {0} is ambiguous, found {1} different " 235 "symbols. Please use id flag to disambiguate.\n",
240 RefsRequest RefRequest;
241 RefRequest.IDs.insert(IDs.begin(), IDs.end());
242 llvm::Regex RegexFilter(Filter);
243 Index->refs(RefRequest, [&RegexFilter](
const Ref &R) {
246 llvm::outs() << U.takeError();
249 if (RegexFilter.match(U->body()))
250 llvm::outs() << R <<
"\n";
260 {
"find",
"Search for symbols with fuzzyFind", std::make_unique<FuzzyFind>},
261 {
"lookup",
"Dump symbol details by ID or qualified name",
262 std::make_unique<Lookup>},
263 {
"refs",
"Find references by ID or qualified name",
264 std::make_unique<Refs>},
267 std::unique_ptr<SymbolIndex> openIndex(llvm::StringRef Index) {
275 int main(
int argc,
const char *argv[]) {
278 llvm::cl::ParseCommandLineOptions(argc, argv, Overview);
279 llvm::cl::ResetCommandLineParser();
280 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
282 std::unique_ptr<SymbolIndex>
Index;
283 reportTime(
"Dex build", [&]() {
284 Index = openIndex(IndexPath);
288 llvm::outs() <<
"Failed to open the index.\n";
292 llvm::LineEditor LE(
"dexp");
294 while (llvm::Optional<std::string> Request = LE.readLine()) {
296 std::replace(Request->begin(), Request->end(),
' ',
'\0');
297 llvm::SmallVector<llvm::StringRef, 8> Args;
298 llvm::StringRef(*Request).split(Args,
'\0', -1,
302 if (Args.front() ==
"help") {
303 llvm::outs() <<
"dexp - Index explorer\nCommands:\n";
304 for (
const auto &C : CommandInfo)
305 llvm::outs() << llvm::formatv(
"{0,16} - {1}\n", C.Name, C.Description);
306 llvm::outs() <<
"Get detailed command help with e.g. `find -help`.\n";
309 llvm::SmallVector<const char *, 8> FakeArgv;
310 for (llvm::StringRef S : Args)
311 FakeArgv.push_back(S.data());
313 bool Recognized =
false;
314 for (
const auto &Cmd : CommandInfo) {
315 if (Cmd.Name == Args.front()) {
317 Cmd.Implementation()->parseAndRun(FakeArgv, Cmd.Description, *Index);
322 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)
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::string toYAML(const Symbol &)
static std::string replace(llvm::StringRef Haystack, llvm::StringRef Needle, llvm::StringRef Repl)
===– Representation.cpp - ClangDoc Representation --------—*- 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