@@ -133,14 +133,14 @@ ClangdServer::Options::operator TUScheduler::Options() const {
133133 Opts.StorePreamblesInMemory = StorePreamblesInMemory;
134134 Opts.UpdateDebounce = UpdateDebounce;
135135 Opts.AsyncPreambleBuilds = AsyncPreambleBuilds;
136+ Opts.ContextProvider = ContextProvider;
136137 return Opts;
137138}
138139
139140ClangdServer::ClangdServer (const GlobalCompilationDatabase &CDB,
140141 const ThreadsafeFS &TFS, const Options &Opts,
141142 Callbacks *Callbacks)
142- : ConfigProvider(Opts.ConfigProvider), CDB(CDB), TFS(TFS),
143- ServerCallbacks(Callbacks),
143+ : CDB(CDB), TFS(TFS),
144144 DynamicIdx(Opts.BuildDynamicSymbolIndex
145145 ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex,
146146 Opts.CollectMainFileRefs)
@@ -153,14 +153,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
153153 // FIXME(ioeric): this can be slow and we may be able to index on less
154154 // critical paths.
155155 WorkScheduler(
156- CDB,
157- [&, this ] {
158- TUScheduler::Options O (Opts);
159- O.ContextProvider = [this ](PathRef P) {
160- return createProcessingContext (P);
161- };
162- return O;
163- }(),
156+ CDB, TUScheduler::Options(Opts),
164157 std::make_unique<UpdateIndexCallbacks>(
165158 DynamicIdx.get(), Callbacks, Opts.TheiaSemanticHighlighting)) {
166159 // Adds an index to the stack, at higher priority than existing indexes.
@@ -181,9 +174,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
181174 if (Callbacks)
182175 Callbacks->onBackgroundIndexProgress (S);
183176 };
184- BGOpts.ContextProvider = [this ](PathRef P) {
185- return createProcessingContext (P);
186- };
177+ BGOpts.ContextProvider = Opts.ContextProvider ;
187178 BGOpts.CollectMainFileRefs = Opts.CollectMainFileRefs ;
188179 BackgroundIdx = std::make_unique<BackgroundIndex>(
189180 TFS, CDB,
@@ -216,6 +207,83 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
216207 BackgroundIdx->boostRelated (File);
217208}
218209
210+ std::function<Context(PathRef)>
211+ ClangdServer::createConfiguredContextProvider (const config::Provider *Provider,
212+ Callbacks *Publish) {
213+ if (!Provider)
214+ return [](llvm::StringRef) { return Context::current ().clone (); };
215+
216+ struct Impl {
217+ const config::Provider *Provider;
218+ ClangdServer::Callbacks *Publish;
219+ std::mutex PublishMu;
220+
221+ Impl (const config::Provider *Provider, ClangdServer::Callbacks *Publish)
222+ : Provider(Provider), Publish(Publish) {}
223+
224+ Context operator ()(llvm::StringRef File) {
225+ config::Params Params;
226+ // Don't reread config files excessively often.
227+ // FIXME: when we see a config file change event, use the event timestamp?
228+ Params.FreshTime =
229+ std::chrono::steady_clock::now () - std::chrono::seconds (5 );
230+ llvm::SmallString<256 > PosixPath;
231+ if (!File.empty ()) {
232+ assert (llvm::sys::path::is_absolute (File));
233+ llvm::sys::path::native (File, PosixPath, llvm::sys::path::Style::posix);
234+ Params.Path = PosixPath.str ();
235+ }
236+
237+ llvm::StringMap<std::vector<Diag>> ReportableDiagnostics;
238+ Config C = Provider->getConfig (Params, [&](const llvm::SMDiagnostic &D) {
239+ // Create the map entry even for note diagnostics we don't report.
240+ // This means that when the file is parsed with no warnings, we
241+ // publish an empty set of diagnostics, clearing any the client has.
242+ handleDiagnostic (D, !Publish || D.getFilename ().empty ()
243+ ? nullptr
244+ : &ReportableDiagnostics[D.getFilename ()]);
245+ });
246+ // Blindly publish diagnostics for the (unopened) parsed config files.
247+ // We must avoid reporting diagnostics for *the same file* concurrently.
248+ // Source diags are published elsewhere, but those are different files.
249+ if (!ReportableDiagnostics.empty ()) {
250+ std::lock_guard<std::mutex> Lock (PublishMu);
251+ for (auto &Entry : ReportableDiagnostics)
252+ Publish->onDiagnosticsReady (Entry.first (), /* Version=*/ " " ,
253+ std::move (Entry.second ));
254+ }
255+ return Context::current ().derive (Config::Key, std::move (C));
256+ }
257+
258+ void handleDiagnostic (const llvm::SMDiagnostic &D,
259+ std::vector<Diag> *ClientDiagnostics) {
260+ switch (D.getKind ()) {
261+ case llvm::SourceMgr::DK_Error:
262+ elog (" config error at {0}:{1}:{2}: {3}" , D.getFilename (), D.getLineNo (),
263+ D.getColumnNo (), D.getMessage ());
264+ break ;
265+ case llvm::SourceMgr::DK_Warning:
266+ log (" config warning at {0}:{1}:{2}: {3}" , D.getFilename (),
267+ D.getLineNo (), D.getColumnNo (), D.getMessage ());
268+ break ;
269+ case llvm::SourceMgr::DK_Note:
270+ case llvm::SourceMgr::DK_Remark:
271+ vlog (" config note at {0}:{1}:{2}: {3}" , D.getFilename (), D.getLineNo (),
272+ D.getColumnNo (), D.getMessage ());
273+ ClientDiagnostics = nullptr ; // Don't emit notes as LSP diagnostics.
274+ break ;
275+ }
276+ if (ClientDiagnostics)
277+ ClientDiagnostics->push_back (toDiag (D, Diag::ClangdConfig));
278+ }
279+ };
280+
281+ // Copyable wrapper.
282+ return [I (std::make_shared<Impl>(Provider, Publish))](llvm::StringRef Path) {
283+ return (*I)(Path);
284+ };
285+ }
286+
219287void ClangdServer::removeDocument (PathRef File) { WorkScheduler.remove (File); }
220288
221289void ClangdServer::codeComplete (PathRef File, Position Pos,
@@ -802,62 +870,6 @@ llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const {
802870 return WorkScheduler.fileStats ();
803871}
804872
805- Context ClangdServer::createProcessingContext (PathRef File) const {
806- if (!ConfigProvider)
807- return Context::current ().clone ();
808-
809- config::Params Params;
810- // Don't reread config files excessively often.
811- // FIXME: when we see a config file change event, use the event timestamp.
812- Params.FreshTime = std::chrono::steady_clock::now () - std::chrono::seconds (5 );
813- llvm::SmallString<256 > PosixPath;
814- if (!File.empty ()) {
815- assert (llvm::sys::path::is_absolute (File));
816- llvm::sys::path::native (File, PosixPath, llvm::sys::path::Style::posix);
817- Params.Path = PosixPath.str ();
818- }
819-
820- llvm::StringMap<std::vector<Diag>> ReportableDiagnostics;
821- auto ConfigDiagnosticHandler = [&](const llvm::SMDiagnostic &D) {
822- // Ensure we create the map entry even for note diagnostics we don't report.
823- // This means that when the file is parsed with no warnings, we'll
824- // publish an empty set of diagnostics, clearing any the client has.
825- auto *Reportable = D.getFilename ().empty ()
826- ? nullptr
827- : &ReportableDiagnostics[D.getFilename ()];
828- switch (D.getKind ()) {
829- case llvm::SourceMgr::DK_Error:
830- elog (" config error at {0}:{1}:{2}: {3}" , D.getFilename (), D.getLineNo (),
831- D.getColumnNo (), D.getMessage ());
832- if (Reportable)
833- Reportable->push_back (toDiag (D, Diag::ClangdConfig));
834- break ;
835- case llvm::SourceMgr::DK_Warning:
836- log (" config warning at {0}:{1}:{2}: {3}" , D.getFilename (), D.getLineNo (),
837- D.getColumnNo (), D.getMessage ());
838- if (Reportable)
839- Reportable->push_back (toDiag (D, Diag::ClangdConfig));
840- break ;
841- case llvm::SourceMgr::DK_Note:
842- case llvm::SourceMgr::DK_Remark:
843- vlog (" config note at {0}:{1}:{2}: {3}" , D.getFilename (), D.getLineNo (),
844- D.getColumnNo (), D.getMessage ());
845- break ;
846- }
847- };
848- Config C = ConfigProvider->getConfig (Params, ConfigDiagnosticHandler);
849- // Blindly publish diagnostics for the (unopened) parsed config files.
850- // We must avoid reporting diagnostics for *the same file* concurrently.
851- // Source file diags are published elsewhere, but those are different files.
852- if (!ReportableDiagnostics.empty ()) {
853- std::lock_guard<std::mutex> Lock (ConfigDiagnosticsMu);
854- for (auto &Entry : ReportableDiagnostics)
855- ServerCallbacks->onDiagnosticsReady (Entry.first (), /* Version=*/ " " ,
856- std::move (Entry.second ));
857- }
858- return Context::current ().derive (Config::Key, std::move (C));
859- }
860-
861873LLVM_NODISCARD bool
862874ClangdServer::blockUntilIdleForTest (llvm::Optional<double > TimeoutSeconds) {
863875 return WorkScheduler.blockUntilIdle (timeoutSeconds (TimeoutSeconds)) &&
0 commit comments