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

Emit publishSemanticHighlighting in LSP if enabled


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D63919

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/ClangdLSPServer.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/test/initialize-params.test

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,16 @@
 # CHECK-NEXT:      "hoverProvider": true,
 # CHECK-NEXT:      "referencesProvider": true,
 # CHECK-NEXT:      "renameProvider": true,
+# CHECK-NEXT:      "semanticHighlighting": {
+# CHECK-NEXT:        "scopes": [
+# CHECK-NEXT:          [
+# CHECK-NEXT:            "variable.cpp"
+# CHECK-NEXT:          ],
+# CHECK-NEXT:          [
+# CHECK-NEXT:            "entity.name.function.cpp"
+# CHECK-NEXT:          ]
+# CHECK-NEXT:        ]
+# CHECK-NEXT:      },
 # CHECK-NEXT:      "signatureHelpProvider": {
 # CHECK-NEXT:        "triggerCharacters": [
 # CHECK-NEXT:          "(",
Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.h
+++ clang-tools-extra/clangd/SemanticHighlighting.h
@@ -27,10 +27,34 @@
 
 bool operator==(const HighlightingToken &Lhs, const HighlightingToken &Rhs);
 
+// Contains all highlightings in a single line.
+struct LineHighlighting {
+  LineHighlighting(unsigned int Line = 0,
+                   std::vector<HighlightingToken> Tokens = {})
+      : Line(Line), Tokens(Tokens) {}
+  unsigned int Line;
+  std::vector<HighlightingToken> Tokens;
+};
+
+llvm::json::Value toJSON(const LineHighlighting &Highlighting);
+
 // Returns all HighlightingTokens from an AST. Only generates highlights for the
 // main AST.
 std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST);
 
+// Gets the TextMate scopes as a double nested array where the
+// SemanticHighlightKind indexes correctly into this vector.
+std::vector<std::vector<std::string>> getSemanticTextMateScopes();
+
+// Converts a vector of HighlightingTokens to a vector of LineHighlightings.
+// Assumes the HighlightingToken's vector is ordered in ascending order by line
+// and secondarily by column of the token's start position. The returned
+// LineHighlighting vector will be ordered in ascending order according to the
+// line number. The tokens in a LineHighlighting are ordered in ascending order
+// according to the token's start character.
+std::vector<LineHighlighting>
+highlightingTokensToLines(llvm::ArrayRef<HighlightingToken> Tokens);
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -63,8 +63,69 @@
   }
 };
 
+// Encode binary data into base64.
+// This was copied from compiler-rt/lib/fuzzer/FuzzerUtil.cpp.
+// FIXME: Factor this out into llvm/Support?
+std::string encodeBase64(const llvm::SmallVectorImpl<char> &U) {
+  static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                              "abcdefghijklmnopqrstuvwxyz"
+                              "0123456789+/";
+  std::string Res;
+  size_t I;
+  for (I = 0; I + 2 < U.size(); I += 3) {
+    uint32_t X = (U[I] << 16) + (U[I + 1] << 8) + U[I + 2];
+    Res += Table[(X >> 18) & 63];
+    Res += Table[(X >> 12) & 63];
+    Res += Table[(X >> 6) & 63];
+    Res += Table[X & 63];
+  }
+  if (I + 1 == U.size()) {
+    uint32_t X = (U[I] << 16);
+    Res += Table[(X >> 18) & 63];
+    Res += Table[(X >> 12) & 63];
+    Res += "==";
+  } else if (I + 2 == U.size()) {
+    uint32_t X = (U[I] << 16) + (U[I + 1] << 8);
+    Res += Table[(X >> 18) & 63];
+    Res += Table[(X >> 12) & 63];
+    Res += Table[(X >> 6) & 63];
+    Res += "=";
+  }
+  return Res;
+}
+
+void write32be(uint32_t I, llvm::raw_ostream &OS) {
+  std::array<char, 4> Buf;
+  llvm::support::endian::write32be(Buf.data(), I);
+  OS.write(Buf.data(), Buf.size());
+}
+
+void write16be(uint16_t I, llvm::raw_ostream &OS) {
+  std::array<char, 2> Buf;
+  llvm::support::endian::write16be(Buf.data(), I);
+  OS.write(Buf.data(), Buf.size());
+}
 } // namespace
 
+llvm::json::Value toJSON(const LineHighlighting &Highlight) {
+  llvm::SmallVector<char, 128> BinaryHighlights;
+  llvm::raw_svector_ostream OS(BinaryHighlights);
+
+  for (size_t I = 0, End = Highlight.Tokens.size(); I < End; I++) {
+    // Each token should consists of 2 32 bit integers. The first integer is the
+    // start column of the token. The second integer's first 16 bits are the
+    // length of the token. The rest of the second integer is the scope index of
+    // the token.
+    HighlightingToken Token = Highlight.Tokens[I];
+    write32be(Token.R.start.character, OS);
+    write16be(Token.R.end.character - Token.R.start.character, OS);
+    write16be(static_cast<int>(Token.Kind), OS);
+  }
+
+  return llvm::json::Object{{"line", Highlight.Line},
+                            {"tokens", encodeBase64(BinaryHighlights)}};
+}
+
 bool operator==(const HighlightingToken &Lhs, const HighlightingToken &Rhs) {
   return Lhs.Kind == Rhs.Kind && Lhs.R == Rhs.R;
 }
