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