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