lightmelodies updated this revision to Diff 306916.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D91930/new/

https://reviews.llvm.org/D91930

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/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/TUScheduler.h
  clang-tools-extra/clangd/XRefs.cpp
  clang-tools-extra/clangd/XRefs.h
  clang-tools-extra/clangd/test/initialize-params.test
  clang-tools-extra/clangd/tool/ClangdMain.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
@@ -1966,6 +1966,83 @@
                         URIForFile::canonicalize(testPath("bar.h"), "")})));
 }
 
+CodeLens lens(const URIForFile &URI, const CodeLensType &Type,
+              const Range &Range) {
+  return CodeLens{Range, None, CodeLensResolveData{Type, {URI, Range}}};
+}
+
+CodeLens lens(const URIForFile &URI, const std::string &Title,
+              const CodeLensType &Type, const Range &Range) {
+  Command Cmd;
+  Cmd.title = Title;
+  Cmd.command = std::string(ExecuteCommandParams::CLANGD_CLIENT_CODELENS);
+  Cmd.codeLens = CodeLensResolveData{Type, {URI, Range}};
+  return CodeLens{Range, Cmd, None};
+}
+
+TEST(CodeLensTest, Reference) {
+  Annotations Main(R"cpp(
+    class $X[[X]] {
+    };
+    int $main[[main]]() {
+      X x;
+    }
+  )cpp");
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+  auto Path = URIForFile::canonicalize(testPath(TU.Filename), testRoot());
+  if (auto CodeLens1 =
+          getDocumentCodeLens(AST, nullptr, testPath(TU.Filename))) {
+    EXPECT_THAT(
+        *CodeLens1,
+        ElementsAre(lens(Path, CodeLensType::Reference, Main.range("X")),
+                    lens(Path, CodeLensType::Reference, Main.range("main"))));
+    std::vector<CodeLens> CodeLens2;
+    for (auto &&CD : *CodeLens1) {
+      CodeLens2.emplace_back(
+          *resolveCodeLens(AST, CD, 0, nullptr, testPath(TU.Filename)));
+    }
+    EXPECT_THAT(
+        CodeLens2,
+        ElementsAre(
+            lens(Path, "2 refs", CodeLensType::Reference, Main.range("X")),
+            lens(Path, "1 refs", CodeLensType::Reference, Main.range("main"))));
+  }
+}
+
+TEST(CodeLensTest, Inheritance) {
+  Annotations Main(R"cpp(
+    class $X[[X]] {
+    public:
+      virtual void $m1[[method]]();
+    };
+
+    class $Y[[Y]] : public X {
+    public:
+      void $m2[[method]]() override;
+    };
+  )cpp");
+  TestTU TU;
+  TU.Code = std::string(Main.code());
+  auto AST = TU.build();
+  auto URI = URIForFile::canonicalize(testPath(TU.Filename), testRoot());
+  if (auto CodeLens =
+          getDocumentCodeLens(AST, TU.index().get(), testPath(TU.Filename))) {
+    EXPECT_THAT(
+        *CodeLens,
+        ElementsAre(
+            lens(URI, CodeLensType::Reference, Main.range("X")),
+            lens(URI, "1 derived", CodeLensType::TypeChildren, Main.range("X")),
+            lens(URI, CodeLensType::Reference, Main.range("m1")),
+            lens(URI, "1 derived", CodeLensType::MemberChildren,
+                 Main.range("m1")),
+            lens(URI, CodeLensType::Reference, Main.range("Y")),
+            lens(URI, "1 base", CodeLensType::TypeParent, Main.range("Y")),
+            lens(URI, CodeLensType::Reference, Main.range("m2")),
+            lens(URI, "1 base", CodeLensType::MemberParent, Main.range("m2"))));
+  }
+}
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===================================================================
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -330,6 +330,11 @@
     Hidden,
 };
 
+opt<bool> EnableCodeLens{
+    "code-lens", cat(Features), desc("Enable preview of CodeLens feature"),
+    init(false), Hidden,
+};
+
 opt<unsigned> WorkerThreadsCount{
     "j",
     cat(Misc),
@@ -762,6 +767,7 @@
   Opts.BuildRecoveryAST = RecoveryAST;
   Opts.PreserveRecoveryASTType = RecoveryASTType;
   Opts.FoldingRanges = FoldingRanges;
+  Opts.CodeLens = EnableCodeLens;
 
   Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
   Opts.CodeComplete.Limit = LimitResults;
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
@@ -63,7 +63,8 @@
 # CHECK-NEXT:      "executeCommandProvider": {
 # CHECK-NEXT:        "commands": [
 # CHECK-NEXT:          "clangd.applyFix",
-# CHECK-NEXT:          "clangd.applyTweak"
+# CHECK-NEXT:          "clangd.applyTweak",
+# CHECK-NEXT:          "clangd.server.codeLens"
 # CHECK-NEXT:        ]
 # CHECK-NEXT:      },
 # CHECK-NEXT:      "hoverProvider": true,
Index: clang-tools-extra/clangd/XRefs.h
===================================================================
--- clang-tools-extra/clangd/XRefs.h
+++ clang-tools-extra/clangd/XRefs.h
@@ -106,6 +106,12 @@
     ParsedAST &AST, Position Pos, int Resolve, TypeHierarchyDirection Direction,
     const SymbolIndex *Index = nullptr, PathRef TUPath = PathRef{});
 
