blob: a1ec2af11ea6d1388cbaa554ad600fd2e035f137 [file] [log] [blame]
Ilya Biryukov38d79772017-05-16 09:38:59 +00001//===--- 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 "ClangdUnitStore.h"
14#include "DraftStore.h"
15#include "GlobalCompilationDatabase.h"
16#include "clang/Frontend/ASTUnit.h"
17#include "clang/Tooling/CompilationDatabase.h"
18#include "clang/Tooling/Core/Replacement.h"
19#include "llvm/ADT/IntrusiveRefCntPtr.h"
20#include "llvm/ADT/Optional.h"
21#include "llvm/ADT/StringRef.h"
22
23#include "ClangdUnit.h"
24#include "Protocol.h"
25
26#include <condition_variable>
Ilya Biryukovf01af682017-05-23 13:42:59 +000027#include <functional>
Ilya Biryukov38d79772017-05-16 09:38:59 +000028#include <mutex>
29#include <string>
30#include <thread>
Ilya Biryukov22602992017-05-30 15:11:02 +000031#include <type_traits>
Ilya Biryukov38d79772017-05-16 09:38:59 +000032#include <utility>
33
34namespace clang {
35class PCHContainerOperations;
36
37namespace clangd {
38
Ilya Biryukovafb55542017-05-16 14:40:30 +000039/// Turn a [line, column] pair into an offset in Code.
40size_t positionToOffset(StringRef Code, Position P);
41
42/// Turn an offset in Code into a [line, column] pair.
43Position offsetToPosition(StringRef Code, size_t Offset);
44
Ilya Biryukov22602992017-05-30 15:11:02 +000045/// A tag supplied by the FileSytemProvider.
Ilya Biryukove36035b2017-06-13 08:24:48 +000046typedef std::string VFSTag;
Ilya Biryukov22602992017-05-30 15:11:02 +000047
48/// A value of an arbitrary type and VFSTag that was supplied by the
49/// FileSystemProvider when this value was computed.
50template <class T> class Tagged {
51public:
52 template <class U>
Ilya Biryukove36035b2017-06-13 08:24:48 +000053 Tagged(U &&Value, VFSTag Tag)
54 : Value(std::forward<U>(Value)), Tag(std::move(Tag)) {}
Ilya Biryukov22602992017-05-30 15:11:02 +000055
56 template <class U>
57 Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
58
59 template <class U>
Ilya Biryukove36035b2017-06-13 08:24:48 +000060 Tagged(Tagged<U> &&Other)
61 : Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
Ilya Biryukov22602992017-05-30 15:11:02 +000062
63 T Value;
64 VFSTag Tag;
65};
66
67template <class T>
68Tagged<typename std::decay<T>::type> make_tagged(T &&Value, VFSTag Tag) {
Ilya Biryukovc22824a2017-08-09 12:55:13 +000069 return Tagged<typename std::decay<T>::type>(std::forward<T>(Value), Tag);
Ilya Biryukov22602992017-05-30 15:11:02 +000070}
71
Ilya Biryukov38d79772017-05-16 09:38:59 +000072class DiagnosticsConsumer {
73public:
74 virtual ~DiagnosticsConsumer() = default;
75
76 /// Called by ClangdServer when \p Diagnostics for \p File are ready.
Ilya Biryukov22602992017-05-30 15:11:02 +000077 virtual void
78 onDiagnosticsReady(PathRef File,
79 Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
Ilya Biryukov38d79772017-05-16 09:38:59 +000080};
81
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000082class FileSystemProvider {
83public:
84 virtual ~FileSystemProvider() = default;
Ilya Biryukovaf0c04b2017-06-14 09:46:44 +000085 /// Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
86 /// Name of the file that will be parsed is passed in \p File.
87 ///
Ilya Biryukov22602992017-05-30 15:11:02 +000088 /// \return A filesystem that will be used for all file accesses in clangd.
89 /// A Tag returned by this method will be propagated to all results of clangd
90 /// that will use this filesystem.
Ilya Biryukovaf0c04b2017-06-14 09:46:44 +000091 virtual Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
92 getTaggedFileSystem(PathRef File) = 0;
Ilya Biryukov0f62ed22017-05-26 12:26:51 +000093};
94
95class RealFileSystemProvider : public FileSystemProvider {
96public:
Ilya Biryukov22602992017-05-30 15:11:02 +000097 /// \return getRealFileSystem() tagged with default tag, i.e. VFSTag()
Ilya Biryukovaf0c04b2017-06-14 09:46:44 +000098 Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
99 getTaggedFileSystem(PathRef File) override;
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000100};
101
Ilya Biryukov38d79772017-05-16 09:38:59 +0000102class ClangdServer;
103
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000104/// Returns a number of a default async threads to use for ClangdScheduler.
105/// Returned value is always >= 1 (i.e. will not cause requests to be processed
106/// synchronously).
107unsigned getDefaultAsyncThreadsCount();
108
109/// Handles running WorkerRequests of ClangdServer on a number of worker
110/// threads.
Ilya Biryukov38d79772017-05-16 09:38:59 +0000111class ClangdScheduler {
112public:
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000113 /// If \p AsyncThreadsCount is 0, requests added using addToFront and addToEnd
114 /// will be processed synchronously on the calling thread.
115 // Otherwise, \p AsyncThreadsCount threads will be created to schedule the
116 // requests.
117 ClangdScheduler(unsigned AsyncThreadsCount);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000118 ~ClangdScheduler();
119
Ilya Biryukov02d58702017-08-01 15:51:38 +0000120 /// Add a new request to run function \p F with args \p As to the start of the
121 /// queue. The request will be run on a separate thread.
122 template <class Func, class... Args>
123 void addToFront(Func &&F, Args &&... As) {
124 if (RunSynchronously) {
125 std::forward<Func>(F)(std::forward<Args>(As)...);
126 return;
127 }
128
129 {
130 std::lock_guard<std::mutex> Lock(Mutex);
131 RequestQueue.push_front(std::async(std::launch::deferred,
132 std::forward<Func>(F),
133 std::forward<Args>(As)...));
134 }
135 RequestCV.notify_one();
136 }
137
138 /// Add a new request to run function \p F with args \p As to the end of the
139 /// queue. The request will be run on a separate thread.
140 template <class Func, class... Args> void addToEnd(Func &&F, Args &&... As) {
141 if (RunSynchronously) {
142 std::forward<Func>(F)(std::forward<Args>(As)...);
143 return;
144 }
145
146 {
147 std::lock_guard<std::mutex> Lock(Mutex);
148 RequestQueue.push_back(std::async(std::launch::deferred,
149 std::forward<Func>(F),
150 std::forward<Args>(As)...));
151 }
152 RequestCV.notify_one();
153 }
Ilya Biryukov38d79772017-05-16 09:38:59 +0000154
155private:
156 bool RunSynchronously;
157 std::mutex Mutex;
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000158 /// We run some tasks on separate threads(parsing, CppFile cleanup).
159 /// These threads looks into RequestQueue to find requests to handle and
160 /// terminate when Done is set to true.
161 std::vector<std::thread> Workers;
162 /// Setting Done to true will make the worker threads terminate.
Ilya Biryukov38d79772017-05-16 09:38:59 +0000163 bool Done = false;
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000164 /// A queue of requests. Elements of this vector are async computations (i.e.
165 /// results of calling std::async(std::launch::deferred, ...)).
Ilya Biryukov02d58702017-08-01 15:51:38 +0000166 std::deque<std::future<void>> RequestQueue;
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000167 /// Condition variable to wake up worker threads.
Ilya Biryukov38d79772017-05-16 09:38:59 +0000168 std::condition_variable RequestCV;
169};
170
171/// Provides API to manage ASTs for a collection of C++ files and request
Ilya Biryukov75337e82017-08-22 09:16:46 +0000172/// various language features.
173/// Currently supports async diagnostics, code completion, formatting and goto
174/// definition.
Ilya Biryukov38d79772017-05-16 09:38:59 +0000175class ClangdServer {
176public:
Ilya Biryukov75337e82017-08-22 09:16:46 +0000177 /// Creates a new ClangdServer instance.
178 /// To process parsing requests asynchronously, ClangdServer will spawn \p
179 /// AsyncThreadsCount worker threads. However, if \p AsyncThreadsCount is 0,
180 /// all requests will be processed on the calling thread.
181 ///
182 /// ClangdServer uses \p FSProvider to get an instance of vfs::FileSystem for
183 /// each parsing request. Results of code completion and diagnostics also
184 /// include a tag, that \p FSProvider returns along with the vfs::FileSystem.
185 ///
186 /// The value of \p ResourceDir will be used to search for internal headers
187 /// (overriding defaults and -resource-dir compiler flag). If \p ResourceDir
188 /// is None, ClangdServer will call CompilerInvocation::GetResourcePath() to
189 /// obtain the standard resource directory.
190 ///
191 /// ClangdServer uses \p CDB to obtain compilation arguments for parsing. Note
192 /// that ClangdServer only obtains compilation arguments once for each newly
193 /// added file (i.e., when processing a first call to addDocument) and reuses
194 /// those arguments for subsequent reparses. However, ClangdServer will check
195 /// if compilation arguments changed on calls to forceReparse().
196 ///
197 /// After each parsing request finishes, ClangdServer reports diagnostics to
198 /// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a
199 /// worker thread. Therefore, instances of \p DiagConsumer must properly
200 /// synchronize access to shared state.
Ilya Biryukov103c9512017-06-13 15:59:43 +0000201 ClangdServer(GlobalCompilationDatabase &CDB,
202 DiagnosticsConsumer &DiagConsumer,
Ilya Biryukovdb8b2d72017-08-14 08:45:47 +0000203 FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
Ilya Biryukova46f7a92017-06-28 10:34:50 +0000204 llvm::Optional<StringRef> ResourceDir = llvm::None);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000205
206 /// Add a \p File to the list of tracked C++ files or update the contents if
207 /// \p File is already tracked. Also schedules parsing of the AST for it on a
208 /// separate thread. When the parsing is complete, DiagConsumer passed in
209 /// constructor will receive onDiagnosticsReady callback.
Ilya Biryukov02d58702017-08-01 15:51:38 +0000210 /// \return A future that will become ready when the rebuild (including
211 /// diagnostics) is finished.
212 std::future<void> addDocument(PathRef File, StringRef Contents);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000213 /// Remove \p File from list of tracked files, schedule a request to free
214 /// resources associated with it.
Ilya Biryukov91dbf5b2017-08-14 08:37:32 +0000215 /// \return A future that will become ready when the file is removed and all
216 /// associated resources are freed.
Ilya Biryukov02d58702017-08-01 15:51:38 +0000217 std::future<void> removeDocument(PathRef File);
Ilya Biryukov0f62ed22017-05-26 12:26:51 +0000218 /// Force \p File to be reparsed using the latest contents.
Ilya Biryukov91dbf5b2017-08-14 08:37:32 +0000219 /// Will also check if CompileCommand, provided by GlobalCompilationDatabase
220 /// for \p File has changed. If it has, will remove currently stored Preamble
221 /// and AST and rebuild them from scratch.
Ilya Biryukov02d58702017-08-01 15:51:38 +0000222 std::future<void> forceReparse(PathRef File);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000223
Ilya Biryukov0e27ce42017-06-13 14:15:56 +0000224 /// Run code completion for \p File at \p Pos. If \p OverridenContents is not
225 /// None, they will used only for code completion, i.e. no diagnostics update
226 /// will be scheduled and a draft for \p File will not be updated.
227 /// If \p OverridenContents is None, contents of the current draft for \p File
228 /// will be used.
Ilya Biryukoved99e4c2017-07-31 17:09:29 +0000229 /// If \p UsedFS is non-null, it will be overwritten by vfs::FileSystem used
230 /// for completion.
231 /// This method should only be called for currently tracked
232 /// files.
Ilya Biryukov0e27ce42017-06-13 14:15:56 +0000233 Tagged<std::vector<CompletionItem>>
234 codeComplete(PathRef File, Position Pos,
Ilya Biryukoved99e4c2017-07-31 17:09:29 +0000235 llvm::Optional<StringRef> OverridenContents = llvm::None,
236 IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
Marc-Andre Laperle2cbf0372017-06-28 16:12:10 +0000237 /// Get definition of symbol at a specified \p Line and \p Column in \p File.
238 Tagged<std::vector<Location>> findDefinitions(PathRef File, Position Pos);
Ilya Biryukov38d79772017-05-16 09:38:59 +0000239
Ilya Biryukovafb55542017-05-16 14:40:30 +0000240 /// Run formatting for \p Rng inside \p File.
241 std::vector<tooling::Replacement> formatRange(PathRef File, Range Rng);
242 /// Run formatting for the whole \p File.
243 std::vector<tooling::Replacement> formatFile(PathRef File);
244 /// Run formatting after a character was typed at \p Pos in \p File.
245 std::vector<tooling::Replacement> formatOnType(PathRef File, Position Pos);
246
Ilya Biryukov38d79772017-05-16 09:38:59 +0000247 /// Gets current document contents for \p File. \p File must point to a
248 /// currently tracked file.
Ilya Biryukovafb55542017-05-16 14:40:30 +0000249 /// FIXME(ibiryukov): This function is here to allow offset-to-Position
250 /// conversions in outside code, maybe there's a way to get rid of it.
Ilya Biryukov38d79772017-05-16 09:38:59 +0000251 std::string getDocument(PathRef File);
252
Ilya Biryukovf01af682017-05-23 13:42:59 +0000253 /// Only for testing purposes.
254 /// Waits until all requests to worker thread are finished and dumps AST for
255 /// \p File. \p File must be in the list of added documents.
256 std::string dumpAST(PathRef File);
257
Ilya Biryukov38d79772017-05-16 09:38:59 +0000258private:
Ilya Biryukovc5ad35f2017-08-14 08:17:24 +0000259 std::future<void>
260 scheduleReparseAndDiags(PathRef File, VersionedDraft Contents,
261 std::shared_ptr<CppFile> Resources,
262 Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS);
263
264 std::future<void> scheduleCancelRebuild(std::shared_ptr<CppFile> Resources);
265
Ilya Biryukov103c9512017-06-13 15:59:43 +0000266 GlobalCompilationDatabase &CDB;
267 DiagnosticsConsumer &DiagConsumer;
268 FileSystemProvider &FSProvider;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000269 DraftStore DraftMgr;
Ilya Biryukov02d58702017-08-01 15:51:38 +0000270 CppFileCollection Units;
Ilya Biryukova46f7a92017-06-28 10:34:50 +0000271 std::string ResourceDir;
Ilya Biryukov38d79772017-05-16 09:38:59 +0000272 std::shared_ptr<PCHContainerOperations> PCHs;
273 // WorkScheduler has to be the last member, because its destructor has to be
274 // called before all other members to stop the worker thread that references
275 // ClangdServer
276 ClangdScheduler WorkScheduler;
277};
278
279} // namespace clangd
280} // namespace clang
281
282#endif