alexzielenski created this revision.
alexzielenski added reviewers: MaskRay, ilya-biryukov, sammccall.
alexzielenski created this object with visibility "All Users".
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, jkorous.
Herald added a project: clang.
alexzielenski requested review of this revision.

This patch adds support for rainbow semantic highlighting seem in ccls and 
cquery into clangd's implementation of SemanticTokensProvider. The method of 
implementation is to create new modifiers "0, 1, 2, 4", which are pinned to the 
hash of a SymbolID, to be used by the user in the creation of their theme for 
rainbow highlighting.

This patch has a few flaws as well as minor improvements to clangd's 
classification of symbols and takes you 80% of the way. I mainly created it for 
my own purposes since I cannot read code without this feature. Hopefully you 
can merge something like this upstream so I don't have to keep maintaining my 
local checkout.

I attached an example of what this looks like, and below is the configuration I 
used in my VSCode settings so you can try for yourself:
F12954809: Screen Shot 2020-09-14 at 9.38.54 PM.png 
<https://reviews.llvm.org/F12954809>

  json
      "editor.semanticTokenColorCustomizations": {
          "enabled": true,
          "rules": {
              "variable:cpp": "#92d7f2",
              "variable.1:cpp": "#78a8d5",
              "variable.2:cpp": "#9be0e6",
              "variable.3:cpp": "#9cc2f7",
              "variable.4:cpp": "#5ba9b8",
              "variable.local:cpp": "#99bfcb",
              "variable.local.1:cpp": "#c2e9f5",
              "variable.local.2:cpp": "#add4e0",
              "variable.local.3:cpp": "#99bfcb",
              "variable.local.4:cpp": "#81afc5",
              "variable.member:cpp": "#4fbbbc",
              "variable.member.1:cpp": "#3ba7e5",
              "variable.member.2:cpp": "#33d4d1",
              "variable.member.3:cpp": "#4eb4f3",
              "variable.member.4:cpp": "#6ed7d8",
              "variable.static.member:cpp": "#66d4f6",
              "variable.static.member.1:cpp": "#2f8fa5",
              "variable.static.member.2:cpp": "#4ee2f4",
              "variable.static.member.3:cpp": "#88aee1",
              "variable.static.member.4:cpp": "#4ab9ce",
              "parameter:cpp": "#a4beb8",
              "parameter.1:cpp": "#7fd6c5",
              "parameter.2:cpp": "#b8d3cd",
              "parameter.3:cpp": "#9fcaac",
              "parameter.4:cpp": "#a8dfd4",
              "function:cpp": "#61cca5",
              "function.1:cpp": "#1fe1fb",
              "function.2:cpp": "#43c88b",
              "function.3:cpp": "#3edbd8",
              "function.4:cpp": "#63e5a6",
              "function.member:cpp": "#4bd6fd",
              "function.member.1:cpp": "#28e6cc",
              "function.member.2:cpp": "#4eb4f3",
              "function.member.3:cpp": "#4cccb7",
              "function.member.4:cpp": "#4abfe8",
              "function.static:cpp": "#9fa8e1",
              "function.static.1:cpp": "#20d8fd",
              "function.static.2:cpp": "#9ca6f4",
              "function.static.3:cpp": "#5bcdf6",
              "function.static.4:cpp": "#b4bcf7",
              "type:cpp": "#edc158",
              "type.1:cpp": "#f67b3e",
              "type.2:cpp": "#ecc02b",
              "type.3:cpp": "#e59668",
              "type.4:cpp": "#e6a01e",
              "enum:cpp": "#d55bc0",
              "enum.1:cpp": "#ee4d69",
              "enum.2:cpp": "#f570d9",
              "enum.3:cpp": "#f7457c",
              "enum.4:cpp": "#e44cc0",
              "enumConstant:cpp": "#f53d8b",
              "enumConstant.1:cpp": "#f53d8b",
              "enumConstant.2:cpp": "#ef489e",
              "enumConstant.3:cpp": "#d452a8",
              "enumConstant.4:cpp": "#d452a8",
              "macro:cpp": "#a85e16",
              "macro.1:cpp": "#d29e3b",
              "macro.2:cpp": "#8d630a",
              "macro.3:cpp": "#dc803d",
              "macro.4:cpp": "#dd9549",
              "namespace:cpp": "#c6a94d",
              "namespace.1:cpp": "#8c6a25",
              "namespace.2:cpp": "#daca74",
              "namespace.3:cpp": "#ab722f",
              "namespace.4:cpp": "#a19435"
          }
      }




Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87669

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/SemanticHighlighting.cpp
  clang-tools-extra/clangd/SemanticHighlighting.h

Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.h
+++ clang-tools-extra/clangd/SemanticHighlighting.h
@@ -70,6 +70,7 @@
 struct HighlightingToken {
   HighlightingKind Kind;
   Range R;
+  unsigned char Index = 0;
 };
 
 bool operator==(const HighlightingToken &L, const HighlightingToken &R);
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -131,6 +131,53 @@
   return Result;
 }
 
