Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/ClangdLSPServer.cpp @ 207:2e18cbf3894f
LLVM12
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 Jun 2021 06:07:14 +0900 |
parents | 0572611fdcc8 |
children | 5f17cb93ff66 |
comparison
equal
deleted
inserted
replaced
173:0572611fdcc8 | 207:2e18cbf3894f |
---|---|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 // | 6 // |
7 //===----------------------------------------------------------------------===// | 7 //===----------------------------------------------------------------------===// |
8 | 8 |
9 #include "ClangdLSPServer.h" | 9 #include "ClangdLSPServer.h" |
10 #include "ClangdServer.h" | |
10 #include "CodeComplete.h" | 11 #include "CodeComplete.h" |
11 #include "Diagnostics.h" | 12 #include "Diagnostics.h" |
12 #include "DraftStore.h" | 13 #include "DraftStore.h" |
14 #include "DumpAST.h" | |
13 #include "GlobalCompilationDatabase.h" | 15 #include "GlobalCompilationDatabase.h" |
16 #include "LSPBinder.h" | |
14 #include "Protocol.h" | 17 #include "Protocol.h" |
15 #include "SemanticHighlighting.h" | 18 #include "SemanticHighlighting.h" |
16 #include "SourceCode.h" | 19 #include "SourceCode.h" |
17 #include "TUScheduler.h" | 20 #include "TUScheduler.h" |
18 #include "URI.h" | 21 #include "URI.h" |
19 #include "refactor/Tweak.h" | 22 #include "refactor/Tweak.h" |
20 #include "support/Context.h" | 23 #include "support/Context.h" |
24 #include "support/MemoryTree.h" | |
21 #include "support/Trace.h" | 25 #include "support/Trace.h" |
26 #include "clang/AST/ASTContext.h" | |
22 #include "clang/Basic/Version.h" | 27 #include "clang/Basic/Version.h" |
23 #include "clang/Tooling/Core/Replacement.h" | 28 #include "clang/Tooling/Core/Replacement.h" |
24 #include "llvm/ADT/ArrayRef.h" | 29 #include "llvm/ADT/ArrayRef.h" |
25 #include "llvm/ADT/Optional.h" | 30 #include "llvm/ADT/Optional.h" |
26 #include "llvm/ADT/ScopeExit.h" | 31 #include "llvm/ADT/ScopeExit.h" |
27 #include "llvm/ADT/StringRef.h" | 32 #include "llvm/ADT/StringRef.h" |
28 #include "llvm/ADT/iterator_range.h" | 33 #include "llvm/ADT/iterator_range.h" |
34 #include "llvm/Support/Allocator.h" | |
29 #include "llvm/Support/Errc.h" | 35 #include "llvm/Support/Errc.h" |
30 #include "llvm/Support/Error.h" | 36 #include "llvm/Support/Error.h" |
31 #include "llvm/Support/FormatVariadic.h" | 37 #include "llvm/Support/FormatVariadic.h" |
32 #include "llvm/Support/JSON.h" | 38 #include "llvm/Support/JSON.h" |
33 #include "llvm/Support/Path.h" | 39 #include "llvm/Support/Path.h" |
34 #include "llvm/Support/SHA1.h" | 40 #include "llvm/Support/SHA1.h" |
35 #include "llvm/Support/ScopedPrinter.h" | 41 #include "llvm/Support/ScopedPrinter.h" |
42 #include "llvm/Support/raw_ostream.h" | |
43 #include <chrono> | |
36 #include <cstddef> | 44 #include <cstddef> |
45 #include <cstdint> | |
46 #include <functional> | |
37 #include <memory> | 47 #include <memory> |
38 #include <mutex> | 48 #include <mutex> |
39 #include <string> | 49 #include <string> |
40 #include <vector> | 50 #include <vector> |
41 | 51 |
42 namespace clang { | 52 namespace clang { |
43 namespace clangd { | 53 namespace clangd { |
44 namespace { | 54 namespace { |
45 | |
46 // Tracks end-to-end latency of high level lsp calls. Measurements are in | 55 // Tracks end-to-end latency of high level lsp calls. Measurements are in |
47 // seconds. | 56 // seconds. |
48 constexpr trace::Metric LSPLatency("lsp_latency", trace::Metric::Distribution, | 57 constexpr trace::Metric LSPLatency("lsp_latency", trace::Metric::Distribution, |
49 "method_name"); | 58 "method_name"); |
50 | 59 |
51 // LSP defines file versions as numbers that increase. | 60 // LSP defines file versions as numbers that increase. |
52 // ClangdServer treats them as opaque and therefore uses strings instead. | 61 // ClangdServer treats them as opaque and therefore uses strings instead. |
53 std::string encodeVersion(int64_t LSPVersion) { | 62 std::string encodeVersion(llvm::Optional<int64_t> LSPVersion) { |
54 return llvm::to_string(LSPVersion); | 63 return LSPVersion ? llvm::to_string(*LSPVersion) : ""; |
55 } | 64 } |
56 llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) { | 65 llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) { |
57 int64_t Result; | 66 int64_t Result; |
58 if (llvm::to_integer(Encoded, Result, 10)) | 67 if (llvm::to_integer(Encoded, Result, 10)) |
59 return Result; | 68 return Result; |
60 else if (!Encoded.empty()) // Empty can be e.g. diagnostics on close. | 69 if (!Encoded.empty()) // Empty can be e.g. diagnostics on close. |
61 elog("unexpected non-numeric version {0}", Encoded); | 70 elog("unexpected non-numeric version {0}", Encoded); |
62 return llvm::None; | 71 return llvm::None; |
63 } | 72 } |
73 | |
74 const llvm::StringLiteral APPLY_FIX_COMMAND = "clangd.applyFix"; | |
75 const llvm::StringLiteral APPLY_TWEAK_COMMAND = "clangd.applyTweak"; | |
64 | 76 |
65 /// Transforms a tweak into a code action that would apply it if executed. | 77 /// Transforms a tweak into a code action that would apply it if executed. |
66 /// EXPECTS: T.prepare() was called and returned true. | 78 /// EXPECTS: T.prepare() was called and returned true. |
67 CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File, | 79 CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File, |
68 Range Selection) { | 80 Range Selection) { |
69 CodeAction CA; | 81 CodeAction CA; |
70 CA.title = T.Title; | 82 CA.title = T.Title; |
71 switch (T.Intent) { | 83 CA.kind = T.Kind.str(); |
72 case Tweak::Refactor: | |
73 CA.kind = std::string(CodeAction::REFACTOR_KIND); | |
74 break; | |
75 case Tweak::Info: | |
76 CA.kind = std::string(CodeAction::INFO_KIND); | |
77 break; | |
78 } | |
79 // This tweak may have an expensive second stage, we only run it if the user | 84 // This tweak may have an expensive second stage, we only run it if the user |
80 // actually chooses it in the UI. We reply with a command that would run the | 85 // actually chooses it in the UI. We reply with a command that would run the |
81 // corresponding tweak. | 86 // corresponding tweak. |
82 // FIXME: for some tweaks, computing the edits is cheap and we could send them | 87 // FIXME: for some tweaks, computing the edits is cheap and we could send them |
83 // directly. | 88 // directly. |
84 CA.command.emplace(); | 89 CA.command.emplace(); |
85 CA.command->title = T.Title; | 90 CA.command->title = T.Title; |
86 CA.command->command = std::string(Command::CLANGD_APPLY_TWEAK); | 91 CA.command->command = std::string(APPLY_TWEAK_COMMAND); |
87 CA.command->tweakArgs.emplace(); | 92 TweakArgs Args; |
88 CA.command->tweakArgs->file = File; | 93 Args.file = File; |
89 CA.command->tweakArgs->tweakID = T.ID; | 94 Args.tweakID = T.ID; |
90 CA.command->tweakArgs->selection = Selection; | 95 Args.selection = Selection; |
96 CA.command->argument = std::move(Args); | |
91 return CA; | 97 return CA; |
92 } | 98 } |
93 | 99 |
94 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms, | 100 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms, |
95 SymbolKindBitset Kinds) { | 101 SymbolKindBitset Kinds) { |
113 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I) | 119 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I) |
114 Defaults.set(I); | 120 Defaults.set(I); |
115 return Defaults; | 121 return Defaults; |
116 } | 122 } |
117 | 123 |
118 // Build a lookup table (HighlightingKind => {TextMate Scopes}), which is sent | |
119 // to the LSP client. | |
120 std::vector<std::vector<std::string>> buildHighlightScopeLookupTable() { | |
121 std::vector<std::vector<std::string>> LookupTable; | |
122 // HighlightingKind is using as the index. | |
123 for (int KindValue = 0; KindValue <= (int)HighlightingKind::LastKind; | |
124 ++KindValue) | |
125 LookupTable.push_back( | |
126 {std::string(toTextMateScope((HighlightingKind)(KindValue)))}); | |
127 return LookupTable; | |
128 } | |
129 | |
130 // Makes sure edits in \p FE are applicable to latest file contents reported by | 124 // Makes sure edits in \p FE are applicable to latest file contents reported by |
131 // editor. If not generates an error message containing information about files | 125 // editor. If not generates an error message containing information about files |
132 // that needs to be saved. | 126 // that needs to be saved. |
133 llvm::Error validateEdits(const DraftStore &DraftMgr, const FileEdits &FE) { | 127 llvm::Error validateEdits(const ClangdServer &Server, const FileEdits &FE) { |
134 size_t InvalidFileCount = 0; | 128 size_t InvalidFileCount = 0; |
135 llvm::StringRef LastInvalidFile; | 129 llvm::StringRef LastInvalidFile; |
136 for (const auto &It : FE) { | 130 for (const auto &It : FE) { |
137 if (auto Draft = DraftMgr.getDraft(It.first())) { | 131 if (auto Draft = Server.getDraft(It.first())) { |
138 // If the file is open in user's editor, make sure the version we | 132 // If the file is open in user's editor, make sure the version we |
139 // saw and current version are compatible as this is the text that | 133 // saw and current version are compatible as this is the text that |
140 // will be replaced by editors. | 134 // will be replaced by editors. |
141 if (!It.second.canApplyTo(Draft->Contents)) { | 135 if (!It.second.canApplyTo(*Draft)) { |
142 ++InvalidFileCount; | 136 ++InvalidFileCount; |
143 LastInvalidFile = It.first(); | 137 LastInvalidFile = It.first(); |
144 } | 138 } |
145 } | 139 } |
146 } | 140 } |
147 if (!InvalidFileCount) | 141 if (!InvalidFileCount) |
148 return llvm::Error::success(); | 142 return llvm::Error::success(); |
149 if (InvalidFileCount == 1) | 143 if (InvalidFileCount == 1) |
150 return llvm::createStringError(llvm::inconvertibleErrorCode(), | 144 return error("File must be saved first: {0}", LastInvalidFile); |
151 "File must be saved first: " + | 145 return error("Files must be saved first: {0} (and {1} others)", |
152 LastInvalidFile); | 146 LastInvalidFile, InvalidFileCount - 1); |
153 return llvm::createStringError( | 147 } |
154 llvm::inconvertibleErrorCode(), | |
155 "Files must be saved first: " + LastInvalidFile + " (and " + | |
156 llvm::to_string(InvalidFileCount - 1) + " others)"); | |
157 } | |
158 | |
159 } // namespace | 148 } // namespace |
160 | 149 |
161 // MessageHandler dispatches incoming LSP messages. | 150 // MessageHandler dispatches incoming LSP messages. |
162 // It handles cross-cutting concerns: | 151 // It handles cross-cutting concerns: |
163 // - serializes/deserializes protocol objects to JSON | 152 // - serializes/deserializes protocol objects to JSON |
168 class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { | 157 class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { |
169 public: | 158 public: |
170 MessageHandler(ClangdLSPServer &Server) : Server(Server) {} | 159 MessageHandler(ClangdLSPServer &Server) : Server(Server) {} |
171 | 160 |
172 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { | 161 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { |
162 trace::Span Tracer(Method, LSPLatency); | |
163 SPAN_ATTACH(Tracer, "Params", Params); | |
173 WithContext HandlerContext(handlerContext()); | 164 WithContext HandlerContext(handlerContext()); |
174 log("<-- {0}", Method); | 165 log("<-- {0}", Method); |
175 if (Method == "exit") | 166 if (Method == "exit") |
176 return false; | 167 return false; |
177 if (!Server.Server) | 168 auto Handler = Server.Handlers.NotificationHandlers.find(Method); |
169 if (Handler != Server.Handlers.NotificationHandlers.end()) { | |
170 Handler->second(std::move(Params)); | |
171 Server.maybeExportMemoryProfile(); | |
172 Server.maybeCleanupMemory(); | |
173 } else if (!Server.Server) { | |
178 elog("Notification {0} before initialization", Method); | 174 elog("Notification {0} before initialization", Method); |
179 else if (Method == "$/cancelRequest") | 175 } else if (Method == "$/cancelRequest") { |
180 onCancel(std::move(Params)); | 176 onCancel(std::move(Params)); |
181 else if (auto Handler = Notifications.lookup(Method)) | 177 } else { |
182 Handler(std::move(Params)); | |
183 else | |
184 log("unhandled notification {0}", Method); | 178 log("unhandled notification {0}", Method); |
179 } | |
185 return true; | 180 return true; |
186 } | 181 } |
187 | 182 |
188 bool onCall(llvm::StringRef Method, llvm::json::Value Params, | 183 bool onCall(llvm::StringRef Method, llvm::json::Value Params, |
189 llvm::json::Value ID) override { | 184 llvm::json::Value ID) override { |
192 WithContext WithCancel(cancelableRequestContext(ID)); | 187 WithContext WithCancel(cancelableRequestContext(ID)); |
193 trace::Span Tracer(Method, LSPLatency); | 188 trace::Span Tracer(Method, LSPLatency); |
194 SPAN_ATTACH(Tracer, "Params", Params); | 189 SPAN_ATTACH(Tracer, "Params", Params); |
195 ReplyOnce Reply(ID, Method, &Server, Tracer.Args); | 190 ReplyOnce Reply(ID, Method, &Server, Tracer.Args); |
196 log("<-- {0}({1})", Method, ID); | 191 log("<-- {0}({1})", Method, ID); |
197 if (!Server.Server && Method != "initialize") { | 192 auto Handler = Server.Handlers.MethodHandlers.find(Method); |
193 if (Handler != Server.Handlers.MethodHandlers.end()) { | |
194 Handler->second(std::move(Params), std::move(Reply)); | |
195 } else if (!Server.Server) { | |
198 elog("Call {0} before initialization.", Method); | 196 elog("Call {0} before initialization.", Method); |
199 Reply(llvm::make_error<LSPError>("server not initialized", | 197 Reply(llvm::make_error<LSPError>("server not initialized", |
200 ErrorCode::ServerNotInitialized)); | 198 ErrorCode::ServerNotInitialized)); |
201 } else if (auto Handler = Calls.lookup(Method)) | 199 } else { |
202 Handler(std::move(Params), std::move(Reply)); | |
203 else | |
204 Reply(llvm::make_error<LSPError>("method not found", | 200 Reply(llvm::make_error<LSPError>("method not found", |
205 ErrorCode::MethodNotFound)); | 201 ErrorCode::MethodNotFound)); |
202 } | |
206 return true; | 203 return true; |
207 } | 204 } |
208 | 205 |
209 bool onReply(llvm::json::Value ID, | 206 bool onReply(llvm::json::Value ID, |
210 llvm::Expected<llvm::json::Value> Result) override { | 207 llvm::Expected<llvm::json::Value> Result) override { |
243 ReplyHandler(std::move(Err)); | 240 ReplyHandler(std::move(Err)); |
244 } | 241 } |
245 return true; | 242 return true; |
246 } | 243 } |
247 | 244 |
248 // Bind an LSP method name to a call. | |
249 template <typename Param, typename Result> | |
250 void bind(const char *Method, | |
251 void (ClangdLSPServer::*Handler)(const Param &, Callback<Result>)) { | |
252 Calls[Method] = [Method, Handler, this](llvm::json::Value RawParams, | |
253 ReplyOnce Reply) { | |
254 Param P; | |
255 if (fromJSON(RawParams, P)) { | |
256 (Server.*Handler)(P, std::move(Reply)); | |
257 } else { | |
258 elog("Failed to decode {0} request.", Method); | |
259 Reply(llvm::make_error<LSPError>("failed to decode request", | |
260 ErrorCode::InvalidRequest)); | |
261 } | |
262 }; | |
263 } | |
264 | |
265 // Bind a reply callback to a request. The callback will be invoked when | 245 // Bind a reply callback to a request. The callback will be invoked when |
266 // clangd receives the reply from the LSP client. | 246 // clangd receives the reply from the LSP client. |
267 // Return a call id of the request. | 247 // Return a call id of the request. |
268 llvm::json::Value bindReply(Callback<llvm::json::Value> Reply) { | 248 llvm::json::Value bindReply(Callback<llvm::json::Value> Reply) { |
269 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB; | 249 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB; |
282 OldestCB = std::move(ReplyCallbacks.front()); | 262 OldestCB = std::move(ReplyCallbacks.front()); |
283 ReplyCallbacks.pop_front(); | 263 ReplyCallbacks.pop_front(); |
284 } | 264 } |
285 } | 265 } |
286 if (OldestCB) | 266 if (OldestCB) |
287 OldestCB->second(llvm::createStringError( | 267 OldestCB->second( |
288 llvm::inconvertibleErrorCode(), | 268 error("failed to receive a client reply for request ({0})", |
289 llvm::formatv("failed to receive a client reply for request ({0})", | 269 OldestCB->first)); |
290 OldestCB->first))); | |
291 return ID; | 270 return ID; |
292 } | |
293 | |
294 // Bind an LSP method name to a notification. | |
295 template <typename Param> | |
296 void bind(const char *Method, | |
297 void (ClangdLSPServer::*Handler)(const Param &)) { | |
298 Notifications[Method] = [Method, Handler, | |
299 this](llvm::json::Value RawParams) { | |
300 Param P; | |
301 if (!fromJSON(RawParams, P)) { | |
302 elog("Failed to decode {0} request.", Method); | |
303 return; | |
304 } | |
305 trace::Span Tracer(Method, LSPLatency); | |
306 SPAN_ATTACH(Tracer, "Params", RawParams); | |
307 (Server.*Handler)(P); | |
308 }; | |
309 } | 271 } |
310 | 272 |
311 private: | 273 private: |
312 // Function object to reply to an LSP call. | 274 // Function object to reply to an LSP call. |
313 // Each instance must be called exactly once, otherwise: | 275 // Each instance must be called exactly once, otherwise: |
377 Server->Transp.reply(std::move(ID), std::move(Err)); | 339 Server->Transp.reply(std::move(ID), std::move(Err)); |
378 } | 340 } |
379 } | 341 } |
380 }; | 342 }; |
381 | 343 |
382 llvm::StringMap<std::function<void(llvm::json::Value)>> Notifications; | |
383 llvm::StringMap<std::function<void(llvm::json::Value, ReplyOnce)>> Calls; | |
384 | |
385 // Method calls may be cancelled by ID, so keep track of their state. | 344 // Method calls may be cancelled by ID, so keep track of their state. |
386 // This needs a mutex: handlers may finish on a different thread, and that's | 345 // This needs a mutex: handlers may finish on a different thread, and that's |
387 // when we clean up entries in the map. | 346 // when we clean up entries in the map. |
388 mutable std::mutex RequestCancelersMutex; | 347 mutable std::mutex RequestCancelersMutex; |
389 llvm::StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers; | 348 llvm::StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers; |
404 } | 363 } |
405 | 364 |
406 Context handlerContext() const { | 365 Context handlerContext() const { |
407 return Context::current().derive( | 366 return Context::current().derive( |
408 kCurrentOffsetEncoding, | 367 kCurrentOffsetEncoding, |
409 Server.NegotiatedOffsetEncoding.getValueOr(OffsetEncoding::UTF16)); | 368 Server.Opts.Encoding.getValueOr(OffsetEncoding::UTF16)); |
410 } | 369 } |
411 | 370 |
412 // We run cancelable requests in a context that does two things: | 371 // We run cancelable requests in a context that does two things: |
413 // - allows cancellation using RequestCancelers[ID] | 372 // - allows cancellation using RequestCancelers[ID] |
414 // - cleans up the entry in RequestCancelers when it's no longer needed | 373 // - cleans up the entry in RequestCancelers when it's no longer needed |
449 ClangdLSPServer &Server; | 408 ClangdLSPServer &Server; |
450 }; | 409 }; |
451 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks; | 410 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks; |
452 | 411 |
453 // call(), notify(), and reply() wrap the Transport, adding logging and locking. | 412 // call(), notify(), and reply() wrap the Transport, adding logging and locking. |
454 void ClangdLSPServer::callRaw(StringRef Method, llvm::json::Value Params, | 413 void ClangdLSPServer::callMethod(StringRef Method, llvm::json::Value Params, |
455 Callback<llvm::json::Value> CB) { | 414 Callback<llvm::json::Value> CB) { |
456 auto ID = MsgHandler->bindReply(std::move(CB)); | 415 auto ID = MsgHandler->bindReply(std::move(CB)); |
457 log("--> {0}({1})", Method, ID); | 416 log("--> {0}({1})", Method, ID); |
458 std::lock_guard<std::mutex> Lock(TranspWriter); | 417 std::lock_guard<std::mutex> Lock(TranspWriter); |
459 Transp.call(Method, std::move(Params), ID); | 418 Transp.call(Method, std::move(Params), ID); |
460 } | 419 } |
461 | 420 |
462 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) { | 421 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) { |
463 log("--> {0}", Method); | 422 log("--> {0}", Method); |
423 maybeCleanupMemory(); | |
464 std::lock_guard<std::mutex> Lock(TranspWriter); | 424 std::lock_guard<std::mutex> Lock(TranspWriter); |
465 Transp.notify(Method, std::move(Params)); | 425 Transp.notify(Method, std::move(Params)); |
466 } | 426 } |
467 | 427 |
468 static std::vector<llvm::StringRef> semanticTokenTypes() { | 428 static std::vector<llvm::StringRef> semanticTokenTypes() { |
471 ++I) | 431 ++I) |
472 Types.push_back(toSemanticTokenType(static_cast<HighlightingKind>(I))); | 432 Types.push_back(toSemanticTokenType(static_cast<HighlightingKind>(I))); |
473 return Types; | 433 return Types; |
474 } | 434 } |
475 | 435 |
436 static std::vector<llvm::StringRef> semanticTokenModifiers() { | |
437 std::vector<llvm::StringRef> Modifiers; | |
438 for (unsigned I = 0; | |
439 I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) | |
440 Modifiers.push_back( | |
441 toSemanticTokenModifier(static_cast<HighlightingModifier>(I))); | |
442 return Modifiers; | |
443 } | |
444 | |
476 void ClangdLSPServer::onInitialize(const InitializeParams &Params, | 445 void ClangdLSPServer::onInitialize(const InitializeParams &Params, |
477 Callback<llvm::json::Value> Reply) { | 446 Callback<llvm::json::Value> Reply) { |
478 // Determine character encoding first as it affects constructed ClangdServer. | 447 // Determine character encoding first as it affects constructed ClangdServer. |
479 if (Params.capabilities.offsetEncoding && !NegotiatedOffsetEncoding) { | 448 if (Params.capabilities.offsetEncoding && !Opts.Encoding) { |
480 NegotiatedOffsetEncoding = OffsetEncoding::UTF16; // fallback | 449 Opts.Encoding = OffsetEncoding::UTF16; // fallback |
481 for (OffsetEncoding Supported : *Params.capabilities.offsetEncoding) | 450 for (OffsetEncoding Supported : *Params.capabilities.offsetEncoding) |
482 if (Supported != OffsetEncoding::UnsupportedEncoding) { | 451 if (Supported != OffsetEncoding::UnsupportedEncoding) { |
483 NegotiatedOffsetEncoding = Supported; | 452 Opts.Encoding = Supported; |
484 break; | 453 break; |
485 } | 454 } |
486 } | 455 } |
487 | 456 |
488 ClangdServerOpts.TheiaSemanticHighlighting = | |
489 Params.capabilities.TheiaSemanticHighlighting; | |
490 if (Params.capabilities.TheiaSemanticHighlighting && | 457 if (Params.capabilities.TheiaSemanticHighlighting && |
491 Params.capabilities.SemanticTokens) { | 458 !Params.capabilities.SemanticTokens) { |
492 log("Client supports legacy semanticHighlights notification and standard " | 459 elog("Client requested legacy semanticHighlights notification, which is " |
493 "semanticTokens request, choosing the latter (no notifications)."); | 460 "no longer supported. Migrate to standard semanticTokens request"); |
494 ClangdServerOpts.TheiaSemanticHighlighting = false; | |
495 } | 461 } |
496 | 462 |
497 if (Params.rootUri && *Params.rootUri) | 463 if (Params.rootUri && *Params.rootUri) |
498 ClangdServerOpts.WorkspaceRoot = std::string(Params.rootUri->file()); | 464 Opts.WorkspaceRoot = std::string(Params.rootUri->file()); |
499 else if (Params.rootPath && !Params.rootPath->empty()) | 465 else if (Params.rootPath && !Params.rootPath->empty()) |
500 ClangdServerOpts.WorkspaceRoot = *Params.rootPath; | 466 Opts.WorkspaceRoot = *Params.rootPath; |
501 if (Server) | 467 if (Server) |
502 return Reply(llvm::make_error<LSPError>("server already initialized", | 468 return Reply(llvm::make_error<LSPError>("server already initialized", |
503 ErrorCode::InvalidRequest)); | 469 ErrorCode::InvalidRequest)); |
504 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) | 470 if (Opts.UseDirBasedCDB) { |
505 CompileCommandsDir = Dir; | 471 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); |
506 if (UseDirBasedCDB) { | 472 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) |
507 BaseCDB = std::make_unique<DirectoryBasedGlobalCompilationDatabase>( | 473 CDBOpts.CompileCommandsDir = Dir; |
508 CompileCommandsDir); | 474 CDBOpts.ContextProvider = Opts.ContextProvider; |
509 BaseCDB = getQueryDriverDatabase( | 475 BaseCDB = |
510 llvm::makeArrayRef(ClangdServerOpts.QueryDriverGlobs), | 476 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts); |
511 std::move(BaseCDB)); | 477 BaseCDB = getQueryDriverDatabase(llvm::makeArrayRef(Opts.QueryDriverGlobs), |
478 std::move(BaseCDB)); | |
512 } | 479 } |
513 auto Mangler = CommandMangler::detect(); | 480 auto Mangler = CommandMangler::detect(); |
514 if (ClangdServerOpts.ResourceDir) | 481 if (Opts.ResourceDir) |
515 Mangler.ResourceDir = *ClangdServerOpts.ResourceDir; | 482 Mangler.ResourceDir = *Opts.ResourceDir; |
516 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, | 483 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, |
517 tooling::ArgumentsAdjuster(Mangler)); | 484 tooling::ArgumentsAdjuster(std::move(Mangler))); |
518 { | 485 { |
519 // Switch caller's context with LSPServer's background context. Since we | 486 // Switch caller's context with LSPServer's background context. Since we |
520 // rather want to propagate information from LSPServer's context into the | 487 // rather want to propagate information from LSPServer's context into the |
521 // Server, CDB, etc. | 488 // Server, CDB, etc. |
522 WithContext MainContext(BackgroundContext.clone()); | 489 WithContext MainContext(BackgroundContext.clone()); |
523 llvm::Optional<WithContextValue> WithOffsetEncoding; | 490 llvm::Optional<WithContextValue> WithOffsetEncoding; |
524 if (NegotiatedOffsetEncoding) | 491 if (Opts.Encoding) |
525 WithOffsetEncoding.emplace(kCurrentOffsetEncoding, | 492 WithOffsetEncoding.emplace(kCurrentOffsetEncoding, *Opts.Encoding); |
526 *NegotiatedOffsetEncoding); | 493 Server.emplace(*CDB, TFS, Opts, |
527 Server.emplace(*CDB, FSProvider, ClangdServerOpts, | |
528 static_cast<ClangdServer::Callbacks *>(this)); | 494 static_cast<ClangdServer::Callbacks *>(this)); |
529 } | 495 } |
530 applyConfiguration(Params.initializationOptions.ConfigSettings); | 496 applyConfiguration(Params.initializationOptions.ConfigSettings); |
531 | 497 |
532 CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets; | 498 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets; |
533 CCOpts.IncludeFixIts = Params.capabilities.CompletionFixes; | 499 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes; |
534 if (!CCOpts.BundleOverloads.hasValue()) | 500 if (!Opts.CodeComplete.BundleOverloads.hasValue()) |
535 CCOpts.BundleOverloads = Params.capabilities.HasSignatureHelp; | 501 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp; |
536 CCOpts.DocumentationFormat = | 502 Opts.CodeComplete.DocumentationFormat = |
537 Params.capabilities.CompletionDocumentationFormat; | 503 Params.capabilities.CompletionDocumentationFormat; |
538 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; | 504 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; |
539 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; | 505 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; |
540 DiagOpts.EmitRelatedLocations = | 506 DiagOpts.EmitRelatedLocations = |
541 Params.capabilities.DiagnosticRelatedInformation; | 507 Params.capabilities.DiagnosticRelatedInformation; |
550 HoverContentFormat = Params.capabilities.HoverContentFormat; | 516 HoverContentFormat = Params.capabilities.HoverContentFormat; |
551 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; | 517 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; |
552 if (Params.capabilities.WorkDoneProgress) | 518 if (Params.capabilities.WorkDoneProgress) |
553 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; | 519 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; |
554 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation; | 520 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation; |
521 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests; | |
522 | |
523 llvm::json::Object ServerCaps{ | |
524 {"textDocumentSync", | |
525 llvm::json::Object{ | |
526 {"openClose", true}, | |
527 {"change", (int)TextDocumentSyncKind::Incremental}, | |
528 {"save", true}, | |
529 }}, | |
530 {"documentFormattingProvider", true}, | |
531 {"documentRangeFormattingProvider", true}, | |
532 {"documentOnTypeFormattingProvider", | |
533 llvm::json::Object{ | |
534 {"firstTriggerCharacter", "\n"}, | |
535 {"moreTriggerCharacter", {}}, | |
536 }}, | |
537 {"completionProvider", | |
538 llvm::json::Object{ | |
539 {"allCommitCharacters", | |
540 {" ", "\t", "(", ")", "[", "]", "{", "}", "<", | |
541 ">", ":", ";", ",", "+", "-", "/", "*", "%", | |
542 "^", "&", "#", "?", ".", "=", "\"", "'", "|"}}, | |
543 {"resolveProvider", false}, | |
544 // We do extra checks, e.g. that > is part of ->. | |
545 {"triggerCharacters", {".", "<", ">", ":", "\"", "/"}}, | |
546 }}, | |
547 {"semanticTokensProvider", | |
548 llvm::json::Object{ | |
549 {"full", llvm::json::Object{{"delta", true}}}, | |
550 {"range", false}, | |
551 {"legend", | |
552 llvm::json::Object{{"tokenTypes", semanticTokenTypes()}, | |
553 {"tokenModifiers", semanticTokenModifiers()}}}, | |
554 }}, | |
555 {"signatureHelpProvider", | |
556 llvm::json::Object{ | |
557 {"triggerCharacters", {"(", ","}}, | |
558 }}, | |
559 {"declarationProvider", true}, | |
560 {"definitionProvider", true}, | |
561 {"implementationProvider", true}, | |
562 {"documentHighlightProvider", true}, | |
563 {"documentLinkProvider", | |
564 llvm::json::Object{ | |
565 {"resolveProvider", false}, | |
566 }}, | |
567 {"hoverProvider", true}, | |
568 {"selectionRangeProvider", true}, | |
569 {"documentSymbolProvider", true}, | |
570 {"workspaceSymbolProvider", true}, | |
571 {"referencesProvider", true}, | |
572 {"astProvider", true}, // clangd extension | |
573 {"typeHierarchyProvider", true}, | |
574 {"memoryUsageProvider", true}, // clangd extension | |
575 {"compilationDatabase", // clangd extension | |
576 llvm::json::Object{{"automaticReload", true}}}, | |
577 {"callHierarchyProvider", true}, | |
578 }; | |
579 | |
580 { | |
581 LSPBinder Binder(Handlers, *this); | |
582 bindMethods(Binder, Params.capabilities); | |
583 if (Opts.FeatureModules) | |
584 for (auto &Mod : *Opts.FeatureModules) | |
585 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps); | |
586 } | |
555 | 587 |
556 // Per LSP, renameProvider can be either boolean or RenameOptions. | 588 // Per LSP, renameProvider can be either boolean or RenameOptions. |
557 // RenameOptions will be specified if the client states it supports prepare. | 589 // RenameOptions will be specified if the client states it supports prepare. |
558 llvm::json::Value RenameProvider = | 590 ServerCaps["renameProvider"] = |
559 llvm::json::Object{{"prepareProvider", true}}; | 591 Params.capabilities.RenamePrepareSupport |
560 if (!Params.capabilities.RenamePrepareSupport) // Only boolean allowed per LSP | 592 ? llvm::json::Object{{"prepareProvider", true}} |
561 RenameProvider = true; | 593 : llvm::json::Value(true); |
562 | 594 |
563 // Per LSP, codeActionProvide can be either boolean or CodeActionOptions. | 595 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions. |
564 // CodeActionOptions is only valid if the client supports action literal | 596 // CodeActionOptions is only valid if the client supports action literal |
565 // via textDocument.codeAction.codeActionLiteralSupport. | 597 // via textDocument.codeAction.codeActionLiteralSupport. |
566 llvm::json::Value CodeActionProvider = true; | 598 llvm::json::Value CodeActionProvider = true; |
567 if (Params.capabilities.CodeActionStructure) | 599 ServerCaps["codeActionProvider"] = |
568 CodeActionProvider = llvm::json::Object{ | 600 Params.capabilities.CodeActionStructure |
569 {"codeActionKinds", | 601 ? llvm::json::Object{{"codeActionKinds", |
570 {CodeAction::QUICKFIX_KIND, CodeAction::REFACTOR_KIND, | 602 {CodeAction::QUICKFIX_KIND, |
571 CodeAction::INFO_KIND}}}; | 603 CodeAction::REFACTOR_KIND, |
604 CodeAction::INFO_KIND}}} | |
605 : llvm::json::Value(true); | |
606 | |
607 if (Opts.FoldingRanges) | |
608 ServerCaps["foldingRangeProvider"] = true; | |
609 | |
610 if (Opts.InlayHints) | |
611 ServerCaps["clangdInlayHintsProvider"] = true; | |
612 | |
613 std::vector<llvm::StringRef> Commands; | |
614 for (llvm::StringRef Command : Handlers.CommandHandlers.keys()) | |
615 Commands.push_back(Command); | |
616 llvm::sort(Commands); | |
617 ServerCaps["executeCommandProvider"] = | |
618 llvm::json::Object{{"commands", Commands}}; | |
572 | 619 |
573 llvm::json::Object Result{ | 620 llvm::json::Object Result{ |
574 {{"serverInfo", | 621 {{"serverInfo", |
575 llvm::json::Object{{"name", "clangd"}, | 622 llvm::json::Object{{"name", "clangd"}, |
576 {"version", getClangToolFullVersion("clangd")}}}, | 623 {"version", getClangToolFullVersion("clangd")}}}, |
577 {"capabilities", | 624 {"capabilities", std::move(ServerCaps)}}}; |
578 llvm::json::Object{ | 625 if (Opts.Encoding) |
579 {"textDocumentSync", | 626 Result["offsetEncoding"] = *Opts.Encoding; |
580 llvm::json::Object{ | |
581 {"openClose", true}, | |
582 {"change", (int)TextDocumentSyncKind::Incremental}, | |
583 {"save", true}, | |
584 }}, | |
585 {"documentFormattingProvider", true}, | |
586 {"documentRangeFormattingProvider", true}, | |
587 {"documentOnTypeFormattingProvider", | |
588 llvm::json::Object{ | |
589 {"firstTriggerCharacter", "\n"}, | |
590 {"moreTriggerCharacter", {}}, | |
591 }}, | |
592 {"codeActionProvider", std::move(CodeActionProvider)}, | |
593 {"completionProvider", | |
594 llvm::json::Object{ | |
595 {"allCommitCharacters", " \t()[]{}<>:;,+-/*%^&#?.=\"'|"}, | |
596 {"resolveProvider", false}, | |
597 // We do extra checks, e.g. that > is part of ->. | |
598 {"triggerCharacters", {".", "<", ">", ":", "\"", "/"}}, | |
599 }}, | |
600 {"semanticTokensProvider", | |
601 llvm::json::Object{ | |
602 {"documentProvider", llvm::json::Object{{"edits", true}}}, | |
603 {"rangeProvider", false}, | |
604 {"legend", | |
605 llvm::json::Object{{"tokenTypes", semanticTokenTypes()}, | |
606 {"tokenModifiers", llvm::json::Array()}}}, | |
607 }}, | |
608 {"signatureHelpProvider", | |
609 llvm::json::Object{ | |
610 {"triggerCharacters", {"(", ","}}, | |
611 }}, | |
612 {"declarationProvider", true}, | |
613 {"definitionProvider", true}, | |
614 {"documentHighlightProvider", true}, | |
615 {"documentLinkProvider", | |
616 llvm::json::Object{ | |
617 {"resolveProvider", false}, | |
618 }}, | |
619 {"hoverProvider", true}, | |
620 {"renameProvider", std::move(RenameProvider)}, | |
621 {"selectionRangeProvider", true}, | |
622 {"documentSymbolProvider", true}, | |
623 {"workspaceSymbolProvider", true}, | |
624 {"referencesProvider", true}, | |
625 {"executeCommandProvider", | |
626 llvm::json::Object{ | |
627 {"commands", | |
628 {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND, | |
629 ExecuteCommandParams::CLANGD_APPLY_TWEAK}}, | |
630 }}, | |
631 {"typeHierarchyProvider", true}, | |
632 }}}}; | |
633 if (NegotiatedOffsetEncoding) | |
634 Result["offsetEncoding"] = *NegotiatedOffsetEncoding; | |
635 if (ClangdServerOpts.TheiaSemanticHighlighting) | |
636 Result.getObject("capabilities") | |
637 ->insert( | |
638 {"semanticHighlighting", | |
639 llvm::json::Object{{"scopes", buildHighlightScopeLookupTable()}}}); | |
640 Reply(std::move(Result)); | 627 Reply(std::move(Result)); |
641 } | 628 } |
642 | 629 |
643 void ClangdLSPServer::onInitialized(const InitializedParams &Params) {} | 630 void ClangdLSPServer::onInitialized(const InitializedParams &Params) {} |
644 | 631 |
645 void ClangdLSPServer::onShutdown(const ShutdownParams &Params, | 632 void ClangdLSPServer::onShutdown(const NoParams &, |
646 Callback<std::nullptr_t> Reply) { | 633 Callback<std::nullptr_t> Reply) { |
647 // Do essentially nothing, just say we're ready to exit. | 634 // Do essentially nothing, just say we're ready to exit. |
648 ShutdownRequestReceived = true; | 635 ShutdownRequestReceived = true; |
649 Reply(nullptr); | 636 Reply(nullptr); |
650 } | 637 } |
651 | 638 |
652 // sync is a clangd extension: it blocks until all background work completes. | 639 // sync is a clangd extension: it blocks until all background work completes. |
653 // It blocks the calling thread, so no messages are processed until it returns! | 640 // It blocks the calling thread, so no messages are processed until it returns! |
654 void ClangdLSPServer::onSync(const NoParams &Params, | 641 void ClangdLSPServer::onSync(const NoParams &, Callback<std::nullptr_t> Reply) { |
655 Callback<std::nullptr_t> Reply) { | |
656 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60)) | 642 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60)) |
657 Reply(nullptr); | 643 Reply(nullptr); |
658 else | 644 else |
659 Reply(llvm::createStringError(llvm::inconvertibleErrorCode(), | 645 Reply(error("Not idle after a minute")); |
660 "Not idle after a minute")); | |
661 } | 646 } |
662 | 647 |
663 void ClangdLSPServer::onDocumentDidOpen( | 648 void ClangdLSPServer::onDocumentDidOpen( |
664 const DidOpenTextDocumentParams &Params) { | 649 const DidOpenTextDocumentParams &Params) { |
665 PathRef File = Params.textDocument.uri.file(); | 650 PathRef File = Params.textDocument.uri.file(); |
666 | 651 |
667 const std::string &Contents = Params.textDocument.text; | 652 const std::string &Contents = Params.textDocument.text; |
668 | 653 |
669 auto Version = DraftMgr.addDraft(File, Params.textDocument.version, Contents); | 654 Server->addDocument(File, Contents, |
670 Server->addDocument(File, Contents, encodeVersion(Version), | 655 encodeVersion(Params.textDocument.version), |
671 WantDiagnostics::Yes); | 656 WantDiagnostics::Yes); |
672 } | 657 } |
673 | 658 |
674 void ClangdLSPServer::onDocumentDidChange( | 659 void ClangdLSPServer::onDocumentDidChange( |
675 const DidChangeTextDocumentParams &Params) { | 660 const DidChangeTextDocumentParams &Params) { |
677 if (Params.wantDiagnostics.hasValue()) | 662 if (Params.wantDiagnostics.hasValue()) |
678 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes | 663 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes |
679 : WantDiagnostics::No; | 664 : WantDiagnostics::No; |
680 | 665 |
681 PathRef File = Params.textDocument.uri.file(); | 666 PathRef File = Params.textDocument.uri.file(); |
682 llvm::Expected<DraftStore::Draft> Draft = DraftMgr.updateDraft( | 667 auto Code = Server->getDraft(File); |
683 File, Params.textDocument.version, Params.contentChanges); | 668 if (!Code) { |
684 if (!Draft) { | 669 log("Trying to incrementally change non-added document: {0}", File); |
685 // If this fails, we are most likely going to be not in sync anymore with | |
686 // the client. It is better to remove the draft and let further operations | |
687 // fail rather than giving wrong results. | |
688 DraftMgr.removeDraft(File); | |
689 Server->removeDocument(File); | |
690 elog("Failed to update {0}: {1}", File, Draft.takeError()); | |
691 return; | 670 return; |
692 } | 671 } |
693 | 672 std::string NewCode(*Code); |
694 Server->addDocument(File, Draft->Contents, encodeVersion(Draft->Version), | 673 for (const auto &Change : Params.contentChanges) { |
674 if (auto Err = applyChange(NewCode, Change)) { | |
675 // If this fails, we are most likely going to be not in sync anymore with | |
676 // the client. It is better to remove the draft and let further | |
677 // operations fail rather than giving wrong results. | |
678 Server->removeDocument(File); | |
679 elog("Failed to update {0}: {1}", File, std::move(Err)); | |
680 return; | |
681 } | |
682 } | |
683 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version), | |
695 WantDiags, Params.forceRebuild); | 684 WantDiags, Params.forceRebuild); |
696 } | 685 } |
697 | 686 |
698 void ClangdLSPServer::onDocumentDidSave( | 687 void ClangdLSPServer::onDocumentDidSave( |
699 const DidSaveTextDocumentParams &Params) { | 688 const DidSaveTextDocumentParams &Params) { |
700 reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; }); | 689 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; }); |
701 } | 690 } |
702 | 691 |
703 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { | 692 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { |
704 // We could also reparse all open files here. However: | 693 // We could also reparse all open files here. However: |
705 // - this could be frequent, and revalidating all the preambles isn't free | 694 // - this could be frequent, and revalidating all the preambles isn't free |
706 // - this is useful e.g. when switching git branches, but we're likely to see | 695 // - this is useful e.g. when switching git branches, but we're likely to see |
707 // fresh headers but still have the old-branch main-file content | 696 // fresh headers but still have the old-branch main-file content |
708 Server->onFileEvent(Params); | 697 Server->onFileEvent(Params); |
698 // FIXME: observe config files, immediately expire time-based caches, reparse: | |
699 // - compile_commands.json and compile_flags.txt | |
700 // - .clang_format and .clang-tidy | |
701 // - .clangd and clangd/config.yaml | |
709 } | 702 } |
710 | 703 |
711 void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, | 704 void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, |
712 Callback<llvm::json::Value> Reply) { | 705 Callback<llvm::json::Value> Reply) { |
713 auto ApplyEdit = [this](WorkspaceEdit WE, std::string SuccessMessage, | 706 auto It = Handlers.CommandHandlers.find(Params.command); |
714 decltype(Reply) Reply) { | 707 if (It == Handlers.CommandHandlers.end()) { |
715 ApplyWorkspaceEditParams Edit; | 708 return Reply(llvm::make_error<LSPError>( |
716 Edit.edit = std::move(WE); | |
717 call<ApplyWorkspaceEditResponse>( | |
718 "workspace/applyEdit", std::move(Edit), | |
719 [Reply = std::move(Reply), SuccessMessage = std::move(SuccessMessage)]( | |
720 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable { | |
721 if (!Response) | |
722 return Reply(Response.takeError()); | |
723 if (!Response->applied) { | |
724 std::string Reason = Response->failureReason | |
725 ? *Response->failureReason | |
726 : "unknown reason"; | |
727 return Reply(llvm::createStringError( | |
728 llvm::inconvertibleErrorCode(), | |
729 ("edits were not applied: " + Reason).c_str())); | |
730 } | |
731 return Reply(SuccessMessage); | |
732 }); | |
733 }; | |
734 | |
735 if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND && | |
736 Params.workspaceEdit) { | |
737 // The flow for "apply-fix" : | |
738 // 1. We publish a diagnostic, including fixits | |
739 // 2. The user clicks on the diagnostic, the editor asks us for code actions | |
740 // 3. We send code actions, with the fixit embedded as context | |
741 // 4. The user selects the fixit, the editor asks us to apply it | |
742 // 5. We unwrap the changes and send them back to the editor | |
743 // 6. The editor applies the changes (applyEdit), and sends us a reply | |
744 // 7. We unwrap the reply and send a reply to the editor. | |
745 ApplyEdit(*Params.workspaceEdit, "Fix applied.", std::move(Reply)); | |
746 } else if (Params.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK && | |
747 Params.tweakArgs) { | |
748 auto Code = DraftMgr.getDraft(Params.tweakArgs->file.file()); | |
749 if (!Code) | |
750 return Reply(llvm::createStringError( | |
751 llvm::inconvertibleErrorCode(), | |
752 "trying to apply a code action for a non-added file")); | |
753 | |
754 auto Action = [this, ApplyEdit, Reply = std::move(Reply), | |
755 File = Params.tweakArgs->file, Code = std::move(*Code)]( | |
756 llvm::Expected<Tweak::Effect> R) mutable { | |
757 if (!R) | |
758 return Reply(R.takeError()); | |
759 | |
760 assert(R->ShowMessage || | |
761 (!R->ApplyEdits.empty() && "tweak has no effect")); | |
762 | |
763 if (R->ShowMessage) { | |
764 ShowMessageParams Msg; | |
765 Msg.message = *R->ShowMessage; | |
766 Msg.type = MessageType::Info; | |
767 notify("window/showMessage", Msg); | |
768 } | |
769 // When no edit is specified, make sure we Reply(). | |
770 if (R->ApplyEdits.empty()) | |
771 return Reply("Tweak applied."); | |
772 | |
773 if (auto Err = validateEdits(DraftMgr, R->ApplyEdits)) | |
774 return Reply(std::move(Err)); | |
775 | |
776 WorkspaceEdit WE; | |
777 WE.changes.emplace(); | |
778 for (const auto &It : R->ApplyEdits) { | |
779 (*WE.changes)[URI::createFile(It.first()).toString()] = | |
780 It.second.asTextEdits(); | |
781 } | |
782 // ApplyEdit will take care of calling Reply(). | |
783 return ApplyEdit(std::move(WE), "Tweak applied.", std::move(Reply)); | |
784 }; | |
785 Server->applyTweak(Params.tweakArgs->file.file(), | |
786 Params.tweakArgs->selection, Params.tweakArgs->tweakID, | |
787 std::move(Action)); | |
788 } else { | |
789 // We should not get here because ExecuteCommandParams would not have | |
790 // parsed in the first place and this handler should not be called. But if | |
791 // more commands are added, this will be here has a safe guard. | |
792 Reply(llvm::make_error<LSPError>( | |
793 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), | 709 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), |
794 ErrorCode::InvalidParams)); | 710 ErrorCode::InvalidParams)); |
795 } | 711 } |
712 It->second(Params.argument, std::move(Reply)); | |
713 } | |
714 | |
715 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit &WE, | |
716 Callback<llvm::json::Value> Reply) { | |
717 // The flow for "apply-fix" : | |
718 // 1. We publish a diagnostic, including fixits | |
719 // 2. The user clicks on the diagnostic, the editor asks us for code actions | |
720 // 3. We send code actions, with the fixit embedded as context | |
721 // 4. The user selects the fixit, the editor asks us to apply it | |
722 // 5. We unwrap the changes and send them back to the editor | |
723 // 6. The editor applies the changes (applyEdit), and sends us a reply | |
724 // 7. We unwrap the reply and send a reply to the editor. | |
725 applyEdit(WE, "Fix applied.", std::move(Reply)); | |
726 } | |
727 | |
728 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, | |
729 Callback<llvm::json::Value> Reply) { | |
730 auto Action = [this, Reply = std::move(Reply)]( | |
731 llvm::Expected<Tweak::Effect> R) mutable { | |
732 if (!R) | |
733 return Reply(R.takeError()); | |
734 | |
735 assert(R->ShowMessage || (!R->ApplyEdits.empty() && "tweak has no effect")); | |
736 | |
737 if (R->ShowMessage) { | |
738 ShowMessageParams Msg; | |
739 Msg.message = *R->ShowMessage; | |
740 Msg.type = MessageType::Info; | |
741 ShowMessage(Msg); | |
742 } | |
743 // When no edit is specified, make sure we Reply(). | |
744 if (R->ApplyEdits.empty()) | |
745 return Reply("Tweak applied."); | |
746 | |
747 if (auto Err = validateEdits(*Server, R->ApplyEdits)) | |
748 return Reply(std::move(Err)); | |
749 | |
750 WorkspaceEdit WE; | |
751 for (const auto &It : R->ApplyEdits) { | |
752 WE.changes[URI::createFile(It.first()).toString()] = | |
753 It.second.asTextEdits(); | |
754 } | |
755 // ApplyEdit will take care of calling Reply(). | |
756 return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply)); | |
757 }; | |
758 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID, | |
759 std::move(Action)); | |
760 } | |
761 | |
762 void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success, | |
763 Callback<llvm::json::Value> Reply) { | |
764 ApplyWorkspaceEditParams Edit; | |
765 Edit.edit = std::move(WE); | |
766 ApplyWorkspaceEdit( | |
767 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)]( | |
768 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable { | |
769 if (!Response) | |
770 return Reply(Response.takeError()); | |
771 if (!Response->applied) { | |
772 std::string Reason = Response->failureReason | |
773 ? *Response->failureReason | |
774 : "unknown reason"; | |
775 return Reply(error("edits were not applied: {0}", Reason)); | |
776 } | |
777 return Reply(SuccessMessage); | |
778 }); | |
796 } | 779 } |
797 | 780 |
798 void ClangdLSPServer::onWorkspaceSymbol( | 781 void ClangdLSPServer::onWorkspaceSymbol( |
799 const WorkspaceSymbolParams &Params, | 782 const WorkspaceSymbolParams &Params, |
800 Callback<std::vector<SymbolInformation>> Reply) { | 783 Callback<std::vector<SymbolInformation>> Reply) { |
801 Server->workspaceSymbols( | 784 Server->workspaceSymbols( |
802 Params.query, CCOpts.Limit, | 785 Params.query, Params.limit.getValueOr(Opts.CodeComplete.Limit), |
803 [Reply = std::move(Reply), | 786 [Reply = std::move(Reply), |
804 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable { | 787 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable { |
805 if (!Items) | 788 if (!Items) |
806 return Reply(Items.takeError()); | 789 return Reply(Items.takeError()); |
807 for (auto &Sym : *Items) | 790 for (auto &Sym : *Items) |
811 }); | 794 }); |
812 } | 795 } |
813 | 796 |
814 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, | 797 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, |
815 Callback<llvm::Optional<Range>> Reply) { | 798 Callback<llvm::Optional<Range>> Reply) { |
816 Server->prepareRename(Params.textDocument.uri.file(), Params.position, | 799 Server->prepareRename( |
817 RenameOpts, std::move(Reply)); | 800 Params.textDocument.uri.file(), Params.position, /*NewName*/ llvm::None, |
801 Opts.Rename, | |
802 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable { | |
803 if (!Result) | |
804 return Reply(Result.takeError()); | |
805 return Reply(std::move(Result->Target)); | |
806 }); | |
818 } | 807 } |
819 | 808 |
820 void ClangdLSPServer::onRename(const RenameParams &Params, | 809 void ClangdLSPServer::onRename(const RenameParams &Params, |
821 Callback<WorkspaceEdit> Reply) { | 810 Callback<WorkspaceEdit> Reply) { |
822 Path File = std::string(Params.textDocument.uri.file()); | 811 Path File = std::string(Params.textDocument.uri.file()); |
823 if (!DraftMgr.getDraft(File)) | 812 if (!Server->getDraft(File)) |
824 return Reply(llvm::make_error<LSPError>( | 813 return Reply(llvm::make_error<LSPError>( |
825 "onRename called for non-added file", ErrorCode::InvalidParams)); | 814 "onRename called for non-added file", ErrorCode::InvalidParams)); |
826 Server->rename( | 815 Server->rename(File, Params.position, Params.newName, Opts.Rename, |
827 File, Params.position, Params.newName, RenameOpts, | 816 [File, Params, Reply = std::move(Reply), |
828 [File, Params, Reply = std::move(Reply), | 817 this](llvm::Expected<RenameResult> R) mutable { |
829 this](llvm::Expected<FileEdits> Edits) mutable { | 818 if (!R) |
830 if (!Edits) | 819 return Reply(R.takeError()); |
831 return Reply(Edits.takeError()); | 820 if (auto Err = validateEdits(*Server, R->GlobalChanges)) |
832 if (auto Err = validateEdits(DraftMgr, *Edits)) | 821 return Reply(std::move(Err)); |
833 return Reply(std::move(Err)); | 822 WorkspaceEdit Result; |
834 WorkspaceEdit Result; | 823 for (const auto &Rep : R->GlobalChanges) { |
835 Result.changes.emplace(); | 824 Result.changes[URI::createFile(Rep.first()).toString()] = |
836 for (const auto &Rep : *Edits) { | 825 Rep.second.asTextEdits(); |
837 (*Result.changes)[URI::createFile(Rep.first()).toString()] = | 826 } |
838 Rep.second.asTextEdits(); | 827 Reply(Result); |
839 } | 828 }); |
840 Reply(Result); | |
841 }); | |
842 } | 829 } |
843 | 830 |
844 void ClangdLSPServer::onDocumentDidClose( | 831 void ClangdLSPServer::onDocumentDidClose( |
845 const DidCloseTextDocumentParams &Params) { | 832 const DidCloseTextDocumentParams &Params) { |
846 PathRef File = Params.textDocument.uri.file(); | 833 PathRef File = Params.textDocument.uri.file(); |
847 DraftMgr.removeDraft(File); | |
848 Server->removeDocument(File); | 834 Server->removeDocument(File); |
849 | 835 |
850 { | 836 { |
851 std::lock_guard<std::mutex> Lock(FixItsMutex); | 837 std::lock_guard<std::mutex> Lock(FixItsMutex); |
852 FixItsMap.erase(File); | 838 FixItsMap.erase(File); |
853 } | |
854 { | |
855 std::lock_guard<std::mutex> HLock(HighlightingsMutex); | |
856 FileToHighlightings.erase(File); | |
857 } | 839 } |
858 { | 840 { |
859 std::lock_guard<std::mutex> HLock(SemanticTokensMutex); | 841 std::lock_guard<std::mutex> HLock(SemanticTokensMutex); |
860 LastSemanticTokens.erase(File); | 842 LastSemanticTokens.erase(File); |
861 } | 843 } |
864 // VSCode). Note that this cannot race with actual diagnostics responses | 846 // VSCode). Note that this cannot race with actual diagnostics responses |
865 // because removeDocument() guarantees no diagnostic callbacks will be | 847 // because removeDocument() guarantees no diagnostic callbacks will be |
866 // executed after it returns. | 848 // executed after it returns. |
867 PublishDiagnosticsParams Notification; | 849 PublishDiagnosticsParams Notification; |
868 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); | 850 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); |
869 publishDiagnostics(Notification); | 851 PublishDiagnostics(Notification); |
870 } | 852 } |
871 | 853 |
872 void ClangdLSPServer::onDocumentOnTypeFormatting( | 854 void ClangdLSPServer::onDocumentOnTypeFormatting( |
873 const DocumentOnTypeFormattingParams &Params, | 855 const DocumentOnTypeFormattingParams &Params, |
874 Callback<std::vector<TextEdit>> Reply) { | 856 Callback<std::vector<TextEdit>> Reply) { |
875 auto File = Params.textDocument.uri.file(); | 857 auto File = Params.textDocument.uri.file(); |
876 auto Code = DraftMgr.getDraft(File); | 858 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply)); |
877 if (!Code) | |
878 return Reply(llvm::make_error<LSPError>( | |
879 "onDocumentOnTypeFormatting called for non-added file", | |
880 ErrorCode::InvalidParams)); | |
881 | |
882 Reply(Server->formatOnType(Code->Contents, File, Params.position, Params.ch)); | |
883 } | 859 } |
884 | 860 |
885 void ClangdLSPServer::onDocumentRangeFormatting( | 861 void ClangdLSPServer::onDocumentRangeFormatting( |
886 const DocumentRangeFormattingParams &Params, | 862 const DocumentRangeFormattingParams &Params, |
887 Callback<std::vector<TextEdit>> Reply) { | 863 Callback<std::vector<TextEdit>> Reply) { |
888 auto File = Params.textDocument.uri.file(); | 864 auto File = Params.textDocument.uri.file(); |
889 auto Code = DraftMgr.getDraft(File); | 865 auto Code = Server->getDraft(File); |
890 if (!Code) | 866 Server->formatFile(File, Params.range, |
891 return Reply(llvm::make_error<LSPError>( | 867 [Code = std::move(Code), Reply = std::move(Reply)]( |
892 "onDocumentRangeFormatting called for non-added file", | 868 llvm::Expected<tooling::Replacements> Result) mutable { |
893 ErrorCode::InvalidParams)); | 869 if (Result) |
894 | 870 Reply(replacementsToEdits(*Code, Result.get())); |
895 auto ReplacementsOrError = | 871 else |
896 Server->formatRange(Code->Contents, File, Params.range); | 872 Reply(Result.takeError()); |
897 if (ReplacementsOrError) | 873 }); |
898 Reply(replacementsToEdits(Code->Contents, ReplacementsOrError.get())); | |
899 else | |
900 Reply(ReplacementsOrError.takeError()); | |
901 } | 874 } |
902 | 875 |
903 void ClangdLSPServer::onDocumentFormatting( | 876 void ClangdLSPServer::onDocumentFormatting( |
904 const DocumentFormattingParams &Params, | 877 const DocumentFormattingParams &Params, |
905 Callback<std::vector<TextEdit>> Reply) { | 878 Callback<std::vector<TextEdit>> Reply) { |
906 auto File = Params.textDocument.uri.file(); | 879 auto File = Params.textDocument.uri.file(); |
907 auto Code = DraftMgr.getDraft(File); | 880 auto Code = Server->getDraft(File); |
908 if (!Code) | 881 Server->formatFile(File, |
909 return Reply(llvm::make_error<LSPError>( | 882 /*Rng=*/llvm::None, |
910 "onDocumentFormatting called for non-added file", | 883 [Code = std::move(Code), Reply = std::move(Reply)]( |
911 ErrorCode::InvalidParams)); | 884 llvm::Expected<tooling::Replacements> Result) mutable { |
912 | 885 if (Result) |
913 auto ReplacementsOrError = Server->formatFile(Code->Contents, File); | 886 Reply(replacementsToEdits(*Code, Result.get())); |
914 if (ReplacementsOrError) | 887 else |
915 Reply(replacementsToEdits(Code->Contents, ReplacementsOrError.get())); | 888 Reply(Result.takeError()); |
916 else | 889 }); |
917 Reply(ReplacementsOrError.takeError()); | |
918 } | 890 } |
919 | 891 |
920 /// The functions constructs a flattened view of the DocumentSymbol hierarchy. | 892 /// The functions constructs a flattened view of the DocumentSymbol hierarchy. |
921 /// Used by the clients that do not support the hierarchical view. | 893 /// Used by the clients that do not support the hierarchical view. |
922 static std::vector<SymbolInformation> | 894 static std::vector<SymbolInformation> |
923 flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols, | 895 flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols, |
924 const URIForFile &FileURI) { | 896 const URIForFile &FileURI) { |
925 | |
926 std::vector<SymbolInformation> Results; | 897 std::vector<SymbolInformation> Results; |
927 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process = | 898 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process = |
928 [&](const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) { | 899 [&](const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) { |
929 SymbolInformation SI; | 900 SymbolInformation SI; |
930 SI.containerName = std::string(ParentName ? "" : *ParentName); | 901 SI.containerName = std::string(ParentName ? "" : *ParentName); |
959 else | 930 else |
960 return Reply(flattenSymbolHierarchy(*Items, FileURI)); | 931 return Reply(flattenSymbolHierarchy(*Items, FileURI)); |
961 }); | 932 }); |
962 } | 933 } |
963 | 934 |
935 void ClangdLSPServer::onFoldingRange( | |
936 const FoldingRangeParams &Params, | |
937 Callback<std::vector<FoldingRange>> Reply) { | |
938 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply)); | |
939 } | |
940 | |
964 static llvm::Optional<Command> asCommand(const CodeAction &Action) { | 941 static llvm::Optional<Command> asCommand(const CodeAction &Action) { |
965 Command Cmd; | 942 Command Cmd; |
966 if (Action.command && Action.edit) | 943 if (Action.command && Action.edit) |
967 return None; // Not representable. (We never emit these anyway). | 944 return None; // Not representable. (We never emit these anyway). |
968 if (Action.command) { | 945 if (Action.command) { |
969 Cmd = *Action.command; | 946 Cmd = *Action.command; |
970 } else if (Action.edit) { | 947 } else if (Action.edit) { |
971 Cmd.command = std::string(Command::CLANGD_APPLY_FIX_COMMAND); | 948 Cmd.command = std::string(APPLY_FIX_COMMAND); |
972 Cmd.workspaceEdit = *Action.edit; | 949 Cmd.argument = *Action.edit; |
973 } else { | 950 } else { |
974 return None; | 951 return None; |
975 } | 952 } |
976 Cmd.title = Action.title; | 953 Cmd.title = Action.title; |
977 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) | 954 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) |
980 } | 957 } |
981 | 958 |
982 void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, | 959 void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, |
983 Callback<llvm::json::Value> Reply) { | 960 Callback<llvm::json::Value> Reply) { |
984 URIForFile File = Params.textDocument.uri; | 961 URIForFile File = Params.textDocument.uri; |
985 auto Code = DraftMgr.getDraft(File.file()); | 962 // Checks whether a particular CodeActionKind is included in the response. |
986 if (!Code) | 963 auto KindAllowed = [Only(Params.context.only)](llvm::StringRef Kind) { |
987 return Reply(llvm::make_error<LSPError>( | 964 if (Only.empty()) |
988 "onCodeAction called for non-added file", ErrorCode::InvalidParams)); | 965 return true; |
966 return llvm::any_of(Only, [&](llvm::StringRef Base) { | |
967 return Kind.consume_front(Base) && (Kind.empty() || Kind.startswith(".")); | |
968 }); | |
969 }; | |
970 | |
989 // We provide a code action for Fixes on the specified diagnostics. | 971 // We provide a code action for Fixes on the specified diagnostics. |
990 std::vector<CodeAction> FixIts; | 972 std::vector<CodeAction> FixIts; |
991 for (const Diagnostic &D : Params.context.diagnostics) { | 973 if (KindAllowed(CodeAction::QUICKFIX_KIND)) { |
992 for (auto &F : getFixes(File.file(), D)) { | 974 for (const Diagnostic &D : Params.context.diagnostics) { |
993 FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); | 975 for (auto &F : getFixes(File.file(), D)) { |
994 FixIts.back().diagnostics = {D}; | 976 FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); |
977 FixIts.back().diagnostics = {D}; | |
978 } | |
995 } | 979 } |
996 } | 980 } |
997 | 981 |
998 // Now enumerate the semantic code actions. | 982 // Now enumerate the semantic code actions. |
999 auto ConsumeActions = | 983 auto ConsumeActions = |
1000 [Reply = std::move(Reply), File, Code = std::move(*Code), | 984 [Reply = std::move(Reply), File, Selection = Params.range, |
1001 Selection = Params.range, FixIts = std::move(FixIts), this]( | 985 FixIts = std::move(FixIts), this]( |
1002 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks) mutable { | 986 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks) mutable { |
1003 if (!Tweaks) | 987 if (!Tweaks) |
1004 return Reply(Tweaks.takeError()); | 988 return Reply(Tweaks.takeError()); |
1005 | 989 |
1006 std::vector<CodeAction> Actions = std::move(FixIts); | 990 std::vector<CodeAction> Actions = std::move(FixIts); |
1007 Actions.reserve(Actions.size() + Tweaks->size()); | 991 Actions.reserve(Actions.size() + Tweaks->size()); |
1008 for (const auto &T : *Tweaks) | 992 for (const auto &T : *Tweaks) |
1009 Actions.push_back(toCodeAction(T, File, Selection)); | 993 Actions.push_back(toCodeAction(T, File, Selection)); |
994 | |
995 // If there's exactly one quick-fix, call it "preferred". | |
996 // We never consider refactorings etc as preferred. | |
997 CodeAction *OnlyFix = nullptr; | |
998 for (auto &Action : Actions) { | |
999 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) { | |
1000 if (OnlyFix) { | |
1001 OnlyFix->isPreferred = false; | |
1002 break; | |
1003 } | |
1004 Action.isPreferred = true; | |
1005 OnlyFix = &Action; | |
1006 } | |
1007 } | |
1010 | 1008 |
1011 if (SupportsCodeAction) | 1009 if (SupportsCodeAction) |
1012 return Reply(llvm::json::Array(Actions)); | 1010 return Reply(llvm::json::Array(Actions)); |
1013 std::vector<Command> Commands; | 1011 std::vector<Command> Commands; |
1014 for (const auto &Action : Actions) { | 1012 for (const auto &Action : Actions) { |
1015 if (auto Command = asCommand(Action)) | 1013 if (auto Command = asCommand(Action)) |
1016 Commands.push_back(std::move(*Command)); | 1014 Commands.push_back(std::move(*Command)); |
1017 } | 1015 } |
1018 return Reply(llvm::json::Array(Commands)); | 1016 return Reply(llvm::json::Array(Commands)); |
1019 }; | 1017 }; |
1020 | 1018 Server->enumerateTweaks( |
1021 Server->enumerateTweaks(File.file(), Params.range, std::move(ConsumeActions)); | 1019 File.file(), Params.range, |
1020 [this, KindAllowed(std::move(KindAllowed))](const Tweak &T) { | |
1021 return Opts.TweakFilter(T) && KindAllowed(T.kind()); | |
1022 }, | |
1023 std::move(ConsumeActions)); | |
1022 } | 1024 } |
1023 | 1025 |
1024 void ClangdLSPServer::onCompletion(const CompletionParams &Params, | 1026 void ClangdLSPServer::onCompletion(const CompletionParams &Params, |
1025 Callback<CompletionList> Reply) { | 1027 Callback<CompletionList> Reply) { |
1026 if (!shouldRunCompletion(Params)) { | 1028 if (!shouldRunCompletion(Params)) { |
1027 // Clients sometimes auto-trigger completions in undesired places (e.g. | 1029 // Clients sometimes auto-trigger completions in undesired places (e.g. |
1028 // 'a >^ '), we return empty results in those cases. | 1030 // 'a >^ '), we return empty results in those cases. |
1029 vlog("ignored auto-triggered completion, preceding char did not match"); | 1031 vlog("ignored auto-triggered completion, preceding char did not match"); |
1030 return Reply(CompletionList()); | 1032 return Reply(CompletionList()); |
1031 } | 1033 } |
1032 Server->codeComplete(Params.textDocument.uri.file(), Params.position, CCOpts, | 1034 auto Opts = this->Opts.CodeComplete; |
1033 [Reply = std::move(Reply), | 1035 if (Params.limit && *Params.limit >= 0) |
1036 Opts.Limit = *Params.limit; | |
1037 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts, | |
1038 [Reply = std::move(Reply), Opts, | |
1034 this](llvm::Expected<CodeCompleteResult> List) mutable { | 1039 this](llvm::Expected<CodeCompleteResult> List) mutable { |
1035 if (!List) | 1040 if (!List) |
1036 return Reply(List.takeError()); | 1041 return Reply(List.takeError()); |
1037 CompletionList LSPList; | 1042 CompletionList LSPList; |
1038 LSPList.isIncomplete = List->HasMore; | 1043 LSPList.isIncomplete = List->HasMore; |
1039 for (const auto &R : List->Completions) { | 1044 for (const auto &R : List->Completions) { |
1040 CompletionItem C = R.render(CCOpts); | 1045 CompletionItem C = R.render(Opts); |
1041 C.kind = adjustKindToCapability( | 1046 C.kind = adjustKindToCapability( |
1042 C.kind, SupportedCompletionItemKinds); | 1047 C.kind, SupportedCompletionItemKinds); |
1043 LSPList.items.push_back(std::move(C)); | 1048 LSPList.items.push_back(std::move(C)); |
1044 } | 1049 } |
1045 return Reply(std::move(LSPList)); | 1050 return Reply(std::move(LSPList)); |
1181 Callback<Optional<TypeHierarchyItem>> Reply) { | 1186 Callback<Optional<TypeHierarchyItem>> Reply) { |
1182 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, | 1187 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, |
1183 std::move(Reply)); | 1188 std::move(Reply)); |
1184 } | 1189 } |
1185 | 1190 |
1191 void ClangdLSPServer::onPrepareCallHierarchy( | |
1192 const CallHierarchyPrepareParams &Params, | |
1193 Callback<std::vector<CallHierarchyItem>> Reply) { | |
1194 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position, | |
1195 std::move(Reply)); | |
1196 } | |
1197 | |
1198 void ClangdLSPServer::onCallHierarchyIncomingCalls( | |
1199 const CallHierarchyIncomingCallsParams &Params, | |
1200 Callback<std::vector<CallHierarchyIncomingCall>> Reply) { | |
1201 Server->incomingCalls(Params.item, std::move(Reply)); | |
1202 } | |
1203 | |
1204 void ClangdLSPServer::onCallHierarchyOutgoingCalls( | |
1205 const CallHierarchyOutgoingCallsParams &Params, | |
1206 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) { | |
1207 // FIXME: To be implemented. | |
1208 Reply(std::vector<CallHierarchyOutgoingCall>{}); | |
1209 } | |
1210 | |
1211 void ClangdLSPServer::onInlayHints(const InlayHintsParams &Params, | |
1212 Callback<std::vector<InlayHint>> Reply) { | |
1213 Server->inlayHints(Params.textDocument.uri.file(), std::move(Reply)); | |
1214 } | |
1215 | |
1186 void ClangdLSPServer::applyConfiguration( | 1216 void ClangdLSPServer::applyConfiguration( |
1187 const ConfigurationSettings &Settings) { | 1217 const ConfigurationSettings &Settings) { |
1188 // Per-file update to the compilation database. | 1218 // Per-file update to the compilation database. |
1189 llvm::StringSet<> ModifiedFiles; | 1219 llvm::StringSet<> ModifiedFiles; |
1190 for (auto &Entry : Settings.compilationDatabaseChanges) { | 1220 for (auto &Entry : Settings.compilationDatabaseChanges) { |
1198 CDB->setCompileCommand(File, std::move(New)); | 1228 CDB->setCompileCommand(File, std::move(New)); |
1199 ModifiedFiles.insert(File); | 1229 ModifiedFiles.insert(File); |
1200 } | 1230 } |
1201 } | 1231 } |
1202 | 1232 |
1203 reparseOpenFilesIfNeeded( | 1233 Server->reparseOpenFilesIfNeeded( |
1204 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); | 1234 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); |
1205 } | 1235 } |
1206 | 1236 |
1207 void ClangdLSPServer::publishTheiaSemanticHighlighting( | 1237 void ClangdLSPServer::maybeExportMemoryProfile() { |
1208 const TheiaSemanticHighlightingParams &Params) { | 1238 if (!trace::enabled() || !ShouldProfile()) |
1209 notify("textDocument/semanticHighlighting", Params); | 1239 return; |
1210 } | 1240 |
1211 | 1241 static constexpr trace::Metric MemoryUsage( |
1212 void ClangdLSPServer::publishDiagnostics( | 1242 "memory_usage", trace::Metric::Value, "component_name"); |
1213 const PublishDiagnosticsParams &Params) { | 1243 trace::Span Tracer("ProfileBrief"); |
1214 notify("textDocument/publishDiagnostics", Params); | 1244 MemoryTree MT; |
1245 profile(MT); | |
1246 record(MT, "clangd_lsp_server", MemoryUsage); | |
1247 } | |
1248 | |
1249 void ClangdLSPServer::maybeCleanupMemory() { | |
1250 if (!Opts.MemoryCleanup || !ShouldCleanupMemory()) | |
1251 return; | |
1252 Opts.MemoryCleanup(); | |
1215 } | 1253 } |
1216 | 1254 |
1217 // FIXME: This function needs to be properly tested. | 1255 // FIXME: This function needs to be properly tested. |
1218 void ClangdLSPServer::onChangeConfiguration( | 1256 void ClangdLSPServer::onChangeConfiguration( |
1219 const DidChangeConfigurationParams &Params) { | 1257 const DidChangeConfigurationParams &Params) { |
1220 applyConfiguration(Params.settings); | 1258 applyConfiguration(Params.settings); |
1221 } | 1259 } |
1222 | 1260 |
1223 void ClangdLSPServer::onReference(const ReferenceParams &Params, | 1261 void ClangdLSPServer::onReference(const ReferenceParams &Params, |
1224 Callback<std::vector<Location>> Reply) { | 1262 Callback<std::vector<Location>> Reply) { |
1225 Server->findReferences(Params.textDocument.uri.file(), Params.position, | 1263 Server->findReferences( |
1226 CCOpts.Limit, | 1264 Params.textDocument.uri.file(), Params.position, Opts.ReferencesLimit, |
1227 [Reply = std::move(Reply)]( | 1265 [Reply = std::move(Reply), |
1228 llvm::Expected<ReferencesResult> Refs) mutable { | 1266 IncludeDecl(Params.context.includeDeclaration)]( |
1229 if (!Refs) | 1267 llvm::Expected<ReferencesResult> Refs) mutable { |
1230 return Reply(Refs.takeError()); | 1268 if (!Refs) |
1231 return Reply(std::move(Refs->References)); | 1269 return Reply(Refs.takeError()); |
1232 }); | 1270 // Filter out declarations if the client asked. |
1271 std::vector<Location> Result; | |
1272 Result.reserve(Refs->References.size()); | |
1273 for (auto &Ref : Refs->References) { | |
1274 bool IsDecl = Ref.Attributes & ReferencesResult::Declaration; | |
1275 if (IncludeDecl || !IsDecl) | |
1276 Result.push_back(std::move(Ref.Loc)); | |
1277 } | |
1278 return Reply(std::move(Result)); | |
1279 }); | |
1280 } | |
1281 | |
1282 void ClangdLSPServer::onGoToImplementation( | |
1283 const TextDocumentPositionParams &Params, | |
1284 Callback<std::vector<Location>> Reply) { | |
1285 Server->findImplementations( | |
1286 Params.textDocument.uri.file(), Params.position, | |
1287 [Reply = std::move(Reply)]( | |
1288 llvm::Expected<std::vector<LocatedSymbol>> Overrides) mutable { | |
1289 if (!Overrides) | |
1290 return Reply(Overrides.takeError()); | |
1291 std::vector<Location> Impls; | |
1292 for (const LocatedSymbol &Sym : *Overrides) | |
1293 Impls.push_back(Sym.PreferredDeclaration); | |
1294 return Reply(std::move(Impls)); | |
1295 }); | |
1233 } | 1296 } |
1234 | 1297 |
1235 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, | 1298 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, |
1236 Callback<std::vector<SymbolDetails>> Reply) { | 1299 Callback<std::vector<SymbolDetails>> Reply) { |
1237 Server->symbolInfo(Params.textDocument.uri.file(), Params.position, | 1300 Server->symbolInfo(Params.textDocument.uri.file(), Params.position, |
1302 } | 1365 } |
1303 CB(std::move(Result)); | 1366 CB(std::move(Result)); |
1304 }); | 1367 }); |
1305 } | 1368 } |
1306 | 1369 |
1307 void ClangdLSPServer::onSemanticTokensEdits( | 1370 void ClangdLSPServer::onSemanticTokensDelta( |
1308 const SemanticTokensEditsParams &Params, | 1371 const SemanticTokensDeltaParams &Params, |
1309 Callback<SemanticTokensOrEdits> CB) { | 1372 Callback<SemanticTokensOrDelta> CB) { |
1310 Server->semanticHighlights( | 1373 Server->semanticHighlights( |
1311 Params.textDocument.uri.file(), | 1374 Params.textDocument.uri.file(), |
1312 [this, PrevResultID(Params.previousResultId), | 1375 [this, PrevResultID(Params.previousResultId), |
1313 File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( | 1376 File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( |
1314 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { | 1377 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { |
1315 if (!HT) | 1378 if (!HT) |
1316 return CB(HT.takeError()); | 1379 return CB(HT.takeError()); |
1317 std::vector<SemanticToken> Toks = toSemanticTokens(*HT); | 1380 std::vector<SemanticToken> Toks = toSemanticTokens(*HT); |
1318 | 1381 |
1319 SemanticTokensOrEdits Result; | 1382 SemanticTokensOrDelta Result; |
1320 { | 1383 { |
1321 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); | 1384 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); |
1322 auto &Last = LastSemanticTokens[File]; | 1385 auto &Last = LastSemanticTokens[File]; |
1323 | 1386 |
1324 if (PrevResultID == Last.resultId) { | 1387 if (PrevResultID == Last.resultId) { |
1325 Result.edits = diffTokens(Last.tokens, Toks); | 1388 Result.edits = diffTokens(Last.tokens, Toks); |
1326 } else { | 1389 } else { |
1327 vlog("semanticTokens/edits: wanted edits vs {0} but last result " | 1390 vlog("semanticTokens/full/delta: wanted edits vs {0} but last " |
1328 "had ID {1}. Returning full token list.", | 1391 "result had ID {1}. Returning full token list.", |
1329 PrevResultID, Last.resultId); | 1392 PrevResultID, Last.resultId); |
1330 Result.tokens = Toks; | 1393 Result.tokens = Toks; |
1331 } | 1394 } |
1332 | 1395 |
1333 Last.tokens = std::move(Toks); | 1396 Last.tokens = std::move(Toks); |
1337 | 1400 |
1338 CB(std::move(Result)); | 1401 CB(std::move(Result)); |
1339 }); | 1402 }); |
1340 } | 1403 } |
1341 | 1404 |
1342 ClangdLSPServer::ClangdLSPServer( | 1405 void ClangdLSPServer::onMemoryUsage(const NoParams &, |
1343 class Transport &Transp, const FileSystemProvider &FSProvider, | 1406 Callback<MemoryTree> Reply) { |
1344 const clangd::CodeCompleteOptions &CCOpts, | 1407 llvm::BumpPtrAllocator DetailAlloc; |
1345 const clangd::RenameOptions &RenameOpts, | 1408 MemoryTree MT(&DetailAlloc); |
1346 llvm::Optional<Path> CompileCommandsDir, bool UseDirBasedCDB, | 1409 profile(MT); |
1347 llvm::Optional<OffsetEncoding> ForcedOffsetEncoding, | 1410 Reply(std::move(MT)); |
1348 const ClangdServer::Options &Opts) | 1411 } |
1349 : BackgroundContext(Context::current().clone()), Transp(Transp), | 1412 |
1350 MsgHandler(new MessageHandler(*this)), FSProvider(FSProvider), | 1413 void ClangdLSPServer::onAST(const ASTParams &Params, |
1351 CCOpts(CCOpts), RenameOpts(RenameOpts), | 1414 Callback<llvm::Optional<ASTNode>> CB) { |
1415 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB)); | |
1416 } | |
1417 | |
1418 ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, | |
1419 const ClangdLSPServer::Options &Opts) | |
1420 : ShouldProfile(/*Period=*/std::chrono::minutes(5), | |
1421 /*Delay=*/std::chrono::minutes(1)), | |
1422 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), | |
1423 /*Delay=*/std::chrono::minutes(1)), | |
1424 BackgroundContext(Context::current().clone()), Transp(Transp), | |
1425 MsgHandler(new MessageHandler(*this)), TFS(TFS), | |
1352 SupportedSymbolKinds(defaultSymbolKinds()), | 1426 SupportedSymbolKinds(defaultSymbolKinds()), |
1353 SupportedCompletionItemKinds(defaultCompletionItemKinds()), | 1427 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { |
1354 UseDirBasedCDB(UseDirBasedCDB), | 1428 if (Opts.ConfigProvider) { |
1355 CompileCommandsDir(std::move(CompileCommandsDir)), ClangdServerOpts(Opts), | 1429 assert(!Opts.ContextProvider && |
1356 NegotiatedOffsetEncoding(ForcedOffsetEncoding) { | 1430 "Only one of ConfigProvider and ContextProvider allowed!"); |
1431 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( | |
1432 Opts.ConfigProvider, this); | |
1433 } | |
1434 LSPBinder Bind(this->Handlers, *this); | |
1435 Bind.method("initialize", this, &ClangdLSPServer::onInitialize); | |
1436 } | |
1437 | |
1438 void ClangdLSPServer::bindMethods(LSPBinder &Bind, | |
1439 const ClientCapabilities &Caps) { | |
1357 // clang-format off | 1440 // clang-format off |
1358 MsgHandler->bind("initialize", &ClangdLSPServer::onInitialize); | 1441 Bind.notification("initialized", this, &ClangdLSPServer::onInitialized); |
1359 MsgHandler->bind("initialized", &ClangdLSPServer::onInitialized); | 1442 Bind.method("shutdown", this, &ClangdLSPServer::onShutdown); |
1360 MsgHandler->bind("shutdown", &ClangdLSPServer::onShutdown); | 1443 Bind.method("sync", this, &ClangdLSPServer::onSync); |
1361 MsgHandler->bind("sync", &ClangdLSPServer::onSync); | 1444 Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting); |
1362 MsgHandler->bind("textDocument/rangeFormatting", &ClangdLSPServer::onDocumentRangeFormatting); | 1445 Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting); |
1363 MsgHandler->bind("textDocument/onTypeFormatting", &ClangdLSPServer::onDocumentOnTypeFormatting); | 1446 Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting); |
1364 MsgHandler->bind("textDocument/formatting", &ClangdLSPServer::onDocumentFormatting); | 1447 Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction); |
1365 MsgHandler->bind("textDocument/codeAction", &ClangdLSPServer::onCodeAction); | 1448 Bind.method("textDocument/completion", this, &ClangdLSPServer::onCompletion); |
1366 MsgHandler->bind("textDocument/completion", &ClangdLSPServer::onCompletion); | 1449 Bind.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp); |
1367 MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp); | 1450 Bind.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition); |
1368 MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition); | 1451 Bind.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration); |
1369 MsgHandler->bind("textDocument/declaration", &ClangdLSPServer::onGoToDeclaration); | 1452 Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation); |
1370 MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference); | 1453 Bind.method("textDocument/references", this, &ClangdLSPServer::onReference); |
1371 MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader); | 1454 Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); |
1372 MsgHandler->bind("textDocument/prepareRename", &ClangdLSPServer::onPrepareRename); | 1455 Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); |
1373 MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename); | 1456 Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); |
1374 MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover); | 1457 Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); |
1375 MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol); | 1458 Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); |
1376 MsgHandler->bind("workspace/executeCommand", &ClangdLSPServer::onCommand); | 1459 Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); |
1377 MsgHandler->bind("textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight); | 1460 Bind.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight); |
1378 MsgHandler->bind("workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol); | 1461 Bind.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol); |
1379 MsgHandler->bind("textDocument/didOpen", &ClangdLSPServer::onDocumentDidOpen); | 1462 Bind.method("textDocument/ast", this, &ClangdLSPServer::onAST); |
1380 MsgHandler->bind("textDocument/didClose", &ClangdLSPServer::onDocumentDidClose); | 1463 Bind.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen); |
1381 MsgHandler->bind("textDocument/didChange", &ClangdLSPServer::onDocumentDidChange); | 1464 Bind.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose); |
1382 MsgHandler->bind("textDocument/didSave", &ClangdLSPServer::onDocumentDidSave); | 1465 Bind.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange); |
1383 MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent); | 1466 Bind.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave); |
1384 MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration); | 1467 Bind.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent); |
1385 MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo); | 1468 Bind.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration); |
1386 MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy); | 1469 Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo); |
1387 MsgHandler->bind("typeHierarchy/resolve", &ClangdLSPServer::onResolveTypeHierarchy); | 1470 Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy); |
1388 MsgHandler->bind("textDocument/selectionRange", &ClangdLSPServer::onSelectionRange); | 1471 Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy); |
1389 MsgHandler->bind("textDocument/documentLink", &ClangdLSPServer::onDocumentLink); | 1472 Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy); |
1390 MsgHandler->bind("textDocument/semanticTokens", &ClangdLSPServer::onSemanticTokens); | 1473 Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls); |
1391 MsgHandler->bind("textDocument/semanticTokens/edits", &ClangdLSPServer::onSemanticTokensEdits); | 1474 Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls); |
1475 Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange); | |
1476 Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink); | |
1477 Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens); | |
1478 Bind.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta); | |
1479 Bind.method("clangd/inlayHints", this, &ClangdLSPServer::onInlayHints); | |
1480 Bind.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage); | |
1481 if (Opts.FoldingRanges) | |
1482 Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange); | |
1483 Bind.command(APPLY_FIX_COMMAND, this, &ClangdLSPServer::onCommandApplyEdit); | |
1484 Bind.command(APPLY_TWEAK_COMMAND, this, &ClangdLSPServer::onCommandApplyTweak); | |
1485 | |
1486 ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit"); | |
1487 PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics"); | |
1488 ShowMessage = Bind.outgoingNotification("window/showMessage"); | |
1489 NotifyFileStatus = Bind.outgoingNotification("textDocument/clangd.fileStatus"); | |
1490 CreateWorkDoneProgress = Bind.outgoingMethod("window/workDoneProgress/create"); | |
1491 BeginWorkDoneProgress = Bind.outgoingNotification("$/progress"); | |
1492 ReportWorkDoneProgress = Bind.outgoingNotification("$/progress"); | |
1493 EndWorkDoneProgress = Bind.outgoingNotification("$/progress"); | |
1494 if(Caps.SemanticTokenRefreshSupport) | |
1495 SemanticTokensRefresh = Bind.outgoingMethod("workspace/semanticTokens/refresh"); | |
1392 // clang-format on | 1496 // clang-format on |
1393 } | 1497 } |
1394 | 1498 |
1395 ClangdLSPServer::~ClangdLSPServer() { | 1499 ClangdLSPServer::~ClangdLSPServer() { |
1396 IsBeingDestroyed = true; | 1500 IsBeingDestroyed = true; |
1406 elog("Transport error: {0}", std::move(Err)); | 1510 elog("Transport error: {0}", std::move(Err)); |
1407 CleanExit = false; | 1511 CleanExit = false; |
1408 } | 1512 } |
1409 | 1513 |
1410 return CleanExit && ShutdownRequestReceived; | 1514 return CleanExit && ShutdownRequestReceived; |
1515 } | |
1516 | |
1517 void ClangdLSPServer::profile(MemoryTree &MT) const { | |
1518 if (Server) | |
1519 Server->profile(MT.child("clangd_server")); | |
1411 } | 1520 } |
1412 | 1521 |
1413 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File, | 1522 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File, |
1414 const clangd::Diagnostic &D) { | 1523 const clangd::Diagnostic &D) { |
1415 std::lock_guard<std::mutex> Lock(FixItsMutex); | 1524 std::lock_guard<std::mutex> Lock(FixItsMutex); |
1433 // of simplicity here. | 1542 // of simplicity here. |
1434 bool ClangdLSPServer::shouldRunCompletion( | 1543 bool ClangdLSPServer::shouldRunCompletion( |
1435 const CompletionParams &Params) const { | 1544 const CompletionParams &Params) const { |
1436 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter) | 1545 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter) |
1437 return true; | 1546 return true; |
1438 auto Code = DraftMgr.getDraft(Params.textDocument.uri.file()); | 1547 auto Code = Server->getDraft(Params.textDocument.uri.file()); |
1439 if (!Code) | 1548 if (!Code) |
1440 return true; // completion code will log the error for untracked doc. | 1549 return true; // completion code will log the error for untracked doc. |
1441 auto Offset = positionToOffset(Code->Contents, Params.position, | 1550 auto Offset = positionToOffset(*Code, Params.position, |
1442 /*AllowColumnsBeyondLineLength=*/false); | 1551 /*AllowColumnsBeyondLineLength=*/false); |
1443 if (!Offset) { | 1552 if (!Offset) { |
1444 vlog("could not convert position '{0}' to offset for file '{1}'", | 1553 vlog("could not convert position '{0}' to offset for file '{1}'", |
1445 Params.position, Params.textDocument.uri.file()); | 1554 Params.position, Params.textDocument.uri.file()); |
1446 return true; | 1555 return true; |
1447 } | 1556 } |
1448 return allowImplicitCompletion(Code->Contents, *Offset); | 1557 return allowImplicitCompletion(*Code, *Offset); |
1449 } | |
1450 | |
1451 void ClangdLSPServer::onHighlightingsReady( | |
1452 PathRef File, llvm::StringRef Version, | |
1453 std::vector<HighlightingToken> Highlightings) { | |
1454 std::vector<HighlightingToken> Old; | |
1455 std::vector<HighlightingToken> HighlightingsCopy = Highlightings; | |
1456 { | |
1457 std::lock_guard<std::mutex> Lock(HighlightingsMutex); | |
1458 Old = std::move(FileToHighlightings[File]); | |
1459 FileToHighlightings[File] = std::move(HighlightingsCopy); | |
1460 } | |
1461 // LSP allows us to send incremental edits of highlightings. Also need to diff | |
1462 // to remove highlightings from tokens that should no longer have them. | |
1463 std::vector<LineHighlightings> Diffed = diffHighlightings(Highlightings, Old); | |
1464 TheiaSemanticHighlightingParams Notification; | |
1465 Notification.TextDocument.uri = | |
1466 URIForFile::canonicalize(File, /*TUPath=*/File); | |
1467 Notification.TextDocument.version = decodeVersion(Version); | |
1468 Notification.Lines = toTheiaSemanticHighlightingInformation(Diffed); | |
1469 publishTheiaSemanticHighlighting(Notification); | |
1470 } | 1558 } |
1471 | 1559 |
1472 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, | 1560 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, |
1473 std::vector<Diag> Diagnostics) { | 1561 std::vector<Diag> Diagnostics) { |
1474 PublishDiagnosticsParams Notification; | 1562 PublishDiagnosticsParams Notification; |
1489 std::lock_guard<std::mutex> Lock(FixItsMutex); | 1577 std::lock_guard<std::mutex> Lock(FixItsMutex); |
1490 FixItsMap[File] = LocalFixIts; | 1578 FixItsMap[File] = LocalFixIts; |
1491 } | 1579 } |
1492 | 1580 |
1493 // Send a notification to the LSP client. | 1581 // Send a notification to the LSP client. |
1494 publishDiagnostics(Notification); | 1582 PublishDiagnostics(Notification); |
1495 } | 1583 } |
1496 | 1584 |
1497 void ClangdLSPServer::onBackgroundIndexProgress( | 1585 void ClangdLSPServer::onBackgroundIndexProgress( |
1498 const BackgroundQueue::Stats &Stats) { | 1586 const BackgroundQueue::Stats &Stats) { |
1499 static const char ProgressToken[] = "backgroundIndexProgress"; | 1587 static const char ProgressToken[] = "backgroundIndexProgress"; |
1588 | |
1589 // The background index did some work, maybe we need to cleanup | |
1590 maybeCleanupMemory(); | |
1591 | |
1500 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); | 1592 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); |
1501 | 1593 |
1502 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) { | 1594 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) { |
1503 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) { | 1595 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) { |
1504 WorkDoneProgressBegin Begin; | 1596 WorkDoneProgressBegin Begin; |
1505 Begin.percentage = true; | 1597 Begin.percentage = true; |
1506 Begin.title = "indexing"; | 1598 Begin.title = "indexing"; |
1507 progress(ProgressToken, std::move(Begin)); | 1599 BeginWorkDoneProgress({ProgressToken, std::move(Begin)}); |
1508 BackgroundIndexProgressState = BackgroundIndexProgress::Live; | 1600 BackgroundIndexProgressState = BackgroundIndexProgress::Live; |
1509 } | 1601 } |
1510 | 1602 |
1511 if (Stats.Completed < Stats.Enqueued) { | 1603 if (Stats.Completed < Stats.Enqueued) { |
1512 assert(Stats.Enqueued > Stats.LastIdle); | 1604 assert(Stats.Enqueued > Stats.LastIdle); |
1513 WorkDoneProgressReport Report; | 1605 WorkDoneProgressReport Report; |
1514 Report.percentage = 100.0 * (Stats.Completed - Stats.LastIdle) / | 1606 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) / |
1515 (Stats.Enqueued - Stats.LastIdle); | 1607 (Stats.Enqueued - Stats.LastIdle); |
1516 Report.message = | 1608 Report.message = |
1517 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle, | 1609 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle, |
1518 Stats.Enqueued - Stats.LastIdle); | 1610 Stats.Enqueued - Stats.LastIdle); |
1519 progress(ProgressToken, std::move(Report)); | 1611 ReportWorkDoneProgress({ProgressToken, std::move(Report)}); |
1520 } else { | 1612 } else { |
1521 assert(Stats.Completed == Stats.Enqueued); | 1613 assert(Stats.Completed == Stats.Enqueued); |
1522 progress(ProgressToken, WorkDoneProgressEnd()); | 1614 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()}); |
1523 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; | 1615 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; |
1524 } | 1616 } |
1525 }; | 1617 }; |
1526 | 1618 |
1527 switch (BackgroundIndexProgressState) { | 1619 switch (BackgroundIndexProgressState) { |
1539 // Cache this update for when the progress bar is available. | 1631 // Cache this update for when the progress bar is available. |
1540 PendingBackgroundIndexProgress = Stats; | 1632 PendingBackgroundIndexProgress = Stats; |
1541 BackgroundIndexProgressState = BackgroundIndexProgress::Creating; | 1633 BackgroundIndexProgressState = BackgroundIndexProgress::Creating; |
1542 WorkDoneProgressCreateParams CreateRequest; | 1634 WorkDoneProgressCreateParams CreateRequest; |
1543 CreateRequest.token = ProgressToken; | 1635 CreateRequest.token = ProgressToken; |
1544 call<std::nullptr_t>( | 1636 CreateWorkDoneProgress( |
1545 "window/workDoneProgress/create", CreateRequest, | 1637 CreateRequest, |
1546 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) { | 1638 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) { |
1547 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); | 1639 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); |
1548 if (E) { | 1640 if (E) { |
1549 NotifyProgress(this->PendingBackgroundIndexProgress); | 1641 NotifyProgress(this->PendingBackgroundIndexProgress); |
1550 } else { | 1642 } else { |
1571 // reasonable time interval (e.g. 0.5s). | 1663 // reasonable time interval (e.g. 0.5s). |
1572 if (Status.PreambleActivity == PreambleAction::Idle && | 1664 if (Status.PreambleActivity == PreambleAction::Idle && |
1573 (Status.ASTActivity.K == ASTAction::Building || | 1665 (Status.ASTActivity.K == ASTAction::Building || |
1574 Status.ASTActivity.K == ASTAction::RunningAction)) | 1666 Status.ASTActivity.K == ASTAction::RunningAction)) |
1575 return; | 1667 return; |
1576 notify("textDocument/clangd.fileStatus", Status.render(File)); | 1668 NotifyFileStatus(Status.render(File)); |
1577 } | 1669 } |
1578 | 1670 |
1579 void ClangdLSPServer::reparseOpenFilesIfNeeded( | 1671 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File) { |
1580 llvm::function_ref<bool(llvm::StringRef File)> Filter) { | 1672 if (SemanticTokensRefresh) { |
1581 // Reparse only opened files that were modified. | 1673 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) { |
1582 for (const Path &FilePath : DraftMgr.getActiveFiles()) | 1674 if (E) |
1583 if (Filter(FilePath)) | 1675 return; |
1584 if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race? | 1676 elog("Failed to refresh semantic tokens: {0}", E.takeError()); |
1585 Server->addDocument(FilePath, std::move(Draft->Contents), | 1677 }); |
1586 encodeVersion(Draft->Version), | 1678 } |
1587 WantDiagnostics::Auto); | 1679 } |
1588 } | |
1589 | |
1590 } // namespace clangd | 1680 } // namespace clangd |
1591 } // namespace clang | 1681 } // namespace clang |