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

TODO: Is the visitor the right approach?
TODO: Add tests
TODO:   Test that only highlightings for main file are returned, but not 
headers.

...as specified at

  https://github.com/microsoft/vscode-languageserver-node/pull/367

See also

  https://github.com/microsoft/vscode-languageserver-node/issues/368

So far, this is an *incomplete* implementation providing highlighting
for identifiers of VarDecl/DeclRefExpr.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D63331

Files:
  clang-tools-extra/clangd/CMakeLists.txt
  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/ClangdUnit.cpp
  clang-tools-extra/clangd/ClangdUnit.h
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.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/test/initialize-params.test
  clang-tools-extra/clangd/thirdparty/base64/base64.cpp
  clang-tools-extra/clangd/thirdparty/base64/base64.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
@@ -537,8 +537,10 @@
   MockCompilationDatabase CDB(BuildDir, RelPathPrefix);
 
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   MockFSProvider FS;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   // Fill the filesystem.
   auto FooCpp = testPath("src/foo.cpp");
@@ -1653,8 +1655,10 @@
 TEST(GoToInclude, All) {
   MockFSProvider FS;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   const char *SourceContents = R"cpp(
@@ -1728,8 +1732,10 @@
   // good preamble.
   MockFSProvider FS;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      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
@@ -684,7 +684,9 @@
   } CaptureTUStatus;
   MockFSProvider FS;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, CaptureTUStatus, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, CaptureTUStatus, HighlightConsumer,
