344 lines · c
1//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H11 12#include "ClangdServer.h"13#include "Diagnostics.h"14#include "GlobalCompilationDatabase.h"15#include "LSPBinder.h"16#include "Protocol.h"17#include "Transport.h"18#include "support/Context.h"19#include "support/MemoryTree.h"20#include "support/Path.h"21#include "support/Threading.h"22#include "llvm/ADT/ArrayRef.h"23#include "llvm/Support/JSON.h"24#include <chrono>25#include <cstddef>26#include <cstdint>27#include <memory>28#include <optional>29#include <vector>30 31namespace clang {32namespace clangd {33 34/// This class exposes ClangdServer's capabilities via Language Server Protocol.35///36/// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to37/// corresponding JSON-RPC methods ("initialize").38/// The server also supports $/cancelRequest (MessageHandler provides this).39class ClangdLSPServer : private ClangdServer::Callbacks,40 private LSPBinder::RawOutgoing {41public:42 struct Options : ClangdServer::Options {43 /// Supplies configuration (overrides ClangdServer::ContextProvider).44 config::Provider *ConfigProvider = nullptr;45 /// Look for compilation databases, rather than using compile commands46 /// set via LSP (extensions) only.47 bool UseDirBasedCDB = true;48 /// The offset-encoding to use, or std::nullopt to negotiate it over LSP.49 std::optional<OffsetEncoding> Encoding;50 /// If set, periodically called to release memory.51 /// Consider malloc_trim(3)52 std::function<void()> MemoryCleanup = nullptr;53 54 /// Per-feature options. Generally ClangdServer lets these vary55 /// per-request, but LSP allows limited/no customizations.56 clangd::CodeCompleteOptions CodeComplete;57 MarkupKind SignatureHelpDocumentationFormat = MarkupKind::PlainText;58 clangd::RenameOptions Rename;59 /// Returns true if the tweak should be enabled.60 std::function<bool(const Tweak &)> TweakFilter = [](const Tweak &T) {61 return !T.hidden(); // only enable non-hidden tweaks.62 };63 64 /// Limit the number of references returned (0 means no limit).65 size_t ReferencesLimit = 0;66 67 /// Flag to hint the experimental modules support is enabled.68 bool EnableExperimentalModulesSupport = false;69 };70 71 ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS,72 const ClangdLSPServer::Options &Opts);73 /// The destructor blocks on any outstanding background tasks.74 ~ClangdLSPServer();75 76 ClangdLSPServer(const ClangdLSPServer &other) = delete;77 ClangdLSPServer &operator=(const ClangdLSPServer &other) = delete;78 79 /// Run LSP server loop, communicating with the Transport provided in the80 /// constructor. This method must not be executed more than once.81 ///82 /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.83 bool run();84 85 /// Profiles resource-usage.86 void profile(MemoryTree &MT) const;87 88private:89 // Implement ClangdServer::Callbacks.90 void onDiagnosticsReady(PathRef File, llvm::StringRef Version,91 llvm::ArrayRef<Diag> Diagnostics) override;92 void onFileUpdated(PathRef File, const TUStatus &Status) override;93 void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;94 void onSemanticsMaybeChanged(PathRef File) override;95 void onInactiveRegionsReady(PathRef File,96 std::vector<Range> InactiveRegions) override;97 98 // LSP methods. Notifications have signature void(const Params&).99 // Calls have signature void(const Params&, Callback<Response>).100 void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);101 void onInitialized(const InitializedParams &);102 void onShutdown(const NoParams &, Callback<std::nullptr_t>);103 void onSync(const NoParams &, Callback<std::nullptr_t>);104 void onDocumentDidOpen(const DidOpenTextDocumentParams &);105 void onDocumentDidChange(const DidChangeTextDocumentParams &);106 void onDocumentDidClose(const DidCloseTextDocumentParams &);107 void onDocumentDidSave(const DidSaveTextDocumentParams &);108 void onAST(const ASTParams &, Callback<std::optional<ASTNode>>);109 void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &,110 Callback<std::vector<TextEdit>>);111 void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,112 Callback<std::vector<TextEdit>>);113 void onDocumentRangesFormatting(const DocumentRangesFormattingParams &,114 Callback<std::vector<TextEdit>>);115 void onDocumentFormatting(const DocumentFormattingParams &,116 Callback<std::vector<TextEdit>>);117 // The results are serialized 'vector<DocumentSymbol>' if118 // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>'119 // otherwise.120 void onDocumentSymbol(const DocumentSymbolParams &,121 Callback<llvm::json::Value>);122 void onFoldingRange(const FoldingRangeParams &,123 Callback<std::vector<FoldingRange>>);124 void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>);125 void onCompletion(const CompletionParams &, Callback<CompletionList>);126 void onSignatureHelp(const TextDocumentPositionParams &,127 Callback<SignatureHelp>);128 void onGoToDeclaration(const TextDocumentPositionParams &,129 Callback<std::vector<Location>>);130 void onGoToDefinition(const TextDocumentPositionParams &,131 Callback<std::vector<Location>>);132 void onGoToType(const TextDocumentPositionParams &,133 Callback<std::vector<Location>>);134 void onGoToImplementation(const TextDocumentPositionParams &,135 Callback<std::vector<Location>>);136 void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>);137 void onSwitchSourceHeader(const TextDocumentIdentifier &,138 Callback<std::optional<URIForFile>>);139 void onDocumentHighlight(const TextDocumentPositionParams &,140 Callback<std::vector<DocumentHighlight>>);141 void onFileEvent(const DidChangeWatchedFilesParams &);142 void onWorkspaceSymbol(const WorkspaceSymbolParams &,143 Callback<std::vector<SymbolInformation>>);144 void onPrepareRename(const TextDocumentPositionParams &,145 Callback<PrepareRenameResult>);146 void onRename(const RenameParams &, Callback<WorkspaceEdit>);147 void onHover(const TextDocumentPositionParams &,148 Callback<std::optional<Hover>>);149 void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &,150 Callback<std::vector<TypeHierarchyItem>>);151 void onSuperTypes(const ResolveTypeHierarchyItemParams &,152 Callback<std::optional<std::vector<TypeHierarchyItem>>>);153 void onSubTypes(const ResolveTypeHierarchyItemParams &,154 Callback<std::vector<TypeHierarchyItem>>);155 void onTypeHierarchy(const TypeHierarchyPrepareParams &,156 Callback<llvm::json::Value>);157 void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,158 Callback<llvm::json::Value>);159 void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,160 Callback<std::vector<CallHierarchyItem>>);161 void onCallHierarchyIncomingCalls(162 const CallHierarchyIncomingCallsParams &,163 Callback<std::vector<CallHierarchyIncomingCall>>);164 void onCallHierarchyOutgoingCalls(165 const CallHierarchyOutgoingCallsParams &,166 Callback<std::vector<CallHierarchyOutgoingCall>>);167 void onClangdInlayHints(const InlayHintsParams &,168 Callback<llvm::json::Value>);169 void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>);170 void onChangeConfiguration(const DidChangeConfigurationParams &);171 void onSymbolInfo(const TextDocumentPositionParams &,172 Callback<std::vector<SymbolDetails>>);173 void onSelectionRange(const SelectionRangeParams &,174 Callback<std::vector<SelectionRange>>);175 void onDocumentLink(const DocumentLinkParams &,176 Callback<std::vector<DocumentLink>>);177 void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>);178 void onSemanticTokensDelta(const SemanticTokensDeltaParams &,179 Callback<SemanticTokensOrDelta>);180 /// This is a clangd extension. Provides a json tree representing memory usage181 /// hierarchy.182 void onMemoryUsage(const NoParams &, Callback<MemoryTree>);183 void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>);184 185 /// Implement commands.186 void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>);187 void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>);188 void onCommandApplyRename(const RenameParams &, Callback<llvm::json::Value>);189 190 /// Outgoing LSP calls.191 LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams,192 ApplyWorkspaceEditResponse>193 ApplyWorkspaceEdit;194 LSPBinder::OutgoingNotification<ShowMessageParams> ShowMessage;195 LSPBinder::OutgoingNotification<PublishDiagnosticsParams> PublishDiagnostics;196 LSPBinder::OutgoingNotification<FileStatus> NotifyFileStatus;197 LSPBinder::OutgoingNotification<InactiveRegionsParams> PublishInactiveRegions;198 LSPBinder::OutgoingMethod<WorkDoneProgressCreateParams, std::nullptr_t>199 CreateWorkDoneProgress;200 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressBegin>>201 BeginWorkDoneProgress;202 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressReport>>203 ReportWorkDoneProgress;204 LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressEnd>>205 EndWorkDoneProgress;206 LSPBinder::OutgoingMethod<NoParams, std::nullptr_t> SemanticTokensRefresh;207 208 void applyEdit(WorkspaceEdit WE, llvm::json::Value Success,209 Callback<llvm::json::Value> Reply);210 211 void bindMethods(LSPBinder &, const ClientCapabilities &Caps);212 std::optional<ClangdServer::DiagRef> getDiagRef(StringRef File,213 const clangd::Diagnostic &D);214 215 /// Checks if completion request should be ignored. We need this due to the216 /// limitation of the LSP. Per LSP, a client sends requests for all "trigger217 /// character" we specify, but for '>' and ':' we need to check they actually218 /// produce '->' and '::', respectively.219 bool shouldRunCompletion(const CompletionParams &Params) const;220 221 void applyConfiguration(const ConfigurationSettings &Settings);222 223 /// Runs profiling and exports memory usage metrics if tracing is enabled and224 /// profiling hasn't happened recently.225 void maybeExportMemoryProfile();226 PeriodicThrottler ShouldProfile;227 228 /// Run the MemoryCleanup callback if it's time.229 /// This method is thread safe.230 void maybeCleanupMemory();231 PeriodicThrottler ShouldCleanupMemory;232 233 /// Since initialization of CDBs and ClangdServer is done lazily, the234 /// following context captures the one used while creating ClangdLSPServer and235 /// passes it to above mentioned object instances to make sure they share the236 /// same state.237 Context BackgroundContext;238 239 /// Used to indicate that the 'shutdown' request was received from the240 /// Language Server client.241 bool ShutdownRequestReceived = false;242 243 /// Used to indicate the ClangdLSPServer is being destroyed.244 std::atomic<bool> IsBeingDestroyed = {false};245 246 // FIXME: The caching is a temporary solution to get corresponding clangd 247 // diagnostic from a LSP diagnostic.248 // Ideally, ClangdServer can generate an identifier for each diagnostic,249 // emit them via the LSP's data field (which was newly added in LSP 3.16).250 std::mutex DiagRefMutex;251 struct DiagKey {252 clangd::Range Rng;253 std::string Message;254 bool operator<(const DiagKey &Other) const {255 return std::tie(Rng, Message) < std::tie(Other.Rng, Other.Message);256 }257 };258 DiagKey toDiagKey(const clangd::Diagnostic &LSPDiag) {259 return {LSPDiag.range, LSPDiag.message};260 }261 /// A map from LSP diagnostic to clangd-naive diagnostic.262 typedef std::map<DiagKey, ClangdServer::DiagRef>263 DiagnosticToDiagRefMap;264 /// Caches the mapping LSP and clangd-naive diagnostics per file.265 llvm::StringMap<DiagnosticToDiagRefMap>266 DiagRefMap;267 268 // Last semantic-tokens response, for incremental requests.269 std::mutex SemanticTokensMutex;270 llvm::StringMap<SemanticTokens> LastSemanticTokens;271 272 // Most code should not deal with Transport, callMethod, notify directly.273 // Use LSPBinder to handle incoming and outgoing calls.274 clangd::Transport &Transp;275 class MessageHandler;276 std::unique_ptr<MessageHandler> MsgHandler;277 std::mutex TranspWriter;278 279 void callMethod(StringRef Method, llvm::json::Value Params,280 Callback<llvm::json::Value> CB) override;281 void notify(StringRef Method, llvm::json::Value Params) override;282 283 LSPBinder::RawHandlers Handlers;284 285 const ThreadsafeFS &TFS;286 /// Options used for diagnostics.287 ClangdDiagnosticOptions DiagOpts;288 /// The supported kinds of the client.289 SymbolKindBitset SupportedSymbolKinds;290 /// The supported completion item kinds of the client.291 CompletionItemKindBitset SupportedCompletionItemKinds;292 // Whether the client supports CompletionItem.labelDetails.293 bool SupportsCompletionLabelDetails = false;294 /// Whether the client supports CodeAction response objects.295 bool SupportsCodeAction = false;296 /// From capabilities of textDocument/documentSymbol.297 bool SupportsHierarchicalDocumentSymbol = false;298 /// Whether the client supports showing file status.299 bool SupportFileStatus = false;300 /// Whether the client supports attaching a container string to references.301 bool SupportsReferenceContainer = false;302 /// Which kind of markup should we use in textDocument/hover responses.303 MarkupKind HoverContentFormat = MarkupKind::PlainText;304 /// Whether the client supports offsets for parameter info labels.305 bool SupportsOffsetsInSignatureHelp = false;306 /// Whether the client supports the versioned document changes.307 bool SupportsDocumentChanges = false;308 /// Whether the client supports change annotations on text edits.309 bool SupportsChangeAnnotation = false;310 311 std::mutex BackgroundIndexProgressMutex;312 enum class BackgroundIndexProgress {313 // Client doesn't support reporting progress. No transitions possible.314 Unsupported,315 // The queue is idle, and the client has no progress bar.316 // Can transition to Creating when we have some activity.317 Empty,318 // We've requested the client to create a progress bar.319 // Meanwhile, the state is buffered in PendingBackgroundIndexProgress.320 Creating,321 // The client has a progress bar, and we can send it updates immediately.322 Live,323 } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;324 // The progress to send when the progress bar is created.325 // Only valid in state Creating.326 BackgroundQueue::Stats PendingBackgroundIndexProgress;327 /// LSP extension: skip WorkDoneProgressCreate, just send progress streams.328 bool BackgroundIndexSkipCreate = false;329 330 Options Opts;331 // The CDB is created by the "initialize" LSP method.332 std::unique_ptr<GlobalCompilationDatabase> BaseCDB;333 // CDB is BaseCDB plus any commands overridden via LSP extensions.334 std::optional<OverlayCDB> CDB;335 // The ClangdServer is created by the "initialize" LSP method.336 std::optional<ClangdServer> Server;337 // Manages to build module files.338 std::optional<ModulesBuilder> ModulesManager;339};340} // namespace clangd341} // namespace clang342 343#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H344