+llvm::Optional<TypeHierarchyItem>
+getMemberHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
+                   TypeHierarchyDirection Direction,
+                   const SymbolIndex *Index = nullptr,
+                   PathRef TUPath = PathRef{});
+
 void resolveTypeHierarchy(TypeHierarchyItem &Item, int ResolveLevels,
                           TypeHierarchyDirection Direction,
                           const SymbolIndex *Index);
@@ -113,6 +119,15 @@
 /// Returns all decls that are referenced in the \p FD except local symbols.
 llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
                                                  const FunctionDecl *FD);
+
+/// Get codelens
+llvm::Expected<std::vector<CodeLens>>
+getDocumentCodeLens(ParsedAST &AST, const SymbolIndex *Index, PathRef Path);
+
+llvm::Expected<CodeLens> resolveCodeLens(ParsedAST &AST, const CodeLens &Params,
+                                         uint32_t Limit,
+                                         const SymbolIndex *Index,
+                                         PathRef Path);
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/XRefs.cpp
===================================================================
--- clang-tools-extra/clangd/XRefs.cpp
+++ clang-tools-extra/clangd/XRefs.cpp
@@ -1554,8 +1554,6 @@
                  TypeHierarchyDirection Direction, const SymbolIndex *Index,
                  PathRef TUPath) {
   const CXXRecordDecl *CXXRD = findRecordTypeAt(AST, Pos);
-  if (!CXXRD)
-    return llvm::None;
 
   bool WantParents = Direction == TypeHierarchyDirection::Parents ||
                      Direction == TypeHierarchyDirection::Both;
@@ -1570,10 +1568,13 @@
   // specialization parameters, while the children would involve classes
   // that derive from other specializations of the template.
   if (WantChildren) {
-    if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CXXRD))
+    if (auto *CTSD = dyn_cast_or_null<ClassTemplateSpecializationDecl>(CXXRD))
       CXXRD = CTSD->getTemplateInstantiationPattern();
   }
 
+  if (!CXXRD)
+    return llvm::None;
+
   Optional<TypeHierarchyItem> Result =
       declToTypeHierarchyItem(AST.getASTContext(), *CXXRD);
   if (!Result)
@@ -1617,6 +1618,102 @@
   }
 }
 
