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/Path.h"
21 #include "clang/Tooling/Core/Replacement.h"
22 #include "llvm/ADT/Optional.h"
23 #include "llvm/ADT/StringSet.h"
24 #include "llvm/Support/JSON.h"
25 #include <memory>
26 
27 namespace clang {
28 namespace clangd {
29 
30 class SymbolIndex;
31 
32 /// This class exposes ClangdServer's capabilities via Language Server Protocol.
33 ///
34 /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to
35 /// corresponding JSON-RPC methods ("initialize").
36 /// The server also supports $/cancelRequest (MessageHandler provides this).
37 class ClangdLSPServer : private ClangdServer::Callbacks {
38 public:
39   /// If \p CompileCommandsDir has a value, compile_commands.json will be
40   /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
41   /// for compile_commands.json in all parent directories of each file.
42   /// If UseDirBasedCDB is false, compile commands are not read from disk.
43   // FIXME: Clean up signature around CDBs.
44   ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS,
45                   const clangd::CodeCompleteOptions &CCOpts,
46                   const clangd::RenameOptions &RenameOpts,
47                   llvm::Optional<Path> CompileCommandsDir, bool UseDirBasedCDB,
48                   llvm::Optional<OffsetEncoding> ForcedOffsetEncoding,
49                   const ClangdServer::Options &Opts);
50   /// The destructor blocks on any outstanding background tasks.
51   ~ClangdLSPServer();
52 
53   /// Run LSP server loop, communicating with the Transport provided in the
54   /// constructor. This method must not be executed more than once.
55   ///
56   /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
57   bool run();
58 
59 private:
60   // Implement ClangdServer::Callbacks.
61   void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
62                           std::vector<Diag> Diagnostics) override;
63   void onFileUpdated(PathRef File, const TUStatus &Status) override;
64   void
65   onHighlightingsReady(PathRef File, llvm::StringRef Version,
66                        std::vector<HighlightingToken> Highlightings) override;
67   void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
68 
69   // LSP methods. Notifications have signature void(const Params&).
70   // Calls have signature void(const Params&, Callback<Response>).
71   void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);
72   void onInitialized(const InitializedParams &);
73   void onShutdown(const ShutdownParams &, Callback<std::nullptr_t>);
74   void onSync(const NoParams &, Callback<std::nullptr_t>);
75   void onDocumentDidOpen(const DidOpenTextDocumentParams &);
76   void onDocumentDidChange(const DidChangeTextDocumentParams &);
77   void onDocumentDidClose(const DidCloseTextDocumentParams &);
78   void onDocumentDidSave(const DidSaveTextDocumentParams &);
79   void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &,
80                                   Callback<std::vector<TextEdit>>);
81   void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
82                                  Callback<std::vector<TextEdit>>);
83   void onDocumentFormatting(const DocumentFormattingParams &,
84                             Callback<std::vector<TextEdit>>);
85   // The results are serialized 'vector<DocumentSymbol>' if
86   // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>'
87   // otherwise.
88   void onDocumentSymbol(const DocumentSymbolParams &,
89                         Callback<llvm::json::Value>);
90   void onFoldingRange(const FoldingRangeParams &,
91                       Callback<std::vector<FoldingRange>>);
92   void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>);
93   void onCompletion(const CompletionParams &, Callback<CompletionList>);
94   void onSignatureHelp(const TextDocumentPositionParams &,
95                        Callback<SignatureHelp>);
96   void onGoToDeclaration(const TextDocumentPositionParams &,
97                          Callback<std::vector<Location>>);
98   void onGoToDefinition(const TextDocumentPositionParams &,
99                         Callback<std::vector<Location>>);
100   void onReference(const ReferenceParams &, Callback<std::vector<Location>>);
101   void onSwitchSourceHeader(const TextDocumentIdentifier &,
102                             Callback<llvm::Optional<URIForFile>>);
103   void onDocumentHighlight(const TextDocumentPositionParams &,
104                            Callback<std::vector<DocumentHighlight>>);
105   void onFileEvent(const DidChangeWatchedFilesParams &);
106   void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>);
107   void onWorkspaceSymbol(const WorkspaceSymbolParams &,
108                          Callback<std::vector<SymbolInformation>>);
109   void onPrepareRename(const TextDocumentPositionParams &,
110                        Callback<llvm::Optional<Range>>);
111   void onRename(const RenameParams &, Callback<WorkspaceEdit>);
112   void onHover(const TextDocumentPositionParams &,
113                Callback<llvm::Optional<Hover>>);
114   void onTypeHierarchy(const TypeHierarchyParams &,
115                        Callback<llvm::Optional<TypeHierarchyItem>>);
116   void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
117                               Callback<llvm::Optional<TypeHierarchyItem>>);
118   void onChangeConfiguration(const DidChangeConfigurationParams &);
119   void onSymbolInfo(const TextDocumentPositionParams &,
120                     Callback<std::vector<SymbolDetails>>);
121   void onSelectionRange(const SelectionRangeParams &,
122                         Callback<std::vector<SelectionRange>>);
123   void onDocumentLink(const DocumentLinkParams &,
124                       Callback<std::vector<DocumentLink>>);
125   void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>);
126   void onSemanticTokensDelta(const SemanticTokensDeltaParams &,
127                              Callback<SemanticTokensOrDelta>);
128 
129   std::vector<Fix> getFixes(StringRef File, const clangd::Diagnostic &D);
130 
131   /// Checks if completion request should be ignored. We need this due to the
132   /// limitation of the LSP. Per LSP, a client sends requests for all "trigger
133   /// character" we specify, but for '>' and ':' we need to check they actually
134   /// produce '->' and '::', respectively.
135   bool shouldRunCompletion(const CompletionParams &Params) const;
136 
137   /// Requests a reparse of currently opened files using their latest source.
138   /// This will typically only rebuild if something other than the source has
139   /// changed (e.g. the CDB yields different flags, or files included in the
140   /// preamble have been modified).
141   void reparseOpenFilesIfNeeded(
142       llvm::function_ref<bool(llvm::StringRef File)> Filter);
143   void applyConfiguration(const ConfigurationSettings &Settings);
144 
145   /// Sends a "publishSemanticHighlighting" notification to the LSP client.
146   void
147   publishTheiaSemanticHighlighting(const TheiaSemanticHighlightingParams &);
148 
149   /// Sends a "publishDiagnostics" notification to the LSP client.
150   void publishDiagnostics(const PublishDiagnosticsParams &);
151 
152   /// Since initialization of CDBs and ClangdServer is done lazily, the
153   /// following context captures the one used while creating ClangdLSPServer and
154   /// passes it to above mentioned object instances to make sure they share the
155   /// same state.
156   Context BackgroundContext;
157 
158   /// Used to indicate that the 'shutdown' request was received from the
159   /// Language Server client.
160   bool ShutdownRequestReceived = false;
161 
162   /// Used to indicate the ClangdLSPServer is being destroyed.
163   std::atomic<bool> IsBeingDestroyed = {false};
164 
165   std::mutex FixItsMutex;
166   typedef std::map<clangd::Diagnostic, std::vector<Fix>, LSPDiagnosticCompare>
167       DiagnosticToReplacementMap;
168   /// Caches FixIts per file and diagnostics
169   llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
170   std::mutex HighlightingsMutex;
171   llvm::StringMap<std::vector<HighlightingToken>> FileToHighlightings;
172   // Last semantic-tokens response, for incremental requests.
173   std::mutex SemanticTokensMutex;
174   llvm::StringMap<SemanticTokens> LastSemanticTokens;
175 
176   // Most code should not deal with Transport directly.
177   // MessageHandler deals with incoming messages, use call() etc for outgoing.
178   clangd::Transport &Transp;
179   class MessageHandler;
180   std::unique_ptr<MessageHandler> MsgHandler;
181   std::mutex TranspWriter;
182 
183   template <typename Response>
call(StringRef Method,llvm::json::Value Params,Callback<Response> CB)184   void call(StringRef Method, llvm::json::Value Params, Callback<Response> CB) {
185     // Wrap the callback with LSP conversion and error-handling.
186     auto HandleReply =
187         [CB = std::move(CB), Ctx = Context::current().clone()](
188             llvm::Expected<llvm::json::Value> RawResponse) mutable {
189           Response Rsp;
190           if (!RawResponse) {
191             CB(RawResponse.takeError());
192           } else if (fromJSON(*RawResponse, Rsp)) {
193             CB(std::move(Rsp));
194           } else {
195             elog("Failed to decode {0} response", *RawResponse);
196             CB(llvm::make_error<LSPError>("failed to decode response",
197                                           ErrorCode::InvalidParams));
198           }
199         };
200     callRaw(Method, std::move(Params), std::move(HandleReply));
201   }
202   void callRaw(StringRef Method, llvm::json::Value Params,
203                Callback<llvm::json::Value> CB);
204   void notify(StringRef Method, llvm::json::Value Params);
progress(const llvm::json::Value & Token,T Value)205   template <typename T> void progress(const llvm::json::Value &Token, T Value) {
206     ProgressParams<T> Params;
207     Params.token = Token;
208     Params.value = std::move(Value);
209     notify("$/progress", Params);
210   }
211 
212   const ThreadsafeFS &TFS;
213   /// Options used for code completion
214   clangd::CodeCompleteOptions CCOpts;
215   /// Options used for rename.
216   clangd::RenameOptions RenameOpts;
217   /// Options used for diagnostics.
218   ClangdDiagnosticOptions DiagOpts;
219   /// The supported kinds of the client.
220   SymbolKindBitset SupportedSymbolKinds;
221   /// The supported completion item kinds of the client.
222   CompletionItemKindBitset SupportedCompletionItemKinds;
223   /// Whether the client supports CodeAction response objects.
224   bool SupportsCodeAction = false;
225   /// From capabilities of textDocument/documentSymbol.
226   bool SupportsHierarchicalDocumentSymbol = false;
227   /// Whether the client supports showing file status.
228   bool SupportFileStatus = false;
229   /// Which kind of markup should we use in textDocument/hover responses.
230   MarkupKind HoverContentFormat = MarkupKind::PlainText;
231   /// Whether the client supports offsets for parameter info labels.
232   bool SupportsOffsetsInSignatureHelp = false;
233   std::mutex BackgroundIndexProgressMutex;
234   enum class BackgroundIndexProgress {
235     // Client doesn't support reporting progress. No transitions possible.
236     Unsupported,
237     // The queue is idle, and the client has no progress bar.
238     // Can transition to Creating when we have some activity.
239     Empty,
240     // We've requested the client to create a progress bar.
241     // Meanwhile, the state is buffered in PendingBackgroundIndexProgress.
242     Creating,
243     // The client has a progress bar, and we can send it updates immediately.
244     Live,
245   } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
246   // The progress to send when the progress bar is created.
247   // Only valid in state Creating.
248   BackgroundQueue::Stats PendingBackgroundIndexProgress;
249   /// LSP extension: skip WorkDoneProgressCreate, just send progress streams.
250   bool BackgroundIndexSkipCreate = false;
251   // Store of the current versions of the open documents.
252   DraftStore DraftMgr;
253 
254   // The CDB is created by the "initialize" LSP method.
255   bool UseDirBasedCDB;                     // FIXME: make this a capability.
256   llvm::Optional<Path> CompileCommandsDir; // FIXME: merge with capability?
257   std::unique_ptr<GlobalCompilationDatabase> BaseCDB;
258   // CDB is BaseCDB plus any commands overridden via LSP extensions.
259   llvm::Optional<OverlayCDB> CDB;
260   ClangdServer::Options ClangdServerOpts;
261   llvm::Optional<OffsetEncoding> NegotiatedOffsetEncoding;
262   // The ClangdServer is created by the "initialize" LSP method.
263   llvm::Optional<ClangdServer> Server;
264 };
265 } // namespace clangd
266 } // namespace clang
267 
268 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
269