|  | //===--- SyncAPI.cpp - Sync version of ClangdServer's API --------*- C++-*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "SyncAPI.h" | 
|  | #include "index/Index.h" | 
|  |  | 
|  | namespace clang { | 
|  | namespace clangd { | 
|  |  | 
|  | void runAddDocument(ClangdServer &Server, PathRef File, | 
|  | llvm::StringRef Contents, llvm::StringRef Version, | 
|  | WantDiagnostics WantDiags, bool ForceRebuild) { | 
|  | Server.addDocument(File, Contents, Version, WantDiags, ForceRebuild); | 
|  | if (!Server.blockUntilIdleForTest()) | 
|  | llvm_unreachable("not idle after addDocument"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | /// A helper that waits for async callbacks to fire and exposes their result in | 
|  | /// the output variable. Intended to be used in the following way: | 
|  | ///    T Result; | 
|  | ///    someAsyncFunc(Param1, Param2, /*Callback=*/capture(Result)); | 
|  | template <typename T> struct CaptureProxy { | 
|  | CaptureProxy(llvm::Optional<T> &Target) : Target(&Target) { | 
|  | assert(!Target.hasValue()); | 
|  | } | 
|  |  | 
|  | CaptureProxy(const CaptureProxy &) = delete; | 
|  | CaptureProxy &operator=(const CaptureProxy &) = delete; | 
|  | // We need move ctor to return a value from the 'capture' helper. | 
|  | CaptureProxy(CaptureProxy &&Other) : Target(Other.Target) { | 
|  | Other.Target = nullptr; | 
|  | } | 
|  | CaptureProxy &operator=(CaptureProxy &&) = delete; | 
|  |  | 
|  | operator llvm::unique_function<void(T)>() && { | 
|  | assert(!Future.valid() && "conversion to callback called multiple times"); | 
|  | Future = Promise.get_future(); | 
|  | return [Promise = std::move(Promise)](T Value) mutable { | 
|  | Promise.set_value(std::make_shared<T>(std::move(Value))); | 
|  | }; | 
|  | } | 
|  |  | 
|  | ~CaptureProxy() { | 
|  | if (!Target) | 
|  | return; | 
|  | assert(Future.valid() && "conversion to callback was not called"); | 
|  | assert(!Target->hasValue()); | 
|  | Target->emplace(std::move(*Future.get())); | 
|  | } | 
|  |  | 
|  | private: | 
|  | llvm::Optional<T> *Target; | 
|  | // Using shared_ptr to workaround compilation errors with MSVC. | 
|  | // MSVC only allows default-constructible and copyable objects as future<> | 
|  | // arguments. | 
|  | std::promise<std::shared_ptr<T>> Promise; | 
|  | std::future<std::shared_ptr<T>> Future; | 
|  | }; | 
|  |  | 
|  | template <typename T> CaptureProxy<T> capture(llvm::Optional<T> &Target) { | 
|  | return CaptureProxy<T>(Target); | 
|  | } | 
|  | } // namespace | 
|  |  | 
|  | llvm::Expected<CodeCompleteResult> | 
|  | runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, | 
|  | clangd::CodeCompleteOptions Opts) { | 
|  | llvm::Optional<llvm::Expected<CodeCompleteResult>> Result; | 
|  | Server.codeComplete(File, Pos, Opts, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<SignatureHelp> runSignatureHelp(ClangdServer &Server, | 
|  | PathRef File, Position Pos) { | 
|  | llvm::Optional<llvm::Expected<SignatureHelp>> Result; | 
|  | Server.signatureHelp(File, Pos, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<std::vector<LocatedSymbol>> | 
|  | runLocateSymbolAt(ClangdServer &Server, PathRef File, Position Pos) { | 
|  | llvm::Optional<llvm::Expected<std::vector<LocatedSymbol>>> Result; | 
|  | Server.locateSymbolAt(File, Pos, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<std::vector<DocumentHighlight>> | 
|  | runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos) { | 
|  | llvm::Optional<llvm::Expected<std::vector<DocumentHighlight>>> Result; | 
|  | Server.findDocumentHighlights(File, Pos, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<RenameResult> runRename(ClangdServer &Server, PathRef File, | 
|  | Position Pos, llvm::StringRef NewName, | 
|  | const RenameOptions &RenameOpts) { | 
|  | llvm::Optional<llvm::Expected<RenameResult>> Result; | 
|  | Server.rename(File, Pos, NewName, RenameOpts, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<RenameResult> | 
|  | runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, | 
|  | llvm::Optional<std::string> NewName, | 
|  | const RenameOptions &RenameOpts) { | 
|  | llvm::Optional<llvm::Expected<RenameResult>> Result; | 
|  | Server.prepareRename(File, Pos, NewName, RenameOpts, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<tooling::Replacements> | 
|  | runFormatFile(ClangdServer &Server, PathRef File, StringRef Code) { | 
|  | llvm::Optional<llvm::Expected<tooling::Replacements>> Result; | 
|  | Server.formatFile(File, Code, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query) { | 
|  | FuzzyFindRequest Req; | 
|  | Req.Query = std::string(Query); | 
|  | Req.AnyScope = true; | 
|  | return runFuzzyFind(Index, Req); | 
|  | } | 
|  |  | 
|  | SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req) { | 
|  | SymbolSlab::Builder Builder; | 
|  | Index.fuzzyFind(Req, [&](const Symbol &Sym) { Builder.insert(Sym); }); | 
|  | return std::move(Builder).build(); | 
|  | } | 
|  |  | 
|  | RefSlab getRefs(const SymbolIndex &Index, SymbolID ID) { | 
|  | RefsRequest Req; | 
|  | Req.IDs = {ID}; | 
|  | RefSlab::Builder Slab; | 
|  | Index.refs(Req, [&](const Ref &S) { Slab.insert(ID, S); }); | 
|  | return std::move(Slab).build(); | 
|  | } | 
|  |  | 
|  | llvm::Expected<std::vector<SelectionRange>> | 
|  | runSemanticRanges(ClangdServer &Server, PathRef File, | 
|  | const std::vector<Position> &Pos) { | 
|  | llvm::Optional<llvm::Expected<std::vector<SelectionRange>>> Result; | 
|  | Server.semanticRanges(File, Pos, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Expected<llvm::Optional<clangd::Path>> | 
|  | runSwitchHeaderSource(ClangdServer &Server, PathRef File) { | 
|  | llvm::Optional<llvm::Expected<llvm::Optional<clangd::Path>>> Result; | 
|  | Server.switchSourceHeader(File, capture(Result)); | 
|  | return std::move(*Result); | 
|  | } | 
|  |  | 
|  | llvm::Error runCustomAction(ClangdServer &Server, PathRef File, | 
|  | llvm::function_ref<void(InputsAndAST)> Action) { | 
|  | llvm::Error Result = llvm::Error::success(); | 
|  | Notification Done; | 
|  | Server.customAction(File, "Custom", [&](llvm::Expected<InputsAndAST> AST) { | 
|  | if (!AST) | 
|  | Result = AST.takeError(); | 
|  | else | 
|  | Action(*AST); | 
|  | Done.notify(); | 
|  | }); | 
|  | Done.wait(); | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | } // namespace clangd | 
|  | } // namespace clang |