+static void fillSuperMethods(const CXXMethodDecl &CXXMD, ASTContext &ASTCtx,
+                             std::vector<TypeHierarchyItem> &SuperMethods) {
+  for (const CXXMethodDecl *ParentDecl : CXXMD.overridden_methods()) {
+    if (Optional<TypeHierarchyItem> ParentSym =
+            declToTypeHierarchyItem(ASTCtx, *ParentDecl)) {
+      ParentSym->parents.emplace();
+      fillSuperMethods(*ParentDecl, ASTCtx, *ParentSym->parents);
+      SuperMethods.emplace_back(std::move(*ParentSym));
+    }
+  }
+}
+
+static void fillSubMethods(const SymbolID &ID,
+                           std::vector<TypeHierarchyItem> &SubMethods,
+                           const SymbolIndex *Index, int Levels,
+                           PathRef TUPath) {
+  RelationsRequest Req;
+  Req.Subjects.insert(ID);
+  Req.Predicate = RelationKind::OverriddenBy;
+  Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
+    if (Optional<TypeHierarchyItem> ChildSym =
+            symbolToTypeHierarchyItem(Object, Index, TUPath)) {
+      if (Levels > 1) {
+        ChildSym->children.emplace();
+        fillSubMethods(Object.ID, *ChildSym->children, Index, Levels - 1,
+                       TUPath);
+      }
+      SubMethods.emplace_back(std::move(*ChildSym));
+    }
+  });
+}
+
+const NamedDecl *findNamedDeclAt(ParsedAST &AST, Position Pos) {
+  auto DeclFromNode = [](const SelectionTree::Node *N) -> const NamedDecl * {
+    if (!N)
+      return nullptr;
+
+    // Note: explicitReferenceTargets() will search for both template
+    // instantiations and template patterns, and prefer the former if available
+    // (generally, one will be available for non-dependent specializations of a
+    // class template).
+    auto Decls = explicitReferenceTargets(N->ASTNode, DeclRelation::Underlying);
+    if (Decls.empty())
+      return nullptr;
+
+    return Decls[0];
+  };
+
+  const SourceManager &SM = AST.getSourceManager();
+  const NamedDecl *Result = nullptr;
+  auto Offset = positionToOffset(SM.getBufferData(SM.getMainFileID()), Pos);
+  if (!Offset) {
+    llvm::consumeError(Offset.takeError());
+    return Result;
+  }
+  SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), *Offset,
+                            *Offset, [&](SelectionTree ST) {
+                              Result = DeclFromNode(ST.commonAncestor());
+                              return Result != nullptr;
+                            });
+  return Result;
+}
+
+llvm::Optional<TypeHierarchyItem>
+getMemberHierarchy(ParsedAST &AST, Position Pos, int ResolveLevels,
+                   TypeHierarchyDirection Direction, const SymbolIndex *Index,
+                   PathRef TUPath) {
+  auto CXXMD = dyn_cast_or_null<CXXMethodDecl>(findNamedDeclAt(AST, Pos));
+  if (!CXXMD || !CXXMD->isVirtual())
+    return llvm::None;
+
+  Optional<TypeHierarchyItem> Result =
+      declToTypeHierarchyItem(AST.getASTContext(), *CXXMD);
+
+  if (!Result)
+    return Result;
+
+  if (Direction == TypeHierarchyDirection::Parents ||
+      Direction == TypeHierarchyDirection::Both) {
+    Result->parents.emplace();
+    fillSuperMethods(*CXXMD, AST.getASTContext(), *Result->parents);
+  }
+
+  if ((Direction == TypeHierarchyDirection::Children ||
+       Direction == TypeHierarchyDirection::Both) &&
+      ResolveLevels > 0) {
+    Result->children.emplace();
+
+    if (Index) {
+      if (auto ID = getSymbolID(CXXMD))
+        fillSubMethods(ID, *Result->children, Index, ResolveLevels, TUPath);
+    }
+  }
+  return Result;
+}
+
 llvm::DenseSet<const Decl *> getNonLocalDeclRefs(ParsedAST &AST,
                                                  const FunctionDecl *FD) {
   if (!FD->hasBody())
@@ -1631,5 +1728,95 @@
   });
   return DeclRefs;
 }
