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