| //===--- 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, llvm::Optional<Range> Rng) { |
| llvm::Optional<llvm::Expected<tooling::Replacements>> Result; |
| Server.formatFile(File, Rng, 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 |