+
+void collectCodeLens(ParsedAST &AST, const DocumentSymbol &Symbol,
+                     const SymbolIndex *Index, PathRef Path,
+                     std::vector<CodeLens> &Result) {
+  CodeLensResolveData Data;
+  Data.location =
+      Location{URIForFile::canonicalize(Path, Path), Symbol.selectionRange};
+  Data.type = CodeLensType::Reference;
+  Result.emplace_back(CodeLens{Symbol.selectionRange, None, Data});
+  // Collect codelens for hierarchy directly.
+  Position Pos = Symbol.selectionRange.start;
+  Command Cmd;
+  Cmd.command = std::string(ExecuteCommandParams::CLANGD_CLIENT_CODELENS);
+  switch (Symbol.kind) {
+  case SymbolKind::Class:
+  case SymbolKind::Struct:
+  case SymbolKind::Interface: {
+    auto CXXRD = dyn_cast_or_null<CXXRecordDecl>(findNamedDeclAt(AST, Pos));
+    if (CXXRD && CXXRD->hasDefinition() && CXXRD->getNumBases()) {
+      Data.type = CodeLensType::TypeParent;
+      Cmd.codeLens = Data;
+      Cmd.title = std::to_string(CXXRD->getNumBases()) + " base";
+      Result.emplace_back(CodeLens{Symbol.selectionRange, Cmd, None});
+    }
+    auto Items = getTypeHierarchy(AST, Pos, 1, TypeHierarchyDirection::Children,
+                                  Index, Path);
+    if (Items && Items->children && Items->children->size()) {
+      Data.type = CodeLensType::TypeChildren;
+      Cmd.codeLens = Data;
+      Cmd.title = std::to_string(Items->children->size()) + " derived";
+      Result.emplace_back(CodeLens{Symbol.selectionRange, Cmd, None});
+    }
+    break;
+  }
+  case SymbolKind::Method: {
+    auto CXXMD = dyn_cast_or_null<CXXMethodDecl>(
+        findNamedDeclAt(AST, Symbol.selectionRange.start));
+    if (CXXMD && CXXMD->isVirtual()) {
+      if (CXXMD->size_overridden_methods()) {
+        Data.type = CodeLensType::MemberParent;
+        Cmd.codeLens = Data;
+        Cmd.title = std::to_string(CXXMD->size_overridden_methods()) + " base";
+        Result.emplace_back(CodeLens{Symbol.selectionRange, Cmd, None});
+      }
+      auto Items = getMemberHierarchy(
+          AST, Pos, 1, TypeHierarchyDirection::Children, Index, Path);
+      if (Items && Items->children && Items->children->size()) {
+        Data.type = CodeLensType::MemberChildren;
+        Cmd.codeLens = Data;
+        Cmd.title = std::to_string(Items->children->size()) + " derived";
+        Result.emplace_back(CodeLens{Symbol.selectionRange, Cmd, None});
+      }
+    }
+    break;
+  }
+  default:
+    break;
+  }
+  for (auto &&Child : Symbol.children) {
+    collectCodeLens(AST, Child, Index, Path, Result);
+  }
+}
+
+llvm::Expected<std::vector<CodeLens>>
+getDocumentCodeLens(ParsedAST &AST, const SymbolIndex *Index, PathRef Path) {
+  auto DocumentSymbols = getDocumentSymbols(AST);
+  if (!DocumentSymbols)
+    return DocumentSymbols.takeError();
+  std::vector<CodeLens> Result;
+  for (const auto &Symbol : *DocumentSymbols) {
+    collectCodeLens(AST, Symbol, Index, Path, Result);
+  }
+  return Result;
+}
+
+llvm::Expected<CodeLens> resolveCodeLens(ParsedAST &AST, const CodeLens &Params,
+                                         uint32_t Limit,
+                                         const SymbolIndex *Index,
+                                         PathRef Path) {
+  CodeLensResolveData Data = *Params.data;
+  Command Cmd;
+  Cmd.command = std::string(ExecuteCommandParams::CLANGD_CLIENT_CODELENS);
+  Cmd.codeLens = Data;
+  Position Pos = Params.range.start;
+  if (Data.type == CodeLensType::Reference) {
+    auto Refs = findReferences(AST, Pos, Limit, Index).References;
+    Cmd.title = std::to_string(Refs.size()) + " refs";
+  }
+  return CodeLens{Params.range, Cmd, None};
+}
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/TUScheduler.h
===================================================================
--- clang-tools-extra/clangd/TUScheduler.h
+++ clang-tools-extra/clangd/TUScheduler.h
@@ -59,7 +59,7 @@
 struct ASTRetentionPolicy {
   /// Maximum number of ASTs to be retained in memory when there are no pending
   /// requests for them.
-  unsigned MaxRetainedASTs = 3;
+  unsigned MaxRetainedASTs = 16;
 };
 
 /// Clangd may wait after an update to see if another one comes along.
Index: clang-tools-extra/clangd/Protocol.h
===================================================================
--- clang-tools-extra/clangd/Protocol.h
+++ clang-tools-extra/clangd/Protocol.h
@@ -216,6 +216,7 @@
     return std::tie(LHS.uri, LHS.range) < std::tie(RHS.uri, RHS.range);
   }
 };
+bool fromJSON(const llvm::json::Value &, Location &, llvm::json::Path);
 llvm::json::Value toJSON(const Location &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Location &);
 
@@ -898,6 +899,7 @@
   /// Note: "documentChanges" is not currently used because currently there is
   /// no support for versioned edits.
 };
+bool operator==(const WorkspaceEdit &, const WorkspaceEdit &);
 bool fromJSON(const llvm::json::Value &, WorkspaceEdit &, llvm::json::Path);
 llvm::json::Value toJSON(const WorkspaceEdit &WE);
 
@@ -913,9 +915,28 @@
   /// ID of the tweak that should be executed. Corresponds to Tweak::id().
   std::string tweakID;
 };
+bool operator==(const TweakArgs &, const TweakArgs &);
 bool fromJSON(const llvm::json::Value &, TweakArgs &, llvm::json::Path);
 llvm::json::Value toJSON(const TweakArgs &A);
 
+enum class CodeLensType {
+  Reference = 1,
+  TypeParent = 2,
+  TypeChildren = 3,
+  MemberParent = 4,
+  MemberChildren = 5
+};
+bool fromJSON(const llvm::json::Value &, CodeLensType &, llvm::json::Path);
+
+struct CodeLensResolveData {
+  CodeLensType type;
+  Location location;
+};
+bool operator==(const CodeLensResolveData &, const CodeLensResolveData &);
+bool fromJSON(const llvm::json::Value &, CodeLensResolveData &,
+              llvm::json::Path);
+llvm::json::Value toJSON(const CodeLensResolveData &A);
+
 /// Exact commands are not specified in the protocol so we define the
 /// ones supported by Clangd here. The protocol specifies the command arguments
 /// to be "any[]" but to make this safer and more manageable, each command we
