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 "CodeComplete.h"
11 #include "FindSymbols.h"
12 #include "Format.h"
13 #include "FormattedString.h"
14 #include "HeaderSourceSwitch.h"
15 #include "Headers.h"
16 #include "Logger.h"
17 #include "ParsedAST.h"
18 #include "Preamble.h"
19 #include "Protocol.h"
20 #include "SemanticHighlighting.h"
21 #include "SemanticSelection.h"
22 #include "SourceCode.h"
23 #include "TUScheduler.h"
24 #include "Trace.h"
25 #include "XRefs.h"
26 #include "index/CanonicalIncludes.h"
27 #include "index/FileIndex.h"
28 #include "index/Merge.h"
29 #include "refactor/Rename.h"
30 #include "refactor/Tweak.h"
31 #include "clang/Format/Format.h"
32 #include "clang/Frontend/CompilerInstance.h"
33 #include "clang/Frontend/CompilerInvocation.h"
34 #include "clang/Lex/Preprocessor.h"
35 #include "clang/Tooling/CompilationDatabase.h"
36 #include "clang/Tooling/Core/Replacement.h"
37 #include "llvm/ADT/ArrayRef.h"
38 #include "llvm/ADT/Optional.h"
39 #include "llvm/ADT/STLExtras.h"
40 #include "llvm/ADT/ScopeExit.h"
41 #include "llvm/ADT/StringRef.h"
42 #include "llvm/Support/Errc.h"
43 #include "llvm/Support/Error.h"
44 #include "llvm/Support/FileSystem.h"
45 #include "llvm/Support/Path.h"
46 #include "llvm/Support/raw_ostream.h"
47 #include <algorithm>
48 #include <future>
49 #include <memory>
50 #include <mutex>
51 #include <type_traits>
52 
53 namespace clang {
54 namespace clangd {
55 namespace {
56 
57 // Update the FileIndex with new ASTs and plumb the diagnostics responses.
58 struct UpdateIndexCallbacks : public ParsingCallbacks {
UpdateIndexCallbacksclang::clangd::__anon86376a840111::UpdateIndexCallbacks59   UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer,
60                        bool SemanticHighlighting)
61       : FIndex(FIndex), DiagConsumer(DiagConsumer),
62         SemanticHighlighting(SemanticHighlighting) {}
63 
onPreambleASTclang::clangd::__anon86376a840111::UpdateIndexCallbacks64   void onPreambleAST(PathRef Path, ASTContext &Ctx,
65                      std::shared_ptr<clang::Preprocessor> PP,
66                      const CanonicalIncludes &CanonIncludes) override {
67     if (FIndex)
68       FIndex->updatePreamble(Path, Ctx, std::move(PP), CanonIncludes);
69   }
70 
onMainASTclang::clangd::__anon86376a840111::UpdateIndexCallbacks71   void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override {
72     if (FIndex)
73       FIndex->updateMain(Path, AST);
74 
75     std::vector<Diag> Diagnostics = AST.getDiagnostics();
76     std::vector<HighlightingToken> Highlightings;
77     if (SemanticHighlighting)
78       Highlightings = getSemanticHighlightings(AST);
79 
80     Publish([&]() {
81       DiagConsumer.onDiagnosticsReady(Path, std::move(Diagnostics));
82       if (SemanticHighlighting)
83         DiagConsumer.onHighlightingsReady(Path, std::move(Highlightings));
84     });
85   }
86 
onFailedASTclang::clangd::__anon86376a840111::UpdateIndexCallbacks87   void onFailedAST(PathRef Path, std::vector<Diag> Diags,
88                    PublishFn Publish) override {
89     Publish([&]() { DiagConsumer.onDiagnosticsReady(Path, Diags); });
90   }
91 
onFileUpdatedclang::clangd::__anon86376a840111::UpdateIndexCallbacks92   void onFileUpdated(PathRef File, const TUStatus &Status) override {
93     DiagConsumer.onFileUpdated(File, Status);
94   }
95 
96 private:
97   FileIndex *FIndex;
98   DiagnosticsConsumer &DiagConsumer;
99   bool SemanticHighlighting;
100 };
101 } // namespace
102 
optsForTest()103 ClangdServer::Options ClangdServer::optsForTest() {
104   ClangdServer::Options Opts;
105   Opts.UpdateDebounce = std::chrono::steady_clock::duration::zero(); // Faster!
106   Opts.StorePreamblesInMemory = true;
107   Opts.AsyncThreadsCount = 4; // Consistent!
108   Opts.SemanticHighlighting = true;
109   return Opts;
110 }
111 
ClangdServer(const GlobalCompilationDatabase & CDB,const FileSystemProvider & FSProvider,DiagnosticsConsumer & DiagConsumer,const Options & Opts)112 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
113                            const FileSystemProvider &FSProvider,
114                            DiagnosticsConsumer &DiagConsumer,
115                            const Options &Opts)
116     : FSProvider(FSProvider),
117       DynamicIdx(Opts.BuildDynamicSymbolIndex
118                      ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex)
119                      : nullptr),
120       GetClangTidyOptions(Opts.GetClangTidyOptions),
121       SuggestMissingIncludes(Opts.SuggestMissingIncludes),
122       CrossFileRename(Opts.CrossFileRename), TweakFilter(Opts.TweakFilter),
123       WorkspaceRoot(Opts.WorkspaceRoot),
124       // Pass a callback into `WorkScheduler` to extract symbols from a newly
125       // parsed file and rebuild the file index synchronously each time an AST
126       // is parsed.
127       // FIXME(ioeric): this can be slow and we may be able to index on less
128       // critical paths.
129       WorkScheduler(
130           CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory,
131           std::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(), DiagConsumer,
132                                                  Opts.SemanticHighlighting),
133           Opts.UpdateDebounce, Opts.RetentionPolicy) {
134   // Adds an index to the stack, at higher priority than existing indexes.
135   auto AddIndex = [&](SymbolIndex *Idx) {
136     if (this->Index != nullptr) {
137       MergedIdx.push_back(std::make_unique<MergedIndex>(Idx, this->Index));
138       this->Index = MergedIdx.back().get();
139     } else {
140       this->Index = Idx;
141     }
142   };
143   if (Opts.StaticIndex)
144     AddIndex(Opts.StaticIndex);
145   if (Opts.BackgroundIndex) {
146     BackgroundIdx = std::make_unique<BackgroundIndex>(
147         Context::current().clone(), FSProvider, CDB,
148         BackgroundIndexStorage::createDiskBackedStorageFactory(
149             [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }),
150         std::max(Opts.AsyncThreadsCount, 1u));
151     AddIndex(BackgroundIdx.get());
152   }
153   if (DynamicIdx)
154     AddIndex(DynamicIdx.get());
155 }
156 
addDocument(PathRef File,llvm::StringRef Contents,WantDiagnostics WantDiags)157 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
158                                WantDiagnostics WantDiags) {
159   auto FS = FSProvider.getFileSystem();
160 
161   ParseOptions Opts;
162   Opts.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults();
163   // FIXME: call tidy options builder on the worker thread, it can do IO.
164   if (GetClangTidyOptions)
165     Opts.ClangTidyOpts = GetClangTidyOptions(*FS, 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 = FS;
171   Inputs.Contents = Contents;
172   Inputs.Opts = std::move(Opts);
173   Inputs.Index = Index;
174   bool NewFile = WorkScheduler.update(File, Inputs, WantDiags);
175   // If we loaded Foo.h, we want to make sure Foo.cpp is indexed.
176   if (NewFile && BackgroundIdx)
177     BackgroundIdx->boostRelated(File);
178 }
179 
removeDocument(PathRef File)180 void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); }
181 
getDocument(PathRef File) const182 llvm::StringRef ClangdServer::getDocument(PathRef File) const {
183   return WorkScheduler.getContents(File);
184 }
185 
codeComplete(PathRef File,Position Pos,const clangd::CodeCompleteOptions & Opts,Callback<CodeCompleteResult> CB)186 void ClangdServer::codeComplete(PathRef File, Position Pos,
187                                 const clangd::CodeCompleteOptions &Opts,
188                                 Callback<CodeCompleteResult> CB) {
189   // Copy completion options for passing them to async task handler.
190   auto CodeCompleteOpts = Opts;
191   if (!CodeCompleteOpts.Index) // Respect overridden index.
192     CodeCompleteOpts.Index = Index;
193 
194   auto Task = [Pos, FS = FSProvider.getFileSystem(), CodeCompleteOpts,
195                File = File.str(), CB = std::move(CB),
196                this](llvm::Expected<InputsAndPreamble> IP) mutable {
197     if (!IP)
198       return CB(IP.takeError());
199     if (isCancelled())
200       return CB(llvm::make_error<CancelledError>());
201 
202     llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind;
203     if (!IP->Preamble) {
204       // No speculation in Fallback mode, as it's supposed to be much faster
205       // without compiling.
206       vlog("Build for file {0} is not ready. Enter fallback mode.", File);
207     } else {
208       if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) {
209         SpecFuzzyFind.emplace();
210         {
211           std::lock_guard<std::mutex> Lock(
212               CachedCompletionFuzzyFindRequestMutex);
213           SpecFuzzyFind->CachedReq =
214               CachedCompletionFuzzyFindRequestByFile[File];
215         }
216       }
217     }
218     // FIXME(ibiryukov): even if Preamble is non-null, we may want to check
219     // both the old and the new version in case only one of them matches.
220     CodeCompleteResult Result = clangd::codeComplete(
221         File, IP->Command, IP->Preamble, IP->Contents, Pos, FS,
222         CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr);
223     {
224       clang::clangd::trace::Span Tracer("Completion results callback");
225       CB(std::move(Result));
226     }
227     if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) {
228       std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex);
229       CachedCompletionFuzzyFindRequestByFile[File] =
230           SpecFuzzyFind->NewReq.getValue();
231     }
232     // SpecFuzzyFind is only destroyed after speculative fuzzy find finishes.
233     // We don't want `codeComplete` to wait for the async call if it doesn't use
234     // the result (e.g. non-index completion, speculation fails), so that `CB`
235     // is called as soon as results are available.
236   };
237 
238   // We use a potentially-stale preamble because latency is critical here.
239   WorkScheduler.runWithPreamble(
240       "CodeComplete", File,
241       (Opts.RunParser == CodeCompleteOptions::AlwaysParse)
242           ? TUScheduler::Stale
243           : TUScheduler::StaleOrAbsent,
244       std::move(Task));
245 }
246 
signatureHelp(PathRef File,Position Pos,Callback<SignatureHelp> CB)247 void ClangdServer::signatureHelp(PathRef File, Position Pos,
248                                  Callback<SignatureHelp> CB) {
249 
250   auto Action = [Pos, FS = FSProvider.getFileSystem(), File = File.str(),
251                  CB = std::move(CB),
252                  this](llvm::Expected<InputsAndPreamble> IP) mutable {
253     if (!IP)
254       return CB(IP.takeError());
255 
256     auto PreambleData = IP->Preamble;
257     CB(clangd::signatureHelp(File, IP->Command, PreambleData, IP->Contents, Pos,
258                              FS, Index));
259   };
260 
261   // Unlike code completion, we wait for an up-to-date preamble here.
262   // Signature help is often triggered after code completion. If the code
263   // completion inserted a header to make the symbol available, then using
264   // the old preamble would yield useless results.
265   WorkScheduler.runWithPreamble("SignatureHelp", File, TUScheduler::Consistent,
266                                 std::move(Action));
267 }
268 
269 llvm::Expected<tooling::Replacements>
formatRange(llvm::StringRef Code,PathRef File,Range Rng)270 ClangdServer::formatRange(llvm::StringRef Code, PathRef File, Range Rng) {
271   llvm::Expected<size_t> Begin = positionToOffset(Code, Rng.start);
272   if (!Begin)
273     return Begin.takeError();
274   llvm::Expected<size_t> End = positionToOffset(Code, Rng.end);
275   if (!End)
276     return End.takeError();
277   return formatCode(Code, File, {tooling::Range(*Begin, *End - *Begin)});
278 }
279 
280 llvm::Expected<tooling::Replacements>
formatFile(llvm::StringRef Code,PathRef File)281 ClangdServer::formatFile(llvm::StringRef Code, PathRef File) {
282   // Format everything.
283   return formatCode(Code, File, {tooling::Range(0, Code.size())});
284 }
285 
286 llvm::Expected<std::vector<TextEdit>>
formatOnType(llvm::StringRef Code,PathRef File,Position Pos,StringRef TriggerText)287 ClangdServer::formatOnType(llvm::StringRef Code, PathRef File, Position Pos,
288                            StringRef TriggerText) {
289   llvm::Expected<size_t> CursorPos = positionToOffset(Code, Pos);
290   if (!CursorPos)
291     return CursorPos.takeError();
292   auto FS = FSProvider.getFileSystem();
293   auto Style = format::getStyle(format::DefaultFormatStyle, File,
294                                 format::DefaultFallbackStyle, Code, FS.get());
295   if (!Style)
296     return Style.takeError();
297 
298   std::vector<TextEdit> Result;
299   for (const tooling::Replacement &R :
300        formatIncremental(Code, *CursorPos, TriggerText, *Style))
301     Result.push_back(replacementToEdit(Code, R));
302   return Result;
303 }
304 
prepareRename(PathRef File,Position Pos,Callback<llvm::Optional<Range>> CB)305 void ClangdServer::prepareRename(PathRef File, Position Pos,
306                                  Callback<llvm::Optional<Range>> CB) {
307   auto Action = [Pos, File = File.str(), CB = std::move(CB),
308                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
309     if (!InpAST)
310       return CB(InpAST.takeError());
311     auto &AST = InpAST->AST;
312     const auto &SM = AST.getSourceManager();
313     SourceLocation Loc =
314         SM.getMacroArgExpandedLocation(getBeginningOfIdentifier(
315             Pos, AST.getSourceManager(), AST.getLangOpts()));
316     auto Range = getTokenRange(SM, AST.getLangOpts(), Loc);
317     if (!Range)
318       return CB(llvm::None); // "rename" is not valid at the position.
319 
320     if (CrossFileRename)
321       // FIXME: we now assume cross-file rename always succeeds, revisit this.
322       return CB(*Range);
323 
324     // Performing the local rename isn't substantially more expensive than
325     // doing an AST-based check, so we just rename and throw away the results.
326     auto Changes = clangd::rename({Pos, "dummy", AST, File, Index,
327                                    /*AllowCrossFile=*/false,
328                                    /*GetDirtyBuffer=*/nullptr});
329     if (!Changes) {
330       // LSP says to return null on failure, but that will result in a generic
331       // failure message. If we send an LSP error response, clients can surface
332       // the message to users (VSCode does).
333       return CB(Changes.takeError());
334     }
335     return CB(*Range);
336   };
337   WorkScheduler.runWithAST("PrepareRename", File, std::move(Action));
338 }
339 
rename(PathRef File,Position Pos,llvm::StringRef NewName,bool WantFormat,Callback<FileEdits> CB)340 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
341                           bool WantFormat, Callback<FileEdits> CB) {
342   // A snapshot of all file dirty buffers.
343   llvm::StringMap<std::string> Snapshot = WorkScheduler.getAllFileContents();
344   auto Action = [File = File.str(), NewName = NewName.str(), Pos, WantFormat,
345                  CB = std::move(CB), Snapshot = std::move(Snapshot),
346                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
347     if (!InpAST)
348       return CB(InpAST.takeError());
349     auto GetDirtyBuffer =
350         [&Snapshot](PathRef AbsPath) -> llvm::Optional<std::string> {
351       auto It = Snapshot.find(AbsPath);
352       if (It == Snapshot.end())
353         return llvm::None;
354       return It->second;
355     };
356     auto Edits = clangd::rename({Pos, NewName, InpAST->AST, File, Index,
357                                  CrossFileRename, GetDirtyBuffer});
358     if (!Edits)
359       return CB(Edits.takeError());
360 
361     if (WantFormat) {
362       auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
363                                          InpAST->Inputs.FS.get());
364       llvm::Error Err = llvm::Error::success();
365       for (auto &E : *Edits)
366         Err =
367             llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err));
368 
369       if (Err)
370         return CB(std::move(Err));
371     }
372     return CB(std::move(*Edits));
373   };
374   WorkScheduler.runWithAST("Rename", File, std::move(Action));
375 }
376 
377 static llvm::Expected<Tweak::Selection>
tweakSelection(const Range & Sel,const InputsAndAST & AST)378 tweakSelection(const Range &Sel, const InputsAndAST &AST) {
379   auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start);
380   if (!Begin)
381     return Begin.takeError();
382   auto End = positionToOffset(AST.Inputs.Contents, Sel.end);
383   if (!End)
384     return End.takeError();
385   return Tweak::Selection(AST.Inputs.Index, AST.AST, *Begin, *End);
386 }
387 
enumerateTweaks(PathRef File,Range Sel,Callback<std::vector<TweakRef>> CB)388 void ClangdServer::enumerateTweaks(PathRef File, Range Sel,
389                                    Callback<std::vector<TweakRef>> CB) {
390   auto Action = [File = File.str(), Sel, CB = std::move(CB),
391                  this](Expected<InputsAndAST> InpAST) mutable {
392     if (!InpAST)
393       return CB(InpAST.takeError());
394     auto Selection = tweakSelection(Sel, *InpAST);
395     if (!Selection)
396       return CB(Selection.takeError());
397     std::vector<TweakRef> Res;
398     for (auto &T : prepareTweaks(*Selection, TweakFilter))
399       Res.push_back({T->id(), T->title(), T->intent()});
400 
401     CB(std::move(Res));
402   };
403 
404   WorkScheduler.runWithAST("EnumerateTweaks", File, std::move(Action));
405 }
406 
applyTweak(PathRef File,Range Sel,StringRef TweakID,Callback<Tweak::Effect> CB)407 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
408                               Callback<Tweak::Effect> CB) {
409   auto Action =
410       [File = File.str(), Sel, TweakID = TweakID.str(), CB = std::move(CB),
411        FS = FSProvider.getFileSystem()](Expected<InputsAndAST> InpAST) mutable {
412         if (!InpAST)
413           return CB(InpAST.takeError());
414         auto Selection = tweakSelection(Sel, *InpAST);
415         if (!Selection)
416           return CB(Selection.takeError());
417         auto A = prepareTweak(TweakID, *Selection);
418         if (!A)
419           return CB(A.takeError());
420         auto Effect = (*A)->apply(*Selection);
421         if (!Effect)
422           return CB(Effect.takeError());
423         for (auto &It : Effect->ApplyEdits) {
424           Edit &E = It.second;
425           format::FormatStyle Style =
426               getFormatStyleForFile(File, E.InitialCode, FS.get());
427           if (llvm::Error Err = reformatEdit(E, Style))
428             elog("Failed to format {0}: {1}", It.first(), std::move(Err));
429         }
430         return CB(std::move(*Effect));
431       };
432   WorkScheduler.runWithAST("ApplyTweak", File, std::move(Action));
433 }
434 
dumpAST(PathRef File,llvm::unique_function<void (std::string)> Callback)435 void ClangdServer::dumpAST(PathRef File,
436                            llvm::unique_function<void(std::string)> Callback) {
437   auto Action = [Callback = std::move(Callback)](
438                     llvm::Expected<InputsAndAST> InpAST) mutable {
439     if (!InpAST) {
440       llvm::consumeError(InpAST.takeError());
441       return Callback("<no-ast>");
442     }
443     std::string Result;
444 
445     llvm::raw_string_ostream ResultOS(Result);
446     clangd::dumpAST(InpAST->AST, ResultOS);
447     ResultOS.flush();
448 
449     Callback(Result);
450   };
451 
452   WorkScheduler.runWithAST("DumpAST", File, std::move(Action));
453 }
454 
locateSymbolAt(PathRef File,Position Pos,Callback<std::vector<LocatedSymbol>> CB)455 void ClangdServer::locateSymbolAt(PathRef File, Position Pos,
456                                   Callback<std::vector<LocatedSymbol>> CB) {
457   auto Action = [Pos, CB = std::move(CB),
458                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
459     if (!InpAST)
460       return CB(InpAST.takeError());
461     CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index));
462   };
463 
464   WorkScheduler.runWithAST("Definitions", File, std::move(Action));
465 }
466 
switchSourceHeader(PathRef Path,Callback<llvm::Optional<clangd::Path>> CB)467 void ClangdServer::switchSourceHeader(
468     PathRef Path, Callback<llvm::Optional<clangd::Path>> CB) {
469   // We want to return the result as fast as possible, strategy is:
470   //  1) use the file-only heuristic, it requires some IO but it is much
471   //     faster than building AST, but it only works when .h/.cc files are in
472   //     the same directory.
473   //  2) if 1) fails, we use the AST&Index approach, it is slower but supports
474   //     different code layout.
475   if (auto CorrespondingFile =
476           getCorrespondingHeaderOrSource(Path, FSProvider.getFileSystem()))
477     return CB(std::move(CorrespondingFile));
478   auto Action = [Path = Path.str(), CB = std::move(CB),
479                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
480     if (!InpAST)
481       return CB(InpAST.takeError());
482     CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index));
483   };
484   WorkScheduler.runWithAST("SwitchHeaderSource", Path, std::move(Action));
485 }
486 
487 llvm::Expected<tooling::Replacements>
formatCode(llvm::StringRef Code,PathRef File,llvm::ArrayRef<tooling::Range> Ranges)488 ClangdServer::formatCode(llvm::StringRef Code, PathRef File,
489                          llvm::ArrayRef<tooling::Range> Ranges) {
490   // Call clang-format.
491   format::FormatStyle Style =
492       getFormatStyleForFile(File, Code, FSProvider.getFileSystem().get());
493   tooling::Replacements IncludeReplaces =
494       format::sortIncludes(Style, Code, Ranges, File);
495   auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces);
496   if (!Changed)
497     return Changed.takeError();
498 
499   return IncludeReplaces.merge(format::reformat(
500       Style, *Changed,
501       tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges),
502       File));
503 }
504 
findDocumentHighlights(PathRef File,Position Pos,Callback<std::vector<DocumentHighlight>> CB)505 void ClangdServer::findDocumentHighlights(
506     PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) {
507   auto Action =
508       [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
509         if (!InpAST)
510           return CB(InpAST.takeError());
511         CB(clangd::findDocumentHighlights(InpAST->AST, Pos));
512       };
513 
514   WorkScheduler.runWithAST("Highlights", File, std::move(Action));
515 }
516 
findHover(PathRef File,Position Pos,Callback<llvm::Optional<HoverInfo>> CB)517 void ClangdServer::findHover(PathRef File, Position Pos,
518                              Callback<llvm::Optional<HoverInfo>> CB) {
519   auto Action = [File = File.str(), Pos, CB = std::move(CB),
520                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
521     if (!InpAST)
522       return CB(InpAST.takeError());
523     format::FormatStyle Style = getFormatStyleForFile(
524         File, InpAST->Inputs.Contents, InpAST->Inputs.FS.get());
525     CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index));
526   };
527 
528   WorkScheduler.runWithAST("Hover", File, std::move(Action));
529 }
530 
typeHierarchy(PathRef File,Position Pos,int Resolve,TypeHierarchyDirection Direction,Callback<Optional<TypeHierarchyItem>> CB)531 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
532                                  TypeHierarchyDirection Direction,
533                                  Callback<Optional<TypeHierarchyItem>> CB) {
534   auto Action = [File = File.str(), Pos, Resolve, Direction, CB = std::move(CB),
535                  this](Expected<InputsAndAST> InpAST) mutable {
536     if (!InpAST)
537       return CB(InpAST.takeError());
538     CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index,
539                                 File));
540   };
541 
542   WorkScheduler.runWithAST("Type Hierarchy", File, std::move(Action));
543 }
544 
resolveTypeHierarchy(TypeHierarchyItem Item,int Resolve,TypeHierarchyDirection Direction,Callback<llvm::Optional<TypeHierarchyItem>> CB)545 void ClangdServer::resolveTypeHierarchy(
546     TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction,
547     Callback<llvm::Optional<TypeHierarchyItem>> CB) {
548   clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index);
549   CB(Item);
550 }
551 
onFileEvent(const DidChangeWatchedFilesParams & Params)552 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) {
553   // FIXME: Do nothing for now. This will be used for indexing and potentially
554   // invalidating other caches.
555 }
556 
workspaceSymbols(llvm::StringRef Query,int Limit,Callback<std::vector<SymbolInformation>> CB)557 void ClangdServer::workspaceSymbols(
558     llvm::StringRef Query, int Limit,
559     Callback<std::vector<SymbolInformation>> CB) {
560   WorkScheduler.run(
561       "getWorkspaceSymbols",
562       [Query = Query.str(), Limit, CB = std::move(CB), this]() mutable {
563         CB(clangd::getWorkspaceSymbols(Query, Limit, Index,
564                                        WorkspaceRoot.getValueOr("")));
565       });
566 }
567 
documentSymbols(llvm::StringRef File,Callback<std::vector<DocumentSymbol>> CB)568 void ClangdServer::documentSymbols(llvm::StringRef File,
569                                    Callback<std::vector<DocumentSymbol>> CB) {
570   auto Action =
571       [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
572         if (!InpAST)
573           return CB(InpAST.takeError());
574         CB(clangd::getDocumentSymbols(InpAST->AST));
575       };
576   WorkScheduler.runWithAST("documentSymbols", File, std::move(Action));
577 }
578 
findReferences(PathRef File,Position Pos,uint32_t Limit,Callback<ReferencesResult> CB)579 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit,
580                                   Callback<ReferencesResult> CB) {
581   auto Action = [Pos, Limit, CB = std::move(CB),
582                  this](llvm::Expected<InputsAndAST> InpAST) mutable {
583     if (!InpAST)
584       return CB(InpAST.takeError());
585     CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index));
586   };
587 
588   WorkScheduler.runWithAST("References", File, std::move(Action));
589 }
590 
symbolInfo(PathRef File,Position Pos,Callback<std::vector<SymbolDetails>> CB)591 void ClangdServer::symbolInfo(PathRef File, Position Pos,
592                               Callback<std::vector<SymbolDetails>> CB) {
593   auto Action =
594       [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
595         if (!InpAST)
596           return CB(InpAST.takeError());
597         CB(clangd::getSymbolInfo(InpAST->AST, Pos));
598       };
599 
600   WorkScheduler.runWithAST("SymbolInfo", File, std::move(Action));
601 }
602 
semanticRanges(PathRef File,Position Pos,Callback<std::vector<Range>> CB)603 void ClangdServer::semanticRanges(PathRef File, Position Pos,
604                                   Callback<std::vector<Range>> CB) {
605   auto Action =
606       [Pos, CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
607         if (!InpAST)
608           return CB(InpAST.takeError());
609         CB(clangd::getSemanticRanges(InpAST->AST, Pos));
610       };
611   WorkScheduler.runWithAST("SemanticRanges", File, std::move(Action));
612 }
613 
documentLinks(PathRef File,Callback<std::vector<DocumentLink>> CB)614 void ClangdServer::documentLinks(PathRef File,
615                                  Callback<std::vector<DocumentLink>> CB) {
616   auto Action =
617       [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable {
618         if (!InpAST)
619           return CB(InpAST.takeError());
620         CB(clangd::getDocumentLinks(InpAST->AST));
621       };
622   WorkScheduler.runWithAST("DocumentLinks", File, std::move(Action));
623 }
624 
625 std::vector<std::pair<Path, std::size_t>>
getUsedBytesPerFile() const626 ClangdServer::getUsedBytesPerFile() const {
627   return WorkScheduler.getUsedBytesPerFile();
628 }
629 
630 LLVM_NODISCARD bool
blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds)631 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) {
632   return WorkScheduler.blockUntilIdle(timeoutSeconds(TimeoutSeconds)) &&
633          (!BackgroundIdx ||
634           BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds));
635 }
636 
637 } // namespace clangd
638 } // namespace clang
639