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, std::optional<Range> Rng) { |
120 | std::optional<llvm::Expected<tooling::Replacements>> Result; |
121 | Server.formatFile(File, Rng, CB: capture(Target&: Result)); |
122 | return std::move(*Result); |
123 | } |
124 | |
125 | SymbolSlab runFuzzyFind(const SymbolIndex &Index, llvm::StringRef Query) { |
126 | FuzzyFindRequest Req; |
127 | Req.Query = std::string(Query); |
128 | Req.AnyScope = true; |
129 | return runFuzzyFind(Index, Req); |
130 | } |
131 | |
132 | SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req) { |
133 | SymbolSlab::Builder Builder; |
134 | Index.fuzzyFind(Req, Callback: [&](const Symbol &Sym) { Builder.insert(S: Sym); }); |
135 | return std::move(Builder).build(); |
136 | } |
137 | |
138 | RefSlab getRefs(const SymbolIndex &Index, SymbolID ID) { |
139 | RefsRequest Req; |
140 | Req.IDs = {ID}; |
141 | RefSlab::Builder Slab; |
142 | Index.refs(Req, Callback: [&](const Ref &S) { Slab.insert(ID, S); }); |
143 | return std::move(Slab).build(); |
144 | } |
145 | |
146 | llvm::Expected<std::vector<SelectionRange>> |
147 | runSemanticRanges(ClangdServer &Server, PathRef File, |
148 | const std::vector<Position> &Pos) { |
149 | std::optional<llvm::Expected<std::vector<SelectionRange>>> Result; |
150 | Server.semanticRanges(File, Pos, CB: capture(Target&: Result)); |
151 | return std::move(*Result); |
152 | } |
153 | |
154 | llvm::Expected<std::optional<clangd::Path>> |
155 | (ClangdServer &Server, PathRef File) { |
156 | std::optional<llvm::Expected<std::optional<clangd::Path>>> Result; |
157 | Server.switchSourceHeader(Path: File, CB: capture(Target&: Result)); |
158 | return std::move(*Result); |
159 | } |
160 | |
161 | llvm::Error runCustomAction(ClangdServer &Server, PathRef File, |
162 | llvm::function_ref<void(InputsAndAST)> Action) { |
163 | llvm::Error Result = llvm::Error::success(); |
164 | Notification Done; |
165 | Server.customAction(File, Name: "Custom" , Action: [&](llvm::Expected<InputsAndAST> AST) { |
166 | if (!AST) |
167 | Result = AST.takeError(); |
168 | else |
169 | Action(*AST); |
170 | Done.notify(); |
171 | }); |
172 | Done.wait(); |
173 | return Result; |
174 | } |
175 | |
176 | } // namespace clangd |
177 | } // namespace clang |
178 | |