@@ -930,12 +951,20 @@
   // Command to apply the code action. Uses TweakArgs as argument.
   const static llvm::StringLiteral CLANGD_APPLY_TWEAK;
 
+  // Command to identify codeLens. Only used from server to client.
+  // Note it should be implemented in client by calling CLANGD_SERVER_CODELENS,
+  // so that client can handle codeLens result themselves, e.g. invoke
+  // 'editor.action.showReferences' in vscode
+  const static llvm::StringLiteral CLANGD_CLIENT_CODELENS;
+  // The actucal codeLens commands implemented by clangd sever.
+  const static llvm::StringLiteral CLANGD_SERVER_CODELENS;
   /// The command identifier, e.g. CLANGD_APPLY_FIX_COMMAND
   std::string command;
 
   // Arguments
   llvm::Optional<WorkspaceEdit> workspaceEdit;
   llvm::Optional<TweakArgs> tweakArgs;
+  llvm::Optional<CodeLensResolveData> codeLens;
 };
 bool fromJSON(const llvm::json::Value &, ExecuteCommandParams &,
               llvm::json::Path);
@@ -943,8 +972,24 @@
 struct Command : public ExecuteCommandParams {
   std::string title;
 };
+bool operator==(const Command &, const Command &);
 llvm::json::Value toJSON(const Command &C);
 
+/// https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens
+struct CodeLensParams : DocumentSymbolParams {};
+
+struct CodeLens {
+  // CodeLens range.
+  Range range;
+  // CodeLens command.
+  llvm::Optional<Command> command;
+  // CodeLens resolve data.
+  llvm::Optional<CodeLensResolveData> data;
+};
+bool operator==(const CodeLens &, const CodeLens &);
+bool fromJSON(const llvm::json::Value &, CodeLens &, llvm::json::Path);
+llvm::json::Value toJSON(const CodeLens &);
+
 /// A code action represents a change that can be performed in code, e.g. to fix
 /// a problem or to refactor code.
 ///
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -137,6 +137,12 @@
   return OS << R.start << '-' << R.end;
 }
 
+bool fromJSON(const llvm::json::Value &Params, Location &L,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("uri", L.uri) && O.map("range", L.range);
+}
+
 llvm::json::Value toJSON(const Location &P) {
   return llvm::json::Object{
       {"uri", P.uri},
@@ -634,16 +640,53 @@
          O.map("range", R.range) && O.map("context", R.context);
 }
 
+bool operator==(const WorkspaceEdit &LHS, const WorkspaceEdit &RHS) {
+  return LHS.changes == RHS.changes;
+}
+
 bool fromJSON(const llvm::json::Value &Params, WorkspaceEdit &R,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
   return O && O.map("changes", R.changes);
 }
 
+bool fromJSON(const llvm::json::Value &Params, CodeLensType &R,
+              llvm::json::Path P) {
+  if (auto T = Params.getAsInteger()) {
+    if (*T < static_cast<int>(CodeLensType::Reference) ||
+        *T > static_cast<int>(CodeLensType::MemberChildren))
+      return false;
+    R = static_cast<CodeLensType>(*T);
+    return true;
+  }
+  return false;
+}
+
+bool operator==(const CodeLensResolveData &LHS,
+                const CodeLensResolveData &RHS) {
+  return LHS.location == RHS.location && LHS.type == RHS.type;
+}
+
+bool fromJSON(const llvm::json::Value &Params, CodeLensResolveData &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("type", R.type) && O.map("location", R.location);
+}
+
+llvm::json::Value toJSON(const CodeLensResolveData &P) {
+  llvm::json::Object O{{"type", static_cast<int>(P.type)},
+                       {"location", P.location}};
+  return std::move(O);
+}
+
 const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND =
     "clangd.applyFix";
 const llvm::StringLiteral ExecuteCommandParams::CLANGD_APPLY_TWEAK =
     "clangd.applyTweak";
+const llvm::StringLiteral ExecuteCommandParams::CLANGD_CLIENT_CODELENS =
+    "clangd.client.codeLens";
+const llvm::StringLiteral ExecuteCommandParams::CLANGD_SERVER_CODELENS =
+    "clangd.server.codeLens";
 
 bool fromJSON(const llvm::json::Value &Params, ExecuteCommandParams &R,
               llvm::json::Path P) {
@@ -660,6 +703,9 @@
   if (R.command == ExecuteCommandParams::CLANGD_APPLY_TWEAK)
     return Args && Args->size() == 1 &&
            fromJSON(Args->front(), R.tweakArgs, P.field("arguments").index(0));
+  if (R.command == ExecuteCommandParams::CLANGD_SERVER_CODELENS)
+    return Args && Args->size() == 1 &&
+           fromJSON(Args->front(), R.codeLens, P.field("arguments").index(0));
   return false; // Unrecognized command.
 }
 
@@ -726,15 +772,43 @@
   return O && O.map("query", R.query);
 }
 