+                      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
@@ -58,7 +58,8 @@
 class WorkspaceSymbolsTest : public ::testing::Test {
 public:
   WorkspaceSymbolsTest()
-      : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {
+      : Server(CDB, FSProvider, DiagConsumer, HighlightConsumer,
+               optsForTests()) {
     // Make sure the test root directory is created.
     FSProvider.Files[testPath("unused")] = "";
     CDB.ExtraClangFlags = {"-xc++"};
@@ -68,6 +69,7 @@
   MockFSProvider FSProvider;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   ClangdServer Server;
   int Limit = 0;
 
@@ -321,12 +323,14 @@
 class DocumentSymbolsTest : public ::testing::Test {
 public:
   DocumentSymbolsTest()
-      : Server(CDB, FSProvider, DiagConsumer, optsForTests()) {}
+      : Server(CDB, FSProvider, DiagConsumer, HighlightConsumer,
+               optsForTests()) {}
 
 protected:
   MockFSProvider FSProvider;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   ClangdServer Server;
 
   std::vector<DocumentSymbol> getSymbols(PathRef File) {
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -139,7 +139,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   return completions(Server, Text, std::move(IndexSymbols), std::move(Opts),
                      FilePath);
 }
@@ -624,7 +626,9 @@
   FS.Files[BarHeader] = "";
 
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   auto BarURI = URI::create(BarHeader).toString();
   Symbol Sym = cls("ns::X");
   Sym.CanonicalDeclaration.FileURI = BarURI.c_str();
@@ -663,7 +667,9 @@
   MockCompilationDatabase CDB;
 
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   Symbol SymX = cls("ns::X");
   Symbol SymY = cls("ns::Y");
   std::string BarHeader = testPath("bar.h");
@@ -691,7 +697,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   FS.Files[testPath("bar.h")] =
       R"cpp(namespace ns { struct preamble { int member; }; })cpp";
@@ -741,9 +749,10 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   ClangdServer::Options Opts = ClangdServer::optsForTest();
   Opts.BuildDynamicSymbolIndex = true;
-  ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
 
   FS.Files[testPath("foo_header.h")] = R"cpp(
     #pragma once
@@ -774,9 +783,10 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   auto Opts = ClangdServer::optsForTest();
   Opts.BuildDynamicSymbolIndex = true;
-  ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
 
   FS.Files[testPath("foo.h")] = R"cpp(
       namespace ns { class XYZ {}; void foo(int x) {} }
@@ -952,10 +962,11 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   ClangdServer::Options Opts = ClangdServer::optsForTest();
   Opts.StaticIndex = Index.get();
 
-  ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
   auto File = testPath("foo.cpp");
   runAddDocument(Server, File, Text);
   return llvm::cantFail(runSignatureHelp(Server, File, Point));
@@ -1390,7 +1401,9 @@
 
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   Annotations Source(R"cpp(
     #include "foo.h"
@@ -1425,7 +1438,9 @@
 
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   Annotations Source(R"cpp(
     // We ignore namespace comments, for rationale see CodeCompletionStrings.h.
@@ -1490,10 +1505,12 @@
 
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   MockFSProvider FS;
   FS.Files[FooCpp] = "// empty file";
 
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   // Run completion outside the file range.
   Position Pos;
   Pos.line = 100;
@@ -1614,7 +1631,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   CodeCompleteOptions Opts;
   Opts.IncludeFixIts = true;
@@ -1654,7 +1673,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   CodeCompleteOptions Opts;
   Opts.IncludeFixIts = true;
@@ -1734,7 +1755,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   constexpr const char *TestCodes[] = {
       R"cpp(
@@ -1889,9 +1912,10 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
+  HighlightingsConsumer HighlightConsumer;
   ClangdServer::Options Opts = ClangdServer::optsForTest();
   Opts.BuildDynamicSymbolIndex = true;
-  ClangdServer Server(CDB, FS, DiagConsumer, Opts);
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer, Opts);
 
   FS.Files[testPath("foo.h")] = R"cpp(
     struct Foo {
@@ -2061,7 +2085,9 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto File = testPath("foo.cpp");
   Annotations Test(R"cpp(
@@ -2123,7 +2149,9 @@
   FS.Files[FooHeader] = "";
 
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   std::string DeclFile = URI::create(testPath("foo")).toString();
   Symbol sym = func("Func");
@@ -2154,7 +2182,9 @@
   std::string FooHeader = testPath("foo.h");
   FS.Files[FooHeader] = "#define CLANGD_PREAMBLE_HEADER x\n";
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   auto Results = completions(
       R"cpp(#include "foo.h"
           #define CLANGD_PREAMBLE_MAIN x
@@ -2275,7 +2305,9 @@
   std::string BarHeader = testPath("sub/bar.h");
   FS.Files[BarHeader] = "";
   IgnoreDiagnostics DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  HighlightingsConsumer HighlightConsumer;
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      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
@@ -142,9 +142,11 @@
       std::vector<std::pair<PathRef, llvm::StringRef>> ExtraFiles = {},
       bool ExpectErrors = false) {
     MockFSProvider FS;
+    HighlightingsConsumer HighlightConsumer;
     ErrorCheckingDiagConsumer DiagConsumer;
     MockCompilationDatabase CDB;
-    ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+    ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                        ClangdServer::optsForTest());
     for (const auto &FileWithContents : ExtraFiles)
       FS.Files[testPath(FileWithContents.first)] = FileWithContents.second;
 
@@ -155,6 +157,8 @@
     EXPECT_EQ(ExpectErrors, DiagConsumer.hadErrorInLastDiags());
     return Result;
   }
+
+  HighlightingsConsumer HighlightConsumer;
 };
 
 TEST_F(ClangdVFSTest, Parse) {
@@ -196,7 +200,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   const auto SourceContents = R"cpp(
 #include "foo.h"
@@ -231,7 +236,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   const auto SourceContents = R"cpp(
 #include "foo.h"
@@ -284,7 +290,8 @@
   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, HighlightConsumer,
+                      ClangdServer::optsForTest());
   {
     WithContextValue Entrypoint(Secret, 42);
     Server.addDocument(testPath("foo.cpp"), "void main(){}");
@@ -305,7 +312,8 @@
                              {"-xc++", "-target", "x86_64-linux-unknown",
                               "-m64", "--gcc-toolchain=/randomusr",
                               "-stdlib=libstdc++"});
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   // Just a random gcc version string
   SmallString<8> Version("4.9.3");
@@ -350,7 +358,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   const auto SourceContents1 = R"cpp(
@@ -386,7 +395,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   const auto SourceContents = R"cpp(
@@ -438,7 +448,8 @@
   MockFSProvider FS;
   MockCompilationDatabase CDB;
   MultipleErrorCheckingDiagConsumer DiagConsumer;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      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, HighlightConsumer, ClangdServer::optsForTest());
 
   Path FooCpp = testPath("foo.cpp");
   const auto SourceContents = R"cpp(
@@ -518,7 +529,8 @@
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
 
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   // clang cannot create CompilerInvocation if we pass two files in the
@@ -635,7 +647,8 @@
   TestDiagConsumer DiagConsumer;
   {
     MockCompilationDatabase CDB;
-    ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+    ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                        ClangdServer::optsForTest());
 
     // Prepare some random distributions for the test.
     std::random_device RandGen;
@@ -769,7 +782,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto SourceContents = R"cpp(
   #include "foo.h"
@@ -894,7 +908,8 @@
 
   NoConcurrentAccessDiagConsumer DiagConsumer(std::move(StartSecondPromise));
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
   Server.addDocument(FooCpp, SourceContentsWithErrors);
   StartSecond.wait();
   Server.addDocument(FooCpp, SourceContentsWithoutErrors);
@@ -906,7 +921,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto Path = testPath("foo.cpp");
   std::string Code = R"cpp(
@@ -935,7 +951,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto SourcePath = testPath("source/foo.cpp");
   auto HeaderPath = testPath("headers/foo.h");
@@ -1008,9 +1025,11 @@
 
   llvm::StringMap<unsigned> CountStats;
   ListenStatsFSProvider FS(CountStats);
+  HighlightingsConsumer HighlightConsumer;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto SourcePath = testPath("foo.cpp");
   auto HeaderPath = testPath("foo.h");
@@ -1046,7 +1065,8 @@
       "random-plugin",
   };
   OverlayCDB OCDB(&CDB);
-  ClangdServer Server(OCDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(OCDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   const auto SourceContents = "int main() { return 0; }";
@@ -1061,7 +1081,8 @@
   MockFSProvider FS;
   ErrorCheckingDiagConsumer DiagConsumer;
   MockCompilationDatabase CDB;
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
    Annotations Code(R"cpp(
@@ -1131,7 +1152,8 @@
 
   Notification CanReturnCommand;
   DelayedCompilationDatabase CDB(CanReturnCommand);
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
+  ClangdServer Server(CDB, FS, DiagConsumer, HighlightConsumer,
+                      ClangdServer::optsForTest());
 
   auto FooCpp = testPath("foo.cpp");
   Annotations Code(R"cpp(
Index: clang-tools-extra/clangd/thirdparty/base64/base64.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/thirdparty/base64/base64.h
@@ -0,0 +1,14 @@
+//
+//  base64 encoding and decoding with C++.
+//  Version: 1.01.00
+//
+
+#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
+#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
+
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
+
+#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */
Index: clang-tools-extra/clangd/thirdparty/base64/base64.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/thirdparty/base64/base64.cpp
@@ -0,0 +1,122 @@
+/* 
+   base64.cpp and base64.h
+
+   base64 encoding and decoding with C++.
+
+   Version: 1.01.00
+
+   Copyright (C) 2004-2017 René Nyffenegger
+
+   This source code is provided 'as-is', without any express or implied
+   warranty. In no event will the author be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this source code must not be misrepresented; you must not
+      claim that you wrote the original source code. If you use this source code
+      in a product, an acknowledgment in the product documentation would be
+      appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+      misrepresented as being the original source code.
+
+   3. This notice may not be removed or altered from any source distribution.
+
+   René Nyffenegger rene.nyffeneg...@adp-gmbh.ch
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars = 
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+  return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+  std::string ret;
+  int i = 0;
+  int j = 0;
+  unsigned char char_array_3[3];
+  unsigned char char_array_4[4];
+
+  while (in_len--) {
+    char_array_3[i++] = *(bytes_to_encode++);
+    if (i == 3) {
+      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+      char_array_4[3] = char_array_3[2] & 0x3f;
+
+      for(i = 0; (i <4) ; i++)
+        ret += base64_chars[char_array_4[i]];
+      i = 0;
+    }
+  }
+
+  if (i)
+  {
+    for(j = i; j < 3; j++)
+      char_array_3[j] = '\0';
+
+    char_array_4[0] = ( char_array_3[0] & 0xfc) >> 2;
+    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+
+    for (j = 0; (j < i + 1); j++)
+      ret += base64_chars[char_array_4[j]];
+
+    while((i++ < 3))
+      ret += '=';
+
+  }
+
+  return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+  int in_len = encoded_string.size();
+  int i = 0;
+  int j = 0;
+  int in_ = 0;
+  unsigned char char_array_4[4], char_array_3[3];
+  std::string ret;
+
+  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+    char_array_4[i++] = encoded_string[in_]; in_++;
+    if (i ==4) {
+      for (i = 0; i <4; i++)
+        char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+      char_array_3[0] = ( char_array_4[0] << 2       ) + ((char_array_4[1] & 0x30) >> 4);
+      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) +   char_array_4[3];
+
+      for (i = 0; (i < 3); i++)
+        ret += char_array_3[i];
+      i = 0;
+    }
+  }
+
+  if (i) {
+    for (j = 0; j < i; j++)
+      char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+
+    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+  }
+
+  return ret;
+}
Index: clang-tools-extra/clangd/test/initialize-params.test
===================================================================
--- clang-tools-extra/clangd/test/initialize-params.test
+++ clang-tools-extra/clangd/test/initialize-params.test
@@ -33,6 +33,13 @@
 # CHECK-NEXT:      "hoverProvider": true,
 # CHECK-NEXT:      "referencesProvider": true,
 # CHECK-NEXT:      "renameProvider": true,
+# CHECK-NEXT:      "semanticHighlighting": {
+# CHECK-NEXT:        "scopes": [
+# CHECK-NEXT:          [
+# CHECK-NEXT:            "variable.other.cpp"
+# CHECK-NEXT:          ]
+# CHECK-NEXT:        ]
+# CHECK-NEXT:      },
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [
 # CHECK-NEXT:          "(",
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"
@@ -113,6 +114,9 @@
   /// Called whenever the diagnostics for \p File are produced.
   virtual void onDiagnostics(PathRef File, std::vector<Diag> Diags) {}
 
+  virtual void onSemanticHighlight(PathRef File,
+                                   HighlightingsByLine Infos) {}
+
   /// Called whenever the TU status is updated.
   virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}
 };
Index: clang-tools-extra/clangd/TUScheduler.cpp
===================================================================
--- clang-tools-extra/clangd/TUScheduler.cpp
+++ clang-tools-extra/clangd/TUScheduler.cpp
@@ -488,8 +488,10 @@
     if (*AST) {
       {
         std::lock_guard<std::mutex> Lock(DiagsMu);
-        if (ReportDiagnostics)
+        if (ReportDiagnostics) {
           Callbacks.onDiagnostics(FileName, (*AST)->getDiagnostics());
+          Callbacks.onSemanticHighlight(FileName, (*AST)->getHighlightings());
+        }
       }
       trace::Span Span("Running main AST callback");
       Callbacks.onMainAST(FileName, **AST);
Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SemanticHighlighting.h
@@ -0,0 +1,33 @@
+//===--- SemanticHighlighting.h ----------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace clangd {
+
+struct Highlighting {
+  uint32_t character = 0; // zero-based
+  uint16_t length = 0;
+  uint16_t scope = 0;
+};
+using HighlightingsByLine = llvm::DenseMap<unsigned, std::vector<Highlighting>>;
+
+std::string toTokensString(const std::vector<Highlighting> &Highlightings);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Highlighting &I);
+
+} // namespace clangd
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTIC_HIGHLIGHTING_H
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -0,0 +1,100 @@
+//===--- SemanticHighlighting.cpp --------------------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SemanticHighlighting.h"
+#include "thirdparty/base64/base64.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace clangd {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Highlighting &I) {
+  return OS << "Highlighting{" << I.character << ',' << I.length << ','
+            << I.scope << '}';
+}
+
+// _Tokens_:
+//
+// Tokens are encoded in a memory friendly way straight from the wire. The
+// `tokens` string encapsulates multiple tokens as a `base64` encoded string. A
+// single semantic highlighting token can be interpreted as a range with
+// additional TextMate scopes information. The following properties can be
+// inferred from a single token: `character` is the zero-based offset where the
+// range starts. It is represented as a 32-bit unsigned integer. The `length`
+// property is the length of the range a semantic highlighting token. And
+// finally, it also carries the TextMate `scope` information as an integer
+// between zero and 2<sup>16</sup>-1 (inclusive) values. Clients must reuse the
+// `scopes` "lookup table" from the `initialize` request if they want to map the
+// `scope` index value to the actual TextMate scopes represented as a string.
+//
+// _Encoding the Tokens_:
+//
+// Following example shows how three individual tokens are encoded into its
+// `base64` form. Let assume, there is a series of token information (`[12, 15,
+// 1, 2, 5, 0, 7, 1000, 1]`) that can be interpreted as the following.
+//
+// ```json
+// [
+// 	{
+// 		"character": 12,
+// 		"length": 15,
+// 		"scope": 1
+// 	},
+// 	{
+// 		"character": 2,
+// 		"length": 5,
+// 		"scope": 0
+// 	},
+// 	{
+// 		"character": 7,
+// 		"length": 1000,
+// 		"scope": 1
+// 	}
+// ]
+// ```
+//
+// The `character` (`12` )property will be stored as is but the `length` (`15`)
+// and the `scope` (`1`) will be stored as a single 32-bit unsigned integer. The
+// initial value of this 32-bit unsigned integer is zero. First, we set the
+// value of the `length`, then we make some room (2<sup>16</sup>) for the
+// `scope` by shifting the `length` 16 times to the left and applying a bitwise
+// OR with the value of the `scope`.
+//
+// ```
+//
+// 00000000000000000000000000000000 // initial
+// 00000000000000000000000000001111 // set the `length` value (15)
+// 00000000000011110000000000000000 // shift [<< 0x0000010] the `length` and
+//                                     make some space for the scope
+// 00000000000011110000000000000001 // bitwise OR the `scope` value (1)
+// ```
+std::string toTokensString(const std::vector<Highlighting> &Highlightings) {
+  const unsigned int BufferSize = Highlightings.size() * 8;
+  const auto Buffer = llvm::make_unique<unsigned char[]>(BufferSize);
+
+  // Convert Highlightings to byte stream...
+  unsigned Index = 0;
+  for (const auto &H : Highlightings) {
+    Buffer[Index++] = (H.character & 0xFF000000) >> 24;
+    Buffer[Index++] = (H.character & 0x00FF0000) >> 16;
+    Buffer[Index++] = (H.character & 0x0000FF00) >> 8;
+    Buffer[Index++] = (H.character & 0x000000FF);
+
+    Buffer[Index++] = (H.length & 0x0000FF00) >> 8;
+    Buffer[Index++] = (H.length & 0x000000FF);
+
+    Buffer[Index++] = (H.scope & 0x0000FF00) >> 8;
+    Buffer[Index++] = (H.scope & 0x000000FF);
+  }
+
+  // ...in order to convert it to a base64 string.
+  return base64_encode(Buffer.get(), BufferSize);
+}
+
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -601,6 +601,20 @@
 };
 llvm::json::Value toJSON(const DiagnosticRelatedInformation &);
 
+struct SemanticHighlightingInformation {
+    // The zero-based line position in the text document.
+    int line;
+
+    // A base64 encoded string representing every single highlighted characters
+    // with its start position, length and the "lookup table" index of of the
+    // semantic highlighting [TextMate
+    // scopes](https://manual.macromates.com/en/language_grammars). If the
+    // `tokens` is empty or not defined, then no highlighted positions are
+    // available for the line.
+    llvm::Optional<std::string> tokens;
+};
+llvm::json::Value toJSON(const SemanticHighlightingInformation &);
+
 struct CodeAction;
 struct Diagnostic {
   /// The range at which the message applies.
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -1016,5 +1016,16 @@
   return OS << toString(Enc);
 }
 
+llvm::json::Value toJSON(const SemanticHighlightingInformation &I) {
+  llvm::json::Object O{
+      {"line", I.line},
+  };
+
+  if (I.tokens)
+    O["tokens"] = I.tokens;
+
+  return std::move(O);
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ClangdUnit.h
===================================================================
--- clang-tools-extra/clangd/ClangdUnit.h
+++ clang-tools-extra/clangd/ClangdUnit.h
@@ -16,6 +16,7 @@
 #include "Headers.h"
 #include "Path.h"
 #include "Protocol.h"
+#include "SemanticHighlighting.h"
 #include "index/CanonicalIncludes.h"
 #include "index/Index.h"
 #include "clang/Frontend/FrontendAction.h"
@@ -108,6 +109,7 @@
   ArrayRef<Decl *> getLocalTopLevelDecls();
 
   const std::vector<Diag> &getDiagnostics() const;
+  const HighlightingsByLine &getHighlightings() const;
 
   /// Returns the esitmated size of the AST and the accessory structures, in
   /// bytes. Does not include the size of the preamble.
@@ -120,7 +122,8 @@
             std::unique_ptr<CompilerInstance> Clang,
             std::unique_ptr<FrontendAction> Action,
             std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags,
-            IncludeStructure Includes, CanonicalIncludes CanonIncludes);
+            HighlightingsByLine Highlightings, IncludeStructure Includes,
+            CanonicalIncludes CanonIncludes);
 
   // In-memory preambles must outlive the AST, it is important that this member
   // goes before Clang and Action.
@@ -135,6 +138,7 @@
 
   // Data, stored after parsing.
   std::vector<Diag> Diags;
+  HighlightingsByLine Highlightings;
   // Top-level decls inside the current file. Not that this does not include
   // top-level decls from the preamble.
   std::vector<Decl *> LocalTopLevelDecls;
Index: clang-tools-extra/clangd/ClangdUnit.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdUnit.cpp
+++ clang-tools-extra/clangd/ClangdUnit.cpp
@@ -19,6 +19,7 @@
 #include "index/CanonicalIncludes.h"
 #include "index/Index.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TokenKinds.h"
@@ -275,6 +276,50 @@
   const LangOptions &LangOpts;
 };
 
+class FindHighlightingsVisitor
+    : public RecursiveASTVisitor<FindHighlightingsVisitor> {
+public:
+  FindHighlightingsVisitor(SourceManager &SourceManager) : SM(SourceManager) {}
+
+  HighlightingsByLine find(ASTContext &Context) {
+    TraverseAST(Context);
+    return std::move(Lines);
+  }
+
+  bool VisitVarDecl(VarDecl *D) {
+    if (IdentifierInfo *Identifier = D->getIdentifier()) {
+      Highlighting H;
+      H.character = SM.getPresumedColumnNumber(D->getLocation(), nullptr) - 1;
+      H.length = Identifier->getLength();
+      H.scope = 0;
+
+      const auto Line = SM.getPresumedLineNumber(D->getLocation(), nullptr) - 1;
+      Lines[Line].emplace_back(H);
+    }
+
+    return true;
+  }
+
+  bool VisitDeclRefExpr(clang::DeclRefExpr *E) {
+    // TODO: De-dup
+    if (IdentifierInfo *Identifier = E->getDecl()->getIdentifier()) {
+      Highlighting H;
+      H.character = SM.getPresumedColumnNumber(E->getLocation(), nullptr) - 1;
+      H.length = Identifier->getLength();
+      H.scope = 0;
+
+      const auto Line = SM.getPresumedLineNumber(E->getLocation(), nullptr) - 1;
+      Lines[Line].emplace_back(H);
+    }
+
+    return true;
+  }
+
+private:
+  SourceManager &SM;
+  HighlightingsByLine Lines;
+};
+
 } // namespace
 
 void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS) {
@@ -445,9 +490,17 @@
   // Add diagnostics from the preamble, if any.
   if (Preamble)
     Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end());
+
+  HighlightingsByLine Highlightings;
+  {
+    Highlightings = FindHighlightingsVisitor(Clang->getSourceManager())
+                        .find(Clang->getASTContext());
+  }
+
   return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action),
                    std::move(ParsedDecls), std::move(Diags),
-                   std::move(Includes), std::move(CanonIncludes));
+                   std::move(Highlightings), std::move(Includes),
+                   std::move(CanonIncludes));
 }
 
 ParsedAST::ParsedAST(ParsedAST &&Other) = default;
@@ -487,6 +540,10 @@
 
 const std::vector<Diag> &ParsedAST::getDiagnostics() const { return Diags; }
 
+const HighlightingsByLine &ParsedAST::getHighlightings() const {
+  return Highlightings;
+}
+
 std::size_t ParsedAST::getUsedBytes() const {
   auto &AST = getASTContext();
   // FIXME(ibiryukov): we do not account for the dynamically allocated part of
@@ -541,10 +598,11 @@
                      std::unique_ptr<CompilerInstance> Clang,
                      std::unique_ptr<FrontendAction> Action,
                      std::vector<Decl *> LocalTopLevelDecls,
-                     std::vector<Diag> Diags, IncludeStructure Includes,
-                     CanonicalIncludes CanonIncludes)
+                     std::vector<Diag> Diags, HighlightingsByLine Highlightings,
+                     IncludeStructure Includes, CanonicalIncludes CanonIncludes)
     : Preamble(std::move(Preamble)), Clang(std::move(Clang)),
       Action(std::move(Action)), Diags(std::move(Diags)),
+      Highlightings(std::move(Highlightings)),
       LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
       Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
   assert(this->Clang);
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -39,6 +39,13 @@
 namespace clang {
 namespace clangd {
 
+// TODO: Merge with DiagnosticsConsumer to "DocumentAnnotationsConsumer"?
+class HighlightingsConsumer {
+public:
+  virtual ~HighlightingsConsumer() = default;
+  virtual void onHighlightingsReady(PathRef File, HighlightingsByLine Infos){};
+};
+
 // FIXME: find a better name.
 class DiagnosticsConsumer {
 public:
@@ -142,7 +149,8 @@
   /// synchronize access to shared state.
   ClangdServer(const GlobalCompilationDatabase &CDB,
                const FileSystemProvider &FSProvider,
-               DiagnosticsConsumer &DiagConsumer, const Options &Opts);
+               DiagnosticsConsumer &DiagConsumer,
+               HighlightingsConsumer &HighlightConsumer, 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
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ 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,
+                       HighlightingsConsumer &HighlightConsumer)
+      : FIndex(FIndex), DiagConsumer(DiagConsumer),
+        HighlightConsumer(HighlightConsumer) {}
 
   void onPreambleAST(PathRef Path, ASTContext &Ctx,
                      std::shared_ptr<clang::Preprocessor> PP,
@@ -67,6 +69,11 @@
     DiagConsumer.onDiagnosticsReady(File, std::move(Diags));
   }
 
+  void onSemanticHighlight(PathRef File,
+                           HighlightingsByLine Highlightings) override {
+    HighlightConsumer.onHighlightingsReady(File, std::move(Highlightings));
+  }
+
   void onFileUpdated(PathRef File, const TUStatus &Status) override {
     DiagConsumer.onFileUpdated(File, Status);
   }
@@ -74,6 +81,7 @@
 private:
   FileIndex *FIndex;
   DiagnosticsConsumer &DiagConsumer;
+  HighlightingsConsumer &HighlightConsumer;
 };
 } // namespace
 
@@ -88,6 +96,7 @@
 ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
                            const FileSystemProvider &FSProvider,
                            DiagnosticsConsumer &DiagConsumer,
+                           HighlightingsConsumer &HighlightConsumer,
                            const Options &Opts)
     : FSProvider(FSProvider),
       DynamicIdx(Opts.BuildDynamicSymbolIndex
@@ -102,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, HighlightConsumer),
                     Opts.UpdateDebounce, Opts.RetentionPolicy) {
   // Adds an index to the stack, at higher priority than existing indexes.
   auto AddIndex = [&](SymbolIndex *Idx) {
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -31,7 +31,8 @@
 /// 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,
+                            HighlightingsByLine 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
@@ -333,7 +333,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;
@@ -389,6 +389,10 @@
                    ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
              }},
             {"typeHierarchyProvider", true},
+            {"semanticHighlighting",
+             llvm::json::Object{
+                 {"scopes", {{"variable.other.cpp"}}},
+             }},
         }}}};
   if (NegotiatedOffsetEncoding)
     Result["offsetEncoding"] = *NegotiatedOffsetEncoding;
@@ -1078,6 +1082,26 @@
   notify("textDocument/clangd.fileStatus", Status.render(File));
 }
 
+void ClangdLSPServer::onHighlightingsReady(PathRef File,
+                                           HighlightingsByLine Highlightings) {
+  auto URI = URIForFile::canonicalize(File, /*TUPath=*/File);
+
+  std::vector<SemanticHighlightingInformation> LSPHighlightings;
+  for (auto &H : Highlightings) {
+    SemanticHighlightingInformation I;
+    I.line = H.first;
+    if (!H.second.empty())
+      I.tokens = toTokensString(H.second);
+    LSPHighlightings.emplace_back(I);
+  }
+
+  notify("textDocument/semanticHighlighting",
+         llvm::json::Object{
+             {"textDocument", llvm::json::Object{{"version", 1}, {"uri", URI}}},
+             {"lines", std::move(LSPHighlightings)},
+         });
+}
+
 void ClangdLSPServer::reparseOpenedFiles() {
   for (const Path &FilePath : DraftMgr.getActiveFiles())
     Server->addDocument(FilePath, *DraftMgr.getDraft(FilePath),
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -62,6 +62,7 @@
   Quality.cpp
   RIFF.cpp
   Selection.cpp
+  SemanticHighlighting.cpp
   SourceCode.cpp
   Threading.cpp
   Trace.cpp
@@ -95,6 +96,8 @@
   refactor/Rename.cpp
   refactor/Tweak.cpp
 
+  thirdparty/base64/base64.cpp
+
   LINK_LIBS
   clangAST
   clangASTMatchers
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to