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" @@ -55,6 +56,9 @@ // Implement DiagnosticsConsumer. void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override; void onFileUpdated(PathRef File, const TUStatus &Status) override; + void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override; // LSP methods. Notifications have signature void(const Params&). // Calls have signature void(const Params&, 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 @@ -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" @@ -49,6 +50,11 @@ std::vector Diagnostics) = 0; /// Called whenever the file status is updated. virtual void onFileUpdated(PathRef File, const TUStatus &Status){}; + + /// Called by ClangdServer when some \p Highlightings for \p File are ready. + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) {} }; /// When set, used by ClangdServer to get clang-tidy options for each particular @@ -131,6 +137,9 @@ /// Clangd will execute compiler drivers matching one of these globs to /// fetch system include path. std::vector QueryDriverGlobs; + + /// Enable semantic highlighting features. + bool SemanticHighlighting = false; }; // Sensible default options for use in tests. // Features like indexing must be enabled if desired. @@ -304,7 +313,7 @@ // can be caused by missing includes (e.g. member access in incomplete type). bool SuggestMissingIncludes = false; bool EnableHiddenFeatures = false; - + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> CachedCompletionFuzzyFindRequestByFile; 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,10 @@ // 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, + bool SemanticHighlighting) + : FIndex(FIndex), DiagConsumer(DiagConsumer), + SemanticHighlighting(SemanticHighlighting) {} void onPreambleAST(PathRef Path, ASTContext &Ctx, std::shared_ptr PP, @@ -61,6 +63,8 @@ void onMainAST(PathRef Path, ParsedAST &AST) override { if (FIndex) FIndex->updateMain(Path, AST); + if (SemanticHighlighting) + DiagConsumer.onHighlightingsReady(Path, getSemanticHighlightings(AST)); } void onDiagnostics(PathRef File, std::vector Diags) override { @@ -74,6 +78,7 @@ private: FileIndex *FIndex; DiagnosticsConsumer &DiagConsumer; + bool SemanticHighlighting; }; } // namespace @@ -82,6 +87,7 @@ Opts.UpdateDebounce = std::chrono::steady_clock::duration::zero(); // Faster! Opts.StorePreamblesInMemory = true; Opts.AsyncThreadsCount = 4; // Consistent! + Opts.SemanticHighlighting = true; return Opts; } @@ -102,10 +108,11 @@ // is parsed. // 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), - Opts.UpdateDebounce, Opts.RetentionPolicy) { + WorkScheduler( + CDB, Opts.AsyncThreadsCount, Opts.StorePreamblesInMemory, + llvm::make_unique( + DynamicIdx.get(), DiagConsumer, Opts.SemanticHighlighting), + Opts.UpdateDebounce, Opts.RetentionPolicy) { // Adds an index to the stack, at higher priority than existing indexes. auto AddIndex = [&](SymbolIndex *Idx) { if (this->Index != nullptr) { 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 @@ -841,6 +841,56 @@ EXPECT_FALSE(PathResult.hasValue()); } +TEST(ClangdSemanticHighlightingTest, GeneratesHighlightsWhenFileChange) { + class HighlightingsCounterDiagConsumer : public DiagnosticsConsumer { + public: + std::atomic Count = {0}; + + HighlightingsCounterDiagConsumer(std::promise StartSecondReparse) + : StartSecondReparse(std::move(StartSecondReparse)) {} + + void onDiagnosticsReady(PathRef, std::vector) override {} + + virtual void onHighlightingsReady( + PathRef File, std::vector Highlightings) override { + ++Count; + if (FirstRequest) { + FirstRequest = false; + StartSecondReparse.set_value(); + } + } + + private: + std::mutex Mutex; + bool FirstRequest = true; + std::promise StartSecondReparse; + }; + + const auto SourceContents1 = R"cpp( +int a; +)cpp"; + + const auto SourceContents2 = R"cpp( +int a = ; +)cpp"; + + auto FooCpp = testPath("foo.cpp"); + MockFSProvider FS; + FS.Files[FooCpp] = ""; + + std::promise StartSecondPromise; + std::future StartSecond = StartSecondPromise.get_future(); + MockCompilationDatabase MCD; + HighlightingsCounterDiagConsumer DiagConsumer(std::move(StartSecondPromise)); + ClangdServer Server(MCD, FS, DiagConsumer, + ClangdServer::optsForTest()); + Server.addDocument(FooCpp, SourceContents1); + StartSecond.wait(); + Server.addDocument(FooCpp, SourceContents2); + ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for server"; + ASSERT_EQ(DiagConsumer.Count, 2); +} + TEST_F(ClangdThreadingTest, NoConcurrentDiagnostics) { class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer { public: