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