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
|
//===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#include "ClangdUnit.h"
#include "ClangdUnitStore.h"
#include "CodeComplete.h"
#include "DraftStore.h"
#include "Function.h"
#include "GlobalCompilationDatabase.h"
#include "Protocol.h"
#include "index/FileIndex.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include <condition_variable>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
namespace clang {
class PCHContainerOperations;
namespace clangd {
/// A tag supplied by the FileSytemProvider.
typedef std::string VFSTag;
/// A value of an arbitrary type and VFSTag that was supplied by the
/// FileSystemProvider when this value was computed.
template <class T> class Tagged {
public:
// MSVC requires future<> arguments to be default-constructible.
Tagged() = default;
template <class U>
Tagged(U &&Value, VFSTag Tag)
: Value(std::forward<U>(Value)), Tag(std::move(Tag)) {}
template <class U>
Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
template <class U>
Tagged(Tagged<U> &&Other)
: Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
T Value = T();
VFSTag Tag = VFSTag();
};
template <class T>
Tagged<typename std::decay<T>::type> make_tagged(T &&Value, VFSTag Tag) {
return Tagged<typename std::decay<T>::type>(std::forward<T>(Value), Tag);
}
class DiagnosticsConsumer {
public:
virtual ~DiagnosticsConsumer() = default;
/// Called by ClangdServer when \p Diagnostics for \p File are ready.
virtual void
onDiagnosticsReady(PathRef File,
Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
};
class FileSystemProvider {
public:
virtual ~FileSystemProvider() = default;
/// Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
/// Name of the file that will be parsed is passed in \p File.
///
/// \return A filesystem that will be used for all file accesses in clangd.
/// A Tag returned by this method will be propagated to all results of clangd
/// that will use this filesystem.
virtual Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
getTaggedFileSystem(PathRef File) = 0;
};
class RealFileSystemProvider : public FileSystemProvider {
public:
/// \return getRealFileSystem() tagged with default tag, i.e. VFSTag()
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
getTaggedFileSystem(PathRef File) override;
};
class ClangdServer;
/// Returns a number of a default async threads to use for ClangdScheduler.
/// Returned value is always >= 1 (i.e. will not cause requests to be processed
/// synchronously).
unsigned getDefaultAsyncThreadsCount();
/// Handles running WorkerRequests of ClangdServer on a number of worker
/// threads.
class ClangdScheduler {
public:
/// If \p AsyncThreadsCount is 0, requests added using addToFront and addToEnd
/// will be processed synchronously on the calling thread.
// Otherwise, \p AsyncThreadsCount threads will be created to schedule the
// requests.
ClangdScheduler(unsigned AsyncThreadsCount);
~ClangdScheduler();
/// Add a new request to run function \p F with args \p As to the start of the
/// queue. The request will be run on a separate thread.
template <class Func, class... Args>
void addToFront(Func &&F, Args &&... As) {
if (RunSynchronously) {
std::forward<Func>(F)(std::forward<Args>(As)...);
return;
}
{
std::lock_guard<std::mutex> Lock(Mutex);
RequestQueue.push_front(
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
}
RequestCV.notify_one();
}
/// Add a new request to run function \p F with args \p As to the end of the
/// queue. The request will be run on a separate thread.
template <class Func, class... Args> void addToEnd(Func &&F, Args &&... As) {
if (RunSynchronously) {
std::forward<Func>(F)(std::forward<Args>(As)...);
return;
}
{
std::lock_guard<std::mutex> Lock(Mutex);
RequestQueue.push_back(
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
}
RequestCV.notify_one();
}
private:
bool RunSynchronously;
std::mutex Mutex;
/// We run some tasks on separate threads(parsing, CppFile cleanup).
/// These threads looks into RequestQueue to find requests to handle and
/// terminate when Done is set to true.
std::vector<std::thread> Workers;
/// Setting Done to true will make the worker threads terminate.
bool Done = false;
/// A queue of requests. Elements of this vector are async computations (i.e.
/// results of calling std::async(std::launch::deferred, ...)).
std::deque<UniqueFunction<void()>> RequestQueue;
/// Condition variable to wake up worker threads.
std::condition_variable RequestCV;
};
/// Provides API to manage ASTs for a collection of C++ files and request
/// various language features.
/// Currently supports async diagnostics, code completion, formatting and goto
/// definition.
class ClangdServer {
public:
/// Creates a new ClangdServer instance.
/// To process parsing requests asynchronously, ClangdServer will spawn \p
/// AsyncThreadsCount worker threads. However, if \p AsyncThreadsCount is 0,
/// all requests will be processed on the calling thread.
///
/// ClangdServer uses \p FSProvider to get an instance of vfs::FileSystem for
/// each parsing request. Results of code completion and diagnostics also
/// include a tag, that \p FSProvider returns along with the vfs::FileSystem.
///
/// The value of \p ResourceDir will be used to search for internal headers
/// (overriding defaults and -resource-dir compiler flag). If \p ResourceDir
/// is None, ClangdServer will call CompilerInvocation::GetResourcePath() to
/// obtain the standard resource directory.
///
/// 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().
///
/// After each parsing request finishes, ClangdServer reports diagnostics to
/// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a
/// worker thread. Therefore, instances of \p DiagConsumer must properly
/// synchronize access to shared state.
///
/// \p StorePreamblesInMemory defines whether the Preambles generated by
/// clangd are stored in-memory or on disk.
///
/// If \p BuildDynamicSymbolIndex is true, ClangdServer builds a dynamic
/// in-memory index for symbols in all opened files and uses the index to
/// augment code completion results.
ClangdServer(GlobalCompilationDatabase &CDB,
DiagnosticsConsumer &DiagConsumer,
FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
bool BuildDynamicSymbolIndex = false,
llvm::Optional<StringRef> ResourceDir = llvm::None);
/// Set the root path of the workspace.
void setRootPath(PathRef RootPath);
/// 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.
/// \return A future that will become ready when the rebuild (including
/// diagnostics) is finished.
std::future<Context> addDocument(Context Ctx, PathRef File,
StringRef Contents);
/// Remove \p File from list of tracked files, schedule a request to free
/// resources associated with it.
/// \return A future that will become ready when the file is removed and all
/// associated resources are freed.
std::future<Context> removeDocument(Context Ctx, PathRef File);
/// Force \p File to be reparsed using the latest contents.
/// Will also check if CompileCommand, provided by GlobalCompilationDatabase
/// for \p File has changed. If it has, will remove currently stored Preamble
/// and AST and rebuild them from scratch.
std::future<Context> forceReparse(Context Ctx, PathRef File);
/// DEPRECATED. Please use a callback-based version, this API is deprecated
/// and will soon be removed.
///
/// Run code completion for \p File at \p Pos.
///
/// Request is processed asynchronously. You can use the returned future to
/// wait for the results of the async request.
///
/// If \p OverridenContents is not None, they will used only for code
/// completion, i.e. no diagnostics update will be scheduled and a draft for
/// \p File will not be updated. If \p OverridenContents is None, contents of
/// the current draft for \p File will be used. If \p UsedFS is non-null, it
/// will be overwritten by vfs::FileSystem used for completion.
///
/// This method should only be called for currently tracked files. However, it
/// is safe to call removeDocument for \p File after this method returns, even
/// while returned future is not yet ready.
std::future<std::pair<Context, Tagged<CompletionList>>>
codeComplete(Context Ctx, PathRef File, Position Pos,
const clangd::CodeCompleteOptions &Opts,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// A version of `codeComplete` that runs \p Callback on the processing thread
/// when codeComplete results become available.
void
codeComplete(Context Ctx, PathRef File, Position Pos,
const clangd::CodeCompleteOptions &Opts,
UniqueFunction<void(Context, Tagged<CompletionList>)> Callback,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// Provide signature help for \p File at \p Pos. If \p OverridenContents is
/// not None, they will used only for signature help, i.e. no diagnostics
/// update will be scheduled and a draft for \p File will not be updated. If
/// \p OverridenContents is None, contents of the current draft for \p File
/// will be used. If \p UsedFS is non-null, it will be overwritten by
/// vfs::FileSystem used for signature help. This method should only be called
/// for currently tracked files.
llvm::Expected<Tagged<SignatureHelp>>
signatureHelp(const Context &Ctx, PathRef File, Position Pos,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// Get definition of symbol at a specified \p Line and \p Column in \p File.
llvm::Expected<Tagged<std::vector<Location>>>
findDefinitions(const Context &Ctx, PathRef File, Position Pos);
/// Helper function that returns a path to the corresponding source file when
/// given a header file and vice versa.
llvm::Optional<Path> switchSourceHeader(PathRef Path);
/// Get document highlights for a given position.
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
findDocumentHighlights(const Context &Ctx, PathRef File, Position Pos);
/// Run formatting for \p Rng inside \p File with content \p Code.
llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
PathRef File, Range Rng);
/// Run formatting for the whole \p File with content \p Code.
llvm::Expected<tooling::Replacements> formatFile(StringRef Code,
PathRef File);
/// Run formatting after a character was typed at \p Pos in \p File with
/// content \p Code.
llvm::Expected<tooling::Replacements>
formatOnType(StringRef Code, PathRef File, Position Pos);
/// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName.
Expected<std::vector<tooling::Replacement>> rename(const Context &Ctx,
PathRef File, Position Pos,
llvm::StringRef NewName);
/// Gets current document contents for \p File. \p File must point to a
/// currently tracked file.
/// FIXME(ibiryukov): This function is here to allow offset-to-Position
/// conversions in outside code, maybe there's a way to get rid of it.
std::string getDocument(PathRef File);
/// Only for testing purposes.
/// Waits until all requests to worker thread are finished and dumps AST for
/// \p File. \p File must be in the list of added documents.
std::string dumpAST(PathRef File);
/// Called when an event occurs for a watched file in the workspace.
void onFileEvent(const DidChangeWatchedFilesParams &Params);
private:
/// FIXME: This stats several files to find a .clang-format file. I/O can be
/// slow. Think of a way to cache this.
llvm::Expected<tooling::Replacements>
formatCode(llvm::StringRef Code, PathRef File,
ArrayRef<tooling::Range> Ranges);
std::future<Context>
scheduleReparseAndDiags(Context Ctx, PathRef File, VersionedDraft Contents,
std::shared_ptr<CppFile> Resources,
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS);
std::future<Context>
scheduleCancelRebuild(Context Ctx, std::shared_ptr<CppFile> Resources);
GlobalCompilationDatabase &CDB;
DiagnosticsConsumer &DiagConsumer;
FileSystemProvider &FSProvider;
DraftStore DraftMgr;
/// If set, this manages index for symbols in opened files.
std::unique_ptr<FileIndex> FileIdx;
CppFileCollection Units;
std::string ResourceDir;
// If set, this represents the workspace path.
llvm::Optional<std::string> RootPath;
std::shared_ptr<PCHContainerOperations> PCHs;
bool StorePreamblesInMemory;
/// Used to serialize diagnostic callbacks.
/// FIXME(ibiryukov): get rid of an extra map and put all version counters
/// into CppFile.
std::mutex DiagnosticsMutex;
/// Maps from a filename to the latest version of reported diagnostics.
llvm::StringMap<DocVersion> ReportedDiagnosticVersions;
// WorkScheduler has to be the last member, because its destructor has to be
// called before all other members to stop the worker thread that references
// ClangdServer
ClangdScheduler WorkScheduler;
};
} // namespace clangd
} // namespace clang
#endif
|