+bool operator==(const Command &LHS, const Command &RHS) {
+  return LHS.title == RHS.title && LHS.command == RHS.command &&
+         LHS.codeLens == RHS.codeLens && LHS.tweakArgs == RHS.tweakArgs &&
+         LHS.workspaceEdit == RHS.workspaceEdit;
+}
+
 llvm::json::Value toJSON(const Command &C) {
   auto Cmd = llvm::json::Object{{"title", C.title}, {"command", C.command}};
   if (C.workspaceEdit)
     Cmd["arguments"] = {*C.workspaceEdit};
   if (C.tweakArgs)
     Cmd["arguments"] = {*C.tweakArgs};
+  if (C.codeLens)
+    Cmd["arguments"] = {*C.codeLens};
   return std::move(Cmd);
 }
 
+bool operator==(const CodeLens &LHS, const CodeLens &RHS) {
+  return LHS.command == RHS.command && LHS.data == RHS.data &&
+         LHS.range == RHS.range;
+}
+
+bool fromJSON(const llvm::json::Value &Params, CodeLens &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("range", R.range) && O.map("data", R.data);
+}
+
+llvm::json::Value toJSON(const CodeLens &C) {
+  llvm::json::Object O{{"range", C.range}};
+  if (C.command)
+    O["command"] = *C.command;
+  if (C.data)
+    O["data"] = *C.data;
+  return std::move(O);
+}
+
 const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
 const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
 const llvm::StringLiteral CodeAction::INFO_KIND = "info";
@@ -783,6 +857,11 @@
   return llvm::json::Object{{"changes", std::move(FileChanges)}};
 }
 
+bool operator==(const TweakArgs &LHS, const TweakArgs &RHS) {
+  return LHS.file == RHS.file && LHS.tweakID == RHS.tweakID &&
+         LHS.selection == RHS.selection;
+}
+
 bool fromJSON(const llvm::json::Value &Params, TweakArgs &A,
               llvm::json::Path P) {
   llvm::json::ObjectMapper O(Params, P);
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -94,7 +94,7 @@
     /// thread, and callbacks are invoked before "async" functions return.
     unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount();
 
-    /// AST caching policy. The default is to keep up to 3 ASTs in memory.
+    /// AST caching policy. The default is to keep up to 16 ASTs in memory.
     ASTRetentionPolicy RetentionPolicy;
 
     /// Cached preambles are potentially large. If false, store them on disk.
@@ -166,6 +166,9 @@
     /// Enable preview of FoldingRanges feature.
     bool FoldingRanges = false;
 
+    /// Enable preview of CodeLens feature.
+    bool CodeLens = false;
+
     explicit operator TUScheduler::Options() const;
   };
   // Sensible default options for use in tests.
@@ -300,6 +303,9 @@
   /// Apply the code tweak with a specified \p ID.
   void applyTweak(PathRef File, Range Sel, StringRef ID,
                   Callback<Tweak::Effect> CB);
+  /// Implement 'clangd.server.codeLens'
+  void onCodeLensCommand(const CodeLensResolveData &Params, uint32_t Limit,
+                         Callback<llvm::json::Value> CB);
 
   /// Called when an event occurs for a watched file in the workspace.
   void onFileEvent(const DidChangeWatchedFilesParams &Params);
@@ -318,6 +324,10 @@
 
   void semanticHighlights(PathRef File,
                           Callback<std::vector<HighlightingToken>>);
+  /// CodeLenses.
+  void provideCodeLens(PathRef File, Callback<std::vector<CodeLens>> CB);
+  void resolveCodeLens(const CodeLens &Params, uint32_t Limit,
+                       Callback<CodeLens> CB);
 
   /// Describe the AST subtree for a piece of code.
   void getAST(PathRef File, Range R, Callback<llvm::Optional<ASTNode>> CB);
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -576,6 +576,89 @@
   WorkScheduler.runWithAST("ApplyTweak", File, std::move(Action));
 }
 
