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