1 //===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ClangdLSPServer.h" 10 #include "ClangdServer.h" 11 #include "CodeComplete.h" 12 #include "Diagnostics.h" 13 #include "DraftStore.h" 14 #include "DumpAST.h" 15 #include "GlobalCompilationDatabase.h" 16 #include "LSPBinder.h" 17 #include "Protocol.h" 18 #include "SemanticHighlighting.h" 19 #include "SourceCode.h" 20 #include "TUScheduler.h" 21 #include "URI.h" 22 #include "refactor/Tweak.h" 23 #include "support/Context.h" 24 #include "support/MemoryTree.h" 25 #include "support/Trace.h" 26 #include "clang/AST/ASTContext.h" 27 #include "clang/Basic/Version.h" 28 #include "clang/Tooling/Core/Replacement.h" 29 #include "llvm/ADT/ArrayRef.h" 30 #include "llvm/ADT/Optional.h" 31 #include "llvm/ADT/ScopeExit.h" 32 #include "llvm/ADT/StringRef.h" 33 #include "llvm/ADT/iterator_range.h" 34 #include "llvm/Support/Allocator.h" 35 #include "llvm/Support/Errc.h" 36 #include "llvm/Support/Error.h" 37 #include "llvm/Support/FormatVariadic.h" 38 #include "llvm/Support/JSON.h" 39 #include "llvm/Support/Path.h" 40 #include "llvm/Support/SHA1.h" 41 #include "llvm/Support/ScopedPrinter.h" 42 #include "llvm/Support/raw_ostream.h" 43 #include <chrono> 44 #include <cstddef> 45 #include <cstdint> 46 #include <functional> 47 #include <memory> 48 #include <mutex> 49 #include <string> 50 #include <vector> 51 52 namespace clang { 53 namespace clangd { 54 namespace { 55 // Tracks end-to-end latency of high level lsp calls. Measurements are in 56 // seconds. 57 constexpr trace::Metric LSPLatency("lsp_latency", trace::Metric::Distribution, 58 "method_name"); 59 60 // LSP defines file versions as numbers that increase. 61 // ClangdServer treats them as opaque and therefore uses strings instead. 62 std::string encodeVersion(llvm::Optional<int64_t> LSPVersion) { 63 return LSPVersion ? llvm::to_string(*LSPVersion) : ""; 64 } 65 llvm::Optional<int64_t> decodeVersion(llvm::StringRef Encoded) { 66 int64_t Result; 67 if (llvm::to_integer(Encoded, Result, 10)) 68 return Result; 69 if (!Encoded.empty()) // Empty can be e.g. diagnostics on close. 70 elog("unexpected non-numeric version {0}", Encoded); 71 return llvm::None; 72 } 73 74 const llvm::StringLiteral APPLY_FIX_COMMAND = "clangd.applyFix"; 75 const llvm::StringLiteral APPLY_TWEAK_COMMAND = "clangd.applyTweak"; 76 77 /// Transforms a tweak into a code action that would apply it if executed. 78 /// EXPECTS: T.prepare() was called and returned true. 79 CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File, 80 Range Selection) { 81 CodeAction CA; 82 CA.title = T.Title; 83 CA.kind = T.Kind.str(); 84 // This tweak may have an expensive second stage, we only run it if the user 85 // actually chooses it in the UI. We reply with a command that would run the 86 // corresponding tweak. 87 // FIXME: for some tweaks, computing the edits is cheap and we could send them 88 // directly. 89 CA.command.emplace(); 90 CA.command->title = T.Title; 91 CA.command->command = std::string(APPLY_TWEAK_COMMAND); 92 TweakArgs Args; 93 Args.file = File; 94 Args.tweakID = T.ID; 95 Args.selection = Selection; 96 CA.command->argument = std::move(Args); 97 return CA; 98 } 99 100 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms, 101 SymbolKindBitset Kinds) { 102 for (auto &S : Syms) { 103 S.kind = adjustKindToCapability(S.kind, Kinds); 104 adjustSymbolKinds(S.children, Kinds); 105 } 106 } 107 108 SymbolKindBitset defaultSymbolKinds() { 109 SymbolKindBitset Defaults; 110 for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array); 111 ++I) 112 Defaults.set(I); 113 return Defaults; 114 } 115 116 CompletionItemKindBitset defaultCompletionItemKinds() { 117 CompletionItemKindBitset Defaults; 118 for (size_t I = CompletionItemKindMin; 119 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I) 120 Defaults.set(I); 121 return Defaults; 122 } 123 124 // Makes sure edits in \p FE are applicable to latest file contents reported by 125 // editor. If not generates an error message containing information about files 126 // that needs to be saved. 127 llvm::Error validateEdits(const ClangdServer &Server, const FileEdits &FE) { 128 size_t InvalidFileCount = 0; 129 llvm::StringRef LastInvalidFile; 130 for (const auto &It : FE) { 131 if (auto Draft = Server.getDraft(It.first())) { 132 // If the file is open in user's editor, make sure the version we 133 // saw and current version are compatible as this is the text that 134 // will be replaced by editors. 135 if (!It.second.canApplyTo(*Draft)) { 136 ++InvalidFileCount; 137 LastInvalidFile = It.first(); 138 } 139 } 140 } 141 if (!InvalidFileCount) 142 return llvm::Error::success(); 143 if (InvalidFileCount == 1) 144 return error("File must be saved first: {0}", LastInvalidFile); 145 return error("Files must be saved first: {0} (and {1} others)", 146 LastInvalidFile, InvalidFileCount - 1); 147 } 148 } // namespace 149 150 // MessageHandler dispatches incoming LSP messages. 151 // It handles cross-cutting concerns: 152 // - serializes/deserializes protocol objects to JSON 153 // - logging of inbound messages 154 // - cancellation handling 155 // - basic call tracing 156 // MessageHandler ensures that initialize() is called before any other handler. 157 class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { 158 public: 159 MessageHandler(ClangdLSPServer &Server) : Server(Server) {} 160 161 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { 162 trace::Span Tracer(Method, LSPLatency); 163 SPAN_ATTACH(Tracer, "Params", Params); 164 WithContext HandlerContext(handlerContext()); 165 log("<-- {0}", Method); 166 if (Method == "exit") 167 return false; 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) { 174 elog("Notification {0} before initialization", Method); 175 } else if (Method == "$/cancelRequest") { 176 onCancel(std::move(Params)); 177 } else { 178 log("unhandled notification {0}", Method); 179 } 180 return true; 181 } 182 183 bool onCall(llvm::StringRef Method, llvm::json::Value Params, 184 llvm::json::Value ID) override { 185 WithContext HandlerContext(handlerContext()); 186 // Calls can be canceled by the client. Add cancellation context. 187 WithContext WithCancel(cancelableRequestContext(ID)); 188 trace::Span Tracer(Method, LSPLatency); 189 SPAN_ATTACH(Tracer, "Params", Params); 190 ReplyOnce Reply(ID, Method, &Server, Tracer.Args); 191 log("<-- {0}({1})", Method, ID); 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) { 196 elog("Call {0} before initialization.", Method); 197 Reply(llvm::make_error<LSPError>("server not initialized", 198 ErrorCode::ServerNotInitialized)); 199 } else { 200 Reply(llvm::make_error<LSPError>("method not found", 201 ErrorCode::MethodNotFound)); 202 } 203 return true; 204 } 205 206 bool onReply(llvm::json::Value ID, 207 llvm::Expected<llvm::json::Value> Result) override { 208 WithContext HandlerContext(handlerContext()); 209 210 Callback<llvm::json::Value> ReplyHandler = nullptr; 211 if (auto IntID = ID.getAsInteger()) { 212 std::lock_guard<std::mutex> Mutex(CallMutex); 213 // Find a corresponding callback for the request ID; 214 for (size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) { 215 if (ReplyCallbacks[Index].first == *IntID) { 216 ReplyHandler = std::move(ReplyCallbacks[Index].second); 217 ReplyCallbacks.erase(ReplyCallbacks.begin() + 218 Index); // remove the entry 219 break; 220 } 221 } 222 } 223 224 if (!ReplyHandler) { 225 // No callback being found, use a default log callback. 226 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) { 227 elog("received a reply with ID {0}, but there was no such call", ID); 228 if (!Result) 229 llvm::consumeError(Result.takeError()); 230 }; 231 } 232 233 // Log and run the reply handler. 234 if (Result) { 235 log("<-- reply({0})", ID); 236 ReplyHandler(std::move(Result)); 237 } else { 238 auto Err = Result.takeError(); 239 log("<-- reply({0}) error: {1}", ID, Err); 240 ReplyHandler(std::move(Err)); 241 } 242 return true; 243 } 244 245 // Bind a reply callback to a request. The callback will be invoked when 246 // clangd receives the reply from the LSP client. 247 // Return a call id of the request. 248 llvm::json::Value bindReply(Callback<llvm::json::Value> Reply) { 249 llvm::Optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB; 250 int ID; 251 { 252 std::lock_guard<std::mutex> Mutex(CallMutex); 253 ID = NextCallID++; 254 ReplyCallbacks.emplace_back(ID, std::move(Reply)); 255 256 // If the queue overflows, we assume that the client didn't reply the 257 // oldest request, and run the corresponding callback which replies an 258 // error to the client. 259 if (ReplyCallbacks.size() > MaxReplayCallbacks) { 260 elog("more than {0} outstanding LSP calls, forgetting about {1}", 261 MaxReplayCallbacks, ReplyCallbacks.front().first); 262 OldestCB = std::move(ReplyCallbacks.front()); 263 ReplyCallbacks.pop_front(); 264 } 265 } 266 if (OldestCB) 267 OldestCB->second( 268 error("failed to receive a client reply for request ({0})", 269 OldestCB->first)); 270 return ID; 271 } 272 273 private: 274 // Function object to reply to an LSP call. 275 // Each instance must be called exactly once, otherwise: 276 // - the bug is logged, and (in debug mode) an assert will fire 277 // - if there was no reply, an error reply is sent 278 // - if there were multiple replies, only the first is sent 279 class ReplyOnce { 280 std::atomic<bool> Replied = {false}; 281 std::chrono::steady_clock::time_point Start; 282 llvm::json::Value ID; 283 std::string Method; 284 ClangdLSPServer *Server; // Null when moved-from. 285 llvm::json::Object *TraceArgs; 286 287 public: 288 ReplyOnce(const llvm::json::Value &ID, llvm::StringRef Method, 289 ClangdLSPServer *Server, llvm::json::Object *TraceArgs) 290 : Start(std::chrono::steady_clock::now()), ID(ID), Method(Method), 291 Server(Server), TraceArgs(TraceArgs) { 292 assert(Server); 293 } 294 ReplyOnce(ReplyOnce &&Other) 295 : Replied(Other.Replied.load()), Start(Other.Start), 296 ID(std::move(Other.ID)), Method(std::move(Other.Method)), 297 Server(Other.Server), TraceArgs(Other.TraceArgs) { 298 Other.Server = nullptr; 299 } 300 ReplyOnce &operator=(ReplyOnce &&) = delete; 301 ReplyOnce(const ReplyOnce &) = delete; 302 ReplyOnce &operator=(const ReplyOnce &) = delete; 303 304 ~ReplyOnce() { 305 // There's one legitimate reason to never reply to a request: clangd's 306 // request handler send a call to the client (e.g. applyEdit) and the 307 // client never replied. In this case, the ReplyOnce is owned by 308 // ClangdLSPServer's reply callback table and is destroyed along with the 309 // server. We don't attempt to send a reply in this case, there's little 310 // to be gained from doing so. 311 if (Server && !Server->IsBeingDestroyed && !Replied) { 312 elog("No reply to message {0}({1})", Method, ID); 313 assert(false && "must reply to all calls!"); 314 (*this)(llvm::make_error<LSPError>("server failed to reply", 315 ErrorCode::InternalError)); 316 } 317 } 318 319 void operator()(llvm::Expected<llvm::json::Value> Reply) { 320 assert(Server && "moved-from!"); 321 if (Replied.exchange(true)) { 322 elog("Replied twice to message {0}({1})", Method, ID); 323 assert(false && "must reply to each call only once!"); 324 return; 325 } 326 auto Duration = std::chrono::steady_clock::now() - Start; 327 if (Reply) { 328 log("--> reply:{0}({1}) {2:ms}", Method, ID, Duration); 329 if (TraceArgs) 330 (*TraceArgs)["Reply"] = *Reply; 331 std::lock_guard<std::mutex> Lock(Server->TranspWriter); 332 Server->Transp.reply(std::move(ID), std::move(Reply)); 333 } else { 334 llvm::Error Err = Reply.takeError(); 335 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err); 336 if (TraceArgs) 337 (*TraceArgs)["Error"] = llvm::to_string(Err); 338 std::lock_guard<std::mutex> Lock(Server->TranspWriter); 339 Server->Transp.reply(std::move(ID), std::move(Err)); 340 } 341 } 342 }; 343 344 // Method calls may be cancelled by ID, so keep track of their state. 345 // This needs a mutex: handlers may finish on a different thread, and that's 346 // when we clean up entries in the map. 347 mutable std::mutex RequestCancelersMutex; 348 llvm::StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers; 349 unsigned NextRequestCookie = 0; // To disambiguate reused IDs, see below. 350 void onCancel(const llvm::json::Value &Params) { 351 const llvm::json::Value *ID = nullptr; 352 if (auto *O = Params.getAsObject()) 353 ID = O->get("id"); 354 if (!ID) { 355 elog("Bad cancellation request: {0}", Params); 356 return; 357 } 358 auto StrID = llvm::to_string(*ID); 359 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 360 auto It = RequestCancelers.find(StrID); 361 if (It != RequestCancelers.end()) 362 It->second.first(); // Invoke the canceler. 363 } 364 365 Context handlerContext() const { 366 return Context::current().derive( 367 kCurrentOffsetEncoding, 368 Server.Opts.Encoding.getValueOr(OffsetEncoding::UTF16)); 369 } 370 371 // We run cancelable requests in a context that does two things: 372 // - allows cancellation using RequestCancelers[ID] 373 // - cleans up the entry in RequestCancelers when it's no longer needed 374 // If a client reuses an ID, the last wins and the first cannot be canceled. 375 Context cancelableRequestContext(const llvm::json::Value &ID) { 376 auto Task = cancelableTask( 377 /*Reason=*/static_cast<int>(ErrorCode::RequestCancelled)); 378 auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key. 379 auto Cookie = NextRequestCookie++; // No lock, only called on main thread. 380 { 381 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 382 RequestCancelers[StrID] = {std::move(Task.second), Cookie}; 383 } 384 // When the request ends, we can clean up the entry we just added. 385 // The cookie lets us check that it hasn't been overwritten due to ID 386 // reuse. 387 return Task.first.derive(llvm::make_scope_exit([this, StrID, Cookie] { 388 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 389 auto It = RequestCancelers.find(StrID); 390 if (It != RequestCancelers.end() && It->second.second == Cookie) 391 RequestCancelers.erase(It); 392 })); 393 } 394 395 // The maximum number of callbacks held in clangd. 396 // 397 // We bound the maximum size to the pending map to prevent memory leakage 398 // for cases where LSP clients don't reply for the request. 399 // This has to go after RequestCancellers and RequestCancellersMutex since it 400 // can contain a callback that has a cancelable context. 401 static constexpr int MaxReplayCallbacks = 100; 402 mutable std::mutex CallMutex; 403 int NextCallID = 0; /* GUARDED_BY(CallMutex) */ 404 std::deque<std::pair</*RequestID*/ int, 405 /*ReplyHandler*/ Callback<llvm::json::Value>>> 406 ReplyCallbacks; /* GUARDED_BY(CallMutex) */ 407 408 ClangdLSPServer &Server; 409 }; 410 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks; 411 412 // call(), notify(), and reply() wrap the Transport, adding logging and locking. 413 void ClangdLSPServer::callMethod(StringRef Method, llvm::json::Value Params, 414 Callback<llvm::json::Value> CB) { 415 auto ID = MsgHandler->bindReply(std::move(CB)); 416 log("--> {0}({1})", Method, ID); 417 std::lock_guard<std::mutex> Lock(TranspWriter); 418 Transp.call(Method, std::move(Params), ID); 419 } 420 421 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) { 422 log("--> {0}", Method); 423 maybeCleanupMemory(); 424 std::lock_guard<std::mutex> Lock(TranspWriter); 425 Transp.notify(Method, std::move(Params)); 426 } 427 428 static std::vector<llvm::StringRef> semanticTokenTypes() { 429 std::vector<llvm::StringRef> Types; 430 for (unsigned I = 0; I <= static_cast<unsigned>(HighlightingKind::LastKind); 431 ++I) 432 Types.push_back(toSemanticTokenType(static_cast<HighlightingKind>(I))); 433 return Types; 434 } 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 445 void ClangdLSPServer::onInitialize(const InitializeParams &Params, 446 Callback<llvm::json::Value> Reply) { 447 // Determine character encoding first as it affects constructed ClangdServer. 448 if (Params.capabilities.offsetEncoding && !Opts.Encoding) { 449 Opts.Encoding = OffsetEncoding::UTF16; // fallback 450 for (OffsetEncoding Supported : *Params.capabilities.offsetEncoding) 451 if (Supported != OffsetEncoding::UnsupportedEncoding) { 452 Opts.Encoding = Supported; 453 break; 454 } 455 } 456 457 if (Params.capabilities.TheiaSemanticHighlighting && 458 !Params.capabilities.SemanticTokens) { 459 elog("Client requested legacy semanticHighlights notification, which is " 460 "no longer supported. Migrate to standard semanticTokens request"); 461 } 462 463 if (Params.rootUri && *Params.rootUri) 464 Opts.WorkspaceRoot = std::string(Params.rootUri->file()); 465 else if (Params.rootPath && !Params.rootPath->empty()) 466 Opts.WorkspaceRoot = *Params.rootPath; 467 if (Server) 468 return Reply(llvm::make_error<LSPError>("server already initialized", 469 ErrorCode::InvalidRequest)); 470 if (Opts.UseDirBasedCDB) { 471 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); 472 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) 473 CDBOpts.CompileCommandsDir = Dir; 474 CDBOpts.ContextProvider = Opts.ContextProvider; 475 BaseCDB = 476 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts); 477 BaseCDB = getQueryDriverDatabase(llvm::makeArrayRef(Opts.QueryDriverGlobs), 478 std::move(BaseCDB)); 479 } 480 auto Mangler = CommandMangler::detect(); 481 if (Opts.ResourceDir) 482 Mangler.ResourceDir = *Opts.ResourceDir; 483 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, 484 tooling::ArgumentsAdjuster(std::move(Mangler))); 485 { 486 // Switch caller's context with LSPServer's background context. Since we 487 // rather want to propagate information from LSPServer's context into the 488 // Server, CDB, etc. 489 WithContext MainContext(BackgroundContext.clone()); 490 llvm::Optional<WithContextValue> WithOffsetEncoding; 491 if (Opts.Encoding) 492 WithOffsetEncoding.emplace(kCurrentOffsetEncoding, *Opts.Encoding); 493 Server.emplace(*CDB, TFS, Opts, 494 static_cast<ClangdServer::Callbacks *>(this)); 495 } 496 applyConfiguration(Params.initializationOptions.ConfigSettings); 497 498 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets; 499 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes; 500 if (!Opts.CodeComplete.BundleOverloads.hasValue()) 501 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp; 502 Opts.CodeComplete.DocumentationFormat = 503 Params.capabilities.CompletionDocumentationFormat; 504 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; 505 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; 506 DiagOpts.EmitRelatedLocations = 507 Params.capabilities.DiagnosticRelatedInformation; 508 if (Params.capabilities.WorkspaceSymbolKinds) 509 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds; 510 if (Params.capabilities.CompletionItemKinds) 511 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds; 512 SupportsCodeAction = Params.capabilities.CodeActionStructure; 513 SupportsHierarchicalDocumentSymbol = 514 Params.capabilities.HierarchicalDocumentSymbol; 515 SupportFileStatus = Params.initializationOptions.FileStatus; 516 HoverContentFormat = Params.capabilities.HoverContentFormat; 517 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; 518 if (Params.capabilities.WorkDoneProgress) 519 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; 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 } 587 588 // Per LSP, renameProvider can be either boolean or RenameOptions. 589 // RenameOptions will be specified if the client states it supports prepare. 590 ServerCaps["renameProvider"] = 591 Params.capabilities.RenamePrepareSupport 592 ? llvm::json::Object{{"prepareProvider", true}} 593 : llvm::json::Value(true); 594 595 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions. 596 // CodeActionOptions is only valid if the client supports action literal 597 // via textDocument.codeAction.codeActionLiteralSupport. 598 llvm::json::Value CodeActionProvider = true; 599 ServerCaps["codeActionProvider"] = 600 Params.capabilities.CodeActionStructure 601 ? llvm::json::Object{{"codeActionKinds", 602 {CodeAction::QUICKFIX_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 std::vector<llvm::StringRef> Commands; 611 for (llvm::StringRef Command : Handlers.CommandHandlers.keys()) 612 Commands.push_back(Command); 613 llvm::sort(Commands); 614 ServerCaps["executeCommandProvider"] = 615 llvm::json::Object{{"commands", Commands}}; 616 617 llvm::json::Object Result{ 618 {{"serverInfo", 619 llvm::json::Object{{"name", "clangd"}, 620 {"version", getClangToolFullVersion("clangd")}}}, 621 {"capabilities", std::move(ServerCaps)}}}; 622 if (Opts.Encoding) 623 Result["offsetEncoding"] = *Opts.Encoding; 624 Reply(std::move(Result)); 625 } 626 627 void ClangdLSPServer::onInitialized(const InitializedParams &Params) {} 628 629 void ClangdLSPServer::onShutdown(const NoParams &, 630 Callback<std::nullptr_t> Reply) { 631 // Do essentially nothing, just say we're ready to exit. 632 ShutdownRequestReceived = true; 633 Reply(nullptr); 634 } 635 636 // sync is a clangd extension: it blocks until all background work completes. 637 // It blocks the calling thread, so no messages are processed until it returns! 638 void ClangdLSPServer::onSync(const NoParams &, Callback<std::nullptr_t> Reply) { 639 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60)) 640 Reply(nullptr); 641 else 642 Reply(error("Not idle after a minute")); 643 } 644 645 void ClangdLSPServer::onDocumentDidOpen( 646 const DidOpenTextDocumentParams &Params) { 647 PathRef File = Params.textDocument.uri.file(); 648 649 const std::string &Contents = Params.textDocument.text; 650 651 Server->addDocument(File, Contents, 652 encodeVersion(Params.textDocument.version), 653 WantDiagnostics::Yes); 654 } 655 656 void ClangdLSPServer::onDocumentDidChange( 657 const DidChangeTextDocumentParams &Params) { 658 auto WantDiags = WantDiagnostics::Auto; 659 if (Params.wantDiagnostics.hasValue()) 660 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes 661 : WantDiagnostics::No; 662 663 PathRef File = Params.textDocument.uri.file(); 664 auto Code = Server->getDraft(File); 665 if (!Code) { 666 log("Trying to incrementally change non-added document: {0}", File); 667 return; 668 } 669 std::string NewCode(*Code); 670 for (const auto &Change : Params.contentChanges) { 671 if (auto Err = applyChange(NewCode, Change)) { 672 // If this fails, we are most likely going to be not in sync anymore with 673 // the client. It is better to remove the draft and let further 674 // operations fail rather than giving wrong results. 675 Server->removeDocument(File); 676 elog("Failed to update {0}: {1}", File, std::move(Err)); 677 return; 678 } 679 } 680 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version), 681 WantDiags, Params.forceRebuild); 682 } 683 684 void ClangdLSPServer::onDocumentDidSave( 685 const DidSaveTextDocumentParams &Params) { 686 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; }); 687 } 688 689 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { 690 // We could also reparse all open files here. However: 691 // - this could be frequent, and revalidating all the preambles isn't free 692 // - this is useful e.g. when switching git branches, but we're likely to see 693 // fresh headers but still have the old-branch main-file content 694 Server->onFileEvent(Params); 695 // FIXME: observe config files, immediately expire time-based caches, reparse: 696 // - compile_commands.json and compile_flags.txt 697 // - .clang_format and .clang-tidy 698 // - .clangd and clangd/config.yaml 699 } 700 701 void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, 702 Callback<llvm::json::Value> Reply) { 703 auto It = Handlers.CommandHandlers.find(Params.command); 704 if (It == Handlers.CommandHandlers.end()) { 705 return Reply(llvm::make_error<LSPError>( 706 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), 707 ErrorCode::InvalidParams)); 708 } 709 It->second(Params.argument, std::move(Reply)); 710 } 711 712 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit &WE, 713 Callback<llvm::json::Value> Reply) { 714 // The flow for "apply-fix" : 715 // 1. We publish a diagnostic, including fixits 716 // 2. The user clicks on the diagnostic, the editor asks us for code actions 717 // 3. We send code actions, with the fixit embedded as context 718 // 4. The user selects the fixit, the editor asks us to apply it 719 // 5. We unwrap the changes and send them back to the editor 720 // 6. The editor applies the changes (applyEdit), and sends us a reply 721 // 7. We unwrap the reply and send a reply to the editor. 722 applyEdit(WE, "Fix applied.", std::move(Reply)); 723 } 724 725 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, 726 Callback<llvm::json::Value> Reply) { 727 auto Action = [this, Reply = std::move(Reply), 728 File = Args.file](llvm::Expected<Tweak::Effect> R) mutable { 729 if (!R) 730 return Reply(R.takeError()); 731 732 assert(R->ShowMessage || (!R->ApplyEdits.empty() && "tweak has no effect")); 733 734 if (R->ShowMessage) { 735 ShowMessageParams Msg; 736 Msg.message = *R->ShowMessage; 737 Msg.type = MessageType::Info; 738 ShowMessage(Msg); 739 } 740 // When no edit is specified, make sure we Reply(). 741 if (R->ApplyEdits.empty()) 742 return Reply("Tweak applied."); 743 744 if (auto Err = validateEdits(*Server, R->ApplyEdits)) 745 return Reply(std::move(Err)); 746 747 WorkspaceEdit WE; 748 WE.changes.emplace(); 749 for (const auto &It : R->ApplyEdits) { 750 (*WE.changes)[URI::createFile(It.first()).toString()] = 751 It.second.asTextEdits(); 752 } 753 // ApplyEdit will take care of calling Reply(). 754 return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply)); 755 }; 756 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID, 757 std::move(Action)); 758 } 759 760 void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success, 761 Callback<llvm::json::Value> Reply) { 762 ApplyWorkspaceEditParams Edit; 763 Edit.edit = std::move(WE); 764 ApplyWorkspaceEdit( 765 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)]( 766 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable { 767 if (!Response) 768 return Reply(Response.takeError()); 769 if (!Response->applied) { 770 std::string Reason = Response->failureReason 771 ? *Response->failureReason 772 : "unknown reason"; 773 return Reply(error("edits were not applied: {0}", Reason)); 774 } 775 return Reply(SuccessMessage); 776 }); 777 } 778 779 void ClangdLSPServer::onWorkspaceSymbol( 780 const WorkspaceSymbolParams &Params, 781 Callback<std::vector<SymbolInformation>> Reply) { 782 Server->workspaceSymbols( 783 Params.query, Params.limit.getValueOr(Opts.CodeComplete.Limit), 784 [Reply = std::move(Reply), 785 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable { 786 if (!Items) 787 return Reply(Items.takeError()); 788 for (auto &Sym : *Items) 789 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); 790 791 Reply(std::move(*Items)); 792 }); 793 } 794 795 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, 796 Callback<llvm::Optional<Range>> Reply) { 797 Server->prepareRename( 798 Params.textDocument.uri.file(), Params.position, /*NewName*/ llvm::None, 799 Opts.Rename, 800 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable { 801 if (!Result) 802 return Reply(Result.takeError()); 803 return Reply(std::move(Result->Target)); 804 }); 805 } 806 807 void ClangdLSPServer::onRename(const RenameParams &Params, 808 Callback<WorkspaceEdit> Reply) { 809 Path File = std::string(Params.textDocument.uri.file()); 810 if (!Server->getDraft(File)) 811 return Reply(llvm::make_error<LSPError>( 812 "onRename called for non-added file", ErrorCode::InvalidParams)); 813 Server->rename( 814 File, Params.position, Params.newName, Opts.Rename, 815 [File, Params, Reply = std::move(Reply), 816 this](llvm::Expected<RenameResult> R) mutable { 817 if (!R) 818 return Reply(R.takeError()); 819 if (auto Err = validateEdits(*Server, R->GlobalChanges)) 820 return Reply(std::move(Err)); 821 WorkspaceEdit Result; 822 Result.changes.emplace(); 823 for (const auto &Rep : R->GlobalChanges) { 824 (*Result.changes)[URI::createFile(Rep.first()).toString()] = 825 Rep.second.asTextEdits(); 826 } 827 Reply(Result); 828 }); 829 } 830 831 void ClangdLSPServer::onDocumentDidClose( 832 const DidCloseTextDocumentParams &Params) { 833 PathRef File = Params.textDocument.uri.file(); 834 Server->removeDocument(File); 835 836 { 837 std::lock_guard<std::mutex> Lock(FixItsMutex); 838 FixItsMap.erase(File); 839 } 840 { 841 std::lock_guard<std::mutex> HLock(SemanticTokensMutex); 842 LastSemanticTokens.erase(File); 843 } 844 // clangd will not send updates for this file anymore, so we empty out the 845 // list of diagnostics shown on the client (e.g. in the "Problems" pane of 846 // VSCode). Note that this cannot race with actual diagnostics responses 847 // because removeDocument() guarantees no diagnostic callbacks will be 848 // executed after it returns. 849 PublishDiagnosticsParams Notification; 850 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 851 PublishDiagnostics(Notification); 852 } 853 854 void ClangdLSPServer::onDocumentOnTypeFormatting( 855 const DocumentOnTypeFormattingParams &Params, 856 Callback<std::vector<TextEdit>> Reply) { 857 auto File = Params.textDocument.uri.file(); 858 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply)); 859 } 860 861 void ClangdLSPServer::onDocumentRangeFormatting( 862 const DocumentRangeFormattingParams &Params, 863 Callback<std::vector<TextEdit>> Reply) { 864 auto File = Params.textDocument.uri.file(); 865 auto Code = Server->getDraft(File); 866 Server->formatFile(File, Params.range, 867 [Code = std::move(Code), Reply = std::move(Reply)]( 868 llvm::Expected<tooling::Replacements> Result) mutable { 869 if (Result) 870 Reply(replacementsToEdits(*Code, Result.get())); 871 else 872 Reply(Result.takeError()); 873 }); 874 } 875 876 void ClangdLSPServer::onDocumentFormatting( 877 const DocumentFormattingParams &Params, 878 Callback<std::vector<TextEdit>> Reply) { 879 auto File = Params.textDocument.uri.file(); 880 auto Code = Server->getDraft(File); 881 Server->formatFile(File, 882 /*Rng=*/llvm::None, 883 [Code = std::move(Code), Reply = std::move(Reply)]( 884 llvm::Expected<tooling::Replacements> Result) mutable { 885 if (Result) 886 Reply(replacementsToEdits(*Code, Result.get())); 887 else 888 Reply(Result.takeError()); 889 }); 890 } 891 892 /// The functions constructs a flattened view of the DocumentSymbol hierarchy. 893 /// Used by the clients that do not support the hierarchical view. 894 static std::vector<SymbolInformation> 895 flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols, 896 const URIForFile &FileURI) { 897 std::vector<SymbolInformation> Results; 898 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process = 899 [&](const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) { 900 SymbolInformation SI; 901 SI.containerName = std::string(ParentName ? "" : *ParentName); 902 SI.name = S.name; 903 SI.kind = S.kind; 904 SI.location.range = S.range; 905 SI.location.uri = FileURI; 906 907 Results.push_back(std::move(SI)); 908 std::string FullName = 909 !ParentName ? S.name : (ParentName->str() + "::" + S.name); 910 for (auto &C : S.children) 911 Process(C, /*ParentName=*/FullName); 912 }; 913 for (auto &S : Symbols) 914 Process(S, /*ParentName=*/""); 915 return Results; 916 } 917 918 void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params, 919 Callback<llvm::json::Value> Reply) { 920 URIForFile FileURI = Params.textDocument.uri; 921 Server->documentSymbols( 922 Params.textDocument.uri.file(), 923 [this, FileURI, Reply = std::move(Reply)]( 924 llvm::Expected<std::vector<DocumentSymbol>> Items) mutable { 925 if (!Items) 926 return Reply(Items.takeError()); 927 adjustSymbolKinds(*Items, SupportedSymbolKinds); 928 if (SupportsHierarchicalDocumentSymbol) 929 return Reply(std::move(*Items)); 930 else 931 return Reply(flattenSymbolHierarchy(*Items, FileURI)); 932 }); 933 } 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 941 static llvm::Optional<Command> asCommand(const CodeAction &Action) { 942 Command Cmd; 943 if (Action.command && Action.edit) 944 return None; // Not representable. (We never emit these anyway). 945 if (Action.command) { 946 Cmd = *Action.command; 947 } else if (Action.edit) { 948 Cmd.command = std::string(APPLY_FIX_COMMAND); 949 Cmd.argument = *Action.edit; 950 } else { 951 return None; 952 } 953 Cmd.title = Action.title; 954 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) 955 Cmd.title = "Apply fix: " + Cmd.title; 956 return Cmd; 957 } 958 959 void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, 960 Callback<llvm::json::Value> Reply) { 961 URIForFile File = Params.textDocument.uri; 962 // Checks whether a particular CodeActionKind is included in the response. 963 auto KindAllowed = [Only(Params.context.only)](llvm::StringRef Kind) { 964 if (Only.empty()) 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 971 // We provide a code action for Fixes on the specified diagnostics. 972 std::vector<CodeAction> FixIts; 973 if (KindAllowed(CodeAction::QUICKFIX_KIND)) { 974 for (const Diagnostic &D : Params.context.diagnostics) { 975 for (auto &F : getFixes(File.file(), D)) { 976 FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); 977 FixIts.back().diagnostics = {D}; 978 } 979 } 980 } 981 982 // Now enumerate the semantic code actions. 983 auto ConsumeActions = 984 [Reply = std::move(Reply), File, Selection = Params.range, 985 FixIts = std::move(FixIts), this]( 986 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks) mutable { 987 if (!Tweaks) 988 return Reply(Tweaks.takeError()); 989 990 std::vector<CodeAction> Actions = std::move(FixIts); 991 Actions.reserve(Actions.size() + Tweaks->size()); 992 for (const auto &T : *Tweaks) 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 } 1008 1009 if (SupportsCodeAction) 1010 return Reply(llvm::json::Array(Actions)); 1011 std::vector<Command> Commands; 1012 for (const auto &Action : Actions) { 1013 if (auto Command = asCommand(Action)) 1014 Commands.push_back(std::move(*Command)); 1015 } 1016 return Reply(llvm::json::Array(Commands)); 1017 }; 1018 Server->enumerateTweaks( 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)); 1024 } 1025 1026 void ClangdLSPServer::onCompletion(const CompletionParams &Params, 1027 Callback<CompletionList> Reply) { 1028 if (!shouldRunCompletion(Params)) { 1029 // Clients sometimes auto-trigger completions in undesired places (e.g. 1030 // 'a >^ '), we return empty results in those cases. 1031 vlog("ignored auto-triggered completion, preceding char did not match"); 1032 return Reply(CompletionList()); 1033 } 1034 auto Opts = this->Opts.CodeComplete; 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, 1039 this](llvm::Expected<CodeCompleteResult> List) mutable { 1040 if (!List) 1041 return Reply(List.takeError()); 1042 CompletionList LSPList; 1043 LSPList.isIncomplete = List->HasMore; 1044 for (const auto &R : List->Completions) { 1045 CompletionItem C = R.render(Opts); 1046 C.kind = adjustKindToCapability( 1047 C.kind, SupportedCompletionItemKinds); 1048 LSPList.items.push_back(std::move(C)); 1049 } 1050 return Reply(std::move(LSPList)); 1051 }); 1052 } 1053 1054 void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params, 1055 Callback<SignatureHelp> Reply) { 1056 Server->signatureHelp(Params.textDocument.uri.file(), Params.position, 1057 [Reply = std::move(Reply), this]( 1058 llvm::Expected<SignatureHelp> Signature) mutable { 1059 if (!Signature) 1060 return Reply(Signature.takeError()); 1061 if (SupportsOffsetsInSignatureHelp) 1062 return Reply(std::move(*Signature)); 1063 // Strip out the offsets from signature help for 1064 // clients that only support string labels. 1065 for (auto &SigInfo : Signature->signatures) { 1066 for (auto &Param : SigInfo.parameters) 1067 Param.labelOffsets.reset(); 1068 } 1069 return Reply(std::move(*Signature)); 1070 }); 1071 } 1072 1073 // Go to definition has a toggle function: if def and decl are distinct, then 1074 // the first press gives you the def, the second gives you the matching def. 1075 // getToggle() returns the counterpart location that under the cursor. 1076 // 1077 // We return the toggled location alone (ignoring other symbols) to encourage 1078 // editors to "bounce" quickly between locations, without showing a menu. 1079 static Location *getToggle(const TextDocumentPositionParams &Point, 1080 LocatedSymbol &Sym) { 1081 // Toggle only makes sense with two distinct locations. 1082 if (!Sym.Definition || *Sym.Definition == Sym.PreferredDeclaration) 1083 return nullptr; 1084 if (Sym.Definition->uri.file() == Point.textDocument.uri.file() && 1085 Sym.Definition->range.contains(Point.position)) 1086 return &Sym.PreferredDeclaration; 1087 if (Sym.PreferredDeclaration.uri.file() == Point.textDocument.uri.file() && 1088 Sym.PreferredDeclaration.range.contains(Point.position)) 1089 return &*Sym.Definition; 1090 return nullptr; 1091 } 1092 1093 void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params, 1094 Callback<std::vector<Location>> Reply) { 1095 Server->locateSymbolAt( 1096 Params.textDocument.uri.file(), Params.position, 1097 [Params, Reply = std::move(Reply)]( 1098 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1099 if (!Symbols) 1100 return Reply(Symbols.takeError()); 1101 std::vector<Location> Defs; 1102 for (auto &S : *Symbols) { 1103 if (Location *Toggle = getToggle(Params, S)) 1104 return Reply(std::vector<Location>{std::move(*Toggle)}); 1105 Defs.push_back(S.Definition.getValueOr(S.PreferredDeclaration)); 1106 } 1107 Reply(std::move(Defs)); 1108 }); 1109 } 1110 1111 void ClangdLSPServer::onGoToDeclaration( 1112 const TextDocumentPositionParams &Params, 1113 Callback<std::vector<Location>> Reply) { 1114 Server->locateSymbolAt( 1115 Params.textDocument.uri.file(), Params.position, 1116 [Params, Reply = std::move(Reply)]( 1117 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1118 if (!Symbols) 1119 return Reply(Symbols.takeError()); 1120 std::vector<Location> Decls; 1121 for (auto &S : *Symbols) { 1122 if (Location *Toggle = getToggle(Params, S)) 1123 return Reply(std::vector<Location>{std::move(*Toggle)}); 1124 Decls.push_back(std::move(S.PreferredDeclaration)); 1125 } 1126 Reply(std::move(Decls)); 1127 }); 1128 } 1129 1130 void ClangdLSPServer::onSwitchSourceHeader( 1131 const TextDocumentIdentifier &Params, 1132 Callback<llvm::Optional<URIForFile>> Reply) { 1133 Server->switchSourceHeader( 1134 Params.uri.file(), 1135 [Reply = std::move(Reply), 1136 Params](llvm::Expected<llvm::Optional<clangd::Path>> Path) mutable { 1137 if (!Path) 1138 return Reply(Path.takeError()); 1139 if (*Path) 1140 return Reply(URIForFile::canonicalize(**Path, Params.uri.file())); 1141 return Reply(llvm::None); 1142 }); 1143 } 1144 1145 void ClangdLSPServer::onDocumentHighlight( 1146 const TextDocumentPositionParams &Params, 1147 Callback<std::vector<DocumentHighlight>> Reply) { 1148 Server->findDocumentHighlights(Params.textDocument.uri.file(), 1149 Params.position, std::move(Reply)); 1150 } 1151 1152 void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, 1153 Callback<llvm::Optional<Hover>> Reply) { 1154 Server->findHover(Params.textDocument.uri.file(), Params.position, 1155 [Reply = std::move(Reply), this]( 1156 llvm::Expected<llvm::Optional<HoverInfo>> H) mutable { 1157 if (!H) 1158 return Reply(H.takeError()); 1159 if (!*H) 1160 return Reply(llvm::None); 1161 1162 Hover R; 1163 R.contents.kind = HoverContentFormat; 1164 R.range = (*H)->SymRange; 1165 switch (HoverContentFormat) { 1166 case MarkupKind::PlainText: 1167 R.contents.value = (*H)->present().asPlainText(); 1168 return Reply(std::move(R)); 1169 case MarkupKind::Markdown: 1170 R.contents.value = (*H)->present().asMarkdown(); 1171 return Reply(std::move(R)); 1172 }; 1173 llvm_unreachable("unhandled MarkupKind"); 1174 }); 1175 } 1176 1177 void ClangdLSPServer::onTypeHierarchy( 1178 const TypeHierarchyParams &Params, 1179 Callback<Optional<TypeHierarchyItem>> Reply) { 1180 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position, 1181 Params.resolve, Params.direction, std::move(Reply)); 1182 } 1183 1184 void ClangdLSPServer::onResolveTypeHierarchy( 1185 const ResolveTypeHierarchyItemParams &Params, 1186 Callback<Optional<TypeHierarchyItem>> Reply) { 1187 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, 1188 std::move(Reply)); 1189 } 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::applyConfiguration( 1212 const ConfigurationSettings &Settings) { 1213 // Per-file update to the compilation database. 1214 llvm::StringSet<> ModifiedFiles; 1215 for (auto &Entry : Settings.compilationDatabaseChanges) { 1216 PathRef File = Entry.first; 1217 auto Old = CDB->getCompileCommand(File); 1218 auto New = 1219 tooling::CompileCommand(std::move(Entry.second.workingDirectory), File, 1220 std::move(Entry.second.compilationCommand), 1221 /*Output=*/""); 1222 if (Old != New) { 1223 CDB->setCompileCommand(File, std::move(New)); 1224 ModifiedFiles.insert(File); 1225 } 1226 } 1227 1228 Server->reparseOpenFilesIfNeeded( 1229 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); 1230 } 1231 1232 void ClangdLSPServer::maybeExportMemoryProfile() { 1233 if (!trace::enabled() || !ShouldProfile()) 1234 return; 1235 1236 static constexpr trace::Metric MemoryUsage( 1237 "memory_usage", trace::Metric::Value, "component_name"); 1238 trace::Span Tracer("ProfileBrief"); 1239 MemoryTree MT; 1240 profile(MT); 1241 record(MT, "clangd_lsp_server", MemoryUsage); 1242 } 1243 1244 void ClangdLSPServer::maybeCleanupMemory() { 1245 if (!Opts.MemoryCleanup || !ShouldCleanupMemory()) 1246 return; 1247 Opts.MemoryCleanup(); 1248 } 1249 1250 // FIXME: This function needs to be properly tested. 1251 void ClangdLSPServer::onChangeConfiguration( 1252 const DidChangeConfigurationParams &Params) { 1253 applyConfiguration(Params.settings); 1254 } 1255 1256 void ClangdLSPServer::onReference(const ReferenceParams &Params, 1257 Callback<std::vector<Location>> Reply) { 1258 Server->findReferences( 1259 Params.textDocument.uri.file(), Params.position, Opts.CodeComplete.Limit, 1260 [Reply = std::move(Reply), 1261 IncludeDecl(Params.context.includeDeclaration)]( 1262 llvm::Expected<ReferencesResult> Refs) mutable { 1263 if (!Refs) 1264 return Reply(Refs.takeError()); 1265 // Filter out declarations if the client asked. 1266 std::vector<Location> Result; 1267 Result.reserve(Refs->References.size()); 1268 for (auto &Ref : Refs->References) { 1269 bool IsDecl = Ref.Attributes & ReferencesResult::Declaration; 1270 if (IncludeDecl || !IsDecl) 1271 Result.push_back(std::move(Ref.Loc)); 1272 } 1273 return Reply(std::move(Result)); 1274 }); 1275 } 1276 1277 void ClangdLSPServer::onGoToImplementation( 1278 const TextDocumentPositionParams &Params, 1279 Callback<std::vector<Location>> Reply) { 1280 Server->findImplementations( 1281 Params.textDocument.uri.file(), Params.position, 1282 [Reply = std::move(Reply)]( 1283 llvm::Expected<std::vector<LocatedSymbol>> Overrides) mutable { 1284 if (!Overrides) 1285 return Reply(Overrides.takeError()); 1286 std::vector<Location> Impls; 1287 for (const LocatedSymbol &Sym : *Overrides) 1288 Impls.push_back(Sym.PreferredDeclaration); 1289 return Reply(std::move(Impls)); 1290 }); 1291 } 1292 1293 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, 1294 Callback<std::vector<SymbolDetails>> Reply) { 1295 Server->symbolInfo(Params.textDocument.uri.file(), Params.position, 1296 std::move(Reply)); 1297 } 1298 1299 void ClangdLSPServer::onSelectionRange( 1300 const SelectionRangeParams &Params, 1301 Callback<std::vector<SelectionRange>> Reply) { 1302 Server->semanticRanges( 1303 Params.textDocument.uri.file(), Params.positions, 1304 [Reply = std::move(Reply)]( 1305 llvm::Expected<std::vector<SelectionRange>> Ranges) mutable { 1306 if (!Ranges) 1307 return Reply(Ranges.takeError()); 1308 return Reply(std::move(*Ranges)); 1309 }); 1310 } 1311 1312 void ClangdLSPServer::onDocumentLink( 1313 const DocumentLinkParams &Params, 1314 Callback<std::vector<DocumentLink>> Reply) { 1315 1316 // TODO(forster): This currently resolves all targets eagerly. This is slow, 1317 // because it blocks on the preamble/AST being built. We could respond to the 1318 // request faster by using string matching or the lexer to find the includes 1319 // and resolving the targets lazily. 1320 Server->documentLinks( 1321 Params.textDocument.uri.file(), 1322 [Reply = std::move(Reply)]( 1323 llvm::Expected<std::vector<DocumentLink>> Links) mutable { 1324 if (!Links) { 1325 return Reply(Links.takeError()); 1326 } 1327 return Reply(std::move(Links)); 1328 }); 1329 } 1330 1331 // Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ... 1332 static void increment(std::string &S) { 1333 for (char &C : llvm::reverse(S)) { 1334 if (C != '9') { 1335 ++C; 1336 return; 1337 } 1338 C = '0'; 1339 } 1340 S.insert(S.begin(), '1'); 1341 } 1342 1343 void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams &Params, 1344 Callback<SemanticTokens> CB) { 1345 Server->semanticHighlights( 1346 Params.textDocument.uri.file(), 1347 [this, File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( 1348 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1349 if (!HT) 1350 return CB(HT.takeError()); 1351 SemanticTokens Result; 1352 Result.tokens = toSemanticTokens(*HT); 1353 { 1354 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1355 auto &Last = LastSemanticTokens[File]; 1356 1357 Last.tokens = Result.tokens; 1358 increment(Last.resultId); 1359 Result.resultId = Last.resultId; 1360 } 1361 CB(std::move(Result)); 1362 }); 1363 } 1364 1365 void ClangdLSPServer::onSemanticTokensDelta( 1366 const SemanticTokensDeltaParams &Params, 1367 Callback<SemanticTokensOrDelta> CB) { 1368 Server->semanticHighlights( 1369 Params.textDocument.uri.file(), 1370 [this, PrevResultID(Params.previousResultId), 1371 File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( 1372 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1373 if (!HT) 1374 return CB(HT.takeError()); 1375 std::vector<SemanticToken> Toks = toSemanticTokens(*HT); 1376 1377 SemanticTokensOrDelta Result; 1378 { 1379 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1380 auto &Last = LastSemanticTokens[File]; 1381 1382 if (PrevResultID == Last.resultId) { 1383 Result.edits = diffTokens(Last.tokens, Toks); 1384 } else { 1385 vlog("semanticTokens/full/delta: wanted edits vs {0} but last " 1386 "result had ID {1}. Returning full token list.", 1387 PrevResultID, Last.resultId); 1388 Result.tokens = Toks; 1389 } 1390 1391 Last.tokens = std::move(Toks); 1392 increment(Last.resultId); 1393 Result.resultId = Last.resultId; 1394 } 1395 1396 CB(std::move(Result)); 1397 }); 1398 } 1399 1400 void ClangdLSPServer::onMemoryUsage(const NoParams &, 1401 Callback<MemoryTree> Reply) { 1402 llvm::BumpPtrAllocator DetailAlloc; 1403 MemoryTree MT(&DetailAlloc); 1404 profile(MT); 1405 Reply(std::move(MT)); 1406 } 1407 1408 void ClangdLSPServer::onAST(const ASTParams &Params, 1409 Callback<llvm::Optional<ASTNode>> CB) { 1410 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB)); 1411 } 1412 1413 ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, 1414 const ClangdLSPServer::Options &Opts) 1415 : ShouldProfile(/*Period=*/std::chrono::minutes(5), 1416 /*Delay=*/std::chrono::minutes(1)), 1417 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), 1418 /*Delay=*/std::chrono::minutes(1)), 1419 BackgroundContext(Context::current().clone()), Transp(Transp), 1420 MsgHandler(new MessageHandler(*this)), TFS(TFS), 1421 SupportedSymbolKinds(defaultSymbolKinds()), 1422 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { 1423 if (Opts.ConfigProvider) { 1424 assert(!Opts.ContextProvider && 1425 "Only one of ConfigProvider and ContextProvider allowed!"); 1426 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( 1427 Opts.ConfigProvider, this); 1428 } 1429 LSPBinder Bind(this->Handlers, *this); 1430 Bind.method("initialize", this, &ClangdLSPServer::onInitialize); 1431 } 1432 1433 void ClangdLSPServer::bindMethods(LSPBinder &Bind, 1434 const ClientCapabilities &Caps) { 1435 // clang-format off 1436 Bind.notification("initialized", this, &ClangdLSPServer::onInitialized); 1437 Bind.method("shutdown", this, &ClangdLSPServer::onShutdown); 1438 Bind.method("sync", this, &ClangdLSPServer::onSync); 1439 Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting); 1440 Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting); 1441 Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting); 1442 Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction); 1443 Bind.method("textDocument/completion", this, &ClangdLSPServer::onCompletion); 1444 Bind.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp); 1445 Bind.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition); 1446 Bind.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration); 1447 Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation); 1448 Bind.method("textDocument/references", this, &ClangdLSPServer::onReference); 1449 Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); 1450 Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); 1451 Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); 1452 Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); 1453 Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); 1454 Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); 1455 Bind.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight); 1456 Bind.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol); 1457 Bind.method("textDocument/ast", this, &ClangdLSPServer::onAST); 1458 Bind.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen); 1459 Bind.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose); 1460 Bind.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange); 1461 Bind.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave); 1462 Bind.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent); 1463 Bind.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration); 1464 Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo); 1465 Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy); 1466 Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy); 1467 Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy); 1468 Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls); 1469 Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls); 1470 Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange); 1471 Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink); 1472 Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens); 1473 Bind.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta); 1474 Bind.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage); 1475 if (Opts.FoldingRanges) 1476 Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange); 1477 Bind.command(APPLY_FIX_COMMAND, this, &ClangdLSPServer::onCommandApplyEdit); 1478 Bind.command(APPLY_TWEAK_COMMAND, this, &ClangdLSPServer::onCommandApplyTweak); 1479 1480 ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit"); 1481 PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics"); 1482 ShowMessage = Bind.outgoingNotification("window/showMessage"); 1483 NotifyFileStatus = Bind.outgoingNotification("textDocument/clangd.fileStatus"); 1484 CreateWorkDoneProgress = Bind.outgoingMethod("window/workDoneProgress/create"); 1485 BeginWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1486 ReportWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1487 EndWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1488 if(Caps.SemanticTokenRefreshSupport) 1489 SemanticTokensRefresh = Bind.outgoingMethod("workspace/semanticTokens/refresh"); 1490 // clang-format on 1491 } 1492 1493 ClangdLSPServer::~ClangdLSPServer() { 1494 IsBeingDestroyed = true; 1495 // Explicitly destroy ClangdServer first, blocking on threads it owns. 1496 // This ensures they don't access any other members. 1497 Server.reset(); 1498 } 1499 1500 bool ClangdLSPServer::run() { 1501 // Run the Language Server loop. 1502 bool CleanExit = true; 1503 if (auto Err = Transp.loop(*MsgHandler)) { 1504 elog("Transport error: {0}", std::move(Err)); 1505 CleanExit = false; 1506 } 1507 1508 return CleanExit && ShutdownRequestReceived; 1509 } 1510 1511 void ClangdLSPServer::profile(MemoryTree &MT) const { 1512 if (Server) 1513 Server->profile(MT.child("clangd_server")); 1514 } 1515 1516 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File, 1517 const clangd::Diagnostic &D) { 1518 std::lock_guard<std::mutex> Lock(FixItsMutex); 1519 auto DiagToFixItsIter = FixItsMap.find(File); 1520 if (DiagToFixItsIter == FixItsMap.end()) 1521 return {}; 1522 1523 const auto &DiagToFixItsMap = DiagToFixItsIter->second; 1524 auto FixItsIter = DiagToFixItsMap.find(D); 1525 if (FixItsIter == DiagToFixItsMap.end()) 1526 return {}; 1527 1528 return FixItsIter->second; 1529 } 1530 1531 // A completion request is sent when the user types '>' or ':', but we only 1532 // want to trigger on '->' and '::'. We check the preceeding text to make 1533 // sure it matches what we expected. 1534 // Running the lexer here would be more robust (e.g. we can detect comments 1535 // and avoid triggering completion there), but we choose to err on the side 1536 // of simplicity here. 1537 bool ClangdLSPServer::shouldRunCompletion( 1538 const CompletionParams &Params) const { 1539 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter) 1540 return true; 1541 auto Code = Server->getDraft(Params.textDocument.uri.file()); 1542 if (!Code) 1543 return true; // completion code will log the error for untracked doc. 1544 auto Offset = positionToOffset(*Code, Params.position, 1545 /*AllowColumnsBeyondLineLength=*/false); 1546 if (!Offset) { 1547 vlog("could not convert position '{0}' to offset for file '{1}'", 1548 Params.position, Params.textDocument.uri.file()); 1549 return true; 1550 } 1551 return allowImplicitCompletion(*Code, *Offset); 1552 } 1553 1554 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, 1555 std::vector<Diag> Diagnostics) { 1556 PublishDiagnosticsParams Notification; 1557 Notification.version = decodeVersion(Version); 1558 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 1559 DiagnosticToReplacementMap LocalFixIts; // Temporary storage 1560 for (auto &Diag : Diagnostics) { 1561 toLSPDiags(Diag, Notification.uri, DiagOpts, 1562 [&](clangd::Diagnostic Diag, llvm::ArrayRef<Fix> Fixes) { 1563 auto &FixItsForDiagnostic = LocalFixIts[Diag]; 1564 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic)); 1565 Notification.diagnostics.push_back(std::move(Diag)); 1566 }); 1567 } 1568 1569 // Cache FixIts 1570 { 1571 std::lock_guard<std::mutex> Lock(FixItsMutex); 1572 FixItsMap[File] = LocalFixIts; 1573 } 1574 1575 // Send a notification to the LSP client. 1576 PublishDiagnostics(Notification); 1577 } 1578 1579 void ClangdLSPServer::onBackgroundIndexProgress( 1580 const BackgroundQueue::Stats &Stats) { 1581 static const char ProgressToken[] = "backgroundIndexProgress"; 1582 1583 // The background index did some work, maybe we need to cleanup 1584 maybeCleanupMemory(); 1585 1586 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1587 1588 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) { 1589 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) { 1590 WorkDoneProgressBegin Begin; 1591 Begin.percentage = true; 1592 Begin.title = "indexing"; 1593 BeginWorkDoneProgress({ProgressToken, std::move(Begin)}); 1594 BackgroundIndexProgressState = BackgroundIndexProgress::Live; 1595 } 1596 1597 if (Stats.Completed < Stats.Enqueued) { 1598 assert(Stats.Enqueued > Stats.LastIdle); 1599 WorkDoneProgressReport Report; 1600 Report.percentage = 100.0 * (Stats.Completed - Stats.LastIdle) / 1601 (Stats.Enqueued - Stats.LastIdle); 1602 Report.message = 1603 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle, 1604 Stats.Enqueued - Stats.LastIdle); 1605 ReportWorkDoneProgress({ProgressToken, std::move(Report)}); 1606 } else { 1607 assert(Stats.Completed == Stats.Enqueued); 1608 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()}); 1609 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; 1610 } 1611 }; 1612 1613 switch (BackgroundIndexProgressState) { 1614 case BackgroundIndexProgress::Unsupported: 1615 return; 1616 case BackgroundIndexProgress::Creating: 1617 // Cache this update for when the progress bar is available. 1618 PendingBackgroundIndexProgress = Stats; 1619 return; 1620 case BackgroundIndexProgress::Empty: { 1621 if (BackgroundIndexSkipCreate) { 1622 NotifyProgress(Stats); 1623 break; 1624 } 1625 // Cache this update for when the progress bar is available. 1626 PendingBackgroundIndexProgress = Stats; 1627 BackgroundIndexProgressState = BackgroundIndexProgress::Creating; 1628 WorkDoneProgressCreateParams CreateRequest; 1629 CreateRequest.token = ProgressToken; 1630 CreateWorkDoneProgress( 1631 CreateRequest, 1632 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) { 1633 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1634 if (E) { 1635 NotifyProgress(this->PendingBackgroundIndexProgress); 1636 } else { 1637 elog("Failed to create background index progress bar: {0}", 1638 E.takeError()); 1639 // give up forever rather than thrashing about 1640 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; 1641 } 1642 }); 1643 break; 1644 } 1645 case BackgroundIndexProgress::Live: 1646 NotifyProgress(Stats); 1647 break; 1648 } 1649 } 1650 1651 void ClangdLSPServer::onFileUpdated(PathRef File, const TUStatus &Status) { 1652 if (!SupportFileStatus) 1653 return; 1654 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these 1655 // two statuses are running faster in practice, which leads the UI constantly 1656 // changing, and doesn't provide much value. We may want to emit status at a 1657 // reasonable time interval (e.g. 0.5s). 1658 if (Status.PreambleActivity == PreambleAction::Idle && 1659 (Status.ASTActivity.K == ASTAction::Building || 1660 Status.ASTActivity.K == ASTAction::RunningAction)) 1661 return; 1662 NotifyFileStatus(Status.render(File)); 1663 } 1664 1665 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File) { 1666 if (SemanticTokensRefresh) { 1667 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) { 1668 if (E) 1669 return; 1670 elog("Failed to refresh semantic tokens: {0}", E.takeError()); 1671 }); 1672 } 1673 } 1674 } // namespace clangd 1675 } // namespace clang 1676