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 {"clangdInlayHintsProvider", true}, 575 {"memoryUsageProvider", true}, // clangd extension 576 {"compilationDatabase", // clangd extension 577 llvm::json::Object{{"automaticReload", true}}}, 578 {"callHierarchyProvider", true}, 579 }; 580 581 { 582 LSPBinder Binder(Handlers, *this); 583 bindMethods(Binder, Params.capabilities); 584 if (Opts.FeatureModules) 585 for (auto &Mod : *Opts.FeatureModules) 586 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps); 587 } 588 589 // Per LSP, renameProvider can be either boolean or RenameOptions. 590 // RenameOptions will be specified if the client states it supports prepare. 591 ServerCaps["renameProvider"] = 592 Params.capabilities.RenamePrepareSupport 593 ? llvm::json::Object{{"prepareProvider", true}} 594 : llvm::json::Value(true); 595 596 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions. 597 // CodeActionOptions is only valid if the client supports action literal 598 // via textDocument.codeAction.codeActionLiteralSupport. 599 llvm::json::Value CodeActionProvider = true; 600 ServerCaps["codeActionProvider"] = 601 Params.capabilities.CodeActionStructure 602 ? llvm::json::Object{{"codeActionKinds", 603 {CodeAction::QUICKFIX_KIND, 604 CodeAction::REFACTOR_KIND, 605 CodeAction::INFO_KIND}}} 606 : llvm::json::Value(true); 607 608 if (Opts.FoldingRanges) 609 ServerCaps["foldingRangeProvider"] = true; 610 611 std::vector<llvm::StringRef> Commands; 612 for (llvm::StringRef Command : Handlers.CommandHandlers.keys()) 613 Commands.push_back(Command); 614 llvm::sort(Commands); 615 ServerCaps["executeCommandProvider"] = 616 llvm::json::Object{{"commands", Commands}}; 617 618 llvm::json::Object Result{ 619 {{"serverInfo", 620 llvm::json::Object{{"name", "clangd"}, 621 {"version", getClangToolFullVersion("clangd")}}}, 622 {"capabilities", std::move(ServerCaps)}}}; 623 if (Opts.Encoding) 624 Result["offsetEncoding"] = *Opts.Encoding; 625 Reply(std::move(Result)); 626 } 627 628 void ClangdLSPServer::onInitialized(const InitializedParams &Params) {} 629 630 void ClangdLSPServer::onShutdown(const NoParams &, 631 Callback<std::nullptr_t> Reply) { 632 // Do essentially nothing, just say we're ready to exit. 633 ShutdownRequestReceived = true; 634 Reply(nullptr); 635 } 636 637 // sync is a clangd extension: it blocks until all background work completes. 638 // It blocks the calling thread, so no messages are processed until it returns! 639 void ClangdLSPServer::onSync(const NoParams &, Callback<std::nullptr_t> Reply) { 640 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60)) 641 Reply(nullptr); 642 else 643 Reply(error("Not idle after a minute")); 644 } 645 646 void ClangdLSPServer::onDocumentDidOpen( 647 const DidOpenTextDocumentParams &Params) { 648 PathRef File = Params.textDocument.uri.file(); 649 650 const std::string &Contents = Params.textDocument.text; 651 652 Server->addDocument(File, Contents, 653 encodeVersion(Params.textDocument.version), 654 WantDiagnostics::Yes); 655 } 656 657 void ClangdLSPServer::onDocumentDidChange( 658 const DidChangeTextDocumentParams &Params) { 659 auto WantDiags = WantDiagnostics::Auto; 660 if (Params.wantDiagnostics.hasValue()) 661 WantDiags = Params.wantDiagnostics.getValue() ? WantDiagnostics::Yes 662 : WantDiagnostics::No; 663 664 PathRef File = Params.textDocument.uri.file(); 665 auto Code = Server->getDraft(File); 666 if (!Code) { 667 log("Trying to incrementally change non-added document: {0}", File); 668 return; 669 } 670 std::string NewCode(*Code); 671 for (const auto &Change : Params.contentChanges) { 672 if (auto Err = applyChange(NewCode, Change)) { 673 // If this fails, we are most likely going to be not in sync anymore with 674 // the client. It is better to remove the draft and let further 675 // operations fail rather than giving wrong results. 676 Server->removeDocument(File); 677 elog("Failed to update {0}: {1}", File, std::move(Err)); 678 return; 679 } 680 } 681 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version), 682 WantDiags, Params.forceRebuild); 683 } 684 685 void ClangdLSPServer::onDocumentDidSave( 686 const DidSaveTextDocumentParams &Params) { 687 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; }); 688 } 689 690 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { 691 // We could also reparse all open files here. However: 692 // - this could be frequent, and revalidating all the preambles isn't free 693 // - this is useful e.g. when switching git branches, but we're likely to see 694 // fresh headers but still have the old-branch main-file content 695 Server->onFileEvent(Params); 696 // FIXME: observe config files, immediately expire time-based caches, reparse: 697 // - compile_commands.json and compile_flags.txt 698 // - .clang_format and .clang-tidy 699 // - .clangd and clangd/config.yaml 700 } 701 702 void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, 703 Callback<llvm::json::Value> Reply) { 704 auto It = Handlers.CommandHandlers.find(Params.command); 705 if (It == Handlers.CommandHandlers.end()) { 706 return Reply(llvm::make_error<LSPError>( 707 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), 708 ErrorCode::InvalidParams)); 709 } 710 It->second(Params.argument, std::move(Reply)); 711 } 712 713 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit &WE, 714 Callback<llvm::json::Value> Reply) { 715 // The flow for "apply-fix" : 716 // 1. We publish a diagnostic, including fixits 717 // 2. The user clicks on the diagnostic, the editor asks us for code actions 718 // 3. We send code actions, with the fixit embedded as context 719 // 4. The user selects the fixit, the editor asks us to apply it 720 // 5. We unwrap the changes and send them back to the editor 721 // 6. The editor applies the changes (applyEdit), and sends us a reply 722 // 7. We unwrap the reply and send a reply to the editor. 723 applyEdit(WE, "Fix applied.", std::move(Reply)); 724 } 725 726 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, 727 Callback<llvm::json::Value> Reply) { 728 auto Action = [this, Reply = std::move(Reply), 729 File = Args.file](llvm::Expected<Tweak::Effect> R) mutable { 730 if (!R) 731 return Reply(R.takeError()); 732 733 assert(R->ShowMessage || (!R->ApplyEdits.empty() && "tweak has no effect")); 734 735 if (R->ShowMessage) { 736 ShowMessageParams Msg; 737 Msg.message = *R->ShowMessage; 738 Msg.type = MessageType::Info; 739 ShowMessage(Msg); 740 } 741 // When no edit is specified, make sure we Reply(). 742 if (R->ApplyEdits.empty()) 743 return Reply("Tweak applied."); 744 745 if (auto Err = validateEdits(*Server, R->ApplyEdits)) 746 return Reply(std::move(Err)); 747 748 WorkspaceEdit WE; 749 WE.changes.emplace(); 750 for (const auto &It : R->ApplyEdits) { 751 (*WE.changes)[URI::createFile(It.first()).toString()] = 752 It.second.asTextEdits(); 753 } 754 // ApplyEdit will take care of calling Reply(). 755 return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply)); 756 }; 757 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID, 758 std::move(Action)); 759 } 760 761 void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success, 762 Callback<llvm::json::Value> Reply) { 763 ApplyWorkspaceEditParams Edit; 764 Edit.edit = std::move(WE); 765 ApplyWorkspaceEdit( 766 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)]( 767 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable { 768 if (!Response) 769 return Reply(Response.takeError()); 770 if (!Response->applied) { 771 std::string Reason = Response->failureReason 772 ? *Response->failureReason 773 : "unknown reason"; 774 return Reply(error("edits were not applied: {0}", Reason)); 775 } 776 return Reply(SuccessMessage); 777 }); 778 } 779 780 void ClangdLSPServer::onWorkspaceSymbol( 781 const WorkspaceSymbolParams &Params, 782 Callback<std::vector<SymbolInformation>> Reply) { 783 Server->workspaceSymbols( 784 Params.query, Params.limit.getValueOr(Opts.CodeComplete.Limit), 785 [Reply = std::move(Reply), 786 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable { 787 if (!Items) 788 return Reply(Items.takeError()); 789 for (auto &Sym : *Items) 790 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); 791 792 Reply(std::move(*Items)); 793 }); 794 } 795 796 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, 797 Callback<llvm::Optional<Range>> Reply) { 798 Server->prepareRename( 799 Params.textDocument.uri.file(), Params.position, /*NewName*/ llvm::None, 800 Opts.Rename, 801 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable { 802 if (!Result) 803 return Reply(Result.takeError()); 804 return Reply(std::move(Result->Target)); 805 }); 806 } 807 808 void ClangdLSPServer::onRename(const RenameParams &Params, 809 Callback<WorkspaceEdit> Reply) { 810 Path File = std::string(Params.textDocument.uri.file()); 811 if (!Server->getDraft(File)) 812 return Reply(llvm::make_error<LSPError>( 813 "onRename called for non-added file", ErrorCode::InvalidParams)); 814 Server->rename( 815 File, Params.position, Params.newName, Opts.Rename, 816 [File, Params, Reply = std::move(Reply), 817 this](llvm::Expected<RenameResult> R) mutable { 818 if (!R) 819 return Reply(R.takeError()); 820 if (auto Err = validateEdits(*Server, R->GlobalChanges)) 821 return Reply(std::move(Err)); 822 WorkspaceEdit Result; 823 Result.changes.emplace(); 824 for (const auto &Rep : R->GlobalChanges) { 825 (*Result.changes)[URI::createFile(Rep.first()).toString()] = 826 Rep.second.asTextEdits(); 827 } 828 Reply(Result); 829 }); 830 } 831 832 void ClangdLSPServer::onDocumentDidClose( 833 const DidCloseTextDocumentParams &Params) { 834 PathRef File = Params.textDocument.uri.file(); 835 Server->removeDocument(File); 836 837 { 838 std::lock_guard<std::mutex> Lock(FixItsMutex); 839 FixItsMap.erase(File); 840 } 841 { 842 std::lock_guard<std::mutex> HLock(SemanticTokensMutex); 843 LastSemanticTokens.erase(File); 844 } 845 // clangd will not send updates for this file anymore, so we empty out the 846 // list of diagnostics shown on the client (e.g. in the "Problems" pane of 847 // VSCode). Note that this cannot race with actual diagnostics responses 848 // because removeDocument() guarantees no diagnostic callbacks will be 849 // executed after it returns. 850 PublishDiagnosticsParams Notification; 851 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 852 PublishDiagnostics(Notification); 853 } 854 855 void ClangdLSPServer::onDocumentOnTypeFormatting( 856 const DocumentOnTypeFormattingParams &Params, 857 Callback<std::vector<TextEdit>> Reply) { 858 auto File = Params.textDocument.uri.file(); 859 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply)); 860 } 861 862 void ClangdLSPServer::onDocumentRangeFormatting( 863 const DocumentRangeFormattingParams &Params, 864 Callback<std::vector<TextEdit>> Reply) { 865 auto File = Params.textDocument.uri.file(); 866 auto Code = Server->getDraft(File); 867 Server->formatFile(File, Params.range, 868 [Code = std::move(Code), Reply = std::move(Reply)]( 869 llvm::Expected<tooling::Replacements> Result) mutable { 870 if (Result) 871 Reply(replacementsToEdits(*Code, Result.get())); 872 else 873 Reply(Result.takeError()); 874 }); 875 } 876 877 void ClangdLSPServer::onDocumentFormatting( 878 const DocumentFormattingParams &Params, 879 Callback<std::vector<TextEdit>> Reply) { 880 auto File = Params.textDocument.uri.file(); 881 auto Code = Server->getDraft(File); 882 Server->formatFile(File, 883 /*Rng=*/llvm::None, 884 [Code = std::move(Code), Reply = std::move(Reply)]( 885 llvm::Expected<tooling::Replacements> Result) mutable { 886 if (Result) 887 Reply(replacementsToEdits(*Code, Result.get())); 888 else 889 Reply(Result.takeError()); 890 }); 891 } 892 893 /// The functions constructs a flattened view of the DocumentSymbol hierarchy. 894 /// Used by the clients that do not support the hierarchical view. 895 static std::vector<SymbolInformation> 896 flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols, 897 const URIForFile &FileURI) { 898 std::vector<SymbolInformation> Results; 899 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process = 900 [&](const DocumentSymbol &S, llvm::Optional<llvm::StringRef> ParentName) { 901 SymbolInformation SI; 902 SI.containerName = std::string(ParentName ? "" : *ParentName); 903 SI.name = S.name; 904 SI.kind = S.kind; 905 SI.location.range = S.range; 906 SI.location.uri = FileURI; 907 908 Results.push_back(std::move(SI)); 909 std::string FullName = 910 !ParentName ? S.name : (ParentName->str() + "::" + S.name); 911 for (auto &C : S.children) 912 Process(C, /*ParentName=*/FullName); 913 }; 914 for (auto &S : Symbols) 915 Process(S, /*ParentName=*/""); 916 return Results; 917 } 918 919 void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params, 920 Callback<llvm::json::Value> Reply) { 921 URIForFile FileURI = Params.textDocument.uri; 922 Server->documentSymbols( 923 Params.textDocument.uri.file(), 924 [this, FileURI, Reply = std::move(Reply)]( 925 llvm::Expected<std::vector<DocumentSymbol>> Items) mutable { 926 if (!Items) 927 return Reply(Items.takeError()); 928 adjustSymbolKinds(*Items, SupportedSymbolKinds); 929 if (SupportsHierarchicalDocumentSymbol) 930 return Reply(std::move(*Items)); 931 else 932 return Reply(flattenSymbolHierarchy(*Items, FileURI)); 933 }); 934 } 935 936 void ClangdLSPServer::onFoldingRange( 937 const FoldingRangeParams &Params, 938 Callback<std::vector<FoldingRange>> Reply) { 939 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply)); 940 } 941 942 static llvm::Optional<Command> asCommand(const CodeAction &Action) { 943 Command Cmd; 944 if (Action.command && Action.edit) 945 return None; // Not representable. (We never emit these anyway). 946 if (Action.command) { 947 Cmd = *Action.command; 948 } else if (Action.edit) { 949 Cmd.command = std::string(APPLY_FIX_COMMAND); 950 Cmd.argument = *Action.edit; 951 } else { 952 return None; 953 } 954 Cmd.title = Action.title; 955 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) 956 Cmd.title = "Apply fix: " + Cmd.title; 957 return Cmd; 958 } 959 960 void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, 961 Callback<llvm::json::Value> Reply) { 962 URIForFile File = Params.textDocument.uri; 963 // Checks whether a particular CodeActionKind is included in the response. 964 auto KindAllowed = [Only(Params.context.only)](llvm::StringRef Kind) { 965 if (Only.empty()) 966 return true; 967 return llvm::any_of(Only, [&](llvm::StringRef Base) { 968 return Kind.consume_front(Base) && (Kind.empty() || Kind.startswith(".")); 969 }); 970 }; 971 972 // We provide a code action for Fixes on the specified diagnostics. 973 std::vector<CodeAction> FixIts; 974 if (KindAllowed(CodeAction::QUICKFIX_KIND)) { 975 for (const Diagnostic &D : Params.context.diagnostics) { 976 for (auto &F : getFixes(File.file(), D)) { 977 FixIts.push_back(toCodeAction(F, Params.textDocument.uri)); 978 FixIts.back().diagnostics = {D}; 979 } 980 } 981 } 982 983 // Now enumerate the semantic code actions. 984 auto ConsumeActions = 985 [Reply = std::move(Reply), File, Selection = Params.range, 986 FixIts = std::move(FixIts), this]( 987 llvm::Expected<std::vector<ClangdServer::TweakRef>> Tweaks) mutable { 988 if (!Tweaks) 989 return Reply(Tweaks.takeError()); 990 991 std::vector<CodeAction> Actions = std::move(FixIts); 992 Actions.reserve(Actions.size() + Tweaks->size()); 993 for (const auto &T : *Tweaks) 994 Actions.push_back(toCodeAction(T, File, Selection)); 995 996 // If there's exactly one quick-fix, call it "preferred". 997 // We never consider refactorings etc as preferred. 998 CodeAction *OnlyFix = nullptr; 999 for (auto &Action : Actions) { 1000 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) { 1001 if (OnlyFix) { 1002 OnlyFix->isPreferred = false; 1003 break; 1004 } 1005 Action.isPreferred = true; 1006 OnlyFix = &Action; 1007 } 1008 } 1009 1010 if (SupportsCodeAction) 1011 return Reply(llvm::json::Array(Actions)); 1012 std::vector<Command> Commands; 1013 for (const auto &Action : Actions) { 1014 if (auto Command = asCommand(Action)) 1015 Commands.push_back(std::move(*Command)); 1016 } 1017 return Reply(llvm::json::Array(Commands)); 1018 }; 1019 Server->enumerateTweaks( 1020 File.file(), Params.range, 1021 [this, KindAllowed(std::move(KindAllowed))](const Tweak &T) { 1022 return Opts.TweakFilter(T) && KindAllowed(T.kind()); 1023 }, 1024 std::move(ConsumeActions)); 1025 } 1026 1027 void ClangdLSPServer::onCompletion(const CompletionParams &Params, 1028 Callback<CompletionList> Reply) { 1029 if (!shouldRunCompletion(Params)) { 1030 // Clients sometimes auto-trigger completions in undesired places (e.g. 1031 // 'a >^ '), we return empty results in those cases. 1032 vlog("ignored auto-triggered completion, preceding char did not match"); 1033 return Reply(CompletionList()); 1034 } 1035 auto Opts = this->Opts.CodeComplete; 1036 if (Params.limit && *Params.limit >= 0) 1037 Opts.Limit = *Params.limit; 1038 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts, 1039 [Reply = std::move(Reply), Opts, 1040 this](llvm::Expected<CodeCompleteResult> List) mutable { 1041 if (!List) 1042 return Reply(List.takeError()); 1043 CompletionList LSPList; 1044 LSPList.isIncomplete = List->HasMore; 1045 for (const auto &R : List->Completions) { 1046 CompletionItem C = R.render(Opts); 1047 C.kind = adjustKindToCapability( 1048 C.kind, SupportedCompletionItemKinds); 1049 LSPList.items.push_back(std::move(C)); 1050 } 1051 return Reply(std::move(LSPList)); 1052 }); 1053 } 1054 1055 void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params, 1056 Callback<SignatureHelp> Reply) { 1057 Server->signatureHelp(Params.textDocument.uri.file(), Params.position, 1058 [Reply = std::move(Reply), this]( 1059 llvm::Expected<SignatureHelp> Signature) mutable { 1060 if (!Signature) 1061 return Reply(Signature.takeError()); 1062 if (SupportsOffsetsInSignatureHelp) 1063 return Reply(std::move(*Signature)); 1064 // Strip out the offsets from signature help for 1065 // clients that only support string labels. 1066 for (auto &SigInfo : Signature->signatures) { 1067 for (auto &Param : SigInfo.parameters) 1068 Param.labelOffsets.reset(); 1069 } 1070 return Reply(std::move(*Signature)); 1071 }); 1072 } 1073 1074 // Go to definition has a toggle function: if def and decl are distinct, then 1075 // the first press gives you the def, the second gives you the matching def. 1076 // getToggle() returns the counterpart location that under the cursor. 1077 // 1078 // We return the toggled location alone (ignoring other symbols) to encourage 1079 // editors to "bounce" quickly between locations, without showing a menu. 1080 static Location *getToggle(const TextDocumentPositionParams &Point, 1081 LocatedSymbol &Sym) { 1082 // Toggle only makes sense with two distinct locations. 1083 if (!Sym.Definition || *Sym.Definition == Sym.PreferredDeclaration) 1084 return nullptr; 1085 if (Sym.Definition->uri.file() == Point.textDocument.uri.file() && 1086 Sym.Definition->range.contains(Point.position)) 1087 return &Sym.PreferredDeclaration; 1088 if (Sym.PreferredDeclaration.uri.file() == Point.textDocument.uri.file() && 1089 Sym.PreferredDeclaration.range.contains(Point.position)) 1090 return &*Sym.Definition; 1091 return nullptr; 1092 } 1093 1094 void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params, 1095 Callback<std::vector<Location>> Reply) { 1096 Server->locateSymbolAt( 1097 Params.textDocument.uri.file(), Params.position, 1098 [Params, Reply = std::move(Reply)]( 1099 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1100 if (!Symbols) 1101 return Reply(Symbols.takeError()); 1102 std::vector<Location> Defs; 1103 for (auto &S : *Symbols) { 1104 if (Location *Toggle = getToggle(Params, S)) 1105 return Reply(std::vector<Location>{std::move(*Toggle)}); 1106 Defs.push_back(S.Definition.getValueOr(S.PreferredDeclaration)); 1107 } 1108 Reply(std::move(Defs)); 1109 }); 1110 } 1111 1112 void ClangdLSPServer::onGoToDeclaration( 1113 const TextDocumentPositionParams &Params, 1114 Callback<std::vector<Location>> Reply) { 1115 Server->locateSymbolAt( 1116 Params.textDocument.uri.file(), Params.position, 1117 [Params, Reply = std::move(Reply)]( 1118 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1119 if (!Symbols) 1120 return Reply(Symbols.takeError()); 1121 std::vector<Location> Decls; 1122 for (auto &S : *Symbols) { 1123 if (Location *Toggle = getToggle(Params, S)) 1124 return Reply(std::vector<Location>{std::move(*Toggle)}); 1125 Decls.push_back(std::move(S.PreferredDeclaration)); 1126 } 1127 Reply(std::move(Decls)); 1128 }); 1129 } 1130 1131 void ClangdLSPServer::onSwitchSourceHeader( 1132 const TextDocumentIdentifier &Params, 1133 Callback<llvm::Optional<URIForFile>> Reply) { 1134 Server->switchSourceHeader( 1135 Params.uri.file(), 1136 [Reply = std::move(Reply), 1137 Params](llvm::Expected<llvm::Optional<clangd::Path>> Path) mutable { 1138 if (!Path) 1139 return Reply(Path.takeError()); 1140 if (*Path) 1141 return Reply(URIForFile::canonicalize(**Path, Params.uri.file())); 1142 return Reply(llvm::None); 1143 }); 1144 } 1145 1146 void ClangdLSPServer::onDocumentHighlight( 1147 const TextDocumentPositionParams &Params, 1148 Callback<std::vector<DocumentHighlight>> Reply) { 1149 Server->findDocumentHighlights(Params.textDocument.uri.file(), 1150 Params.position, std::move(Reply)); 1151 } 1152 1153 void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, 1154 Callback<llvm::Optional<Hover>> Reply) { 1155 Server->findHover(Params.textDocument.uri.file(), Params.position, 1156 [Reply = std::move(Reply), this]( 1157 llvm::Expected<llvm::Optional<HoverInfo>> H) mutable { 1158 if (!H) 1159 return Reply(H.takeError()); 1160 if (!*H) 1161 return Reply(llvm::None); 1162 1163 Hover R; 1164 R.contents.kind = HoverContentFormat; 1165 R.range = (*H)->SymRange; 1166 switch (HoverContentFormat) { 1167 case MarkupKind::PlainText: 1168 R.contents.value = (*H)->present().asPlainText(); 1169 return Reply(std::move(R)); 1170 case MarkupKind::Markdown: 1171 R.contents.value = (*H)->present().asMarkdown(); 1172 return Reply(std::move(R)); 1173 }; 1174 llvm_unreachable("unhandled MarkupKind"); 1175 }); 1176 } 1177 1178 void ClangdLSPServer::onTypeHierarchy( 1179 const TypeHierarchyParams &Params, 1180 Callback<Optional<TypeHierarchyItem>> Reply) { 1181 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position, 1182 Params.resolve, Params.direction, std::move(Reply)); 1183 } 1184 1185 void ClangdLSPServer::onResolveTypeHierarchy( 1186 const ResolveTypeHierarchyItemParams &Params, 1187 Callback<Optional<TypeHierarchyItem>> Reply) { 1188 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, 1189 std::move(Reply)); 1190 } 1191 1192 void ClangdLSPServer::onPrepareCallHierarchy( 1193 const CallHierarchyPrepareParams &Params, 1194 Callback<std::vector<CallHierarchyItem>> Reply) { 1195 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position, 1196 std::move(Reply)); 1197 } 1198 1199 void ClangdLSPServer::onCallHierarchyIncomingCalls( 1200 const CallHierarchyIncomingCallsParams &Params, 1201 Callback<std::vector<CallHierarchyIncomingCall>> Reply) { 1202 Server->incomingCalls(Params.item, std::move(Reply)); 1203 } 1204 1205 void ClangdLSPServer::onCallHierarchyOutgoingCalls( 1206 const CallHierarchyOutgoingCallsParams &Params, 1207 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) { 1208 // FIXME: To be implemented. 1209 Reply(std::vector<CallHierarchyOutgoingCall>{}); 1210 } 1211 1212 void ClangdLSPServer::onInlayHints(const InlayHintsParams &Params, 1213 Callback<std::vector<InlayHint>> Reply) { 1214 Server->inlayHints(Params.textDocument.uri.file(), std::move(Reply)); 1215 } 1216 1217 void ClangdLSPServer::applyConfiguration( 1218 const ConfigurationSettings &Settings) { 1219 // Per-file update to the compilation database. 1220 llvm::StringSet<> ModifiedFiles; 1221 for (auto &Entry : Settings.compilationDatabaseChanges) { 1222 PathRef File = Entry.first; 1223 auto Old = CDB->getCompileCommand(File); 1224 auto New = 1225 tooling::CompileCommand(std::move(Entry.second.workingDirectory), File, 1226 std::move(Entry.second.compilationCommand), 1227 /*Output=*/""); 1228 if (Old != New) { 1229 CDB->setCompileCommand(File, std::move(New)); 1230 ModifiedFiles.insert(File); 1231 } 1232 } 1233 1234 Server->reparseOpenFilesIfNeeded( 1235 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); 1236 } 1237 1238 void ClangdLSPServer::maybeExportMemoryProfile() { 1239 if (!trace::enabled() || !ShouldProfile()) 1240 return; 1241 1242 static constexpr trace::Metric MemoryUsage( 1243 "memory_usage", trace::Metric::Value, "component_name"); 1244 trace::Span Tracer("ProfileBrief"); 1245 MemoryTree MT; 1246 profile(MT); 1247 record(MT, "clangd_lsp_server", MemoryUsage); 1248 } 1249 1250 void ClangdLSPServer::maybeCleanupMemory() { 1251 if (!Opts.MemoryCleanup || !ShouldCleanupMemory()) 1252 return; 1253 Opts.MemoryCleanup(); 1254 } 1255 1256 // FIXME: This function needs to be properly tested. 1257 void ClangdLSPServer::onChangeConfiguration( 1258 const DidChangeConfigurationParams &Params) { 1259 applyConfiguration(Params.settings); 1260 } 1261 1262 void ClangdLSPServer::onReference(const ReferenceParams &Params, 1263 Callback<std::vector<Location>> Reply) { 1264 Server->findReferences( 1265 Params.textDocument.uri.file(), Params.position, Opts.CodeComplete.Limit, 1266 [Reply = std::move(Reply), 1267 IncludeDecl(Params.context.includeDeclaration)]( 1268 llvm::Expected<ReferencesResult> Refs) mutable { 1269 if (!Refs) 1270 return Reply(Refs.takeError()); 1271 // Filter out declarations if the client asked. 1272 std::vector<Location> Result; 1273 Result.reserve(Refs->References.size()); 1274 for (auto &Ref : Refs->References) { 1275 bool IsDecl = Ref.Attributes & ReferencesResult::Declaration; 1276 if (IncludeDecl || !IsDecl) 1277 Result.push_back(std::move(Ref.Loc)); 1278 } 1279 return Reply(std::move(Result)); 1280 }); 1281 } 1282 1283 void ClangdLSPServer::onGoToImplementation( 1284 const TextDocumentPositionParams &Params, 1285 Callback<std::vector<Location>> Reply) { 1286 Server->findImplementations( 1287 Params.textDocument.uri.file(), Params.position, 1288 [Reply = std::move(Reply)]( 1289 llvm::Expected<std::vector<LocatedSymbol>> Overrides) mutable { 1290 if (!Overrides) 1291 return Reply(Overrides.takeError()); 1292 std::vector<Location> Impls; 1293 for (const LocatedSymbol &Sym : *Overrides) 1294 Impls.push_back(Sym.PreferredDeclaration); 1295 return Reply(std::move(Impls)); 1296 }); 1297 } 1298 1299 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, 1300 Callback<std::vector<SymbolDetails>> Reply) { 1301 Server->symbolInfo(Params.textDocument.uri.file(), Params.position, 1302 std::move(Reply)); 1303 } 1304 1305 void ClangdLSPServer::onSelectionRange( 1306 const SelectionRangeParams &Params, 1307 Callback<std::vector<SelectionRange>> Reply) { 1308 Server->semanticRanges( 1309 Params.textDocument.uri.file(), Params.positions, 1310 [Reply = std::move(Reply)]( 1311 llvm::Expected<std::vector<SelectionRange>> Ranges) mutable { 1312 if (!Ranges) 1313 return Reply(Ranges.takeError()); 1314 return Reply(std::move(*Ranges)); 1315 }); 1316 } 1317 1318 void ClangdLSPServer::onDocumentLink( 1319 const DocumentLinkParams &Params, 1320 Callback<std::vector<DocumentLink>> Reply) { 1321 1322 // TODO(forster): This currently resolves all targets eagerly. This is slow, 1323 // because it blocks on the preamble/AST being built. We could respond to the 1324 // request faster by using string matching or the lexer to find the includes 1325 // and resolving the targets lazily. 1326 Server->documentLinks( 1327 Params.textDocument.uri.file(), 1328 [Reply = std::move(Reply)]( 1329 llvm::Expected<std::vector<DocumentLink>> Links) mutable { 1330 if (!Links) { 1331 return Reply(Links.takeError()); 1332 } 1333 return Reply(std::move(Links)); 1334 }); 1335 } 1336 1337 // Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ... 1338 static void increment(std::string &S) { 1339 for (char &C : llvm::reverse(S)) { 1340 if (C != '9') { 1341 ++C; 1342 return; 1343 } 1344 C = '0'; 1345 } 1346 S.insert(S.begin(), '1'); 1347 } 1348 1349 void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams &Params, 1350 Callback<SemanticTokens> CB) { 1351 Server->semanticHighlights( 1352 Params.textDocument.uri.file(), 1353 [this, File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( 1354 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1355 if (!HT) 1356 return CB(HT.takeError()); 1357 SemanticTokens Result; 1358 Result.tokens = toSemanticTokens(*HT); 1359 { 1360 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1361 auto &Last = LastSemanticTokens[File]; 1362 1363 Last.tokens = Result.tokens; 1364 increment(Last.resultId); 1365 Result.resultId = Last.resultId; 1366 } 1367 CB(std::move(Result)); 1368 }); 1369 } 1370 1371 void ClangdLSPServer::onSemanticTokensDelta( 1372 const SemanticTokensDeltaParams &Params, 1373 Callback<SemanticTokensOrDelta> CB) { 1374 Server->semanticHighlights( 1375 Params.textDocument.uri.file(), 1376 [this, PrevResultID(Params.previousResultId), 1377 File(Params.textDocument.uri.file().str()), CB(std::move(CB))]( 1378 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1379 if (!HT) 1380 return CB(HT.takeError()); 1381 std::vector<SemanticToken> Toks = toSemanticTokens(*HT); 1382 1383 SemanticTokensOrDelta Result; 1384 { 1385 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1386 auto &Last = LastSemanticTokens[File]; 1387 1388 if (PrevResultID == Last.resultId) { 1389 Result.edits = diffTokens(Last.tokens, Toks); 1390 } else { 1391 vlog("semanticTokens/full/delta: wanted edits vs {0} but last " 1392 "result had ID {1}. Returning full token list.", 1393 PrevResultID, Last.resultId); 1394 Result.tokens = Toks; 1395 } 1396 1397 Last.tokens = std::move(Toks); 1398 increment(Last.resultId); 1399 Result.resultId = Last.resultId; 1400 } 1401 1402 CB(std::move(Result)); 1403 }); 1404 } 1405 1406 void ClangdLSPServer::onMemoryUsage(const NoParams &, 1407 Callback<MemoryTree> Reply) { 1408 llvm::BumpPtrAllocator DetailAlloc; 1409 MemoryTree MT(&DetailAlloc); 1410 profile(MT); 1411 Reply(std::move(MT)); 1412 } 1413 1414 void ClangdLSPServer::onAST(const ASTParams &Params, 1415 Callback<llvm::Optional<ASTNode>> CB) { 1416 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB)); 1417 } 1418 1419 ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, 1420 const ClangdLSPServer::Options &Opts) 1421 : ShouldProfile(/*Period=*/std::chrono::minutes(5), 1422 /*Delay=*/std::chrono::minutes(1)), 1423 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), 1424 /*Delay=*/std::chrono::minutes(1)), 1425 BackgroundContext(Context::current().clone()), Transp(Transp), 1426 MsgHandler(new MessageHandler(*this)), TFS(TFS), 1427 SupportedSymbolKinds(defaultSymbolKinds()), 1428 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { 1429 if (Opts.ConfigProvider) { 1430 assert(!Opts.ContextProvider && 1431 "Only one of ConfigProvider and ContextProvider allowed!"); 1432 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( 1433 Opts.ConfigProvider, this); 1434 } 1435 LSPBinder Bind(this->Handlers, *this); 1436 Bind.method("initialize", this, &ClangdLSPServer::onInitialize); 1437 } 1438 1439 void ClangdLSPServer::bindMethods(LSPBinder &Bind, 1440 const ClientCapabilities &Caps) { 1441 // clang-format off 1442 Bind.notification("initialized", this, &ClangdLSPServer::onInitialized); 1443 Bind.method("shutdown", this, &ClangdLSPServer::onShutdown); 1444 Bind.method("sync", this, &ClangdLSPServer::onSync); 1445 Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting); 1446 Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting); 1447 Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting); 1448 Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction); 1449 Bind.method("textDocument/completion", this, &ClangdLSPServer::onCompletion); 1450 Bind.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp); 1451 Bind.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition); 1452 Bind.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration); 1453 Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation); 1454 Bind.method("textDocument/references", this, &ClangdLSPServer::onReference); 1455 Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); 1456 Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); 1457 Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); 1458 Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); 1459 Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); 1460 Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); 1461 Bind.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight); 1462 Bind.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol); 1463 Bind.method("textDocument/ast", this, &ClangdLSPServer::onAST); 1464 Bind.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen); 1465 Bind.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose); 1466 Bind.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange); 1467 Bind.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave); 1468 Bind.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent); 1469 Bind.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration); 1470 Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo); 1471 Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy); 1472 Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy); 1473 Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy); 1474 Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls); 1475 Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls); 1476 Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange); 1477 Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink); 1478 Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens); 1479 Bind.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta); 1480 Bind.method("clangd/inlayHints", this, &ClangdLSPServer::onInlayHints); 1481 Bind.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage); 1482 if (Opts.FoldingRanges) 1483 Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange); 1484 Bind.command(APPLY_FIX_COMMAND, this, &ClangdLSPServer::onCommandApplyEdit); 1485 Bind.command(APPLY_TWEAK_COMMAND, this, &ClangdLSPServer::onCommandApplyTweak); 1486 1487 ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit"); 1488 PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics"); 1489 ShowMessage = Bind.outgoingNotification("window/showMessage"); 1490 NotifyFileStatus = Bind.outgoingNotification("textDocument/clangd.fileStatus"); 1491 CreateWorkDoneProgress = Bind.outgoingMethod("window/workDoneProgress/create"); 1492 BeginWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1493 ReportWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1494 EndWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1495 if(Caps.SemanticTokenRefreshSupport) 1496 SemanticTokensRefresh = Bind.outgoingMethod("workspace/semanticTokens/refresh"); 1497 // clang-format on 1498 } 1499 1500 ClangdLSPServer::~ClangdLSPServer() { 1501 IsBeingDestroyed = true; 1502 // Explicitly destroy ClangdServer first, blocking on threads it owns. 1503 // This ensures they don't access any other members. 1504 Server.reset(); 1505 } 1506 1507 bool ClangdLSPServer::run() { 1508 // Run the Language Server loop. 1509 bool CleanExit = true; 1510 if (auto Err = Transp.loop(*MsgHandler)) { 1511 elog("Transport error: {0}", std::move(Err)); 1512 CleanExit = false; 1513 } 1514 1515 return CleanExit && ShutdownRequestReceived; 1516 } 1517 1518 void ClangdLSPServer::profile(MemoryTree &MT) const { 1519 if (Server) 1520 Server->profile(MT.child("clangd_server")); 1521 } 1522 1523 std::vector<Fix> ClangdLSPServer::getFixes(llvm::StringRef File, 1524 const clangd::Diagnostic &D) { 1525 std::lock_guard<std::mutex> Lock(FixItsMutex); 1526 auto DiagToFixItsIter = FixItsMap.find(File); 1527 if (DiagToFixItsIter == FixItsMap.end()) 1528 return {}; 1529 1530 const auto &DiagToFixItsMap = DiagToFixItsIter->second; 1531 auto FixItsIter = DiagToFixItsMap.find(D); 1532 if (FixItsIter == DiagToFixItsMap.end()) 1533 return {}; 1534 1535 return FixItsIter->second; 1536 } 1537 1538 // A completion request is sent when the user types '>' or ':', but we only 1539 // want to trigger on '->' and '::'. We check the preceeding text to make 1540 // sure it matches what we expected. 1541 // Running the lexer here would be more robust (e.g. we can detect comments 1542 // and avoid triggering completion there), but we choose to err on the side 1543 // of simplicity here. 1544 bool ClangdLSPServer::shouldRunCompletion( 1545 const CompletionParams &Params) const { 1546 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter) 1547 return true; 1548 auto Code = Server->getDraft(Params.textDocument.uri.file()); 1549 if (!Code) 1550 return true; // completion code will log the error for untracked doc. 1551 auto Offset = positionToOffset(*Code, Params.position, 1552 /*AllowColumnsBeyondLineLength=*/false); 1553 if (!Offset) { 1554 vlog("could not convert position '{0}' to offset for file '{1}'", 1555 Params.position, Params.textDocument.uri.file()); 1556 return true; 1557 } 1558 return allowImplicitCompletion(*Code, *Offset); 1559 } 1560 1561 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, 1562 std::vector<Diag> Diagnostics) { 1563 PublishDiagnosticsParams Notification; 1564 Notification.version = decodeVersion(Version); 1565 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 1566 DiagnosticToReplacementMap LocalFixIts; // Temporary storage 1567 for (auto &Diag : Diagnostics) { 1568 toLSPDiags(Diag, Notification.uri, DiagOpts, 1569 [&](clangd::Diagnostic Diag, llvm::ArrayRef<Fix> Fixes) { 1570 auto &FixItsForDiagnostic = LocalFixIts[Diag]; 1571 llvm::copy(Fixes, std::back_inserter(FixItsForDiagnostic)); 1572 Notification.diagnostics.push_back(std::move(Diag)); 1573 }); 1574 } 1575 1576 // Cache FixIts 1577 { 1578 std::lock_guard<std::mutex> Lock(FixItsMutex); 1579 FixItsMap[File] = LocalFixIts; 1580 } 1581 1582 // Send a notification to the LSP client. 1583 PublishDiagnostics(Notification); 1584 } 1585 1586 void ClangdLSPServer::onBackgroundIndexProgress( 1587 const BackgroundQueue::Stats &Stats) { 1588 static const char ProgressToken[] = "backgroundIndexProgress"; 1589 1590 // The background index did some work, maybe we need to cleanup 1591 maybeCleanupMemory(); 1592 1593 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1594 1595 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) { 1596 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) { 1597 WorkDoneProgressBegin Begin; 1598 Begin.percentage = true; 1599 Begin.title = "indexing"; 1600 BeginWorkDoneProgress({ProgressToken, std::move(Begin)}); 1601 BackgroundIndexProgressState = BackgroundIndexProgress::Live; 1602 } 1603 1604 if (Stats.Completed < Stats.Enqueued) { 1605 assert(Stats.Enqueued > Stats.LastIdle); 1606 WorkDoneProgressReport Report; 1607 Report.percentage = 100.0 * (Stats.Completed - Stats.LastIdle) / 1608 (Stats.Enqueued - Stats.LastIdle); 1609 Report.message = 1610 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle, 1611 Stats.Enqueued - Stats.LastIdle); 1612 ReportWorkDoneProgress({ProgressToken, std::move(Report)}); 1613 } else { 1614 assert(Stats.Completed == Stats.Enqueued); 1615 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()}); 1616 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; 1617 } 1618 }; 1619 1620 switch (BackgroundIndexProgressState) { 1621 case BackgroundIndexProgress::Unsupported: 1622 return; 1623 case BackgroundIndexProgress::Creating: 1624 // Cache this update for when the progress bar is available. 1625 PendingBackgroundIndexProgress = Stats; 1626 return; 1627 case BackgroundIndexProgress::Empty: { 1628 if (BackgroundIndexSkipCreate) { 1629 NotifyProgress(Stats); 1630 break; 1631 } 1632 // Cache this update for when the progress bar is available. 1633 PendingBackgroundIndexProgress = Stats; 1634 BackgroundIndexProgressState = BackgroundIndexProgress::Creating; 1635 WorkDoneProgressCreateParams CreateRequest; 1636 CreateRequest.token = ProgressToken; 1637 CreateWorkDoneProgress( 1638 CreateRequest, 1639 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) { 1640 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1641 if (E) { 1642 NotifyProgress(this->PendingBackgroundIndexProgress); 1643 } else { 1644 elog("Failed to create background index progress bar: {0}", 1645 E.takeError()); 1646 // give up forever rather than thrashing about 1647 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; 1648 } 1649 }); 1650 break; 1651 } 1652 case BackgroundIndexProgress::Live: 1653 NotifyProgress(Stats); 1654 break; 1655 } 1656 } 1657 1658 void ClangdLSPServer::onFileUpdated(PathRef File, const TUStatus &Status) { 1659 if (!SupportFileStatus) 1660 return; 1661 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these 1662 // two statuses are running faster in practice, which leads the UI constantly 1663 // changing, and doesn't provide much value. We may want to emit status at a 1664 // reasonable time interval (e.g. 0.5s). 1665 if (Status.PreambleActivity == PreambleAction::Idle && 1666 (Status.ASTActivity.K == ASTAction::Building || 1667 Status.ASTActivity.K == ASTAction::RunningAction)) 1668 return; 1669 NotifyFileStatus(Status.render(File)); 1670 } 1671 1672 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File) { 1673 if (SemanticTokensRefresh) { 1674 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) { 1675 if (E) 1676 return; 1677 elog("Failed to refresh semantic tokens: {0}", E.takeError()); 1678 }); 1679 } 1680 } 1681 } // namespace clangd 1682 } // namespace clang 1683