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) { 31 assert(!Target.hasValue()); 32 } 33 34 CaptureProxy(const CaptureProxy &) = delete; 35 CaptureProxy &operator=(const CaptureProxy &) = delete; 36 // We need move ctor to return a value from the 'capture' helper. 37 CaptureProxy(CaptureProxy &&Other) : Target(Other.Target) { 38 Other.Target = nullptr; 39 } 40 CaptureProxy &operator=(CaptureProxy &&) = delete; 41 42 operator llvm::unique_function<void(T)>() && { 43 assert(!Future.valid() && "conversion to callback called multiple times"); 44 Future = Promise.get_future(); 45 return [Promise = std::move(Promise)](T Value) mutable { 46 Promise.set_value(std::make_shared<T>(std::move(Value))); 47 }; 48 } 49 50 ~CaptureProxy() { 51 if (!Target) 52 return; 53 assert(Future.valid() && "conversion to callback was not called"); 54 assert(!Target->hasValue()); 55 Target->emplace(std::move(*Future.get())); 56 } 57 58 private: 59 llvm::Optional<T> *Target; 60 // Using shared_ptr to workaround compilation errors with MSVC. 61 // MSVC only allows default-constructible and copyable objects as future<> 62 // arguments. 63 std::promise<std::shared_ptr<T>> Promise; 64 std::future<std::shared_ptr<T>> Future; 65 }; 66 67 template <typename T> CaptureProxy<T> capture(llvm::Optional<T> &Target) { 68 return CaptureProxy<T>(Target); 69 } 70 } // namespace 71 72 llvm::Expected<CodeCompleteResult> 73 runCodeComplete(ClangdServer &Server, PathRef File, Position Pos, 74 clangd::CodeCompleteOptions Opts) { 75 llvm::Optional<llvm::Expected<CodeCompleteResult>> Result; 76 Server.codeComplete(File, Pos, Opts, capture(Result)); 77 return std::move(*Result); 78 } 79 80 llvm::Expected<SignatureHelp> runSignatureHelp(ClangdServer &Server, 81 PathRef File, Position Pos, 82 MarkupKind DocumentationFormat) { 83 llvm::Optional<llvm::Expected<SignatureHelp>> Result; 84 Server.signatureHelp(File, Pos, DocumentationFormat, capture(Result)); 85 return std::move(*Result); 86 } 87 88 llvm::Expected<std::vector<LocatedSymbol>> 89 runLocateSymbolAt(ClangdServer &Server, PathRef File, Position Pos) { 90 llvm::Optional<llvm::Expected<std::vector<LocatedSymbol>>> Result; 91 Server.locateSymbolAt(File, Pos, capture(Result)); 92 return std::move(*Result); 93 } 94 95 llvm::Expected<std::vector<DocumentHighlight>> 96 runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos) { 97 llvm::Optional<llvm::Expected<std::vector<DocumentHighlight>>> Result; 98 Server.findDocumentHighlights(File, Pos, capture(Result)); 99 return std::move(*Result); 100 } 101 102 llvm::Expected<RenameResult> runRename(ClangdServer &Server, PathRef File, 103 Position Pos, llvm::StringRef NewName, 104 const RenameOptions &RenameOpts) { 105 llvm::Optional<llvm::Expected<RenameResult>> Result; 106 Server.rename(File, Pos, NewName, RenameOpts, capture(Result)); 107 return std::move(*Result); 108 } 109 110 llvm::Expected<RenameResult> 111 runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, 112 llvm::Optional<std::string> NewName, 113 const RenameOptions &RenameOpts) { 114 llvm::Optional<llvm::Expected<RenameResult>> Result; 115 Server.prepareRename(File, Pos, NewName, RenameOpts, capture(Result)); 116 return std::move(*Result); 117 } 118 119 llvm::Expected<tooling::Replacements> 120 runFormatFile(ClangdServer &Server, PathRef File, llvm::Optional<Range> Rng) { 121 llvm::Optional<llvm::Expected<tooling::Replacements>> Result; 122 Server.formatFile(File, Rng, capture(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, [&](const Symbol &Sym) { Builder.insert(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, [&](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 llvm::Optional<llvm::Expected<std::vector<SelectionRange>>> Result; 151 Server.semanticRanges(File, Pos, capture(Result)); 152 return std::move(*Result); 153 } 154 155 llvm::Expected<llvm::Optional<clangd::Path>> 156 runSwitchHeaderSource(ClangdServer &Server, PathRef File) { 157 llvm::Optional<llvm::Expected<llvm::Optional<clangd::Path>>> Result; 158 Server.switchSourceHeader(File, capture(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, "Custom", [&](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