Line data Source code
1 : //===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===//
2 : //
3 : // The LLVM Compiler Infrastructure
4 : //
5 : // This file is distributed under the University of Illinois Open Source
6 : // License. See LICENSE.TXT for details.
7 : //
8 : //===----------------------------------------------------------------------===//
9 :
10 : #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
11 : #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
12 :
13 : #include "Cancellation.h"
14 : #include "ClangdUnit.h"
15 : #include "CodeComplete.h"
16 : #include "FSProvider.h"
17 : #include "Function.h"
18 : #include "GlobalCompilationDatabase.h"
19 : #include "Protocol.h"
20 : #include "TUScheduler.h"
21 : #include "index/FileIndex.h"
22 : #include "index/Index.h"
23 : #include "clang/Tooling/CompilationDatabase.h"
24 : #include "clang/Tooling/Core/Replacement.h"
25 : #include "llvm/ADT/IntrusiveRefCntPtr.h"
26 : #include "llvm/ADT/Optional.h"
27 : #include "llvm/ADT/StringRef.h"
28 : #include <functional>
29 : #include <future>
30 : #include <string>
31 : #include <type_traits>
32 : #include <utility>
33 :
34 : namespace clang {
35 : class PCHContainerOperations;
36 :
37 : namespace clangd {
38 :
39 : class DiagnosticsConsumer {
40 : public:
41 0 : virtual ~DiagnosticsConsumer() = default;
42 :
43 : /// Called by ClangdServer when \p Diagnostics for \p File are ready.
44 : virtual void onDiagnosticsReady(PathRef File,
45 : std::vector<Diag> Diagnostics) = 0;
46 : };
47 :
48 : /// Manages a collection of source files and derived data (ASTs, indexes),
49 : /// and provides language-aware features such as code completion.
50 : ///
51 : /// The primary client is ClangdLSPServer which exposes these features via
52 : /// the Language Server protocol. ClangdServer may also be embedded directly,
53 : /// though its API is not stable over time.
54 : ///
55 : /// ClangdServer should be used from a single thread. Many potentially-slow
56 : /// operations have asynchronous APIs and deliver their results on another
57 : /// thread.
58 : /// Such operations support cancellation: if the caller sets up a cancelable
59 : /// context, many operations will notice cancellation and fail early.
60 : /// (ClangdLSPServer uses this to implement $/cancelRequest).
61 : class ClangdServer {
62 : public:
63 : struct Options {
64 : /// To process requests asynchronously, ClangdServer spawns worker threads.
65 : /// If this is zero, no threads are spawned. All work is done on the calling
66 : /// thread, and callbacks are invoked before "async" functions return.
67 : unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount();
68 :
69 : /// AST caching policy. The default is to keep up to 3 ASTs in memory.
70 : ASTRetentionPolicy RetentionPolicy;
71 :
72 : /// Cached preambles are potentially large. If false, store them on disk.
73 : bool StorePreamblesInMemory = true;
74 :
75 : /// If true, ClangdServer builds a dynamic in-memory index for symbols in
76 : /// opened files and uses the index to augment code completion results.
77 : bool BuildDynamicSymbolIndex = false;
78 : /// Use a heavier and faster in-memory index implementation.
79 : /// FIXME: we should make this true if it isn't too slow to build!.
80 : bool HeavyweightDynamicSymbolIndex = false;
81 :
82 : /// URI schemes to use when building the dynamic index.
83 : /// If empty, the default schemes in SymbolCollector will be used.
84 : std::vector<std::string> URISchemes;
85 :
86 : /// If set, use this index to augment code completion results.
87 : SymbolIndex *StaticIndex = nullptr;
88 :
89 : /// The resource directory is used to find internal headers, overriding
90 : /// defaults and -resource-dir compiler flag).
91 : /// If None, ClangdServer calls CompilerInvocation::GetResourcePath() to
92 : /// obtain the standard resource directory.
93 : llvm::Optional<std::string> ResourceDir = llvm::None;
94 :
95 : /// Time to wait after a new file version before computing diagnostics.
96 : std::chrono::steady_clock::duration UpdateDebounce =
97 : std::chrono::milliseconds(500);
98 : };
99 : // Sensible default options for use in tests.
100 : // Features like indexing must be enabled if desired.
101 : static Options optsForTest();
102 :
103 : /// Creates a new ClangdServer instance.
104 : ///
105 : /// ClangdServer uses \p CDB to obtain compilation arguments for parsing. Note
106 : /// that ClangdServer only obtains compilation arguments once for each newly
107 : /// added file (i.e., when processing a first call to addDocument) and reuses
108 : /// those arguments for subsequent reparses. However, ClangdServer will check
109 : /// if compilation arguments changed on calls to forceReparse().
110 : ///
111 : /// After each parsing request finishes, ClangdServer reports diagnostics to
112 : /// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a
113 : /// worker thread. Therefore, instances of \p DiagConsumer must properly
114 : /// synchronize access to shared state.
115 : ClangdServer(const GlobalCompilationDatabase &CDB,
116 : const FileSystemProvider &FSProvider,
117 : DiagnosticsConsumer &DiagConsumer, const Options &Opts);
118 :
119 : /// Set the root path of the workspace.
120 : void setRootPath(PathRef RootPath);
121 :
122 : /// Add a \p File to the list of tracked C++ files or update the contents if
123 : /// \p File is already tracked. Also schedules parsing of the AST for it on a
124 : /// separate thread. When the parsing is complete, DiagConsumer passed in
125 : /// constructor will receive onDiagnosticsReady callback.
126 : void addDocument(PathRef File, StringRef Contents,
127 : WantDiagnostics WD = WantDiagnostics::Auto);
128 :
129 : /// Remove \p File from list of tracked files, schedule a request to free
130 : /// resources associated with it.
131 : void removeDocument(PathRef File);
132 :
133 : /// Run code completion for \p File at \p Pos.
134 : /// Request is processed asynchronously.
135 : ///
136 : /// This method should only be called for currently tracked files. However, it
137 : /// is safe to call removeDocument for \p File after this method returns, even
138 : /// while returned future is not yet ready.
139 : /// A version of `codeComplete` that runs \p Callback on the processing thread
140 : /// when codeComplete results become available.
141 : void codeComplete(PathRef File, Position Pos,
142 : const clangd::CodeCompleteOptions &Opts,
143 : Callback<CodeCompleteResult> CB);
144 :
145 : /// Provide signature help for \p File at \p Pos. This method should only be
146 : /// called for tracked files.
147 : void signatureHelp(PathRef File, Position Pos, Callback<SignatureHelp> CB);
148 :
149 : /// Get definition of symbol at a specified \p Line and \p Column in \p File.
150 : void findDefinitions(PathRef File, Position Pos,
151 : Callback<std::vector<Location>> CB);
152 :
153 : /// Helper function that returns a path to the corresponding source file when
154 : /// given a header file and vice versa.
155 : llvm::Optional<Path> switchSourceHeader(PathRef Path);
156 :
157 : /// Get document highlights for a given position.
158 : void findDocumentHighlights(PathRef File, Position Pos,
159 : Callback<std::vector<DocumentHighlight>> CB);
160 :
161 : /// Get code hover for a given position.
162 : void findHover(PathRef File, Position Pos,
163 : Callback<llvm::Optional<Hover>> CB);
164 :
165 : /// Retrieve the top symbols from the workspace matching a query.
166 : void workspaceSymbols(StringRef Query, int Limit,
167 : Callback<std::vector<SymbolInformation>> CB);
168 :
169 : /// Retrieve the symbols within the specified file.
170 : void documentSymbols(StringRef File,
171 : Callback<std::vector<SymbolInformation>> CB);
172 :
173 : /// Retrieve locations for symbol references.
174 : void findReferences(PathRef File, Position Pos,
175 : Callback<std::vector<Location>> CB);
176 :
177 : /// Run formatting for \p Rng inside \p File with content \p Code.
178 : llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
179 : PathRef File, Range Rng);
180 :
181 : /// Run formatting for the whole \p File with content \p Code.
182 : llvm::Expected<tooling::Replacements> formatFile(StringRef Code,
183 : PathRef File);
184 :
185 : /// Run formatting after a character was typed at \p Pos in \p File with
186 : /// content \p Code.
187 : llvm::Expected<tooling::Replacements>
188 : formatOnType(StringRef Code, PathRef File, Position Pos);
189 :
190 : /// Rename all occurrences of the symbol at the \p Pos in \p File to
191 : /// \p NewName.
192 : void rename(PathRef File, Position Pos, llvm::StringRef NewName,
193 : Callback<std::vector<tooling::Replacement>> CB);
194 :
195 : /// Only for testing purposes.
196 : /// Waits until all requests to worker thread are finished and dumps AST for
197 : /// \p File. \p File must be in the list of added documents.
198 : void dumpAST(PathRef File, llvm::unique_function<void(std::string)> Callback);
199 : /// Called when an event occurs for a watched file in the workspace.
200 : void onFileEvent(const DidChangeWatchedFilesParams &Params);
201 :
202 : /// Returns estimated memory usage for each of the currently open files.
203 : /// The order of results is unspecified.
204 : /// Overall memory usage of clangd may be significantly more than reported
205 : /// here, as this metric does not account (at least) for:
206 : /// - memory occupied by static and dynamic index,
207 : /// - memory required for in-flight requests,
208 : /// FIXME: those metrics might be useful too, we should add them.
209 : std::vector<std::pair<Path, std::size_t>> getUsedBytesPerFile() const;
210 :
211 : /// Returns the active dynamic index if one was built.
212 : /// This can be useful for testing, debugging, or observing memory usage.
213 : const SymbolIndex *dynamicIndex() const { return DynamicIdx.get(); }
214 :
215 : // Blocks the main thread until the server is idle. Only for use in tests.
216 : // Returns false if the timeout expires.
217 : LLVM_NODISCARD bool
218 : blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds = 10);
219 :
220 : private:
221 : /// FIXME: This stats several files to find a .clang-format file. I/O can be
222 : /// slow. Think of a way to cache this.
223 : llvm::Expected<tooling::Replacements>
224 : formatCode(llvm::StringRef Code, PathRef File,
225 : ArrayRef<tooling::Range> Ranges);
226 :
227 : typedef uint64_t DocVersion;
228 :
229 : void consumeDiagnostics(PathRef File, DocVersion Version,
230 : std::vector<Diag> Diags);
231 :
232 : tooling::CompileCommand getCompileCommand(PathRef File);
233 :
234 : const GlobalCompilationDatabase &CDB;
235 : DiagnosticsConsumer &DiagConsumer;
236 : const FileSystemProvider &FSProvider;
237 :
238 : /// Used to synchronize diagnostic responses for added and removed files.
239 : llvm::StringMap<DocVersion> InternalVersion;
240 :
241 : Path ResourceDir;
242 : // The index used to look up symbols. This could be:
243 : // - null (all index functionality is optional)
244 : // - the dynamic index owned by ClangdServer (DynamicIdx)
245 : // - the static index passed to the constructor
246 : // - a merged view of a static and dynamic index (MergedIndex)
247 : const SymbolIndex *Index;
248 : // If present, an index of symbols in open files. Read via *Index.
249 : std::unique_ptr<FileIndex> DynamicIdx;
250 : // If present, storage for the merged static/dynamic index. Read via *Index.
251 : std::unique_ptr<SymbolIndex> MergedIdx;
252 :
253 : // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)
254 : llvm::StringMap<llvm::Optional<FuzzyFindRequest>>
255 : CachedCompletionFuzzyFindRequestByFile;
256 : mutable std::mutex CachedCompletionFuzzyFindRequestMutex;
257 :
258 : // If set, this represents the workspace path.
259 : llvm::Optional<std::string> RootPath;
260 : std::shared_ptr<PCHContainerOperations> PCHs;
261 : /// Used to serialize diagnostic callbacks.
262 : /// FIXME(ibiryukov): get rid of an extra map and put all version counters
263 : /// into CppFile.
264 : std::mutex DiagnosticsMutex;
265 : /// Maps from a filename to the latest version of reported diagnostics.
266 : llvm::StringMap<DocVersion> ReportedDiagnosticVersions;
267 : // WorkScheduler has to be the last member, because its destructor has to be
268 : // called before all other members to stop the worker thread that references
269 : // ClangdServer.
270 : TUScheduler WorkScheduler;
271 : };
272 :
273 : } // namespace clangd
274 : } // namespace clang
275 :
276 : #endif
|