Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/ClangdServer.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | 0572611fdcc8 |
children | 5f17cb93ff66 |
comparison
equal
deleted
inserted
replaced
220:42394fc6a535 | 221:79ff65ed7e25 |
---|---|
6 // | 6 // |
7 //===-------------------------------------------------------------------===// | 7 //===-------------------------------------------------------------------===// |
8 | 8 |
9 #include "ClangdServer.h" | 9 #include "ClangdServer.h" |
10 #include "CodeComplete.h" | 10 #include "CodeComplete.h" |
11 #include "Config.h" | |
12 #include "Diagnostics.h" | |
13 #include "DumpAST.h" | |
11 #include "FindSymbols.h" | 14 #include "FindSymbols.h" |
12 #include "Format.h" | 15 #include "Format.h" |
13 #include "HeaderSourceSwitch.h" | 16 #include "HeaderSourceSwitch.h" |
14 #include "Headers.h" | 17 #include "Headers.h" |
18 #include "InlayHints.h" | |
15 #include "ParsedAST.h" | 19 #include "ParsedAST.h" |
16 #include "Preamble.h" | 20 #include "Preamble.h" |
17 #include "Protocol.h" | 21 #include "Protocol.h" |
18 #include "SemanticHighlighting.h" | 22 #include "SemanticHighlighting.h" |
19 #include "SemanticSelection.h" | 23 #include "SemanticSelection.h" |
25 #include "index/Merge.h" | 29 #include "index/Merge.h" |
26 #include "refactor/Rename.h" | 30 #include "refactor/Rename.h" |
27 #include "refactor/Tweak.h" | 31 #include "refactor/Tweak.h" |
28 #include "support/Logger.h" | 32 #include "support/Logger.h" |
29 #include "support/Markup.h" | 33 #include "support/Markup.h" |
34 #include "support/MemoryTree.h" | |
35 #include "support/ThreadsafeFS.h" | |
30 #include "support/Trace.h" | 36 #include "support/Trace.h" |
31 #include "clang/Format/Format.h" | 37 #include "clang/Format/Format.h" |
32 #include "clang/Frontend/CompilerInstance.h" | 38 #include "clang/Frontend/CompilerInstance.h" |
33 #include "clang/Frontend/CompilerInvocation.h" | 39 #include "clang/Frontend/CompilerInvocation.h" |
34 #include "clang/Lex/Preprocessor.h" | 40 #include "clang/Lex/Preprocessor.h" |
36 #include "clang/Tooling/Core/Replacement.h" | 42 #include "clang/Tooling/Core/Replacement.h" |
37 #include "llvm/ADT/ArrayRef.h" | 43 #include "llvm/ADT/ArrayRef.h" |
38 #include "llvm/ADT/Optional.h" | 44 #include "llvm/ADT/Optional.h" |
39 #include "llvm/ADT/STLExtras.h" | 45 #include "llvm/ADT/STLExtras.h" |
40 #include "llvm/ADT/ScopeExit.h" | 46 #include "llvm/ADT/ScopeExit.h" |
47 #include "llvm/ADT/StringExtras.h" | |
41 #include "llvm/ADT/StringRef.h" | 48 #include "llvm/ADT/StringRef.h" |
42 #include "llvm/Support/Errc.h" | 49 #include "llvm/Support/Errc.h" |
43 #include "llvm/Support/Error.h" | 50 #include "llvm/Support/Error.h" |
44 #include "llvm/Support/FileSystem.h" | 51 #include "llvm/Support/FileSystem.h" |
45 #include "llvm/Support/Path.h" | 52 #include "llvm/Support/Path.h" |
46 #include "llvm/Support/ScopedPrinter.h" | 53 #include "llvm/Support/ScopedPrinter.h" |
47 #include "llvm/Support/raw_ostream.h" | 54 #include "llvm/Support/raw_ostream.h" |
48 #include <algorithm> | 55 #include <algorithm> |
56 #include <chrono> | |
49 #include <future> | 57 #include <future> |
50 #include <memory> | 58 #include <memory> |
51 #include <mutex> | 59 #include <mutex> |
60 #include <string> | |
52 #include <type_traits> | 61 #include <type_traits> |
53 | 62 |
54 namespace clang { | 63 namespace clang { |
55 namespace clangd { | 64 namespace clangd { |
56 namespace { | 65 namespace { |
57 | 66 |
58 // Update the FileIndex with new ASTs and plumb the diagnostics responses. | 67 // Update the FileIndex with new ASTs and plumb the diagnostics responses. |
59 struct UpdateIndexCallbacks : public ParsingCallbacks { | 68 struct UpdateIndexCallbacks : public ParsingCallbacks { |
60 UpdateIndexCallbacks(FileIndex *FIndex, | 69 UpdateIndexCallbacks(FileIndex *FIndex, |
61 ClangdServer::Callbacks *ServerCallbacks, | 70 ClangdServer::Callbacks *ServerCallbacks) |
62 bool TheiaSemanticHighlighting) | 71 : FIndex(FIndex), ServerCallbacks(ServerCallbacks) {} |
63 : FIndex(FIndex), ServerCallbacks(ServerCallbacks), | |
64 TheiaSemanticHighlighting(TheiaSemanticHighlighting) {} | |
65 | 72 |
66 void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx, | 73 void onPreambleAST(PathRef Path, llvm::StringRef Version, ASTContext &Ctx, |
67 std::shared_ptr<clang::Preprocessor> PP, | 74 std::shared_ptr<clang::Preprocessor> PP, |
68 const CanonicalIncludes &CanonIncludes) override { | 75 const CanonicalIncludes &CanonIncludes) override { |
69 if (FIndex) | 76 if (FIndex) |
72 | 79 |
73 void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override { | 80 void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) override { |
74 if (FIndex) | 81 if (FIndex) |
75 FIndex->updateMain(Path, AST); | 82 FIndex->updateMain(Path, AST); |
76 | 83 |
77 std::vector<Diag> Diagnostics = AST.getDiagnostics(); | 84 assert(AST.getDiagnostics().hasValue() && |
78 std::vector<HighlightingToken> Highlightings; | 85 "We issue callback only with fresh preambles"); |
79 if (TheiaSemanticHighlighting) | 86 std::vector<Diag> Diagnostics = *AST.getDiagnostics(); |
80 Highlightings = getSemanticHighlightings(AST); | |
81 | |
82 if (ServerCallbacks) | 87 if (ServerCallbacks) |
83 Publish([&]() { | 88 Publish([&]() { |
84 ServerCallbacks->onDiagnosticsReady(Path, AST.version(), | 89 ServerCallbacks->onDiagnosticsReady(Path, AST.version(), |
85 std::move(Diagnostics)); | 90 std::move(Diagnostics)); |
86 if (TheiaSemanticHighlighting) | |
87 ServerCallbacks->onHighlightingsReady(Path, AST.version(), | |
88 std::move(Highlightings)); | |
89 }); | 91 }); |
90 } | 92 } |
91 | 93 |
92 void onFailedAST(PathRef Path, llvm::StringRef Version, | 94 void onFailedAST(PathRef Path, llvm::StringRef Version, |
93 std::vector<Diag> Diags, PublishFn Publish) override { | 95 std::vector<Diag> Diags, PublishFn Publish) override { |
99 void onFileUpdated(PathRef File, const TUStatus &Status) override { | 101 void onFileUpdated(PathRef File, const TUStatus &Status) override { |
100 if (ServerCallbacks) | 102 if (ServerCallbacks) |
101 ServerCallbacks->onFileUpdated(File, Status); | 103 ServerCallbacks->onFileUpdated(File, Status); |
102 } | 104 } |
103 | 105 |
106 void onPreamblePublished(PathRef File) override { | |
107 if (ServerCallbacks) | |
108 ServerCallbacks->onSemanticsMaybeChanged(File); | |
109 } | |
110 | |
104 private: | 111 private: |
105 FileIndex *FIndex; | 112 FileIndex *FIndex; |
106 ClangdServer::Callbacks *ServerCallbacks; | 113 ClangdServer::Callbacks *ServerCallbacks; |
107 bool TheiaSemanticHighlighting; | |
108 }; | 114 }; |
115 | |
116 class DraftStoreFS : public ThreadsafeFS { | |
117 public: | |
118 DraftStoreFS(const ThreadsafeFS &Base, const DraftStore &Drafts) | |
119 : Base(Base), DirtyFiles(Drafts) {} | |
120 | |
121 private: | |
122 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> viewImpl() const override { | |
123 auto OFS = llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>( | |
124 Base.view(llvm::None)); | |
125 OFS->pushOverlay(DirtyFiles.asVFS()); | |
126 return OFS; | |
127 } | |
128 | |
129 const ThreadsafeFS &Base; | |
130 const DraftStore &DirtyFiles; | |
131 }; | |
132 | |
109 } // namespace | 133 } // namespace |
110 | 134 |
111 ClangdServer::Options ClangdServer::optsForTest() { | 135 ClangdServer::Options ClangdServer::optsForTest() { |
112 ClangdServer::Options Opts; | 136 ClangdServer::Options Opts; |
113 Opts.UpdateDebounce = DebouncePolicy::fixed(/*zero*/ {}); | 137 Opts.UpdateDebounce = DebouncePolicy::fixed(/*zero*/ {}); |
114 Opts.StorePreamblesInMemory = true; | 138 Opts.StorePreamblesInMemory = true; |
115 Opts.AsyncThreadsCount = 4; // Consistent! | 139 Opts.AsyncThreadsCount = 4; // Consistent! |
116 Opts.TheiaSemanticHighlighting = true; | |
117 return Opts; | 140 return Opts; |
118 } | 141 } |
119 | 142 |
120 ClangdServer::Options::operator TUScheduler::Options() const { | 143 ClangdServer::Options::operator TUScheduler::Options() const { |
121 TUScheduler::Options Opts; | 144 TUScheduler::Options Opts; |
122 Opts.AsyncThreadsCount = AsyncThreadsCount; | 145 Opts.AsyncThreadsCount = AsyncThreadsCount; |
123 Opts.RetentionPolicy = RetentionPolicy; | 146 Opts.RetentionPolicy = RetentionPolicy; |
124 Opts.StorePreamblesInMemory = StorePreamblesInMemory; | 147 Opts.StorePreamblesInMemory = StorePreamblesInMemory; |
125 Opts.UpdateDebounce = UpdateDebounce; | 148 Opts.UpdateDebounce = UpdateDebounce; |
149 Opts.ContextProvider = ContextProvider; | |
126 return Opts; | 150 return Opts; |
127 } | 151 } |
128 | 152 |
129 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, | 153 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, |
130 const FileSystemProvider &FSProvider, | 154 const ThreadsafeFS &TFS, const Options &Opts, |
131 const Options &Opts, Callbacks *Callbacks) | 155 Callbacks *Callbacks) |
132 : FSProvider(FSProvider), | 156 : FeatureModules(Opts.FeatureModules), CDB(CDB), TFS(TFS), |
133 DynamicIdx(Opts.BuildDynamicSymbolIndex | 157 DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr), |
134 ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex) | 158 ClangTidyProvider(Opts.ClangTidyProvider), |
135 : nullptr), | 159 WorkspaceRoot(Opts.WorkspaceRoot), |
136 GetClangTidyOptions(Opts.GetClangTidyOptions), | 160 Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate |
137 SuggestMissingIncludes(Opts.SuggestMissingIncludes), | 161 : TUScheduler::NoInvalidation), |
138 BuildRecoveryAST(Opts.BuildRecoveryAST), | 162 DirtyFS(std::make_unique<DraftStoreFS>(TFS, DraftMgr)) { |
139 PreserveRecoveryASTType(Opts.PreserveRecoveryASTType), | 163 // Pass a callback into `WorkScheduler` to extract symbols from a newly |
140 TweakFilter(Opts.TweakFilter), WorkspaceRoot(Opts.WorkspaceRoot), | 164 // parsed file and rebuild the file index synchronously each time an AST |
141 // Pass a callback into `WorkScheduler` to extract symbols from a newly | 165 // is parsed. |
142 // parsed file and rebuild the file index synchronously each time an AST | 166 WorkScheduler.emplace( |
143 // is parsed. | 167 CDB, TUScheduler::Options(Opts), |
144 // FIXME(ioeric): this can be slow and we may be able to index on less | 168 std::make_unique<UpdateIndexCallbacks>(DynamicIdx.get(), Callbacks)); |
145 // critical paths. | |
146 WorkScheduler( | |
147 CDB, TUScheduler::Options(Opts), | |
148 std::make_unique<UpdateIndexCallbacks>( | |
149 DynamicIdx.get(), Callbacks, Opts.TheiaSemanticHighlighting)) { | |
150 // Adds an index to the stack, at higher priority than existing indexes. | 169 // Adds an index to the stack, at higher priority than existing indexes. |
151 auto AddIndex = [&](SymbolIndex *Idx) { | 170 auto AddIndex = [&](SymbolIndex *Idx) { |
152 if (this->Index != nullptr) { | 171 if (this->Index != nullptr) { |
153 MergedIdx.push_back(std::make_unique<MergedIndex>(Idx, this->Index)); | 172 MergedIdx.push_back(std::make_unique<MergedIndex>(Idx, this->Index)); |
154 this->Index = MergedIdx.back().get(); | 173 this->Index = MergedIdx.back().get(); |
157 } | 176 } |
158 }; | 177 }; |
159 if (Opts.StaticIndex) | 178 if (Opts.StaticIndex) |
160 AddIndex(Opts.StaticIndex); | 179 AddIndex(Opts.StaticIndex); |
161 if (Opts.BackgroundIndex) { | 180 if (Opts.BackgroundIndex) { |
181 BackgroundIndex::Options BGOpts; | |
182 BGOpts.ThreadPoolSize = std::max(Opts.AsyncThreadsCount, 1u); | |
183 BGOpts.OnProgress = [Callbacks](BackgroundQueue::Stats S) { | |
184 if (Callbacks) | |
185 Callbacks->onBackgroundIndexProgress(S); | |
186 }; | |
187 BGOpts.ContextProvider = Opts.ContextProvider; | |
162 BackgroundIdx = std::make_unique<BackgroundIndex>( | 188 BackgroundIdx = std::make_unique<BackgroundIndex>( |
163 Context::current().clone(), FSProvider, CDB, | 189 TFS, CDB, |
164 BackgroundIndexStorage::createDiskBackedStorageFactory( | 190 BackgroundIndexStorage::createDiskBackedStorageFactory( |
165 [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }), | 191 [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); }), |
166 std::max(Opts.AsyncThreadsCount, 1u), | 192 std::move(BGOpts)); |
167 [Callbacks](BackgroundQueue::Stats S) { | |
168 if (Callbacks) | |
169 Callbacks->onBackgroundIndexProgress(S); | |
170 }); | |
171 AddIndex(BackgroundIdx.get()); | 193 AddIndex(BackgroundIdx.get()); |
172 } | 194 } |
173 if (DynamicIdx) | 195 if (DynamicIdx) |
174 AddIndex(DynamicIdx.get()); | 196 AddIndex(DynamicIdx.get()); |
197 | |
198 if (Opts.FeatureModules) { | |
199 FeatureModule::Facilities F{ | |
200 *this->WorkScheduler, | |
201 this->Index, | |
202 this->TFS, | |
203 }; | |
204 for (auto &Mod : *Opts.FeatureModules) | |
205 Mod.initialize(F); | |
206 } | |
207 } | |
208 | |
209 ClangdServer::~ClangdServer() { | |
210 // Destroying TUScheduler first shuts down request threads that might | |
211 // otherwise access members concurrently. | |
212 // (Nobody can be using TUScheduler because we're on the main thread). | |
213 WorkScheduler.reset(); | |
214 // Now requests have stopped, we can shut down feature modules. | |
215 if (FeatureModules) { | |
216 for (auto &Mod : *FeatureModules) | |
217 Mod.stop(); | |
218 for (auto &Mod : *FeatureModules) | |
219 Mod.blockUntilIdle(Deadline::infinity()); | |
220 } | |
175 } | 221 } |
176 | 222 |
177 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, | 223 void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, |
178 llvm::StringRef Version, | 224 llvm::StringRef Version, |
179 WantDiagnostics WantDiags, bool ForceRebuild) { | 225 WantDiagnostics WantDiags, bool ForceRebuild) { |
180 auto FS = FSProvider.getFileSystem(); | 226 std::string ActualVersion = DraftMgr.addDraft(File, Version, Contents); |
181 | |
182 ParseOptions Opts; | 227 ParseOptions Opts; |
183 Opts.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults(); | |
184 // FIXME: call tidy options builder on the worker thread, it can do IO. | |
185 if (GetClangTidyOptions) | |
186 Opts.ClangTidyOpts = GetClangTidyOptions(*FS, File); | |
187 Opts.SuggestMissingIncludes = SuggestMissingIncludes; | |
188 | 228 |
189 // Compile command is set asynchronously during update, as it can be slow. | 229 // Compile command is set asynchronously during update, as it can be slow. |
190 ParseInputs Inputs; | 230 ParseInputs Inputs; |
191 Inputs.FS = FS; | 231 Inputs.TFS = &TFS; |
192 Inputs.Contents = std::string(Contents); | 232 Inputs.Contents = std::string(Contents); |
193 Inputs.Version = Version.str(); | 233 Inputs.Version = std::move(ActualVersion); |
194 Inputs.ForceRebuild = ForceRebuild; | 234 Inputs.ForceRebuild = ForceRebuild; |
195 Inputs.Opts = std::move(Opts); | 235 Inputs.Opts = std::move(Opts); |
196 Inputs.Index = Index; | 236 Inputs.Index = Index; |
197 Inputs.Opts.BuildRecoveryAST = BuildRecoveryAST; | 237 Inputs.ClangTidyProvider = ClangTidyProvider; |
198 Inputs.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; | 238 Inputs.FeatureModules = FeatureModules; |
199 bool NewFile = WorkScheduler.update(File, Inputs, WantDiags); | 239 bool NewFile = WorkScheduler->update(File, Inputs, WantDiags); |
200 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. | 240 // If we loaded Foo.h, we want to make sure Foo.cpp is indexed. |
201 if (NewFile && BackgroundIdx) | 241 if (NewFile && BackgroundIdx) |
202 BackgroundIdx->boostRelated(File); | 242 BackgroundIdx->boostRelated(File); |
203 } | 243 } |
204 | 244 |
205 void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } | 245 void ClangdServer::reparseOpenFilesIfNeeded( |
246 llvm::function_ref<bool(llvm::StringRef File)> Filter) { | |
247 // Reparse only opened files that were modified. | |
248 for (const Path &FilePath : DraftMgr.getActiveFiles()) | |
249 if (Filter(FilePath)) | |
250 if (auto Draft = DraftMgr.getDraft(FilePath)) // else disappeared in race? | |
251 addDocument(FilePath, *Draft->Contents, Draft->Version, | |
252 WantDiagnostics::Auto); | |
253 } | |
254 | |
255 std::shared_ptr<const std::string> ClangdServer::getDraft(PathRef File) const { | |
256 auto Draft = DraftMgr.getDraft(File); | |
257 if (!Draft) | |
258 return nullptr; | |
259 return std::move(Draft->Contents); | |
260 } | |
261 | |
262 std::function<Context(PathRef)> | |
263 ClangdServer::createConfiguredContextProvider(const config::Provider *Provider, | |
264 Callbacks *Publish) { | |
265 if (!Provider) | |
266 return [](llvm::StringRef) { return Context::current().clone(); }; | |
267 | |
268 struct Impl { | |
269 const config::Provider *Provider; | |
270 ClangdServer::Callbacks *Publish; | |
271 std::mutex PublishMu; | |
272 | |
273 Impl(const config::Provider *Provider, ClangdServer::Callbacks *Publish) | |
274 : Provider(Provider), Publish(Publish) {} | |
275 | |
276 Context operator()(llvm::StringRef File) { | |
277 config::Params Params; | |
278 // Don't reread config files excessively often. | |
279 // FIXME: when we see a config file change event, use the event timestamp? | |
280 Params.FreshTime = | |
281 std::chrono::steady_clock::now() - std::chrono::seconds(5); | |
282 llvm::SmallString<256> PosixPath; | |
283 if (!File.empty()) { | |
284 assert(llvm::sys::path::is_absolute(File)); | |
285 llvm::sys::path::native(File, PosixPath, llvm::sys::path::Style::posix); | |
286 Params.Path = PosixPath.str(); | |
287 } | |
288 | |
289 llvm::StringMap<std::vector<Diag>> ReportableDiagnostics; | |
290 Config C = Provider->getConfig(Params, [&](const llvm::SMDiagnostic &D) { | |
291 // Create the map entry even for note diagnostics we don't report. | |
292 // This means that when the file is parsed with no warnings, we | |
293 // publish an empty set of diagnostics, clearing any the client has. | |
294 handleDiagnostic(D, !Publish || D.getFilename().empty() | |
295 ? nullptr | |
296 : &ReportableDiagnostics[D.getFilename()]); | |
297 }); | |
298 // Blindly publish diagnostics for the (unopened) parsed config files. | |
299 // We must avoid reporting diagnostics for *the same file* concurrently. | |
300 // Source diags are published elsewhere, but those are different files. | |
301 if (!ReportableDiagnostics.empty()) { | |
302 std::lock_guard<std::mutex> Lock(PublishMu); | |
303 for (auto &Entry : ReportableDiagnostics) | |
304 Publish->onDiagnosticsReady(Entry.first(), /*Version=*/"", | |
305 std::move(Entry.second)); | |
306 } | |
307 return Context::current().derive(Config::Key, std::move(C)); | |
308 } | |
309 | |
310 void handleDiagnostic(const llvm::SMDiagnostic &D, | |
311 std::vector<Diag> *ClientDiagnostics) { | |
312 switch (D.getKind()) { | |
313 case llvm::SourceMgr::DK_Error: | |
314 elog("config error at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), | |
315 D.getColumnNo(), D.getMessage()); | |
316 break; | |
317 case llvm::SourceMgr::DK_Warning: | |
318 log("config warning at {0}:{1}:{2}: {3}", D.getFilename(), | |
319 D.getLineNo(), D.getColumnNo(), D.getMessage()); | |
320 break; | |
321 case llvm::SourceMgr::DK_Note: | |
322 case llvm::SourceMgr::DK_Remark: | |
323 vlog("config note at {0}:{1}:{2}: {3}", D.getFilename(), D.getLineNo(), | |
324 D.getColumnNo(), D.getMessage()); | |
325 ClientDiagnostics = nullptr; // Don't emit notes as LSP diagnostics. | |
326 break; | |
327 } | |
328 if (ClientDiagnostics) | |
329 ClientDiagnostics->push_back(toDiag(D, Diag::ClangdConfig)); | |
330 } | |
331 }; | |
332 | |
333 // Copyable wrapper. | |
334 return [I(std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) { | |
335 return (*I)(Path); | |
336 }; | |
337 } | |
338 | |
339 void ClangdServer::removeDocument(PathRef File) { | |
340 DraftMgr.removeDraft(File); | |
341 WorkScheduler->remove(File); | |
342 } | |
206 | 343 |
207 void ClangdServer::codeComplete(PathRef File, Position Pos, | 344 void ClangdServer::codeComplete(PathRef File, Position Pos, |
208 const clangd::CodeCompleteOptions &Opts, | 345 const clangd::CodeCompleteOptions &Opts, |
209 Callback<CodeCompleteResult> CB) { | 346 Callback<CodeCompleteResult> CB) { |
210 // Copy completion options for passing them to async task handler. | 347 // Copy completion options for passing them to async task handler. |
211 auto CodeCompleteOpts = Opts; | 348 auto CodeCompleteOpts = Opts; |
212 if (!CodeCompleteOpts.Index) // Respect overridden index. | 349 if (!CodeCompleteOpts.Index) // Respect overridden index. |
213 CodeCompleteOpts.Index = Index; | 350 CodeCompleteOpts.Index = Index; |
214 | 351 |
215 auto Task = [Pos, FS = FSProvider.getFileSystem(), CodeCompleteOpts, | 352 auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB), |
216 File = File.str(), CB = std::move(CB), | |
217 this](llvm::Expected<InputsAndPreamble> IP) mutable { | 353 this](llvm::Expected<InputsAndPreamble> IP) mutable { |
218 if (!IP) | 354 if (!IP) |
219 return CB(IP.takeError()); | 355 return CB(IP.takeError()); |
220 if (auto Reason = isCancelled()) | 356 if (auto Reason = isCancelled()) |
221 return CB(llvm::make_error<CancelledError>(Reason)); | 357 return CB(llvm::make_error<CancelledError>(Reason)); |
223 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; | 359 llvm::Optional<SpeculativeFuzzyFind> SpecFuzzyFind; |
224 if (!IP->Preamble) { | 360 if (!IP->Preamble) { |
225 // No speculation in Fallback mode, as it's supposed to be much faster | 361 // No speculation in Fallback mode, as it's supposed to be much faster |
226 // without compiling. | 362 // without compiling. |
227 vlog("Build for file {0} is not ready. Enter fallback mode.", File); | 363 vlog("Build for file {0} is not ready. Enter fallback mode.", File); |
228 } else { | 364 } else if (CodeCompleteOpts.Index) { |
229 if (CodeCompleteOpts.Index && CodeCompleteOpts.SpeculativeIndexRequest) { | 365 SpecFuzzyFind.emplace(); |
230 SpecFuzzyFind.emplace(); | 366 { |
231 { | 367 std::lock_guard<std::mutex> Lock(CachedCompletionFuzzyFindRequestMutex); |
232 std::lock_guard<std::mutex> Lock( | 368 SpecFuzzyFind->CachedReq = CachedCompletionFuzzyFindRequestByFile[File]; |
233 CachedCompletionFuzzyFindRequestMutex); | |
234 SpecFuzzyFind->CachedReq = | |
235 CachedCompletionFuzzyFindRequestByFile[File]; | |
236 } | |
237 } | 369 } |
238 } | 370 } |
371 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()}; | |
372 ParseInput.Index = Index; | |
373 | |
374 CodeCompleteOpts.MainFileSignals = IP->Signals; | |
375 CodeCompleteOpts.AllScopes = Config::current().Completion.AllScopes; | |
239 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check | 376 // FIXME(ibiryukov): even if Preamble is non-null, we may want to check |
240 // both the old and the new version in case only one of them matches. | 377 // both the old and the new version in case only one of them matches. |
241 CodeCompleteResult Result = clangd::codeComplete( | 378 CodeCompleteResult Result = clangd::codeComplete( |
242 File, IP->Command, IP->Preamble, IP->Contents, Pos, FS, | 379 File, Pos, IP->Preamble, ParseInput, CodeCompleteOpts, |
243 CodeCompleteOpts, SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); | 380 SpecFuzzyFind ? SpecFuzzyFind.getPointer() : nullptr); |
244 { | 381 { |
245 clang::clangd::trace::Span Tracer("Completion results callback"); | 382 clang::clangd::trace::Span Tracer("Completion results callback"); |
246 CB(std::move(Result)); | 383 CB(std::move(Result)); |
247 } | 384 } |
248 if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) { | 385 if (SpecFuzzyFind && SpecFuzzyFind->NewReq.hasValue()) { |
255 // the result (e.g. non-index completion, speculation fails), so that `CB` | 392 // the result (e.g. non-index completion, speculation fails), so that `CB` |
256 // is called as soon as results are available. | 393 // is called as soon as results are available. |
257 }; | 394 }; |
258 | 395 |
259 // We use a potentially-stale preamble because latency is critical here. | 396 // We use a potentially-stale preamble because latency is critical here. |
260 WorkScheduler.runWithPreamble( | 397 WorkScheduler->runWithPreamble( |
261 "CodeComplete", File, | 398 "CodeComplete", File, |
262 (Opts.RunParser == CodeCompleteOptions::AlwaysParse) | 399 (Opts.RunParser == CodeCompleteOptions::AlwaysParse) |
263 ? TUScheduler::Stale | 400 ? TUScheduler::Stale |
264 : TUScheduler::StaleOrAbsent, | 401 : TUScheduler::StaleOrAbsent, |
265 std::move(Task)); | 402 std::move(Task)); |
266 } | 403 } |
267 | 404 |
268 void ClangdServer::signatureHelp(PathRef File, Position Pos, | 405 void ClangdServer::signatureHelp(PathRef File, Position Pos, |
269 Callback<SignatureHelp> CB) { | 406 Callback<SignatureHelp> CB) { |
270 | 407 |
271 auto Action = [Pos, FS = FSProvider.getFileSystem(), File = File.str(), | 408 auto Action = [Pos, File = File.str(), CB = std::move(CB), |
272 CB = std::move(CB), | |
273 this](llvm::Expected<InputsAndPreamble> IP) mutable { | 409 this](llvm::Expected<InputsAndPreamble> IP) mutable { |
274 if (!IP) | 410 if (!IP) |
275 return CB(IP.takeError()); | 411 return CB(IP.takeError()); |
276 | 412 |
277 const auto *PreambleData = IP->Preamble; | 413 const auto *PreambleData = IP->Preamble; |
278 if (!PreambleData) | 414 if (!PreambleData) |
279 return CB(llvm::createStringError(llvm::inconvertibleErrorCode(), | 415 return CB(error("Failed to parse includes")); |
280 "Failed to parse includes")); | 416 |
281 | 417 ParseInputs ParseInput{IP->Command, &TFS, IP->Contents.str()}; |
282 CB(clangd::signatureHelp(File, IP->Command, *PreambleData, IP->Contents, | 418 ParseInput.Index = Index; |
283 Pos, FS, Index)); | 419 CB(clangd::signatureHelp(File, Pos, *PreambleData, ParseInput)); |
284 }; | 420 }; |
285 | 421 |
286 // Unlike code completion, we wait for a preamble here. | 422 // Unlike code completion, we wait for a preamble here. |
287 WorkScheduler.runWithPreamble("SignatureHelp", File, TUScheduler::Stale, | 423 WorkScheduler->runWithPreamble("SignatureHelp", File, TUScheduler::Stale, |
288 std::move(Action)); | 424 std::move(Action)); |
289 } | 425 } |
290 | 426 |
291 llvm::Expected<tooling::Replacements> | 427 void ClangdServer::formatFile(PathRef File, llvm::Optional<Range> Rng, |
292 ClangdServer::formatRange(llvm::StringRef Code, PathRef File, Range Rng) { | 428 Callback<tooling::Replacements> CB) { |
293 llvm::Expected<size_t> Begin = positionToOffset(Code, Rng.start); | 429 auto Code = getDraft(File); |
294 if (!Begin) | 430 if (!Code) |
295 return Begin.takeError(); | 431 return CB(llvm::make_error<LSPError>("trying to format non-added document", |
296 llvm::Expected<size_t> End = positionToOffset(Code, Rng.end); | 432 ErrorCode::InvalidParams)); |
297 if (!End) | 433 tooling::Range RequestedRange; |
298 return End.takeError(); | 434 if (Rng) { |
299 return formatCode(Code, File, {tooling::Range(*Begin, *End - *Begin)}); | 435 llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start); |
300 } | 436 if (!Begin) |
301 | 437 return CB(Begin.takeError()); |
302 llvm::Expected<tooling::Replacements> | 438 llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end); |
303 ClangdServer::formatFile(llvm::StringRef Code, PathRef File) { | 439 if (!End) |
304 // Format everything. | 440 return CB(End.takeError()); |
305 return formatCode(Code, File, {tooling::Range(0, Code.size())}); | 441 RequestedRange = tooling::Range(*Begin, *End - *Begin); |
306 } | 442 } else { |
307 | 443 RequestedRange = tooling::Range(0, Code->size()); |
308 llvm::Expected<std::vector<TextEdit>> | 444 } |
309 ClangdServer::formatOnType(llvm::StringRef Code, PathRef File, Position Pos, | 445 |
310 StringRef TriggerText) { | 446 // Call clang-format. |
311 llvm::Expected<size_t> CursorPos = positionToOffset(Code, Pos); | 447 auto Action = [File = File.str(), Code = std::move(*Code), |
448 Ranges = std::vector<tooling::Range>{RequestedRange}, | |
449 CB = std::move(CB), this]() mutable { | |
450 format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS); | |
451 tooling::Replacements IncludeReplaces = | |
452 format::sortIncludes(Style, Code, Ranges, File); | |
453 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); | |
454 if (!Changed) | |
455 return CB(Changed.takeError()); | |
456 | |
457 CB(IncludeReplaces.merge(format::reformat( | |
458 Style, *Changed, | |
459 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), | |
460 File))); | |
461 }; | |
462 WorkScheduler->runQuick("Format", File, std::move(Action)); | |
463 } | |
464 | |
465 void ClangdServer::formatOnType(PathRef File, Position Pos, | |
466 StringRef TriggerText, | |
467 Callback<std::vector<TextEdit>> CB) { | |
468 auto Code = getDraft(File); | |
469 if (!Code) | |
470 return CB(llvm::make_error<LSPError>("trying to format non-added document", | |
471 ErrorCode::InvalidParams)); | |
472 llvm::Expected<size_t> CursorPos = positionToOffset(*Code, Pos); | |
312 if (!CursorPos) | 473 if (!CursorPos) |
313 return CursorPos.takeError(); | 474 return CB(CursorPos.takeError()); |
314 auto FS = FSProvider.getFileSystem(); | 475 auto Action = [File = File.str(), Code = std::move(*Code), |
315 auto Style = format::getStyle(format::DefaultFormatStyle, File, | 476 TriggerText = TriggerText.str(), CursorPos = *CursorPos, |
316 format::DefaultFallbackStyle, Code, FS.get()); | 477 CB = std::move(CB), this]() mutable { |
317 if (!Style) | 478 auto Style = format::getStyle(format::DefaultFormatStyle, File, |
318 return Style.takeError(); | 479 format::DefaultFallbackStyle, Code, |
319 | 480 TFS.view(/*CWD=*/llvm::None).get()); |
320 std::vector<TextEdit> Result; | 481 if (!Style) |
321 for (const tooling::Replacement &R : | 482 return CB(Style.takeError()); |
322 formatIncremental(Code, *CursorPos, TriggerText, *Style)) | 483 |
323 Result.push_back(replacementToEdit(Code, R)); | 484 std::vector<TextEdit> Result; |
324 return Result; | 485 for (const tooling::Replacement &R : |
486 formatIncremental(Code, CursorPos, TriggerText, *Style)) | |
487 Result.push_back(replacementToEdit(Code, R)); | |
488 return CB(Result); | |
489 }; | |
490 WorkScheduler->runQuick("FormatOnType", File, std::move(Action)); | |
325 } | 491 } |
326 | 492 |
327 void ClangdServer::prepareRename(PathRef File, Position Pos, | 493 void ClangdServer::prepareRename(PathRef File, Position Pos, |
494 llvm::Optional<std::string> NewName, | |
328 const RenameOptions &RenameOpts, | 495 const RenameOptions &RenameOpts, |
329 Callback<llvm::Optional<Range>> CB) { | 496 Callback<RenameResult> CB) { |
330 auto Action = [Pos, File = File.str(), CB = std::move(CB), RenameOpts, | 497 auto Action = [Pos, File = File.str(), CB = std::move(CB), |
331 this](llvm::Expected<InputsAndAST> InpAST) mutable { | 498 NewName = std::move(NewName), |
332 if (!InpAST) | 499 RenameOpts](llvm::Expected<InputsAndAST> InpAST) mutable { |
333 return CB(InpAST.takeError()); | 500 if (!InpAST) |
334 auto &AST = InpAST->AST; | 501 return CB(InpAST.takeError()); |
335 const auto &SM = AST.getSourceManager(); | 502 // prepareRename is latency-sensitive: we don't query the index, as we |
336 auto Loc = sourceLocationInMainFile(SM, Pos); | 503 // only need main-file references |
337 if (!Loc) | 504 auto Results = |
338 return CB(Loc.takeError()); | 505 clangd::rename({Pos, NewName.getValueOr("__clangd_rename_placeholder"), |
339 const auto *TouchingIdentifier = | 506 InpAST->AST, File, /*FS=*/nullptr, |
340 spelledIdentifierTouching(*Loc, AST.getTokens()); | 507 /*Index=*/nullptr, RenameOpts}); |
341 if (!TouchingIdentifier) | 508 if (!Results) { |
342 return CB(llvm::None); // no rename on non-identifiers. | |
343 | |
344 auto Range = halfOpenToRange( | |
345 SM, CharSourceRange::getCharRange(TouchingIdentifier->location(), | |
346 TouchingIdentifier->endLocation())); | |
347 | |
348 if (RenameOpts.AllowCrossFile) | |
349 // FIXME: we now assume cross-file rename always succeeds, revisit this. | |
350 return CB(Range); | |
351 | |
352 // Performing the local rename isn't substantially more expensive than | |
353 // doing an AST-based check, so we just rename and throw away the results. | |
354 auto Changes = clangd::rename({Pos, "dummy", AST, File, Index, RenameOpts, | |
355 /*GetDirtyBuffer=*/nullptr}); | |
356 if (!Changes) { | |
357 // LSP says to return null on failure, but that will result in a generic | 509 // LSP says to return null on failure, but that will result in a generic |
358 // failure message. If we send an LSP error response, clients can surface | 510 // failure message. If we send an LSP error response, clients can surface |
359 // the message to users (VSCode does). | 511 // the message to users (VSCode does). |
360 return CB(Changes.takeError()); | 512 return CB(Results.takeError()); |
361 } | 513 } |
362 return CB(Range); | 514 return CB(*Results); |
363 }; | 515 }; |
364 WorkScheduler.runWithAST("PrepareRename", File, std::move(Action)); | 516 WorkScheduler->runWithAST("PrepareRename", File, std::move(Action)); |
365 } | 517 } |
366 | 518 |
367 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, | 519 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName, |
368 const RenameOptions &Opts, Callback<FileEdits> CB) { | 520 const RenameOptions &Opts, |
369 // A snapshot of all file dirty buffers. | 521 Callback<RenameResult> CB) { |
370 llvm::StringMap<std::string> Snapshot = WorkScheduler.getAllFileContents(); | |
371 auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts, | 522 auto Action = [File = File.str(), NewName = NewName.str(), Pos, Opts, |
372 CB = std::move(CB), Snapshot = std::move(Snapshot), | 523 CB = std::move(CB), |
373 this](llvm::Expected<InputsAndAST> InpAST) mutable { | 524 this](llvm::Expected<InputsAndAST> InpAST) mutable { |
374 // Tracks number of files edited per invocation. | 525 // Tracks number of files edited per invocation. |
375 static constexpr trace::Metric RenameFiles("rename_files", | 526 static constexpr trace::Metric RenameFiles("rename_files", |
376 trace::Metric::Distribution); | 527 trace::Metric::Distribution); |
377 if (!InpAST) | 528 if (!InpAST) |
378 return CB(InpAST.takeError()); | 529 return CB(InpAST.takeError()); |
379 auto GetDirtyBuffer = | 530 auto R = clangd::rename({Pos, NewName, InpAST->AST, File, |
380 [&Snapshot](PathRef AbsPath) -> llvm::Optional<std::string> { | 531 DirtyFS->view(llvm::None), Index, Opts}); |
381 auto It = Snapshot.find(AbsPath); | 532 if (!R) |
382 if (It == Snapshot.end()) | 533 return CB(R.takeError()); |
383 return llvm::None; | |
384 return It->second; | |
385 }; | |
386 auto Edits = clangd::rename( | |
387 {Pos, NewName, InpAST->AST, File, Index, Opts, GetDirtyBuffer}); | |
388 if (!Edits) | |
389 return CB(Edits.takeError()); | |
390 | 534 |
391 if (Opts.WantFormat) { | 535 if (Opts.WantFormat) { |
392 auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, | 536 auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents, |
393 InpAST->Inputs.FS.get()); | 537 *InpAST->Inputs.TFS); |
394 llvm::Error Err = llvm::Error::success(); | 538 llvm::Error Err = llvm::Error::success(); |
395 for (auto &E : *Edits) | 539 for (auto &E : R->GlobalChanges) |
396 Err = | 540 Err = |
397 llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); | 541 llvm::joinErrors(reformatEdit(E.getValue(), Style), std::move(Err)); |
398 | 542 |
399 if (Err) | 543 if (Err) |
400 return CB(std::move(Err)); | 544 return CB(std::move(Err)); |
401 } | 545 } |
402 RenameFiles.record(Edits->size()); | 546 RenameFiles.record(R->GlobalChanges.size()); |
403 return CB(std::move(*Edits)); | 547 return CB(*R); |
404 }; | 548 }; |
405 WorkScheduler.runWithAST("Rename", File, std::move(Action)); | 549 WorkScheduler->runWithAST("Rename", File, std::move(Action)); |
406 } | 550 } |
407 | 551 |
408 // May generate several candidate selections, due to SelectionTree ambiguity. | 552 // May generate several candidate selections, due to SelectionTree ambiguity. |
409 // vector of pointers because GCC doesn't like non-copyable Selection. | 553 // vector of pointers because GCC doesn't like non-copyable Selection. |
410 static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>> | 554 static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>> |
411 tweakSelection(const Range &Sel, const InputsAndAST &AST) { | 555 tweakSelection(const Range &Sel, const InputsAndAST &AST, |
556 llvm::vfs::FileSystem *FS) { | |
412 auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start); | 557 auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start); |
413 if (!Begin) | 558 if (!Begin) |
414 return Begin.takeError(); | 559 return Begin.takeError(); |
415 auto End = positionToOffset(AST.Inputs.Contents, Sel.end); | 560 auto End = positionToOffset(AST.Inputs.Contents, Sel.end); |
416 if (!End) | 561 if (!End) |
418 std::vector<std::unique_ptr<Tweak::Selection>> Result; | 563 std::vector<std::unique_ptr<Tweak::Selection>> Result; |
419 SelectionTree::createEach( | 564 SelectionTree::createEach( |
420 AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End, | 565 AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End, |
421 [&](SelectionTree T) { | 566 [&](SelectionTree T) { |
422 Result.push_back(std::make_unique<Tweak::Selection>( | 567 Result.push_back(std::make_unique<Tweak::Selection>( |
423 AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T))); | 568 AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T), FS)); |
424 return false; | 569 return false; |
425 }); | 570 }); |
426 assert(!Result.empty() && "Expected at least one SelectionTree"); | 571 assert(!Result.empty() && "Expected at least one SelectionTree"); |
427 return std::move(Result); | 572 return std::move(Result); |
428 } | 573 } |
429 | 574 |
430 void ClangdServer::enumerateTweaks(PathRef File, Range Sel, | 575 void ClangdServer::enumerateTweaks( |
431 Callback<std::vector<TweakRef>> CB) { | 576 PathRef File, Range Sel, llvm::unique_function<bool(const Tweak &)> Filter, |
577 Callback<std::vector<TweakRef>> CB) { | |
432 // Tracks number of times a tweak has been offered. | 578 // Tracks number of times a tweak has been offered. |
433 static constexpr trace::Metric TweakAvailable( | 579 static constexpr trace::Metric TweakAvailable( |
434 "tweak_available", trace::Metric::Counter, "tweak_id"); | 580 "tweak_available", trace::Metric::Counter, "tweak_id"); |
435 auto Action = [File = File.str(), Sel, CB = std::move(CB), | 581 auto Action = [Sel, CB = std::move(CB), Filter = std::move(Filter), |
436 this](Expected<InputsAndAST> InpAST) mutable { | 582 FeatureModules(this->FeatureModules)]( |
437 if (!InpAST) | 583 Expected<InputsAndAST> InpAST) mutable { |
438 return CB(InpAST.takeError()); | 584 if (!InpAST) |
439 auto Selections = tweakSelection(Sel, *InpAST); | 585 return CB(InpAST.takeError()); |
586 auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr); | |
440 if (!Selections) | 587 if (!Selections) |
441 return CB(Selections.takeError()); | 588 return CB(Selections.takeError()); |
442 std::vector<TweakRef> Res; | 589 std::vector<TweakRef> Res; |
443 // Don't allow a tweak to fire more than once across ambiguous selections. | 590 // Don't allow a tweak to fire more than once across ambiguous selections. |
444 llvm::DenseSet<llvm::StringRef> PreparedTweaks; | 591 llvm::DenseSet<llvm::StringRef> PreparedTweaks; |
445 auto Filter = [&](const Tweak &T) { | 592 auto DeduplicatingFilter = [&](const Tweak &T) { |
446 return TweakFilter(T) && !PreparedTweaks.count(T.id()); | 593 return Filter(T) && !PreparedTweaks.count(T.id()); |
447 }; | 594 }; |
448 for (const auto &Sel : *Selections) { | 595 for (const auto &Sel : *Selections) { |
449 for (auto &T : prepareTweaks(*Sel, Filter)) { | 596 for (auto &T : prepareTweaks(*Sel, DeduplicatingFilter, FeatureModules)) { |
450 Res.push_back({T->id(), T->title(), T->intent()}); | 597 Res.push_back({T->id(), T->title(), T->kind()}); |
451 PreparedTweaks.insert(T->id()); | 598 PreparedTweaks.insert(T->id()); |
452 TweakAvailable.record(1, T->id()); | 599 TweakAvailable.record(1, T->id()); |
453 } | 600 } |
454 } | 601 } |
455 | 602 |
456 CB(std::move(Res)); | 603 CB(std::move(Res)); |
457 }; | 604 }; |
458 | 605 |
459 WorkScheduler.runWithAST("EnumerateTweaks", File, std::move(Action), | 606 WorkScheduler->runWithAST("EnumerateTweaks", File, std::move(Action), |
460 TUScheduler::InvalidateOnUpdate); | 607 Transient); |
461 } | 608 } |
462 | 609 |
463 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, | 610 void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID, |
464 Callback<Tweak::Effect> CB) { | 611 Callback<Tweak::Effect> CB) { |
465 // Tracks number of times a tweak has been applied. | 612 // Tracks number of times a tweak has been attempted. |
466 static constexpr trace::Metric TweakAttempt( | 613 static constexpr trace::Metric TweakAttempt( |
467 "tweak_attempt", trace::Metric::Counter, "tweak_id"); | 614 "tweak_attempt", trace::Metric::Counter, "tweak_id"); |
615 // Tracks number of times a tweak has failed to produce edits. | |
616 static constexpr trace::Metric TweakFailed( | |
617 "tweak_failed", trace::Metric::Counter, "tweak_id"); | |
468 TweakAttempt.record(1, TweakID); | 618 TweakAttempt.record(1, TweakID); |
469 auto Action = | 619 auto Action = [File = File.str(), Sel, TweakID = TweakID.str(), |
470 [File = File.str(), Sel, TweakID = TweakID.str(), CB = std::move(CB), | 620 CB = std::move(CB), |
471 FS = FSProvider.getFileSystem()](Expected<InputsAndAST> InpAST) mutable { | 621 this](Expected<InputsAndAST> InpAST) mutable { |
472 if (!InpAST) | 622 if (!InpAST) |
473 return CB(InpAST.takeError()); | 623 return CB(InpAST.takeError()); |
474 auto Selections = tweakSelection(Sel, *InpAST); | 624 auto FS = DirtyFS->view(llvm::None); |
475 if (!Selections) | 625 auto Selections = tweakSelection(Sel, *InpAST, FS.get()); |
476 return CB(Selections.takeError()); | 626 if (!Selections) |
477 llvm::Optional<llvm::Expected<Tweak::Effect>> Effect; | 627 return CB(Selections.takeError()); |
478 // Try each selection, take the first one that prepare()s. | 628 llvm::Optional<llvm::Expected<Tweak::Effect>> Effect; |
479 // If they all fail, Effect will hold get the last error. | 629 // Try each selection, take the first one that prepare()s. |
480 for (const auto &Selection : *Selections) { | 630 // If they all fail, Effect will hold get the last error. |
481 auto T = prepareTweak(TweakID, *Selection); | 631 for (const auto &Selection : *Selections) { |
482 if (T) { | 632 auto T = prepareTweak(TweakID, *Selection, FeatureModules); |
483 Effect = (*T)->apply(*Selection); | 633 if (T) { |
484 break; | 634 Effect = (*T)->apply(*Selection); |
485 } | 635 break; |
486 Effect = T.takeError(); | 636 } |
487 } | 637 Effect = T.takeError(); |
488 assert(Effect.hasValue() && "Expected at least one selection"); | 638 } |
489 if (*Effect) { | 639 assert(Effect.hasValue() && "Expected at least one selection"); |
490 // Tweaks don't apply clang-format, do that centrally here. | 640 if (*Effect) { |
491 for (auto &It : (*Effect)->ApplyEdits) { | 641 // Tweaks don't apply clang-format, do that centrally here. |
492 Edit &E = It.second; | 642 for (auto &It : (*Effect)->ApplyEdits) { |
493 format::FormatStyle Style = | 643 Edit &E = It.second; |
494 getFormatStyleForFile(File, E.InitialCode, FS.get()); | 644 format::FormatStyle Style = |
495 if (llvm::Error Err = reformatEdit(E, Style)) | 645 getFormatStyleForFile(File, E.InitialCode, TFS); |
496 elog("Failed to format {0}: {1}", It.first(), std::move(Err)); | 646 if (llvm::Error Err = reformatEdit(E, Style)) |
497 } | 647 elog("Failed to format {0}: {1}", It.first(), std::move(Err)); |
498 } | 648 } |
499 return CB(std::move(*Effect)); | 649 } else { |
500 }; | 650 TweakFailed.record(1, TweakID); |
501 WorkScheduler.runWithAST("ApplyTweak", File, std::move(Action)); | 651 } |
502 } | 652 return CB(std::move(*Effect)); |
503 | 653 }; |
504 void ClangdServer::dumpAST(PathRef File, | 654 WorkScheduler->runWithAST("ApplyTweak", File, std::move(Action)); |
505 llvm::unique_function<void(std::string)> Callback) { | |
506 auto Action = [Callback = std::move(Callback)]( | |
507 llvm::Expected<InputsAndAST> InpAST) mutable { | |
508 if (!InpAST) { | |
509 llvm::consumeError(InpAST.takeError()); | |
510 return Callback("<no-ast>"); | |
511 } | |
512 std::string Result; | |
513 | |
514 llvm::raw_string_ostream ResultOS(Result); | |
515 clangd::dumpAST(InpAST->AST, ResultOS); | |
516 ResultOS.flush(); | |
517 | |
518 Callback(Result); | |
519 }; | |
520 | |
521 WorkScheduler.runWithAST("DumpAST", File, std::move(Action)); | |
522 } | 655 } |
523 | 656 |
524 void ClangdServer::locateSymbolAt(PathRef File, Position Pos, | 657 void ClangdServer::locateSymbolAt(PathRef File, Position Pos, |
525 Callback<std::vector<LocatedSymbol>> CB) { | 658 Callback<std::vector<LocatedSymbol>> CB) { |
526 auto Action = [Pos, CB = std::move(CB), | 659 auto Action = [Pos, CB = std::move(CB), |
528 if (!InpAST) | 661 if (!InpAST) |
529 return CB(InpAST.takeError()); | 662 return CB(InpAST.takeError()); |
530 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index)); | 663 CB(clangd::locateSymbolAt(InpAST->AST, Pos, Index)); |
531 }; | 664 }; |
532 | 665 |
533 WorkScheduler.runWithAST("Definitions", File, std::move(Action)); | 666 WorkScheduler->runWithAST("Definitions", File, std::move(Action)); |
534 } | 667 } |
535 | 668 |
536 void ClangdServer::switchSourceHeader( | 669 void ClangdServer::switchSourceHeader( |
537 PathRef Path, Callback<llvm::Optional<clangd::Path>> CB) { | 670 PathRef Path, Callback<llvm::Optional<clangd::Path>> CB) { |
538 // We want to return the result as fast as possible, strategy is: | 671 // We want to return the result as fast as possible, strategy is: |
539 // 1) use the file-only heuristic, it requires some IO but it is much | 672 // 1) use the file-only heuristic, it requires some IO but it is much |
540 // faster than building AST, but it only works when .h/.cc files are in | 673 // faster than building AST, but it only works when .h/.cc files are in |
541 // the same directory. | 674 // the same directory. |
542 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports | 675 // 2) if 1) fails, we use the AST&Index approach, it is slower but supports |
543 // different code layout. | 676 // different code layout. |
544 if (auto CorrespondingFile = getCorrespondingHeaderOrSource( | 677 if (auto CorrespondingFile = |
545 std::string(Path), FSProvider.getFileSystem())) | 678 getCorrespondingHeaderOrSource(Path, TFS.view(llvm::None))) |
546 return CB(std::move(CorrespondingFile)); | 679 return CB(std::move(CorrespondingFile)); |
547 auto Action = [Path = Path.str(), CB = std::move(CB), | 680 auto Action = [Path = Path.str(), CB = std::move(CB), |
548 this](llvm::Expected<InputsAndAST> InpAST) mutable { | 681 this](llvm::Expected<InputsAndAST> InpAST) mutable { |
549 if (!InpAST) | 682 if (!InpAST) |
550 return CB(InpAST.takeError()); | 683 return CB(InpAST.takeError()); |
551 CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index)); | 684 CB(getCorrespondingHeaderOrSource(Path, InpAST->AST, Index)); |
552 }; | 685 }; |
553 WorkScheduler.runWithAST("SwitchHeaderSource", Path, std::move(Action)); | 686 WorkScheduler->runWithAST("SwitchHeaderSource", Path, std::move(Action)); |
554 } | |
555 | |
556 llvm::Expected<tooling::Replacements> | |
557 ClangdServer::formatCode(llvm::StringRef Code, PathRef File, | |
558 llvm::ArrayRef<tooling::Range> Ranges) { | |
559 // Call clang-format. | |
560 format::FormatStyle Style = | |
561 getFormatStyleForFile(File, Code, FSProvider.getFileSystem().get()); | |
562 tooling::Replacements IncludeReplaces = | |
563 format::sortIncludes(Style, Code, Ranges, File); | |
564 auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); | |
565 if (!Changed) | |
566 return Changed.takeError(); | |
567 | |
568 return IncludeReplaces.merge(format::reformat( | |
569 Style, *Changed, | |
570 tooling::calculateRangesAfterReplacements(IncludeReplaces, Ranges), | |
571 File)); | |
572 } | 687 } |
573 | 688 |
574 void ClangdServer::findDocumentHighlights( | 689 void ClangdServer::findDocumentHighlights( |
575 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) { | 690 PathRef File, Position Pos, Callback<std::vector<DocumentHighlight>> CB) { |
576 auto Action = | 691 auto Action = |
578 if (!InpAST) | 693 if (!InpAST) |
579 return CB(InpAST.takeError()); | 694 return CB(InpAST.takeError()); |
580 CB(clangd::findDocumentHighlights(InpAST->AST, Pos)); | 695 CB(clangd::findDocumentHighlights(InpAST->AST, Pos)); |
581 }; | 696 }; |
582 | 697 |
583 WorkScheduler.runWithAST("Highlights", File, std::move(Action), | 698 WorkScheduler->runWithAST("Highlights", File, std::move(Action), Transient); |
584 TUScheduler::InvalidateOnUpdate); | |
585 } | 699 } |
586 | 700 |
587 void ClangdServer::findHover(PathRef File, Position Pos, | 701 void ClangdServer::findHover(PathRef File, Position Pos, |
588 Callback<llvm::Optional<HoverInfo>> CB) { | 702 Callback<llvm::Optional<HoverInfo>> CB) { |
589 auto Action = [File = File.str(), Pos, CB = std::move(CB), | 703 auto Action = [File = File.str(), Pos, CB = std::move(CB), |
590 this](llvm::Expected<InputsAndAST> InpAST) mutable { | 704 this](llvm::Expected<InputsAndAST> InpAST) mutable { |
591 if (!InpAST) | 705 if (!InpAST) |
592 return CB(InpAST.takeError()); | 706 return CB(InpAST.takeError()); |
593 format::FormatStyle Style = getFormatStyleForFile( | 707 format::FormatStyle Style = getFormatStyleForFile( |
594 File, InpAST->Inputs.Contents, InpAST->Inputs.FS.get()); | 708 File, InpAST->Inputs.Contents, *InpAST->Inputs.TFS); |
595 CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index)); | 709 CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index)); |
596 }; | 710 }; |
597 | 711 |
598 WorkScheduler.runWithAST("Hover", File, std::move(Action), | 712 WorkScheduler->runWithAST("Hover", File, std::move(Action), Transient); |
599 TUScheduler::InvalidateOnUpdate); | |
600 } | 713 } |
601 | 714 |
602 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve, | 715 void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve, |
603 TypeHierarchyDirection Direction, | 716 TypeHierarchyDirection Direction, |
604 Callback<Optional<TypeHierarchyItem>> CB) { | 717 Callback<Optional<TypeHierarchyItem>> CB) { |
608 return CB(InpAST.takeError()); | 721 return CB(InpAST.takeError()); |
609 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index, | 722 CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction, Index, |
610 File)); | 723 File)); |
611 }; | 724 }; |
612 | 725 |
613 WorkScheduler.runWithAST("Type Hierarchy", File, std::move(Action)); | 726 WorkScheduler->runWithAST("TypeHierarchy", File, std::move(Action)); |
614 } | 727 } |
615 | 728 |
616 void ClangdServer::resolveTypeHierarchy( | 729 void ClangdServer::resolveTypeHierarchy( |
617 TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, | 730 TypeHierarchyItem Item, int Resolve, TypeHierarchyDirection Direction, |
618 Callback<llvm::Optional<TypeHierarchyItem>> CB) { | 731 Callback<llvm::Optional<TypeHierarchyItem>> CB) { |
619 clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); | 732 WorkScheduler->run( |
620 CB(Item); | 733 "Resolve Type Hierarchy", "", [=, CB = std::move(CB)]() mutable { |
734 clangd::resolveTypeHierarchy(Item, Resolve, Direction, Index); | |
735 CB(Item); | |
736 }); | |
737 } | |
738 | |
739 void ClangdServer::prepareCallHierarchy( | |
740 PathRef File, Position Pos, Callback<std::vector<CallHierarchyItem>> CB) { | |
741 auto Action = [File = File.str(), Pos, | |
742 CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable { | |
743 if (!InpAST) | |
744 return CB(InpAST.takeError()); | |
745 CB(clangd::prepareCallHierarchy(InpAST->AST, Pos, File)); | |
746 }; | |
747 WorkScheduler->runWithAST("CallHierarchy", File, std::move(Action)); | |
748 } | |
749 | |
750 void ClangdServer::incomingCalls( | |
751 const CallHierarchyItem &Item, | |
752 Callback<std::vector<CallHierarchyIncomingCall>> CB) { | |
753 WorkScheduler->run("Incoming Calls", "", | |
754 [CB = std::move(CB), Item, this]() mutable { | |
755 CB(clangd::incomingCalls(Item, Index)); | |
756 }); | |
757 } | |
758 | |
759 void ClangdServer::inlayHints(PathRef File, | |
760 Callback<std::vector<InlayHint>> CB) { | |
761 auto Action = [CB = std::move(CB)](Expected<InputsAndAST> InpAST) mutable { | |
762 if (!InpAST) | |
763 return CB(InpAST.takeError()); | |
764 CB(clangd::inlayHints(InpAST->AST)); | |
765 }; | |
766 WorkScheduler->runWithAST("InlayHints", File, std::move(Action)); | |
621 } | 767 } |
622 | 768 |
623 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { | 769 void ClangdServer::onFileEvent(const DidChangeWatchedFilesParams &Params) { |
624 // FIXME: Do nothing for now. This will be used for indexing and potentially | 770 // FIXME: Do nothing for now. This will be used for indexing and potentially |
625 // invalidating other caches. | 771 // invalidating other caches. |
626 } | 772 } |
627 | 773 |
628 void ClangdServer::workspaceSymbols( | 774 void ClangdServer::workspaceSymbols( |
629 llvm::StringRef Query, int Limit, | 775 llvm::StringRef Query, int Limit, |
630 Callback<std::vector<SymbolInformation>> CB) { | 776 Callback<std::vector<SymbolInformation>> CB) { |
631 WorkScheduler.run( | 777 WorkScheduler->run( |
632 "getWorkspaceSymbols", | 778 "getWorkspaceSymbols", /*Path=*/"", |
633 [Query = Query.str(), Limit, CB = std::move(CB), this]() mutable { | 779 [Query = Query.str(), Limit, CB = std::move(CB), this]() mutable { |
634 CB(clangd::getWorkspaceSymbols(Query, Limit, Index, | 780 CB(clangd::getWorkspaceSymbols(Query, Limit, Index, |
635 WorkspaceRoot.getValueOr(""))); | 781 WorkspaceRoot.getValueOr(""))); |
636 }); | 782 }); |
637 } | 783 } |
642 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { | 788 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { |
643 if (!InpAST) | 789 if (!InpAST) |
644 return CB(InpAST.takeError()); | 790 return CB(InpAST.takeError()); |
645 CB(clangd::getDocumentSymbols(InpAST->AST)); | 791 CB(clangd::getDocumentSymbols(InpAST->AST)); |
646 }; | 792 }; |
647 WorkScheduler.runWithAST("documentSymbols", File, std::move(Action), | 793 WorkScheduler->runWithAST("DocumentSymbols", File, std::move(Action), |
648 TUScheduler::InvalidateOnUpdate); | 794 Transient); |
795 } | |
796 | |
797 void ClangdServer::foldingRanges(llvm::StringRef File, | |
798 Callback<std::vector<FoldingRange>> CB) { | |
799 auto Action = | |
800 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { | |
801 if (!InpAST) | |
802 return CB(InpAST.takeError()); | |
803 CB(clangd::getFoldingRanges(InpAST->AST)); | |
804 }; | |
805 WorkScheduler->runWithAST("FoldingRanges", File, std::move(Action), | |
806 Transient); | |
807 } | |
808 | |
809 void ClangdServer::findImplementations( | |
810 PathRef File, Position Pos, Callback<std::vector<LocatedSymbol>> CB) { | |
811 auto Action = [Pos, CB = std::move(CB), | |
812 this](llvm::Expected<InputsAndAST> InpAST) mutable { | |
813 if (!InpAST) | |
814 return CB(InpAST.takeError()); | |
815 CB(clangd::findImplementations(InpAST->AST, Pos, Index)); | |
816 }; | |
817 | |
818 WorkScheduler->runWithAST("Implementations", File, std::move(Action)); | |
649 } | 819 } |
650 | 820 |
651 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, | 821 void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, |
652 Callback<ReferencesResult> CB) { | 822 Callback<ReferencesResult> CB) { |
653 auto Action = [Pos, Limit, CB = std::move(CB), | 823 auto Action = [Pos, Limit, CB = std::move(CB), |
655 if (!InpAST) | 825 if (!InpAST) |
656 return CB(InpAST.takeError()); | 826 return CB(InpAST.takeError()); |
657 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index)); | 827 CB(clangd::findReferences(InpAST->AST, Pos, Limit, Index)); |
658 }; | 828 }; |
659 | 829 |
660 WorkScheduler.runWithAST("References", File, std::move(Action)); | 830 WorkScheduler->runWithAST("References", File, std::move(Action)); |
661 } | 831 } |
662 | 832 |
663 void ClangdServer::symbolInfo(PathRef File, Position Pos, | 833 void ClangdServer::symbolInfo(PathRef File, Position Pos, |
664 Callback<std::vector<SymbolDetails>> CB) { | 834 Callback<std::vector<SymbolDetails>> CB) { |
665 auto Action = | 835 auto Action = |
667 if (!InpAST) | 837 if (!InpAST) |
668 return CB(InpAST.takeError()); | 838 return CB(InpAST.takeError()); |
669 CB(clangd::getSymbolInfo(InpAST->AST, Pos)); | 839 CB(clangd::getSymbolInfo(InpAST->AST, Pos)); |
670 }; | 840 }; |
671 | 841 |
672 WorkScheduler.runWithAST("SymbolInfo", File, std::move(Action)); | 842 WorkScheduler->runWithAST("SymbolInfo", File, std::move(Action)); |
673 } | 843 } |
674 | 844 |
675 void ClangdServer::semanticRanges(PathRef File, | 845 void ClangdServer::semanticRanges(PathRef File, |
676 const std::vector<Position> &Positions, | 846 const std::vector<Position> &Positions, |
677 Callback<std::vector<SelectionRange>> CB) { | 847 Callback<std::vector<SelectionRange>> CB) { |
686 else | 856 else |
687 return CB(Range.takeError()); | 857 return CB(Range.takeError()); |
688 } | 858 } |
689 CB(std::move(Result)); | 859 CB(std::move(Result)); |
690 }; | 860 }; |
691 WorkScheduler.runWithAST("SemanticRanges", File, std::move(Action)); | 861 WorkScheduler->runWithAST("SemanticRanges", File, std::move(Action)); |
692 } | 862 } |
693 | 863 |
694 void ClangdServer::documentLinks(PathRef File, | 864 void ClangdServer::documentLinks(PathRef File, |
695 Callback<std::vector<DocumentLink>> CB) { | 865 Callback<std::vector<DocumentLink>> CB) { |
696 auto Action = | 866 auto Action = |
697 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { | 867 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { |
698 if (!InpAST) | 868 if (!InpAST) |
699 return CB(InpAST.takeError()); | 869 return CB(InpAST.takeError()); |
700 CB(clangd::getDocumentLinks(InpAST->AST)); | 870 CB(clangd::getDocumentLinks(InpAST->AST)); |
701 }; | 871 }; |
702 WorkScheduler.runWithAST("DocumentLinks", File, std::move(Action), | 872 WorkScheduler->runWithAST("DocumentLinks", File, std::move(Action), |
703 TUScheduler::InvalidateOnUpdate); | 873 Transient); |
704 } | 874 } |
705 | 875 |
706 void ClangdServer::semanticHighlights( | 876 void ClangdServer::semanticHighlights( |
707 PathRef File, Callback<std::vector<HighlightingToken>> CB) { | 877 PathRef File, Callback<std::vector<HighlightingToken>> CB) { |
708 auto Action = | 878 auto Action = |
709 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { | 879 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { |
710 if (!InpAST) | 880 if (!InpAST) |
711 return CB(InpAST.takeError()); | 881 return CB(InpAST.takeError()); |
712 CB(clangd::getSemanticHighlightings(InpAST->AST)); | 882 CB(clangd::getSemanticHighlightings(InpAST->AST)); |
713 }; | 883 }; |
714 WorkScheduler.runWithAST("SemanticHighlights", File, std::move(Action), | 884 WorkScheduler->runWithAST("SemanticHighlights", File, std::move(Action), |
715 TUScheduler::InvalidateOnUpdate); | 885 Transient); |
886 } | |
887 | |
888 void ClangdServer::getAST(PathRef File, llvm::Optional<Range> R, | |
889 Callback<llvm::Optional<ASTNode>> CB) { | |
890 auto Action = | |
891 [R, CB(std::move(CB))](llvm::Expected<InputsAndAST> Inputs) mutable { | |
892 if (!Inputs) | |
893 return CB(Inputs.takeError()); | |
894 if (!R) { | |
895 // It's safe to pass in the TU, as dumpAST() does not | |
896 // deserialize the preamble. | |
897 auto Node = DynTypedNode::create( | |
898 *Inputs->AST.getASTContext().getTranslationUnitDecl()); | |
899 return CB(dumpAST(Node, Inputs->AST.getTokens(), | |
900 Inputs->AST.getASTContext())); | |
901 } | |
902 unsigned Start, End; | |
903 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->start)) | |
904 Start = *Offset; | |
905 else | |
906 return CB(Offset.takeError()); | |
907 if (auto Offset = positionToOffset(Inputs->Inputs.Contents, R->end)) | |
908 End = *Offset; | |
909 else | |
910 return CB(Offset.takeError()); | |
911 bool Success = SelectionTree::createEach( | |
912 Inputs->AST.getASTContext(), Inputs->AST.getTokens(), Start, End, | |
913 [&](SelectionTree T) { | |
914 if (const SelectionTree::Node *N = T.commonAncestor()) { | |
915 CB(dumpAST(N->ASTNode, Inputs->AST.getTokens(), | |
916 Inputs->AST.getASTContext())); | |
917 return true; | |
918 } | |
919 return false; | |
920 }); | |
921 if (!Success) | |
922 CB(llvm::None); | |
923 }; | |
924 WorkScheduler->runWithAST("GetAST", File, std::move(Action)); | |
925 } | |
926 | |
927 void ClangdServer::customAction(PathRef File, llvm::StringRef Name, | |
928 Callback<InputsAndAST> Action) { | |
929 WorkScheduler->runWithAST(Name, File, std::move(Action)); | |
930 } | |
931 | |
932 void ClangdServer::diagnostics(PathRef File, Callback<std::vector<Diag>> CB) { | |
933 auto Action = | |
934 [CB = std::move(CB)](llvm::Expected<InputsAndAST> InpAST) mutable { | |
935 if (!InpAST) | |
936 return CB(InpAST.takeError()); | |
937 if (auto Diags = InpAST->AST.getDiagnostics()) | |
938 return CB(*Diags); | |
939 // FIXME: Use ServerCancelled error once it is settled in LSP-3.17. | |
940 return CB(llvm::make_error<LSPError>("server is busy parsing includes", | |
941 ErrorCode::InternalError)); | |
942 }; | |
943 | |
944 WorkScheduler->runWithAST("Diagnostics", File, std::move(Action)); | |
716 } | 945 } |
717 | 946 |
718 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const { | 947 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const { |
719 return WorkScheduler.fileStats(); | 948 return WorkScheduler->fileStats(); |
720 } | 949 } |
721 | 950 |
722 LLVM_NODISCARD bool | 951 LLVM_NODISCARD bool |
723 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) { | 952 ClangdServer::blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds) { |
724 return WorkScheduler.blockUntilIdle(timeoutSeconds(TimeoutSeconds)) && | 953 // Order is important here: we don't want to block on A and then B, |
725 (!BackgroundIdx || | 954 // if B might schedule work on A. |
726 BackgroundIdx->blockUntilIdleForTest(TimeoutSeconds)); | 955 |
727 } | 956 // Nothing else can schedule work on TUScheduler, because it's not threadsafe |
728 | 957 // and we're blocking the main thread. |
958 if (!WorkScheduler->blockUntilIdle(timeoutSeconds(TimeoutSeconds))) | |
959 return false; | |
960 | |
961 // Unfortunately we don't have strict topological order between the rest of | |
962 // the components. E.g. CDB broadcast triggers backrgound indexing. | |
963 // This queries the CDB which may discover new work if disk has changed. | |
964 // | |
965 // So try each one a few times in a loop. | |
966 // If there are no tricky interactions then all after the first are no-ops. | |
967 // Then on the last iteration, verify they're idle without waiting. | |
968 // | |
969 // There's a small chance they're juggling work and we didn't catch them :-( | |
970 for (llvm::Optional<double> Timeout : | |
971 {TimeoutSeconds, TimeoutSeconds, llvm::Optional<double>(0)}) { | |
972 if (!CDB.blockUntilIdle(timeoutSeconds(Timeout))) | |
973 return false; | |
974 if (BackgroundIdx && !BackgroundIdx->blockUntilIdleForTest(Timeout)) | |
975 return false; | |
976 if (FeatureModules && llvm::any_of(*FeatureModules, [&](FeatureModule &M) { | |
977 return !M.blockUntilIdle(timeoutSeconds(Timeout)); | |
978 })) | |
979 return false; | |
980 } | |
981 | |
982 assert(WorkScheduler->blockUntilIdle(Deadline::zero()) && | |
983 "Something scheduled work while we're blocking the main thread!"); | |
984 return true; | |
985 } | |
986 | |
987 void ClangdServer::profile(MemoryTree &MT) const { | |
988 if (DynamicIdx) | |
989 DynamicIdx->profile(MT.child("dynamic_index")); | |
990 if (BackgroundIdx) | |
991 BackgroundIdx->profile(MT.child("background_index")); | |
992 WorkScheduler->profile(MT.child("tuscheduler")); | |
993 } | |
729 } // namespace clangd | 994 } // namespace clangd |
730 } // namespace clang | 995 } // namespace clang |