+void ClangdServer::onCodeLensCommand(const CodeLensResolveData &Params,
+                                     uint32_t Limit,
+                                     Callback<llvm::json::Value> Reply) {
+  auto File = Params.location.uri.file();
+  auto Pos = Params.location.range.start;
+  switch (Params.type) {
+  case CodeLensType::Reference: {
+    auto Action =
+        [Reply = std::move(Reply)](Expected<ReferencesResult> Refs) mutable {
+          if (!Refs)
+            return Reply(Refs.takeError());
+          Reply(Refs->References);
+        };
+    findReferences(File, Pos, Limit, std::move(Action));
+    break;
+  }
+  case CodeLensType::TypeParent: {
+    auto Action = [File, Pos, Reply = std::move(Reply),
+                   this](Expected<InputsAndAST> InpAST) mutable {
+      std::vector<Location> Results;
+      auto Items = getTypeHierarchy(
+          InpAST->AST, Pos, 1, TypeHierarchyDirection::Parents, Index, File);
+      if (Items && Items->parents) {
+        for (auto Item : *Items->parents) {
+          Results.emplace_back(Location{Item.uri, Item.selectionRange});
+        }
+      }
+      Reply(std::move(Results));
+    };
+    WorkScheduler.runWithAST("CodeLensCommand", File, std::move(Action));
+    break;
+  }
+  case CodeLensType::TypeChildren: {
+    auto Action = [File, Pos, Reply = std::move(Reply),
+                   this](Expected<InputsAndAST> InpAST) mutable {
+      std::vector<Location> Results;
+      auto Items = getTypeHierarchy(
+          InpAST->AST, Pos, 1, TypeHierarchyDirection::Children, Index, File);
+      if (Items && Items->children) {
+        for (auto Item : *Items->children) {
+          Results.emplace_back(Location{Item.uri, Item.selectionRange});
+        }
+      }
+      Reply(std::move(Results));
+    };
+    WorkScheduler.runWithAST("CodeLensCommand", File, std::move(Action));
+    break;
+  }
+  case CodeLensType::MemberParent: {
+    auto Action = [File, Pos, Reply = std::move(Reply),
+                   this](Expected<InputsAndAST> InpAST) mutable {
+      std::vector<Location> Results;
+      auto Items = getMemberHierarchy(
+          InpAST->AST, Pos, 1, TypeHierarchyDirection::Parents, Index, File);
+      if (Items && Items->parents) {
+        for (auto Item : *Items->parents) {
+          Results.emplace_back(Location{Item.uri, Item.selectionRange});
+        }
+      }
+      Reply(std::move(Results));
+    };
+    WorkScheduler.runWithAST("CodeLensCommand", File, std::move(Action));
+    break;
+  }
+  case CodeLensType::MemberChildren: {
+    auto Action = [File, Pos, Reply = std::move(Reply),
+                   this](Expected<InputsAndAST> InpAST) mutable {
+      std::vector<Location> Results;
+      auto Items = getMemberHierarchy(
+          InpAST->AST, Pos, 1, TypeHierarchyDirection::Children, Index, File);
+      if (Items && Items->children) {
+        for (auto Item : *Items->children) {
+          Results.emplace_back(Location{Item.uri, Item.selectionRange});
+        }
+      }
+      Reply(std::move(Results));
+    };
+    WorkScheduler.runWithAST("CodeLensCommand", File, std::move(Action));
+    break;
+  }
+  }
+}
+
 void ClangdServer::locateSymbolAt(PathRef File, Position Pos,
                                   Callback<std::vector<LocatedSymbol>> CB) {
   auto Action = [Pos, CB = std::move(CB),
@@ -816,6 +899,30 @@
       };
   WorkScheduler.runWithAST("GetAST", File, std::move(Action));
 }
