1 //===--- ClangdServer.cpp - Main clangd server code --------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===-------------------------------------------------------------------===// 8 9 #include "ClangdServer.h" 10 #include "ClangdUnit.h" 11 #include "CodeComplete.h" 12 #include "FindSymbols.h" 13 #include "Headers.h" 14 #include "Protocol.h" 15 #include "SourceCode.h" 16 #include "TUScheduler.h" 17 #include "Trace.h" 18 #include "index/CanonicalIncludes.h" 19 #include "index/FileIndex.h" 20 #include "index/Merge.h" 21 #include "refactor/Tweak.h" 22 #include "clang/Format/Format.h" 23 #include "clang/Frontend/CompilerInstance.h" 24 #include "clang/Frontend/CompilerInvocation.h" 25 #include "clang/Lex/Preprocessor.h" 26 #include "clang/Tooling/CompilationDatabase.h" 27 #include "clang/Tooling/Core/Replacement.h" 28 #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" 29 #include "clang/Tooling/Refactoring/Rename/RenamingAction.h" 30 #include "llvm/ADT/ArrayRef.h" 31 #include "llvm/ADT/Optional.h" 32 #include "llvm/ADT/ScopeExit.h" 33 #include "llvm/ADT/StringRef.h" 34 #include "llvm/Support/Errc.h" 35 #include "llvm/Support/Error.h" 36 #include "llvm/Support/FileSystem.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/raw_ostream.h" 39 #include <future> 40 #include <memory> 41 #include <mutex> 42 43 namespace clang { 44 namespace clangd { 45 namespace { 46 47 // Expand a DiagnosticError to make it print-friendly (print the detailed 48 // message, rather than "clang diagnostic"). 49 llvm::Error expandDiagnostics(llvm::Error Err, DiagnosticsEngine &DE) { 50 if (auto Diag = DiagnosticError::take(Err)) { 51 llvm::cantFail(std::move(Err)); 52 SmallVector<char, 128> DiagMessage; 53 Diag->second.EmitToString(DE, DiagMessage); 54 return llvm::make_error<llvm::StringError>(DiagMessage, 55 llvm::inconvertibleErrorCode()); 56 } 57 return Err; 58 } 59 60 class RefactoringResultCollector final 61 : public tooling::RefactoringResultConsumer { 62 public: 63 void handleError(llvm::Error Err) override { 64 assert(!Result.hasValue()); 65 Result = std::move(Err); 66 } 67 68 // Using the handle(SymbolOccurrences) from parent class. 69 using tooling::RefactoringResultConsumer::handle; 70 71 void handle(tooling::AtomicChanges SourceReplacements) override { 72 assert(!Result.hasValue()); 73 Result = std::move(SourceReplacements); 74 } 75 76 llvm::Optional<llvm::Expected<tooling::AtomicChanges>> Result; 77 }; 78 79 // Update the FileIndex with new ASTs and plumb the diagnostics responses. 80 struct UpdateIndexCallbacks : public ParsingCallbacks { 81 UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer) 82 : FIndex(FIndex), DiagConsumer(DiagConsumer) {} 83 84 void onPreambleAST(PathRef Path, ASTContext &Ctx, 85 std::shared_ptr<clang::Preprocessor> PP, 86 const CanonicalIncludes &CanonIncludes) override { 87 if (FIndex) 88 FIndex->updatePreamble(Path, Ctx, std::move(PP), CanonIncludes); 89 } 90 91 void onMainAST(PathRef Path, ParsedAST &AST) override { 92 if (FIndex) 93 FIndex->updateMain(Path, AST); 94 } 95 96 void onDiagnostics(PathRef File, std::vector<Diag> Diags) override { 97 DiagConsumer.onDiagnosticsReady(File, std::move(Diags)); 98 } 99 100 void onFileUpdated(PathRef File, const TUStatus &Status) override { 101 DiagConsumer.onFileUpdated(File, Status); 102 } 103 104 private: 105 FileIndex *FIndex; 106 DiagnosticsConsumer &DiagConsumer; 107 }; 108 } // namespace 109 110 ClangdServer::Options ClangdServer::optsForTest() { 111 ClangdServer::Options Opts; 112 Opts.UpdateDebounce = std::chrono::steady_clock::duration::zero(); // Faster! 113 Opts.StorePreamblesInMemory = true; 114 Opts.AsyncThreadsCount = 4; // Consistent! 115 return Opts; 116 } 117 118 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, 119 const FileSystemProvider &FSProvider, 120 DiagnosticsConsumer &DiagConsumer, 121 const Options &Opts) 122 : FSProvider(FSProvider), 123 DynamicIdx(Opts.BuildDynamicSymbolIndex 124 ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex) 125 : nullptr), 126 ClangTidyOptProvider(Opts.ClangTidyOptProvider), 127 SuggestMissingIncludes(Opts.SuggestMissingIncludes), 128 WorkspaceRoot(Opts.WorkspaceRoot), 129 // Pass a callback into `WorkScheduler` to extract symbols from a newly 130 // parsed file and rebuild the file index synchronously each time an AST 131 // is parsed. 132 // FIXME(ioeric): this can be slow and we may be able to index on less 133 // critical paths. 134 WorkScheduler(CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, 135 llvm::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(), 136 DiagConsumer), 137 Opts.UpdateDebounce, Opts.RetentionPolicy) { 138 // Adds an index to the stack, at higher priority than existing indexes. 139 auto AddIndex = [&](SymbolIndex *Idx) { 140 if (this->Index != nullptr) { 141 MergedIdx.push_back(llvm::make_unique<MergedIndex>(Idx, this->Index)); 142 this->Index = MergedIdx.back().get(); 143 } else { 144 this->Index = Idx; 145 } 146 }; 147 if (Opts.StaticIndex) 148 AddIndex(Opts.StaticIndex); 149 if (Opts.BackgroundIndex) { 150 BackgroundIdx = llvm::make_unique<BackgroundIndex>( 151 Context::current().clone(), FSProvider, CDB, 152 BackgroundIndexStorage::createDiskBackedStorageFactory(), 153 Opts.BackgroundIndexRebuildPeriodMs); 154 AddIndex(BackgroundIdx.get()); 155 } 156 if (DynamicIdx) 157 AddIndex(DynamicIdx.get()); 158 } 159 160 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, 161 WantDiagnostics WantDiags) { 162 ParseOptions Opts; 163 Opts.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults(); 164 if (ClangTidyOptProvider) 165 Opts.ClangTidyOpts = ClangTidyOptProvider->getOptions(File); 166 Opts.SuggestMissingIncludes = SuggestMissingIncludes; 167 168 // Compile command is set asynchronously during update, as it can be slow. 169 ParseInputs Inputs; 170 Inputs.FS = FSProvider.getFileSystem(); 171 Inputs.Contents = Contents; 172 Inputs.Opts = std::move(Opts); 173 Inputs.Index = Index; 174 WorkScheduler.update(File, Inputs, WantDiags); 175 } 176 177 void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } 178 179 void ClangdServer::codeComplete(PathRef File, Position Pos, 180 const clangd::CodeCompleteOptions &Opts, 181 Callback<CodeCompleteResult> CB) { 182 // Copy completion options for passing them to async task handler. 183 auto CodeCompleteOpts = Opts; 184 if (!CodeCompleteOpts.Index) // Respect overridden index. 185 CodeCompleteOpts.Index = Index; 186 187 auto FS = FSProvider.getFileSystem(); 188 auto Task = [Pos, FS, CodeCompleteOpts, 189 this](Path File, Callback<CodeCompleteResult> CB, 190 llvm::Expected<InputsAndPreamble> IP) { 191 if (!IP) 192 return CB(IP.takeError()); 193 if (isCancelled()) 194 return CB(llvm::make_error<CancelledError>()); 195 196 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; 197 if (!IP->Preamble) { 198 // No speculation in Fallback mode, as it's supposed to be much faster 199 // without compiling. 200 vlog("Build for file {0} is not ready. Enter fallback mode.", File); 201 } else { 202 if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) { 203 SpecFuzzyFind.emplace(); 204 { 205 std::lock_guard<std::mutex> Lock( 206 CachedCompletionFuzzyFindRequestMutex); 207 SpecFuzzyFind->CachedReq = 208 CachedCompletionFuzzyFindRequestByFile[File]; 209 } 210 } 211 } 212 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check 213 // both the old and the new version in case only one of them matches. 214 CodeCompleteResult Result = clangd::codeComplete( 215 File, IP->Command, IP->Preamble, IP->Contents, Pos, FS, 216 CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); 217 { 218 clang::clangd::trace::Span Tracer("Completion results callback"); 219 CB(std::move(Result)); 220 } 221 if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) { 222 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); 223 CachedCompletionFuzzyFindRequestByFile[File] = 224 SpecFuzzyFind->NewReq.getValue(); 225 } 226 // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes. 227 // We don't want `codeComplete` to wait for the async call if it doesn't use 228 // the result (e.g. non-index completion, speculation fails), so that `CB` 229 // is called as soon as results are available. 230 }; 231 232 // We use a potentially-stale preamble because latency is critical here. 233 WorkScheduler.runWithPreamble("CodeComplete", File, 234 Opts.AllowFallback ? TUScheduler::StaleOrAbsent 235 : TUScheduler::Stale, 236 Bind(Task, File.str(), std::move(CB))); 237 } 238 239 void ClangdServer::signatureHelp(PathRef File, Position Pos, 240 Callback<SignatureHelp> CB) { 241 242 auto FS = FSProvider.getFileSystem(); 243 auto *Index = this->Index; 244 auto Action = [Pos, FS, Index](Path File, Callback<SignatureHelp> CB, 245 llvm::Expected<InputsAndPreamble> IP) { 246 if (!IP) 247 return CB(IP.takeError()); 248 249 auto PreambleData = IP->Preamble; 250 CB(clangd::signatureHelp(File, IP->Command, PreambleData, IP->Contents, Pos, 251 FS, Index)); 252 }; 253 254 // Unlike code completion, we wait for an up-to-date preamble here. 255 // Signature help is often triggered after code completion. If the code 256 // completion inserted a header to make the symbol available, then using 257 // the old preamble would yield useless results. 258 WorkScheduler.runWithPreamble("SignatureHelp", File, TUScheduler::Consistent, 259 Bind(Action, File.str(), std::move(CB))); 260 } 261 262 llvm::Expected<tooling::Replacements> 263 ClangdServer::formatRange(llvm::StringRef Code, PathRef File, Range Rng) { 264 llvm::Expected<size_t> Begin = positionToOffset(Code, Rng.start); 265 if (!Begin) 266 return Begin.takeError(); 267 llvm::Expected<size_t> End = positionToOffset(Code, Rng.end); 268 if (!End) 269 return End.takeError(); 270 return formatCode(Code, File, {tooling::Range(*Begin, *End - *Begin)}); 271 } 272 273 llvm::Expected<tooling::Replacements> 274 ClangdServer::formatFile(llvm::StringRef Code, PathRef File) { 275 // Format everything. 276 return formatCode(Code, File, {tooling::Range(0, Code.size())}); 277 } 278 279 llvm::Expected<tooling::Replacements> 280 ClangdServer::formatOnType(llvm::StringRef Code, PathRef File, Position Pos) { 281 // Look for the previous opening brace from the character position and 282 // format starting from there. 283 llvm::Expected<size_t> CursorPos = positionToOffset(Code, Pos); 284 if (!CursorPos) 285 return CursorPos.takeError(); 286 size_t PreviousLBracePos = 287 llvm::StringRef(Code).find_last_of('{', *CursorPos); 288 if (PreviousLBracePos == llvm::StringRef::npos) 289 PreviousLBracePos = *CursorPos; 290 size_t Len = *CursorPos - PreviousLBracePos; 291 292 return formatCode(Code, File, {tooling::Range(PreviousLBracePos, Len)}); 293 } 294 295 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, 296 Callback<std::vector<TextEdit>> CB) { 297 auto Action = [Pos](Path File, std::string NewName, 298 Callback<std::vector<TextEdit>> CB, 299 llvm::Expected<InputsAndAST> InpAST) { 300 if (!InpAST) 301 return CB(InpAST.takeError()); 302 auto &AST = InpAST->AST; 303 304 RefactoringResultCollector ResultCollector; 305 const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); 306 SourceLocation SourceLocationBeg = 307 clangd::getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID()); 308 tooling::RefactoringRuleContext Context( 309 AST.getASTContext().getSourceManager()); 310 Context.setASTContext(AST.getASTContext()); 311 auto Rename = clang::tooling::RenameOccurrences::initiate( 312 Context, SourceRange(SourceLocationBeg), NewName); 313 if (!Rename) 314 return CB(expandDiagnostics(Rename.takeError(), 315 AST.getASTContext().getDiagnostics())); 316 317 Rename->invoke(ResultCollector, Context); 318 319 assert(ResultCollector.Result.hasValue()); 320 if (!ResultCollector.Result.getValue()) 321 return CB(expandDiagnostics(ResultCollector.Result->takeError(), 322 AST.getASTContext().getDiagnostics())); 323 324 std::vector<TextEdit> Replacements; 325 for (const tooling::AtomicChange &Change : ResultCollector.Result->get()) { 326 tooling::Replacements ChangeReps = Change.getReplacements(); 327 for (const auto &Rep : ChangeReps) { 328 // FIXME: Right now we only support renaming the main file, so we 329 // drop replacements not for the main file. In the future, we might 330 // consider to support: 331 // * rename in any included header 332 // * rename only in the "main" header 333 // * provide an error if there are symbols we won't rename (e.g. 334 // std::vector) 335 // * rename globally in project 336 // * rename in open files 337 if (Rep.getFilePath() == File) 338 Replacements.push_back( 339 replacementToEdit(InpAST->Inputs.Contents, Rep)); 340 } 341 } 342 return CB(std::move(Replacements)); 343 }; 344 345 WorkScheduler.runWithAST( 346 "Rename", File, Bind(Action, File.str(), NewName.str(), std::move(CB))); 347 } 348 349 static llvm::Expected<Tweak::Selection> 350 tweakSelection(const Range &Sel, const InputsAndAST &AST) { 351 auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start); 352 if (!Begin) 353 return Begin.takeError(); 354 auto End = positionToOffset(AST.Inputs.Contents, Sel.end); 355 if (!End) 356 return End.takeError(); 357 return Tweak::Selection(AST.AST, *Begin, *End); 358 } 359 360 void ClangdServer::enumerateTweaks(PathRef File, Range Sel, 361 Callback<std::vector<TweakRef>> CB) { 362 auto Action = [Sel](decltype(CB) CB, std::string File, 363 Expected<InputsAndAST> InpAST) { 364 if (!InpAST) 365 return CB(InpAST.takeError()); 366 auto Selection = tweakSelection(Sel, *InpAST); 367 if (!Selection) 368 return CB(Selection.takeError()); 369 std::vector<TweakRef> Res; 370 for (auto &T : prepareTweaks(*Selection)) 371 Res.push_back({T->id(), T->title()}); 372 CB(std::move(Res)); 373 }; 374 375 WorkScheduler.runWithAST("EnumerateTweaks", File, 376 Bind(Action, std::move(CB), File.str())); 377 } 378 379 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, 380 Callback<tooling::Replacements> CB) { 381 auto Action = [Sel](decltype(CB) CB, std::string File, std::string TweakID, 382 Expected<InputsAndAST> InpAST) { 383 if (!InpAST) 384 return CB(InpAST.takeError()); 385 auto Selection = tweakSelection(Sel, *InpAST); 386 if (!Selection) 387 return CB(Selection.takeError()); 388 auto A = prepareTweak(TweakID, *Selection); 389 if (!A) 390 return CB(A.takeError()); 391 auto RawReplacements = (*A)->apply(*Selection); 392 if (!RawReplacements) 393 return CB(RawReplacements.takeError()); 394 // FIXME: this function has I/O operations (find .clang-format file), figure 395 // out a way to cache the format style. 396 auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, 397 InpAST->Inputs.FS.get()); 398 return CB( 399 cleanupAndFormat(InpAST->Inputs.Contents, *RawReplacements, Style)); 400 }; 401 WorkScheduler.runWithAST( 402 "ApplyTweak", File, 403 Bind(Action, std::move(CB), File.str(), TweakID.str())); 404 } 405 406 void ClangdServer::dumpAST(PathRef File, 407 llvm::unique_function<void(std::string)> Callback) { 408 auto Action = [](decltype(Callback) Callback, 409 llvm::Expected<InputsAndAST> InpAST) { 410 if (!InpAST) { 411 llvm::consumeError(InpAST.takeError()); 412 return Callback("<no-ast>"); 413 } 414 std::string Result; 415 416 llvm::raw_string_ostream ResultOS(Result); 417 clangd::dumpAST(InpAST->AST, ResultOS); 418 ResultOS.flush(); 419 420 Callback(Result); 421 }; 422 423 WorkScheduler.runWithAST("DumpAST", File, Bind(Action, std::move(Callback))); 424 } 425 426 void ClangdServer::locateSymbolAt(PathRef File, Position Pos, 427 Callback<std::vector<LocatedSymbol>> CB) { 428 auto Action = [Pos, this](decltype(CB) CB, 429 llvm::Expected<InputsAndAST> InpAST) { 430 if (!InpAST) 431 return CB(InpAST.takeError()); 432 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index)); 433 }; 434 435 WorkScheduler.runWithAST("Definitions", File, Bind(Action, std::move(CB))); 436 } 437 438 llvm::Optional<Path> ClangdServer::switchSourceHeader(PathRef Path) { 439 440 llvm::StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx", 441 ".c++", ".m", ".mm"}; 442 llvm::StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx", ".inc"}; 443 444 llvm::StringRef PathExt = llvm::sys::path::extension(Path); 445 446 // Lookup in a list of known extensions. 447 auto SourceIter = 448 llvm::find_if(SourceExtensions, [&PathExt](PathRef SourceExt) { 449 return SourceExt.equals_lower(PathExt); 450 }); 451 bool IsSource = SourceIter != std::end(SourceExtensions); 452 453 auto HeaderIter = 454 llvm::find_if(HeaderExtensions, [&PathExt](PathRef HeaderExt) { 455 return HeaderExt.equals_lower(PathExt); 456 }); 457 458 bool IsHeader = HeaderIter != std::end(HeaderExtensions); 459 460 // We can only switch between the known extensions. 461 if (!IsSource && !IsHeader) 462 return None; 463 464 // Array to lookup extensions for the switch. An opposite of where original 465 // extension was found. 466 llvm::ArrayRef<llvm::StringRef> NewExts; 467 if (IsSource) 468 NewExts = HeaderExtensions; 469 else 470 NewExts = SourceExtensions; 471 472 // Storage for the new path. 473 llvm::SmallString<128> NewPath = llvm::StringRef(Path); 474 475 // Instance of vfs::FileSystem, used for file existence checks. 476 auto FS = FSProvider.getFileSystem(); 477 478 // Loop through switched extension candidates. 479 for (llvm::StringRef NewExt : NewExts) { 480 llvm::sys::path::replace_extension(NewPath, NewExt); 481 if (FS->exists(NewPath)) 482 return NewPath.str().str(); // First str() to convert from SmallString to 483 // StringRef, second to convert from StringRef 484 // to std::string 485 486 // Also check NewExt in upper-case, just in case. 487 llvm::sys::path::replace_extension(NewPath, NewExt.upper()); 488 if (FS->exists(NewPath)) 489 return NewPath.str().str(); 490 } 491 492 return None; 493 } 494 495 llvm::Expected<tooling::Replacements> 496 ClangdServer::formatCode(llvm::StringRef Code, PathRef File, 497 llvm::ArrayRef<tooling::Range> Ranges) { 498 // Call clang-format. 499 auto FS = FSProvider.getFileSystem(); 500 auto Style = format::getStyle(format::DefaultFormatStyle, File, 501 format::DefaultFallbackStyle, Code, FS.get()); 502 if (!Style) 503 return Style.takeError(); 504 505 tooling::Replacements IncludeReplaces = 506 format::sortIncludes(*Style, Code, Ranges, File); 507 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); 508 if (!Changed) 509 return Changed.takeError(); 510 511 return IncludeReplaces.merge(format::reformat( 512 Style.get(), *Changed, 513 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), 514 File)); 515 } 516 517 void ClangdServer::findDocumentHighlights( 518 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) { 519 auto Action = [Pos](Callback<std::vector<DocumentHighlight>> CB, 520 llvm::Expected<InputsAndAST> InpAST) { 521 if (!InpAST) 522 return CB(InpAST.takeError()); 523 CB(clangd::findDocumentHighlights(InpAST->AST, Pos)); 524 }; 525 526 WorkScheduler.runWithAST("Highlights", File, Bind(Action, std::move(CB))); 527 } 528 529 void ClangdServer::findHover(PathRef File, Position Pos, 530 Callback<llvm::Optional<Hover>> CB) { 531 auto Action = [Pos](Callback<llvm::Optional<Hover>> CB, 532 llvm::Expected<InputsAndAST> InpAST) { 533 if (!InpAST) 534 return CB(InpAST.takeError()); 535 CB(clangd::getHover(InpAST->AST, Pos)); 536 }; 537 538 WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB))); 539 } 540 541 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve, 542 TypeHierarchyDirection Direction, 543 Callback<Optional<TypeHierarchyItem>> CB) { 544 auto Action = [Pos, Resolve, Direction](decltype(CB) CB, 545 Expected<InputsAndAST> InpAST) { 546 if (!InpAST) 547 return CB(InpAST.takeError()); 548 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction)); 549 }; 550 551 WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB))); 552 } 553 554 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { 555 // FIXME: Do nothing for now. This will be used for indexing and potentially 556 // invalidating other caches. 557 } 558 559 void ClangdServer::workspaceSymbols( 560 llvm::StringRef Query, int Limit, 561 Callback<std::vector<SymbolInformation>> CB) { 562 std::string QueryCopy = Query; 563 WorkScheduler.run( 564 "getWorkspaceSymbols", 565 Bind( 566 [QueryCopy, Limit, this](decltype(CB) CB) { 567 CB(clangd::getWorkspaceSymbols(QueryCopy, Limit, Index, 568 WorkspaceRoot.getValueOr(""))); 569 }, 570 std::move(CB))); 571 } 572 573 void ClangdServer::documentSymbols(llvm::StringRef File, 574 Callback<std::vector<DocumentSymbol>> CB) { 575 auto Action = [](Callback<std::vector<DocumentSymbol>> CB, 576 llvm::Expected<InputsAndAST> InpAST) { 577 if (!InpAST) 578 return CB(InpAST.takeError()); 579 CB(clangd::getDocumentSymbols(InpAST->AST)); 580 }; 581 WorkScheduler.runWithAST("documentSymbols", File, 582 Bind(Action, std::move(CB))); 583 } 584 585 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, 586 Callback<std::vector<Location>> CB) { 587 auto Action = [Pos, Limit, this](Callback<std::vector<Location>> CB, 588 llvm::Expected<InputsAndAST> InpAST) { 589 if (!InpAST) 590 return CB(InpAST.takeError()); 591 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index)); 592 }; 593 594 WorkScheduler.runWithAST("References", File, Bind(Action, std::move(CB))); 595 } 596 597 void ClangdServer::symbolInfo(PathRef File, Position Pos, 598 Callback<std::vector<SymbolDetails>> CB) { 599 auto Action = [Pos](Callback<std::vector<SymbolDetails>> CB, 600 llvm::Expected<InputsAndAST> InpAST) { 601 if (!InpAST) 602 return CB(InpAST.takeError()); 603 CB(clangd::getSymbolInfo(InpAST->AST, Pos)); 604 }; 605 606 WorkScheduler.runWithAST("SymbolInfo", File, Bind(Action, std::move(CB))); 607 } 608 609 std::vector<std::pair<Path, std::size_t>> 610 ClangdServer::getUsedBytesPerFile() const { 611 return WorkScheduler.getUsedBytesPerFile(); 612 } 613 614 LLVM_NODISCARD bool 615 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) { 616 return WorkScheduler.blockUntilIdle(timeoutSeconds(TimeoutSeconds)) && 617 (!BackgroundIdx || 618 BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); 619 } 620 621 } // namespace clangd 622 } // namespace clang 623