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
|
//===--- FeatureModule.h - Plugging features into clangd ----------*-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_FEATUREMODULE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H
#include "support/Function.h"
#include "support/Threading.h"
#include "clang/Basic/Diagnostic.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include <memory>
#include <type_traits>
#include <vector>
namespace clang {
namespace clangd {
struct Diag;
class LSPBinder;
class SymbolIndex;
class ThreadsafeFS;
class TUScheduler;
class Tweak;
/// A FeatureModule contributes a vertical feature to clangd.
///
/// The lifetime of a module is roughly:
/// - feature modules are created before the LSP server, in ClangdMain.cpp
/// - these modules are then passed to ClangdLSPServer in a FeatureModuleSet
/// - initializeLSP() is called when the editor calls initialize.
// - initialize() is then called by ClangdServer as it is constructed.
/// - module hooks can be called by the server at this point.
/// Server facilities (scheduler etc) are available.
/// - ClangdServer will not be destroyed until all the requests are done.
/// FIXME: Block server shutdown until all the modules are idle.
/// - When shutting down, ClangdServer will wait for all requests to
/// finish, call stop(), and then blockUntilIdle().
/// - feature modules will be destroyed after ClangdLSPServer is destroyed.
///
/// FeatureModules are not threadsafe in general. A module's entrypoints are:
/// - method handlers registered in initializeLSP()
/// - public methods called directly via ClangdServer.featureModule<T>()->...
/// - specific overridable "hook" methods inherited from FeatureModule
/// Unless otherwise specified, these are only called on the main thread.
///
/// Conventionally, standard feature modules live in the `clangd` namespace,
/// and other exposed details live in a sub-namespace.
class FeatureModule {
public:
virtual ~FeatureModule() {
/// Perform shutdown sequence on destruction in case the ClangdServer was
/// never initialized. Usually redundant, but shutdown is idempotent.
stop();
blockUntilIdle(Deadline::infinity());
}
/// Called by the server to connect this feature module to LSP.
/// The module should register the methods/notifications/commands it handles,
/// and update the server capabilities to advertise them.
///
/// This is only called if the module is running in ClangdLSPServer!
/// FeatureModules with a public interface should work without LSP bindings.
virtual void initializeLSP(LSPBinder &Bind,
const llvm::json::Object &ClientCaps,
llvm::json::Object &ServerCaps) {}
/// Shared server facilities needed by the module to get its work done.
struct Facilities {
TUScheduler &Scheduler;
const SymbolIndex *Index;
const ThreadsafeFS &FS;
};
/// Called by the server to prepare this module for use.
void initialize(const Facilities &F);
/// Requests that the module cancel background work and go idle soon.
/// Does not block, the caller will call blockUntilIdle() instead.
/// After a module is stop()ed, it should not receive any more requests.
/// Called by the server when shutting down.
/// May be called multiple times, should be idempotent.
virtual void stop() {}
/// Waits until the module is idle (no background work) or a deadline expires.
/// In general all modules should eventually go idle, though it may take a
/// long time (e.g. background indexing).
/// FeatureModules should go idle quickly if stop() has been called.
/// Called by the server when shutting down, and also by tests.
virtual bool blockUntilIdle(Deadline) { return true; }
/// Tweaks implemented by this module. Can be called asynchronously when
/// enumerating or applying code actions.
virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {}
/// Extension point that allows modules to observe and modify an AST build.
/// One instance is created each time clangd produces a ParsedAST or
/// PrecompiledPreamble. For a given instance, lifecycle methods are always
/// called on a single thread.
struct ASTListener {
/// Listeners are destroyed once the AST is built.
virtual ~ASTListener() = default;
/// Called everytime a diagnostic is encountered. Modules can use this
/// modify the final diagnostic, or store some information to surface code
/// actions later on.
virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}
};
/// Can be called asynchronously before building an AST.
virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }
protected:
/// Accessors for modules to access shared server facilities they depend on.
Facilities &facilities();
/// The scheduler is used to run tasks on worker threads and access ASTs.
TUScheduler &scheduler() { return facilities().Scheduler; }
/// The index is used to get information about the whole codebase.
const SymbolIndex *index() { return facilities().Index; }
/// The filesystem is used to read source files on disk.
const ThreadsafeFS &fs() { return facilities().FS; }
/// Types of function objects that feature modules use for outgoing calls.
/// (Bound throuh LSPBinder, made available here for convenience).
template <typename P>
using OutgoingNotification = llvm::unique_function<void(const P &)>;
template <typename P, typename R>
using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;
private:
llvm::Optional<Facilities> Fac;
};
/// A FeatureModuleSet is a collection of feature modules installed in clangd.
///
/// Modules can be looked up by type, or used via the FeatureModule interface.
/// This allows individual modules to expose a public API.
/// For this reason, there can be only one feature module of each type.
///
/// The set owns the modules. It is itself owned by main, not ClangdServer.
class FeatureModuleSet {
std::vector<std::unique_ptr<FeatureModule>> Modules;
llvm::DenseMap<void *, FeatureModule *> Map;
template <typename Mod> struct ID {
static_assert(std::is_base_of<FeatureModule, Mod>::value &&
std::is_final<Mod>::value,
"Modules must be final classes derived from clangd::Module");
static int Key;
};
bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);
public:
FeatureModuleSet() = default;
using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;
using const_iterator =
llvm::pointee_iterator<decltype(Modules)::const_iterator>;
iterator begin() { return iterator(Modules.begin()); }
iterator end() { return iterator(Modules.end()); }
const_iterator begin() const { return const_iterator(Modules.begin()); }
const_iterator end() const { return const_iterator(Modules.end()); }
template <typename Mod> bool add(std::unique_ptr<Mod> M) {
return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);
}
template <typename Mod> Mod *get() {
return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));
}
template <typename Mod> const Mod *get() const {
return const_cast<FeatureModuleSet *>(this)->get<Mod>();
}
};
template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;
} // namespace clangd
} // namespace clang
#endif
|