jvikstrom created this revision.
jvikstrom added reviewers: hokein, ilya-biryukov.
Herald added subscribers: cfe-commits, kadircet, arphaman, jkorous, MaskRay, 
javed.absar.
Herald added a project: clang.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D63821

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdLSPServer.h
  clang-tools-extra/clangd/ClangdServer.cpp
  clang-tools-extra/clangd/ClangdServer.h
  clang-tools-extra/clangd/SemanticHighlighting.cpp
  clang-tools-extra/clangd/SemanticHighlighting.h
  clang-tools-extra/clangd/TUScheduler.cpp
  clang-tools-extra/clangd/TUScheduler.h
  clang-tools-extra/clangd/unittests/ClangdTests.cpp
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
  clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
  clang-tools-extra/clangd/unittests/XRefsTests.cpp

Index: clang-tools-extra/clangd/unittests/XRefsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/XRefsTests.cpp
+++ 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<Diag> Diagnostics) override {}
+
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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.
Index: clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp
+++ 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<void()> 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<llvm::unique_function<void(PathRef File, std::vector<Diag>)>>
@@ -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<void(std::vector<Diag>)> 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<void(std::vector<Diag>)> 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<Diag>) { Ready.wait(); });
-    updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes,
+    updateWithDiags(S, Path, "request diags", WantDiagnostics::Yes, false,
                     [&](std::vector<Diag>) { ++CallbackCount; });
-    updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto,
+    updateWithDiags(S, Path, "auto (clobbered)", WantDiagnostics::Auto, false,
                     [&](std::vector<Diag>) {
                       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<Diag>) {
                       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<Diag>) { ++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<Diag>) {
                       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<Diag>) { ++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<Diag>) { ++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 <A>", WantDiagnostics::Yes, [&]() {
+    updateWithCallback(S, Path, "#include <A>", 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 <B>"), WantDiagnostics::Yes);
+    S.update(Path, getInputs(Path, "#include <B>"),
+             WantDiagnostics::Yes, false);
 
     S.runWithPreamble("StaleRead", Path, TUScheduler::Stale,
                       [&](Expected<InputsAndPreamble> 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<Diag> 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<Diag>) {
                 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<InputsAndPreamble> 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<const void *> 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<bool> Updated(false);
     Updated = false;
-    updateWithDiags(S, Source, Contents, WantDiagnostics::Yes,
+    updateWithDiags(S, Source, Contents, WantDiagnostics::Yes, false,
                     [&Updated](std::vector<Diag>) { 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<Diag>) { ADD_FAILURE() << "Should not be called."; });
   S.runWithAST("touchAST", FooCpp, [](Expected<InputsAndAST> 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<bool> SeenDiags(false);
-  updateWithDiags(S, FooCpp, Contents, WantDiagnostics::Auto,
+  updateWithDiags(S, FooCpp, Contents, WantDiagnostics::Auto, false,
                   [&](std::vector<Diag>) { 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<Diag>) { 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<Diag> Diagnostics) override {}
@@ -677,6 +680,10 @@
       AllStatus.push_back(Status);
     }
 
+    virtual void
+    onHighlightingsReady(PathRef File,
+                      std::vector<HighlightingToken> Highlightings) override {}
+
     std::vector<TUStatus> allStatus() {
       std::lock_guard<std::mutex> 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:
Index: clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp
+++ 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<Diag> Diagnostics) override {}
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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;
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ 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<Diag> Diagnostics) override {}
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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 "^"
Index: clang-tools-extra/clangd/unittests/ClangdTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ 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<Diag> Diagnostics) override {
@@ -73,6 +73,10 @@
     return HadErrorInLastDiags;
   }
 
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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<Diag> Diagnostics) override {
@@ -101,6 +105,10 @@
     return Result;
   }
 
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> Highlightings) override {}
+
   void clear() {
     std::lock_guard<std::mutex> 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<Diag> Diagnostics) override {
       Got = Context::current().getExisting(Secret);
     }
+  virtual void
+  onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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<HighlightingToken> Highlightings) override {}
+
     std::vector<FileStat> takeFileStats() {
       std::lock_guard<std::mutex> 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<int> Count = {0};
 
@@ -864,6 +879,10 @@
         std::this_thread::sleep_for(std::chrono::milliseconds(50));
       }
     }
+    virtual void
+    onHighlightingsReady(PathRef File,
+                    std::vector<HighlightingToken> 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(
Index: clang-tools-extra/clangd/TUScheduler.h
===================================================================
--- clang-tools-extra/clangd/TUScheduler.h
+++ 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<HighlightingToken> 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
Index: clang-tools-extra/clangd/TUScheduler.cpp
===================================================================
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ 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<void(llvm::Expected<InputsAndAST>)> 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<std::mutex> 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<std::mutex> 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<std::unique_ptr<ParsedAST>> AST = IdleASTs.take(this);
     if (!AST) {
       llvm::Optional<ParsedAST> 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<std::mutex> Lock(DiagsMu);
         if (ReportDiagnostics)
           Callbacks.onDiagnostics(FileName, (*AST)->getDiagnostics());
+        DiagsWereReported = true;
       }
+
+      if (WantHighlightings) {
+        std::vector<HighlightingToken> 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<FileData> &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) {
Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.h
+++ 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<HighlightingToken> getSemanticHighlightings(ParsedAST &AST);
+// Returns a map where all HighlightingKinds are mapped to a vector of TextMate
+// scopes.
+std::map<HighlightingKind, std::vector<std::string>>
+getSemanticHighlightingScopes();
 
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -74,5 +74,11 @@
   return HighlightingTokenCollector(AST).collectTokens();
 }
 
+std::map<HighlightingKind, std::vector<std::string>>
+getSemanticHighlightingScopes() {
+  return {{HighlightingKind::Variable, {"variable"}},
+          {HighlightingKind::Function, {"entity.name.function"}}};
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ 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<HighlightingToken> 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<std::string> 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<llvm::Optional<FuzzyFindRequest>>
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ 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<clang::Preprocessor> PP,
@@ -71,9 +72,15 @@
     DiagConsumer.onFileUpdated(File, Status);
   }
 
+  virtual void onHighlightings(PathRef File,
+                            std::vector<HighlightingToken> 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<UpdateIndexCallbacks>(DynamicIdx.get(),
-                                                            DiagConsumer),
+                    llvm::make_unique<UpdateIndexCallbacks>(
+                        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); }
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ 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<Diag> Diagnostics) override;
   void onFileUpdated(PathRef File, const TUStatus &Status) override;
 
+  // Implement HighlightingsConsumer
+  void onHighlightingsReady(PathRef File,
+                         std::vector<HighlightingToken> Highlightings) override;
+
   // LSP methods. Notifications have signature void(const Params&).
   // Calls have signature void(const Params&, Callback<Response>).
   void onInitialize(const InitializeParams &, Callback<llvm::json::Value>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ 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<DiagnosticsConsumer &>(*this),
-                 ClangdServerOpts);
+                 static_cast<HighlightingsConsumer &>(*this), ClangdServerOpts);
   applyConfiguration(Params.initializationOptions.ConfigSettings);
 
   CCOpts.EnableSnippets = Params.capabilities.CompletionSnippets;
@@ -1063,6 +1063,11 @@
   return true;
 }
 
+void ClangdLSPServer::onHighlightingsReady(PathRef File,
+                                        std::vector<HighlightingToken> Highlightings) {
+
+}
+
 void ClangdLSPServer::onDiagnosticsReady(PathRef File,
                                          std::vector<Diag> Diagnostics) {
   auto URI = URIForFile::canonicalize(File, /*TUPath=*/File);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to