162 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- 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 "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 {
 | 
						|
class LSPBinder;
 | 
						|
class SymbolIndex;
 | 
						|
class ThreadsafeFS;
 | 
						|
class TUScheduler;
 | 
						|
 | 
						|
/// 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; }
 | 
						|
 | 
						|
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
 |