brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.4 KiB · 55ca908 Raw
207 lines · c
1//===--- FeatureModule.h - Plugging features into clangd ----------*-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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FEATUREMODULE_H11 12#include "support/Function.h"13#include "support/Threading.h"14#include "clang/Basic/Diagnostic.h"15#include "llvm/ADT/FunctionExtras.h"16#include "llvm/Support/Compiler.h"17#include "llvm/Support/JSON.h"18#include "llvm/Support/Registry.h"19#include <memory>20#include <optional>21#include <type_traits>22#include <vector>23 24namespace clang {25class CompilerInstance;26namespace clangd {27struct Diag;28class LSPBinder;29class SymbolIndex;30class ThreadsafeFS;31class TUScheduler;32class Tweak;33 34/// A FeatureModule contributes a vertical feature to clangd.35///36/// The lifetime of a module is roughly:37///  - feature modules are created before the LSP server, in ClangdMain.cpp38///  - these modules are then passed to ClangdLSPServer in a FeatureModuleSet39///  - initializeLSP() is called when the editor calls initialize.40//   - initialize() is then called by ClangdServer as it is constructed.41///  - module hooks can be called by the server at this point.42///    Server facilities (scheduler etc) are available.43///  - ClangdServer will not be destroyed until all the requests are done.44///    FIXME: Block server shutdown until all the modules are idle.45///  - When shutting down, ClangdServer will wait for all requests to46///    finish, call stop(), and then blockUntilIdle().47///  - feature modules will be destroyed after ClangdLSPServer is destroyed.48///49/// FeatureModules are not threadsafe in general. A module's entrypoints are:50///   - method handlers registered in initializeLSP()51///   - public methods called directly via ClangdServer.featureModule<T>()->...52///   - specific overridable "hook" methods inherited from FeatureModule53/// Unless otherwise specified, these are only called on the main thread.54///55/// Conventionally, standard feature modules live in the `clangd` namespace,56/// and other exposed details live in a sub-namespace.57class FeatureModule {58public:59  virtual ~FeatureModule() {60    /// Perform shutdown sequence on destruction in case the ClangdServer was61    /// never initialized. Usually redundant, but shutdown is idempotent.62    stop();63    blockUntilIdle(Deadline::infinity());64  }65 66  /// Called by the server to connect this feature module to LSP.67  /// The module should register the methods/notifications/commands it handles,68  /// and update the server capabilities to advertise them.69  ///70  /// This is only called if the module is running in ClangdLSPServer!71  /// FeatureModules with a public interface should work without LSP bindings.72  virtual void initializeLSP(LSPBinder &Bind,73                             const llvm::json::Object &ClientCaps,74                             llvm::json::Object &ServerCaps) {}75 76  /// Shared server facilities needed by the module to get its work done.77  struct Facilities {78    TUScheduler &Scheduler;79    const SymbolIndex *Index;80    const ThreadsafeFS &FS;81  };82  /// Called by the server to prepare this module for use.83  void initialize(const Facilities &F);84 85  /// Requests that the module cancel background work and go idle soon.86  /// Does not block, the caller will call blockUntilIdle() instead.87  /// After a module is stop()ed, it should not receive any more requests.88  /// Called by the server when shutting down.89  /// May be called multiple times, should be idempotent.90  virtual void stop() {}91 92  /// Waits until the module is idle (no background work) or a deadline expires.93  /// In general all modules should eventually go idle, though it may take a94  /// long time (e.g. background indexing).95  /// FeatureModules should go idle quickly if stop() has been called.96  /// Called by the server when shutting down, and also by tests.97  virtual bool blockUntilIdle(Deadline) { return true; }98 99  /// Tweaks implemented by this module. Can be called asynchronously when100  /// enumerating or applying code actions.101  virtual void contributeTweaks(std::vector<std::unique_ptr<Tweak>> &Out) {}102 103  /// Extension point that allows modules to observe and modify an AST build.104  /// One instance is created each time clangd produces a ParsedAST or105  /// PrecompiledPreamble. For a given instance, lifecycle methods are always106  /// called on a single thread.107  struct ASTListener {108    /// Listeners are destroyed once the AST is built.109    virtual ~ASTListener() = default;110 111    /// Called before every AST build, both for main file and preamble. The call112    /// happens immediately before FrontendAction::Execute(), with Preprocessor113    /// set up already and after BeginSourceFile() on main file was called.114    virtual void beforeExecute(CompilerInstance &CI) {}115 116    /// Called everytime a diagnostic is encountered. Modules can use this117    /// modify the final diagnostic, or store some information to surface code118    /// actions later on.119    virtual void sawDiagnostic(const clang::Diagnostic &, clangd::Diag &) {}120  };121  /// Can be called asynchronously before building an AST.122  virtual std::unique_ptr<ASTListener> astListeners() { return nullptr; }123 124protected:125  /// Accessors for modules to access shared server facilities they depend on.126  Facilities &facilities();127  /// The scheduler is used to run tasks on worker threads and access ASTs.128  TUScheduler &scheduler() { return facilities().Scheduler; }129  /// The index is used to get information about the whole codebase.130  const SymbolIndex *index() { return facilities().Index; }131  /// The filesystem is used to read source files on disk.132  const ThreadsafeFS &fs() { return facilities().FS; }133 134  /// Types of function objects that feature modules use for outgoing calls.135  /// (Bound throuh LSPBinder, made available here for convenience).136  template <typename P>137  using OutgoingNotification = llvm::unique_function<void(const P &)>;138  template <typename P, typename R>139  using OutgoingMethod = llvm::unique_function<void(const P &, Callback<R>)>;140 141private:142  std::optional<Facilities> Fac;143};144 145/// A FeatureModuleSet is a collection of feature modules installed in clangd.146///147/// Modules added with explicit type specification can be looked up by type, or148/// used via the FeatureModule interface. This allows individual modules to149/// expose a public API. For this reason, there can be only one feature module150/// of each type.151///152/// Modules added using a base class pointer can be used only via the153/// FeatureModule interface and can't be looked up by type, thus custom public154/// API (if provided by the module) can't be used.155///156/// The set owns the modules. It is itself owned by main, not ClangdServer.157class FeatureModuleSet {158  std::vector<std::unique_ptr<FeatureModule>> Modules;159  llvm::DenseMap<void *, FeatureModule *> Map;160 161  template <typename Mod> struct ID {162    static_assert(std::is_base_of<FeatureModule, Mod>::value &&163                      std::is_final<Mod>::value,164                  "Modules must be final classes derived from clangd::Module");165    static int Key;166  };167 168  bool addImpl(void *Key, std::unique_ptr<FeatureModule>, const char *Source);169 170public:171  FeatureModuleSet() = default;172 173  static FeatureModuleSet fromRegistry();174 175  using iterator = llvm::pointee_iterator<decltype(Modules)::iterator>;176  using const_iterator =177      llvm::pointee_iterator<decltype(Modules)::const_iterator>;178  iterator begin() { return iterator(Modules.begin()); }179  iterator end() { return iterator(Modules.end()); }180  const_iterator begin() const { return const_iterator(Modules.begin()); }181  const_iterator end() const { return const_iterator(Modules.end()); }182 183  void add(std::unique_ptr<FeatureModule> M);184  template <typename Mod> bool add(std::unique_ptr<Mod> M) {185    return addImpl(&ID<Mod>::Key, std::move(M), LLVM_PRETTY_FUNCTION);186  }187  template <typename Mod> Mod *get() {188    return static_cast<Mod *>(Map.lookup(&ID<Mod>::Key));189  }190  template <typename Mod> const Mod *get() const {191    return const_cast<FeatureModuleSet *>(this)->get<Mod>();192  }193};194 195template <typename Mod> int FeatureModuleSet::ID<Mod>::Key;196 197using FeatureModuleRegistry = llvm::Registry<FeatureModule>;198 199} // namespace clangd200} // namespace clang201 202namespace llvm {203extern template class Registry<clang::clangd::FeatureModule>;204} // namespace llvm205 206#endif207