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