@@ -73,5 +134,35 @@
   return HighlightingTokenCollector(AST).collectTokens();
 }
 
+std::vector<LineHighlighting>
+highlightingTokensToLines(llvm::ArrayRef<HighlightingToken> Tokens) {
+  std::vector<LineHighlighting> Lines;
+  int LastLine = -1;
+  // FIXME: Tokens might be multiple lines long (block comments) in this case
+  // this needs to add multiple lines for those tokens.
+  for (const auto &Token : Tokens) {
+    if (Token.R.start.line != LastLine) {
+      Lines.push_back(LineHighlighting(Token.R.start.line));
+      LastLine = Token.R.start.line;
+    }
+
+    Lines.back().Tokens.push_back(Token);
+  }
+
+  return Lines;
+}
+
+std::vector<std::vector<std::string>> getSemanticTextMateScopes() {
+  std::map<HighlightingKind, std::vector<std::string>> Scopes = {
+      {HighlightingKind::Variable, {"variable.cpp"}},
+      {HighlightingKind::Function, {"entity.name.function.cpp"}}};
+  std::vector<std::vector<std::string>> NestedScopes(Scopes.size());
+  for (const auto &Scope : Scopes) {
+    NestedScopes[static_cast<int>(Scope.first)] = Scope.second;
+  }
+
+  return NestedScopes;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -406,6 +406,9 @@
   /// textDocument.codeAction.codeActionLiteralSupport.
   bool CodeActionStructure = false;
 
+  /// Client supports semantic highlighting.
+  bool SemanticHighlighting = false;
+
   /// Supported encodings for LSP character offsets. (clangd extension).
   llvm::Optional<std::vector<OffsetEncoding>> offsetEncoding;
 
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -273,6 +273,12 @@
   if (!O)
     return false;
   if (auto *TextDocument = O->getObject("textDocument")) {
+    if (auto *SemanticHighlighting =
+            TextDocument->getObject("semanticHighlightingCapabilities")) {
+      if (auto SemanticHighlightingSupport =
+              SemanticHighlighting->getBoolean("semanticHighlighting"))
+        R.SemanticHighlighting = *SemanticHighlightingSupport;
+    }
     if (auto *Diagnostics = TextDocument->getObject("publishDiagnostics")) {
       if (auto CategorySupport = Diagnostics->getBoolean("categorySupport"))
         R.DiagnosticCategory = *CategorySupport;
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -55,6 +55,8 @@
   // Implement DiagnosticsConsumer.
   void onDiagnosticsReady(PathRef File, std::vector<Diag> Diagnostics) override;
   void onFileUpdated(PathRef File, const TUStatus &Status) override;
+  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>).
@@ -115,6 +117,11 @@
   void reparseOpenedFiles();
   void applyConfiguration(const ConfigurationSettings &Settings);
 
+  /// Sends a "publishSemanticHighlighting" notification to the LSP client.
+  void
+  publishSemanticHighlighting(const URIForFile &File,
+                              std::vector<LineHighlighting> Highlightings);
+
   /// Sends a "publishDiagnostics" notification to the LSP client.
   void publishDiagnostics(const URIForFile &File,
                           std::vector<clangd::Diagnostic> Diagnostics);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -328,6 +328,8 @@
     WithOffsetEncoding.emplace(kCurrentOffsetEncoding,
                                *NegotiatedOffsetEncoding);
 
+  ClangdServerOpts.SemanticHighlighting =
+      Params.capabilities.SemanticHighlighting;
   if (Params.rootUri && *Params.rootUri)
     ClangdServerOpts.WorkspaceRoot = Params.rootUri->file();
   else if (Params.rootPath && !Params.rootPath->empty())
@@ -403,6 +405,8 @@
                   {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND,
                    ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
              }},
+            {"semanticHighlighting",
+             llvm::json::Object{{"scopes", getSemanticTextMateScopes()}}},
             {"typeHierarchyProvider", true},
         }}}};
   if (NegotiatedOffsetEncoding)
@@ -927,6 +931,16 @@
     reparseOpenedFiles();
 }
 
+void ClangdLSPServer::publishSemanticHighlighting(
+    const URIForFile &File,
+    std::vector<clangd::LineHighlighting> Highlightings) {
+  notify("textDocument/semanticHighlighting",
+         llvm::json::Object{
+             {"textDocument", TextDocumentIdentifier{File}},
+             {"lines", std::move(Highlightings)},
+         });
+}
+
 void ClangdLSPServer::publishDiagnostics(
     const URIForFile &File, std::vector<clangd::Diagnostic> Diagnostics) {
   // Publish diagnostics.
@@ -1063,6 +1077,12 @@
   return true;
 }
 
+void ClangdLSPServer::onHighlightingsReady(
+    PathRef File, std::vector<HighlightingToken> Highlightings) {
+  publishSemanticHighlighting(URIForFile::canonicalize(File, /*TUPath=*/File),
+                              highlightingTokensToLines(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