1 //===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ClangdLSPServer.h"
10 #include "ClangdServer.h"
11 #include "CodeComplete.h"
12 #include "CompileCommands.h"
13 #include "Diagnostics.h"
15 #include "GlobalCompilationDatabase.h"
16 #include "LSPBinder.h"
17 #include "ModulesBuilder.h"
19 #include "SemanticHighlighting.h"
20 #include "SourceCode.h"
21 #include "TUScheduler.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"
57 // Tracks end-to-end latency of high level lsp calls. Measurements are in
59 constexpr trace::Metric
LSPLatency("lsp_latency", trace::Metric::Distribution
,
62 // LSP defines file versions as numbers that increase.
63 // ClangdServer treats them as opaque and therefore uses strings instead.
64 std::string
encodeVersion(std::optional
<int64_t> LSPVersion
) {
65 return LSPVersion
? llvm::to_string(*LSPVersion
) : "";
67 std::optional
<int64_t> decodeVersion(llvm::StringRef Encoded
) {
69 if (llvm::to_integer(Encoded
, Result
, 10))
71 if (!Encoded
.empty()) // Empty can be e.g. diagnostics on close.
72 elog("unexpected non-numeric version {0}", Encoded
);
76 const llvm::StringLiteral ApplyFixCommand
= "clangd.applyFix";
77 const llvm::StringLiteral ApplyTweakCommand
= "clangd.applyTweak";
78 const llvm::StringLiteral ApplyRenameCommand
= "clangd.applyRename";
80 CodeAction
toCodeAction(const ClangdServer::CodeActionResult::Rename
&R
,
81 const URIForFile
&File
) {
83 CA
.title
= R
.FixMessage
;
84 CA
.kind
= std::string(CodeAction::REFACTOR_KIND
);
86 CA
.command
->title
= R
.FixMessage
;
87 CA
.command
->command
= std::string(ApplyRenameCommand
);
89 Params
.textDocument
= TextDocumentIdentifier
{File
};
90 Params
.position
= R
.Diag
.Range
.start
;
91 Params
.newName
= R
.NewName
;
92 CA
.command
->argument
= Params
;
96 /// Transforms a tweak into a code action that would apply it if executed.
97 /// EXPECTS: T.prepare() was called and returned true.
98 CodeAction
toCodeAction(const ClangdServer::TweakRef
&T
, const URIForFile
&File
,
102 CA
.kind
= T
.Kind
.str();
103 // This tweak may have an expensive second stage, we only run it if the user
104 // actually chooses it in the UI. We reply with a command that would run the
105 // corresponding tweak.
106 // FIXME: for some tweaks, computing the edits is cheap and we could send them
108 CA
.command
.emplace();
109 CA
.command
->title
= T
.Title
;
110 CA
.command
->command
= std::string(ApplyTweakCommand
);
114 Args
.selection
= Selection
;
115 CA
.command
->argument
= std::move(Args
);
119 /// Convert from Fix to LSP CodeAction.
120 CodeAction
toCodeAction(const Fix
&F
, const URIForFile
&File
,
121 const std::optional
<int64_t> &Version
,
122 bool SupportsDocumentChanges
,
123 bool SupportChangeAnnotation
) {
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=*/""});
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(
140 SupportChangeAnnotation
? E
.annotationId
: ""});
141 if (SupportChangeAnnotation
) {
142 for (const auto &[AID
, Annotation
]: F
.Annotations
)
143 Action
.edit
->changeAnnotations
[AID
] = Annotation
;
149 void adjustSymbolKinds(llvm::MutableArrayRef
<DocumentSymbol
> Syms
,
150 SymbolKindBitset Kinds
) {
151 for (auto &S
: Syms
) {
152 S
.kind
= adjustKindToCapability(S
.kind
, Kinds
);
153 adjustSymbolKinds(S
.children
, Kinds
);
157 SymbolKindBitset
defaultSymbolKinds() {
158 SymbolKindBitset Defaults
;
159 for (size_t I
= SymbolKindMin
; I
<= static_cast<size_t>(SymbolKind::Array
);
165 CompletionItemKindBitset
defaultCompletionItemKinds() {
166 CompletionItemKindBitset Defaults
;
167 for (size_t I
= CompletionItemKindMin
;
168 I
<= static_cast<size_t>(CompletionItemKind::Reference
); ++I
)
173 // Makes sure edits in \p FE are applicable to latest file contents reported by
174 // editor. If not generates an error message containing information about files
175 // that needs to be saved.
176 llvm::Error
validateEdits(const ClangdServer
&Server
, const FileEdits
&FE
) {
177 size_t InvalidFileCount
= 0;
178 llvm::StringRef LastInvalidFile
;
179 for (const auto &It
: FE
) {
180 if (auto Draft
= Server
.getDraft(It
.first())) {
181 // If the file is open in user's editor, make sure the version we
182 // saw and current version are compatible as this is the text that
183 // will be replaced by editors.
184 if (!It
.second
.canApplyTo(*Draft
)) {
186 LastInvalidFile
= It
.first();
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);
199 // MessageHandler dispatches incoming LSP messages.
200 // It handles cross-cutting concerns:
201 // - serializes/deserializes protocol objects to JSON
202 // - logging of inbound messages
203 // - cancellation handling
204 // - basic call tracing
205 // MessageHandler ensures that initialize() is called before any other handler.
206 class ClangdLSPServer::MessageHandler
: public Transport::MessageHandler
{
208 MessageHandler(ClangdLSPServer
&Server
) : Server(Server
) {}
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")
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
));
227 log("unhandled notification {0}", Method
);
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
));
249 Reply(llvm::make_error
<LSPError
>("method not found",
250 ErrorCode::MethodNotFound
));
255 bool onReply(llvm::json::Value ID
,
256 llvm::Expected
<llvm::json::Value
> Result
) override
{
257 WithContext
HandlerContext(handlerContext());
259 Callback
<llvm::json::Value
> ReplyHandler
= nullptr;
260 if (auto IntID
= ID
.getAsInteger()) {
261 std::lock_guard
<std::mutex
> Mutex(CallMutex
);
262 // Find a corresponding callback for the request ID;
263 for (size_t Index
= 0; Index
< ReplyCallbacks
.size(); ++Index
) {
264 if (ReplyCallbacks
[Index
].first
== *IntID
) {
265 ReplyHandler
= std::move(ReplyCallbacks
[Index
].second
);
266 ReplyCallbacks
.erase(ReplyCallbacks
.begin() +
267 Index
); // remove the entry
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
);
278 llvm::consumeError(Result
.takeError());
282 // Log and run the reply handler.
284 log("<-- reply({0})", ID
);
285 ReplyHandler(std::move(Result
));
287 auto Err
= Result
.takeError();
288 log("<-- reply({0}) error: {1}", ID
, Err
);
289 ReplyHandler(std::move(Err
));
294 // Bind a reply callback to a request. The callback will be invoked when
295 // clangd receives the reply from the LSP client.
296 // Return a call id of the request.
297 llvm::json::Value
bindReply(Callback
<llvm::json::Value
> Reply
) {
298 std::optional
<std::pair
<int, Callback
<llvm::json::Value
>>> OldestCB
;
301 std::lock_guard
<std::mutex
> Mutex(CallMutex
);
303 ReplyCallbacks
.emplace_back(ID
, std::move(Reply
));
305 // If the queue overflows, we assume that the client didn't reply the
306 // oldest request, and run the corresponding callback which replies an
307 // error to the client.
308 if (ReplyCallbacks
.size() > MaxReplayCallbacks
) {
309 elog("more than {0} outstanding LSP calls, forgetting about {1}",
310 MaxReplayCallbacks
, ReplyCallbacks
.front().first
);
311 OldestCB
= std::move(ReplyCallbacks
.front());
312 ReplyCallbacks
.pop_front();
317 error("failed to receive a client reply for request ({0})",
323 // Function object to reply to an LSP call.
324 // Each instance must be called exactly once, otherwise:
325 // - the bug is logged, and (in debug mode) an assert will fire
326 // - if there was no reply, an error reply is sent
327 // - if there were multiple replies, only the first is sent
329 std::atomic
<bool> Replied
= {false};
330 std::chrono::steady_clock::time_point Start
;
331 llvm::json::Value ID
;
333 ClangdLSPServer
*Server
; // Null when moved-from.
334 llvm::json::Object
*TraceArgs
;
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
) {
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;
349 ReplyOnce
&operator=(ReplyOnce
&&) = delete;
350 ReplyOnce(const ReplyOnce
&) = delete;
351 ReplyOnce
&operator=(const ReplyOnce
&) = delete;
354 // There's one legitimate reason to never reply to a request: clangd's
355 // request handler send a call to the client (e.g. applyEdit) and the
356 // client never replied. In this case, the ReplyOnce is owned by
357 // ClangdLSPServer's reply callback table and is destroyed along with the
358 // server. We don't attempt to send a reply in this case, there's little
359 // to be gained from doing so.
360 if (Server
&& !Server
->IsBeingDestroyed
&& !Replied
) {
361 elog("No reply to message {0}({1})", Method
, ID
);
362 assert(false && "must reply to all calls!");
363 (*this)(llvm::make_error
<LSPError
>("server failed to reply",
364 ErrorCode::InternalError
));
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!");
375 auto Duration
= std::chrono::steady_clock::now() - Start
;
377 log("--> reply:{0}({1}) {2:ms}", Method
, ID
, Duration
);
379 (*TraceArgs
)["Reply"] = *Reply
;
380 std::lock_guard
<std::mutex
> Lock(Server
->TranspWriter
);
381 Server
->Transp
.reply(std::move(ID
), std::move(Reply
));
383 llvm::Error Err
= Reply
.takeError();
384 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method
, ID
, Duration
, Err
);
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
));
393 // Method calls may be cancelled by ID, so keep track of their state.
394 // This needs a mutex: handlers may finish on a different thread, and that's
395 // when we clean up entries in the map.
396 mutable std::mutex RequestCancelersMutex
;
397 llvm::StringMap
<std::pair
<Canceler
, /*Cookie*/ unsigned>> RequestCancelers
;
398 unsigned NextRequestCookie
= 0; // To disambiguate reused IDs, see below.
399 void onCancel(const llvm::json::Value
&Params
) {
400 const llvm::json::Value
*ID
= nullptr;
401 if (auto *O
= Params
.getAsObject())
404 elog("Bad cancellation request: {0}", Params
);
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.
414 Context
handlerContext() const {
415 return Context::current().derive(
416 kCurrentOffsetEncoding
,
417 Server
.Opts
.Encoding
.value_or(OffsetEncoding::UTF16
));
420 // We run cancelable requests in a context that does two things:
421 // - allows cancellation using RequestCancelers[ID]
422 // - cleans up the entry in RequestCancelers when it's no longer needed
423 // If a client reuses an ID, the last wins and the first cannot be canceled.
424 Context
cancelableRequestContext(const llvm::json::Value
&ID
) {
425 auto Task
= cancelableTask(
426 /*Reason=*/static_cast<int>(ErrorCode::RequestCancelled
));
427 auto StrID
= llvm::to_string(ID
); // JSON-serialize ID for map key.
428 auto Cookie
= NextRequestCookie
++; // No lock, only called on main thread.
430 std::lock_guard
<std::mutex
> Lock(RequestCancelersMutex
);
431 RequestCancelers
[StrID
] = {std::move(Task
.second
), Cookie
};
433 // When the request ends, we can clean up the entry we just added.
434 // The cookie lets us check that it hasn't been overwritten due to ID
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
);
444 // The maximum number of callbacks held in clangd.
446 // We bound the maximum size to the pending map to prevent memory leakage
447 // for cases where LSP clients don't reply for the request.
448 // This has to go after RequestCancellers and RequestCancellersMutex since it
449 // can contain a callback that has a cancelable context.
450 static constexpr int MaxReplayCallbacks
= 100;
451 mutable std::mutex CallMutex
;
452 int NextCallID
= 0; /* GUARDED_BY(CallMutex) */
453 std::deque
<std::pair
</*RequestID*/ int,
454 /*ReplyHandler*/ Callback
<llvm::json::Value
>>>
455 ReplyCallbacks
; /* GUARDED_BY(CallMutex) */
457 ClangdLSPServer
&Server
;
459 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks
;
461 // call(), notify(), and reply() wrap the Transport, adding logging and locking.
462 void ClangdLSPServer::callMethod(StringRef Method
, llvm::json::Value Params
,
463 Callback
<llvm::json::Value
> CB
) {
464 auto ID
= MsgHandler
->bindReply(std::move(CB
));
465 log("--> {0}({1})", Method
, ID
);
466 std::lock_guard
<std::mutex
> Lock(TranspWriter
);
467 Transp
.call(Method
, std::move(Params
), ID
);
470 void ClangdLSPServer::notify(llvm::StringRef Method
, llvm::json::Value Params
) {
471 log("--> {0}", Method
);
472 maybeCleanupMemory();
473 std::lock_guard
<std::mutex
> Lock(TranspWriter
);
474 Transp
.notify(Method
, std::move(Params
));
477 static std::vector
<llvm::StringRef
> semanticTokenTypes() {
478 std::vector
<llvm::StringRef
> Types
;
479 for (unsigned I
= 0; I
<= static_cast<unsigned>(HighlightingKind::LastKind
);
481 Types
.push_back(toSemanticTokenType(static_cast<HighlightingKind
>(I
)));
485 static std::vector
<llvm::StringRef
> semanticTokenModifiers() {
486 std::vector
<llvm::StringRef
> Modifiers
;
488 I
<= static_cast<unsigned>(HighlightingModifier::LastModifier
); ++I
)
490 toSemanticTokenModifier(static_cast<HighlightingModifier
>(I
)));
494 void ClangdLSPServer::onInitialize(const InitializeParams
&Params
,
495 Callback
<llvm::json::Value
> Reply
) {
496 // Determine character encoding first as it affects constructed ClangdServer.
497 if (Params
.capabilities
.offsetEncoding
&& !Opts
.Encoding
) {
498 Opts
.Encoding
= OffsetEncoding::UTF16
; // fallback
499 for (OffsetEncoding Supported
: *Params
.capabilities
.offsetEncoding
)
500 if (Supported
!= OffsetEncoding::UnsupportedEncoding
) {
501 Opts
.Encoding
= Supported
;
506 if (Params
.capabilities
.TheiaSemanticHighlighting
&&
507 !Params
.capabilities
.SemanticTokens
) {
508 elog("Client requested legacy semanticHighlights notification, which is "
509 "no longer supported. Migrate to standard semanticTokens request");
512 if (Params
.rootUri
&& *Params
.rootUri
)
513 Opts
.WorkspaceRoot
= std::string(Params
.rootUri
->file());
514 else if (Params
.rootPath
&& !Params
.rootPath
->empty())
515 Opts
.WorkspaceRoot
= *Params
.rootPath
;
517 return Reply(llvm::make_error
<LSPError
>("server already initialized",
518 ErrorCode::InvalidRequest
));
520 Opts
.CodeComplete
.EnableSnippets
= Params
.capabilities
.CompletionSnippets
;
521 Opts
.CodeComplete
.IncludeFixIts
= Params
.capabilities
.CompletionFixes
;
522 if (!Opts
.CodeComplete
.BundleOverloads
)
523 Opts
.CodeComplete
.BundleOverloads
= Params
.capabilities
.HasSignatureHelp
;
524 Opts
.CodeComplete
.DocumentationFormat
=
525 Params
.capabilities
.CompletionDocumentationFormat
;
526 Opts
.SignatureHelpDocumentationFormat
=
527 Params
.capabilities
.SignatureHelpDocumentationFormat
;
528 DiagOpts
.EmbedFixesInDiagnostics
= Params
.capabilities
.DiagnosticFixes
;
529 DiagOpts
.SendDiagnosticCategory
= Params
.capabilities
.DiagnosticCategory
;
530 DiagOpts
.EmitRelatedLocations
=
531 Params
.capabilities
.DiagnosticRelatedInformation
;
532 if (Params
.capabilities
.WorkspaceSymbolKinds
)
533 SupportedSymbolKinds
|= *Params
.capabilities
.WorkspaceSymbolKinds
;
534 if (Params
.capabilities
.CompletionItemKinds
)
535 SupportedCompletionItemKinds
|= *Params
.capabilities
.CompletionItemKinds
;
536 SupportsCompletionLabelDetails
= Params
.capabilities
.CompletionLabelDetail
;
537 SupportsCodeAction
= Params
.capabilities
.CodeActionStructure
;
538 SupportsHierarchicalDocumentSymbol
=
539 Params
.capabilities
.HierarchicalDocumentSymbol
;
540 SupportsReferenceContainer
= Params
.capabilities
.ReferenceContainer
;
541 SupportFileStatus
= Params
.initializationOptions
.FileStatus
;
542 SupportsDocumentChanges
= Params
.capabilities
.DocumentChanges
;
543 SupportsChangeAnnotation
= Params
.capabilities
.ChangeAnnotation
;
544 HoverContentFormat
= Params
.capabilities
.HoverContentFormat
;
545 Opts
.LineFoldingOnly
= Params
.capabilities
.LineFoldingOnly
;
546 SupportsOffsetsInSignatureHelp
= Params
.capabilities
.OffsetsInSignatureHelp
;
547 if (Params
.capabilities
.WorkDoneProgress
)
548 BackgroundIndexProgressState
= BackgroundIndexProgress::Empty
;
549 BackgroundIndexSkipCreate
= Params
.capabilities
.ImplicitProgressCreation
;
550 Opts
.ImplicitCancellation
= !Params
.capabilities
.CancelsStaleRequests
;
551 Opts
.PublishInactiveRegions
= Params
.capabilities
.InactiveRegions
;
553 if (Opts
.UseDirBasedCDB
) {
554 DirectoryBasedGlobalCompilationDatabase::Options
CDBOpts(TFS
);
555 if (const auto &Dir
= Params
.initializationOptions
.compilationDatabasePath
)
556 CDBOpts
.CompileCommandsDir
= Dir
;
557 CDBOpts
.ContextProvider
= Opts
.ContextProvider
;
559 std::make_unique
<DirectoryBasedGlobalCompilationDatabase
>(CDBOpts
);
561 auto Mangler
= CommandMangler::detect();
562 Mangler
.SystemIncludeExtractor
=
563 getSystemIncludeExtractor(llvm::ArrayRef(Opts
.QueryDriverGlobs
));
564 if (Opts
.ResourceDir
)
565 Mangler
.ResourceDir
= *Opts
.ResourceDir
;
566 CDB
.emplace(BaseCDB
.get(), Params
.initializationOptions
.fallbackFlags
,
569 if (Opts
.EnableExperimentalModulesSupport
) {
570 ModulesManager
.emplace(*CDB
);
571 Opts
.ModulesManager
= &*ModulesManager
;
575 // Switch caller's context with LSPServer's background context. Since we
576 // rather want to propagate information from LSPServer's context into the
578 WithContext
MainContext(BackgroundContext
.clone());
579 std::optional
<WithContextValue
> WithOffsetEncoding
;
581 WithOffsetEncoding
.emplace(kCurrentOffsetEncoding
, *Opts
.Encoding
);
582 Server
.emplace(*CDB
, TFS
, Opts
,
583 static_cast<ClangdServer::Callbacks
*>(this));
586 llvm::json::Object ServerCaps
{
590 {"change", (int)TextDocumentSyncKind::Incremental
},
593 {"documentFormattingProvider", true},
594 {"documentRangeFormattingProvider", true},
595 {"documentOnTypeFormattingProvider",
597 {"firstTriggerCharacter", "\n"},
598 {"moreTriggerCharacter", {}},
600 {"completionProvider",
602 // We don't set `(` etc as allCommitCharacters as they interact
603 // poorly with snippet results.
604 // See https://github.com/clangd/vscode-clangd/issues/357
605 // Hopefully we can use them one day without this side-effect:
606 // https://github.com/microsoft/vscode/issues/42544
607 {"resolveProvider", false},
608 // We do extra checks, e.g. that > is part of ->.
609 {"triggerCharacters", {".", "<", ">", ":", "\"", "/", "*"}},
611 {"semanticTokensProvider",
613 {"full", llvm::json::Object
{{"delta", true}}},
616 llvm::json::Object
{{"tokenTypes", semanticTokenTypes()},
617 {"tokenModifiers", semanticTokenModifiers()}}},
619 {"signatureHelpProvider",
621 {"triggerCharacters", {"(", ")", "{", "}", "<", ">", ","}},
623 {"declarationProvider", true},
624 {"definitionProvider", true},
625 {"implementationProvider", true},
626 {"typeDefinitionProvider", true},
627 {"documentHighlightProvider", true},
628 {"documentLinkProvider",
630 {"resolveProvider", false},
632 {"hoverProvider", true},
633 {"selectionRangeProvider", true},
634 {"documentSymbolProvider", true},
635 {"workspaceSymbolProvider", true},
636 {"referencesProvider", true},
637 {"astProvider", true}, // clangd extension
638 {"typeHierarchyProvider", true},
639 // Unfortunately our extension made use of the same capability name as the
640 // standard. Advertise this capability to tell clients that implement our
641 // extension we really have support for the standardized one as well.
642 {"standardTypeHierarchyProvider", true}, // clangd extension
643 {"memoryUsageProvider", true}, // clangd extension
644 {"compilationDatabase", // clangd extension
645 llvm::json::Object
{{"automaticReload", true}}},
646 {"inactiveRegionsProvider", true}, // clangd extension
647 {"callHierarchyProvider", true},
648 {"clangdInlayHintsProvider", true},
649 {"inlayHintProvider", true},
650 {"foldingRangeProvider", true},
654 LSPBinder
Binder(Handlers
, *this);
655 bindMethods(Binder
, Params
.capabilities
);
656 if (Opts
.FeatureModules
)
657 for (auto &Mod
: *Opts
.FeatureModules
)
658 Mod
.initializeLSP(Binder
, Params
.rawCapabilities
, ServerCaps
);
661 // Per LSP, renameProvider can be either boolean or RenameOptions.
662 // RenameOptions will be specified if the client states it supports prepare.
663 ServerCaps
["renameProvider"] =
664 Params
.capabilities
.RenamePrepareSupport
665 ? llvm::json::Object
{{"prepareProvider", true}}
666 : llvm::json::Value(true);
668 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
669 // CodeActionOptions is only valid if the client supports action literal
670 // via textDocument.codeAction.codeActionLiteralSupport.
671 ServerCaps
["codeActionProvider"] =
672 Params
.capabilities
.CodeActionStructure
673 ? llvm::json::Object
{{"codeActionKinds",
674 {CodeAction::QUICKFIX_KIND
,
675 CodeAction::REFACTOR_KIND
,
676 CodeAction::INFO_KIND
}}}
677 : llvm::json::Value(true);
679 std::vector
<llvm::StringRef
> Commands
;
680 for (llvm::StringRef Command
: Handlers
.CommandHandlers
.keys())
681 Commands
.push_back(Command
);
682 llvm::sort(Commands
);
683 ServerCaps
["executeCommandProvider"] =
684 llvm::json::Object
{{"commands", Commands
}};
686 llvm::json::Object Result
{
690 {"version", llvm::formatv("{0} {1} {2}", versionString(),
691 featureString(), platformString())}}},
692 {"capabilities", std::move(ServerCaps
)}}};
694 Result
["offsetEncoding"] = *Opts
.Encoding
;
695 Reply(std::move(Result
));
697 // Apply settings after we're fully initialized.
698 // This can start background indexing and in turn trigger LSP notifications.
699 applyConfiguration(Params
.initializationOptions
.ConfigSettings
);
702 void ClangdLSPServer::onInitialized(const InitializedParams
&Params
) {}
704 void ClangdLSPServer::onShutdown(const NoParams
&,
705 Callback
<std::nullptr_t
> Reply
) {
706 // Do essentially nothing, just say we're ready to exit.
707 ShutdownRequestReceived
= true;
711 // sync is a clangd extension: it blocks until all background work completes.
712 // It blocks the calling thread, so no messages are processed until it returns!
713 void ClangdLSPServer::onSync(const NoParams
&, Callback
<std::nullptr_t
> Reply
) {
714 if (Server
->blockUntilIdleForTest(/*TimeoutSeconds=*/60))
717 Reply(error("Not idle after a minute"));
720 void ClangdLSPServer::onDocumentDidOpen(
721 const DidOpenTextDocumentParams
&Params
) {
722 PathRef File
= Params
.textDocument
.uri
.file();
724 const std::string
&Contents
= Params
.textDocument
.text
;
726 Server
->addDocument(File
, Contents
,
727 encodeVersion(Params
.textDocument
.version
),
728 WantDiagnostics::Yes
);
731 void ClangdLSPServer::onDocumentDidChange(
732 const DidChangeTextDocumentParams
&Params
) {
733 auto WantDiags
= WantDiagnostics::Auto
;
734 if (Params
.wantDiagnostics
)
736 *Params
.wantDiagnostics
? WantDiagnostics::Yes
: WantDiagnostics::No
;
738 PathRef File
= Params
.textDocument
.uri
.file();
739 auto Code
= Server
->getDraft(File
);
741 log("Trying to incrementally change non-added document: {0}", File
);
744 std::string
NewCode(*Code
);
745 for (const auto &Change
: Params
.contentChanges
) {
746 if (auto Err
= applyChange(NewCode
, Change
)) {
747 // If this fails, we are most likely going to be not in sync anymore with
748 // the client. It is better to remove the draft and let further
749 // operations fail rather than giving wrong results.
750 Server
->removeDocument(File
);
751 elog("Failed to update {0}: {1}", File
, std::move(Err
));
755 Server
->addDocument(File
, NewCode
, encodeVersion(Params
.textDocument
.version
),
756 WantDiags
, Params
.forceRebuild
);
759 void ClangdLSPServer::onDocumentDidSave(
760 const DidSaveTextDocumentParams
&Params
) {
761 Server
->reparseOpenFilesIfNeeded([](llvm::StringRef
) { return true; });
764 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
765 // We could also reparse all open files here. However:
766 // - this could be frequent, and revalidating all the preambles isn't free
767 // - this is useful e.g. when switching git branches, but we're likely to see
768 // fresh headers but still have the old-branch main-file content
769 Server
->onFileEvent(Params
);
770 // FIXME: observe config files, immediately expire time-based caches, reparse:
771 // - compile_commands.json and compile_flags.txt
772 // - .clang_format and .clang-tidy
773 // - .clangd and clangd/config.yaml
776 void ClangdLSPServer::onCommand(const ExecuteCommandParams
&Params
,
777 Callback
<llvm::json::Value
> Reply
) {
778 auto It
= Handlers
.CommandHandlers
.find(Params
.command
);
779 if (It
== Handlers
.CommandHandlers
.end()) {
780 return Reply(llvm::make_error
<LSPError
>(
781 llvm::formatv("Unsupported command \"{0}\".", Params
.command
).str(),
782 ErrorCode::InvalidParams
));
784 It
->second(Params
.argument
, std::move(Reply
));
787 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit
&WE
,
788 Callback
<llvm::json::Value
> Reply
) {
789 // The flow for "apply-fix" :
790 // 1. We publish a diagnostic, including fixits
791 // 2. The user clicks on the diagnostic, the editor asks us for code actions
792 // 3. We send code actions, with the fixit embedded as context
793 // 4. The user selects the fixit, the editor asks us to apply it
794 // 5. We unwrap the changes and send them back to the editor
795 // 6. The editor applies the changes (applyEdit), and sends us a reply
796 // 7. We unwrap the reply and send a reply to the editor.
797 applyEdit(WE
, "Fix applied.", std::move(Reply
));
800 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs
&Args
,
801 Callback
<llvm::json::Value
> Reply
) {
802 auto Action
= [this, Reply
= std::move(Reply
)](
803 llvm::Expected
<Tweak::Effect
> R
) mutable {
805 return Reply(R
.takeError());
807 assert(R
->ShowMessage
|| (!R
->ApplyEdits
.empty() && "tweak has no effect"));
809 if (R
->ShowMessage
) {
810 ShowMessageParams Msg
;
811 Msg
.message
= *R
->ShowMessage
;
812 Msg
.type
= MessageType::Info
;
815 // When no edit is specified, make sure we Reply().
816 if (R
->ApplyEdits
.empty())
817 return Reply("Tweak applied.");
819 if (auto Err
= validateEdits(*Server
, R
->ApplyEdits
))
820 return Reply(std::move(Err
));
823 // FIXME: use documentChanges when SupportDocumentChanges is true.
824 WE
.changes
.emplace();
825 for (const auto &It
: R
->ApplyEdits
) {
826 (*WE
.changes
)[URI::createFile(It
.first()).toString()] =
827 It
.second
.asTextEdits();
829 // ApplyEdit will take care of calling Reply().
830 return applyEdit(std::move(WE
), "Tweak applied.", std::move(Reply
));
832 Server
->applyTweak(Args
.file
.file(), Args
.selection
, Args
.tweakID
,
836 void ClangdLSPServer::onCommandApplyRename(const RenameParams
&R
,
837 Callback
<llvm::json::Value
> Reply
) {
838 onRename(R
, [this, Reply
= std::move(Reply
)](
839 llvm::Expected
<WorkspaceEdit
> Edit
) mutable {
841 Reply(Edit
.takeError());
842 applyEdit(std::move(*Edit
), "Rename applied.", std::move(Reply
));
846 void ClangdLSPServer::applyEdit(WorkspaceEdit WE
, llvm::json::Value Success
,
847 Callback
<llvm::json::Value
> Reply
) {
848 ApplyWorkspaceEditParams Edit
;
849 Edit
.edit
= std::move(WE
);
851 Edit
, [Reply
= std::move(Reply
), SuccessMessage
= std::move(Success
)](
852 llvm::Expected
<ApplyWorkspaceEditResponse
> Response
) mutable {
854 return Reply(Response
.takeError());
855 if (!Response
->applied
) {
856 std::string Reason
= Response
->failureReason
857 ? *Response
->failureReason
859 return Reply(error("edits were not applied: {0}", Reason
));
861 return Reply(SuccessMessage
);
865 void ClangdLSPServer::onWorkspaceSymbol(
866 const WorkspaceSymbolParams
&Params
,
867 Callback
<std::vector
<SymbolInformation
>> Reply
) {
868 Server
->workspaceSymbols(
869 Params
.query
, Params
.limit
.value_or(Opts
.CodeComplete
.Limit
),
870 [Reply
= std::move(Reply
),
871 this](llvm::Expected
<std::vector
<SymbolInformation
>> Items
) mutable {
873 return Reply(Items
.takeError());
874 for (auto &Sym
: *Items
)
875 Sym
.kind
= adjustKindToCapability(Sym
.kind
, SupportedSymbolKinds
);
877 Reply(std::move(*Items
));
881 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams
&Params
,
882 Callback
<PrepareRenameResult
> Reply
) {
883 Server
->prepareRename(
884 Params
.textDocument
.uri
.file(), Params
.position
, /*NewName*/ std::nullopt
,
886 [Reply
= std::move(Reply
)](llvm::Expected
<RenameResult
> Result
) mutable {
888 return Reply(Result
.takeError());
889 PrepareRenameResult PrepareResult
;
890 PrepareResult
.range
= Result
->Target
;
891 PrepareResult
.placeholder
= Result
->Placeholder
;
892 return Reply(std::move(PrepareResult
));
896 void ClangdLSPServer::onRename(const RenameParams
&Params
,
897 Callback
<WorkspaceEdit
> Reply
) {
898 Path File
= std::string(Params
.textDocument
.uri
.file());
899 if (!Server
->getDraft(File
))
900 return Reply(llvm::make_error
<LSPError
>(
901 "onRename called for non-added file", ErrorCode::InvalidParams
));
902 Server
->rename(File
, Params
.position
, Params
.newName
, Opts
.Rename
,
903 [File
, Params
, Reply
= std::move(Reply
),
904 this](llvm::Expected
<RenameResult
> R
) mutable {
906 return Reply(R
.takeError());
907 if (auto Err
= validateEdits(*Server
, R
->GlobalChanges
))
908 return Reply(std::move(Err
));
909 WorkspaceEdit Result
;
910 // FIXME: use documentChanges if SupportDocumentChanges is
912 Result
.changes
.emplace();
913 for (const auto &Rep
: R
->GlobalChanges
) {
915 .changes
)[URI::createFile(Rep
.first()).toString()] =
916 Rep
.second
.asTextEdits();
922 void ClangdLSPServer::onDocumentDidClose(
923 const DidCloseTextDocumentParams
&Params
) {
924 PathRef File
= Params
.textDocument
.uri
.file();
925 Server
->removeDocument(File
);
928 std::lock_guard
<std::mutex
> Lock(DiagRefMutex
);
929 DiagRefMap
.erase(File
);
932 std::lock_guard
<std::mutex
> HLock(SemanticTokensMutex
);
933 LastSemanticTokens
.erase(File
);
935 // clangd will not send updates for this file anymore, so we empty out the
936 // list of diagnostics shown on the client (e.g. in the "Problems" pane of
937 // VSCode). Note that this cannot race with actual diagnostics responses
938 // because removeDocument() guarantees no diagnostic callbacks will be
939 // executed after it returns.
940 PublishDiagnosticsParams Notification
;
941 Notification
.uri
= URIForFile::canonicalize(File
, /*TUPath=*/File
);
942 PublishDiagnostics(Notification
);
945 void ClangdLSPServer::onDocumentOnTypeFormatting(
946 const DocumentOnTypeFormattingParams
&Params
,
947 Callback
<std::vector
<TextEdit
>> Reply
) {
948 auto File
= Params
.textDocument
.uri
.file();
949 Server
->formatOnType(File
, Params
.position
, Params
.ch
, std::move(Reply
));
952 void ClangdLSPServer::onDocumentRangeFormatting(
953 const DocumentRangeFormattingParams
&Params
,
954 Callback
<std::vector
<TextEdit
>> Reply
) {
955 auto File
= Params
.textDocument
.uri
.file();
956 auto Code
= Server
->getDraft(File
);
957 Server
->formatFile(File
, Params
.range
,
958 [Code
= std::move(Code
), Reply
= std::move(Reply
)](
959 llvm::Expected
<tooling::Replacements
> Result
) mutable {
961 Reply(replacementsToEdits(*Code
, Result
.get()));
963 Reply(Result
.takeError());
967 void ClangdLSPServer::onDocumentFormatting(
968 const DocumentFormattingParams
&Params
,
969 Callback
<std::vector
<TextEdit
>> Reply
) {
970 auto File
= Params
.textDocument
.uri
.file();
971 auto Code
= Server
->getDraft(File
);
972 Server
->formatFile(File
,
973 /*Rng=*/std::nullopt
,
974 [Code
= std::move(Code
), Reply
= std::move(Reply
)](
975 llvm::Expected
<tooling::Replacements
> Result
) mutable {
977 Reply(replacementsToEdits(*Code
, Result
.get()));
979 Reply(Result
.takeError());
983 /// The functions constructs a flattened view of the DocumentSymbol hierarchy.
984 /// Used by the clients that do not support the hierarchical view.
985 static std::vector
<SymbolInformation
>
986 flattenSymbolHierarchy(llvm::ArrayRef
<DocumentSymbol
> Symbols
,
987 const URIForFile
&FileURI
) {
988 std::vector
<SymbolInformation
> Results
;
989 std::function
<void(const DocumentSymbol
&, llvm::StringRef
)> Process
=
990 [&](const DocumentSymbol
&S
, std::optional
<llvm::StringRef
> ParentName
) {
991 SymbolInformation SI
;
992 SI
.containerName
= std::string(ParentName
? "" : *ParentName
);
995 SI
.location
.range
= S
.range
;
996 SI
.location
.uri
= FileURI
;
998 Results
.push_back(std::move(SI
));
999 std::string FullName
=
1000 !ParentName
? S
.name
: (ParentName
->str() + "::" + S
.name
);
1001 for (auto &C
: S
.children
)
1002 Process(C
, /*ParentName=*/FullName
);
1004 for (auto &S
: Symbols
)
1005 Process(S
, /*ParentName=*/"");
1009 void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams
&Params
,
1010 Callback
<llvm::json::Value
> Reply
) {
1011 URIForFile FileURI
= Params
.textDocument
.uri
;
1012 Server
->documentSymbols(
1013 Params
.textDocument
.uri
.file(),
1014 [this, FileURI
, Reply
= std::move(Reply
)](
1015 llvm::Expected
<std::vector
<DocumentSymbol
>> Items
) mutable {
1017 return Reply(Items
.takeError());
1018 adjustSymbolKinds(*Items
, SupportedSymbolKinds
);
1019 if (SupportsHierarchicalDocumentSymbol
)
1020 return Reply(std::move(*Items
));
1021 return Reply(flattenSymbolHierarchy(*Items
, FileURI
));
1025 void ClangdLSPServer::onFoldingRange(
1026 const FoldingRangeParams
&Params
,
1027 Callback
<std::vector
<FoldingRange
>> Reply
) {
1028 Server
->foldingRanges(Params
.textDocument
.uri
.file(), std::move(Reply
));
1031 static std::optional
<Command
> asCommand(const CodeAction
&Action
) {
1033 if (Action
.command
&& Action
.edit
)
1034 return std::nullopt
; // Not representable. (We never emit these anyway).
1035 if (Action
.command
) {
1036 Cmd
= *Action
.command
;
1037 } else if (Action
.edit
) {
1038 Cmd
.command
= std::string(ApplyFixCommand
);
1039 Cmd
.argument
= *Action
.edit
;
1041 return std::nullopt
;
1043 Cmd
.title
= Action
.title
;
1044 if (Action
.kind
&& *Action
.kind
== CodeAction::QUICKFIX_KIND
)
1045 Cmd
.title
= "Apply fix: " + Cmd
.title
;
1049 void ClangdLSPServer::onCodeAction(const CodeActionParams
&Params
,
1050 Callback
<llvm::json::Value
> Reply
) {
1051 URIForFile File
= Params
.textDocument
.uri
;
1052 std::map
<ClangdServer::DiagRef
, clangd::Diagnostic
> ToLSPDiags
;
1053 ClangdServer::CodeActionInputs Inputs
;
1055 for (const auto& LSPDiag
: Params
.context
.diagnostics
) {
1056 if (auto DiagRef
= getDiagRef(File
.file(), LSPDiag
)) {
1057 ToLSPDiags
[*DiagRef
] = LSPDiag
;
1058 Inputs
.Diagnostics
.push_back(*DiagRef
);
1061 Inputs
.File
= File
.file();
1062 Inputs
.Selection
= Params
.range
;
1063 Inputs
.RequestedActionKinds
= Params
.context
.only
;
1064 Inputs
.TweakFilter
= [this](const Tweak
&T
) {
1065 return Opts
.TweakFilter(T
);
1068 Reply
= std::move(Reply
),
1069 ToLSPDiags
= std::move(ToLSPDiags
), File
,
1070 Selection
= Params
.range
](
1071 llvm::Expected
<ClangdServer::CodeActionResult
> Fixits
) mutable {
1073 return Reply(Fixits
.takeError());
1074 std::vector
<CodeAction
> CAs
;
1075 auto Version
= decodeVersion(Fixits
->Version
);
1076 for (const auto &QF
: Fixits
->QuickFixes
) {
1077 CAs
.push_back(toCodeAction(QF
.F
, File
, Version
, SupportsDocumentChanges
,
1078 SupportsChangeAnnotation
));
1079 if (auto It
= ToLSPDiags
.find(QF
.Diag
);
1080 It
!= ToLSPDiags
.end()) {
1081 CAs
.back().diagnostics
= {It
->second
};
1085 for (const auto &R
: Fixits
->Renames
)
1086 CAs
.push_back(toCodeAction(R
, File
));
1088 for (const auto &TR
: Fixits
->TweakRefs
)
1089 CAs
.push_back(toCodeAction(TR
, File
, Selection
));
1091 // If there's exactly one quick-fix, call it "preferred".
1092 // We never consider refactorings etc as preferred.
1093 CodeAction
*OnlyFix
= nullptr;
1094 for (auto &Action
: CAs
) {
1095 if (Action
.kind
&& *Action
.kind
== CodeAction::QUICKFIX_KIND
) {
1104 OnlyFix
->isPreferred
= true;
1105 if (ToLSPDiags
.size() == 1 &&
1106 ToLSPDiags
.begin()->second
.range
== Selection
)
1107 OnlyFix
->diagnostics
= {ToLSPDiags
.begin()->second
};
1110 if (SupportsCodeAction
)
1111 return Reply(llvm::json::Array(CAs
));
1112 std::vector
<Command
> Commands
;
1113 for (const auto &Action
: CAs
) {
1114 if (auto Command
= asCommand(Action
))
1115 Commands
.push_back(std::move(*Command
));
1117 return Reply(llvm::json::Array(Commands
));
1119 Server
->codeAction(Inputs
, std::move(CB
));
1122 void ClangdLSPServer::onCompletion(const CompletionParams
&Params
,
1123 Callback
<CompletionList
> Reply
) {
1124 if (!shouldRunCompletion(Params
)) {
1125 // Clients sometimes auto-trigger completions in undesired places (e.g.
1126 // 'a >^ '), we return empty results in those cases.
1127 vlog("ignored auto-triggered completion, preceding char did not match");
1128 return Reply(CompletionList());
1130 auto Opts
= this->Opts
.CodeComplete
;
1131 if (Params
.limit
&& *Params
.limit
>= 0)
1132 Opts
.Limit
= *Params
.limit
;
1133 Server
->codeComplete(Params
.textDocument
.uri
.file(), Params
.position
, Opts
,
1134 [Reply
= std::move(Reply
), Opts
,
1135 this](llvm::Expected
<CodeCompleteResult
> List
) mutable {
1137 return Reply(List
.takeError());
1138 CompletionList LSPList
;
1139 LSPList
.isIncomplete
= List
->HasMore
;
1140 for (const auto &R
: List
->Completions
) {
1141 CompletionItem C
= R
.render(Opts
);
1142 C
.kind
= adjustKindToCapability(
1143 C
.kind
, SupportedCompletionItemKinds
);
1144 if (!SupportsCompletionLabelDetails
)
1145 removeCompletionLabelDetails(C
);
1146 LSPList
.items
.push_back(std::move(C
));
1148 return Reply(std::move(LSPList
));
1152 void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams
&Params
,
1153 Callback
<SignatureHelp
> Reply
) {
1154 Server
->signatureHelp(Params
.textDocument
.uri
.file(), Params
.position
,
1155 Opts
.SignatureHelpDocumentationFormat
,
1156 [Reply
= std::move(Reply
), this](
1157 llvm::Expected
<SignatureHelp
> Signature
) mutable {
1159 return Reply(Signature
.takeError());
1160 if (SupportsOffsetsInSignatureHelp
)
1161 return Reply(std::move(*Signature
));
1162 // Strip out the offsets from signature help for
1163 // clients that only support string labels.
1164 for (auto &SigInfo
: Signature
->signatures
) {
1165 for (auto &Param
: SigInfo
.parameters
)
1166 Param
.labelOffsets
.reset();
1168 return Reply(std::move(*Signature
));
1172 // Go to definition has a toggle function: if def and decl are distinct, then
1173 // the first press gives you the def, the second gives you the matching def.
1174 // getToggle() returns the counterpart location that under the cursor.
1176 // We return the toggled location alone (ignoring other symbols) to encourage
1177 // editors to "bounce" quickly between locations, without showing a menu.
1178 static Location
*getToggle(const TextDocumentPositionParams
&Point
,
1179 LocatedSymbol
&Sym
) {
1180 // Toggle only makes sense with two distinct locations.
1181 if (!Sym
.Definition
|| *Sym
.Definition
== Sym
.PreferredDeclaration
)
1183 if (Sym
.Definition
->uri
.file() == Point
.textDocument
.uri
.file() &&
1184 Sym
.Definition
->range
.contains(Point
.position
))
1185 return &Sym
.PreferredDeclaration
;
1186 if (Sym
.PreferredDeclaration
.uri
.file() == Point
.textDocument
.uri
.file() &&
1187 Sym
.PreferredDeclaration
.range
.contains(Point
.position
))
1188 return &*Sym
.Definition
;
1192 void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams
&Params
,
1193 Callback
<std::vector
<Location
>> Reply
) {
1194 Server
->locateSymbolAt(
1195 Params
.textDocument
.uri
.file(), Params
.position
,
1196 [Params
, Reply
= std::move(Reply
)](
1197 llvm::Expected
<std::vector
<LocatedSymbol
>> Symbols
) mutable {
1199 return Reply(Symbols
.takeError());
1200 std::vector
<Location
> Defs
;
1201 for (auto &S
: *Symbols
) {
1202 if (Location
*Toggle
= getToggle(Params
, S
))
1203 return Reply(std::vector
<Location
>{std::move(*Toggle
)});
1204 Defs
.push_back(S
.Definition
.value_or(S
.PreferredDeclaration
));
1206 Reply(std::move(Defs
));
1210 void ClangdLSPServer::onGoToDeclaration(
1211 const TextDocumentPositionParams
&Params
,
1212 Callback
<std::vector
<Location
>> Reply
) {
1213 Server
->locateSymbolAt(
1214 Params
.textDocument
.uri
.file(), Params
.position
,
1215 [Params
, Reply
= std::move(Reply
)](
1216 llvm::Expected
<std::vector
<LocatedSymbol
>> Symbols
) mutable {
1218 return Reply(Symbols
.takeError());
1219 std::vector
<Location
> Decls
;
1220 for (auto &S
: *Symbols
) {
1221 if (Location
*Toggle
= getToggle(Params
, S
))
1222 return Reply(std::vector
<Location
>{std::move(*Toggle
)});
1223 Decls
.push_back(std::move(S
.PreferredDeclaration
));
1225 Reply(std::move(Decls
));
1229 void ClangdLSPServer::onSwitchSourceHeader(
1230 const TextDocumentIdentifier
&Params
,
1231 Callback
<std::optional
<URIForFile
>> Reply
) {
1232 Server
->switchSourceHeader(
1234 [Reply
= std::move(Reply
),
1235 Params
](llvm::Expected
<std::optional
<clangd::Path
>> Path
) mutable {
1237 return Reply(Path
.takeError());
1239 return Reply(URIForFile::canonicalize(**Path
, Params
.uri
.file()));
1240 return Reply(std::nullopt
);
1244 void ClangdLSPServer::onDocumentHighlight(
1245 const TextDocumentPositionParams
&Params
,
1246 Callback
<std::vector
<DocumentHighlight
>> Reply
) {
1247 Server
->findDocumentHighlights(Params
.textDocument
.uri
.file(),
1248 Params
.position
, std::move(Reply
));
1251 void ClangdLSPServer::onHover(const TextDocumentPositionParams
&Params
,
1252 Callback
<std::optional
<Hover
>> Reply
) {
1253 Server
->findHover(Params
.textDocument
.uri
.file(), Params
.position
,
1254 [Reply
= std::move(Reply
),
1255 this](llvm::Expected
<std::optional
<HoverInfo
>> H
) mutable {
1257 return Reply(H
.takeError());
1259 return Reply(std::nullopt
);
1262 R
.contents
.kind
= HoverContentFormat
;
1263 R
.range
= (*H
)->SymRange
;
1264 switch (HoverContentFormat
) {
1265 case MarkupKind::PlainText
:
1266 R
.contents
.value
= (*H
)->present().asPlainText();
1267 return Reply(std::move(R
));
1268 case MarkupKind::Markdown
:
1269 R
.contents
.value
= (*H
)->present().asMarkdown();
1270 return Reply(std::move(R
));
1272 llvm_unreachable("unhandled MarkupKind");
1276 // Our extension has a different representation on the wire than the standard.
1277 // https://clangd.llvm.org/extensions#type-hierarchy
1278 llvm::json::Value
serializeTHIForExtension(TypeHierarchyItem THI
) {
1279 llvm::json::Object Result
{{
1280 {"name", std::move(THI
.name
)},
1281 {"kind", static_cast<int>(THI
.kind
)},
1282 {"uri", std::move(THI
.uri
)},
1283 {"range", THI
.range
},
1284 {"selectionRange", THI
.selectionRange
},
1285 {"data", std::move(THI
.data
)},
1288 Result
["deprecated"] = THI
.deprecated
;
1290 Result
["detail"] = std::move(*THI
.detail
);
1293 llvm::json::Array Parents
;
1294 for (auto &Parent
: *THI
.parents
)
1295 Parents
.emplace_back(serializeTHIForExtension(std::move(Parent
)));
1296 Result
["parents"] = std::move(Parents
);
1300 llvm::json::Array Children
;
1301 for (auto &child
: *THI
.children
)
1302 Children
.emplace_back(serializeTHIForExtension(std::move(child
)));
1303 Result
["children"] = std::move(Children
);
1308 void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams
&Params
,
1309 Callback
<llvm::json::Value
> Reply
) {
1311 [Reply
= std::move(Reply
)](
1312 llvm::Expected
<std::vector
<TypeHierarchyItem
>> Resp
) mutable {
1314 Reply(Resp
.takeError());
1317 if (Resp
->empty()) {
1321 Reply(serializeTHIForExtension(std::move(Resp
->front())));
1323 Server
->typeHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1324 Params
.resolve
, Params
.direction
, std::move(Serialize
));
1327 void ClangdLSPServer::onResolveTypeHierarchy(
1328 const ResolveTypeHierarchyItemParams
&Params
,
1329 Callback
<llvm::json::Value
> Reply
) {
1331 [Reply
= std::move(Reply
)](
1332 llvm::Expected
<std::optional
<TypeHierarchyItem
>> Resp
) mutable {
1334 Reply(Resp
.takeError());
1338 Reply(std::move(*Resp
));
1341 Reply(serializeTHIForExtension(std::move(**Resp
)));
1343 Server
->resolveTypeHierarchy(Params
.item
, Params
.resolve
, Params
.direction
,
1344 std::move(Serialize
));
1347 void ClangdLSPServer::onPrepareTypeHierarchy(
1348 const TypeHierarchyPrepareParams
&Params
,
1349 Callback
<std::vector
<TypeHierarchyItem
>> Reply
) {
1350 Server
->typeHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1351 Params
.resolve
, Params
.direction
, std::move(Reply
));
1354 void ClangdLSPServer::onSuperTypes(
1355 const ResolveTypeHierarchyItemParams
&Params
,
1356 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> Reply
) {
1357 Server
->superTypes(Params
.item
, std::move(Reply
));
1360 void ClangdLSPServer::onSubTypes(
1361 const ResolveTypeHierarchyItemParams
&Params
,
1362 Callback
<std::vector
<TypeHierarchyItem
>> Reply
) {
1363 Server
->subTypes(Params
.item
, std::move(Reply
));
1366 void ClangdLSPServer::onPrepareCallHierarchy(
1367 const CallHierarchyPrepareParams
&Params
,
1368 Callback
<std::vector
<CallHierarchyItem
>> Reply
) {
1369 Server
->prepareCallHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1373 void ClangdLSPServer::onCallHierarchyIncomingCalls(
1374 const CallHierarchyIncomingCallsParams
&Params
,
1375 Callback
<std::vector
<CallHierarchyIncomingCall
>> Reply
) {
1376 Server
->incomingCalls(Params
.item
, std::move(Reply
));
1379 void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams
&Params
,
1380 Callback
<llvm::json::Value
> Reply
) {
1381 // Our extension has a different representation on the wire than the standard.
1382 // We have a "range" property and "kind" is represented as a string, not as an
1384 // https://clangd.llvm.org/extensions#inlay-hints
1385 auto Serialize
= [Reply
= std::move(Reply
)](
1386 llvm::Expected
<std::vector
<InlayHint
>> Hints
) mutable {
1388 Reply(Hints
.takeError());
1391 llvm::json::Array Result
;
1392 Result
.reserve(Hints
->size());
1393 for (auto &Hint
: *Hints
) {
1394 Result
.emplace_back(llvm::json::Object
{
1395 {"kind", llvm::to_string(Hint
.kind
)},
1396 {"range", Hint
.range
},
1397 {"position", Hint
.position
},
1398 // Extension doesn't have paddingLeft/Right so adjust the label
1401 ((Hint
.paddingLeft
? " " : "") + llvm::StringRef(Hint
.joinLabels()) +
1402 (Hint
.paddingRight
? " " : ""))
1406 Reply(std::move(Result
));
1408 Server
->inlayHints(Params
.textDocument
.uri
.file(), Params
.range
,
1409 std::move(Serialize
));
1412 void ClangdLSPServer::onInlayHint(const InlayHintsParams
&Params
,
1413 Callback
<std::vector
<InlayHint
>> Reply
) {
1414 Server
->inlayHints(Params
.textDocument
.uri
.file(), Params
.range
,
1418 void ClangdLSPServer::applyConfiguration(
1419 const ConfigurationSettings
&Settings
) {
1420 // Per-file update to the compilation database.
1421 llvm::StringSet
<> ModifiedFiles
;
1422 for (auto &[File
, Command
] : Settings
.compilationDatabaseChanges
) {
1424 tooling::CompileCommand(std::move(Command
.workingDirectory
), File
,
1425 std::move(Command
.compilationCommand
),
1427 if (CDB
->setCompileCommand(File
, std::move(Cmd
))) {
1428 ModifiedFiles
.insert(File
);
1432 Server
->reparseOpenFilesIfNeeded(
1433 [&](llvm::StringRef File
) { return ModifiedFiles
.count(File
) != 0; });
1436 void ClangdLSPServer::maybeExportMemoryProfile() {
1437 if (!trace::enabled() || !ShouldProfile())
1440 static constexpr trace::Metric
MemoryUsage(
1441 "memory_usage", trace::Metric::Value
, "component_name");
1442 trace::Span
Tracer("ProfileBrief");
1445 record(MT
, "clangd_lsp_server", MemoryUsage
);
1448 void ClangdLSPServer::maybeCleanupMemory() {
1449 if (!Opts
.MemoryCleanup
|| !ShouldCleanupMemory())
1451 Opts
.MemoryCleanup();
1454 // FIXME: This function needs to be properly tested.
1455 void ClangdLSPServer::onChangeConfiguration(
1456 const DidChangeConfigurationParams
&Params
) {
1457 applyConfiguration(Params
.settings
);
1460 void ClangdLSPServer::onReference(
1461 const ReferenceParams
&Params
,
1462 Callback
<std::vector
<ReferenceLocation
>> Reply
) {
1463 Server
->findReferences(Params
.textDocument
.uri
.file(), Params
.position
,
1464 Opts
.ReferencesLimit
, SupportsReferenceContainer
,
1465 [Reply
= std::move(Reply
),
1466 IncludeDecl(Params
.context
.includeDeclaration
)](
1467 llvm::Expected
<ReferencesResult
> Refs
) mutable {
1469 return Reply(Refs
.takeError());
1470 // Filter out declarations if the client asked.
1471 std::vector
<ReferenceLocation
> Result
;
1472 Result
.reserve(Refs
->References
.size());
1473 for (auto &Ref
: Refs
->References
) {
1475 Ref
.Attributes
& ReferencesResult::Declaration
;
1476 if (IncludeDecl
|| !IsDecl
)
1477 Result
.push_back(std::move(Ref
.Loc
));
1479 return Reply(std::move(Result
));
1483 void ClangdLSPServer::onGoToType(const TextDocumentPositionParams
&Params
,
1484 Callback
<std::vector
<Location
>> Reply
) {
1486 Params
.textDocument
.uri
.file(), Params
.position
,
1487 [Reply
= std::move(Reply
)](
1488 llvm::Expected
<std::vector
<LocatedSymbol
>> Types
) mutable {
1490 return Reply(Types
.takeError());
1491 std::vector
<Location
> Response
;
1492 for (const LocatedSymbol
&Sym
: *Types
)
1493 Response
.push_back(Sym
.Definition
.value_or(Sym
.PreferredDeclaration
));
1494 return Reply(std::move(Response
));
1498 void ClangdLSPServer::onGoToImplementation(
1499 const TextDocumentPositionParams
&Params
,
1500 Callback
<std::vector
<Location
>> Reply
) {
1501 Server
->findImplementations(
1502 Params
.textDocument
.uri
.file(), Params
.position
,
1503 [Reply
= std::move(Reply
)](
1504 llvm::Expected
<std::vector
<LocatedSymbol
>> Overrides
) mutable {
1506 return Reply(Overrides
.takeError());
1507 std::vector
<Location
> Impls
;
1508 for (const LocatedSymbol
&Sym
: *Overrides
)
1509 Impls
.push_back(Sym
.Definition
.value_or(Sym
.PreferredDeclaration
));
1510 return Reply(std::move(Impls
));
1514 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams
&Params
,
1515 Callback
<std::vector
<SymbolDetails
>> Reply
) {
1516 Server
->symbolInfo(Params
.textDocument
.uri
.file(), Params
.position
,
1520 void ClangdLSPServer::onSelectionRange(
1521 const SelectionRangeParams
&Params
,
1522 Callback
<std::vector
<SelectionRange
>> Reply
) {
1523 Server
->semanticRanges(
1524 Params
.textDocument
.uri
.file(), Params
.positions
,
1525 [Reply
= std::move(Reply
)](
1526 llvm::Expected
<std::vector
<SelectionRange
>> Ranges
) mutable {
1528 return Reply(Ranges
.takeError());
1529 return Reply(std::move(*Ranges
));
1533 void ClangdLSPServer::onDocumentLink(
1534 const DocumentLinkParams
&Params
,
1535 Callback
<std::vector
<DocumentLink
>> Reply
) {
1537 // TODO(forster): This currently resolves all targets eagerly. This is slow,
1538 // because it blocks on the preamble/AST being built. We could respond to the
1539 // request faster by using string matching or the lexer to find the includes
1540 // and resolving the targets lazily.
1541 Server
->documentLinks(
1542 Params
.textDocument
.uri
.file(),
1543 [Reply
= std::move(Reply
)](
1544 llvm::Expected
<std::vector
<DocumentLink
>> Links
) mutable {
1546 return Reply(Links
.takeError());
1548 return Reply(std::move(Links
));
1552 // Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ...
1553 static void increment(std::string
&S
) {
1554 for (char &C
: llvm::reverse(S
)) {
1561 S
.insert(S
.begin(), '1');
1564 void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams
&Params
,
1565 Callback
<SemanticTokens
> CB
) {
1566 auto File
= Params
.textDocument
.uri
.file();
1567 Server
->semanticHighlights(
1568 Params
.textDocument
.uri
.file(),
1569 [this, File(File
.str()), CB(std::move(CB
)), Code(Server
->getDraft(File
))](
1570 llvm::Expected
<std::vector
<HighlightingToken
>> HT
) mutable {
1572 return CB(HT
.takeError());
1573 SemanticTokens Result
;
1574 Result
.tokens
= toSemanticTokens(*HT
, *Code
);
1576 std::lock_guard
<std::mutex
> Lock(SemanticTokensMutex
);
1577 auto &Last
= LastSemanticTokens
[File
];
1579 Last
.tokens
= Result
.tokens
;
1580 increment(Last
.resultId
);
1581 Result
.resultId
= Last
.resultId
;
1583 CB(std::move(Result
));
1587 void ClangdLSPServer::onSemanticTokensDelta(
1588 const SemanticTokensDeltaParams
&Params
,
1589 Callback
<SemanticTokensOrDelta
> CB
) {
1590 auto File
= Params
.textDocument
.uri
.file();
1591 Server
->semanticHighlights(
1592 Params
.textDocument
.uri
.file(),
1593 [this, PrevResultID(Params
.previousResultId
), File(File
.str()),
1594 CB(std::move(CB
)), Code(Server
->getDraft(File
))](
1595 llvm::Expected
<std::vector
<HighlightingToken
>> HT
) mutable {
1597 return CB(HT
.takeError());
1598 std::vector
<SemanticToken
> Toks
= toSemanticTokens(*HT
, *Code
);
1600 SemanticTokensOrDelta Result
;
1602 std::lock_guard
<std::mutex
> Lock(SemanticTokensMutex
);
1603 auto &Last
= LastSemanticTokens
[File
];
1605 if (PrevResultID
== Last
.resultId
) {
1606 Result
.edits
= diffTokens(Last
.tokens
, Toks
);
1608 vlog("semanticTokens/full/delta: wanted edits vs {0} but last "
1609 "result had ID {1}. Returning full token list.",
1610 PrevResultID
, Last
.resultId
);
1611 Result
.tokens
= Toks
;
1614 Last
.tokens
= std::move(Toks
);
1615 increment(Last
.resultId
);
1616 Result
.resultId
= Last
.resultId
;
1619 CB(std::move(Result
));
1623 void ClangdLSPServer::onMemoryUsage(const NoParams
&,
1624 Callback
<MemoryTree
> Reply
) {
1625 llvm::BumpPtrAllocator DetailAlloc
;
1626 MemoryTree
MT(&DetailAlloc
);
1628 Reply(std::move(MT
));
1631 void ClangdLSPServer::onAST(const ASTParams
&Params
,
1632 Callback
<std::optional
<ASTNode
>> CB
) {
1633 Server
->getAST(Params
.textDocument
.uri
.file(), Params
.range
, std::move(CB
));
1636 ClangdLSPServer::ClangdLSPServer(Transport
&Transp
, const ThreadsafeFS
&TFS
,
1637 const ClangdLSPServer::Options
&Opts
)
1638 : ShouldProfile(/*Period=*/std::chrono::minutes(5),
1639 /*Delay=*/std::chrono::minutes(1)),
1640 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1),
1641 /*Delay=*/std::chrono::minutes(1)),
1642 BackgroundContext(Context::current().clone()), Transp(Transp
),
1643 MsgHandler(new MessageHandler(*this)), TFS(TFS
),
1644 SupportedSymbolKinds(defaultSymbolKinds()),
1645 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts
) {
1646 if (Opts
.ConfigProvider
) {
1647 assert(!Opts
.ContextProvider
&&
1648 "Only one of ConfigProvider and ContextProvider allowed!");
1649 this->Opts
.ContextProvider
= ClangdServer::createConfiguredContextProvider(
1650 Opts
.ConfigProvider
, this);
1652 LSPBinder
Bind(this->Handlers
, *this);
1653 Bind
.method("initialize", this, &ClangdLSPServer::onInitialize
);
1656 void ClangdLSPServer::bindMethods(LSPBinder
&Bind
,
1657 const ClientCapabilities
&Caps
) {
1659 Bind
.notification("initialized", this, &ClangdLSPServer::onInitialized
);
1660 Bind
.method("shutdown", this, &ClangdLSPServer::onShutdown
);
1661 Bind
.method("sync", this, &ClangdLSPServer::onSync
);
1662 Bind
.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting
);
1663 Bind
.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting
);
1664 Bind
.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting
);
1665 Bind
.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction
);
1666 Bind
.method("textDocument/completion", this, &ClangdLSPServer::onCompletion
);
1667 Bind
.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp
);
1668 Bind
.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition
);
1669 Bind
.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration
);
1670 Bind
.method("textDocument/typeDefinition", this, &ClangdLSPServer::onGoToType
);
1671 Bind
.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation
);
1672 Bind
.method("textDocument/references", this, &ClangdLSPServer::onReference
);
1673 Bind
.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader
);
1674 Bind
.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename
);
1675 Bind
.method("textDocument/rename", this, &ClangdLSPServer::onRename
);
1676 Bind
.method("textDocument/hover", this, &ClangdLSPServer::onHover
);
1677 Bind
.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol
);
1678 Bind
.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand
);
1679 Bind
.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight
);
1680 Bind
.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol
);
1681 Bind
.method("textDocument/ast", this, &ClangdLSPServer::onAST
);
1682 Bind
.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen
);
1683 Bind
.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose
);
1684 Bind
.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange
);
1685 Bind
.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave
);
1686 Bind
.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent
);
1687 Bind
.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration
);
1688 Bind
.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo
);
1689 Bind
.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy
);
1690 Bind
.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy
);
1691 Bind
.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy
);
1692 Bind
.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes
);
1693 Bind
.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes
);
1694 Bind
.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy
);
1695 Bind
.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls
);
1696 Bind
.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange
);
1697 Bind
.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink
);
1698 Bind
.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens
);
1699 Bind
.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta
);
1700 Bind
.method("clangd/inlayHints", this, &ClangdLSPServer::onClangdInlayHints
);
1701 Bind
.method("textDocument/inlayHint", this, &ClangdLSPServer::onInlayHint
);
1702 Bind
.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage
);
1703 Bind
.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange
);
1704 Bind
.command(ApplyFixCommand
, this, &ClangdLSPServer::onCommandApplyEdit
);
1705 Bind
.command(ApplyTweakCommand
, this, &ClangdLSPServer::onCommandApplyTweak
);
1706 Bind
.command(ApplyRenameCommand
, this, &ClangdLSPServer::onCommandApplyRename
);
1708 ApplyWorkspaceEdit
= Bind
.outgoingMethod("workspace/applyEdit");
1709 PublishDiagnostics
= Bind
.outgoingNotification("textDocument/publishDiagnostics");
1710 if (Caps
.InactiveRegions
)
1711 PublishInactiveRegions
= Bind
.outgoingNotification("textDocument/inactiveRegions");
1712 ShowMessage
= Bind
.outgoingNotification("window/showMessage");
1713 NotifyFileStatus
= Bind
.outgoingNotification("textDocument/clangd.fileStatus");
1714 CreateWorkDoneProgress
= Bind
.outgoingMethod("window/workDoneProgress/create");
1715 BeginWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1716 ReportWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1717 EndWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1718 if(Caps
.SemanticTokenRefreshSupport
)
1719 SemanticTokensRefresh
= Bind
.outgoingMethod("workspace/semanticTokens/refresh");
1723 ClangdLSPServer::~ClangdLSPServer() {
1724 IsBeingDestroyed
= true;
1725 // Explicitly destroy ClangdServer first, blocking on threads it owns.
1726 // This ensures they don't access any other members.
1730 bool ClangdLSPServer::run() {
1731 // Run the Language Server loop.
1732 bool CleanExit
= true;
1733 if (auto Err
= Transp
.loop(*MsgHandler
)) {
1734 elog("Transport error: {0}", std::move(Err
));
1738 return CleanExit
&& ShutdownRequestReceived
;
1741 void ClangdLSPServer::profile(MemoryTree
&MT
) const {
1743 Server
->profile(MT
.child("clangd_server"));
1746 std::optional
<ClangdServer::DiagRef
>
1747 ClangdLSPServer::getDiagRef(StringRef File
, const clangd::Diagnostic
&D
) {
1748 std::lock_guard
<std::mutex
> Lock(DiagRefMutex
);
1749 auto DiagToDiagRefIter
= DiagRefMap
.find(File
);
1750 if (DiagToDiagRefIter
== DiagRefMap
.end())
1751 return std::nullopt
;
1753 const auto &DiagToDiagRefMap
= DiagToDiagRefIter
->second
;
1754 auto FixItsIter
= DiagToDiagRefMap
.find(toDiagKey(D
));
1755 if (FixItsIter
== DiagToDiagRefMap
.end())
1756 return std::nullopt
;
1758 return FixItsIter
->second
;
1761 // A completion request is sent when the user types '>' or ':', but we only
1762 // want to trigger on '->' and '::'. We check the preceding text to make
1763 // sure it matches what we expected.
1764 // Running the lexer here would be more robust (e.g. we can detect comments
1765 // and avoid triggering completion there), but we choose to err on the side
1766 // of simplicity here.
1767 bool ClangdLSPServer::shouldRunCompletion(
1768 const CompletionParams
&Params
) const {
1769 if (Params
.context
.triggerKind
!= CompletionTriggerKind::TriggerCharacter
)
1771 auto Code
= Server
->getDraft(Params
.textDocument
.uri
.file());
1773 return true; // completion code will log the error for untracked doc.
1774 auto Offset
= positionToOffset(*Code
, Params
.position
,
1775 /*AllowColumnsBeyondLineLength=*/false);
1777 vlog("could not convert position '{0}' to offset for file '{1}'",
1778 Params
.position
, Params
.textDocument
.uri
.file());
1781 return allowImplicitCompletion(*Code
, *Offset
);
1784 void ClangdLSPServer::onDiagnosticsReady(PathRef File
, llvm::StringRef Version
,
1785 llvm::ArrayRef
<Diag
> Diagnostics
) {
1786 PublishDiagnosticsParams Notification
;
1787 Notification
.version
= decodeVersion(Version
);
1788 Notification
.uri
= URIForFile::canonicalize(File
, /*TUPath=*/File
);
1789 DiagnosticToDiagRefMap LocalDiagMap
; // Temporary storage
1790 for (auto &Diag
: Diagnostics
) {
1791 toLSPDiags(Diag
, Notification
.uri
, DiagOpts
,
1792 [&](clangd::Diagnostic LSPDiag
, llvm::ArrayRef
<Fix
> Fixes
) {
1793 if (DiagOpts
.EmbedFixesInDiagnostics
) {
1794 std::vector
<CodeAction
> CodeActions
;
1795 for (const auto &Fix
: Fixes
)
1796 CodeActions
.push_back(toCodeAction(
1797 Fix
, Notification
.uri
, Notification
.version
,
1798 SupportsDocumentChanges
, SupportsChangeAnnotation
));
1799 LSPDiag
.codeActions
.emplace(std::move(CodeActions
));
1800 if (LSPDiag
.codeActions
->size() == 1)
1801 LSPDiag
.codeActions
->front().isPreferred
= true;
1803 LocalDiagMap
[toDiagKey(LSPDiag
)] = {Diag
.Range
, Diag
.Message
};
1804 Notification
.diagnostics
.push_back(std::move(LSPDiag
));
1810 std::lock_guard
<std::mutex
> Lock(DiagRefMutex
);
1811 DiagRefMap
[File
] = LocalDiagMap
;
1814 // Send a notification to the LSP client.
1815 PublishDiagnostics(Notification
);
1818 void ClangdLSPServer::onInactiveRegionsReady(
1819 PathRef File
, std::vector
<Range
> InactiveRegions
) {
1820 InactiveRegionsParams Notification
;
1821 Notification
.TextDocument
= {URIForFile::canonicalize(File
, /*TUPath=*/File
)};
1822 Notification
.InactiveRegions
= std::move(InactiveRegions
);
1824 PublishInactiveRegions(Notification
);
1827 void ClangdLSPServer::onBackgroundIndexProgress(
1828 const BackgroundQueue::Stats
&Stats
) {
1829 static const char ProgressToken
[] = "backgroundIndexProgress";
1831 // The background index did some work, maybe we need to cleanup
1832 maybeCleanupMemory();
1834 std::lock_guard
<std::mutex
> Lock(BackgroundIndexProgressMutex
);
1836 auto NotifyProgress
= [this](const BackgroundQueue::Stats
&Stats
) {
1837 if (BackgroundIndexProgressState
!= BackgroundIndexProgress::Live
) {
1838 WorkDoneProgressBegin Begin
;
1839 Begin
.percentage
= true;
1840 Begin
.title
= "indexing";
1841 BeginWorkDoneProgress({ProgressToken
, std::move(Begin
)});
1842 BackgroundIndexProgressState
= BackgroundIndexProgress::Live
;
1845 if (Stats
.Completed
< Stats
.Enqueued
) {
1846 assert(Stats
.Enqueued
> Stats
.LastIdle
);
1847 WorkDoneProgressReport Report
;
1848 Report
.percentage
= 100 * (Stats
.Completed
- Stats
.LastIdle
) /
1849 (Stats
.Enqueued
- Stats
.LastIdle
);
1851 llvm::formatv("{0}/{1}", Stats
.Completed
- Stats
.LastIdle
,
1852 Stats
.Enqueued
- Stats
.LastIdle
);
1853 ReportWorkDoneProgress({ProgressToken
, std::move(Report
)});
1855 assert(Stats
.Completed
== Stats
.Enqueued
);
1856 EndWorkDoneProgress({ProgressToken
, WorkDoneProgressEnd()});
1857 BackgroundIndexProgressState
= BackgroundIndexProgress::Empty
;
1861 switch (BackgroundIndexProgressState
) {
1862 case BackgroundIndexProgress::Unsupported
:
1864 case BackgroundIndexProgress::Creating
:
1865 // Cache this update for when the progress bar is available.
1866 PendingBackgroundIndexProgress
= Stats
;
1868 case BackgroundIndexProgress::Empty
: {
1869 if (BackgroundIndexSkipCreate
) {
1870 NotifyProgress(Stats
);
1873 // Cache this update for when the progress bar is available.
1874 PendingBackgroundIndexProgress
= Stats
;
1875 BackgroundIndexProgressState
= BackgroundIndexProgress::Creating
;
1876 WorkDoneProgressCreateParams CreateRequest
;
1877 CreateRequest
.token
= ProgressToken
;
1878 CreateWorkDoneProgress(
1880 [this, NotifyProgress
](llvm::Expected
<std::nullptr_t
> E
) {
1881 std::lock_guard
<std::mutex
> Lock(BackgroundIndexProgressMutex
);
1883 NotifyProgress(this->PendingBackgroundIndexProgress
);
1885 elog("Failed to create background index progress bar: {0}",
1887 // give up forever rather than thrashing about
1888 BackgroundIndexProgressState
= BackgroundIndexProgress::Unsupported
;
1893 case BackgroundIndexProgress::Live
:
1894 NotifyProgress(Stats
);
1899 void ClangdLSPServer::onFileUpdated(PathRef File
, const TUStatus
&Status
) {
1900 if (!SupportFileStatus
)
1902 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these
1903 // two statuses are running faster in practice, which leads the UI constantly
1904 // changing, and doesn't provide much value. We may want to emit status at a
1905 // reasonable time interval (e.g. 0.5s).
1906 if (Status
.PreambleActivity
== PreambleAction::Idle
&&
1907 (Status
.ASTActivity
.K
== ASTAction::Building
||
1908 Status
.ASTActivity
.K
== ASTAction::RunningAction
))
1910 NotifyFileStatus(Status
.render(File
));
1913 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File
) {
1914 if (SemanticTokensRefresh
) {
1915 SemanticTokensRefresh(NoParams
{}, [](llvm::Expected
<std::nullptr_t
> E
) {
1918 elog("Failed to refresh semantic tokens: {0}", E
.takeError());
1923 } // namespace clangd
1924 } // namespace clang