summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/ClangdLSPServer.h
blob: 33c8d221f9bd4c7542c090549e9d8122732a6d49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H

#include "ClangdServer.h"
#include "GlobalCompilationDatabase.h"
#include "LSPBinder.h"
#include "Protocol.h"
#include "Transport.h"
#include "support/Context.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/Threading.h"
#include "llvm/Support/JSON.h"
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>

namespace clang {
namespace clangd {

/// This class exposes ClangdServer's capabilities via Language Server Protocol.
///
/// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to
/// corresponding JSON-RPC methods ("initialize").
/// The server also supports $/cancelRequest (MessageHandler provides this).
class ClangdLSPServer : private ClangdServer::Callbacks,
                        private LSPBinder::RawOutgoing {
public:
  struct Options : ClangdServer::Options {
    /// Supplies configuration (overrides ClangdServer::ContextProvider).
    config::Provider *ConfigProvider = nullptr;
    /// Look for compilation databases, rather than using compile commands
    /// set via LSP (extensions) only.
    bool UseDirBasedCDB = true;
    /// The offset-encoding to use, or std::nullopt to negotiate it over LSP.
    std::optional<OffsetEncoding> Encoding;
    /// If set, periodically called to release memory.
    /// Consider malloc_trim(3)
    std::function<void()> MemoryCleanup = nullptr;

    /// Per-feature options. Generally ClangdServer lets these vary
    /// per-request, but LSP allows limited/no customizations.
    clangd::CodeCompleteOptions CodeComplete;
    MarkupKind SignatureHelpDocumentationFormat = MarkupKind::PlainText;
    clangd::RenameOptions Rename;
    /// Returns true if the tweak should be enabled.
    std::function<bool(const Tweak &)> TweakFilter = [](const Tweak &T) {
      return !T.hidden(); // only enable non-hidden tweaks.
    };

    /// Limit the number of references returned (0 means no limit).
    size_t ReferencesLimit = 0;
  };

  ClangdLSPServer(Transport &Transp, const ThreadsafeFS &TFS,
                  const ClangdLSPServer::Options &Opts);
  /// The destructor blocks on any outstanding background tasks.
  ~ClangdLSPServer();

  /// Run LSP server loop, communicating with the Transport provided in the
  /// constructor. This method must not be executed more than once.
  ///
  /// \return Whether we shut down cleanly with a 'shutdown' -> 'exit' sequence.
  bool run();

  /// Profiles resource-usage.
  void profile(MemoryTree &MT) const;

private:
  // Implement ClangdServer::Callbacks.
  void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
                          std::vector<Diag> Diagnostics) override;
  void onFileUpdated(PathRef File, const TUStatus &Status) override;
  void onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) override;
  void onSemanticsMaybeChanged(PathRef File) override;
  void onInactiveRegionsReady(PathRef File,
                              std::vector<Range> InactiveRegions) override;

