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"
18 #include "SemanticHighlighting.h"
19 #include "SourceCode.h"
20 #include "TUScheduler.h"
22 #include "refactor/Tweak.h"
23 #include "support/Cancellation.h"
24 #include "support/Context.h"
25 #include "support/MemoryTree.h"
26 #include "support/Trace.h"
27 #include "clang/Tooling/Core/Replacement.h"
28 #include "llvm/ADT/ArrayRef.h"
29 #include "llvm/ADT/FunctionExtras.h"
30 #include "llvm/ADT/ScopeExit.h"
31 #include "llvm/ADT/StringRef.h"
32 #include "llvm/ADT/Twine.h"
33 #include "llvm/Support/Allocator.h"
34 #include "llvm/Support/Error.h"
35 #include "llvm/Support/FormatVariadic.h"
36 #include "llvm/Support/JSON.h"
37 #include "llvm/Support/SHA1.h"
38 #include "llvm/Support/ScopedPrinter.h"
39 #include "llvm/Support/raw_ostream.h"
54 // Tracks end-to-end latency of high level lsp calls. Measurements are in
56 constexpr trace::Metric
LSPLatency("lsp_latency", trace::Metric::Distribution
,
59 // LSP defines file versions as numbers that increase.
60 // ClangdServer treats them as opaque and therefore uses strings instead.
61 std::string
encodeVersion(std::optional
<int64_t> LSPVersion
) {
62 return LSPVersion
? llvm::to_string(*LSPVersion
) : "";
64 std::optional
<int64_t> decodeVersion(llvm::StringRef Encoded
) {
66 if (llvm::to_integer(Encoded
, Result
, 10))
68 if (!Encoded
.empty()) // Empty can be e.g. diagnostics on close.
69 elog("unexpected non-numeric version {0}", Encoded
);
73 const llvm::StringLiteral ApplyFixCommand
= "clangd.applyFix";
74 const llvm::StringLiteral ApplyTweakCommand
= "clangd.applyTweak";
76 /// Transforms a tweak into a code action that would apply it if executed.
77 /// EXPECTS: T.prepare() was called and returned true.
78 CodeAction
toCodeAction(const ClangdServer::TweakRef
&T
, const URIForFile
&File
,
82 CA
.kind
= T
.Kind
.str();
83 // This tweak may have an expensive second stage, we only run it if the user
84 // actually chooses it in the UI. We reply with a command that would run the
85 // corresponding tweak.
86 // FIXME: for some tweaks, computing the edits is cheap and we could send them
89 CA
.command
->title
= T
.Title
;
90 CA
.command
->command
= std::string(ApplyTweakCommand
);
94 Args
.selection
= Selection
;
95 CA
.command
->argument
= std::move(Args
);
99 /// Convert from Fix to LSP CodeAction.
100 CodeAction
toCodeAction(const Fix
&F
, const URIForFile
&File
,
101 const std::optional
<int64_t> &Version
,
102 bool SupportsDocumentChanges
,
103 bool SupportChangeAnnotation
) {
105 Action
.title
= F
.Message
;
106 Action
.kind
= std::string(CodeAction::QUICKFIX_KIND
);
107 Action
.edit
.emplace();
108 if (!SupportsDocumentChanges
) {
109 Action
.edit
->changes
.emplace();
110 auto &Changes
= (*Action
.edit
->changes
)[File
.uri()];
111 for (const auto &E
: F
.Edits
)
112 Changes
.push_back({E
.range
, E
.newText
, /*annotationId=*/""});
114 Action
.edit
->documentChanges
.emplace();
115 TextDocumentEdit
&Edit
= Action
.edit
->documentChanges
->emplace_back();
116 Edit
.textDocument
= VersionedTextDocumentIdentifier
{{File
}, Version
};
117 for (const auto &E
: F
.Edits
)
118 Edit
.edits
.push_back(
120 SupportChangeAnnotation
? E
.annotationId
: ""});
121 if (SupportChangeAnnotation
) {
122 for (const auto &[AID
, Annotation
]: F
.Annotations
)
123 Action
.edit
->changeAnnotations
[AID
] = Annotation
;
129 void adjustSymbolKinds(llvm::MutableArrayRef
<DocumentSymbol
> Syms
,
130 SymbolKindBitset Kinds
) {
131 for (auto &S
: Syms
) {
132 S
.kind
= adjustKindToCapability(S
.kind
, Kinds
);
133 adjustSymbolKinds(S
.children
, Kinds
);
137 SymbolKindBitset
defaultSymbolKinds() {
138 SymbolKindBitset Defaults
;
139 for (size_t I
= SymbolKindMin
; I
<= static_cast<size_t>(SymbolKind::Array
);
145 CompletionItemKindBitset
defaultCompletionItemKinds() {
146 CompletionItemKindBitset Defaults
;
147 for (size_t I
= CompletionItemKindMin
;
148 I
<= static_cast<size_t>(CompletionItemKind::Reference
); ++I
)
153 // Makes sure edits in \p FE are applicable to latest file contents reported by
154 // editor. If not generates an error message containing information about files
155 // that needs to be saved.
156 llvm::Error
validateEdits(const ClangdServer
&Server
, const FileEdits
&FE
) {
157 size_t InvalidFileCount
= 0;
158 llvm::StringRef LastInvalidFile
;
159 for (const auto &It
: FE
) {
160 if (auto Draft
= Server
.getDraft(It
.first())) {
161 // If the file is open in user's editor, make sure the version we
162 // saw and current version are compatible as this is the text that
163 // will be replaced by editors.
164 if (!It
.second
.canApplyTo(*Draft
)) {
166 LastInvalidFile
= It
.first();
170 if (!InvalidFileCount
)
171 return llvm::Error::success();
172 if (InvalidFileCount
== 1)
173 return error("File must be saved first: {0}", LastInvalidFile
);
174 return error("Files must be saved first: {0} (and {1} others)",
175 LastInvalidFile
, InvalidFileCount
- 1);
179 // MessageHandler dispatches incoming LSP messages.
180 // It handles cross-cutting concerns:
181 // - serializes/deserializes protocol objects to JSON
182 // - logging of inbound messages
183 // - cancellation handling
184 // - basic call tracing
185 // MessageHandler ensures that initialize() is called before any other handler.
186 class ClangdLSPServer::MessageHandler
: public Transport::MessageHandler
{
188 MessageHandler(ClangdLSPServer
&Server
) : Server(Server
) {}
190 bool onNotify(llvm::StringRef Method
, llvm::json::Value Params
) override
{
191 trace::Span
Tracer(Method
, LSPLatency
);
192 SPAN_ATTACH(Tracer
, "Params", Params
);
193 WithContext
HandlerContext(handlerContext());
194 log("<-- {0}", Method
);
195 if (Method
== "exit")
197 auto Handler
= Server
.Handlers
.NotificationHandlers
.find(Method
);
198 if (Handler
!= Server
.Handlers
.NotificationHandlers
.end()) {
199 Handler
->second(std::move(Params
));
200 Server
.maybeExportMemoryProfile();
201 Server
.maybeCleanupMemory();
202 } else if (!Server
.Server
) {
203 elog("Notification {0} before initialization", Method
);
204 } else if (Method
== "$/cancelRequest") {
205 onCancel(std::move(Params
));
207 log("unhandled notification {0}", Method
);
212 bool onCall(llvm::StringRef Method
, llvm::json::Value Params
,
213 llvm::json::Value ID
) override
{
214 WithContext
HandlerContext(handlerContext());
215 // Calls can be canceled by the client. Add cancellation context.
216 WithContext
WithCancel(cancelableRequestContext(ID
));
217 trace::Span
Tracer(Method
, LSPLatency
);
218 SPAN_ATTACH(Tracer
, "Params", Params
);
219 ReplyOnce
Reply(ID
, Method
, &Server
, Tracer
.Args
);
220 log("<-- {0}({1})", Method
, ID
);
221 auto Handler
= Server
.Handlers
.MethodHandlers
.find(Method
);
222 if (Handler
!= Server
.Handlers
.MethodHandlers
.end()) {
223 Handler
->second(std::move(Params
), std::move(Reply
));
224 } else if (!Server
.Server
) {
225 elog("Call {0} before initialization.", Method
);
226 Reply(llvm::make_error
<LSPError
>("server not initialized",
227 ErrorCode::ServerNotInitialized
));
229 Reply(llvm::make_error
<LSPError
>("method not found",
230 ErrorCode::MethodNotFound
));
235 bool onReply(llvm::json::Value ID
,
236 llvm::Expected
<llvm::json::Value
> Result
) override
{
237 WithContext
HandlerContext(handlerContext());
239 Callback
<llvm::json::Value
> ReplyHandler
= nullptr;
240 if (auto IntID
= ID
.getAsInteger()) {
241 std::lock_guard
<std::mutex
> Mutex(CallMutex
);
242 // Find a corresponding callback for the request ID;
243 for (size_t Index
= 0; Index
< ReplyCallbacks
.size(); ++Index
) {
244 if (ReplyCallbacks
[Index
].first
== *IntID
) {
245 ReplyHandler
= std::move(ReplyCallbacks
[Index
].second
);
246 ReplyCallbacks
.erase(ReplyCallbacks
.begin() +
247 Index
); // remove the entry
254 // No callback being found, use a default log callback.
255 ReplyHandler
= [&ID
](llvm::Expected
<llvm::json::Value
> Result
) {
256 elog("received a reply with ID {0}, but there was no such call", ID
);
258 llvm::consumeError(Result
.takeError());
262 // Log and run the reply handler.
264 log("<-- reply({0})", ID
);
265 ReplyHandler(std::move(Result
));
267 auto Err
= Result
.takeError();
268 log("<-- reply({0}) error: {1}", ID
, Err
);
269 ReplyHandler(std::move(Err
));
274 // Bind a reply callback to a request. The callback will be invoked when
275 // clangd receives the reply from the LSP client.
276 // Return a call id of the request.
277 llvm::json::Value
bindReply(Callback
<llvm::json::Value
> Reply
) {
278 std::optional
<std::pair
<int, Callback
<llvm::json::Value
>>> OldestCB
;
281 std::lock_guard
<std::mutex
> Mutex(CallMutex
);
283 ReplyCallbacks
.emplace_back(ID
, std::move(Reply
));
285 // If the queue overflows, we assume that the client didn't reply the
286 // oldest request, and run the corresponding callback which replies an
287 // error to the client.
288 if (ReplyCallbacks
.size() > MaxReplayCallbacks
) {
289 elog("more than {0} outstanding LSP calls, forgetting about {1}",
290 MaxReplayCallbacks
, ReplyCallbacks
.front().first
);
291 OldestCB
= std::move(ReplyCallbacks
.front());
292 ReplyCallbacks
.pop_front();
297 error("failed to receive a client reply for request ({0})",
303 // Function object to reply to an LSP call.
304 // Each instance must be called exactly once, otherwise:
305 // - the bug is logged, and (in debug mode) an assert will fire
306 // - if there was no reply, an error reply is sent
307 // - if there were multiple replies, only the first is sent
309 std::atomic
<bool> Replied
= {false};
310 std::chrono::steady_clock::time_point Start
;
311 llvm::json::Value ID
;
313 ClangdLSPServer
*Server
; // Null when moved-from.
314 llvm::json::Object
*TraceArgs
;
317 ReplyOnce(const llvm::json::Value
&ID
, llvm::StringRef Method
,
318 ClangdLSPServer
*Server
, llvm::json::Object
*TraceArgs
)
319 : Start(std::chrono::steady_clock::now()), ID(ID
), Method(Method
),
320 Server(Server
), TraceArgs(TraceArgs
) {
323 ReplyOnce(ReplyOnce
&&Other
)
324 : Replied(Other
.Replied
.load()), Start(Other
.Start
),
325 ID(std::move(Other
.ID
)), Method(std::move(Other
.Method
)),
326 Server(Other
.Server
), TraceArgs(Other
.TraceArgs
) {
327 Other
.Server
= nullptr;
329 ReplyOnce
&operator=(ReplyOnce
&&) = delete;
330 ReplyOnce(const ReplyOnce
&) = delete;
331 ReplyOnce
&operator=(const ReplyOnce
&) = delete;
334 // There's one legitimate reason to never reply to a request: clangd's
335 // request handler send a call to the client (e.g. applyEdit) and the
336 // client never replied. In this case, the ReplyOnce is owned by
337 // ClangdLSPServer's reply callback table and is destroyed along with the
338 // server. We don't attempt to send a reply in this case, there's little
339 // to be gained from doing so.
340 if (Server
&& !Server
->IsBeingDestroyed
&& !Replied
) {
341 elog("No reply to message {0}({1})", Method
, ID
);
342 assert(false && "must reply to all calls!");
343 (*this)(llvm::make_error
<LSPError
>("server failed to reply",
344 ErrorCode::InternalError
));
348 void operator()(llvm::Expected
<llvm::json::Value
> Reply
) {
349 assert(Server
&& "moved-from!");
350 if (Replied
.exchange(true)) {
351 elog("Replied twice to message {0}({1})", Method
, ID
);
352 assert(false && "must reply to each call only once!");
355 auto Duration
= std::chrono::steady_clock::now() - Start
;
357 log("--> reply:{0}({1}) {2:ms}", Method
, ID
, Duration
);
359 (*TraceArgs
)["Reply"] = *Reply
;
360 std::lock_guard
<std::mutex
> Lock(Server
->TranspWriter
);
361 Server
->Transp
.reply(std::move(ID
), std::move(Reply
));
363 llvm::Error Err
= Reply
.takeError();
364 log("--> reply:{0}({1}) {2:ms}, error: {3}", Method
, ID
, Duration
, Err
);
366 (*TraceArgs
)["Error"] = llvm::to_string(Err
);
367 std::lock_guard
<std::mutex
> Lock(Server
->TranspWriter
);
368 Server
->Transp
.reply(std::move(ID
), std::move(Err
));
373 // Method calls may be cancelled by ID, so keep track of their state.
374 // This needs a mutex: handlers may finish on a different thread, and that's
375 // when we clean up entries in the map.
376 mutable std::mutex RequestCancelersMutex
;
377 llvm::StringMap
<std::pair
<Canceler
, /*Cookie*/ unsigned>> RequestCancelers
;
378 unsigned NextRequestCookie
= 0; // To disambiguate reused IDs, see below.
379 void onCancel(const llvm::json::Value
&Params
) {
380 const llvm::json::Value
*ID
= nullptr;
381 if (auto *O
= Params
.getAsObject())
384 elog("Bad cancellation request: {0}", Params
);
387 auto StrID
= llvm::to_string(*ID
);
388 std::lock_guard
<std::mutex
> Lock(RequestCancelersMutex
);
389 auto It
= RequestCancelers
.find(StrID
);
390 if (It
!= RequestCancelers
.end())
391 It
->second
.first(); // Invoke the canceler.
394 Context
handlerContext() const {
395 return Context::current().derive(
396 kCurrentOffsetEncoding
,
397 Server
.Opts
.Encoding
.value_or(OffsetEncoding::UTF16
));
400 // We run cancelable requests in a context that does two things:
401 // - allows cancellation using RequestCancelers[ID]
402 // - cleans up the entry in RequestCancelers when it's no longer needed
403 // If a client reuses an ID, the last wins and the first cannot be canceled.
404 Context
cancelableRequestContext(const llvm::json::Value
&ID
) {
405 auto Task
= cancelableTask(
406 /*Reason=*/static_cast<int>(ErrorCode::RequestCancelled
));
407 auto StrID
= llvm::to_string(ID
); // JSON-serialize ID for map key.
408 auto Cookie
= NextRequestCookie
++; // No lock, only called on main thread.
410 std::lock_guard
<std::mutex
> Lock(RequestCancelersMutex
);
411 RequestCancelers
[StrID
] = {std::move(Task
.second
), Cookie
};
413 // When the request ends, we can clean up the entry we just added.
414 // The cookie lets us check that it hasn't been overwritten due to ID
416 return Task
.first
.derive(llvm::make_scope_exit([this, StrID
, Cookie
] {
417 std::lock_guard
<std::mutex
> Lock(RequestCancelersMutex
);
418 auto It
= RequestCancelers
.find(StrID
);
419 if (It
!= RequestCancelers
.end() && It
->second
.second
== Cookie
)
420 RequestCancelers
.erase(It
);
424 // The maximum number of callbacks held in clangd.
426 // We bound the maximum size to the pending map to prevent memory leakage
427 // for cases where LSP clients don't reply for the request.
428 // This has to go after RequestCancellers and RequestCancellersMutex since it
429 // can contain a callback that has a cancelable context.
430 static constexpr int MaxReplayCallbacks
= 100;
431 mutable std::mutex CallMutex
;
432 int NextCallID
= 0; /* GUARDED_BY(CallMutex) */
433 std::deque
<std::pair
</*RequestID*/ int,
434 /*ReplyHandler*/ Callback
<llvm::json::Value
>>>
435 ReplyCallbacks
; /* GUARDED_BY(CallMutex) */
437 ClangdLSPServer
&Server
;
439 constexpr int ClangdLSPServer::MessageHandler::MaxReplayCallbacks
;
441 // call(), notify(), and reply() wrap the Transport, adding logging and locking.
442 void ClangdLSPServer::callMethod(StringRef Method
, llvm::json::Value Params
,
443 Callback
<llvm::json::Value
> CB
) {
444 auto ID
= MsgHandler
->bindReply(std::move(CB
));
445 log("--> {0}({1})", Method
, ID
);
446 std::lock_guard
<std::mutex
> Lock(TranspWriter
);
447 Transp
.call(Method
, std::move(Params
), ID
);
450 void ClangdLSPServer::notify(llvm::StringRef Method
, llvm::json::Value Params
) {
451 log("--> {0}", Method
);
452 maybeCleanupMemory();
453 std::lock_guard
<std::mutex
> Lock(TranspWriter
);
454 Transp
.notify(Method
, std::move(Params
));
457 static std::vector
<llvm::StringRef
> semanticTokenTypes() {
458 std::vector
<llvm::StringRef
> Types
;
459 for (unsigned I
= 0; I
<= static_cast<unsigned>(HighlightingKind::LastKind
);
461 Types
.push_back(toSemanticTokenType(static_cast<HighlightingKind
>(I
)));
465 static std::vector
<llvm::StringRef
> semanticTokenModifiers() {
466 std::vector
<llvm::StringRef
> Modifiers
;
468 I
<= static_cast<unsigned>(HighlightingModifier::LastModifier
); ++I
)
470 toSemanticTokenModifier(static_cast<HighlightingModifier
>(I
)));
474 void ClangdLSPServer::onInitialize(const InitializeParams
&Params
,
475 Callback
<llvm::json::Value
> Reply
) {
476 // Determine character encoding first as it affects constructed ClangdServer.
477 if (Params
.capabilities
.offsetEncoding
&& !Opts
.Encoding
) {
478 Opts
.Encoding
= OffsetEncoding::UTF16
; // fallback
479 for (OffsetEncoding Supported
: *Params
.capabilities
.offsetEncoding
)
480 if (Supported
!= OffsetEncoding::UnsupportedEncoding
) {
481 Opts
.Encoding
= Supported
;
486 if (Params
.capabilities
.TheiaSemanticHighlighting
&&
487 !Params
.capabilities
.SemanticTokens
) {
488 elog("Client requested legacy semanticHighlights notification, which is "
489 "no longer supported. Migrate to standard semanticTokens request");
492 if (Params
.rootUri
&& *Params
.rootUri
)
493 Opts
.WorkspaceRoot
= std::string(Params
.rootUri
->file());
494 else if (Params
.rootPath
&& !Params
.rootPath
->empty())
495 Opts
.WorkspaceRoot
= *Params
.rootPath
;
497 return Reply(llvm::make_error
<LSPError
>("server already initialized",
498 ErrorCode::InvalidRequest
));
500 Opts
.CodeComplete
.EnableSnippets
= Params
.capabilities
.CompletionSnippets
;
501 Opts
.CodeComplete
.IncludeFixIts
= Params
.capabilities
.CompletionFixes
;
502 if (!Opts
.CodeComplete
.BundleOverloads
)
503 Opts
.CodeComplete
.BundleOverloads
= Params
.capabilities
.HasSignatureHelp
;
504 Opts
.CodeComplete
.DocumentationFormat
=
505 Params
.capabilities
.CompletionDocumentationFormat
;
506 Opts
.SignatureHelpDocumentationFormat
=
507 Params
.capabilities
.SignatureHelpDocumentationFormat
;
508 DiagOpts
.EmbedFixesInDiagnostics
= Params
.capabilities
.DiagnosticFixes
;
509 DiagOpts
.SendDiagnosticCategory
= Params
.capabilities
.DiagnosticCategory
;
510 DiagOpts
.EmitRelatedLocations
=
511 Params
.capabilities
.DiagnosticRelatedInformation
;
512 if (Params
.capabilities
.WorkspaceSymbolKinds
)
513 SupportedSymbolKinds
|= *Params
.capabilities
.WorkspaceSymbolKinds
;
514 if (Params
.capabilities
.CompletionItemKinds
)
515 SupportedCompletionItemKinds
|= *Params
.capabilities
.CompletionItemKinds
;
516 SupportsCompletionLabelDetails
= Params
.capabilities
.CompletionLabelDetail
;
517 SupportsCodeAction
= Params
.capabilities
.CodeActionStructure
;
518 SupportsHierarchicalDocumentSymbol
=
519 Params
.capabilities
.HierarchicalDocumentSymbol
;
520 SupportsReferenceContainer
= Params
.capabilities
.ReferenceContainer
;
521 SupportFileStatus
= Params
.initializationOptions
.FileStatus
;
522 SupportsDocumentChanges
= Params
.capabilities
.DocumentChanges
;
523 SupportsChangeAnnotation
= Params
.capabilities
.ChangeAnnotation
;
524 HoverContentFormat
= Params
.capabilities
.HoverContentFormat
;
525 Opts
.LineFoldingOnly
= Params
.capabilities
.LineFoldingOnly
;
526 SupportsOffsetsInSignatureHelp
= Params
.capabilities
.OffsetsInSignatureHelp
;
527 if (Params
.capabilities
.WorkDoneProgress
)
528 BackgroundIndexProgressState
= BackgroundIndexProgress::Empty
;
529 BackgroundIndexSkipCreate
= Params
.capabilities
.ImplicitProgressCreation
;
530 Opts
.ImplicitCancellation
= !Params
.capabilities
.CancelsStaleRequests
;
531 Opts
.PublishInactiveRegions
= Params
.capabilities
.InactiveRegions
;
533 if (Opts
.UseDirBasedCDB
) {
534 DirectoryBasedGlobalCompilationDatabase::Options
CDBOpts(TFS
);
535 if (const auto &Dir
= Params
.initializationOptions
.compilationDatabasePath
)
536 CDBOpts
.CompileCommandsDir
= Dir
;
537 CDBOpts
.ContextProvider
= Opts
.ContextProvider
;
539 std::make_unique
<DirectoryBasedGlobalCompilationDatabase
>(CDBOpts
);
541 auto Mangler
= CommandMangler::detect();
542 Mangler
.SystemIncludeExtractor
=
543 getSystemIncludeExtractor(llvm::ArrayRef(Opts
.QueryDriverGlobs
));
544 if (Opts
.ResourceDir
)
545 Mangler
.ResourceDir
= *Opts
.ResourceDir
;
546 CDB
.emplace(BaseCDB
.get(), Params
.initializationOptions
.fallbackFlags
,
549 // Switch caller's context with LSPServer's background context. Since we
550 // rather want to propagate information from LSPServer's context into the
552 WithContext
MainContext(BackgroundContext
.clone());
553 std::optional
<WithContextValue
> WithOffsetEncoding
;
555 WithOffsetEncoding
.emplace(kCurrentOffsetEncoding
, *Opts
.Encoding
);
556 Server
.emplace(*CDB
, TFS
, Opts
,
557 static_cast<ClangdServer::Callbacks
*>(this));
560 llvm::json::Object ServerCaps
{
564 {"change", (int)TextDocumentSyncKind::Incremental
},
567 {"documentFormattingProvider", true},
568 {"documentRangeFormattingProvider", true},
569 {"documentOnTypeFormattingProvider",
571 {"firstTriggerCharacter", "\n"},
572 {"moreTriggerCharacter", {}},
574 {"completionProvider",
576 // We don't set `(` etc as allCommitCharacters as they interact
577 // poorly with snippet results.
578 // See https://github.com/clangd/vscode-clangd/issues/357
579 // Hopefully we can use them one day without this side-effect:
580 // https://github.com/microsoft/vscode/issues/42544
581 {"resolveProvider", false},
582 // We do extra checks, e.g. that > is part of ->.
583 {"triggerCharacters", {".", "<", ">", ":", "\"", "/", "*"}},
585 {"semanticTokensProvider",
587 {"full", llvm::json::Object
{{"delta", true}}},
590 llvm::json::Object
{{"tokenTypes", semanticTokenTypes()},
591 {"tokenModifiers", semanticTokenModifiers()}}},
593 {"signatureHelpProvider",
595 {"triggerCharacters", {"(", ")", "{", "}", "<", ">", ","}},
597 {"declarationProvider", true},
598 {"definitionProvider", true},
599 {"implementationProvider", true},
600 {"typeDefinitionProvider", true},
601 {"documentHighlightProvider", true},
602 {"documentLinkProvider",
604 {"resolveProvider", false},
606 {"hoverProvider", true},
607 {"selectionRangeProvider", true},
608 {"documentSymbolProvider", true},
609 {"workspaceSymbolProvider", true},
610 {"referencesProvider", true},
611 {"astProvider", true}, // clangd extension
612 {"typeHierarchyProvider", true},
613 // Unfortunately our extension made use of the same capability name as the
614 // standard. Advertise this capability to tell clients that implement our
615 // extension we really have support for the standardized one as well.
616 {"standardTypeHierarchyProvider", true}, // clangd extension
617 {"memoryUsageProvider", true}, // clangd extension
618 {"compilationDatabase", // clangd extension
619 llvm::json::Object
{{"automaticReload", true}}},
620 {"inactiveRegionsProvider", true}, // clangd extension
621 {"callHierarchyProvider", true},
622 {"clangdInlayHintsProvider", true},
623 {"inlayHintProvider", true},
624 {"foldingRangeProvider", true},
628 LSPBinder
Binder(Handlers
, *this);
629 bindMethods(Binder
, Params
.capabilities
);
630 if (Opts
.FeatureModules
)
631 for (auto &Mod
: *Opts
.FeatureModules
)
632 Mod
.initializeLSP(Binder
, Params
.rawCapabilities
, ServerCaps
);
635 // Per LSP, renameProvider can be either boolean or RenameOptions.
636 // RenameOptions will be specified if the client states it supports prepare.
637 ServerCaps
["renameProvider"] =
638 Params
.capabilities
.RenamePrepareSupport
639 ? llvm::json::Object
{{"prepareProvider", true}}
640 : llvm::json::Value(true);
642 // Per LSP, codeActionProvider can be either boolean or CodeActionOptions.
643 // CodeActionOptions is only valid if the client supports action literal
644 // via textDocument.codeAction.codeActionLiteralSupport.
645 ServerCaps
["codeActionProvider"] =
646 Params
.capabilities
.CodeActionStructure
647 ? llvm::json::Object
{{"codeActionKinds",
648 {CodeAction::QUICKFIX_KIND
,
649 CodeAction::REFACTOR_KIND
,
650 CodeAction::INFO_KIND
}}}
651 : llvm::json::Value(true);
653 std::vector
<llvm::StringRef
> Commands
;
654 for (llvm::StringRef Command
: Handlers
.CommandHandlers
.keys())
655 Commands
.push_back(Command
);
656 llvm::sort(Commands
);
657 ServerCaps
["executeCommandProvider"] =
658 llvm::json::Object
{{"commands", Commands
}};
660 llvm::json::Object Result
{
664 {"version", llvm::formatv("{0} {1} {2}", versionString(),
665 featureString(), platformString())}}},
666 {"capabilities", std::move(ServerCaps
)}}};
668 Result
["offsetEncoding"] = *Opts
.Encoding
;
669 Reply(std::move(Result
));
671 // Apply settings after we're fully initialized.
672 // This can start background indexing and in turn trigger LSP notifications.
673 applyConfiguration(Params
.initializationOptions
.ConfigSettings
);
676 void ClangdLSPServer::onInitialized(const InitializedParams
&Params
) {}
678 void ClangdLSPServer::onShutdown(const NoParams
&,
679 Callback
<std::nullptr_t
> Reply
) {
680 // Do essentially nothing, just say we're ready to exit.
681 ShutdownRequestReceived
= true;
685 // sync is a clangd extension: it blocks until all background work completes.
686 // It blocks the calling thread, so no messages are processed until it returns!
687 void ClangdLSPServer::onSync(const NoParams
&, Callback
<std::nullptr_t
> Reply
) {
688 if (Server
->blockUntilIdleForTest(/*TimeoutSeconds=*/60))
691 Reply(error("Not idle after a minute"));
694 void ClangdLSPServer::onDocumentDidOpen(
695 const DidOpenTextDocumentParams
&Params
) {
696 PathRef File
= Params
.textDocument
.uri
.file();
698 const std::string
&Contents
= Params
.textDocument
.text
;
700 Server
->addDocument(File
, Contents
,
701 encodeVersion(Params
.textDocument
.version
),
702 WantDiagnostics::Yes
);
705 void ClangdLSPServer::onDocumentDidChange(
706 const DidChangeTextDocumentParams
&Params
) {
707 auto WantDiags
= WantDiagnostics::Auto
;
708 if (Params
.wantDiagnostics
)
710 *Params
.wantDiagnostics
? WantDiagnostics::Yes
: WantDiagnostics::No
;
712 PathRef File
= Params
.textDocument
.uri
.file();
713 auto Code
= Server
->getDraft(File
);
715 log("Trying to incrementally change non-added document: {0}", File
);
718 std::string
NewCode(*Code
);
719 for (const auto &Change
: Params
.contentChanges
) {
720 if (auto Err
= applyChange(NewCode
, Change
)) {
721 // If this fails, we are most likely going to be not in sync anymore with
722 // the client. It is better to remove the draft and let further
723 // operations fail rather than giving wrong results.
724 Server
->removeDocument(File
);
725 elog("Failed to update {0}: {1}", File
, std::move(Err
));
729 Server
->addDocument(File
, NewCode
, encodeVersion(Params
.textDocument
.version
),
730 WantDiags
, Params
.forceRebuild
);
733 void ClangdLSPServer::onDocumentDidSave(
734 const DidSaveTextDocumentParams
&Params
) {
735 Server
->reparseOpenFilesIfNeeded([](llvm::StringRef
) { return true; });
738 void ClangdLSPServer::onFileEvent(const DidChangeWatchedFilesParams
&Params
) {
739 // We could also reparse all open files here. However:
740 // - this could be frequent, and revalidating all the preambles isn't free
741 // - this is useful e.g. when switching git branches, but we're likely to see
742 // fresh headers but still have the old-branch main-file content
743 Server
->onFileEvent(Params
);
744 // FIXME: observe config files, immediately expire time-based caches, reparse:
745 // - compile_commands.json and compile_flags.txt
746 // - .clang_format and .clang-tidy
747 // - .clangd and clangd/config.yaml
750 void ClangdLSPServer::onCommand(const ExecuteCommandParams
&Params
,
751 Callback
<llvm::json::Value
> Reply
) {
752 auto It
= Handlers
.CommandHandlers
.find(Params
.command
);
753 if (It
== Handlers
.CommandHandlers
.end()) {
754 return Reply(llvm::make_error
<LSPError
>(
755 llvm::formatv("Unsupported command \"{0}\".", Params
.command
).str(),
756 ErrorCode::InvalidParams
));
758 It
->second(Params
.argument
, std::move(Reply
));
761 void ClangdLSPServer::onCommandApplyEdit(const WorkspaceEdit
&WE
,
762 Callback
<llvm::json::Value
> Reply
) {
763 // The flow for "apply-fix" :
764 // 1. We publish a diagnostic, including fixits
765 // 2. The user clicks on the diagnostic, the editor asks us for code actions
766 // 3. We send code actions, with the fixit embedded as context
767 // 4. The user selects the fixit, the editor asks us to apply it
768 // 5. We unwrap the changes and send them back to the editor
769 // 6. The editor applies the changes (applyEdit), and sends us a reply
770 // 7. We unwrap the reply and send a reply to the editor.
771 applyEdit(WE
, "Fix applied.", std::move(Reply
));
774 void ClangdLSPServer::onCommandApplyTweak(const TweakArgs
&Args
,
775 Callback
<llvm::json::Value
> Reply
) {
776 auto Action
= [this, Reply
= std::move(Reply
)](
777 llvm::Expected
<Tweak::Effect
> R
) mutable {
779 return Reply(R
.takeError());
781 assert(R
->ShowMessage
|| (!R
->ApplyEdits
.empty() && "tweak has no effect"));
783 if (R
->ShowMessage
) {
784 ShowMessageParams Msg
;
785 Msg
.message
= *R
->ShowMessage
;
786 Msg
.type
= MessageType::Info
;
789 // When no edit is specified, make sure we Reply().
790 if (R
->ApplyEdits
.empty())
791 return Reply("Tweak applied.");
793 if (auto Err
= validateEdits(*Server
, R
->ApplyEdits
))
794 return Reply(std::move(Err
));
797 // FIXME: use documentChanges when SupportDocumentChanges is true.
798 WE
.changes
.emplace();
799 for (const auto &It
: R
->ApplyEdits
) {
800 (*WE
.changes
)[URI::createFile(It
.first()).toString()] =
801 It
.second
.asTextEdits();
803 // ApplyEdit will take care of calling Reply().
804 return applyEdit(std::move(WE
), "Tweak applied.", std::move(Reply
));
806 Server
->applyTweak(Args
.file
.file(), Args
.selection
, Args
.tweakID
,
810 void ClangdLSPServer::applyEdit(WorkspaceEdit WE
, llvm::json::Value Success
,
811 Callback
<llvm::json::Value
> Reply
) {
812 ApplyWorkspaceEditParams Edit
;
813 Edit
.edit
= std::move(WE
);
815 Edit
, [Reply
= std::move(Reply
), SuccessMessage
= std::move(Success
)](
816 llvm::Expected
<ApplyWorkspaceEditResponse
> Response
) mutable {
818 return Reply(Response
.takeError());
819 if (!Response
->applied
) {
820 std::string Reason
= Response
->failureReason
821 ? *Response
->failureReason
823 return Reply(error("edits were not applied: {0}", Reason
));
825 return Reply(SuccessMessage
);
829 void ClangdLSPServer::onWorkspaceSymbol(
830 const WorkspaceSymbolParams
&Params
,
831 Callback
<std::vector
<SymbolInformation
>> Reply
) {
832 Server
->workspaceSymbols(
833 Params
.query
, Params
.limit
.value_or(Opts
.CodeComplete
.Limit
),
834 [Reply
= std::move(Reply
),
835 this](llvm::Expected
<std::vector
<SymbolInformation
>> Items
) mutable {
837 return Reply(Items
.takeError());
838 for (auto &Sym
: *Items
)
839 Sym
.kind
= adjustKindToCapability(Sym
.kind
, SupportedSymbolKinds
);
841 Reply(std::move(*Items
));
845 void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams
&Params
,
846 Callback
<std::optional
<Range
>> Reply
) {
847 Server
->prepareRename(
848 Params
.textDocument
.uri
.file(), Params
.position
, /*NewName*/ std::nullopt
,
850 [Reply
= std::move(Reply
)](llvm::Expected
<RenameResult
> Result
) mutable {
852 return Reply(Result
.takeError());
853 return Reply(std::move(Result
->Target
));
857 void ClangdLSPServer::onRename(const RenameParams
&Params
,
858 Callback
<WorkspaceEdit
> Reply
) {
859 Path File
= std::string(Params
.textDocument
.uri
.file());
860 if (!Server
->getDraft(File
))
861 return Reply(llvm::make_error
<LSPError
>(
862 "onRename called for non-added file", ErrorCode::InvalidParams
));
863 Server
->rename(File
, Params
.position
, Params
.newName
, Opts
.Rename
,
864 [File
, Params
, Reply
= std::move(Reply
),
865 this](llvm::Expected
<RenameResult
> R
) mutable {
867 return Reply(R
.takeError());
868 if (auto Err
= validateEdits(*Server
, R
->GlobalChanges
))
869 return Reply(std::move(Err
));
870 WorkspaceEdit Result
;
871 // FIXME: use documentChanges if SupportDocumentChanges is
873 Result
.changes
.emplace();
874 for (const auto &Rep
: R
->GlobalChanges
) {
876 .changes
)[URI::createFile(Rep
.first()).toString()] =
877 Rep
.second
.asTextEdits();
883 void ClangdLSPServer::onDocumentDidClose(
884 const DidCloseTextDocumentParams
&Params
) {
885 PathRef File
= Params
.textDocument
.uri
.file();
886 Server
->removeDocument(File
);
889 std::lock_guard
<std::mutex
> Lock(FixItsMutex
);
890 FixItsMap
.erase(File
);
893 std::lock_guard
<std::mutex
> HLock(SemanticTokensMutex
);
894 LastSemanticTokens
.erase(File
);
896 // clangd will not send updates for this file anymore, so we empty out the
897 // list of diagnostics shown on the client (e.g. in the "Problems" pane of
898 // VSCode). Note that this cannot race with actual diagnostics responses
899 // because removeDocument() guarantees no diagnostic callbacks will be
900 // executed after it returns.
901 PublishDiagnosticsParams Notification
;
902 Notification
.uri
= URIForFile::canonicalize(File
, /*TUPath=*/File
);
903 PublishDiagnostics(Notification
);
906 void ClangdLSPServer::onDocumentOnTypeFormatting(
907 const DocumentOnTypeFormattingParams
&Params
,
908 Callback
<std::vector
<TextEdit
>> Reply
) {
909 auto File
= Params
.textDocument
.uri
.file();
910 Server
->formatOnType(File
, Params
.position
, Params
.ch
, std::move(Reply
));
913 void ClangdLSPServer::onDocumentRangeFormatting(
914 const DocumentRangeFormattingParams
&Params
,
915 Callback
<std::vector
<TextEdit
>> Reply
) {
916 auto File
= Params
.textDocument
.uri
.file();
917 auto Code
= Server
->getDraft(File
);
918 Server
->formatFile(File
, Params
.range
,
919 [Code
= std::move(Code
), Reply
= std::move(Reply
)](
920 llvm::Expected
<tooling::Replacements
> Result
) mutable {
922 Reply(replacementsToEdits(*Code
, Result
.get()));
924 Reply(Result
.takeError());
928 void ClangdLSPServer::onDocumentFormatting(
929 const DocumentFormattingParams
&Params
,
930 Callback
<std::vector
<TextEdit
>> Reply
) {
931 auto File
= Params
.textDocument
.uri
.file();
932 auto Code
= Server
->getDraft(File
);
933 Server
->formatFile(File
,
934 /*Rng=*/std::nullopt
,
935 [Code
= std::move(Code
), Reply
= std::move(Reply
)](
936 llvm::Expected
<tooling::Replacements
> Result
) mutable {
938 Reply(replacementsToEdits(*Code
, Result
.get()));
940 Reply(Result
.takeError());
944 /// The functions constructs a flattened view of the DocumentSymbol hierarchy.
945 /// Used by the clients that do not support the hierarchical view.
946 static std::vector
<SymbolInformation
>
947 flattenSymbolHierarchy(llvm::ArrayRef
<DocumentSymbol
> Symbols
,
948 const URIForFile
&FileURI
) {
949 std::vector
<SymbolInformation
> Results
;
950 std::function
<void(const DocumentSymbol
&, llvm::StringRef
)> Process
=
951 [&](const DocumentSymbol
&S
, std::optional
<llvm::StringRef
> ParentName
) {
952 SymbolInformation SI
;
953 SI
.containerName
= std::string(ParentName
? "" : *ParentName
);
956 SI
.location
.range
= S
.range
;
957 SI
.location
.uri
= FileURI
;
959 Results
.push_back(std::move(SI
));
960 std::string FullName
=
961 !ParentName
? S
.name
: (ParentName
->str() + "::" + S
.name
);
962 for (auto &C
: S
.children
)
963 Process(C
, /*ParentName=*/FullName
);
965 for (auto &S
: Symbols
)
966 Process(S
, /*ParentName=*/"");
970 void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams
&Params
,
971 Callback
<llvm::json::Value
> Reply
) {
972 URIForFile FileURI
= Params
.textDocument
.uri
;
973 Server
->documentSymbols(
974 Params
.textDocument
.uri
.file(),
975 [this, FileURI
, Reply
= std::move(Reply
)](
976 llvm::Expected
<std::vector
<DocumentSymbol
>> Items
) mutable {
978 return Reply(Items
.takeError());
979 adjustSymbolKinds(*Items
, SupportedSymbolKinds
);
980 if (SupportsHierarchicalDocumentSymbol
)
981 return Reply(std::move(*Items
));
982 return Reply(flattenSymbolHierarchy(*Items
, FileURI
));
986 void ClangdLSPServer::onFoldingRange(
987 const FoldingRangeParams
&Params
,
988 Callback
<std::vector
<FoldingRange
>> Reply
) {
989 Server
->foldingRanges(Params
.textDocument
.uri
.file(), std::move(Reply
));
992 static std::optional
<Command
> asCommand(const CodeAction
&Action
) {
994 if (Action
.command
&& Action
.edit
)
995 return std::nullopt
; // Not representable. (We never emit these anyway).
996 if (Action
.command
) {
997 Cmd
= *Action
.command
;
998 } else if (Action
.edit
) {
999 Cmd
.command
= std::string(ApplyFixCommand
);
1000 Cmd
.argument
= *Action
.edit
;
1002 return std::nullopt
;
1004 Cmd
.title
= Action
.title
;
1005 if (Action
.kind
&& *Action
.kind
== CodeAction::QUICKFIX_KIND
)
1006 Cmd
.title
= "Apply fix: " + Cmd
.title
;
1010 void ClangdLSPServer::onCodeAction(const CodeActionParams
&Params
,
1011 Callback
<llvm::json::Value
> Reply
) {
1012 URIForFile File
= Params
.textDocument
.uri
;
1013 // Checks whether a particular CodeActionKind is included in the response.
1014 auto KindAllowed
= [Only(Params
.context
.only
)](llvm::StringRef Kind
) {
1017 return llvm::any_of(Only
, [&](llvm::StringRef Base
) {
1018 return Kind
.consume_front(Base
) && (Kind
.empty() || Kind
.startswith("."));
1022 // We provide a code action for Fixes on the specified diagnostics.
1023 std::vector
<CodeAction
> FixIts
;
1024 if (KindAllowed(CodeAction::QUICKFIX_KIND
)) {
1025 for (const Diagnostic
&D
: Params
.context
.diagnostics
) {
1026 for (auto &CA
: getFixes(File
.file(), D
)) {
1027 FixIts
.push_back(CA
);
1028 FixIts
.back().diagnostics
= {D
};
1033 // Now enumerate the semantic code actions.
1034 auto ConsumeActions
=
1035 [Diags
= Params
.context
.diagnostics
, Reply
= std::move(Reply
), File
,
1036 Selection
= Params
.range
, FixIts
= std::move(FixIts
), this](
1037 llvm::Expected
<std::vector
<ClangdServer::TweakRef
>> Tweaks
) mutable {
1039 return Reply(Tweaks
.takeError());
1041 std::vector
<CodeAction
> Actions
= std::move(FixIts
);
1042 Actions
.reserve(Actions
.size() + Tweaks
->size());
1043 for (const auto &T
: *Tweaks
)
1044 Actions
.push_back(toCodeAction(T
, File
, Selection
));
1046 // If there's exactly one quick-fix, call it "preferred".
1047 // We never consider refactorings etc as preferred.
1048 CodeAction
*OnlyFix
= nullptr;
1049 for (auto &Action
: Actions
) {
1050 if (Action
.kind
&& *Action
.kind
== CodeAction::QUICKFIX_KIND
) {
1059 OnlyFix
->isPreferred
= true;
1060 if (Diags
.size() == 1 && Diags
.front().range
== Selection
)
1061 OnlyFix
->diagnostics
= {Diags
.front()};
1064 if (SupportsCodeAction
)
1065 return Reply(llvm::json::Array(Actions
));
1066 std::vector
<Command
> Commands
;
1067 for (const auto &Action
: Actions
) {
1068 if (auto Command
= asCommand(Action
))
1069 Commands
.push_back(std::move(*Command
));
1071 return Reply(llvm::json::Array(Commands
));
1073 Server
->enumerateTweaks(
1074 File
.file(), Params
.range
,
1075 [this, KindAllowed(std::move(KindAllowed
))](const Tweak
&T
) {
1076 return Opts
.TweakFilter(T
) && KindAllowed(T
.kind());
1078 std::move(ConsumeActions
));
1081 void ClangdLSPServer::onCompletion(const CompletionParams
&Params
,
1082 Callback
<CompletionList
> Reply
) {
1083 if (!shouldRunCompletion(Params
)) {
1084 // Clients sometimes auto-trigger completions in undesired places (e.g.
1085 // 'a >^ '), we return empty results in those cases.
1086 vlog("ignored auto-triggered completion, preceding char did not match");
1087 return Reply(CompletionList());
1089 auto Opts
= this->Opts
.CodeComplete
;
1090 if (Params
.limit
&& *Params
.limit
>= 0)
1091 Opts
.Limit
= *Params
.limit
;
1092 Server
->codeComplete(Params
.textDocument
.uri
.file(), Params
.position
, Opts
,
1093 [Reply
= std::move(Reply
), Opts
,
1094 this](llvm::Expected
<CodeCompleteResult
> List
) mutable {
1096 return Reply(List
.takeError());
1097 CompletionList LSPList
;
1098 LSPList
.isIncomplete
= List
->HasMore
;
1099 for (const auto &R
: List
->Completions
) {
1100 CompletionItem C
= R
.render(Opts
);
1101 C
.kind
= adjustKindToCapability(
1102 C
.kind
, SupportedCompletionItemKinds
);
1103 if (!SupportsCompletionLabelDetails
)
1104 removeCompletionLabelDetails(C
);
1105 LSPList
.items
.push_back(std::move(C
));
1107 return Reply(std::move(LSPList
));
1111 void ClangdLSPServer::onSignatureHelp(const TextDocumentPositionParams
&Params
,
1112 Callback
<SignatureHelp
> Reply
) {
1113 Server
->signatureHelp(Params
.textDocument
.uri
.file(), Params
.position
,
1114 Opts
.SignatureHelpDocumentationFormat
,
1115 [Reply
= std::move(Reply
), this](
1116 llvm::Expected
<SignatureHelp
> Signature
) mutable {
1118 return Reply(Signature
.takeError());
1119 if (SupportsOffsetsInSignatureHelp
)
1120 return Reply(std::move(*Signature
));
1121 // Strip out the offsets from signature help for
1122 // clients that only support string labels.
1123 for (auto &SigInfo
: Signature
->signatures
) {
1124 for (auto &Param
: SigInfo
.parameters
)
1125 Param
.labelOffsets
.reset();
1127 return Reply(std::move(*Signature
));
1131 // Go to definition has a toggle function: if def and decl are distinct, then
1132 // the first press gives you the def, the second gives you the matching def.
1133 // getToggle() returns the counterpart location that under the cursor.
1135 // We return the toggled location alone (ignoring other symbols) to encourage
1136 // editors to "bounce" quickly between locations, without showing a menu.
1137 static Location
*getToggle(const TextDocumentPositionParams
&Point
,
1138 LocatedSymbol
&Sym
) {
1139 // Toggle only makes sense with two distinct locations.
1140 if (!Sym
.Definition
|| *Sym
.Definition
== Sym
.PreferredDeclaration
)
1142 if (Sym
.Definition
->uri
.file() == Point
.textDocument
.uri
.file() &&
1143 Sym
.Definition
->range
.contains(Point
.position
))
1144 return &Sym
.PreferredDeclaration
;
1145 if (Sym
.PreferredDeclaration
.uri
.file() == Point
.textDocument
.uri
.file() &&
1146 Sym
.PreferredDeclaration
.range
.contains(Point
.position
))
1147 return &*Sym
.Definition
;
1151 void ClangdLSPServer::onGoToDefinition(const TextDocumentPositionParams
&Params
,
1152 Callback
<std::vector
<Location
>> Reply
) {
1153 Server
->locateSymbolAt(
1154 Params
.textDocument
.uri
.file(), Params
.position
,
1155 [Params
, Reply
= std::move(Reply
)](
1156 llvm::Expected
<std::vector
<LocatedSymbol
>> Symbols
) mutable {
1158 return Reply(Symbols
.takeError());
1159 std::vector
<Location
> Defs
;
1160 for (auto &S
: *Symbols
) {
1161 if (Location
*Toggle
= getToggle(Params
, S
))
1162 return Reply(std::vector
<Location
>{std::move(*Toggle
)});
1163 Defs
.push_back(S
.Definition
.value_or(S
.PreferredDeclaration
));
1165 Reply(std::move(Defs
));
1169 void ClangdLSPServer::onGoToDeclaration(
1170 const TextDocumentPositionParams
&Params
,
1171 Callback
<std::vector
<Location
>> Reply
) {
1172 Server
->locateSymbolAt(
1173 Params
.textDocument
.uri
.file(), Params
.position
,
1174 [Params
, Reply
= std::move(Reply
)](
1175 llvm::Expected
<std::vector
<LocatedSymbol
>> Symbols
) mutable {
1177 return Reply(Symbols
.takeError());
1178 std::vector
<Location
> Decls
;
1179 for (auto &S
: *Symbols
) {
1180 if (Location
*Toggle
= getToggle(Params
, S
))
1181 return Reply(std::vector
<Location
>{std::move(*Toggle
)});
1182 Decls
.push_back(std::move(S
.PreferredDeclaration
));
1184 Reply(std::move(Decls
));
1188 void ClangdLSPServer::onSwitchSourceHeader(
1189 const TextDocumentIdentifier
&Params
,
1190 Callback
<std::optional
<URIForFile
>> Reply
) {
1191 Server
->switchSourceHeader(
1193 [Reply
= std::move(Reply
),
1194 Params
](llvm::Expected
<std::optional
<clangd::Path
>> Path
) mutable {
1196 return Reply(Path
.takeError());
1198 return Reply(URIForFile::canonicalize(**Path
, Params
.uri
.file()));
1199 return Reply(std::nullopt
);
1203 void ClangdLSPServer::onDocumentHighlight(
1204 const TextDocumentPositionParams
&Params
,
1205 Callback
<std::vector
<DocumentHighlight
>> Reply
) {
1206 Server
->findDocumentHighlights(Params
.textDocument
.uri
.file(),
1207 Params
.position
, std::move(Reply
));
1210 void ClangdLSPServer::onHover(const TextDocumentPositionParams
&Params
,
1211 Callback
<std::optional
<Hover
>> Reply
) {
1212 Server
->findHover(Params
.textDocument
.uri
.file(), Params
.position
,
1213 [Reply
= std::move(Reply
),
1214 this](llvm::Expected
<std::optional
<HoverInfo
>> H
) mutable {
1216 return Reply(H
.takeError());
1218 return Reply(std::nullopt
);
1221 R
.contents
.kind
= HoverContentFormat
;
1222 R
.range
= (*H
)->SymRange
;
1223 switch (HoverContentFormat
) {
1224 case MarkupKind::PlainText
:
1225 R
.contents
.value
= (*H
)->present().asPlainText();
1226 return Reply(std::move(R
));
1227 case MarkupKind::Markdown
:
1228 R
.contents
.value
= (*H
)->present().asMarkdown();
1229 return Reply(std::move(R
));
1231 llvm_unreachable("unhandled MarkupKind");
1235 // Our extension has a different representation on the wire than the standard.
1236 // https://clangd.llvm.org/extensions#type-hierarchy
1237 llvm::json::Value
serializeTHIForExtension(TypeHierarchyItem THI
) {
1238 llvm::json::Object Result
{{
1239 {"name", std::move(THI
.name
)},
1240 {"kind", static_cast<int>(THI
.kind
)},
1241 {"uri", std::move(THI
.uri
)},
1242 {"range", THI
.range
},
1243 {"selectionRange", THI
.selectionRange
},
1244 {"data", std::move(THI
.data
)},
1247 Result
["deprecated"] = THI
.deprecated
;
1249 Result
["detail"] = std::move(*THI
.detail
);
1252 llvm::json::Array Parents
;
1253 for (auto &Parent
: *THI
.parents
)
1254 Parents
.emplace_back(serializeTHIForExtension(std::move(Parent
)));
1255 Result
["parents"] = std::move(Parents
);
1259 llvm::json::Array Children
;
1260 for (auto &child
: *THI
.children
)
1261 Children
.emplace_back(serializeTHIForExtension(std::move(child
)));
1262 Result
["children"] = std::move(Children
);
1267 void ClangdLSPServer::onTypeHierarchy(const TypeHierarchyPrepareParams
&Params
,
1268 Callback
<llvm::json::Value
> Reply
) {
1270 [Reply
= std::move(Reply
)](
1271 llvm::Expected
<std::vector
<TypeHierarchyItem
>> Resp
) mutable {
1273 Reply(Resp
.takeError());
1276 if (Resp
->empty()) {
1280 Reply(serializeTHIForExtension(std::move(Resp
->front())));
1282 Server
->typeHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1283 Params
.resolve
, Params
.direction
, std::move(Serialize
));
1286 void ClangdLSPServer::onResolveTypeHierarchy(
1287 const ResolveTypeHierarchyItemParams
&Params
,
1288 Callback
<llvm::json::Value
> Reply
) {
1290 [Reply
= std::move(Reply
)](
1291 llvm::Expected
<std::optional
<TypeHierarchyItem
>> Resp
) mutable {
1293 Reply(Resp
.takeError());
1297 Reply(std::move(*Resp
));
1300 Reply(serializeTHIForExtension(std::move(**Resp
)));
1302 Server
->resolveTypeHierarchy(Params
.item
, Params
.resolve
, Params
.direction
,
1303 std::move(Serialize
));
1306 void ClangdLSPServer::onPrepareTypeHierarchy(
1307 const TypeHierarchyPrepareParams
&Params
,
1308 Callback
<std::vector
<TypeHierarchyItem
>> Reply
) {
1309 Server
->typeHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1310 Params
.resolve
, Params
.direction
, std::move(Reply
));
1313 void ClangdLSPServer::onSuperTypes(
1314 const ResolveTypeHierarchyItemParams
&Params
,
1315 Callback
<std::optional
<std::vector
<TypeHierarchyItem
>>> Reply
) {
1316 Server
->superTypes(Params
.item
, std::move(Reply
));
1319 void ClangdLSPServer::onSubTypes(
1320 const ResolveTypeHierarchyItemParams
&Params
,
1321 Callback
<std::vector
<TypeHierarchyItem
>> Reply
) {
1322 Server
->subTypes(Params
.item
, std::move(Reply
));
1325 void ClangdLSPServer::onPrepareCallHierarchy(
1326 const CallHierarchyPrepareParams
&Params
,
1327 Callback
<std::vector
<CallHierarchyItem
>> Reply
) {
1328 Server
->prepareCallHierarchy(Params
.textDocument
.uri
.file(), Params
.position
,
1332 void ClangdLSPServer::onCallHierarchyIncomingCalls(
1333 const CallHierarchyIncomingCallsParams
&Params
,
1334 Callback
<std::vector
<CallHierarchyIncomingCall
>> Reply
) {
1335 Server
->incomingCalls(Params
.item
, std::move(Reply
));
1338 void ClangdLSPServer::onClangdInlayHints(const InlayHintsParams
&Params
,
1339 Callback
<llvm::json::Value
> Reply
) {
1340 // Our extension has a different representation on the wire than the standard.
1341 // We have a "range" property and "kind" is represented as a string, not as an
1343 // https://clangd.llvm.org/extensions#inlay-hints
1344 auto Serialize
= [Reply
= std::move(Reply
)](
1345 llvm::Expected
<std::vector
<InlayHint
>> Hints
) mutable {
1347 Reply(Hints
.takeError());
1350 llvm::json::Array Result
;
1351 Result
.reserve(Hints
->size());
1352 for (auto &Hint
: *Hints
) {
1353 Result
.emplace_back(llvm::json::Object
{
1354 {"kind", llvm::to_string(Hint
.kind
)},
1355 {"range", Hint
.range
},
1356 {"position", Hint
.position
},
1357 // Extension doesn't have paddingLeft/Right so adjust the label
1360 ((Hint
.paddingLeft
? " " : "") + llvm::StringRef(Hint
.label
) +
1361 (Hint
.paddingRight
? " " : ""))
1365 Reply(std::move(Result
));
1367 Server
->inlayHints(Params
.textDocument
.uri
.file(), Params
.range
,
1368 std::move(Serialize
));
1371 void ClangdLSPServer::onInlayHint(const InlayHintsParams
&Params
,
1372 Callback
<std::vector
<InlayHint
>> Reply
) {
1373 Server
->inlayHints(Params
.textDocument
.uri
.file(), Params
.range
,
1377 void ClangdLSPServer::applyConfiguration(
1378 const ConfigurationSettings
&Settings
) {
1379 // Per-file update to the compilation database.
1380 llvm::StringSet
<> ModifiedFiles
;
1381 for (auto &Entry
: Settings
.compilationDatabaseChanges
) {
1382 PathRef File
= Entry
.first
;
1383 auto Old
= CDB
->getCompileCommand(File
);
1385 tooling::CompileCommand(std::move(Entry
.second
.workingDirectory
), File
,
1386 std::move(Entry
.second
.compilationCommand
),
1389 CDB
->setCompileCommand(File
, std::move(New
));
1390 ModifiedFiles
.insert(File
);
1394 Server
->reparseOpenFilesIfNeeded(
1395 [&](llvm::StringRef File
) { return ModifiedFiles
.count(File
) != 0; });
1398 void ClangdLSPServer::maybeExportMemoryProfile() {
1399 if (!trace::enabled() || !ShouldProfile())
1402 static constexpr trace::Metric
MemoryUsage(
1403 "memory_usage", trace::Metric::Value
, "component_name");
1404 trace::Span
Tracer("ProfileBrief");
1407 record(MT
, "clangd_lsp_server", MemoryUsage
);
1410 void ClangdLSPServer::maybeCleanupMemory() {
1411 if (!Opts
.MemoryCleanup
|| !ShouldCleanupMemory())
1413 Opts
.MemoryCleanup();
1416 // FIXME: This function needs to be properly tested.
1417 void ClangdLSPServer::onChangeConfiguration(
1418 const DidChangeConfigurationParams
&Params
) {
1419 applyConfiguration(Params
.settings
);
1422 void ClangdLSPServer::onReference(
1423 const ReferenceParams
&Params
,
1424 Callback
<std::vector
<ReferenceLocation
>> Reply
) {
1425 Server
->findReferences(Params
.textDocument
.uri
.file(), Params
.position
,
1426 Opts
.ReferencesLimit
, SupportsReferenceContainer
,
1427 [Reply
= std::move(Reply
),
1428 IncludeDecl(Params
.context
.includeDeclaration
)](
1429 llvm::Expected
<ReferencesResult
> Refs
) mutable {
1431 return Reply(Refs
.takeError());
1432 // Filter out declarations if the client asked.
1433 std::vector
<ReferenceLocation
> Result
;
1434 Result
.reserve(Refs
->References
.size());
1435 for (auto &Ref
: Refs
->References
) {
1437 Ref
.Attributes
& ReferencesResult::Declaration
;
1438 if (IncludeDecl
|| !IsDecl
)
1439 Result
.push_back(std::move(Ref
.Loc
));
1441 return Reply(std::move(Result
));
1445 void ClangdLSPServer::onGoToType(const TextDocumentPositionParams
&Params
,
1446 Callback
<std::vector
<Location
>> Reply
) {
1448 Params
.textDocument
.uri
.file(), Params
.position
,
1449 [Reply
= std::move(Reply
)](
1450 llvm::Expected
<std::vector
<LocatedSymbol
>> Types
) mutable {
1452 return Reply(Types
.takeError());
1453 std::vector
<Location
> Response
;
1454 for (const LocatedSymbol
&Sym
: *Types
)
1455 Response
.push_back(Sym
.PreferredDeclaration
);
1456 return Reply(std::move(Response
));
1460 void ClangdLSPServer::onGoToImplementation(
1461 const TextDocumentPositionParams
&Params
,
1462 Callback
<std::vector
<Location
>> Reply
) {
1463 Server
->findImplementations(
1464 Params
.textDocument
.uri
.file(), Params
.position
,
1465 [Reply
= std::move(Reply
)](
1466 llvm::Expected
<std::vector
<LocatedSymbol
>> Overrides
) mutable {
1468 return Reply(Overrides
.takeError());
1469 std::vector
<Location
> Impls
;
1470 for (const LocatedSymbol
&Sym
: *Overrides
)
1471 Impls
.push_back(Sym
.PreferredDeclaration
);
1472 return Reply(std::move(Impls
));
1476 void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams
&Params
,
1477 Callback
<std::vector
<SymbolDetails
>> Reply
) {
1478 Server
->symbolInfo(Params
.textDocument
.uri
.file(), Params
.position
,
1482 void ClangdLSPServer::onSelectionRange(
1483 const SelectionRangeParams
&Params
,
1484 Callback
<std::vector
<SelectionRange
>> Reply
) {
1485 Server
->semanticRanges(
1486 Params
.textDocument
.uri
.file(), Params
.positions
,
1487 [Reply
= std::move(Reply
)](
1488 llvm::Expected
<std::vector
<SelectionRange
>> Ranges
) mutable {
1490 return Reply(Ranges
.takeError());
1491 return Reply(std::move(*Ranges
));
1495 void ClangdLSPServer::onDocumentLink(
1496 const DocumentLinkParams
&Params
,
1497 Callback
<std::vector
<DocumentLink
>> Reply
) {
1499 // TODO(forster): This currently resolves all targets eagerly. This is slow,
1500 // because it blocks on the preamble/AST being built. We could respond to the
1501 // request faster by using string matching or the lexer to find the includes
1502 // and resolving the targets lazily.
1503 Server
->documentLinks(
1504 Params
.textDocument
.uri
.file(),
1505 [Reply
= std::move(Reply
)](
1506 llvm::Expected
<std::vector
<DocumentLink
>> Links
) mutable {
1508 return Reply(Links
.takeError());
1510 return Reply(std::move(Links
));
1514 // Increment a numeric string: "" -> 1 -> 2 -> ... -> 9 -> 10 -> 11 ...
1515 static void increment(std::string
&S
) {
1516 for (char &C
: llvm::reverse(S
)) {
1523 S
.insert(S
.begin(), '1');
1526 void ClangdLSPServer::onSemanticTokens(const SemanticTokensParams
&Params
,
1527 Callback
<SemanticTokens
> CB
) {
1528 auto File
= Params
.textDocument
.uri
.file();
1529 Server
->semanticHighlights(
1530 Params
.textDocument
.uri
.file(),
1531 [this, File(File
.str()), CB(std::move(CB
)), Code(Server
->getDraft(File
))](
1532 llvm::Expected
<std::vector
<HighlightingToken
>> HT
) mutable {
1534 return CB(HT
.takeError());
1535 SemanticTokens Result
;
1536 Result
.tokens
= toSemanticTokens(*HT
, *Code
);
1538 std::lock_guard
<std::mutex
> Lock(SemanticTokensMutex
);
1539 auto &Last
= LastSemanticTokens
[File
];
1541 Last
.tokens
= Result
.tokens
;
1542 increment(Last
.resultId
);
1543 Result
.resultId
= Last
.resultId
;
1545 CB(std::move(Result
));
1549 void ClangdLSPServer::onSemanticTokensDelta(
1550 const SemanticTokensDeltaParams
&Params
,
1551 Callback
<SemanticTokensOrDelta
> CB
) {
1552 auto File
= Params
.textDocument
.uri
.file();
1553 Server
->semanticHighlights(
1554 Params
.textDocument
.uri
.file(),
1555 [this, PrevResultID(Params
.previousResultId
), File(File
.str()),
1556 CB(std::move(CB
)), Code(Server
->getDraft(File
))](
1557 llvm::Expected
<std::vector
<HighlightingToken
>> HT
) mutable {
1559 return CB(HT
.takeError());
1560 std::vector
<SemanticToken
> Toks
= toSemanticTokens(*HT
, *Code
);
1562 SemanticTokensOrDelta Result
;
1564 std::lock_guard
<std::mutex
> Lock(SemanticTokensMutex
);
1565 auto &Last
= LastSemanticTokens
[File
];
1567 if (PrevResultID
== Last
.resultId
) {
1568 Result
.edits
= diffTokens(Last
.tokens
, Toks
);
1570 vlog("semanticTokens/full/delta: wanted edits vs {0} but last "
1571 "result had ID {1}. Returning full token list.",
1572 PrevResultID
, Last
.resultId
);
1573 Result
.tokens
= Toks
;
1576 Last
.tokens
= std::move(Toks
);
1577 increment(Last
.resultId
);
1578 Result
.resultId
= Last
.resultId
;
1581 CB(std::move(Result
));
1585 void ClangdLSPServer::onMemoryUsage(const NoParams
&,
1586 Callback
<MemoryTree
> Reply
) {
1587 llvm::BumpPtrAllocator DetailAlloc
;
1588 MemoryTree
MT(&DetailAlloc
);
1590 Reply(std::move(MT
));
1593 void ClangdLSPServer::onAST(const ASTParams
&Params
,
1594 Callback
<std::optional
<ASTNode
>> CB
) {
1595 Server
->getAST(Params
.textDocument
.uri
.file(), Params
.range
, std::move(CB
));
1598 ClangdLSPServer::ClangdLSPServer(Transport
&Transp
, const ThreadsafeFS
&TFS
,
1599 const ClangdLSPServer::Options
&Opts
)
1600 : ShouldProfile(/*Period=*/std::chrono::minutes(5),
1601 /*Delay=*/std::chrono::minutes(1)),
1602 ShouldCleanupMemory(/*Period=*/std::chrono::minutes(1),
1603 /*Delay=*/std::chrono::minutes(1)),
1604 BackgroundContext(Context::current().clone()), Transp(Transp
),
1605 MsgHandler(new MessageHandler(*this)), TFS(TFS
),
1606 SupportedSymbolKinds(defaultSymbolKinds()),
1607 SupportedCompletionItemKinds(defaultCompletionItemKinds()), Opts(Opts
) {
1608 if (Opts
.ConfigProvider
) {
1609 assert(!Opts
.ContextProvider
&&
1610 "Only one of ConfigProvider and ContextProvider allowed!");
1611 this->Opts
.ContextProvider
= ClangdServer::createConfiguredContextProvider(
1612 Opts
.ConfigProvider
, this);
1614 LSPBinder
Bind(this->Handlers
, *this);
1615 Bind
.method("initialize", this, &ClangdLSPServer::onInitialize
);
1618 void ClangdLSPServer::bindMethods(LSPBinder
&Bind
,
1619 const ClientCapabilities
&Caps
) {
1621 Bind
.notification("initialized", this, &ClangdLSPServer::onInitialized
);
1622 Bind
.method("shutdown", this, &ClangdLSPServer::onShutdown
);
1623 Bind
.method("sync", this, &ClangdLSPServer::onSync
);
1624 Bind
.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting
);
1625 Bind
.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting
);
1626 Bind
.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting
);
1627 Bind
.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction
);
1628 Bind
.method("textDocument/completion", this, &ClangdLSPServer::onCompletion
);
1629 Bind
.method("textDocument/signatureHelp", this, &ClangdLSPServer::onSignatureHelp
);
1630 Bind
.method("textDocument/definition", this, &ClangdLSPServer::onGoToDefinition
);
1631 Bind
.method("textDocument/declaration", this, &ClangdLSPServer::onGoToDeclaration
);
1632 Bind
.method("textDocument/typeDefinition", this, &ClangdLSPServer::onGoToType
);
1633 Bind
.method("textDocument/implementation", this, &ClangdLSPServer::onGoToImplementation
);
1634 Bind
.method("textDocument/references", this, &ClangdLSPServer::onReference
);
1635 Bind
.method("textDocument/switchSourceHeader", this, &ClangdLSPServer::onSwitchSourceHeader
);
1636 Bind
.method("textDocument/prepareRename", this, &ClangdLSPServer::onPrepareRename
);
1637 Bind
.method("textDocument/rename", this, &ClangdLSPServer::onRename
);
1638 Bind
.method("textDocument/hover", this, &ClangdLSPServer::onHover
);
1639 Bind
.method("textDocument/documentSymbol", this, &ClangdLSPServer::onDocumentSymbol
);
1640 Bind
.method("workspace/executeCommand", this, &ClangdLSPServer::onCommand
);
1641 Bind
.method("textDocument/documentHighlight", this, &ClangdLSPServer::onDocumentHighlight
);
1642 Bind
.method("workspace/symbol", this, &ClangdLSPServer::onWorkspaceSymbol
);
1643 Bind
.method("textDocument/ast", this, &ClangdLSPServer::onAST
);
1644 Bind
.notification("textDocument/didOpen", this, &ClangdLSPServer::onDocumentDidOpen
);
1645 Bind
.notification("textDocument/didClose", this, &ClangdLSPServer::onDocumentDidClose
);
1646 Bind
.notification("textDocument/didChange", this, &ClangdLSPServer::onDocumentDidChange
);
1647 Bind
.notification("textDocument/didSave", this, &ClangdLSPServer::onDocumentDidSave
);
1648 Bind
.notification("workspace/didChangeWatchedFiles", this, &ClangdLSPServer::onFileEvent
);
1649 Bind
.notification("workspace/didChangeConfiguration", this, &ClangdLSPServer::onChangeConfiguration
);
1650 Bind
.method("textDocument/symbolInfo", this, &ClangdLSPServer::onSymbolInfo
);
1651 Bind
.method("textDocument/typeHierarchy", this, &ClangdLSPServer::onTypeHierarchy
);
1652 Bind
.method("typeHierarchy/resolve", this, &ClangdLSPServer::onResolveTypeHierarchy
);
1653 Bind
.method("textDocument/prepareTypeHierarchy", this, &ClangdLSPServer::onPrepareTypeHierarchy
);
1654 Bind
.method("typeHierarchy/supertypes", this, &ClangdLSPServer::onSuperTypes
);
1655 Bind
.method("typeHierarchy/subtypes", this, &ClangdLSPServer::onSubTypes
);
1656 Bind
.method("textDocument/prepareCallHierarchy", this, &ClangdLSPServer::onPrepareCallHierarchy
);
1657 Bind
.method("callHierarchy/incomingCalls", this, &ClangdLSPServer::onCallHierarchyIncomingCalls
);
1658 Bind
.method("textDocument/selectionRange", this, &ClangdLSPServer::onSelectionRange
);
1659 Bind
.method("textDocument/documentLink", this, &ClangdLSPServer::onDocumentLink
);
1660 Bind
.method("textDocument/semanticTokens/full", this, &ClangdLSPServer::onSemanticTokens
);
1661 Bind
.method("textDocument/semanticTokens/full/delta", this, &ClangdLSPServer::onSemanticTokensDelta
);
1662 Bind
.method("clangd/inlayHints", this, &ClangdLSPServer::onClangdInlayHints
);
1663 Bind
.method("textDocument/inlayHint", this, &ClangdLSPServer::onInlayHint
);
1664 Bind
.method("$/memoryUsage", this, &ClangdLSPServer::onMemoryUsage
);
1665 Bind
.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange
);
1666 Bind
.command(ApplyFixCommand
, this, &ClangdLSPServer::onCommandApplyEdit
);
1667 Bind
.command(ApplyTweakCommand
, this, &ClangdLSPServer::onCommandApplyTweak
);
1669 ApplyWorkspaceEdit
= Bind
.outgoingMethod("workspace/applyEdit");
1670 PublishDiagnostics
= Bind
.outgoingNotification("textDocument/publishDiagnostics");
1671 if (Caps
.InactiveRegions
)
1672 PublishInactiveRegions
= Bind
.outgoingNotification("textDocument/inactiveRegions");
1673 ShowMessage
= Bind
.outgoingNotification("window/showMessage");
1674 NotifyFileStatus
= Bind
.outgoingNotification("textDocument/clangd.fileStatus");
1675 CreateWorkDoneProgress
= Bind
.outgoingMethod("window/workDoneProgress/create");
1676 BeginWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1677 ReportWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1678 EndWorkDoneProgress
= Bind
.outgoingNotification("$/progress");
1679 if(Caps
.SemanticTokenRefreshSupport
)
1680 SemanticTokensRefresh
= Bind
.outgoingMethod("workspace/semanticTokens/refresh");
1684 ClangdLSPServer::~ClangdLSPServer() {
1685 IsBeingDestroyed
= true;
1686 // Explicitly destroy ClangdServer first, blocking on threads it owns.
1687 // This ensures they don't access any other members.
1691 bool ClangdLSPServer::run() {
1692 // Run the Language Server loop.
1693 bool CleanExit
= true;
1694 if (auto Err
= Transp
.loop(*MsgHandler
)) {
1695 elog("Transport error: {0}", std::move(Err
));
1699 return CleanExit
&& ShutdownRequestReceived
;
1702 void ClangdLSPServer::profile(MemoryTree
&MT
) const {
1704 Server
->profile(MT
.child("clangd_server"));
1707 std::vector
<CodeAction
> ClangdLSPServer::getFixes(llvm::StringRef File
,
1708 const clangd::Diagnostic
&D
) {
1709 std::lock_guard
<std::mutex
> Lock(FixItsMutex
);
1710 auto DiagToFixItsIter
= FixItsMap
.find(File
);
1711 if (DiagToFixItsIter
== FixItsMap
.end())
1714 const auto &DiagToFixItsMap
= DiagToFixItsIter
->second
;
1715 auto FixItsIter
= DiagToFixItsMap
.find(D
);
1716 if (FixItsIter
== DiagToFixItsMap
.end())
1719 return FixItsIter
->second
;
1722 // A completion request is sent when the user types '>' or ':', but we only
1723 // want to trigger on '->' and '::'. We check the preceding text to make
1724 // sure it matches what we expected.
1725 // Running the lexer here would be more robust (e.g. we can detect comments
1726 // and avoid triggering completion there), but we choose to err on the side
1727 // of simplicity here.
1728 bool ClangdLSPServer::shouldRunCompletion(
1729 const CompletionParams
&Params
) const {
1730 if (Params
.context
.triggerKind
!= CompletionTriggerKind::TriggerCharacter
)
1732 auto Code
= Server
->getDraft(Params
.textDocument
.uri
.file());
1734 return true; // completion code will log the error for untracked doc.
1735 auto Offset
= positionToOffset(*Code
, Params
.position
,
1736 /*AllowColumnsBeyondLineLength=*/false);
1738 vlog("could not convert position '{0}' to offset for file '{1}'",
1739 Params
.position
, Params
.textDocument
.uri
.file());
1742 return allowImplicitCompletion(*Code
, *Offset
);
1745 void ClangdLSPServer::onDiagnosticsReady(PathRef File
, llvm::StringRef Version
,
1746 llvm::ArrayRef
<Diag
> Diagnostics
) {
1747 PublishDiagnosticsParams Notification
;
1748 Notification
.version
= decodeVersion(Version
);
1749 Notification
.uri
= URIForFile::canonicalize(File
, /*TUPath=*/File
);
1750 DiagnosticToReplacementMap LocalFixIts
; // Temporary storage
1751 for (auto &Diag
: Diagnostics
) {
1752 toLSPDiags(Diag
, Notification
.uri
, DiagOpts
,
1753 [&](clangd::Diagnostic Diag
, llvm::ArrayRef
<Fix
> Fixes
) {
1754 std::vector
<CodeAction
> CodeActions
;
1755 for (const auto &Fix
: Fixes
)
1756 CodeActions
.push_back(toCodeAction(Fix
, Notification
.uri
,
1757 Notification
.version
,
1758 SupportsDocumentChanges
,
1759 SupportsChangeAnnotation
));
1761 if (DiagOpts
.EmbedFixesInDiagnostics
) {
1762 Diag
.codeActions
.emplace(CodeActions
);
1763 if (Diag
.codeActions
->size() == 1)
1764 Diag
.codeActions
->front().isPreferred
= true;
1766 LocalFixIts
[Diag
] = std::move(CodeActions
);
1767 Notification
.diagnostics
.push_back(std::move(Diag
));
1773 std::lock_guard
<std::mutex
> Lock(FixItsMutex
);
1774 FixItsMap
[File
] = LocalFixIts
;
1777 // Send a notification to the LSP client.
1778 PublishDiagnostics(Notification
);
1781 void ClangdLSPServer::onInactiveRegionsReady(
1782 PathRef File
, std::vector
<Range
> InactiveRegions
) {
1783 InactiveRegionsParams Notification
;
1784 Notification
.TextDocument
= {URIForFile::canonicalize(File
, /*TUPath=*/File
)};
1785 Notification
.InactiveRegions
= std::move(InactiveRegions
);
1787 PublishInactiveRegions(Notification
);
1790 void ClangdLSPServer::onBackgroundIndexProgress(
1791 const BackgroundQueue::Stats
&Stats
) {
1792 static const char ProgressToken
[] = "backgroundIndexProgress";
1794 // The background index did some work, maybe we need to cleanup
1795 maybeCleanupMemory();
1797 std::lock_guard
<std::mutex
> Lock(BackgroundIndexProgressMutex
);
1799 auto NotifyProgress
= [this](const BackgroundQueue::Stats
&Stats
) {
1800 if (BackgroundIndexProgressState
!= BackgroundIndexProgress::Live
) {
1801 WorkDoneProgressBegin Begin
;
1802 Begin
.percentage
= true;
1803 Begin
.title
= "indexing";
1804 BeginWorkDoneProgress({ProgressToken
, std::move(Begin
)});
1805 BackgroundIndexProgressState
= BackgroundIndexProgress::Live
;
1808 if (Stats
.Completed
< Stats
.Enqueued
) {
1809 assert(Stats
.Enqueued
> Stats
.LastIdle
);
1810 WorkDoneProgressReport Report
;
1811 Report
.percentage
= 100 * (Stats
.Completed
- Stats
.LastIdle
) /
1812 (Stats
.Enqueued
- Stats
.LastIdle
);
1814 llvm::formatv("{0}/{1}", Stats
.Completed
- Stats
.LastIdle
,
1815 Stats
.Enqueued
- Stats
.LastIdle
);
1816 ReportWorkDoneProgress({ProgressToken
, std::move(Report
)});
1818 assert(Stats
.Completed
== Stats
.Enqueued
);
1819 EndWorkDoneProgress({ProgressToken
, WorkDoneProgressEnd()});
1820 BackgroundIndexProgressState
= BackgroundIndexProgress::Empty
;
1824 switch (BackgroundIndexProgressState
) {
1825 case BackgroundIndexProgress::Unsupported
:
1827 case BackgroundIndexProgress::Creating
:
1828 // Cache this update for when the progress bar is available.
1829 PendingBackgroundIndexProgress
= Stats
;
1831 case BackgroundIndexProgress::Empty
: {
1832 if (BackgroundIndexSkipCreate
) {
1833 NotifyProgress(Stats
);
1836 // Cache this update for when the progress bar is available.
1837 PendingBackgroundIndexProgress
= Stats
;
1838 BackgroundIndexProgressState
= BackgroundIndexProgress::Creating
;
1839 WorkDoneProgressCreateParams CreateRequest
;
1840 CreateRequest
.token
= ProgressToken
;
1841 CreateWorkDoneProgress(
1843 [this, NotifyProgress
](llvm::Expected
<std::nullptr_t
> E
) {
1844 std::lock_guard
<std::mutex
> Lock(BackgroundIndexProgressMutex
);
1846 NotifyProgress(this->PendingBackgroundIndexProgress
);
1848 elog("Failed to create background index progress bar: {0}",
1850 // give up forever rather than thrashing about
1851 BackgroundIndexProgressState
= BackgroundIndexProgress::Unsupported
;
1856 case BackgroundIndexProgress::Live
:
1857 NotifyProgress(Stats
);
1862 void ClangdLSPServer::onFileUpdated(PathRef File
, const TUStatus
&Status
) {
1863 if (!SupportFileStatus
)
1865 // FIXME: we don't emit "BuildingFile" and `RunningAction`, as these
1866 // two statuses are running faster in practice, which leads the UI constantly
1867 // changing, and doesn't provide much value. We may want to emit status at a
1868 // reasonable time interval (e.g. 0.5s).
1869 if (Status
.PreambleActivity
== PreambleAction::Idle
&&
1870 (Status
.ASTActivity
.K
== ASTAction::Building
||
1871 Status
.ASTActivity
.K
== ASTAction::RunningAction
))
1873 NotifyFileStatus(Status
.render(File
));
1876 void ClangdLSPServer::onSemanticsMaybeChanged(PathRef File
) {
1877 if (SemanticTokensRefresh
) {
1878 SemanticTokensRefresh(NoParams
{}, [](llvm::Expected
<std::nullptr_t
> E
) {
1881 elog("Failed to refresh semantic tokens: {0}", E
.takeError());
1886 } // namespace clangd
1887 } // namespace clang