+unsigned char idxForType(const Type *TP);
+unsigned char idxForDecl(const NamedDecl *D) {
+  if (auto *USD = dyn_cast<UsingShadowDecl>(D)) {
+    if (auto *Target = USD->getTargetDecl())
+      D = Target;
+  }
+  if (auto *TD = dyn_cast<TemplateDecl>(D)) {
+    if (auto *Templated = TD->getTemplatedDecl())
+      D = Templated;
+  }
+  if (auto *TD = dyn_cast<TypedefNameDecl>(D)) {
+    // We try to highlight typedefs as their underlying type.
+    if (auto K = idxForType(TD->getUnderlyingType().getTypePtrOrNull()))
+      return K;
+    // And fallback to a generic kind if this fails.
+    return 0;
+  }
+
+  if (auto ID = getSymbolID(D)) {
+    return hash_value(*ID) % 255;
+  }
+
+  return D->getGlobalID() % 255;
+}
+
+unsigned char idxForType(const Type *TP) {
+  if (!TP)
+    return 0;
+  if (TP->isBuiltinType()) // Builtins are special, they do not have decls.
+    return 0;
+  if (auto *TD = dyn_cast<TemplateTypeParmType>(TP))
+    return idxForDecl(TD->getDecl());
+  if (auto *TD = TP->getAsTagDecl())
+    return idxForDecl(TD);
+  return 0;
+}
+
+unsigned char idxForReference(const ReferenceLoc &R) {
+  unsigned char Result = 0;
+  for (const NamedDecl *Decl : R.Targets) {
+    if (!canHighlightName(Decl->getDeclName()))
+      continue;
+    Result = idxForDecl(Decl);
+  }
+  return Result;
+}
+
 // For a macro usage `DUMP(foo)`, we want:
 //  - DUMP --> "macro"
 //  - foo --> "variable".
@@ -184,7 +231,8 @@
 
   void addToken(HighlightingToken T) { Tokens.push_back(T); }
 
-  void addToken(SourceLocation Loc, HighlightingKind Kind) {
+  void addToken(SourceLocation Loc, HighlightingKind Kind,
+                unsigned char idx = 0) {
     Loc = getHighlightableSpellingToken(Loc, SourceMgr);
     if (Loc.isInvalid())
       return;
@@ -193,7 +241,7 @@
 
     auto Range = halfOpenToRange(SourceMgr,
                                  Tok->range(SourceMgr).toCharRange(SourceMgr));
-    Tokens.push_back(HighlightingToken{Kind, std::move(Range)});
+    Tokens.push_back(HighlightingToken{Kind, std::move(Range), idx});
   }
 
   std::vector<HighlightingToken> collect(ParsedAST &AST) && {
@@ -284,7 +332,7 @@
 
   bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) {
     if (auto K = kindForType(L.getTypePtr()))
-      H.addToken(L.getBeginLoc(), *K);
+      H.addToken(L.getBeginLoc(), *K, idxForType(L.getTypePtr()));
     return true;
   }
 
@@ -293,7 +341,8 @@
     if (!AT)
       return true;
     if (auto K = kindForType(AT->getDeducedType().getTypePtrOrNull()))
-      H.addToken(D->getTypeSpecStartLoc(), *K);
+      H.addToken(D->getTypeSpecStartLoc(), *K,
+                 idxForType(AT->getDeducedType().getTypePtrOrNull()));
     return true;
   }
 
