summaryrefslogtreecommitdiff
path: root/clang-tools-extra/clangd/FeatureModule.h
blob: 7b6883507be3fa32332c9667b0c67f72297d6a7d (plain)
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
//===--- 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/Support/Compiler.h"
#include "llvm/Support/JSON.h"
#include <memory>
#include <optional>
#include <type_traits>
#include <vector>

namespace clang {
class CompilerInstance;
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 before every AST build, both for main file and preamble. The call
    /// happens immediately before FrontendAction::Execute(), with Preprocessor
    /// set up already and after BeginSourceFile() on main file was called.
    virtual void beforeExecute(CompilerInstance &CI) {}

    /// 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:
  std::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