| 1 | //===--- SyncAPI.cpp - Sync version of ClangdServer's API --------*- 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 "SyncAPI.h" |
| 10 | #include "Protocol.h" |
| 11 | #include "index/Index.h" |
| 12 | #include <optional> |
| 13 | |
| 14 | namespace clang { |
| 15 | namespace clangd { |
| 16 | |
| 17 | void runAddDocument(ClangdServer &Server, PathRef File, |
| 18 | llvm::StringRef Contents, llvm::StringRef Version, |
| 19 | WantDiagnostics WantDiags, bool ForceRebuild) { |
| 20 | Server.addDocument(File, Contents, Version, WD: WantDiags, ForceRebuild); |
| 21 | if (!Server.blockUntilIdleForTest()) |
| 22 | llvm_unreachable("not idle after addDocument" ); |
| 23 | } |
| 24 | |
| 25 | namespace { |
| 26 | /// A helper that waits for async callbacks to fire and exposes their result in |
| 27 | /// the output variable. Intended to be used in the following way: |
| 28 | /// T Result; |
| 29 | /// someAsyncFunc(Param1, Param2, /*Callback=*/capture(Result)); |
| 30 | template <typename T> struct CaptureProxy { |
| 31 | CaptureProxy(std::optional<T> &Target) : Target(&Target) { assert(!Target); } |
| 32 | |
| 33 | CaptureProxy(const CaptureProxy &) = delete; |
| 34 | CaptureProxy &operator=(const CaptureProxy &) = delete; |
| 35 | // We need move ctor to return a value from the 'capture' helper. |
| 36 | CaptureProxy(CaptureProxy &&Other) : Target(Other.Target) { |
| 37 | Other.Target = nullptr; |
| 38 | } |
| 39 | CaptureProxy &operator=(CaptureProxy &&) = delete; |
| 40 | |
| 41 | operator llvm::unique_function<void(T)>() && { |
| 42 | assert(!Future.valid() && "conversion to callback called multiple times" ); |
| 43 | Future = Promise.get_future(); |
| 44 | return [Promise = std::move(Promise)](T Value) mutable { |
| 45 | Promise.set_value(std::make_shared<T>(std::move(Value))); |
| 46 | }; |
| 47 | } |
| 48 | |
| 49 | ~CaptureProxy() { |
| 50 | if (!Target) |
| 51 | return; |
| 52 | assert(Future.valid() && "conversion to callback was not called" ); |
| 53 | assert(!Target->has_value()); |
| 54 | Target->emplace(std::move(*Future.get())); |
| 55 | } |
| 56 | |
| 57 | private: |
| 58 | std::optional<T> *Target; |
| 59 | // Using shared_ptr to workaround compilation errors with MSVC. |
| 60 | // MSVC only allows default-constructible and copyable objects as future<> |
| 61 | // arguments. |
| 62 | std::promise<std::shared_ptr<T>> Promise; |
| 63 | std::future<std::shared_ptr<T>> Future; |
| 64 | }; |
| 65 | |
| 66 | template <typename T> CaptureProxy<T> capture(std::optional<T> &Target) { |
| 67 | return CaptureProxy<T>(Target); |
| 68 | } |
| 69 | } // namespace |
| 70 | |
| 71 | llvm::Expected<CodeCompleteResult> |
| 72 | runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, |
| 73 | clangd::CodeCompleteOptions Opts) { |
| 74 | std::optional<llvm::Expected<CodeCompleteResult>> Result; |
| 75 | Server.codeComplete(File, Pos, Opts, CB: capture(Target&: Result)); |
| 76 | return std::move(*Result); |
| 77 | } |
| 78 | |
| 79 | llvm::Expected<SignatureHelp> runSignatureHelp(ClangdServer &Server, |
| 80 | PathRef File, Position Pos, |
| 81 | MarkupKind DocumentationFormat) { |
| 82 | std::optional<llvm::Expected<SignatureHelp>> Result; |
| 83 | Server.signatureHelp(File, Pos, DocumentationFormat, CB: capture(Target&: Result)); |
| 84 | return std::move(*Result); |
| 85 | } |
| 86 | |
| 87 | llvm::Expected<std::vector<LocatedSymbol>> |
| 88 | runLocateSymbolAt(ClangdServer &Server, PathRef File, Position Pos) { |
| 89 | std::optional<llvm::Expected<std::vector<LocatedSymbol>>> Result; |
| 90 | Server.locateSymbolAt(File, Pos, CB: capture(Target&: Result)); |
| 91 | return std::move(*Result); |
| 92 | } |
| 93 | |
| 94 | llvm::Expected<std::vector<DocumentHighlight>> |
| 95 | runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos) { |
| 96 | std::optional<llvm::Expected<std::vector<DocumentHighlight>>> Result; |
| 97 | Server.findDocumentHighlights(File, Pos, CB: capture(Target&: Result)); |
| 98 | return std::move(*Result); |
| 99 | } |
| 100 | |
| 101 | llvm::Expected<RenameResult> runRename(ClangdServer &Server, PathRef File, |
| 102 | Position Pos, llvm::StringRef NewName, |
| 103 | const RenameOptions &RenameOpts) { |
| 104 | std::optional<llvm::Expected<RenameResult>> Result; |
| 105 | Server.rename(File, Pos, NewName, Opts: RenameOpts, CB: capture(Target&: Result)); |
| 106 | return std::move(*Result); |
| 107 | } |
| 108 | |
| 109 | llvm::Expected<RenameResult> |
| 110 | runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, |
| 111 | std::optional<std::string> NewName, |
| 112 | const RenameOptions &RenameOpts) { |
| 113 | std::optional<llvm::Expected<RenameResult>> Result; |
| 114 | Server.prepareRename(File, Pos, NewName, RenameOpts, CB: capture(Target&: Result)); |
| 115 | return std::move(*Result); |
| 116 | } |
| 117 | |
| 118 | llvm::Expected<tooling::Replacements> |
| 119 | runFormatFile(ClangdServer &Server, PathRef File, |
| 120 | const std::vector<Range> &Rngs) { |
| 121 | std::optional<llvm::Expected<tooling::Replacements>> Result; |
| 122 | Server.formatFile(File, Rngs, CB: capture(Target&: Result)); |
| 123 | return std::move(*Result); |
| 124 | } |
| 125 | |
| 126 | SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query) { |
| 127 | FuzzyFindRequest Req; |
| 128 | Req.Query = std::string(Query); |
| 129 | Req.AnyScope = true; |
| 130 | return runFuzzyFind(Index, Req); |
| 131 | } |
| 132 | |
| 133 | SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req) { |
| 134 | SymbolSlab::Builder Builder; |
| 135 | Index.fuzzyFind(Req, Callback: [&](const Symbol &Sym) { Builder.insert(S: Sym); }); |
| 136 | return std::move(Builder).build(); |
| 137 | } |
| 138 | |
| 139 | RefSlab getRefs(const SymbolIndex &Index, SymbolID ID) { |
| 140 | RefsRequest Req; |
| 141 | Req.IDs = {ID}; |
| 142 | RefSlab::Builder Slab; |
| 143 | Index.refs(Req, Callback: [&](const Ref &S) { Slab.insert(ID, S); }); |
| 144 | return std::move(Slab).build(); |
| 145 | } |
| 146 | |
| 147 | llvm::Expected<std::vector<SelectionRange>> |
| 148 | runSemanticRanges(ClangdServer &Server, PathRef File, |
| 149 | const std::vector<Position> &Pos) { |
| 150 | std::optional<llvm::Expected<std::vector<SelectionRange>>> Result; |
| 151 | Server.semanticRanges(File, Pos, CB: capture(Target&: Result)); |
| 152 | return std::move(*Result); |
| 153 | } |
| 154 | |
| 155 | llvm::Expected<std::optional<clangd::Path>> |
| 156 | (ClangdServer &Server, PathRef File) { |
| 157 | std::optional<llvm::Expected<std::optional<clangd::Path>>> Result; |
| 158 | Server.switchSourceHeader(Path: File, CB: capture(Target&: Result)); |
| 159 | return std::move(*Result); |
| 160 | } |
| 161 | |
| 162 | llvm::Error runCustomAction(ClangdServer &Server, PathRef File, |
| 163 | llvm::function_ref<void(InputsAndAST)> Action) { |
| 164 | llvm::Error Result = llvm::Error::success(); |
| 165 | Notification Done; |
| 166 | Server.customAction(File, Name: "Custom" , Action: [&](llvm::Expected<InputsAndAST> AST) { |
| 167 | if (!AST) |
| 168 | Result = AST.takeError(); |
| 169 | else |
| 170 | Action(*AST); |
| 171 | Done.notify(); |
| 172 | }); |
| 173 | Done.wait(); |
| 174 | return Result; |
| 175 | } |
| 176 | |
| 177 | } // namespace clangd |
| 178 | } // namespace clang |
| 179 | |