  // LSP methods. Notifications have signature void(const Params&).
  // Calls have signature void(const Params&, Callback<Response>).
  void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);
  void onInitialized(const InitializedParams &);
  void onShutdown(const NoParams &, Callback<std::nullptr_t>);
  void onSync(const NoParams &, Callback<std::nullptr_t>);
  void onDocumentDidOpen(const DidOpenTextDocumentParams &);
  void onDocumentDidChange(const DidChangeTextDocumentParams &);
  void onDocumentDidClose(const DidCloseTextDocumentParams &);
  void onDocumentDidSave(const DidSaveTextDocumentParams &);
  void onAST(const ASTParams &, Callback<std::optional<ASTNode>>);
  void onDocumentOnTypeFormatting(const DocumentOnTypeFormattingParams &,
                                  Callback<std::vector<TextEdit>>);
  void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
                                 Callback<std::vector<TextEdit>>);
  void onDocumentFormatting(const DocumentFormattingParams &,
                            Callback<std::vector<TextEdit>>);
  // The results are serialized 'vector<DocumentSymbol>' if
  // SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>'
  // otherwise.
  void onDocumentSymbol(const DocumentSymbolParams &,
                        Callback<llvm::json::Value>);
  void onFoldingRange(const FoldingRangeParams &,
                      Callback<std::vector<FoldingRange>>);
  void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>);
  void onCompletion(const CompletionParams &, Callback<CompletionList>);
  void onSignatureHelp(const TextDocumentPositionParams &,
                       Callback<SignatureHelp>);
  void onGoToDeclaration(const TextDocumentPositionParams &,
                         Callback<std::vector<Location>>);
  void onGoToDefinition(const TextDocumentPositionParams &,
                        Callback<std::vector<Location>>);
  void onGoToType(const TextDocumentPositionParams &,
                  Callback<std::vector<Location>>);
  void onGoToImplementation(const TextDocumentPositionParams &,
                            Callback<std::vector<Location>>);
  void onReference(const ReferenceParams &, Callback<std::vector<ReferenceLocation>>);
  void onSwitchSourceHeader(const TextDocumentIdentifier &,
                            Callback<std::optional<URIForFile>>);
  void onDocumentHighlight(const TextDocumentPositionParams &,
                           Callback<std::vector<DocumentHighlight>>);
  void onFileEvent(const DidChangeWatchedFilesParams &);
  void onWorkspaceSymbol(const WorkspaceSymbolParams &,
                         Callback<std::vector<SymbolInformation>>);
  void onPrepareRename(const TextDocumentPositionParams &,
                       Callback<std::optional<Range>>);
  void onRename(const RenameParams &, Callback<WorkspaceEdit>);
  void onHover(const TextDocumentPositionParams &,
               Callback<std::optional<Hover>>);
  void onPrepareTypeHierarchy(const TypeHierarchyPrepareParams &,
                              Callback<std::vector<TypeHierarchyItem>>);
  void onSuperTypes(const ResolveTypeHierarchyItemParams &,
                    Callback<std::optional<std::vector<TypeHierarchyItem>>>);
  void onSubTypes(const ResolveTypeHierarchyItemParams &,
                  Callback<std::vector<TypeHierarchyItem>>);
  void onTypeHierarchy(const TypeHierarchyPrepareParams &,
                       Callback<llvm::json::Value>);
  void onResolveTypeHierarchy(const ResolveTypeHierarchyItemParams &,
                              Callback<llvm::json::Value>);
  void onPrepareCallHierarchy(const CallHierarchyPrepareParams &,
                              Callback<std::vector<CallHierarchyItem>>);
  void onCallHierarchyIncomingCalls(
      const CallHierarchyIncomingCallsParams &,
      Callback<std::vector<CallHierarchyIncomingCall>>);
  void onCallHierarchyOutgoingCalls(
      const CallHierarchyOutgoingCallsParams &,
      Callback<std::vector<CallHierarchyOutgoingCall>>);
  void onClangdInlayHints(const InlayHintsParams &,
                          Callback<llvm::json::Value>);
  void onInlayHint(const InlayHintsParams &, Callback<std::vector<InlayHint>>);
  void onChangeConfiguration(const DidChangeConfigurationParams &);
  void onSymbolInfo(const TextDocumentPositionParams &,
                    Callback<std::vector<SymbolDetails>>);
  void onSelectionRange(const SelectionRangeParams &,
                        Callback<std::vector<SelectionRange>>);
  void onDocumentLink(const DocumentLinkParams &,
                      Callback<std::vector<DocumentLink>>);
  void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>);
  void onSemanticTokensDelta(const SemanticTokensDeltaParams &,
                             Callback<SemanticTokensOrDelta>);
  /// This is a clangd extension. Provides a json tree representing memory usage
  /// hierarchy.
  void onMemoryUsage(const NoParams &, Callback<MemoryTree>);
  void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>);

  /// Implement commands.
  void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>);
  void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>);

  /// Outgoing LSP calls.
  LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams,
                            ApplyWorkspaceEditResponse>
      ApplyWorkspaceEdit;
  LSPBinder::OutgoingNotification<ShowMessageParams> ShowMessage;
  LSPBinder::OutgoingNotification<PublishDiagnosticsParams> PublishDiagnostics;
  LSPBinder::OutgoingNotification<FileStatus> NotifyFileStatus;
  LSPBinder::OutgoingNotification<InactiveRegionsParams> PublishInactiveRegions;
  LSPBinder::OutgoingMethod<WorkDoneProgressCreateParams, std::nullptr_t>
      CreateWorkDoneProgress;
  LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressBegin>>
      BeginWorkDoneProgress;
  LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressReport>>
      ReportWorkDoneProgress;
  LSPBinder::OutgoingNotification<ProgressParams<WorkDoneProgressEnd>>
      EndWorkDoneProgress;
  LSPBinder::OutgoingMethod<NoParams, std::nullptr_t> SemanticTokensRefresh;

  void applyEdit(WorkspaceEdit WE, llvm::json::Value Success,
                 Callback<llvm::json::Value> Reply);

  void bindMethods(LSPBinder &, const ClientCapabilities &Caps);
  std::vector<CodeAction> getFixes(StringRef File, const clangd::Diagnostic &D);


  /// Checks if completion request should be ignored. We need this due to the
  /// limitation of the LSP. Per LSP, a client sends requests for all "trigger
  /// character" we specify, but for '>' and ':' we need to check they actually
  /// produce '->' and '::', respectively.
  bool shouldRunCompletion(const CompletionParams &Params) const;

  void applyConfiguration(const ConfigurationSettings &Settings);

  /// Runs profiling and exports memory usage metrics if tracing is enabled and
  /// profiling hasn't happened recently.
  void maybeExportMemoryProfile();
  PeriodicThrottler ShouldProfile;

  /// Run the MemoryCleanup callback if it's time.
  /// This method is thread safe.
  void maybeCleanupMemory();
  PeriodicThrottler ShouldCleanupMemory;

  /// Since initialization of CDBs and ClangdServer is done lazily, the
  /// following context captures the one used while creating ClangdLSPServer and
  /// passes it to above mentioned object instances to make sure they share the
  /// same state.
  Context BackgroundContext;

  /// Used to indicate that the 'shutdown' request was received from the
  /// Language Server client.
  bool ShutdownRequestReceived = false;

  /// Used to indicate the ClangdLSPServer is being destroyed.
  std::atomic<bool> IsBeingDestroyed = {false};

  std::mutex FixItsMutex;
  typedef std::map<clangd::Diagnostic, std::vector<CodeAction>,
                   LSPDiagnosticCompare>
      DiagnosticToReplacementMap;
  /// Caches FixIts per file and diagnostics
  llvm::StringMap<DiagnosticToReplacementMap>
      FixItsMap;
  // Last semantic-tokens response, for incremental requests.
  std::mutex SemanticTokensMutex;
  llvm::StringMap<SemanticTokens> LastSemanticTokens;

  // Most code should not deal with Transport, callMethod, notify directly.
  // Use LSPBinder to handle incoming and outgoing calls.
  clangd::Transport &Transp;
  class MessageHandler;
  std::unique_ptr<MessageHandler> MsgHandler;
  std::mutex TranspWriter;

  void callMethod(StringRef Method, llvm::json::Value Params,
                  Callback<llvm::json::Value> CB) override;
  void notify(StringRef Method, llvm::json::Value Params) override;

  LSPBinder::RawHandlers Handlers;

  const ThreadsafeFS &TFS;
  /// Options used for diagnostics.
  ClangdDiagnosticOptions DiagOpts;
  /// The supported kinds of the client.
  SymbolKindBitset SupportedSymbolKinds;
  /// The supported completion item kinds of the client.
  CompletionItemKindBitset SupportedCompletionItemKinds;
  /// Whether the client supports CodeAction response objects.
  bool SupportsCodeAction = false;
  /// From capabilities of textDocument/documentSymbol.
  bool SupportsHierarchicalDocumentSymbol = false;
  /// Whether the client supports showing file status.
  bool SupportFileStatus = false;
  /// Whether the client supports attaching a container string to references.
  bool SupportsReferenceContainer = false;
  /// Which kind of markup should we use in textDocument/hover responses.
  MarkupKind HoverContentFormat = MarkupKind::PlainText;
  /// Whether the client supports offsets for parameter info labels.
  bool SupportsOffsetsInSignatureHelp = false;
  /// Whether the client supports the versioned document changes.
  bool SupportsDocumentChanges = false;
  /// Whether the client supports change annotations on text edits.
  bool SupportsChangeAnnotation = false;

  std::mutex BackgroundIndexProgressMutex;
  enum class BackgroundIndexProgress {
    // Client doesn't support reporting progress. No transitions possible.
    Unsupported,
    // The queue is idle, and the client has no progress bar.
    // Can transition to Creating when we have some activity.
    Empty,
    // We've requested the client to create a progress bar.
    // Meanwhile, the state is buffered in PendingBackgroundIndexProgress.
    Creating,
    // The client has a progress bar, and we can send it updates immediately.
    Live,
  } BackgroundIndexProgressState = BackgroundIndexProgress::Unsupported;
  // The progress to send when the progress bar is created.
  // Only valid in state Creating.
  BackgroundQueue::Stats PendingBackgroundIndexProgress;
  /// LSP extension: skip WorkDoneProgressCreate, just send progress streams.
  bool BackgroundIndexSkipCreate = false;

  Options Opts;
  // The CDB is created by the "initialize" LSP method.
  std::unique_ptr<GlobalCompilationDatabase> BaseCDB;
  // CDB is BaseCDB plus any commands overridden via LSP extensions.
  std::optional<OverlayCDB> CDB;
  // The ClangdServer is created by the "initialize" LSP method.
  std::optional<ClangdServer> Server;
};
} // namespace clangd
} // namespace clang

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H