brintos

brintos / llvm-project-archived public Read only

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