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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
11 
12 #include "ClangdServer.h"
13 #include "DraftStore.h"
14 #include "Features.inc"
15 #include "FindSymbols.h"
16 #include "GlobalCompilationDatabase.h"
17 #include "Protocol.h"
18 #include "Transport.h"
19 #include "support/Context.h"
20 #include "support/MemoryTree.h"
21 #include "support/Path.h"
22 #include "clang/Tooling/Core/Replacement.h"
23 #include "llvm/ADT/Optional.h"
24 #include "llvm/ADT/StringSet.h"
25 #include "llvm/Support/JSON.h"
26 #include <chrono>
27 #include <cstddef>
28 #include <memory>
29 
30 namespace clang {
31 namespace clangd {
32 
33 class SymbolIndex;
34 
35 /// This class exposes ClangdServer's capabilities via Language Server Protocol.
36 ///
37 /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to
38 /// corresponding JSON-RPC methods ("initialize").
39 /// The server also supports $/cancelRequest (MessageHandler provides this).
40 class ClangdLSPServer : private ClangdServer::Callbacks {
41 public:
42   struct Options : ClangdServer::Options {
43     /// Look for compilation databases, rather than using compile commands
44     /// set via LSP (extensions) only.
45     bool UseDirBasedCDB = true;
46     /// A fixed directory to search for a compilation database in.
47     /// If not set, we search upward from the source file.
48     llvm::Optional<Path> CompileCommandsDir;
49     /// The offset-encoding to use, or None to negotiate it over LSP.
50     llvm::Optional<OffsetEncoding> Encoding;
51 
52     /// Per-feature options. Generally ClangdServer lets these vary
53     /// per-request, but LSP allows limited/no customizations.
54     clangd::CodeCompleteOptions CodeComplete;
55     clangd::RenameOptions Rename;
56     /// Returns true if the tweak should be enabled.
57     std::function<bool(const Tweak &)> TweakFilter = [](const Tweak &T) {
58       return !T.hidden(); // only enable non-hidden tweaks.
59     };
60   };
61 
62   ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS,
63                   const ClangdLSPServer::Options &Opts);
64   /// The destructor blocks on any outstanding background tasks.
65   ~ClangdLSPServer();
66 
67   /// Run LSP server loop, communicating with the Transport provided in the
68   /// constructor. This method must not be executed more than once.
69   ///
70   /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
71   bool run();
72 
73   /// Profiles resource-usage.
74   void profile(MemoryTree &MT) const;
75 
76 private:
77   // Implement ClangdServer::Callbacks.
78   void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
79                           std::vector<Diag> Diagnostics) override;
80   void onFileUpdated(PathRef File, const TUStatus &Status) override;
81   void
82   onHighlightingsReady(PathRef File, llvm::StringRef Version,
83                        std::vector<HighlightingToken> Highlightings) override;
84   void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
85 
86   // LSP methods. Notifications have signature void(const Params&).
87   // Calls have signature void(const Params&, Callback<Response>).
88   void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);
89   void onInitialized(const InitializedParams &);
90   void onShutdown(const ShutdownParams &, Callback<std::nullptr_t>);
91   void onSync(const NoParams &, Callback<std::nullptr_t>);
92   void onDocumentDidOpen(const DidOpenTextDocumentParams &);
93   void onDocumentDidChange(const DidChangeTextDocumentParams &);
94   void onDocumentDidClose(const DidCloseTextDocumentParams &);
95   void onDocumentDidSave(const DidSaveTextDocumentParams &);
96   void onAST(const ASTParams &, Callback<llvm::Optional<ASTNode>>);
97   void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &,
98                                   Callback<std::vector<TextEdit>>);
99   void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
100                                  Callback<std::vector<TextEdit>>);
101   void onDocumentFormatting(const DocumentFormattingParams &,
102                             Callback<std::vector<TextEdit>>);
103   // The results are serialized 'vector<DocumentSymbol>' if
104   // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>'
105   // otherwise.
106   void onDocumentSymbol(const DocumentSymbolParams &,
107                         Callback<llvm::json::Value>);
108   void onFoldingRange(const FoldingRangeParams &,
109                       Callback<std::vector<FoldingRange>>);
110   void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>);
111   void onCompletion(const CompletionParams &, Callback<CompletionList>);
112   void onSignatureHelp(const TextDocumentPositionParams &,
113                        Callback<SignatureHelp>);
114   void onGoToDeclaration(const TextDocumentPositionParams &,
115                          Callback<std::vector<Location>>);
116   void onGoToDefinition(const TextDocumentPositionParams &,
117                         Callback<std::vector<Location>>);
118   void onGoToImplementation(const TextDocumentPositionParams &,
119                             Callback<std::vector<Location>>);
120   void onReference(const ReferenceParams &, Callback<std::vector<Location>>);
121   void onSwitchSourceHeader(const TextDocumentIdentifier &,
122                             Callback<llvm::Optional<URIForFile>>);
123   void onDocumentHighlight(const TextDocumentPositionParams &,
124                            Callback<std::vector<DocumentHighlight>>);
125   void onFileEvent(const DidChangeWatchedFilesParams &);
126   void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>);
127   void onWorkspaceSymbol(const WorkspaceSymbolParams &,
128                          Callback<std::vector<SymbolInformation>>);
129   void onPrepareRename(const TextDocumentPositionParams &,
130                        Callback<llvm::Optional<Range>>);
131   void onRename(const RenameParams &, Callback<WorkspaceEdit>);
132   void onHover(const TextDocumentPositionParams &,
133                Callback<llvm::Optional<Hover>>);
134   void onTypeHierarchy(const TypeHierarchyParams &,
135                        Callback<llvm::Optional<TypeHierarchyItem>>);
136   void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
137                               Callback<llvm::Optional<TypeHierarchyItem>>);
138   void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,
139                               Callback<std::vector<CallHierarchyItem>>);
140   void onCallHierarchyIncomingCalls(
141       const CallHierarchyIncomingCallsParams &,
142       Callback<std::vector<CallHierarchyIncomingCall>>);
143   void onCallHierarchyOutgoingCalls(
144       const CallHierarchyOutgoingCallsParams &,
145       Callback<std::vector<CallHierarchyOutgoingCall>>);
146   void onChangeConfiguration(const DidChangeConfigurationParams &);
147   void onSymbolInfo(const TextDocumentPositionParams &,
148                     Callback<std::vector<SymbolDetails>>);
149   void onSelectionRange(const SelectionRangeParams &,
150                         Callback<std::vector<SelectionRange>>);
151   void onDocumentLink(const DocumentLinkParams &,
152                       Callback<std::vector<DocumentLink>>);
153   void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>);
154   void onSemanticTokensDelta(const SemanticTokensDeltaParams &,
155                              Callback<SemanticTokensOrDelta>);
156   /// This is a clangd extension. Provides a json tree representing memory usage
157   /// hierarchy.
158   void onMemoryUsage(const NoParams &, Callback<MemoryTree>);
159 
160   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
161 
162   /// Checks if completion request should be ignored. We need this due to the
163   /// limitation of the LSP. Per LSP, a client sends requests for all "trigger
164   /// character" we specify, but for '>' and ':' we need to check they actually
165   /// produce '->' and '::', respectively.
166   bool shouldRunCompletion(const CompletionParams &Params) const;
167 
168   /// Requests a reparse of currently opened files using their latest source.
169   /// This will typically only rebuild if something other than the source has
170   /// changed (e.g. the CDB yields different flags, or files included in the
171   /// preamble have been modified).
172   void reparseOpenFilesIfNeeded(
173       llvm::function_ref<bool(llvm::StringRef File)> Filter);
174   void applyConfiguration(const ConfigurationSettings &Settings);
175 
176   /// Sends a "publishSemanticHighlighting" notification to the LSP client.
177   void
178   publishTheiaSemanticHighlighting(const TheiaSemanticHighlightingParams &);
179 
180   /// Sends a "publishDiagnostics" notification to the LSP client.
181   void publishDiagnostics(const PublishDiagnosticsParams &);
182 
183   /// Runs profiling and exports memory usage metrics if tracing is enabled and
184   /// profiling hasn't happened recently.
185   void maybeExportMemoryProfile();
186 
187   /// Timepoint until which profiling is off. It is used to throttle profiling
188   /// requests.
189   std::chrono::steady_clock::time_point NextProfileTime;
190 
191   /// Since initialization of CDBs and ClangdServer is done lazily, the
192   /// following context captures the one used while creating ClangdLSPServer and
193   /// passes it to above mentioned object instances to make sure they share the
194   /// same state.
195   Context BackgroundContext;
196 
197   /// Used to indicate that the 'shutdown' request was received from the
198   /// Language Server client.
199   bool ShutdownRequestReceived = false;
200 
201   /// Used to indicate the ClangdLSPServer is being destroyed.
202   std::atomic<bool> IsBeingDestroyed = {false};
203 
204   std::mutex FixItsMutex;
205   typedef std::map<clangd::Diagnostic, std::vector<Fix>, LSPDiagnosticCompare>
206       DiagnosticToReplacementMap;
207   /// Caches FixIts per file and diagnostics
208   llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
209   std::mutex HighlightingsMutex;
210   llvm::StringMap<std::vector<HighlightingToken>> FileToHighlightings;
211   // Last semantic-tokens response, for incremental requests.
212   std::mutex SemanticTokensMutex;
213   llvm::StringMap<SemanticTokens> LastSemanticTokens;
214 
215   // Most code should not deal with Transport directly.
216   // MessageHandler deals with incoming messages, use call() etc for outgoing.
217   clangd::Transport &Transp;
218   class MessageHandler;
219   std::unique_ptr<MessageHandler> MsgHandler;
220   std::mutex TranspWriter;
221 
222   template <typename T>
parse(const llvm::json::Value & Raw,llvm::StringRef PayloadName,llvm::StringRef PayloadKind)223   static Expected<T> parse(const llvm::json::Value &Raw,
224                            llvm::StringRef PayloadName,
225                            llvm::StringRef PayloadKind) {
226     T Result;
227     llvm::json::Path::Root Root;
228     if (!fromJSON(Raw, Result, Root)) {
229       elog("Failed to decode {0} {1}: {2}", PayloadName, PayloadKind,
230            Root.getError());
231       // Dump the relevant parts of the broken message.
232       std::string Context;
233       llvm::raw_string_ostream OS(Context);
234       Root.printErrorContext(Raw, OS);
235       vlog("{0}", OS.str());
236       // Report the error (e.g. to the client).
237       return llvm::make_error<LSPError>(
238           llvm::formatv("failed to decode {0} {1}: {2}", PayloadName,
239                         PayloadKind, fmt_consume(Root.getError())),
240           ErrorCode::InvalidParams);
241     }
242     return std::move(Result);
243   }
244 
245   template <typename Response>
call(StringRef Method,llvm::json::Value Params,Callback<Response> CB)246   void call(StringRef Method, llvm::json::Value Params, Callback<Response> CB) {
247     // Wrap the callback with LSP conversion and error-handling.
248     auto HandleReply =
249         [CB = std::move(CB), Ctx = Context::current().clone(),
250          Method = Method.str()](
251             llvm::Expected<llvm::json::Value> RawResponse) mutable {
252           if (!RawResponse)
253             return CB(RawResponse.takeError());
254           CB(parse<Response>(*RawResponse, Method, "response"));
255         };
256     callRaw(Method, std::move(Params), std::move(HandleReply));
257   }
258   void callRaw(StringRef Method, llvm::json::Value Params,
259                Callback<llvm::json::Value> CB);
260   void notify(StringRef Method, llvm::json::Value Params);
progress(const llvm::json::Value & Token,T Value)261   template <typename T> void progress(const llvm::json::Value &Token, T Value) {
262     ProgressParams<T> Params;
263     Params.token = Token;
264     Params.value = std::move(Value);
265     notify("$/progress", Params);
266   }
267 
268   const ThreadsafeFS &TFS;
269   /// Options used for diagnostics.
270   ClangdDiagnosticOptions DiagOpts;
271   /// The supported kinds of the client.
272   SymbolKindBitset SupportedSymbolKinds;
273   /// The supported completion item kinds of the client.
274   CompletionItemKindBitset SupportedCompletionItemKinds;
275   /// Whether the client supports CodeAction response objects.
276   bool SupportsCodeAction = false;
277   /// From capabilities of textDocument/documentSymbol.
278   bool SupportsHierarchicalDocumentSymbol = false;
279   /// Whether the client supports showing file status.
280   bool SupportFileStatus = false;
281   /// Which kind of markup should we use in textDocument/hover responses.
282   MarkupKind HoverContentFormat = MarkupKind::PlainText;
283   /// Whether the client supports offsets for parameter info labels.
284   bool SupportsOffsetsInSignatureHelp = false;
285   std::mutex BackgroundIndexProgressMutex;
286   enum class BackgroundIndexProgress {
287     // Client doesn't support reporting progress. No transitions possible.
288     Unsupported,
289     // The queue is idle, and the client has no progress bar.
290     // Can transition to Creating when we have some activity.
291     Empty,
292     // We've requested the client to create a progress bar.
293     // Meanwhile, the state is buffered in PendingBackgroundIndexProgress.
294     Creating,
295     // The client has a progress bar, and we can send it updates immediately.
296     Live,
297   } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
298   // The progress to send when the progress bar is created.
299   // Only valid in state Creating.
300   BackgroundQueue::Stats PendingBackgroundIndexProgress;
301   /// LSP extension: skip WorkDoneProgressCreate, just send progress streams.
302   bool BackgroundIndexSkipCreate = false;
303   // Store of the current versions of the open documents.
304   DraftStore DraftMgr;
305 
306   Options Opts;
307   // The CDB is created by the "initialize" LSP method.
308   std::unique_ptr<GlobalCompilationDatabase> BaseCDB;
309   // CDB is BaseCDB plus any commands overridden via LSP extensions.
310   llvm::Optional<OverlayCDB> CDB;
311   // The ClangdServer is created by the "initialize" LSP method.
312   llvm::Optional<ClangdServer> Server;
313 };
314 } // namespace clangd
315 } // namespace clang
316 
317 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
318