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 "CompileCommands.h" 13 #include "Diagnostics.h" 14 #include "Feature.h" 15 #include "GlobalCompilationDatabase.h" 16 #include "LSPBinder.h" 17 #include "ModulesBuilder.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/Cancellation.h" 25 #include "support/Context.h" 26 #include "support/MemoryTree.h" 27 #include "support/Trace.h" 28 #include "clang/Tooling/Core/Replacement.h" 29 #include "llvm/ADT/ArrayRef.h" 30 #include "llvm/ADT/FunctionExtras.h" 31 #include "llvm/ADT/ScopeExit.h" 32 #include "llvm/ADT/StringRef.h" 33 #include "llvm/ADT/Twine.h" 34 #include "llvm/Support/Allocator.h" 35 #include "llvm/Support/Error.h" 36 #include "llvm/Support/FormatVariadic.h" 37 #include "llvm/Support/JSON.h" 38 #include "llvm/Support/SHA1.h" 39 #include "llvm/Support/ScopedPrinter.h" 40 #include "llvm/Support/raw_ostream.h" 41 #include <chrono> 42 #include <cstddef> 43 #include <cstdint> 44 #include <functional> 45 #include <map> 46 #include <memory> 47 #include <mutex> 48 #include <optional> 49 #include <string> 50 #include <utility> 51 #include <vector> 52 53 namespace clang { 54 namespace clangd { 55 56 namespace { 57 // Tracks end-to-end latency of high level lsp calls. Measurements are in 58 // seconds. 59 constexpr trace::Metric LSPLatency("lsp_latency", trace::Metric::Distribution, 60 "method_name"); 61 62 // LSP defines file versions as numbers that increase. 63 // ClangdServer treats them as opaque and therefore uses strings instead. 64 std::string encodeVersion(std::optional<int64_t> LSPVersion) { 65 return LSPVersion ? llvm::to_string(*LSPVersion) : ""; 66 } 67 std::optional<int64_t> decodeVersion(llvm::StringRef Encoded) { 68 int64_t Result; 69 if (llvm::to_integer(Encoded, Result, 10)) 70 return Result; 71 if (!Encoded.empty()) // Empty can be e.g. diagnostics on close. 72 elog("unexpected non-numeric version {0}", Encoded); 73 return std::nullopt; 74 } 75 76 const llvm::StringLiteral ApplyFixCommand = "clangd.applyFix"; 77 const llvm::StringLiteral ApplyTweakCommand = "clangd.applyTweak"; 78 const llvm::StringLiteral ApplyRenameCommand = "clangd.applyRename"; 79 80 CodeAction toCodeAction(const ClangdServer::CodeActionResult::Rename &R, 81 const URIForFile &File) { 82 CodeAction CA; 83 CA.title = R.FixMessage; 84 CA.kind = std::string(CodeAction::REFACTOR_KIND); 85 CA.command.emplace(); 86 CA.command->title = R.FixMessage; 87 CA.command->command = std::string(ApplyRenameCommand); 88 RenameParams Params; 89 Params.textDocument = TextDocumentIdentifier{File}; 90 Params.position = R.Diag.Range.start; 91 Params.newName = R.NewName; 92 CA.command->argument = Params; 93 return CA; 94 } 95 96 /// Transforms a tweak into a code action that would apply it if executed. 97 /// EXPECTS: T.prepare() was called and returned true. 98 CodeAction toCodeAction(const ClangdServer::TweakRef &T, const URIForFile &File, 99 Range Selection) { 100 CodeAction CA; 101 CA.title = T.Title; 102 CA.kind = T.Kind.str(); 103 // This tweak may have an expensive second stage, we only run it if the user 104 // actually chooses it in the UI. We reply with a command that would run the 105 // corresponding tweak. 106 // FIXME: for some tweaks, computing the edits is cheap and we could send them 107 // directly. 108 CA.command.emplace(); 109 CA.command->title = T.Title; 110 CA.command->command = std::string(ApplyTweakCommand); 111 TweakArgs Args; 112 Args.file = File; 113 Args.tweakID = T.ID; 114 Args.selection = Selection; 115 CA.command->argument = std::move(Args); 116 return CA; 117 } 118 119 /// Convert from Fix to LSP CodeAction. 120 CodeAction toCodeAction(const Fix &F, const URIForFile &File, 121 const std::optional<int64_t> &Version, 122 bool SupportsDocumentChanges, 123 bool SupportChangeAnnotation) { 124 CodeAction Action; 125 Action.title = F.Message; 126 Action.kind = std::string(CodeAction::QUICKFIX_KIND); 127 Action.edit.emplace(); 128 if (!SupportsDocumentChanges) { 129 Action.edit->changes.emplace(); 130 auto &Changes = (*Action.edit->changes)[File.uri()]; 131 for (const auto &E : F.Edits) 132 Changes.push_back({E.range, E.newText, /*annotationId=*/""}); 133 } else { 134 Action.edit->documentChanges.emplace(); 135 TextDocumentEdit &Edit = Action.edit->documentChanges->emplace_back(); 136 Edit.textDocument = VersionedTextDocumentIdentifier{{File}, Version}; 137 for (const auto &E : F.Edits) 138 Edit.edits.push_back( 139 {E.range, E.newText, 140 SupportChangeAnnotation ? E.annotationId : ""}); 141 if (SupportChangeAnnotation) { 142 for (const auto &[AID, Annotation]: F.Annotations) 143 Action.edit->changeAnnotations[AID] = Annotation; 144 } 145 } 146 return Action; 147 } 148 149 void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms, 150 SymbolKindBitset Kinds) { 151 for (auto &S : Syms) { 152 S.kind = adjustKindToCapability(S.kind, Kinds); 153 adjustSymbolKinds(S.children, Kinds); 154 } 155 } 156 157 SymbolKindBitset defaultSymbolKinds() { 158 SymbolKindBitset Defaults; 159 for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array); 160 ++I) 161 Defaults.set(I); 162 return Defaults; 163 } 164 165 CompletionItemKindBitset defaultCompletionItemKinds() { 166 CompletionItemKindBitset Defaults; 167 for (size_t I = CompletionItemKindMin; 168 I <= static_cast<size_t>(CompletionItemKind::Reference); ++I) 169 Defaults.set(I); 170 return Defaults; 171 } 172 173 // Makes sure edits in \p FE are applicable to latest file contents reported by 174 // editor. If not generates an error message containing information about files 175 // that needs to be saved. 176 llvm::Error validateEdits(const ClangdServer &Server, const FileEdits &FE) { 177 size_t InvalidFileCount = 0; 178 llvm::StringRef LastInvalidFile; 179 for (const auto &It : FE) { 180 if (auto Draft = Server.getDraft(It.first())) { 181 // If the file is open in user's editor, make sure the version we 182 // saw and current version are compatible as this is the text that 183 // will be replaced by editors. 184 if (!It.second.canApplyTo(*Draft)) { 185 ++InvalidFileCount; 186 LastInvalidFile = It.first(); 187 } 188 } 189 } 190 if (!InvalidFileCount) 191 return llvm::Error::success(); 192 if (InvalidFileCount == 1) 193 return error("File must be saved first: {0}", LastInvalidFile); 194 return error("Files must be saved first: {0} (and {1} others)", 195 LastInvalidFile, InvalidFileCount - 1); 196 } 197 } // namespace 198 199 // MessageHandler dispatches incoming LSP messages. 200 // It handles cross-cutting concerns: 201 // - serializes/deserializes protocol objects to JSON 202 // - logging of inbound messages 203 // - cancellation handling 204 // - basic call tracing 205 // MessageHandler ensures that initialize() is called before any other handler. 206 class ClangdLSPServer::MessageHandler : public Transport::MessageHandler { 207 public: 208 MessageHandler(ClangdLSPServer &Server) : Server(Server) {} 209 210 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override { 211 trace::Span Tracer(Method, LSPLatency); 212 SPAN_ATTACH(Tracer, "Params", Params); 213 WithContext HandlerContext(handlerContext()); 214 log("<-- {0}", Method); 215 if (Method == "exit") 216 return false; 217 auto Handler = Server.Handlers.NotificationHandlers.find(Method); 218 if (Handler != Server.Handlers.NotificationHandlers.end()) { 219 Handler->second(std::move(Params)); 220 Server.maybeExportMemoryProfile(); 221 Server.maybeCleanupMemory(); 222 } else if (!Server.Server) { 223 elog("Notification {0} before initialization", Method); 224 } else if (Method == "$/cancelRequest") { 225 onCancel(std::move(Params)); 226 } else { 227 log("unhandled notification {0}", Method); 228 } 229 return true; 230 } 231 232 bool onCall(llvm::StringRef Method, llvm::json::Value Params, 233 llvm::json::Value ID) override { 234 WithContext HandlerContext(handlerContext()); 235 // Calls can be canceled by the client. Add cancellation context. 236 WithContext WithCancel(cancelableRequestContext(ID)); 237 trace::Span Tracer(Method, LSPLatency); 238 SPAN_ATTACH(Tracer, "Params", Params); 239 ReplyOnce Reply(ID, Method, &Server, Tracer.Args); 240 log("<-- {0}({1})", Method, ID); 241 auto Handler = Server.Handlers.MethodHandlers.find(Method); 242 if (Handler != Server.Handlers.MethodHandlers.end()) { 243 Handler->second(std::move(Params), std::move(Reply)); 244 } else if (!Server.Server) { 245 elog("Call {0} before initialization.", Method); 246 Reply(llvm::make_error<LSPError>("server not initialized", 247 ErrorCode::ServerNotInitialized)); 248 } else { 249 Reply(llvm::make_error<LSPError>("method not found", 250 ErrorCode::MethodNotFound)); 251 } 252 return true; 253 } 254 255 bool onReply(llvm::json::Value ID, 256 llvm::Expected<llvm::json::Value> Result) override { 257 WithContext HandlerContext(handlerContext()); 258 259 Callback<llvm::json::Value> ReplyHandler = nullptr; 260 if (auto IntID = ID.getAsInteger()) { 261 std::lock_guard<std::mutex> Mutex(CallMutex); 262 // Find a corresponding callback for the request ID; 263 for (size_t Index = 0; Index < ReplyCallbacks.size(); ++Index) { 264 if (ReplyCallbacks[Index].first == *IntID) { 265 ReplyHandler = std::move(ReplyCallbacks[Index].second); 266 ReplyCallbacks.erase(ReplyCallbacks.begin() + 267 Index); // remove the entry 268 break; 269 } 270 } 271 } 272 273 if (!ReplyHandler) { 274 // No callback being found, use a default log callback. 275 ReplyHandler = [&ID](llvm::Expected<llvm::json::Value> Result) { 276 elog("received a reply with ID {0}, but there was no such call", ID); 277 if (!Result) 278 llvm::consumeError(Result.takeError()); 279 }; 280 } 281 282 // Log and run the reply handler. 283 if (Result) { 284 log("<-- reply({0})", ID); 285 ReplyHandler(std::move(Result)); 286 } else { 287 auto Err = Result.takeError(); 288 log("<-- reply({0}) error: {1}", ID, Err); 289 ReplyHandler(std::move(Err)); 290 } 291 return true; 292 } 293 294 // Bind a reply callback to a request. The callback will be invoked when 295 // clangd receives the reply from the LSP client. 296 // Return a call id of the request. 297 llvm::json::Value bindReply(Callback<llvm::json::Value> Reply) { 298 std::optional<std::pair<int, Callback<llvm::json::Value>>> OldestCB; 299 int ID; 300 { 301 std::lock_guard<std::mutex> Mutex(CallMutex); 302 ID = NextCallID++; 303 ReplyCallbacks.emplace_back(ID, std::move(Reply)); 304 305 // If the queue overflows, we assume that the client didn't reply the 306 // oldest request, and run the corresponding callback which replies an 307 // error to the client. 308 if (ReplyCallbacks.size() > MaxReplayCallbacks) { 309 elog("more than {0} outstanding LSP calls, forgetting about {1}", 310 MaxReplayCallbacks, ReplyCallbacks.front().first); 311 OldestCB = std::move(ReplyCallbacks.front()); 312 ReplyCallbacks.pop_front(); 313 } 314 } 315 if (OldestCB) 316 OldestCB->second( 317 error("failed to receive a client reply for request ({0})", 318 OldestCB->first)); 319 return ID; 320 } 321 322 private: 323 // Function object to reply to an LSP call. 324 // Each instance must be called exactly once, otherwise: 325 // - the bug is logged, and (in debug mode) an assert will fire 326 // - if there was no reply, an error reply is sent 327 // - if there were multiple replies, only the first is sent 328 class ReplyOnce { 329 std::atomic<bool> Replied = {false}; 330 std::chrono::steady_clock::time_point Start; 331 llvm::json::Value ID; 332 std::string Method; 333 ClangdLSPServer *Server; // Null when moved-from. 334 llvm::json::Object *TraceArgs; 335 336 public: 337 ReplyOnce(const llvm::json::Value &ID, llvm::StringRef Method, 338 ClangdLSPServer *Server, llvm::json::Object *TraceArgs) 339 : Start(std::chrono::steady_clock::now()), ID(ID), Method(Method), 340 Server(Server), TraceArgs(TraceArgs) { 341 assert(Server); 342 } 343 ReplyOnce(ReplyOnce &&Other) 344 : Replied(Other.Replied.load()), Start(Other.Start), 345 ID(std::move(Other.ID)), Method(std::move(Other.Method)), 346 Server(Other.Server), TraceArgs(Other.TraceArgs) { 347 Other.Server = nullptr; 348 } 349 ReplyOnce &operator=(ReplyOnce &&) = delete; 350 ReplyOnce(const ReplyOnce &) = delete; 351 ReplyOnce &operator=(const ReplyOnce &) = delete; 352 353 ~ReplyOnce() { 354 // There's one legitimate reason to never reply to a request: clangd's 355 // request handler send a call to the client (e.g. applyEdit) and the 356 // client never replied. In this case, the ReplyOnce is owned by 357 // ClangdLSPServer's reply callback table and is destroyed along with the 358 // server. We don't attempt to send a reply in this case, there's little 359 // to be gained from doing so. 360 if (Server && !Server->IsBeingDestroyed && !Replied) { 361 elog("No reply to message {0}({1})", Method, ID); 362 assert(false && "must reply to all calls!"); 363 (*this)(llvm::make_error<LSPError>("server failed to reply", 364 ErrorCode::InternalError)); 365 } 366 } 367 368 void operator()(llvm::Expected<llvm::json::Value> Reply) { 369 assert(Server && "moved-from!"); 370 if (Replied.exchange(true)) { 371 elog("Replied twice to message {0}({1})", Method, ID); 372 assert(false && "must reply to each call only once!"); 373 return; 374 } 375 auto Duration = std::chrono::steady_clock::now() - Start; 376 if (Reply) { 377 log("--> reply:{0}({1}) {2:ms}", Method, ID, Duration); 378 if (TraceArgs) 379 (*TraceArgs)["Reply"] = *Reply; 380 std::lock_guard<std::mutex> Lock(Server->TranspWriter); 381 Server->Transp.reply(std::move(ID), std::move(Reply)); 382 } else { 383 llvm::Error Err = Reply.takeError(); 384 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method, ID, Duration, Err); 385 if (TraceArgs) 386 (*TraceArgs)["Error"] = llvm::to_string(Err); 387 std::lock_guard<std::mutex> Lock(Server->TranspWriter); 388 Server->Transp.reply(std::move(ID), std::move(Err)); 389 } 390 } 391 }; 392 393 // Method calls may be cancelled by ID, so keep track of their state. 394 // This needs a mutex: handlers may finish on a different thread, and that's 395 // when we clean up entries in the map. 396 mutable std::mutex RequestCancelersMutex; 397 llvm::StringMap<std::pair<Canceler, /*Cookie*/ unsigned>> RequestCancelers; 398 unsigned NextRequestCookie = 0; // To disambiguate reused IDs, see below. 399 void onCancel(const llvm::json::Value &Params) { 400 const llvm::json::Value *ID = nullptr; 401 if (auto *O = Params.getAsObject()) 402 ID = O->get("id"); 403 if (!ID) { 404 elog("Bad cancellation request: {0}", Params); 405 return; 406 } 407 auto StrID = llvm::to_string(*ID); 408 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 409 auto It = RequestCancelers.find(StrID); 410 if (It != RequestCancelers.end()) 411 It->second.first(); // Invoke the canceler. 412 } 413 414 Context handlerContext() const { 415 return Context::current().derive( 416 kCurrentOffsetEncoding, 417 Server.Opts.Encoding.value_or(OffsetEncoding::UTF16)); 418 } 419 420 // We run cancelable requests in a context that does two things: 421 // - allows cancellation using RequestCancelers[ID] 422 // - cleans up the entry in RequestCancelers when it's no longer needed 423 // If a client reuses an ID, the last wins and the first cannot be canceled. 424 Context cancelableRequestContext(const llvm::json::Value &ID) { 425 auto Task = cancelableTask( 426 /*Reason=*/static_cast<int>(ErrorCode::RequestCancelled)); 427 auto StrID = llvm::to_string(ID); // JSON-serialize ID for map key. 428 auto Cookie = NextRequestCookie++; // No lock, only called on main thread. 429 { 430 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 431 RequestCancelers[StrID] = {std::move(Task.second), Cookie}; 432 } 433 // When the request ends, we can clean up the entry we just added. 434 // The cookie lets us check that it hasn't been overwritten due to ID 435 // reuse. 436 return Task.first.derive(llvm::make_scope_exit([this, StrID, Cookie] { 437 std::lock_guard<std::mutex> Lock(RequestCancelersMutex); 438 auto It = RequestCancelers.find(StrID); 439 if (It != RequestCancelers.end() && It->second.second == Cookie) 440 RequestCancelers.erase(It); 441 })); 442 } 443 444 // The maximum number of callbacks held in clangd. 445 // 446 // We bound the maximum size to the pending map to prevent memory leakage 447 // for cases where LSP clients don't reply for the request. 448 // This has to go after RequestCancellers and RequestCancellersMutex since it 449 // can contain a callback that has a cancelable context. 450 static constexpr int MaxReplayCallbacks = 100; 451 mutable std::mutex CallMutex; 452 int NextCallID = 0; /* GUARDED_BY(CallMutex) */ 453 std::deque<std::pair</*RequestID*/ int, 454 /*ReplyHandler*/ Callback<llvm::json::Value>>> 455 ReplyCallbacks; /* GUARDED_BY(CallMutex) */ 456 457 ClangdLSPServer &Server; 458 }; 459 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks; 460 461 // call(), notify(), and reply() wrap the Transport, adding logging and locking. 462 void ClangdLSPServer::callMethod(StringRef Method, llvm::json::Value Params, 463 Callback<llvm::json::Value> CB) { 464 auto ID = MsgHandler->bindReply(std::move(CB)); 465 log("--> {0}({1})", Method, ID); 466 std::lock_guard<std::mutex> Lock(TranspWriter); 467 Transp.call(Method, std::move(Params), ID); 468 } 469 470 void ClangdLSPServer::notify(llvm::StringRef Method, llvm::json::Value Params) { 471 log("--> {0}", Method); 472 maybeCleanupMemory(); 473 std::lock_guard<std::mutex> Lock(TranspWriter); 474 Transp.notify(Method, std::move(Params)); 475 } 476 477 static std::vector<llvm::StringRef> semanticTokenTypes() { 478 std::vector<llvm::StringRef> Types; 479 for (unsigned I = 0; I <= static_cast<unsigned>(HighlightingKind::LastKind); 480 ++I) 481 Types.push_back(toSemanticTokenType(static_cast<HighlightingKind>(I))); 482 return Types; 483 } 484 485 static std::vector<llvm::StringRef> semanticTokenModifiers() { 486 std::vector<llvm::StringRef> Modifiers; 487 for (unsigned I = 0; 488 I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) 489 Modifiers.push_back( 490 toSemanticTokenModifier(static_cast<HighlightingModifier>(I))); 491 return Modifiers; 492 } 493 494 void ClangdLSPServer::onInitialize(const InitializeParams &Params, 495 Callback<llvm::json::Value> Reply) { 496 // Determine character encoding first as it affects constructed ClangdServer. 497 if (Params.capabilities.offsetEncoding && !Opts.Encoding) { 498 Opts.Encoding = OffsetEncoding::UTF16; // fallback 499 for (OffsetEncoding Supported : *Params.capabilities.offsetEncoding) 500 if (Supported != OffsetEncoding::UnsupportedEncoding) { 501 Opts.Encoding = Supported; 502 break; 503 } 504 } 505 506 if (Params.capabilities.TheiaSemanticHighlighting && 507 !Params.capabilities.SemanticTokens) { 508 elog("Client requested legacy semanticHighlights notification, which is " 509 "no longer supported. Migrate to standard semanticTokens request"); 510 } 511 512 if (Params.rootUri && *Params.rootUri) 513 Opts.WorkspaceRoot = std::string(Params.rootUri->file()); 514 else if (Params.rootPath && !Params.rootPath->empty()) 515 Opts.WorkspaceRoot = *Params.rootPath; 516 if (Server) 517 return Reply(llvm::make_error<LSPError>("server already initialized", 518 ErrorCode::InvalidRequest)); 519 520 Opts.CodeComplete.EnableSnippets = Params.capabilities.CompletionSnippets; 521 Opts.CodeComplete.IncludeFixIts = Params.capabilities.CompletionFixes; 522 if (!Opts.CodeComplete.BundleOverloads) 523 Opts.CodeComplete.BundleOverloads = Params.capabilities.HasSignatureHelp; 524 Opts.CodeComplete.DocumentationFormat = 525 Params.capabilities.CompletionDocumentationFormat; 526 Opts.SignatureHelpDocumentationFormat = 527 Params.capabilities.SignatureHelpDocumentationFormat; 528 DiagOpts.EmbedFixesInDiagnostics = Params.capabilities.DiagnosticFixes; 529 DiagOpts.SendDiagnosticCategory = Params.capabilities.DiagnosticCategory; 530 DiagOpts.EmitRelatedLocations = 531 Params.capabilities.DiagnosticRelatedInformation; 532 if (Params.capabilities.WorkspaceSymbolKinds) 533 SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds; 534 if (Params.capabilities.CompletionItemKinds) 535 SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds; 536 SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail; 537 SupportsCodeAction = Params.capabilities.CodeActionStructure; 538 SupportsHierarchicalDocumentSymbol = 539 Params.capabilities.HierarchicalDocumentSymbol; 540 SupportsReferenceContainer = Params.capabilities.ReferenceContainer; 541 SupportFileStatus = Params.initializationOptions.FileStatus; 542 SupportsDocumentChanges = Params.capabilities.DocumentChanges; 543 SupportsChangeAnnotation = Params.capabilities.ChangeAnnotation; 544 HoverContentFormat = Params.capabilities.HoverContentFormat; 545 Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly; 546 SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp; 547 if (Params.capabilities.WorkDoneProgress) 548 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; 549 BackgroundIndexSkipCreate = Params.capabilities.ImplicitProgressCreation; 550 Opts.ImplicitCancellation = !Params.capabilities.CancelsStaleRequests; 551 Opts.PublishInactiveRegions = Params.capabilities.InactiveRegions; 552 553 if (Opts.UseDirBasedCDB) { 554 DirectoryBasedGlobalCompilationDatabase::Options CDBOpts(TFS); 555 if (const auto &Dir = Params.initializationOptions.compilationDatabasePath) 556 CDBOpts.CompileCommandsDir = Dir; 557 CDBOpts.ContextProvider = Opts.ContextProvider; 558 BaseCDB = 559 std::make_unique<DirectoryBasedGlobalCompilationDatabase>(CDBOpts); 560 } 561 auto Mangler = CommandMangler::detect(); 562 Mangler.SystemIncludeExtractor = 563 getSystemIncludeExtractor(llvm::ArrayRef(Opts.QueryDriverGlobs)); 564 if (Opts.ResourceDir) 565 Mangler.ResourceDir = *Opts.ResourceDir; 566 CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, 567 std::move(Mangler)); 568 569 if (Opts.EnableExperimentalModulesSupport) { 570 ModulesManager.emplace(*CDB); 571 Opts.ModulesManager = &*ModulesManager; 572 } 573 574 { 575 // Switch caller's context with LSPServer's background context. Since we 576 // rather want to propagate information from LSPServer's context into the 577 // Server, CDB, etc. 578 WithContext MainContext(BackgroundContext.clone()); 579 std::optional<WithContextValue> WithOffsetEncoding; 580 if (Opts.Encoding) 581 WithOffsetEncoding.emplace(kCurrentOffsetEncoding, *Opts.Encoding); 582 Server.emplace(*CDB, TFS, Opts, 583 static_cast<ClangdServer::Callbacks *>(this)); 584 } 585 586 llvm::json::Object ServerCaps{ 587 {"textDocumentSync", 588 llvm::json::Object{ 589 {"openClose", true}, 590 {"change", (int)TextDocumentSyncKind::Incremental}, 591 {"save", true}, 592 }}, 593 {"documentFormattingProvider", true}, 594 {"documentRangeFormattingProvider", true}, 595 {"documentOnTypeFormattingProvider", 596 llvm::json::Object{ 597 {"firstTriggerCharacter", "\n"}, 598 {"moreTriggerCharacter", {}}, 599 }}, 600 {"completionProvider", 601 llvm::json::Object{ 602 // We don't set `(` etc as allCommitCharacters as they interact 603 // poorly with snippet results. 604 // See https://github.com/clangd/vscode-clangd/issues/357 605 // Hopefully we can use them one day without this side-effect: 606 // https://github.com/microsoft/vscode/issues/42544 607 {"resolveProvider", false}, 608 // We do extra checks, e.g. that > is part of ->. 609 {"triggerCharacters", {".", "<", ">", ":", "\"", "/", "*"}}, 610 }}, 611 {"semanticTokensProvider", 612 llvm::json::Object{ 613 {"full", llvm::json::Object{{"delta", true}}}, 614 {"range", false}, 615 {"legend", 616 llvm::json::Object{{"tokenTypes", semanticTokenTypes()}, 617 {"tokenModifiers", semanticTokenModifiers()}}}, 618 }}, 619 {"signatureHelpProvider", 620 llvm::json::Object{ 621 {"triggerCharacters", {"(", ")", "{", "}", "<", ">", ","}}, 622 }}, 623 {"declarationProvider", true}, 624 {"definitionProvider", true}, 625 {"implementationProvider", true}, 626 {"typeDefinitionProvider", true}, 627 {"documentHighlightProvider", true}, 628 {"documentLinkProvider", 629 llvm::json::Object{ 630 {"resolveProvider", false}, 631 }}, 632 {"hoverProvider", true}, 633 {"selectionRangeProvider", true}, 634 {"documentSymbolProvider", true}, 635 {"workspaceSymbolProvider", true}, 636 {"referencesProvider", true}, 637 {"astProvider", true}, // clangd extension 638 {"typeHierarchyProvider", true}, 639 // Unfortunately our extension made use of the same capability name as the 640 // standard. Advertise this capability to tell clients that implement our 641 // extension we really have support for the standardized one as well. 642 {"standardTypeHierarchyProvider", true}, // clangd extension 643 {"memoryUsageProvider", true}, // clangd extension 644 {"compilationDatabase", // clangd extension 645 llvm::json::Object{{"automaticReload", true}}}, 646 {"inactiveRegionsProvider", true}, // clangd extension 647 {"callHierarchyProvider", true}, 648 {"clangdInlayHintsProvider", true}, 649 {"inlayHintProvider", true}, 650 {"foldingRangeProvider", true}, 651 }; 652 653 { 654 LSPBinder Binder(Handlers, *this); 655 bindMethods(Binder, Params.capabilities); 656 if (Opts.FeatureModules) 657 for (auto &Mod : *Opts.FeatureModules) 658 Mod.initializeLSP(Binder, Params.rawCapabilities, ServerCaps); 659 } 660 661 // Per LSP, renameProvider can be either boolean or RenameOptions. 662 // RenameOptions will be specified if the client states it supports prepare. 663 ServerCaps["renameProvider"] = 664 Params.capabilities.RenamePrepareSupport 665 ? llvm::json::Object{{"prepareProvider", true}} 666 : llvm::json::Value(true); 667 668 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions. 669 // CodeActionOptions is only valid if the client supports action literal 670 // via textDocument.codeAction.codeActionLiteralSupport. 671 ServerCaps["codeActionProvider"] = 672 Params.capabilities.CodeActionStructure 673 ? llvm::json::Object{{"codeActionKinds", 674 {CodeAction::QUICKFIX_KIND, 675 CodeAction::REFACTOR_KIND, 676 CodeAction::INFO_KIND}}} 677 : llvm::json::Value(true); 678 679 std::vector<llvm::StringRef> Commands; 680 for (llvm::StringRef Command : Handlers.CommandHandlers.keys()) 681 Commands.push_back(Command); 682 llvm::sort(Commands); 683 ServerCaps["executeCommandProvider"] = 684 llvm::json::Object{{"commands", Commands}}; 685 686 llvm::json::Object Result{ 687 {{"serverInfo", 688 llvm::json::Object{ 689 {"name", "clangd"}, 690 {"version", llvm::formatv("{0} {1} {2}", versionString(), 691 featureString(), platformString())}}}, 692 {"capabilities", std::move(ServerCaps)}}}; 693 if (Opts.Encoding) 694 Result["offsetEncoding"] = *Opts.Encoding; 695 Reply(std::move(Result)); 696 697 // Apply settings after we're fully initialized. 698 // This can start background indexing and in turn trigger LSP notifications. 699 applyConfiguration(Params.initializationOptions.ConfigSettings); 700 } 701 702 void ClangdLSPServer::onInitialized(const InitializedParams &Params) {} 703 704 void ClangdLSPServer::onShutdown(const NoParams &, 705 Callback<std::nullptr_t> Reply) { 706 // Do essentially nothing, just say we're ready to exit. 707 ShutdownRequestReceived = true; 708 Reply(nullptr); 709 } 710 711 // sync is a clangd extension: it blocks until all background work completes. 712 // It blocks the calling thread, so no messages are processed until it returns! 713 void ClangdLSPServer::onSync(const NoParams &, Callback<std::nullptr_t> Reply) { 714 if (Server->blockUntilIdleForTest(/*TimeoutSeconds=*/60)) 715 Reply(nullptr); 716 else 717 Reply(error("Not idle after a minute")); 718 } 719 720 void ClangdLSPServer::onDocumentDidOpen( 721 const DidOpenTextDocumentParams &Params) { 722 PathRef File = Params.textDocument.uri.file(); 723 724 const std::string &Contents = Params.textDocument.text; 725 726 Server->addDocument(File, Contents, 727 encodeVersion(Params.textDocument.version), 728 WantDiagnostics::Yes); 729 } 730 731 void ClangdLSPServer::onDocumentDidChange( 732 const DidChangeTextDocumentParams &Params) { 733 auto WantDiags = WantDiagnostics::Auto; 734 if (Params.wantDiagnostics) 735 WantDiags = 736 *Params.wantDiagnostics ? WantDiagnostics::Yes : WantDiagnostics::No; 737 738 PathRef File = Params.textDocument.uri.file(); 739 auto Code = Server->getDraft(File); 740 if (!Code) { 741 log("Trying to incrementally change non-added document: {0}", File); 742 return; 743 } 744 std::string NewCode(*Code); 745 for (const auto &Change : Params.contentChanges) { 746 if (auto Err = applyChange(NewCode, Change)) { 747 // If this fails, we are most likely going to be not in sync anymore with 748 // the client. It is better to remove the draft and let further 749 // operations fail rather than giving wrong results. 750 Server->removeDocument(File); 751 elog("Failed to update {0}: {1}", File, std::move(Err)); 752 return; 753 } 754 } 755 Server->addDocument(File, NewCode, encodeVersion(Params.textDocument.version), 756 WantDiags, Params.forceRebuild); 757 } 758 759 void ClangdLSPServer::onDocumentDidSave( 760 const DidSaveTextDocumentParams &Params) { 761 Server->reparseOpenFilesIfNeeded([](llvm::StringRef) { return true; }); 762 } 763 764 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { 765 // We could also reparse all open files here. However: 766 // - this could be frequent, and revalidating all the preambles isn't free 767 // - this is useful e.g. when switching git branches, but we're likely to see 768 // fresh headers but still have the old-branch main-file content 769 Server->onFileEvent(Params); 770 // FIXME: observe config files, immediately expire time-based caches, reparse: 771 // - compile_commands.json and compile_flags.txt 772 // - .clang_format and .clang-tidy 773 // - .clangd and clangd/config.yaml 774 } 775 776 void ClangdLSPServer::onCommand(const ExecuteCommandParams &Params, 777 Callback<llvm::json::Value> Reply) { 778 auto It = Handlers.CommandHandlers.find(Params.command); 779 if (It == Handlers.CommandHandlers.end()) { 780 return Reply(llvm::make_error<LSPError>( 781 llvm::formatv("Unsupported command \"{0}\".", Params.command).str(), 782 ErrorCode::InvalidParams)); 783 } 784 It->second(Params.argument, std::move(Reply)); 785 } 786 787 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit &WE, 788 Callback<llvm::json::Value> Reply) { 789 // The flow for "apply-fix" : 790 // 1. We publish a diagnostic, including fixits 791 // 2. The user clicks on the diagnostic, the editor asks us for code actions 792 // 3. We send code actions, with the fixit embedded as context 793 // 4. The user selects the fixit, the editor asks us to apply it 794 // 5. We unwrap the changes and send them back to the editor 795 // 6. The editor applies the changes (applyEdit), and sends us a reply 796 // 7. We unwrap the reply and send a reply to the editor. 797 applyEdit(WE, "Fix applied.", std::move(Reply)); 798 } 799 800 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs &Args, 801 Callback<llvm::json::Value> Reply) { 802 auto Action = [this, Reply = std::move(Reply)]( 803 llvm::Expected<Tweak::Effect> R) mutable { 804 if (!R) 805 return Reply(R.takeError()); 806 807 assert(R->ShowMessage || (!R->ApplyEdits.empty() && "tweak has no effect")); 808 809 if (R->ShowMessage) { 810 ShowMessageParams Msg; 811 Msg.message = *R->ShowMessage; 812 Msg.type = MessageType::Info; 813 ShowMessage(Msg); 814 } 815 // When no edit is specified, make sure we Reply(). 816 if (R->ApplyEdits.empty()) 817 return Reply("Tweak applied."); 818 819 if (auto Err = validateEdits(*Server, R->ApplyEdits)) 820 return Reply(std::move(Err)); 821 822 WorkspaceEdit WE; 823 // FIXME: use documentChanges when SupportDocumentChanges is true. 824 WE.changes.emplace(); 825 for (const auto &It : R->ApplyEdits) { 826 (*WE.changes)[URI::createFile(It.first()).toString()] = 827 It.second.asTextEdits(); 828 } 829 // ApplyEdit will take care of calling Reply(). 830 return applyEdit(std::move(WE), "Tweak applied.", std::move(Reply)); 831 }; 832 Server->applyTweak(Args.file.file(), Args.selection, Args.tweakID, 833 std::move(Action)); 834 } 835 836 void ClangdLSPServer::onCommandApplyRename(const RenameParams &R, 837 Callback<llvm::json::Value> Reply) { 838 onRename(R, [this, Reply = std::move(Reply)]( 839 llvm::Expected<WorkspaceEdit> Edit) mutable { 840 if (!Edit) 841 Reply(Edit.takeError()); 842 applyEdit(std::move(*Edit), "Rename applied.", std::move(Reply)); 843 }); 844 } 845 846 void ClangdLSPServer::applyEdit(WorkspaceEdit WE, llvm::json::Value Success, 847 Callback<llvm::json::Value> Reply) { 848 ApplyWorkspaceEditParams Edit; 849 Edit.edit = std::move(WE); 850 ApplyWorkspaceEdit( 851 Edit, [Reply = std::move(Reply), SuccessMessage = std::move(Success)]( 852 llvm::Expected<ApplyWorkspaceEditResponse> Response) mutable { 853 if (!Response) 854 return Reply(Response.takeError()); 855 if (!Response->applied) { 856 std::string Reason = Response->failureReason 857 ? *Response->failureReason 858 : "unknown reason"; 859 return Reply(error("edits were not applied: {0}", Reason)); 860 } 861 return Reply(SuccessMessage); 862 }); 863 } 864 865 void ClangdLSPServer::onWorkspaceSymbol( 866 const WorkspaceSymbolParams &Params, 867 Callback<std::vector<SymbolInformation>> Reply) { 868 Server->workspaceSymbols( 869 Params.query, Params.limit.value_or(Opts.CodeComplete.Limit), 870 [Reply = std::move(Reply), 871 this](llvm::Expected<std::vector<SymbolInformation>> Items) mutable { 872 if (!Items) 873 return Reply(Items.takeError()); 874 for (auto &Sym : *Items) 875 Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds); 876 877 Reply(std::move(*Items)); 878 }); 879 } 880 881 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params, 882 Callback<PrepareRenameResult> Reply) { 883 Server->prepareRename( 884 Params.textDocument.uri.file(), Params.position, /*NewName*/ std::nullopt, 885 Opts.Rename, 886 [Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable { 887 if (!Result) 888 return Reply(Result.takeError()); 889 PrepareRenameResult PrepareResult; 890 PrepareResult.range = Result->Target; 891 PrepareResult.placeholder = Result->Placeholder; 892 return Reply(std::move(PrepareResult)); 893 }); 894 } 895 896 void ClangdLSPServer::onRename(const RenameParams &Params, 897 Callback<WorkspaceEdit> Reply) { 898 Path File = std::string(Params.textDocument.uri.file()); 899 if (!Server->getDraft(File)) 900 return Reply(llvm::make_error<LSPError>( 901 "onRename called for non-added file", ErrorCode::InvalidParams)); 902 Server->rename(File, Params.position, Params.newName, Opts.Rename, 903 [File, Params, Reply = std::move(Reply), 904 this](llvm::Expected<RenameResult> R) mutable { 905 if (!R) 906 return Reply(R.takeError()); 907 if (auto Err = validateEdits(*Server, R->GlobalChanges)) 908 return Reply(std::move(Err)); 909 WorkspaceEdit Result; 910 // FIXME: use documentChanges if SupportDocumentChanges is 911 // true. 912 Result.changes.emplace(); 913 for (const auto &Rep : R->GlobalChanges) { 914 (*Result 915 .changes)[URI::createFile(Rep.first()).toString()] = 916 Rep.second.asTextEdits(); 917 } 918 Reply(Result); 919 }); 920 } 921 922 void ClangdLSPServer::onDocumentDidClose( 923 const DidCloseTextDocumentParams &Params) { 924 PathRef File = Params.textDocument.uri.file(); 925 Server->removeDocument(File); 926 927 { 928 std::lock_guard<std::mutex> Lock(DiagRefMutex); 929 DiagRefMap.erase(File); 930 } 931 { 932 std::lock_guard<std::mutex> HLock(SemanticTokensMutex); 933 LastSemanticTokens.erase(File); 934 } 935 // clangd will not send updates for this file anymore, so we empty out the 936 // list of diagnostics shown on the client (e.g. in the "Problems" pane of 937 // VSCode). Note that this cannot race with actual diagnostics responses 938 // because removeDocument() guarantees no diagnostic callbacks will be 939 // executed after it returns. 940 PublishDiagnosticsParams Notification; 941 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 942 PublishDiagnostics(Notification); 943 } 944 945 void ClangdLSPServer::onDocumentOnTypeFormatting( 946 const DocumentOnTypeFormattingParams &Params, 947 Callback<std::vector<TextEdit>> Reply) { 948 auto File = Params.textDocument.uri.file(); 949 Server->formatOnType(File, Params.position, Params.ch, std::move(Reply)); 950 } 951 952 void ClangdLSPServer::onDocumentRangeFormatting( 953 const DocumentRangeFormattingParams &Params, 954 Callback<std::vector<TextEdit>> Reply) { 955 auto File = Params.textDocument.uri.file(); 956 auto Code = Server->getDraft(File); 957 Server->formatFile(File, Params.range, 958 [Code = std::move(Code), Reply = std::move(Reply)]( 959 llvm::Expected<tooling::Replacements> Result) mutable { 960 if (Result) 961 Reply(replacementsToEdits(*Code, Result.get())); 962 else 963 Reply(Result.takeError()); 964 }); 965 } 966 967 void ClangdLSPServer::onDocumentFormatting( 968 const DocumentFormattingParams &Params, 969 Callback<std::vector<TextEdit>> Reply) { 970 auto File = Params.textDocument.uri.file(); 971 auto Code = Server->getDraft(File); 972 Server->formatFile(File, 973 /*Rng=*/std::nullopt, 974 [Code = std::move(Code), Reply = std::move(Reply)]( 975 llvm::Expected<tooling::Replacements> Result) mutable { 976 if (Result) 977 Reply(replacementsToEdits(*Code, Result.get())); 978 else 979 Reply(Result.takeError()); 980 }); 981 } 982 983 /// The functions constructs a flattened view of the DocumentSymbol hierarchy. 984 /// Used by the clients that do not support the hierarchical view. 985 static std::vector<SymbolInformation> 986 flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols, 987 const URIForFile &FileURI) { 988 std::vector<SymbolInformation> Results; 989 std::function<void(const DocumentSymbol &, llvm::StringRef)> Process = 990 [&](const DocumentSymbol &S, std::optional<llvm::StringRef> ParentName) { 991 SymbolInformation SI; 992 SI.containerName = std::string(ParentName ? "" : *ParentName); 993 SI.name = S.name; 994 SI.kind = S.kind; 995 SI.location.range = S.range; 996 SI.location.uri = FileURI; 997 998 Results.push_back(std::move(SI)); 999 std::string FullName = 1000 !ParentName ? S.name : (ParentName->str() + "::" + S.name); 1001 for (auto &C : S.children) 1002 Process(C, /*ParentName=*/FullName); 1003 }; 1004 for (auto &S : Symbols) 1005 Process(S, /*ParentName=*/""); 1006 return Results; 1007 } 1008 1009 void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params, 1010 Callback<llvm::json::Value> Reply) { 1011 URIForFile FileURI = Params.textDocument.uri; 1012 Server->documentSymbols( 1013 Params.textDocument.uri.file(), 1014 [this, FileURI, Reply = std::move(Reply)]( 1015 llvm::Expected<std::vector<DocumentSymbol>> Items) mutable { 1016 if (!Items) 1017 return Reply(Items.takeError()); 1018 adjustSymbolKinds(*Items, SupportedSymbolKinds); 1019 if (SupportsHierarchicalDocumentSymbol) 1020 return Reply(std::move(*Items)); 1021 return Reply(flattenSymbolHierarchy(*Items, FileURI)); 1022 }); 1023 } 1024 1025 void ClangdLSPServer::onFoldingRange( 1026 const FoldingRangeParams &Params, 1027 Callback<std::vector<FoldingRange>> Reply) { 1028 Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply)); 1029 } 1030 1031 static std::optional<Command> asCommand(const CodeAction &Action) { 1032 Command Cmd; 1033 if (Action.command && Action.edit) 1034 return std::nullopt; // Not representable. (We never emit these anyway). 1035 if (Action.command) { 1036 Cmd = *Action.command; 1037 } else if (Action.edit) { 1038 Cmd.command = std::string(ApplyFixCommand); 1039 Cmd.argument = *Action.edit; 1040 } else { 1041 return std::nullopt; 1042 } 1043 Cmd.title = Action.title; 1044 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) 1045 Cmd.title = "Apply fix: " + Cmd.title; 1046 return Cmd; 1047 } 1048 1049 void ClangdLSPServer::onCodeAction(const CodeActionParams &Params, 1050 Callback<llvm::json::Value> Reply) { 1051 URIForFile File = Params.textDocument.uri; 1052 std::map<ClangdServer::DiagRef, clangd::Diagnostic> ToLSPDiags; 1053 ClangdServer::CodeActionInputs Inputs; 1054 1055 for (const auto& LSPDiag : Params.context.diagnostics) { 1056 if (auto DiagRef = getDiagRef(File.file(), LSPDiag)) { 1057 ToLSPDiags[*DiagRef] = LSPDiag; 1058 Inputs.Diagnostics.push_back(*DiagRef); 1059 } 1060 } 1061 Inputs.File = File.file(); 1062 Inputs.Selection = Params.range; 1063 Inputs.RequestedActionKinds = Params.context.only; 1064 Inputs.TweakFilter = [this](const Tweak &T) { 1065 return Opts.TweakFilter(T); 1066 }; 1067 auto CB = [this, 1068 Reply = std::move(Reply), 1069 ToLSPDiags = std::move(ToLSPDiags), File, 1070 Selection = Params.range]( 1071 llvm::Expected<ClangdServer::CodeActionResult> Fixits) mutable { 1072 if (!Fixits) 1073 return Reply(Fixits.takeError()); 1074 std::vector<CodeAction> CAs; 1075 auto Version = decodeVersion(Fixits->Version); 1076 for (const auto &QF : Fixits->QuickFixes) { 1077 CAs.push_back(toCodeAction(QF.F, File, Version, SupportsDocumentChanges, 1078 SupportsChangeAnnotation)); 1079 if (auto It = ToLSPDiags.find(QF.Diag); 1080 It != ToLSPDiags.end()) { 1081 CAs.back().diagnostics = {It->second}; 1082 } 1083 } 1084 1085 for (const auto &R : Fixits->Renames) 1086 CAs.push_back(toCodeAction(R, File)); 1087 1088 for (const auto &TR : Fixits->TweakRefs) 1089 CAs.push_back(toCodeAction(TR, File, Selection)); 1090 1091 // If there's exactly one quick-fix, call it "preferred". 1092 // We never consider refactorings etc as preferred. 1093 CodeAction *OnlyFix = nullptr; 1094 for (auto &Action : CAs) { 1095 if (Action.kind && *Action.kind == CodeAction::QUICKFIX_KIND) { 1096 if (OnlyFix) { 1097 OnlyFix = nullptr; 1098 break; 1099 } 1100 OnlyFix = &Action; 1101 } 1102 } 1103 if (OnlyFix) { 1104 OnlyFix->isPreferred = true; 1105 if (ToLSPDiags.size() == 1 && 1106 ToLSPDiags.begin()->second.range == Selection) 1107 OnlyFix->diagnostics = {ToLSPDiags.begin()->second}; 1108 } 1109 1110 if (SupportsCodeAction) 1111 return Reply(llvm::json::Array(CAs)); 1112 std::vector<Command> Commands; 1113 for (const auto &Action : CAs) { 1114 if (auto Command = asCommand(Action)) 1115 Commands.push_back(std::move(*Command)); 1116 } 1117 return Reply(llvm::json::Array(Commands)); 1118 }; 1119 Server->codeAction(Inputs, std::move(CB)); 1120 } 1121 1122 void ClangdLSPServer::onCompletion(const CompletionParams &Params, 1123 Callback<CompletionList> Reply) { 1124 if (!shouldRunCompletion(Params)) { 1125 // Clients sometimes auto-trigger completions in undesired places (e.g. 1126 // 'a >^ '), we return empty results in those cases. 1127 vlog("ignored auto-triggered completion, preceding char did not match"); 1128 return Reply(CompletionList()); 1129 } 1130 auto Opts = this->Opts.CodeComplete; 1131 if (Params.limit && *Params.limit >= 0) 1132 Opts.Limit = *Params.limit; 1133 Server->codeComplete(Params.textDocument.uri.file(), Params.position, Opts, 1134 [Reply = std::move(Reply), Opts, 1135 this](llvm::Expected<CodeCompleteResult> List) mutable { 1136 if (!List) 1137 return Reply(List.takeError()); 1138 CompletionList LSPList; 1139 LSPList.isIncomplete = List->HasMore; 1140 for (const auto &R : List->Completions) { 1141 CompletionItem C = R.render(Opts); 1142 C.kind = adjustKindToCapability( 1143 C.kind, SupportedCompletionItemKinds); 1144 if (!SupportsCompletionLabelDetails) 1145 removeCompletionLabelDetails(C); 1146 LSPList.items.push_back(std::move(C)); 1147 } 1148 return Reply(std::move(LSPList)); 1149 }); 1150 } 1151 1152 void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams &Params, 1153 Callback<SignatureHelp> Reply) { 1154 Server->signatureHelp(Params.textDocument.uri.file(), Params.position, 1155 Opts.SignatureHelpDocumentationFormat, 1156 [Reply = std::move(Reply), this]( 1157 llvm::Expected<SignatureHelp> Signature) mutable { 1158 if (!Signature) 1159 return Reply(Signature.takeError()); 1160 if (SupportsOffsetsInSignatureHelp) 1161 return Reply(std::move(*Signature)); 1162 // Strip out the offsets from signature help for 1163 // clients that only support string labels. 1164 for (auto &SigInfo : Signature->signatures) { 1165 for (auto &Param : SigInfo.parameters) 1166 Param.labelOffsets.reset(); 1167 } 1168 return Reply(std::move(*Signature)); 1169 }); 1170 } 1171 1172 // Go to definition has a toggle function: if def and decl are distinct, then 1173 // the first press gives you the def, the second gives you the matching def. 1174 // getToggle() returns the counterpart location that under the cursor. 1175 // 1176 // We return the toggled location alone (ignoring other symbols) to encourage 1177 // editors to "bounce" quickly between locations, without showing a menu. 1178 static Location *getToggle(const TextDocumentPositionParams &Point, 1179 LocatedSymbol &Sym) { 1180 // Toggle only makes sense with two distinct locations. 1181 if (!Sym.Definition || *Sym.Definition == Sym.PreferredDeclaration) 1182 return nullptr; 1183 if (Sym.Definition->uri.file() == Point.textDocument.uri.file() && 1184 Sym.Definition->range.contains(Point.position)) 1185 return &Sym.PreferredDeclaration; 1186 if (Sym.PreferredDeclaration.uri.file() == Point.textDocument.uri.file() && 1187 Sym.PreferredDeclaration.range.contains(Point.position)) 1188 return &*Sym.Definition; 1189 return nullptr; 1190 } 1191 1192 void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams &Params, 1193 Callback<std::vector<Location>> Reply) { 1194 Server->locateSymbolAt( 1195 Params.textDocument.uri.file(), Params.position, 1196 [Params, Reply = std::move(Reply)]( 1197 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1198 if (!Symbols) 1199 return Reply(Symbols.takeError()); 1200 std::vector<Location> Defs; 1201 for (auto &S : *Symbols) { 1202 if (Location *Toggle = getToggle(Params, S)) 1203 return Reply(std::vector<Location>{std::move(*Toggle)}); 1204 Defs.push_back(S.Definition.value_or(S.PreferredDeclaration)); 1205 } 1206 Reply(std::move(Defs)); 1207 }); 1208 } 1209 1210 void ClangdLSPServer::onGoToDeclaration( 1211 const TextDocumentPositionParams &Params, 1212 Callback<std::vector<Location>> Reply) { 1213 Server->locateSymbolAt( 1214 Params.textDocument.uri.file(), Params.position, 1215 [Params, Reply = std::move(Reply)]( 1216 llvm::Expected<std::vector<LocatedSymbol>> Symbols) mutable { 1217 if (!Symbols) 1218 return Reply(Symbols.takeError()); 1219 std::vector<Location> Decls; 1220 for (auto &S : *Symbols) { 1221 if (Location *Toggle = getToggle(Params, S)) 1222 return Reply(std::vector<Location>{std::move(*Toggle)}); 1223 Decls.push_back(std::move(S.PreferredDeclaration)); 1224 } 1225 Reply(std::move(Decls)); 1226 }); 1227 } 1228 1229 void ClangdLSPServer::onSwitchSourceHeader( 1230 const TextDocumentIdentifier &Params, 1231 Callback<std::optional<URIForFile>> Reply) { 1232 Server->switchSourceHeader( 1233 Params.uri.file(), 1234 [Reply = std::move(Reply), 1235 Params](llvm::Expected<std::optional<clangd::Path>> Path) mutable { 1236 if (!Path) 1237 return Reply(Path.takeError()); 1238 if (*Path) 1239 return Reply(URIForFile::canonicalize(**Path, Params.uri.file())); 1240 return Reply(std::nullopt); 1241 }); 1242 } 1243 1244 void ClangdLSPServer::onDocumentHighlight( 1245 const TextDocumentPositionParams &Params, 1246 Callback<std::vector<DocumentHighlight>> Reply) { 1247 Server->findDocumentHighlights(Params.textDocument.uri.file(), 1248 Params.position, std::move(Reply)); 1249 } 1250 1251 void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params, 1252 Callback<std::optional<Hover>> Reply) { 1253 Server->findHover(Params.textDocument.uri.file(), Params.position, 1254 [Reply = std::move(Reply), 1255 this](llvm::Expected<std::optional<HoverInfo>> H) mutable { 1256 if (!H) 1257 return Reply(H.takeError()); 1258 if (!*H) 1259 return Reply(std::nullopt); 1260 1261 Hover R; 1262 R.contents.kind = HoverContentFormat; 1263 R.range = (*H)->SymRange; 1264 switch (HoverContentFormat) { 1265 case MarkupKind::PlainText: 1266 R.contents.value = (*H)->present().asPlainText(); 1267 return Reply(std::move(R)); 1268 case MarkupKind::Markdown: 1269 R.contents.value = (*H)->present().asMarkdown(); 1270 return Reply(std::move(R)); 1271 }; 1272 llvm_unreachable("unhandled MarkupKind"); 1273 }); 1274 } 1275 1276 // Our extension has a different representation on the wire than the standard. 1277 // https://clangd.llvm.org/extensions#type-hierarchy 1278 llvm::json::Value serializeTHIForExtension(TypeHierarchyItem THI) { 1279 llvm::json::Object Result{{ 1280 {"name", std::move(THI.name)}, 1281 {"kind", static_cast<int>(THI.kind)}, 1282 {"uri", std::move(THI.uri)}, 1283 {"range", THI.range}, 1284 {"selectionRange", THI.selectionRange}, 1285 {"data", std::move(THI.data)}, 1286 }}; 1287 if (THI.deprecated) 1288 Result["deprecated"] = THI.deprecated; 1289 if (THI.detail) 1290 Result["detail"] = std::move(*THI.detail); 1291 1292 if (THI.parents) { 1293 llvm::json::Array Parents; 1294 for (auto &Parent : *THI.parents) 1295 Parents.emplace_back(serializeTHIForExtension(std::move(Parent))); 1296 Result["parents"] = std::move(Parents); 1297 } 1298 1299 if (THI.children) { 1300 llvm::json::Array Children; 1301 for (auto &child : *THI.children) 1302 Children.emplace_back(serializeTHIForExtension(std::move(child))); 1303 Result["children"] = std::move(Children); 1304 } 1305 return Result; 1306 } 1307 1308 void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams &Params, 1309 Callback<llvm::json::Value> Reply) { 1310 auto Serialize = 1311 [Reply = std::move(Reply)]( 1312 llvm::Expected<std::vector<TypeHierarchyItem>> Resp) mutable { 1313 if (!Resp) { 1314 Reply(Resp.takeError()); 1315 return; 1316 } 1317 if (Resp->empty()) { 1318 Reply(nullptr); 1319 return; 1320 } 1321 Reply(serializeTHIForExtension(std::move(Resp->front()))); 1322 }; 1323 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position, 1324 Params.resolve, Params.direction, std::move(Serialize)); 1325 } 1326 1327 void ClangdLSPServer::onResolveTypeHierarchy( 1328 const ResolveTypeHierarchyItemParams &Params, 1329 Callback<llvm::json::Value> Reply) { 1330 auto Serialize = 1331 [Reply = std::move(Reply)]( 1332 llvm::Expected<std::optional<TypeHierarchyItem>> Resp) mutable { 1333 if (!Resp) { 1334 Reply(Resp.takeError()); 1335 return; 1336 } 1337 if (!*Resp) { 1338 Reply(std::move(*Resp)); 1339 return; 1340 } 1341 Reply(serializeTHIForExtension(std::move(**Resp))); 1342 }; 1343 Server->resolveTypeHierarchy(Params.item, Params.resolve, Params.direction, 1344 std::move(Serialize)); 1345 } 1346 1347 void ClangdLSPServer::onPrepareTypeHierarchy( 1348 const TypeHierarchyPrepareParams &Params, 1349 Callback<std::vector<TypeHierarchyItem>> Reply) { 1350 Server->typeHierarchy(Params.textDocument.uri.file(), Params.position, 1351 Params.resolve, Params.direction, std::move(Reply)); 1352 } 1353 1354 void ClangdLSPServer::onSuperTypes( 1355 const ResolveTypeHierarchyItemParams &Params, 1356 Callback<std::optional<std::vector<TypeHierarchyItem>>> Reply) { 1357 Server->superTypes(Params.item, std::move(Reply)); 1358 } 1359 1360 void ClangdLSPServer::onSubTypes( 1361 const ResolveTypeHierarchyItemParams &Params, 1362 Callback<std::vector<TypeHierarchyItem>> Reply) { 1363 Server->subTypes(Params.item, std::move(Reply)); 1364 } 1365 1366 void ClangdLSPServer::onPrepareCallHierarchy( 1367 const CallHierarchyPrepareParams &Params, 1368 Callback<std::vector<CallHierarchyItem>> Reply) { 1369 Server->prepareCallHierarchy(Params.textDocument.uri.file(), Params.position, 1370 std::move(Reply)); 1371 } 1372 1373 void ClangdLSPServer::onCallHierarchyIncomingCalls( 1374 const CallHierarchyIncomingCallsParams &Params, 1375 Callback<std::vector<CallHierarchyIncomingCall>> Reply) { 1376 Server->incomingCalls(Params.item, std::move(Reply)); 1377 } 1378 1379 void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams &Params, 1380 Callback<llvm::json::Value> Reply) { 1381 // Our extension has a different representation on the wire than the standard. 1382 // We have a "range" property and "kind" is represented as a string, not as an 1383 // enum value. 1384 // https://clangd.llvm.org/extensions#inlay-hints 1385 auto Serialize = [Reply = std::move(Reply)]( 1386 llvm::Expected<std::vector<InlayHint>> Hints) mutable { 1387 if (!Hints) { 1388 Reply(Hints.takeError()); 1389 return; 1390 } 1391 llvm::json::Array Result; 1392 Result.reserve(Hints->size()); 1393 for (auto &Hint : *Hints) { 1394 Result.emplace_back(llvm::json::Object{ 1395 {"kind", llvm::to_string(Hint.kind)}, 1396 {"range", Hint.range}, 1397 {"position", Hint.position}, 1398 // Extension doesn't have paddingLeft/Right so adjust the label 1399 // accordingly. 1400 {"label", 1401 ((Hint.paddingLeft ? " " : "") + llvm::StringRef(Hint.joinLabels()) + 1402 (Hint.paddingRight ? " " : "")) 1403 .str()}, 1404 }); 1405 } 1406 Reply(std::move(Result)); 1407 }; 1408 Server->inlayHints(Params.textDocument.uri.file(), Params.range, 1409 std::move(Serialize)); 1410 } 1411 1412 void ClangdLSPServer::onInlayHint(const InlayHintsParams &Params, 1413 Callback<std::vector<InlayHint>> Reply) { 1414 Server->inlayHints(Params.textDocument.uri.file(), Params.range, 1415 std::move(Reply)); 1416 } 1417 1418 void ClangdLSPServer::onCallHierarchyOutgoingCalls( 1419 const CallHierarchyOutgoingCallsParams &Params, 1420 Callback<std::vector<CallHierarchyOutgoingCall>> Reply) { 1421 Server->outgoingCalls(Params.item, std::move(Reply)); 1422 } 1423 1424 void ClangdLSPServer::applyConfiguration( 1425 const ConfigurationSettings &Settings) { 1426 // Per-file update to the compilation database. 1427 llvm::StringSet<> ModifiedFiles; 1428 for (auto &[File, Command] : Settings.compilationDatabaseChanges) { 1429 auto Cmd = 1430 tooling::CompileCommand(std::move(Command.workingDirectory), File, 1431 std::move(Command.compilationCommand), 1432 /*Output=*/""); 1433 if (CDB->setCompileCommand(File, std::move(Cmd))) { 1434 ModifiedFiles.insert(File); 1435 } 1436 } 1437 1438 Server->reparseOpenFilesIfNeeded( 1439 [&](llvm::StringRef File) { return ModifiedFiles.count(File) != 0; }); 1440 } 1441 1442 void ClangdLSPServer::maybeExportMemoryProfile() { 1443 if (!trace::enabled() || !ShouldProfile()) 1444 return; 1445 1446 static constexpr trace::Metric MemoryUsage( 1447 "memory_usage", trace::Metric::Value, "component_name"); 1448 trace::Span Tracer("ProfileBrief"); 1449 MemoryTree MT; 1450 profile(MT); 1451 record(MT, "clangd_lsp_server", MemoryUsage); 1452 } 1453 1454 void ClangdLSPServer::maybeCleanupMemory() { 1455 if (!Opts.MemoryCleanup || !ShouldCleanupMemory()) 1456 return; 1457 Opts.MemoryCleanup(); 1458 } 1459 1460 // FIXME: This function needs to be properly tested. 1461 void ClangdLSPServer::onChangeConfiguration( 1462 const DidChangeConfigurationParams &Params) { 1463 applyConfiguration(Params.settings); 1464 } 1465 1466 void ClangdLSPServer::onReference( 1467 const ReferenceParams &Params, 1468 Callback<std::vector<ReferenceLocation>> Reply) { 1469 Server->findReferences(Params.textDocument.uri.file(), Params.position, 1470 Opts.ReferencesLimit, SupportsReferenceContainer, 1471 [Reply = std::move(Reply), 1472 IncludeDecl(Params.context.includeDeclaration)]( 1473 llvm::Expected<ReferencesResult> Refs) mutable { 1474 if (!Refs) 1475 return Reply(Refs.takeError()); 1476 // Filter out declarations if the client asked. 1477 std::vector<ReferenceLocation> Result; 1478 Result.reserve(Refs->References.size()); 1479 for (auto &Ref : Refs->References) { 1480 bool IsDecl = 1481 Ref.Attributes & ReferencesResult::Declaration; 1482 if (IncludeDecl || !IsDecl) 1483 Result.push_back(std::move(Ref.Loc)); 1484 } 1485 return Reply(std::move(Result)); 1486 }); 1487 } 1488 1489 void ClangdLSPServer::onGoToType(const TextDocumentPositionParams &Params, 1490 Callback<std::vector<Location>> Reply) { 1491 Server->findType( 1492 Params.textDocument.uri.file(), Params.position, 1493 [Reply = std::move(Reply)]( 1494 llvm::Expected<std::vector<LocatedSymbol>> Types) mutable { 1495 if (!Types) 1496 return Reply(Types.takeError()); 1497 std::vector<Location> Response; 1498 for (const LocatedSymbol &Sym : *Types) 1499 Response.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration)); 1500 return Reply(std::move(Response)); 1501 }); 1502 } 1503 1504 void ClangdLSPServer::onGoToImplementation( 1505 const TextDocumentPositionParams &Params, 1506 Callback<std::vector<Location>> Reply) { 1507 Server->findImplementations( 1508 Params.textDocument.uri.file(), Params.position, 1509 [Reply = std::move(Reply)]( 1510 llvm::Expected<std::vector<LocatedSymbol>> Overrides) mutable { 1511 if (!Overrides) 1512 return Reply(Overrides.takeError()); 1513 std::vector<Location> Impls; 1514 for (const LocatedSymbol &Sym : *Overrides) 1515 Impls.push_back(Sym.Definition.value_or(Sym.PreferredDeclaration)); 1516 return Reply(std::move(Impls)); 1517 }); 1518 } 1519 1520 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, 1521 Callback<std::vector<SymbolDetails>> Reply) { 1522 Server->symbolInfo(Params.textDocument.uri.file(), Params.position, 1523 std::move(Reply)); 1524 } 1525 1526 void ClangdLSPServer::onSelectionRange( 1527 const SelectionRangeParams &Params, 1528 Callback<std::vector<SelectionRange>> Reply) { 1529 Server->semanticRanges( 1530 Params.textDocument.uri.file(), Params.positions, 1531 [Reply = std::move(Reply)]( 1532 llvm::Expected<std::vector<SelectionRange>> Ranges) mutable { 1533 if (!Ranges) 1534 return Reply(Ranges.takeError()); 1535 return Reply(std::move(*Ranges)); 1536 }); 1537 } 1538 1539 void ClangdLSPServer::onDocumentLink( 1540 const DocumentLinkParams &Params, 1541 Callback<std::vector<DocumentLink>> Reply) { 1542 1543 // TODO(forster): This currently resolves all targets eagerly. This is slow, 1544 // because it blocks on the preamble/AST being built. We could respond to the 1545 // request faster by using string matching or the lexer to find the includes 1546 // and resolving the targets lazily. 1547 Server->documentLinks( 1548 Params.textDocument.uri.file(), 1549 [Reply = std::move(Reply)]( 1550 llvm::Expected<std::vector<DocumentLink>> Links) mutable { 1551 if (!Links) { 1552 return Reply(Links.takeError()); 1553 } 1554 return Reply(std::move(Links)); 1555 }); 1556 } 1557 1558 // Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ... 1559 static void increment(std::string &S) { 1560 for (char &C : llvm::reverse(S)) { 1561 if (C != '9') { 1562 ++C; 1563 return; 1564 } 1565 C = '0'; 1566 } 1567 S.insert(S.begin(), '1'); 1568 } 1569 1570 void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams &Params, 1571 Callback<SemanticTokens> CB) { 1572 auto File = Params.textDocument.uri.file(); 1573 Server->semanticHighlights( 1574 Params.textDocument.uri.file(), 1575 [this, File(File.str()), CB(std::move(CB)), Code(Server->getDraft(File))]( 1576 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1577 if (!HT) 1578 return CB(HT.takeError()); 1579 SemanticTokens Result; 1580 Result.tokens = toSemanticTokens(*HT, *Code); 1581 { 1582 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1583 auto &Last = LastSemanticTokens[File]; 1584 1585 Last.tokens = Result.tokens; 1586 increment(Last.resultId); 1587 Result.resultId = Last.resultId; 1588 } 1589 CB(std::move(Result)); 1590 }); 1591 } 1592 1593 void ClangdLSPServer::onSemanticTokensDelta( 1594 const SemanticTokensDeltaParams &Params, 1595 Callback<SemanticTokensOrDelta> CB) { 1596 auto File = Params.textDocument.uri.file(); 1597 Server->semanticHighlights( 1598 Params.textDocument.uri.file(), 1599 [this, PrevResultID(Params.previousResultId), File(File.str()), 1600 CB(std::move(CB)), Code(Server->getDraft(File))]( 1601 llvm::Expected<std::vector<HighlightingToken>> HT) mutable { 1602 if (!HT) 1603 return CB(HT.takeError()); 1604 std::vector<SemanticToken> Toks = toSemanticTokens(*HT, *Code); 1605 1606 SemanticTokensOrDelta Result; 1607 { 1608 std::lock_guard<std::mutex> Lock(SemanticTokensMutex); 1609 auto &Last = LastSemanticTokens[File]; 1610 1611 if (PrevResultID == Last.resultId) { 1612 Result.edits = diffTokens(Last.tokens, Toks); 1613 } else { 1614 vlog("semanticTokens/full/delta: wanted edits vs {0} but last " 1615 "result had ID {1}. Returning full token list.", 1616 PrevResultID, Last.resultId); 1617 Result.tokens = Toks; 1618 } 1619 1620 Last.tokens = std::move(Toks); 1621 increment(Last.resultId); 1622 Result.resultId = Last.resultId; 1623 } 1624 1625 CB(std::move(Result)); 1626 }); 1627 } 1628 1629 void ClangdLSPServer::onMemoryUsage(const NoParams &, 1630 Callback<MemoryTree> Reply) { 1631 llvm::BumpPtrAllocator DetailAlloc; 1632 MemoryTree MT(&DetailAlloc); 1633 profile(MT); 1634 Reply(std::move(MT)); 1635 } 1636 1637 void ClangdLSPServer::onAST(const ASTParams &Params, 1638 Callback<std::optional<ASTNode>> CB) { 1639 Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB)); 1640 } 1641 1642 ClangdLSPServer::ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS, 1643 const ClangdLSPServer::Options &Opts) 1644 : ShouldProfile(/*Period=*/std::chrono::minutes(5), 1645 /*Delay=*/std::chrono::minutes(1)), 1646 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1), 1647 /*Delay=*/std::chrono::minutes(1)), 1648 BackgroundContext(Context::current().clone()), Transp(Transp), 1649 MsgHandler(new MessageHandler(*this)), TFS(TFS), 1650 SupportedSymbolKinds(defaultSymbolKinds()), 1651 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts) { 1652 if (Opts.ConfigProvider) { 1653 assert(!Opts.ContextProvider && 1654 "Only one of ConfigProvider and ContextProvider allowed!"); 1655 this->Opts.ContextProvider = ClangdServer::createConfiguredContextProvider( 1656 Opts.ConfigProvider, this); 1657 } 1658 LSPBinder Bind(this->Handlers, *this); 1659 Bind.method("initialize", this, &ClangdLSPServer::onInitialize); 1660 } 1661 1662 void ClangdLSPServer::bindMethods(LSPBinder &Bind, 1663 const ClientCapabilities &Caps) { 1664 // clang-format off 1665 Bind.notification("initialized", this, &ClangdLSPServer::onInitialized); 1666 Bind.method("shutdown", this, &ClangdLSPServer::onShutdown); 1667 Bind.method("sync", this, &ClangdLSPServer::onSync); 1668 Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting); 1669 Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting); 1670 Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting); 1671 Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction); 1672 Bind.method("textDocument/completion", this, &ClangdLSPServer::onCompletion); 1673 Bind.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp); 1674 Bind.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition); 1675 Bind.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration); 1676 Bind.method("textDocument/typeDefinition", this, &ClangdLSPServer::onGoToType); 1677 Bind.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation); 1678 Bind.method("textDocument/references", this, &ClangdLSPServer::onReference); 1679 Bind.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader); 1680 Bind.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename); 1681 Bind.method("textDocument/rename", this, &ClangdLSPServer::onRename); 1682 Bind.method("textDocument/hover", this, &ClangdLSPServer::onHover); 1683 Bind.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol); 1684 Bind.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand); 1685 Bind.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight); 1686 Bind.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol); 1687 Bind.method("textDocument/ast", this, &ClangdLSPServer::onAST); 1688 Bind.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen); 1689 Bind.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose); 1690 Bind.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange); 1691 Bind.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave); 1692 Bind.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent); 1693 Bind.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration); 1694 Bind.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo); 1695 Bind.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy); 1696 Bind.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy); 1697 Bind.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy); 1698 Bind.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes); 1699 Bind.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes); 1700 Bind.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy); 1701 Bind.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls); 1702 if (Opts.EnableOutgoingCalls) 1703 Bind.method("callHierarchy/outgoingCalls", this, &ClangdLSPServer::onCallHierarchyOutgoingCalls); 1704 Bind.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange); 1705 Bind.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink); 1706 Bind.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens); 1707 Bind.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta); 1708 Bind.method("clangd/inlayHints", this, &ClangdLSPServer::onClangdInlayHints); 1709 Bind.method("textDocument/inlayHint", this, &ClangdLSPServer::onInlayHint); 1710 Bind.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage); 1711 Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange); 1712 Bind.command(ApplyFixCommand, this, &ClangdLSPServer::onCommandApplyEdit); 1713 Bind.command(ApplyTweakCommand, this, &ClangdLSPServer::onCommandApplyTweak); 1714 Bind.command(ApplyRenameCommand, this, &ClangdLSPServer::onCommandApplyRename); 1715 1716 ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit"); 1717 PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics"); 1718 if (Caps.InactiveRegions) 1719 PublishInactiveRegions = Bind.outgoingNotification("textDocument/inactiveRegions"); 1720 ShowMessage = Bind.outgoingNotification("window/showMessage"); 1721 NotifyFileStatus = Bind.outgoingNotification("textDocument/clangd.fileStatus"); 1722 CreateWorkDoneProgress = Bind.outgoingMethod("window/workDoneProgress/create"); 1723 BeginWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1724 ReportWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1725 EndWorkDoneProgress = Bind.outgoingNotification("$/progress"); 1726 if(Caps.SemanticTokenRefreshSupport) 1727 SemanticTokensRefresh = Bind.outgoingMethod("workspace/semanticTokens/refresh"); 1728 // clang-format on 1729 } 1730 1731 ClangdLSPServer::~ClangdLSPServer() { 1732 IsBeingDestroyed = true; 1733 // Explicitly destroy ClangdServer first, blocking on threads it owns. 1734 // This ensures they don't access any other members. 1735 Server.reset(); 1736 } 1737 1738 bool ClangdLSPServer::run() { 1739 // Run the Language Server loop. 1740 bool CleanExit = true; 1741 if (auto Err = Transp.loop(*MsgHandler)) { 1742 elog("Transport error: {0}", std::move(Err)); 1743 CleanExit = false; 1744 } 1745 1746 return CleanExit && ShutdownRequestReceived; 1747 } 1748 1749 void ClangdLSPServer::profile(MemoryTree &MT) const { 1750 if (Server) 1751 Server->profile(MT.child("clangd_server")); 1752 } 1753 1754 std::optional<ClangdServer::DiagRef> 1755 ClangdLSPServer::getDiagRef(StringRef File, const clangd::Diagnostic &D) { 1756 std::lock_guard<std::mutex> Lock(DiagRefMutex); 1757 auto DiagToDiagRefIter = DiagRefMap.find(File); 1758 if (DiagToDiagRefIter == DiagRefMap.end()) 1759 return std::nullopt; 1760 1761 const auto &DiagToDiagRefMap = DiagToDiagRefIter->second; 1762 auto FixItsIter = DiagToDiagRefMap.find(toDiagKey(D)); 1763 if (FixItsIter == DiagToDiagRefMap.end()) 1764 return std::nullopt; 1765 1766 return FixItsIter->second; 1767 } 1768 1769 // A completion request is sent when the user types '>' or ':', but we only 1770 // want to trigger on '->' and '::'. We check the preceding text to make 1771 // sure it matches what we expected. 1772 // Running the lexer here would be more robust (e.g. we can detect comments 1773 // and avoid triggering completion there), but we choose to err on the side 1774 // of simplicity here. 1775 bool ClangdLSPServer::shouldRunCompletion( 1776 const CompletionParams &Params) const { 1777 if (Params.context.triggerKind != CompletionTriggerKind::TriggerCharacter) 1778 return true; 1779 auto Code = Server->getDraft(Params.textDocument.uri.file()); 1780 if (!Code) 1781 return true; // completion code will log the error for untracked doc. 1782 auto Offset = positionToOffset(*Code, Params.position, 1783 /*AllowColumnsBeyondLineLength=*/false); 1784 if (!Offset) { 1785 vlog("could not convert position '{0}' to offset for file '{1}'", 1786 Params.position, Params.textDocument.uri.file()); 1787 return true; 1788 } 1789 return allowImplicitCompletion(*Code, *Offset); 1790 } 1791 1792 void ClangdLSPServer::onDiagnosticsReady(PathRef File, llvm::StringRef Version, 1793 llvm::ArrayRef<Diag> Diagnostics) { 1794 PublishDiagnosticsParams Notification; 1795 Notification.version = decodeVersion(Version); 1796 Notification.uri = URIForFile::canonicalize(File, /*TUPath=*/File); 1797 DiagnosticToDiagRefMap LocalDiagMap; // Temporary storage 1798 for (auto &Diag : Diagnostics) { 1799 toLSPDiags(Diag, Notification.uri, DiagOpts, 1800 [&](clangd::Diagnostic LSPDiag, llvm::ArrayRef<Fix> Fixes) { 1801 if (DiagOpts.EmbedFixesInDiagnostics) { 1802 std::vector<CodeAction> CodeActions; 1803 for (const auto &Fix : Fixes) 1804 CodeActions.push_back(toCodeAction( 1805 Fix, Notification.uri, Notification.version, 1806 SupportsDocumentChanges, SupportsChangeAnnotation)); 1807 LSPDiag.codeActions.emplace(std::move(CodeActions)); 1808 if (LSPDiag.codeActions->size() == 1) 1809 LSPDiag.codeActions->front().isPreferred = true; 1810 } 1811 LocalDiagMap[toDiagKey(LSPDiag)] = {Diag.Range, Diag.Message}; 1812 Notification.diagnostics.push_back(std::move(LSPDiag)); 1813 }); 1814 } 1815 1816 // Cache DiagRefMap 1817 { 1818 std::lock_guard<std::mutex> Lock(DiagRefMutex); 1819 DiagRefMap[File] = LocalDiagMap; 1820 } 1821 1822 // Send a notification to the LSP client. 1823 PublishDiagnostics(Notification); 1824 } 1825 1826 void ClangdLSPServer::onInactiveRegionsReady( 1827 PathRef File, std::vector<Range> InactiveRegions) { 1828 InactiveRegionsParams Notification; 1829 Notification.TextDocument = {URIForFile::canonicalize(File, /*TUPath=*/File)}; 1830 Notification.InactiveRegions = std::move(InactiveRegions); 1831 1832 PublishInactiveRegions(Notification); 1833 } 1834 1835 void ClangdLSPServer::onBackgroundIndexProgress( 1836 const BackgroundQueue::Stats &Stats) { 1837 static const char ProgressToken[] = "backgroundIndexProgress"; 1838 1839 // The background index did some work, maybe we need to cleanup 1840 maybeCleanupMemory(); 1841 1842 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1843 1844 auto NotifyProgress = [this](const BackgroundQueue::Stats &Stats) { 1845 if (BackgroundIndexProgressState != BackgroundIndexProgress::Live) { 1846 WorkDoneProgressBegin Begin; 1847 Begin.percentage = true; 1848 Begin.title = "indexing"; 1849 BeginWorkDoneProgress({ProgressToken, std::move(Begin)}); 1850 BackgroundIndexProgressState = BackgroundIndexProgress::Live; 1851 } 1852 1853 if (Stats.Completed < Stats.Enqueued) { 1854 assert(Stats.Enqueued > Stats.LastIdle); 1855 WorkDoneProgressReport Report; 1856 Report.percentage = 100 * (Stats.Completed - Stats.LastIdle) / 1857 (Stats.Enqueued - Stats.LastIdle); 1858 Report.message = 1859 llvm::formatv("{0}/{1}", Stats.Completed - Stats.LastIdle, 1860 Stats.Enqueued - Stats.LastIdle); 1861 ReportWorkDoneProgress({ProgressToken, std::move(Report)}); 1862 } else { 1863 assert(Stats.Completed == Stats.Enqueued); 1864 EndWorkDoneProgress({ProgressToken, WorkDoneProgressEnd()}); 1865 BackgroundIndexProgressState = BackgroundIndexProgress::Empty; 1866 } 1867 }; 1868 1869 switch (BackgroundIndexProgressState) { 1870 case BackgroundIndexProgress::Unsupported: 1871 return; 1872 case BackgroundIndexProgress::Creating: 1873 // Cache this update for when the progress bar is available. 1874 PendingBackgroundIndexProgress = Stats; 1875 return; 1876 case BackgroundIndexProgress::Empty: { 1877 if (BackgroundIndexSkipCreate) { 1878 NotifyProgress(Stats); 1879 break; 1880 } 1881 // Cache this update for when the progress bar is available. 1882 PendingBackgroundIndexProgress = Stats; 1883 BackgroundIndexProgressState = BackgroundIndexProgress::Creating; 1884 WorkDoneProgressCreateParams CreateRequest; 1885 CreateRequest.token = ProgressToken; 1886 CreateWorkDoneProgress( 1887 CreateRequest, 1888 [this, NotifyProgress](llvm::Expected<std::nullptr_t> E) { 1889 std::lock_guard<std::mutex> Lock(BackgroundIndexProgressMutex); 1890 if (E) { 1891 NotifyProgress(this->PendingBackgroundIndexProgress); 1892 } else { 1893 elog("Failed to create background index progress bar: {0}", 1894 E.takeError()); 1895 // give up forever rather than thrashing about 1896 BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported; 1897 } 1898 }); 1899 break; 1900 } 1901 case BackgroundIndexProgress::Live: 1902 NotifyProgress(Stats); 1903 break; 1904 } 1905 } 1906 1907 void ClangdLSPServer::onFileUpdated(PathRef File, const TUStatus &Status) { 1908 if (!SupportFileStatus) 1909 return; 1910 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these 1911 // two statuses are running faster in practice, which leads the UI constantly 1912 // changing, and doesn't provide much value. We may want to emit status at a 1913 // reasonable time interval (e.g. 0.5s). 1914 if (Status.PreambleActivity == PreambleAction::Idle && 1915 (Status.ASTActivity.K == ASTAction::Building || 1916 Status.ASTActivity.K == ASTAction::RunningAction)) 1917 return; 1918 NotifyFileStatus(Status.render(File)); 1919 } 1920 1921 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File) { 1922 if (SemanticTokensRefresh) { 1923 SemanticTokensRefresh(NoParams{}, [](llvm::Expected<std::nullptr_t> E) { 1924 if (E) 1925 return; 1926 elog("Failed to refresh semantic tokens: {0}", E.takeError()); 1927 }); 1928 } 1929 } 1930 1931 } // namespace clangd 1932 } // namespace clang 1933