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 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
|
//===--- ClangdServer.h - Main clangd server code ----------------*- 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_CLANGDSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#include "../clang-tidy/ClangTidyOptions.h"
#include "CodeComplete.h"
#include "ConfigProvider.h"
#include "Diagnostics.h"
#include "DraftStore.h"
#include "FeatureModule.h"
#include "GlobalCompilationDatabase.h"
#include "Hover.h"
#include "Protocol.h"
#include "SemanticHighlighting.h"
#include "TUScheduler.h"
#include "XRefs.h"
#include "index/Background.h"
#include "index/FileIndex.h"
#include "index/Index.h"
#include "refactor/Rename.h"
#include "refactor/Tweak.h"
#include "support/Cancellation.h"
#include "support/Function.h"
#include "support/MemoryTree.h"
#include "support/Path.h"
#include "support/ThreadsafeFS.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace clang {
namespace clangd {
/// Manages a collection of source files and derived data (ASTs, indexes),
/// and provides language-aware features such as code completion.
///
/// The primary client is ClangdLSPServer which exposes these features via
/// the Language Server protocol. ClangdServer may also be embedded directly,
/// though its API is not stable over time.
///
/// ClangdServer should be used from a single thread. Many potentially-slow
/// operations have asynchronous APIs and deliver their results on another
/// thread.
/// Such operations support cancellation: if the caller sets up a cancelable
/// context, many operations will notice cancellation and fail early.
/// (ClangdLSPServer uses this to implement $/cancelRequest).
class ClangdServer {
public:
/// Interface with hooks for users of ClangdServer to be notified of events.
class Callbacks {
public:
virtual ~Callbacks() = default;
/// Called by ClangdServer when \p Diagnostics for \p File are ready.
/// These pushed diagnostics might correspond to an older version of the
/// file, they do not interfere with "pull-based" ClangdServer::diagnostics.
/// May be called concurrently for separate files, not for a single file.
virtual void onDiagnosticsReady(PathRef File, llvm::StringRef Version,
std::vector<Diag> Diagnostics) {}
/// Called whenever the file status is updated.
/// May be called concurrently for separate files, not for a single file.
virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
/// Called when background indexing tasks are enqueued/started/completed.
/// Not called concurrently.
virtual void
onBackgroundIndexProgress(const BackgroundQueue::Stats &Stats) {}
/// Called when the meaning of a source code may have changed without an
/// edit. Usually clients assume that responses to requests are valid until
/// they next edit the file. If they're invalidated at other times, we
/// should tell the client. In particular, when an asynchronous preamble
/// build finishes, we can provide more accurate semantic tokens, so we
/// should tell the client to refresh.
virtual void onSemanticsMaybeChanged(PathRef File) {}
};
/// Creates a context provider that loads and installs config.
/// Errors in loading config are reported as diagnostics via Callbacks.
/// (This is typically used as ClangdServer::Options::ContextProvider).
static std::function<Context(PathRef)>
createConfiguredContextProvider(const config::Provider *Provider,
ClangdServer::Callbacks *);
struct Options {
/// To process requests asynchronously, ClangdServer spawns worker threads.
/// If this is zero, no threads are spawned. All work is done on the calling
/// thread, and callbacks are invoked before "async" functions return.
unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount();
/// AST caching policy. The default is to keep up to 3 ASTs in memory.
ASTRetentionPolicy RetentionPolicy;
/// Cached preambles are potentially large. If false, store them on disk.
bool StorePreamblesInMemory = true;
/// If true, ClangdServer builds a dynamic in-memory index for symbols in
/// opened files and uses the index to augment code completion results.
bool BuildDynamicSymbolIndex = false;
/// If true, ClangdServer automatically indexes files in the current project
/// on background threads. The index is stored in the project root.
bool BackgroundIndex = false;
/// If set, use this index to augment code completion results.
SymbolIndex *StaticIndex = nullptr;
/// If set, queried to derive a processing context for some work.
/// Usually used to inject Config (see createConfiguredContextProvider).
///
/// When the provider is called, the active context will be that inherited
/// from the request (e.g. addDocument()), or from the ClangdServer
/// constructor if there is no such request (e.g. background indexing).
///
/// The path is an absolute path of the file being processed.
/// If there is no particular file (e.g. project loading) then it is empty.
std::function<Context(PathRef)> ContextProvider;
/// The Options provider to use when running clang-tidy. If null, clang-tidy
/// checks will be disabled.
TidyProviderRef ClangTidyProvider;
/// Clangd's workspace root. Relevant for "workspace" operations not bound
/// to a particular file.
/// FIXME: If not set, should use the current working directory.
llvm::Optional<std::string> WorkspaceRoot;
/// The resource directory is used to find internal headers, overriding
/// defaults and -resource-dir compiler flag).
/// If None, ClangdServer calls CompilerInvocation::GetResourcePath() to
/// obtain the standard resource directory.
llvm::Optional<std::string> ResourceDir = llvm::None;
/// Time to wait after a new file version before computing diagnostics.
DebouncePolicy UpdateDebounce = DebouncePolicy{
/*Min=*/std::chrono::milliseconds(50),
/*Max=*/std::chrono::milliseconds(500),
/*RebuildRatio=*/1,
};
/// Cancel certain requests if the file changes before they begin running.
/// This is useful for "transient" actions like enumerateTweaks that were
/// likely implicitly generated, and avoids redundant work if clients forget
/// to cancel. Clients that always cancel stale requests should clear this.
bool ImplicitCancellation = true;
/// Clangd will execute compiler drivers matching one of these globs to
/// fetch system include path.
std::vector<std::string> QueryDriverGlobs;
/// Enable preview of FoldingRanges feature.
bool FoldingRanges = false;
FeatureModuleSet *FeatureModules = nullptr;
explicit operator TUScheduler::Options() const;
};
// Sensible default options for use in tests.
// Features like indexing must be enabled if desired.
static Options optsForTest();
/// Creates a new ClangdServer instance.
///
/// ClangdServer uses \p CDB to obtain compilation arguments for parsing. Note
/// that ClangdServer only obtains compilation arguments once for each newly
/// added file (i.e., when processing a first call to addDocument) and reuses
/// those arguments for subsequent reparses. However, ClangdServer will check
/// if compilation arguments changed on calls to forceReparse().
ClangdServer(const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS,
const Options &Opts, Callbacks *Callbacks = nullptr);
~ClangdServer();
/// Gets the installed feature module of a given type, if any.
/// This exposes access the public interface of feature modules that have one.
template <typename Mod> Mod *featureModule() {
return FeatureModules ? FeatureModules->get<Mod>() : nullptr;
}
template <typename Mod> const Mod *featureModule() const {
return FeatureModules ? FeatureModules->get<Mod>() : nullptr;
}
/// Add a \p File to the list of tracked C++ files or update the contents if
/// \p File is already tracked. Also schedules parsing of the AST for it on a
/// separate thread. When the parsing is complete, DiagConsumer passed in
/// constructor will receive onDiagnosticsReady callback.
/// Version identifies this snapshot and is propagated to ASTs, preambles,
/// diagnostics etc built from it. If empty, a version number is generated.
void addDocument(PathRef File, StringRef Contents,
llvm::StringRef Version = "null",
WantDiagnostics WD = WantDiagnostics::Auto,
bool ForceRebuild = false);
/// Remove \p File from list of tracked files, schedule a request to free
/// resources associated with it. Pending diagnostics for closed files may not
/// be delivered, even if requested with WantDiags::Auto or WantDiags::Yes.
/// An empty set of diagnostics will be delivered, with Version = "".
void removeDocument(PathRef File);
/// Requests a reparse of currently opened files using their latest source.
/// This will typically only rebuild if something other than the source has
/// changed (e.g. the CDB yields different flags, or files included in the
/// preamble have been modified).
void reparseOpenFilesIfNeeded(
llvm::function_ref<bool(llvm::StringRef File)> Filter);
/// Run code completion for \p File at \p Pos.
///
/// This method should only be called for currently tracked files.
void codeComplete(PathRef File, Position Pos,
const clangd::CodeCompleteOptions &Opts,
Callback<CodeCompleteResult> CB);
/// Provide signature help for \p File at \p Pos. This method should only be
/// called for tracked files.
void signatureHelp(PathRef File, Position Pos, Callback<SignatureHelp> CB);
/// Find declaration/definition locations of symbol at a specified position.
void locateSymbolAt(PathRef File, Position Pos,
Callback<std::vector<LocatedSymbol>> CB);
/// Switch to a corresponding source file when given a header file, and vice
/// versa.
void switchSourceHeader(PathRef Path,
Callback<llvm::Optional<clangd::Path>> CB);
/// Get document highlights for a given position.
void findDocumentHighlights(PathRef File, Position Pos,
Callback<std::vector<DocumentHighlight>> CB);
/// Get code hover for a given position.
void findHover(PathRef File, Position Pos,
Callback<llvm::Optional<HoverInfo>> CB);
/// Get information about type hierarchy for a given position.
void typeHierarchy(PathRef File, Position Pos, int Resolve,
TypeHierarchyDirection Direction,
Callback<llvm::Optional<TypeHierarchyItem>> CB);
/// Resolve type hierarchy item in the given direction.
void resolveTypeHierarchy(TypeHierarchyItem Item, int Resolve,
TypeHierarchyDirection Direction,
Callback<llvm::Optional<TypeHierarchyItem>> CB);
/// Get information about call hierarchy for a given position.
void prepareCallHierarchy(PathRef File, Position Pos,
Callback<std::vector<CallHierarchyItem>> CB);
/// Resolve incoming calls for a given call hierarchy item.
void incomingCalls(const CallHierarchyItem &Item,
Callback<std::vector<CallHierarchyIncomingCall>>);
/// Resolve inlay hints for a given document.
void inlayHints(PathRef File, Callback<std::vector<InlayHint>>);
/// Retrieve the top symbols from the workspace matching a query.
void workspaceSymbols(StringRef Query, int Limit,
Callback<std::vector<SymbolInformation>> CB);
/// Retrieve the symbols within the specified file.
void documentSymbols(StringRef File,
Callback<std::vector<DocumentSymbol>> CB);
/// Retrieve ranges that can be used to fold code within the specified file.
void foldingRanges(StringRef File, Callback<std::vector<FoldingRange>> CB);
/// Retrieve implementations for virtual method.
void findImplementations(PathRef File, Position Pos,
Callback<std::vector<LocatedSymbol>> CB);
/// Retrieve locations for symbol references.
void findReferences(PathRef File, Position Pos, uint32_t Limit,
Callback<ReferencesResult> CB);
/// Run formatting for the \p File with content \p Code.
/// If \p Rng is non-null, formats only that region.
void formatFile(PathRef File, llvm::Optional<Range> Rng,
Callback<tooling::Replacements> CB);
/// Run formatting after \p TriggerText was typed at \p Pos in \p File with
/// content \p Code.
void formatOnType(PathRef File, Position Pos, StringRef TriggerText,
Callback<std::vector<TextEdit>> CB);
/// Test the validity of a rename operation.
///
/// If NewName is provided, it performs a name validation.
void prepareRename(PathRef File, Position Pos,
llvm::Optional<std::string> NewName,
const RenameOptions &RenameOpts,
Callback<RenameResult> CB);
/// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName.
/// If WantFormat is false, the final TextEdit will be not formatted,
/// embedders could use this method to get all occurrences of the symbol (e.g.
/// highlighting them in prepare stage).
void rename(PathRef File, Position Pos, llvm::StringRef NewName,
const RenameOptions &Opts, Callback<RenameResult> CB);
struct TweakRef {
std::string ID; /// ID to pass for applyTweak.
std::string Title; /// A single-line message to show in the UI.
llvm::StringLiteral Kind;
};
/// Enumerate the code tweaks available to the user at a specified point.
/// Tweaks where Filter returns false will not be checked or included.
void enumerateTweaks(PathRef File, Range Sel,
llvm::unique_function<bool(const Tweak &)> Filter,
Callback<std::vector<TweakRef>> CB);
/// Apply the code tweak with a specified \p ID.
void applyTweak(PathRef File, Range Sel, StringRef ID,
Callback<Tweak::Effect> CB);
/// Called when an event occurs for a watched file in the workspace.
void onFileEvent(const DidChangeWatchedFilesParams &Params);
/// Get symbol info for given position.
/// Clangd extension - not part of official LSP.
void symbolInfo(PathRef File, Position Pos,
Callback<std::vector<SymbolDetails>> CB);
/// Get semantic ranges around a specified position in a file.
void semanticRanges(PathRef File, const std::vector<Position> &Pos,
Callback<std::vector<SelectionRange>> CB);
/// Get all document links in a file.
void documentLinks(PathRef File, Callback<std::vector<DocumentLink>> CB);
void semanticHighlights(PathRef File,
Callback<std::vector<HighlightingToken>>);
/// Describe the AST subtree for a piece of code.
void getAST(PathRef File, llvm::Optional<Range> R,
Callback<llvm::Optional<ASTNode>> CB);
/// Runs an arbitrary action that has access to the AST of the specified file.
/// The action will execute on one of ClangdServer's internal threads.
/// The AST is only valid for the duration of the callback.
/// As with other actions, the file must have been opened.
void customAction(PathRef File, llvm::StringRef Name,
Callback<InputsAndAST> Action);
/// Fetches diagnostics for current version of the \p File. This might fail if
/// server is busy (building a preamble) and would require a long time to
/// prepare diagnostics. If it fails, clients should wait for
/// onSemanticsMaybeChanged and then retry.
/// These 'pulled' diagnostics do not interfere with the diagnostics 'pushed'
/// to Callbacks::onDiagnosticsReady, and clients may use either or both.
void diagnostics(PathRef File, Callback<std::vector<Diag>> CB);
/// Returns estimated memory usage and other statistics for each of the
/// currently open files.
/// Overall memory usage of clangd may be significantly more than reported
/// here, as this metric does not account (at least) for:
/// - memory occupied by static and dynamic index,
/// - memory required for in-flight requests,
/// FIXME: those metrics might be useful too, we should add them.
llvm::StringMap<TUScheduler::FileStats> fileStats() const;
/// Gets the contents of a currently tracked file. Returns nullptr if the file
/// isn't being tracked.
std::shared_ptr<const std::string> getDraft(PathRef File) const;
// Blocks the main thread until the server is idle. Only for use in tests.
// Returns false if the timeout expires.
// FIXME: various subcomponents each get the full timeout, so it's more of
// an order of magnitude than a hard deadline.
LLVM_NODISCARD bool
blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds = 10);
/// Builds a nested representation of memory used by components.
void profile(MemoryTree &MT) const;
private:
FeatureModuleSet *FeatureModules;
const GlobalCompilationDatabase &CDB;
const ThreadsafeFS &TFS;
Path ResourceDir;
// The index used to look up symbols. This could be:
// - null (all index functionality is optional)
// - the dynamic index owned by ClangdServer (DynamicIdx)
// - the static index passed to the constructor
// - a merged view of a static and dynamic index (MergedIndex)
const SymbolIndex *Index = nullptr;
// If present, an index of symbols in open files. Read via *Index.
std::unique_ptr<FileIndex> DynamicIdx;
// If present, the new "auto-index" maintained in background threads.
std::unique_ptr<BackgroundIndex> BackgroundIdx;
// Storage for merged views of the various indexes.
std::vector<std::unique_ptr<SymbolIndex>> MergedIdx;
// When set, provides clang-tidy options for a specific file.
TidyProviderRef ClangTidyProvider;
// GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)
llvm::StringMap<llvm::Optional<FuzzyFindRequest>>
CachedCompletionFuzzyFindRequestByFile;
mutable std::mutex CachedCompletionFuzzyFindRequestMutex;
llvm::Optional<std::string> WorkspaceRoot;
llvm::Optional<TUScheduler> WorkScheduler;
// Invalidation policy used for actions that we assume are "transient".
TUScheduler::ASTActionInvalidation Transient;
// Store of the current versions of the open documents.
// Only written from the main thread (despite being threadsafe).
DraftStore DraftMgr;
std::unique_ptr<ThreadsafeFS> DirtyFS;
};
} // namespace clangd
} // namespace clang
#endif
|