@@ -388,12 +437,14 @@
   // Highlight all decls and references coming from the AST.
   findExplicitReferences(C, [&](ReferenceLoc R) {
     if (auto Kind = kindForReference(R))
-      Builder.addToken(R.NameLoc, *Kind);
+      Builder.addToken(R.NameLoc, *Kind, idxForReference(R));
   });
   // Add highlightings for macro references.
   for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
     for (const auto &M : SIDToRefs.second)
-      Builder.addToken({HighlightingKind::Macro, M});
+      Builder.addToken(
+          {HighlightingKind::Macro, M,
+           static_cast<unsigned char>(hash_value(SIDToRefs.first) % 255)});
   }
   for (const auto &M : AST.getMacros().UnknownMacros)
     Builder.addToken({HighlightingKind::Macro, M});
@@ -540,6 +591,19 @@
     assert(Tok.R.end.line == Tok.R.start.line);
     Out.length = Tok.R.end.character - Tok.R.start.character;
     Out.tokenType = static_cast<unsigned>(Tok.Kind);
+    Out.tokenModifiers = static_cast<unsigned>(1UL << (Tok.Index % 5));
+
+    if (Tok.Kind == HighlightingKind::LocalVariable) {
+      Out.tokenModifiers |= 1 << 5;
+    } else if (Tok.Kind == HighlightingKind::StaticField) {
+      Out.tokenModifiers |= 1 << 6;
+    } else if (Tok.Kind == HighlightingKind::StaticMethod) {
+      Out.tokenModifiers |= 1 << 6;
+    } else if (Tok.Kind == HighlightingKind::Method) {
+      Out.tokenModifiers |= 1 << 7;
+    } else if (Tok.Kind == HighlightingKind::Field) {
+      Out.tokenModifiers |= 1 << 7;
+    }
 
     Last = &Tok;
   }
@@ -549,19 +613,16 @@
   switch (Kind) {
   case HighlightingKind::Variable:
   case HighlightingKind::LocalVariable:
+  case HighlightingKind::Field:
   case HighlightingKind::StaticField:
     return "variable";
   case HighlightingKind::Parameter:
+    // should this be a modifier on variable?
     return "parameter";
-  case HighlightingKind::Function:
-    return "function";
   case HighlightingKind::Method:
-    return "member";
   case HighlightingKind::StaticMethod:
-    // FIXME: better function/member with static modifier?
+  case HighlightingKind::Function:
     return "function";
-  case HighlightingKind::Field:
-    return "member";
   case HighlightingKind::Class:
     return "class";
   case HighlightingKind::Enum:
@@ -584,8 +645,11 @@
     return "type";
   case HighlightingKind::Macro:
     return "macro";
+    // would be cool to change this into "function" or "variable"
+    //  with "preprocessor" modifier
   case HighlightingKind::InactiveCode:
     return "comment";
+    // add "preprocessor" modifier
   }
   llvm_unreachable("unhandled HighlightingKind");
 }
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -460,6 +460,20 @@
   Transp.notify(Method, std::move(Params));
 }
 
+static std::vector<llvm::StringRef> semanticTokenModifiers() {
+  std::vector<llvm::StringRef> Result;
+  Result.push_back("0");
+  Result.push_back("1");
+  Result.push_back("2");
+  Result.push_back("3");
+  Result.push_back("4");
+  Result.push_back("local");
+  Result.push_back("static");
+  Result.push_back("member");
+  // Result.push_back("const");
+  return Result;
+}
+
 static std::vector<llvm::StringRef> semanticTokenTypes() {
   std::vector<llvm::StringRef> Types;
   for (unsigned I = 0; I <= static_cast<unsigned>(HighlightingKind::LastKind);
@@ -600,8 +614,9 @@
                  {"full", llvm::json::Object{{"delta", true}}},
                  {"range", false},
                  {"legend",
-                  llvm::json::Object{{"tokenTypes", semanticTokenTypes()},
-                                     {"tokenModifiers", llvm::json::Array()}}},
+                  llvm::json::Object{
+                      {"tokenTypes", semanticTokenTypes()},
+                      {"tokenModifiers", semanticTokenModifiers()}}},
              }},
             {"signatureHelpProvider",
              llvm::json::Object{
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to