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,8 @@ // 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,10 @@ 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) = 0; }; /// When set, used by ClangdServer to get clang-tidy options for each particular @@ -131,6 +136,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,6 +157,9 @@ /// \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 DiagConsumer. ClangdServer(const GlobalCompilationDatabase &CDB, const FileSystemProvider &FSProvider, DiagnosticsConsumer &DiagConsumer, const Options &Opts); @@ -304,6 +316,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,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 SemanticHighlightingEnabled) + : FIndex(FIndex), DiagConsumer(DiagConsumer), + SemanticHighlightingEnabled(SemanticHighlightingEnabled) {} 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 (SemanticHighlightingEnabled) + DiagConsumer.onHighlightingsReady(Path, getSemanticHighlightings(AST)); } void onDiagnostics(PathRef File, std::vector Diags) override { @@ -74,6 +78,7 @@ private: FileIndex *FIndex; DiagnosticsConsumer &DiagConsumer; + bool SemanticHighlightingEnabled; }; } // namespace @@ -82,6 +87,7 @@ Opts.UpdateDebounce = std::chrono::steady_clock::duration::zero(); // Faster! Opts.StorePreamblesInMemory = true; Opts.AsyncThreadsCount = 4; // Consistent! + Opts.SemanticHighlightingEnabled = true; return Opts; } @@ -96,16 +102,18 @@ 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 // 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.SemanticHighlightingEnabled), + 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/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.cpp"}}, + {HighlightingKind::Function, {"entity.name.function.cpp"}}}; +} + } // namespace clangd } // namespace clang 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 @@ -73,6 +73,10 @@ return HadErrorInLastDiags; } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + private: std::mutex Mutex; bool HadErrorInLastDiags = false; @@ -101,6 +105,10 @@ return Result; } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} + void clear() { std::lock_guard Lock(Mutex); LastDiagsHadError.clear(); @@ -279,6 +287,9 @@ std::vector Diagnostics) override { Got = Context::current().getExisting(Secret); } + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} int Got; } DiagConsumer; MockCompilationDatabase CDB; @@ -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); @@ -845,6 +860,7 @@ class NoConcurrentAccessDiagConsumer : public DiagnosticsConsumer { public: std::atomic Count = {0}; + std::atomic HighlightingCount = {0}; NoConcurrentAccessDiagConsumer(std::promise StartSecondReparse) : StartSecondReparse(std::move(StartSecondReparse)) {} @@ -865,6 +881,11 @@ } } + virtual void onHighlightingsReady( + PathRef File, std::vector Highlightings) override { + ++HighlightingCount; + } + private: std::mutex Mutex; bool FirstRequest = true; @@ -900,6 +921,7 @@ Server.addDocument(FooCpp, SourceContentsWithoutErrors); ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for diagnostics"; ASSERT_EQ(DiagConsumer.Count, 2); // Sanity check - we actually ran both? + ASSERT_EQ(DiagConsumer.HighlightingCount, 2); } TEST_F(ClangdVFSTest, FormatCode) { 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 @@ -45,6 +45,9 @@ class IgnoreDiagnostics : public DiagnosticsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; // GMock helpers for matching completion items. 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 @@ -29,6 +29,9 @@ class IgnoreDiagnostics : public DiagnosticsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; // GMock helpers for matching SymbolInfos items. 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 @@ -677,6 +677,9 @@ AllStatus.push_back(Status); } + virtual void onHighlightingsReady( + PathRef File, std::vector Highlightings) override {} + std::vector allStatus() { std::lock_guard Lock(Mutex); return AllStatus; 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 @@ -36,6 +36,10 @@ class IgnoreDiagnostics : public DiagnosticsConsumer { void onDiagnosticsReady(PathRef File, std::vector Diagnostics) override {} + + virtual void + onHighlightingsReady(PathRef File, + std::vector Highlightings) override {} }; MATCHER_P2(FileRange, File, Range, "") {