diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -16,6 +16,7 @@ #include "GlobalCompilationDatabase.h" #include "Path.h" #include "Protocol.h" +#include "SemanticHighlighting.h" #include "Transport.h" #include "clang/Tooling/Core/Replacement.h" #include "llvm/ADT/Optional.h" @@ -31,7 +32,7 @@ /// MessageHandler binds the implemented LSP methods (e.g. onInitialize) to /// corresponding JSON-RPC methods ("initialize"). /// The server also supports $/cancelRequest (MessageHandler provides this). -class ClangdLSPServer : private DiagnosticsConsumer { +class ClangdLSPServer : private DiagnosticsConsumer, private HighlightingsConsumer { public: /// If \p CompileCommandsDir has a value, compile_commands.json will be /// loaded only from \p CompileCommandsDir. Otherwise, clangd will look @@ -56,6 +57,10 @@ void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override; void onFileUpdated(PathRef File, const TUStatus &Status) override; + // Implement HighlightingsConsumer + void onHighlightingsReady(PathRef File, + std::vector Highlightings) override; + // LSP methods. Notifications have signature void(const Params&). // Calls have signature void(const Params&, Callback). void onInitialize(const InitializeParams &, Callback); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -347,7 +347,7 @@ CDB.emplace(BaseCDB.get(), Params.initializationOptions.fallbackFlags, ClangdServerOpts.ResourceDir); Server.emplace(*CDB, FSProvider, static_cast(*this), - ClangdServerOpts); + static_cast(*this), ClangdServerOpts); applyConfiguration(Params.initializationOptions.ConfigSettings); CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets; @@ -1063,6 +1063,11 @@ return true; } +void ClangdLSPServer::onHighlightingsReady(PathRef File, + std::vector Highlightings) { + +} + void ClangdLSPServer::onDiagnosticsReady(PathRef File, std::vector Diagnostics) { auto URI = URIForFile::canonicalize(File, /*TUPath=*/File); diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -18,6 +18,7 @@ #include "Function.h" #include "GlobalCompilationDatabase.h" #include "Protocol.h" +#include "SemanticHighlighting.h" #include "TUScheduler.h" #include "XRefs.h" #include "index/Background.h" @@ -51,6 +52,15 @@ virtual void onFileUpdated(PathRef File, const TUStatus &Status){}; }; +class HighlightingsConsumer { +public: + virtual ~HighlightingsConsumer() = default; + + // Called by ClangdServer when some \p Highlightings for \p File are ready. + virtual void onHighlightingsReady(PathRef File, + std::vector Highlightings) = 0; +}; + /// When set, used by ClangdServer to get clang-tidy options for each particular /// file. Must be thread-safe. We use this instead of ClangTidyOptionsProvider /// to allow reading tidy configs from the VFS used for parsing. @@ -131,6 +141,10 @@ /// Clangd will execute compiler drivers matching one of these globs to /// fetch system include path. std::vector QueryDriverGlobs; + + // If true Clangd will generate semantic highlightings for the current + // document when it changes. + bool SemanticHighlightingEnabled = false; }; // Sensible default options for use in tests. // Features like indexing must be enabled if desired. @@ -148,9 +162,13 @@ /// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a /// worker thread. Therefore, instances of \p DiagConsumer must properly /// synchronize access to shared state. + /// If semantic highlighting is enabled ClangdServer also generates semantic + /// highlightings for the file after each parsing request. When highlightings + /// are done generating they are sent to \p HConsumer ClangdServer(const GlobalCompilationDatabase &CDB, const FileSystemProvider &FSProvider, - DiagnosticsConsumer &DiagConsumer, const Options &Opts); + DiagnosticsConsumer &DiagConsumer, + HighlightingsConsumer &HConsumer, const Options &Opts); /// Add a \p File to the list of tracked C++ files or update the contents if /// \p File is already tracked. Also schedules parsing of the AST for it on a @@ -304,6 +322,9 @@ // can be caused by missing includes (e.g. member access in incomplete type). bool SuggestMissingIncludes = false; bool EnableHiddenFeatures = false; + + // If this is true semantic highlighting will be generated. + bool SemanticHighlightingEnabled = false; // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -48,8 +48,9 @@ // Update the FileIndex with new ASTs and plumb the diagnostics responses. struct UpdateIndexCallbacks : public ParsingCallbacks { - UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer) - : FIndex(FIndex), DiagConsumer(DiagConsumer) {} + UpdateIndexCallbacks(FileIndex *FIndex, DiagnosticsConsumer &DiagConsumer, + HighlightingsConsumer &HConsumer) + : FIndex(FIndex), DiagConsumer(DiagConsumer), HConsumer(HConsumer) {} void onPreambleAST(PathRef Path, ASTContext &Ctx, std::shared_ptr PP, @@ -71,9 +72,15 @@ DiagConsumer.onFileUpdated(File, Status); } + virtual void onHighlightings(PathRef File, + std::vector Highlightings) override { + HConsumer.onHighlightingsReady(File, Highlightings); + } + private: FileIndex *FIndex; DiagnosticsConsumer &DiagConsumer; + HighlightingsConsumer &HConsumer; }; } // namespace @@ -88,7 +95,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB, const FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, - const Options &Opts) + HighlightingsConsumer &HConsumer, const Options &Opts) : FSProvider(FSProvider), DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex) @@ -96,6 +103,7 @@ GetClangTidyOptions(Opts.GetClangTidyOptions), SuggestMissingIncludes(Opts.SuggestMissingIncludes), EnableHiddenFeatures(Opts.HiddenFeatures), + SemanticHighlightingEnabled(Opts.SemanticHighlightingEnabled), WorkspaceRoot(Opts.WorkspaceRoot), // Pass a callback into `WorkScheduler` to extract symbols from a newly // parsed file and rebuild the file index synchronously each time an AST @@ -103,8 +111,8 @@ // FIXME(ioeric): this can be slow and we may be able to index on less // critical paths. WorkScheduler(CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, - llvm::make_unique(DynamicIdx.get(), - DiagConsumer), + llvm::make_unique( + DynamicIdx.get(), DiagConsumer, HConsumer), Opts.UpdateDebounce, Opts.RetentionPolicy) { // Adds an index to the stack, at higher priority than existing indexes. auto AddIndex = [&](SymbolIndex *Idx) { @@ -145,7 +153,7 @@ Inputs.Contents = Contents; Inputs.Opts = std::move(Opts); Inputs.Index = Index; - WorkScheduler.update(File, Inputs, WantDiags); + WorkScheduler.update(File, Inputs, WantDiags, SemanticHighlightingEnabled); } void ClangdServer::removeDocument(PathRef File) { WorkScheduler.remove(File); } diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h --- a/clang-tools-extra/clangd/SemanticHighlighting.h +++ b/clang-tools-extra/clangd/SemanticHighlighting.h @@ -30,6 +30,10 @@ // Returns all HighlightingTokens from an AST. Only generates highlights for the // main AST. std::vector getSemanticHighlightings(ParsedAST &AST); +// Returns a map where all HighlightingKinds are mapped to a vector of TextMate +// scopes. +std::map> +getSemanticHighlightingScopes(); } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -74,5 +74,11 @@ return HighlightingTokenCollector(AST).collectTokens(); } +std::map> +getSemanticHighlightingScopes() { + return {{HighlightingKind::Variable, {"variable"}}, + {HighlightingKind::Function, {"entity.name.function"}}}; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -12,6 +12,7 @@ #include "ClangdUnit.h" #include "Function.h" #include "GlobalCompilationDatabase.h" +#include "SemanticHighlighting.h" #include "Threading.h" #include "index/CanonicalIncludes.h" #include "llvm/ADT/Optional.h" @@ -115,6 +116,10 @@ /// Called whenever the TU status is updated. virtual void onFileUpdated(PathRef File, const TUStatus &Status) {} + + // Called whenever some highlightings for \p File are updated + virtual void onHighlightings(PathRef File, + std::vector Highlightings) {} }; /// Handles running tasks for ClangdServer and managing the resources (e.g., @@ -148,7 +153,8 @@ /// If diagnostics are requested (Yes), and the context is cancelled /// before they are prepared, they may be skipped if eventual-consistency /// permits it (i.e. WantDiagnostics is downgraded to Auto). - void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD); + void update(PathRef File, ParseInputs Inputs, WantDiagnostics WD, + bool WantHighlightings); /// Remove \p File from the list of tracked files and schedule removal of its /// resources. Pending diagnostics for closed files may not be delivered, even diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -175,7 +175,7 @@ bool StorePreamblesInMemory, ParsingCallbacks &Callbacks); ~ASTWorker(); - void update(ParseInputs Inputs, WantDiagnostics); + void update(ParseInputs Inputs, WantDiagnostics, bool WantHighlightings); void runWithAST(llvm::StringRef Name, llvm::unique_function)> Action); @@ -361,7 +361,8 @@ #endif } -void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) { +void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags, + bool WantHighlightings) { llvm::StringRef TaskName = "Update"; auto Task = [=]() mutable { // Get the actual command as `Inputs` does not have a command. @@ -423,6 +424,7 @@ std::lock_guard Lock(Mutex); LastBuiltPreamble = NewPreamble; } + // Before doing the expensive AST reparse, we want to release our reference // to the old preamble, so it can be freed if there are no other references // to it. @@ -452,19 +454,21 @@ } } - // We only need to build the AST if diagnostics were requested. - if (WantDiags == WantDiagnostics::No) + // We only need to build the AST if diagnostics were requested or if new + // syntax highlights should be generated. + if (WantDiags == WantDiagnostics::No && !WantHighlightings) return; { std::lock_guard Lock(DiagsMu); - // No need to rebuild the AST if we won't send the diagnotics. However, - // note that we don't prevent preamble rebuilds. - if (!ReportDiagnostics) + // No need to rebuild the AST if we won't send the diagnotics or generate + // syntax highlights. However, note that we don't prevent preamble + // rebuilds. + if (!ReportDiagnostics && !WantHighlightings) return; } - // Get the AST for diagnostics. + // Get the AST. llvm::Optional> AST = IdleASTs.take(this); if (!AST) { llvm::Optional NewAST = @@ -481,19 +485,26 @@ Details.ReuseAST = true; emitTUStatus({TUAction::BuildingFile, TaskName}, &Details); } + // We want to report the diagnostics even if this update was cancelled. // It seems more useful than making the clients wait indefinitely if they // spam us with updates. // Note *AST can still be null if buildAST fails. if (*AST) { - { + if(ReportDiagnostics) { std::lock_guard Lock(DiagsMu); if (ReportDiagnostics) Callbacks.onDiagnostics(FileName, (*AST)->getDiagnostics()); + DiagsWereReported = true; } + + if (WantHighlightings) { + std::vector Highlightings = getSemanticHighlightings(**AST); + Callbacks.onHighlightings(FileName, Highlightings); + } + trace::Span Span("Running main AST callback"); Callbacks.onMainAST(FileName, **AST); - DiagsWereReported = true; } // Stash the AST in the cache for further use. IdleASTs.put(this, std::move(*AST)); @@ -861,7 +872,7 @@ } void TUScheduler::update(PathRef File, ParseInputs Inputs, - WantDiagnostics WantDiags) { + WantDiagnostics WantDiags, bool WantHighlightings) { std::unique_ptr &FD = Files[File]; if (!FD) { // Create a new worker to process the AST-related tasks. @@ -874,7 +885,7 @@ } else { FD->Contents = Inputs.Contents; } - FD->Worker->update(std::move(Inputs), WantDiags); + FD->Worker->update(std::move(Inputs), WantDiags, WantHighlightings); } void TUScheduler::remove(PathRef File) { diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -59,7 +59,7 @@ return false; } -class ErrorCheckingDiagConsumer : public DiagnosticsConsumer { +class ErrorCheckingDiagConsumer : public DiagnosticsConsumer, public HighlightingsConsumer { public: void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override { @@ -73,6 +73,10 @@ return HadErrorInLastDiags; } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + private: std::mutex Mutex; bool HadErrorInLastDiags = false; @@ -80,7 +84,7 @@ /// For each file, record whether the last published diagnostics contained at /// least one error. -class MultipleErrorCheckingDiagConsumer : public DiagnosticsConsumer { +class MultipleErrorCheckingDiagConsumer : public DiagnosticsConsumer, public HighlightingsConsumer { public: void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override { @@ -101,6 +105,10 @@ return Result; } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + void clear() { std::lock_guard Lock(Mutex); LastDiagsHadError.clear(); @@ -144,7 +152,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); for (const auto &FileWithContents : ExtraFiles) FS.Files[testPath(FileWithContents.first)] = FileWithContents.second; @@ -196,7 +204,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); const auto SourceContents = R"cpp( #include "foo.h" @@ -231,7 +239,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); const auto SourceContents = R"cpp( #include "foo.h" @@ -274,17 +282,20 @@ } mutable int Got; } FS; - struct DiagConsumer : public DiagnosticsConsumer { + struct DiagConsumer : public DiagnosticsConsumer, public HighlightingsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override { Got = Context::current().getExisting(Secret); } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} int Got; } DiagConsumer; MockCompilationDatabase CDB; // Verify that the context is plumbed to the FS provider and diagnostics. - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); { WithContextValue Entrypoint(Secret, 42); Server.addDocument(testPath("foo.cpp"), "void main(){}"); @@ -305,7 +316,7 @@ {"-xc++", "-target", "x86_64-linux-unknown", "-m64", "--gcc-toolchain=/randomusr", "-stdlib=libstdc++"}); - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); // Just a random gcc version string SmallString<8> Version("4.9.3"); @@ -350,7 +361,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); const auto SourceContents1 = R"cpp( @@ -386,7 +397,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); const auto SourceContents = R"cpp( @@ -438,7 +449,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; MultipleErrorCheckingDiagConsumer DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); auto BarCpp = testPath("bar.cpp"); @@ -482,7 +493,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); Path FooCpp = testPath("foo.cpp"); const auto SourceContents = R"cpp( @@ -518,7 +529,7 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); // clang cannot create CompilerInvocation if we pass two files in the @@ -589,7 +600,7 @@ bool HadErrorsInLastDiags = false; }; - class TestDiagConsumer : public DiagnosticsConsumer { + class TestDiagConsumer : public DiagnosticsConsumer, public HighlightingsConsumer { public: TestDiagConsumer() : Stats(FilesCount, FileStat()) {} @@ -610,6 +621,10 @@ Stats[FileIndex].HadErrorsInLastDiags = HadError; } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + std::vector takeFileStats() { std::lock_guard Lock(Mutex); return std::move(Stats); @@ -635,7 +650,7 @@ TestDiagConsumer DiagConsumer; { MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); // Prepare some random distributions for the test. std::random_device RandGen; @@ -769,7 +784,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto SourceContents = R"cpp( #include "foo.h" @@ -842,7 +857,7 @@ } TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) { - class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer { + class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer, public HighlightingsConsumer { public: std::atomic Count = {0}; @@ -864,6 +879,10 @@ std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + private: std::mutex Mutex; @@ -894,7 +913,7 @@ NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise)); MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); Server.addDocument(FooCpp, SourceContentsWithErrors); StartSecond.wait(); Server.addDocument(FooCpp, SourceContentsWithoutErrors); @@ -906,7 +925,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto Path = testPath("foo.cpp"); std::string Code = R"cpp( @@ -935,7 +954,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto SourcePath = testPath("source/foo.cpp"); auto HeaderPath = testPath("headers/foo.h"); @@ -1010,7 +1029,7 @@ ListenStatsFSProvider FS(CountStats); ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto SourcePath = testPath("foo.cpp"); auto HeaderPath = testPath("foo.h"); @@ -1046,7 +1065,7 @@ "random-plugin", }; OverlayCDB OCDB(&CDB); - ClangdServer Server(OCDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(OCDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); const auto SourceContents = "int main() { return 0; }"; @@ -1061,7 +1080,7 @@ MockFSProvider FS; ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); Annotations Code(R"cpp( @@ -1131,7 +1150,7 @@ Notification CanReturnCommand; DelayedCompilationDatabase CDB(CanReturnCommand); - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); Annotations Code(R"cpp( diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -13,6 +13,7 @@ #include "Matchers.h" #include "Protocol.h" #include "Quality.h" +#include "SemanticHighlighting.h" #include "SourceCode.h" #include "SyncAPI.h" #include "TestFS.h" @@ -42,9 +43,12 @@ using ::testing::Not; using ::testing::UnorderedElementsAre; -class IgnoreDiagnostics : public DiagnosticsConsumer { +class IgnoreDiagnostics : public DiagnosticsConsumer, public HighlightingsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; // GMock helpers for matching completion items. @@ -139,7 +143,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); return completions(Server, Text, std::move(IndexSymbols), std::move(Opts), FilePath); } @@ -624,7 +628,7 @@ FS.Files[BarHeader] = ""; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto BarURI = URI::create(BarHeader).toString(); Symbol Sym = cls("ns::X"); Sym.CanonicalDeclaration.FileURI = BarURI.c_str(); @@ -663,7 +667,7 @@ MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); Symbol SymX = cls("ns::X"); Symbol SymY = cls("ns::Y"); std::string BarHeader = testPath("bar.h"); @@ -691,7 +695,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); FS.Files[testPath("bar.h")] = R"cpp(namespace ns { struct preamble { int member; }; })cpp"; @@ -743,7 +747,7 @@ IgnoreDiagnostics DiagConsumer; ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; - ClangdServer Server(CDB, FS, DiagConsumer, Opts); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, Opts); FS.Files[testPath("foo_header.h")] = R"cpp( #pragma once @@ -776,7 +780,7 @@ IgnoreDiagnostics DiagConsumer; auto Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; - ClangdServer Server(CDB, FS, DiagConsumer, Opts); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, Opts); FS.Files[testPath("foo.h")] = R"cpp( namespace ns { class XYZ {}; void foo(int x) {} } @@ -955,7 +959,7 @@ ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.StaticIndex = Index.get(); - ClangdServer Server(CDB, FS, DiagConsumer, Opts); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, Opts); auto File = testPath("foo.cpp"); runAddDocument(Server, File, Text); return llvm::cantFail(runSignatureHelp(Server, File, Point)); @@ -1394,7 +1398,7 @@ MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); Annotations Source(R"cpp( #include "foo.h" @@ -1429,7 +1433,7 @@ MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); Annotations Source(R"cpp( // We ignore namespace comments, for rationale see CodeCompletionStrings.h. @@ -1497,7 +1501,7 @@ MockFSProvider FS; FS.Files[FooCpp] = "// empty file"; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); // Run completion outside the file range. Position Pos; Pos.line = 100; @@ -1618,7 +1622,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); CodeCompleteOptions Opts; Opts.IncludeFixIts = true; @@ -1658,7 +1662,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); CodeCompleteOptions Opts; Opts.IncludeFixIts = true; @@ -1738,7 +1742,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); constexpr const char *TestCodes[] = { R"cpp( @@ -1895,7 +1899,7 @@ IgnoreDiagnostics DiagConsumer; ClangdServer::Options Opts = ClangdServer::optsForTest(); Opts.BuildDynamicSymbolIndex = true; - ClangdServer Server(CDB, FS, DiagConsumer, Opts); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, Opts); FS.Files[testPath("foo.h")] = R"cpp( struct Foo { @@ -2065,7 +2069,7 @@ MockFSProvider FS; MockCompilationDatabase CDB; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto File = testPath("foo.cpp"); Annotations Test(R"cpp( @@ -2127,7 +2131,7 @@ FS.Files[FooHeader] = ""; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); std::string DeclFile = URI::create(testPath("foo")).toString(); Symbol sym = func("Func"); @@ -2158,7 +2162,7 @@ std::string FooHeader = testPath("foo.h"); FS.Files[FooHeader] = "#define CLANGD_PREAMBLE_HEADER x\n"; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto Results = completions( R"cpp(#include "foo.h" #define CLANGD_PREAMBLE_MAIN x @@ -2279,7 +2283,7 @@ std::string BarHeader = testPath("sub/bar.h"); FS.Files[BarHeader] = ""; IgnoreDiagnostics DiagConsumer; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto Results = completions(Server, R"cpp( #include "^" diff --git a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp --- a/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp @@ -26,9 +26,12 @@ using ::testing::IsEmpty; using ::testing::UnorderedElementsAre; -class IgnoreDiagnostics : public DiagnosticsConsumer { +class IgnoreDiagnostics : public DiagnosticsConsumer, public HighlightingsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; // GMock helpers for matching SymbolInfos items. @@ -58,7 +61,7 @@ class WorkspaceSymbolsTest : public ::testing::Test { public: WorkspaceSymbolsTest() - : Server(CDB, FSProvider, DiagConsumer, optsForTests()) { + : Server(CDB, FSProvider, DiagConsumer, DiagConsumer, optsForTests()) { // Make sure the test root directory is created. FSProvider.Files[testPath("unused")] = ""; CDB.ExtraClangFlags = {"-xc++"}; @@ -321,7 +324,7 @@ class DocumentSymbolsTest : public ::testing::Test { public: DocumentSymbolsTest() - : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {} + : Server(CDB, FSProvider, DiagConsumer, DiagConsumer, optsForTests()) {} protected: MockFSProvider FSProvider; diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Annotations.h" +#include "ClangdServer.h" #include "Context.h" #include "Matchers.h" #include "TUScheduler.h" @@ -44,9 +45,10 @@ void updateWithCallback(TUScheduler &S, PathRef File, llvm::StringRef Contents, WantDiagnostics WD, + bool WantHighlight, llvm::unique_function CB) { WithContextValue Ctx(llvm::make_scope_exit(std::move(CB))); - S.update(File, getInputs(File, Contents), WD); + S.update(File, getInputs(File, Contents), WD, WantHighlight); } static Key)>> @@ -71,7 +73,7 @@ /// any. The TUScheduler should be created with captureDiags as a /// DiagsCallback for this to work. void updateWithDiags(TUScheduler &S, PathRef File, ParseInputs Inputs, - WantDiagnostics WD, + WantDiagnostics WD, bool WantHighlight, llvm::unique_function)> CB) { Path OrigFile = File.str(); WithContextValue Ctx( @@ -82,13 +84,13 @@ CB(std::move(Diags)); }, std::move(CB))); - S.update(File, std::move(Inputs), WD); + S.update(File, std::move(Inputs), WD, WantHighlight); } void updateWithDiags(TUScheduler &S, PathRef File, llvm::StringRef Contents, - WantDiagnostics WD, + WantDiagnostics WD, bool WantHighlight, llvm::unique_function)> CB) { - return updateWithDiags(S, File, getInputs(File, Contents), WD, + return updateWithDiags(S, File, getInputs(File, Contents), WD, WantHighlight, std::move(CB)); } @@ -113,7 +115,7 @@ Files[Missing] = ""; EXPECT_EQ(S.getContents(Added), ""); - S.update(Added, getInputs(Added, "x"), WantDiagnostics::No); + S.update(Added, getInputs(Added, "x"), WantDiagnostics::No, false); EXPECT_EQ(S.getContents(Added), "x"); // Assert each operation for missing file is an error (even if it's available @@ -161,20 +163,20 @@ /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero(), ASTRetentionPolicy()); auto Path = testPath("foo.cpp"); - updateWithDiags(S, Path, "", WantDiagnostics::Yes, + updateWithDiags(S, Path, "", WantDiagnostics::Yes, false, [&](std::vector) { Ready.wait(); }); - updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes, + updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes, false, [&](std::vector) { ++CallbackCount; }); - updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto, + updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto, false, [&](std::vector) { ADD_FAILURE() << "auto should have been cancelled by auto"; }); - updateWithDiags(S, Path, "request no diags", WantDiagnostics::No, + updateWithDiags(S, Path, "request no diags", WantDiagnostics::No, false, [&](std::vector) { ADD_FAILURE() << "no diags should not be called back"; }); - updateWithDiags(S, Path, "auto (produces)", WantDiagnostics::Auto, + updateWithDiags(S, Path, "auto (produces)", WantDiagnostics::Auto, false, [&](std::vector) { ++CallbackCount; }); Ready.notify(); @@ -192,16 +194,16 @@ ASTRetentionPolicy()); // FIXME: we could probably use timeouts lower than 1 second here. auto Path = testPath("foo.cpp"); - updateWithDiags(S, Path, "auto (debounced)", WantDiagnostics::Auto, + updateWithDiags(S, Path, "auto (debounced)", WantDiagnostics::Auto, false, [&](std::vector) { ADD_FAILURE() << "auto should have been debounced and canceled"; }); std::this_thread::sleep_for(std::chrono::milliseconds(200)); - updateWithDiags(S, Path, "auto (timed out)", WantDiagnostics::Auto, + updateWithDiags(S, Path, "auto (timed out)", WantDiagnostics::Auto, false, [&](std::vector) { ++CallbackCount; }); std::this_thread::sleep_for(std::chrono::seconds(2)); - updateWithDiags(S, Path, "auto (shut down)", WantDiagnostics::Auto, + updateWithDiags(S, Path, "auto (shut down)", WantDiagnostics::Auto, false, [&](std::vector) { ++CallbackCount; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); @@ -230,7 +232,7 @@ // Schedule two updates (A, B) and two preamble reads (stale, consistent). // The stale read should see A, and the consistent read should see B. // (We recognize the preambles by their included files). - updateWithCallback(S, Path, "#include ", WantDiagnostics::Yes, [&]() { + updateWithCallback(S, Path, "#include ", WantDiagnostics::Yes, false, [&]() { // This callback runs in between the two preamble updates. // This blocks update B, preventing it from winning the race @@ -243,7 +245,8 @@ // If the second read was stale, it would usually see A. std::this_thread::sleep_for(std::chrono::milliseconds(100)); }); - S.update(Path, getInputs(Path, "#include "), WantDiagnostics::Yes); + S.update(Path, getInputs(Path, "#include "), + WantDiagnostics::Yes, false); S.runWithPreamble("StaleRead", Path, TUScheduler::Stale, [&](Expected Pre) { @@ -289,7 +292,7 @@ auto T = cancelableTask(); WithContext C(std::move(T.first)); updateWithDiags( - S, Path, "//" + ID, WantDiagnostics::Yes, + S, Path, "//" + ID, WantDiagnostics::Yes, false, [&, ID](std::vector Diags) { DiagsSeen.push_back(ID); }); return std::move(T.second); }; @@ -313,7 +316,7 @@ return std::move(T.second); }; - updateWithCallback(S, Path, "", WantDiagnostics::Yes, + updateWithCallback(S, Path, "", WantDiagnostics::Yes, false, [&]() { Proceed.wait(); }); // The second parens indicate cancellation, where present. Update("U1")(); @@ -381,7 +384,7 @@ { WithContextValue WithNonce(NonceKey, ++Nonce); updateWithDiags( - S, File, Inputs, WantDiagnostics::Auto, + S, File, Inputs, WantDiagnostics::Auto, false, [File, Nonce, &Mut, &TotalUpdates](std::vector) { EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce)); @@ -460,16 +463,16 @@ // Build one file in advance. We will not access it later, so it will be the // one that the cache will evict. - updateWithCallback(S, Foo, SourceContents, WantDiagnostics::Yes, + updateWithCallback(S, Foo, SourceContents, WantDiagnostics::Yes, false, [&BuiltASTCounter]() { ++BuiltASTCounter; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 1); // Build two more files. Since we can retain only 2 ASTs, these should be the // ones we see in the cache later. - updateWithCallback(S, Bar, SourceContents, WantDiagnostics::Yes, + updateWithCallback(S, Bar, SourceContents, WantDiagnostics::Yes, false, [&BuiltASTCounter]() { ++BuiltASTCounter; }); - updateWithCallback(S, Baz, SourceContents, WantDiagnostics::Yes, + updateWithCallback(S, Baz, SourceContents, WantDiagnostics::Yes, false, [&BuiltASTCounter]() { ++BuiltASTCounter; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 3); @@ -478,7 +481,7 @@ ASSERT_THAT(S.getFilesWithCachedAST(), UnorderedElementsAre(Bar, Baz)); // Access the old file again. - updateWithCallback(S, Foo, OtherSourceContents, WantDiagnostics::Yes, + updateWithCallback(S, Foo, OtherSourceContents, WantDiagnostics::Yes, false, [&BuiltASTCounter]() { ++BuiltASTCounter; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_EQ(BuiltASTCounter.load(), 4); @@ -506,7 +509,7 @@ int main() {} )cpp"; auto WithEmptyPreamble = R"cpp(int main() {})cpp"; - S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto); + S.update(Foo, getInputs(Foo, WithPreamble), WantDiagnostics::Auto, false); S.runWithPreamble( "getNonEmptyPreamble", Foo, TUScheduler::Stale, [&](Expected Preamble) { @@ -519,7 +522,7 @@ ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); // Update the file which results in an empty preamble. - S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto); + S.update(Foo, getInputs(Foo, WithEmptyPreamble), WantDiagnostics::Auto, false); // Wait for the preamble is being built. ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); S.runWithPreamble( @@ -550,7 +553,7 @@ constexpr int ReadsToSchedule = 10; std::mutex PreamblesMut; std::vector Preambles(ReadsToSchedule, nullptr); - S.update(Foo, getInputs(Foo, NonEmptyPreamble), WantDiagnostics::Auto); + S.update(Foo, getInputs(Foo, NonEmptyPreamble), WantDiagnostics::Auto, false); for (int I = 0; I < ReadsToSchedule; ++I) { S.runWithPreamble( "test", Foo, TUScheduler::Stale, @@ -588,7 +591,7 @@ auto DoUpdate = [&](std::string Contents) -> bool { std::atomic Updated(false); Updated = false; - updateWithDiags(S, Source, Contents, WantDiagnostics::Yes, + updateWithDiags(S, Source, Contents, WantDiagnostics::Yes, false, [&Updated](std::vector) { Updated = true; }); bool UpdateFinished = S.blockUntilIdle(timeoutSeconds(10)); if (!UpdateFinished) @@ -630,7 +633,7 @@ auto Contents = "int a; int b;"; updateWithDiags( - S, FooCpp, Contents, WantDiagnostics::No, + S, FooCpp, Contents, WantDiagnostics::No, false, [](std::vector) { ADD_FAILURE() << "Should not be called."; }); S.runWithAST("touchAST", FooCpp, [](Expected IA) { // Make sure the AST was actually built. @@ -641,7 +644,7 @@ // Even though the inputs didn't change and AST can be reused, we need to // report the diagnostics, as they were not reported previously. std::atomic SeenDiags(false); - updateWithDiags(S, FooCpp, Contents, WantDiagnostics::Auto, + updateWithDiags(S, FooCpp, Contents, WantDiagnostics::Auto, false, [&](std::vector) { SeenDiags = true; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); ASSERT_TRUE(SeenDiags); @@ -649,7 +652,7 @@ // Subsequent request does not get any diagnostics callback because the same // diags have previously been reported and the inputs didn't change. updateWithDiags( - S, FooCpp, Contents, WantDiagnostics::Auto, + S, FooCpp, Contents, WantDiagnostics::Auto, false, [&](std::vector) { ADD_FAILURE() << "Should not be called."; }); ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); } @@ -667,7 +670,7 @@ } TEST_F(TUSchedulerTests, TUStatus) { - class CaptureTUStatus : public DiagnosticsConsumer { + class CaptureTUStatus : public DiagnosticsConsumer, public HighlightingsConsumer { public: void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} @@ -677,6 +680,10 @@ AllStatus.push_back(Status); } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + std::vector allStatus() { std::lock_guard Lock(Mutex); return AllStatus; @@ -688,7 +695,7 @@ } CaptureTUStatus; MockFSProvider FS; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, CaptureTUStatus, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, CaptureTUStatus, CaptureTUStatus, ClangdServer::optsForTest()); Annotations Code("int m^ain () {}"); // We schedule the following tasks in the queue: diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -10,6 +10,7 @@ #include "Compiler.h" #include "Matchers.h" #include "Protocol.h" +#include "SemanticHighlighting.h" #include "SyncAPI.h" #include "TestFS.h" #include "TestTU.h" @@ -33,9 +34,13 @@ using ::testing::Matcher; using ::testing::UnorderedElementsAreArray; -class IgnoreDiagnostics : public DiagnosticsConsumer { +class IgnoreDiagnostics : public DiagnosticsConsumer, public HighlightingsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; MATCHER_P2(FileRange, File, Range, "") { @@ -539,7 +544,7 @@ IgnoreDiagnostics DiagConsumer; MockFSProvider FS; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); // Fill the filesystem. auto FooCpp = testPath("src/foo.cpp"); @@ -1791,7 +1796,7 @@ MockFSProvider FS; IgnoreDiagnostics DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); const char *SourceContents = R"cpp( @@ -1866,7 +1871,7 @@ MockFSProvider FS; IgnoreDiagnostics DiagConsumer; MockCompilationDatabase CDB; - ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest()); + ClangdServer Server(CDB, FS, DiagConsumer, DiagConsumer, ClangdServer::optsForTest()); auto FooCpp = testPath("foo.cpp"); // The trigger locations must be the same.