+void ClangdServer::provideCodeLens(PathRef File,
+                                   Callback<std::vector<CodeLens>> CB) {
+  auto Action = [CB = std::move(CB), File = File.str(),
+                 this](llvm::Expected<InputsAndAST> InpAST) mutable {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getDocumentCodeLens(InpAST->AST, Index, File));
+  };
+  WorkScheduler.runWithAST("DocumentCodeLens", File, std::move(Action),
+                           TUScheduler::InvalidateOnUpdate);
+}
+
+void ClangdServer::resolveCodeLens(const CodeLens &Params, uint32_t Limit,
+                                   Callback<CodeLens> CB) {
+  auto File = Params.data->location.uri.file();
+  auto Action = [CB = std::move(CB), File = File.str(), Params, Limit,
+                 this](llvm::Expected<InputsAndAST> InpAST) mutable {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::resolveCodeLens(InpAST->AST, Params, Limit, Index, File));
+  };
+  WorkScheduler.runWithAST("ResolveCodeLens", File, std::move(Action),
+                           TUScheduler::InvalidateOnUpdate);
+}
 
 void ClangdServer::customAction(PathRef File, llvm::StringRef Name,
                                 Callback<InputsAndAST> Action) {
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -143,6 +143,8 @@
   void onSemanticTokens(const SemanticTokensParams &, Callback<SemanticTokens>);
   void onSemanticTokensDelta(const SemanticTokensDeltaParams &,
                              Callback<SemanticTokensOrDelta>);
+  void onCodeLens(const CodeLensParams &, Callback<std::vector<CodeLens>>);
+  void onCodeLensResolve(const CodeLens &, Callback<CodeLens>);
   /// This is a clangd extension. Provides a json tree representing memory usage
   /// hierarchy.
   void onMemoryUsage(const NoParams &, Callback<MemoryTree>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -620,7 +620,8 @@
              llvm::json::Object{
                  {"commands",
                   {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND,
-                   ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
+                   ExecuteCommandParams::CLANGD_APPLY_TWEAK,
+                   ExecuteCommandParams::CLANGD_SERVER_CODELENS}},
              }},
             {"typeHierarchyProvider", true},
             {"memoryUsageProvider", true}, // clangd extension.
@@ -634,6 +635,11 @@
              llvm::json::Object{{"scopes", buildHighlightScopeLookupTable()}}});
   if (Opts.FoldingRanges)
     Result.getObject("capabilities")->insert({"foldingRangeProvider", true});
+  if (Opts.CodeLens)
+    Result.getObject("capabilities")
+        ->insert({"codeLensProvider", llvm::json::Object{
+                                          {"resolveProvider", true},
+                                      }});
   Reply(std::move(Result));
 }
 
@@ -777,6 +783,10 @@
     Server->applyTweak(Params.tweakArgs->file.file(),
                        Params.tweakArgs->selection, Params.tweakArgs->tweakID,
                        std::move(Action));
+  } else if (Params.command == ExecuteCommandParams::CLANGD_SERVER_CODELENS &&
+             Params.codeLens) {
+    Server->onCodeLensCommand(*Params.codeLens, Opts.CodeComplete.Limit,
+                              std::move(Reply));
   } else {
     // We should not get here because ExecuteCommandParams would not have
     // parsed in the first place and this handler should not be called. But if
@@ -843,7 +853,12 @@
     const DidCloseTextDocumentParams &Params) {
   PathRef File = Params.textDocument.uri.file();
   DraftMgr.removeDraft(File);
-  Server->removeDocument(File);
+  // Rebuilding AST wastes a lot of time, which makes codeLens unacceptably
+  // slow (100x in common). So keep AST of the opened file for a better user
+  // experience. Maybe we need to change the current LRU Cache implementation.
+  // See also https://github.com/clangd/clangd/issues/589.
+
+  // Server->removeDocument(File);
 
   {
     std::lock_guard<std::mutex> Lock(FixItsMutex);
@@ -1398,6 +1413,30 @@
       });
 }
 
+void ClangdLSPServer::onCodeLens(const CodeLensParams &Params,
+                                 Callback<std::vector<CodeLens>> Reply) {
+  URIForFile FileURI = Params.textDocument.uri;
+  Server->provideCodeLens(
+      FileURI.file(),
+      [Reply = std::move(Reply)](
+          llvm::Expected<std::vector<CodeLens>> CodeLens) mutable {
+        if (!CodeLens)
+          return Reply(CodeLens.takeError());
+        return Reply(std::move(*CodeLens));
+      });
+}
+
+void ClangdLSPServer::onCodeLensResolve(const CodeLens &Params,
+                                        Callback<CodeLens> Reply) {
+  Server->resolveCodeLens(
+      Params, Opts.CodeComplete.Limit,
+      [Reply = std::move(Reply)](llvm::Expected<CodeLens> CodeLens) mutable {
+        if (!CodeLens)
+          return Reply(CodeLens.takeError());
+        return Reply(std::move(*CodeLens));
+      });
+}
+
 void ClangdLSPServer::onMemoryUsage(const NoParams &,
                                     Callback<MemoryTree> Reply) {
   llvm::BumpPtrAllocator DetailAlloc;
@@ -1457,6 +1496,10 @@
   MsgHandler->bind("$/memoryUsage", &ClangdLSPServer::onMemoryUsage);
   if (Opts.FoldingRanges)
     MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange);
+  if (Opts.CodeLens) {
+    MsgHandler->bind("textDocument/codeLens", &ClangdLSPServer::onCodeLens);
+    MsgHandler->bind("codeLens/resolve", &ClangdLSPServer::onCodeLensResolve);
+  }
   // clang-format on
 
   // Delay first profile until we've finished warming up.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to