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