Trass3r created this revision.
Herald added subscribers: usaxena95, kadircet, arphaman, javed.absar, mgorny.
Herald added a project: All.
Trass3r edited the summary of this revision.
Trass3r edited projects, added clang-tools-extra; removed All.
Trass3r edited subscribers, added: sammccall; removed: mgorny, javed.absar, 
arphaman, usaxena95.
Herald added a project: All.
Trass3r added a reviewer: nridge.
Trass3r edited the summary of this revision.
Trass3r added a subscriber: lightmelodies.
Trass3r updated this revision to Diff 450421.
Trass3r added a comment.
Trass3r updated this revision to Diff 450422.
Trass3r published this revision for review.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.

"base" refs are only useful on class forward decls
but for some reasons this still shows up on dtor implementations


Trass3r added a comment.

squashed



================
Comment at: clang-tools-extra/clangd/CodeLens.cpp:86
+    }
+    // bases codelens is only useful on forward decls
+    if (!CXXRD->isThisDeclarationADefinition()) {
----------------
they still show up on dtor implementations iirc


Let's gather some feedback on the code changes for codelens support, they've 
been rotting since 2 years already.
I started on it back then and it got taken further by @lightmelodies.
Some small issues remain: 
https://github.com/clangd/clangd/issues/442#issuecomment-1125056812


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131295

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/CodeLens.cpp
  clang-tools-extra/clangd/CodeLens.h
  clang-tools-extra/clangd/Protocol.cpp
  clang-tools-extra/clangd/Protocol.h
  clang-tools-extra/clangd/TUScheduler.h
  clang-tools-extra/clangd/tool/ClangdMain.cpp

Index: clang-tools-extra/clangd/tool/ClangdMain.cpp
===================================================================
--- clang-tools-extra/clangd/tool/ClangdMain.cpp
+++ clang-tools-extra/clangd/tool/ClangdMain.cpp
@@ -341,6 +341,11 @@
     Hidden,
 };
 
+opt<bool> EnableCodeLens{
+    "code-lens", cat(Features), desc("Enable preview of CodeLens feature"),
+    init(true),  Hidden,
+};
+
 opt<unsigned> WorkerThreadsCount{
     "j",
     cat(Misc),
@@ -905,6 +910,7 @@
   Opts.AsyncThreadsCount = WorkerThreadsCount;
   Opts.FoldingRanges = FoldingRanges;
   Opts.MemoryCleanup = getMemoryCleanupFunction();
+  Opts.CodeLens = EnableCodeLens;
 
   Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
   Opts.CodeComplete.Limit = LimitResults;
Index: clang-tools-extra/clangd/TUScheduler.h
===================================================================
--- clang-tools-extra/clangd/TUScheduler.h
+++ clang-tools-extra/clangd/TUScheduler.h
@@ -62,7 +62,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 = 32;
 };
 
 /// 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
@@ -222,6 +222,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 &);
 
@@ -999,6 +1000,9 @@
   const static llvm::StringLiteral QUICKFIX_KIND;
   const static llvm::StringLiteral REFACTOR_KIND;
   const static llvm::StringLiteral INFO_KIND;
+  /// This action should be implemented by client,
+  /// because we can not call 'editor.action.showReferences' directly.
+  const static llvm::StringLiteral SHOW_REFERENCES;
 
   /// The diagnostics that this code action resolves.
   llvm::Optional<std::vector<Diagnostic>> diagnostics;
@@ -1845,6 +1849,33 @@
 llvm::json::Value toJSON(const ASTNode &);
 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const ASTNode &);
 
+/// https://microsoft.github.io/language-server-protocol/specification#textDocument_codeLens
+struct CodeLensResolveData {
+  std::string uri;
+};
+bool fromJSON(const llvm::json::Value &, CodeLensResolveData &,
+              llvm::json::Path);
+llvm::json::Value toJSON(const CodeLensResolveData &A);
+
+struct CodeLensArgument {
+  std::string uri;
+  Position position;
+  std::vector<Location> locations;
+};
+llvm::json::Value toJSON(const CodeLensArgument &A);
+
+struct CodeLensParams : DocumentSymbolParams {};
+
+struct CodeLens {
+  // CodeLens range.
+  Range range;
+  // CodeLens command.
+  llvm::Optional<Command> command;
+  // CodeLens resolve data.
+  llvm::Optional<CodeLensResolveData> data;
+};
+bool fromJSON(const llvm::json::Value &, CodeLens &, llvm::json::Path);
+llvm::json::Value toJSON(const CodeLens &);
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -150,6 +150,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},
@@ -796,6 +802,8 @@
 const llvm::StringLiteral CodeAction::QUICKFIX_KIND = "quickfix";
 const llvm::StringLiteral CodeAction::REFACTOR_KIND = "refactor";
 const llvm::StringLiteral CodeAction::INFO_KIND = "info";
+const llvm::StringLiteral CodeAction::SHOW_REFERENCES =
+    "clangd.action.showReferences";
 
 llvm::json::Value toJSON(const CodeAction &CA) {
   auto CodeAction = llvm::json::Object{{"title", CA.title}};
@@ -1510,5 +1518,36 @@
   return OS;
 }
 
+bool fromJSON(const llvm::json::Value &Params, CodeLensResolveData &R,
+              llvm::json::Path P) {
+  llvm::json::ObjectMapper O(Params, P);
+  return O && O.map("uri", R.uri);
+}
+
+llvm::json::Value toJSON(const CodeLensResolveData &P) {
+  llvm::json::Object O{{"uri", P.uri}};
+  return std::move(O);
+}
+
+llvm::json::Value toJSON(const CodeLensArgument &P) {
+  llvm::json::Object O{
+      {"uri", P.uri}, {"position", P.position}, {"locations", P.locations}};
+  return std::move(O);
+}
+
+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);
+}
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/CodeLens.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/CodeLens.h
@@ -0,0 +1,27 @@
+//===--- CodeLens.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_CODELENS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODELENS_H
+
+#include "ParsedAST.h"
+#include "Protocol.h"
+
+namespace clang {
+namespace clangd {
+llvm::Expected<std::vector<CodeLens>>
+getDocumentCodeLens(ParsedAST &AST, const SymbolIndex *Index, uint32_t Limit,
+                    PathRef Path);
+
+llvm::Expected<CodeLens> resolveCodeLens(ParsedAST &AST, const CodeLens &Params,
+                                         uint32_t Limit,
+                                         const SymbolIndex *Index,
+                                         PathRef Path);
+} // namespace clangd
+} // namespace clang
+#endif
\ No newline at end of file
Index: clang-tools-extra/clangd/CodeLens.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/CodeLens.cpp
@@ -0,0 +1,168 @@
+//===--- CodeLens.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 "CodeLens.h"
+#include "AST.h"
+#include "FindSymbols.h"
+#include "XRefs.h"
+#include "support/Logger.h"
+
+namespace clang {
+namespace clangd {
+llvm::Optional<Location> declToLocation(const Decl *D) {
+  ASTContext &Ctx = D->getASTContext();
+  auto &SM = Ctx.getSourceManager();
+  SourceLocation NameLoc = nameLocation(*D, Ctx.getSourceManager());
+  auto FilePath =
+      getCanonicalPath(SM.getFileEntryForID(SM.getFileID(NameLoc)), SM);
+  auto TUPath = getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
+  if (!FilePath || !TUPath)
+    return llvm::None; // Not useful without a uri.
+
+  Position NameBegin = sourceLocToPosition(SM, NameLoc);
+  Position NameEnd = sourceLocToPosition(
+      SM, Lexer::getLocForEndOfToken(NameLoc, 0, SM, Ctx.getLangOpts()));
+  return Location{URIForFile::canonicalize(*FilePath, *TUPath),
+                  {NameBegin, NameEnd}};
+}
+
+std::vector<Location> lookupIndex(const SymbolIndex *Index, uint32_t Limit,
+                                  PathRef Path, Decl *D, RelationKind R) {
+  std::vector<Location> Results;
+  if (!Index)
+    return Results;
+  auto ID = getSymbolID(D);
+  if (!ID)
+    return Results;
+  RelationsRequest Req;
+  Req.Subjects.insert(ID);
+  Req.Limit = Limit;
+  Req.Predicate = R;
+  Index->relations(Req, [&](const SymbolID &Subject, const Symbol &Object) {
+    if (auto Loc = indexToLSPLocation(Object.CanonicalDeclaration, Path)) {
+      Results.emplace_back(std::move(*Loc));
+    }
+  });
+  return Results;
+}
+
+void traverseDecl(ParsedAST &AST, const SymbolIndex *Index, uint32_t Limit,
+                  PathRef Path, Decl *D, std::vector<CodeLens> &Results) {
+  auto &SM = AST.getSourceManager();
+  // Skip symbols which do not originate from the main file.
+  if (!isInsideMainFile(D->getLocation(), SM))
+    return;
+  if (D->isImplicit() || !isa<NamedDecl>(D) || D->getLocation().isMacroID())
+    return;
+
+  if (auto *Templ = llvm::dyn_cast<TemplateDecl>(D)) {
+    if (auto *TD = Templ->getTemplatedDecl())
+      D = TD;
+  };
+  auto Location = D->getLocation();
+  Range Range = {
+      sourceLocToPosition(SM, Location),
+      sourceLocToPosition(
+          SM, Lexer::getLocForEndOfToken(Location, 0, SM, AST.getLangOpts()))};
+
+  // Namspaces are not indexed, so it's meaningless to provide codelens.
+  if (!isa<NamespaceDecl, NamespaceAliasDecl>(D)) {
+    CodeLensResolveData Data;
+    Data.uri = std::string(Path);
+    Results.emplace_back(CodeLens{Range, None, Data});
+  }
+
+  // handle inheritance codelens directly
+  CodeLensArgument Sub, Super;
+  if (auto *CXXRD = dyn_cast<CXXRecordDecl>(D)) {
+    if (!CXXRD->isEffectivelyFinal()) {
+      Sub.locations = lookupIndex(Index, Limit, Path, D, RelationKind::BaseOf);
+    }
+    // bases codelens is only useful on forward decls
+    if (!CXXRD->isThisDeclarationADefinition()) {
+      for (const auto *P : typeParents(CXXRD)) {
+        if (auto Loc = declToLocation(P->getCanonicalDecl()))
+          Super.locations.emplace_back(*Loc);
+      }
+    }
+  } else if (auto *CXXMD = dyn_cast<CXXMethodDecl>(D)) {
+    if (CXXMD->isVirtual()) {
+      Sub.locations =
+          lookupIndex(Index, Limit, Path, D, RelationKind::OverriddenBy);
+    }
+    for (const auto *P : CXXMD->overridden_methods()) {
+      if (auto Loc = declToLocation(P->getCanonicalDecl()))
+        Super.locations.emplace_back(*Loc);
+    }
+  }
+
+  if (auto Count = Super.locations.size()) {
+    Super.position = Range.start;
+    Super.uri = std::string(Path);
+    Command Cmd;
+    Cmd.command = std::string(CodeAction::SHOW_REFERENCES);
+    Cmd.title = std::to_string(Count) + " base(s)";
+    Cmd.argument = std::move(Super);
+    Results.emplace_back(CodeLens{Range, std::move(Cmd), None});
+  }
+
+  if (auto Count = Sub.locations.size()) {
+    Sub.position = Range.start;
+    Sub.uri = std::string(Path);
+    Command Cmd;
+    Cmd.command = std::string(CodeAction::SHOW_REFERENCES);
+    Cmd.title = std::to_string(Count) + " derived";
+    Cmd.argument = std::move(Sub);
+    Results.emplace_back(CodeLens{Range, std::move(Cmd), None});
+  }
+
+  // Skip symbols inside function body.
+  if (isa<FunctionDecl>(D)) {
+    return;
+  }
+
+  if (auto *Scope = dyn_cast<DeclContext>(D)) {
+    for (auto *C : Scope->decls())
+      traverseDecl(AST, Index, Limit, Path, C, Results);
+  }
+}
+
+llvm::Expected<std::vector<CodeLens>>
+getDocumentCodeLens(ParsedAST &AST, const SymbolIndex *Index, uint32_t Limit,
+                    PathRef Path) {
+  std::vector<CodeLens> Results;
+  Limit = Limit ? Limit : std::numeric_limits<uint32_t>::max();
+  for (auto &TopLevel : AST.getLocalTopLevelDecls())
+    traverseDecl(AST, Index, Limit, Path, TopLevel, Results);
+  return Results;
+}
+
+llvm::Expected<CodeLens> resolveCodeLens(ParsedAST &AST, const CodeLens &Params,
+                                         uint32_t Limit,
+                                         const SymbolIndex *Index,
+                                         PathRef Path) {
+  Command Cmd;
+  Cmd.command = std::string(CodeAction::SHOW_REFERENCES);
+  Position Pos = Params.range.start;
+  if (Params.data) {
+    CodeLensArgument Arg;
+    Arg.uri = std::string(Path);
+    Arg.position = Pos;
+    auto Refs = findReferences(AST, Pos, Limit, Index).References;
+    Arg.locations.reserve(Refs.size());
+    for (auto &Ref : Refs) {
+      Arg.locations.emplace_back(std::move(Ref.Loc));
+    }
+    Cmd.title = std::to_string(Refs.size()) + " ref(s)";
+    Cmd.argument = std::move(Arg);
+    return CodeLens{Params.range, std::move(Cmd), None};
+  }
+  return error("failed to resolve codelens");
+}
+} // namespace clangd
+} // namespace clang
\ No newline at end of file
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -164,6 +164,9 @@
     /// Enable preview of FoldingRanges feature.
     bool FoldingRanges = false;
 
+    /// Enable preview of CodeLens feature.
+    bool CodeLens = false;
+
     FeatureModuleSet *FeatureModules = nullptr;
     /// If true, use the dirty buffer contents when building Preambles.
     bool UseDirtyHeaders = false;
@@ -358,6 +361,12 @@
   void getAST(PathRef File, llvm::Optional<Range> R,
               Callback<llvm::Optional<ASTNode>> CB);
 
+  /// CodeLenses.
+  void provideCodeLens(PathRef File, uint32_t Limit,
+                       Callback<std::vector<CodeLens>> CB);
+  void resolveCodeLens(const CodeLens &Params, uint32_t Limit,
+                       Callback<CodeLens> CB);
+
   /// Runs an arbitrary action that has access to the AST of the specified file.
   /// The action will execute on one of ClangdServer's internal threads.
   /// The AST is only valid for the duration of the callback.
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -8,6 +8,7 @@
 
 #include "ClangdServer.h"
 #include "CodeComplete.h"
+#include "CodeLens.h"
 #include "Config.h"
 #include "Diagnostics.h"
 #include "DumpAST.h"
@@ -364,7 +365,7 @@
 
 void ClangdServer::removeDocument(PathRef File) {
   DraftMgr.removeDraft(File);
-  WorkScheduler->remove(File);
+  // WorkScheduler->remove(File);
 }
 
 void ClangdServer::codeComplete(PathRef File, Position Pos,
@@ -996,6 +997,31 @@
   WorkScheduler->runWithAST("Diagnostics", File, std::move(Action));
 }
 
+void ClangdServer::provideCodeLens(PathRef File, uint32_t Limit,
+                                   Callback<std::vector<CodeLens>> CB) {
+  auto Action = [CB = std::move(CB), File = File.str(), Limit,
+                 this](llvm::Expected<InputsAndAST> InpAST) mutable {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::getDocumentCodeLens(InpAST->AST, Index, Limit, 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->uri;
+  auto Action = [CB = std::move(CB), File, 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);
+}
+
 llvm::StringMap<TUScheduler::FileStats> ClangdServer::fileStats() const {
   return WorkScheduler->fileStats();
 }
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -166,6 +166,10 @@
   void onCommandApplyEdit(const WorkspaceEdit &, Callback<llvm::json::Value>);
   void onCommandApplyTweak(const TweakArgs &, Callback<llvm::json::Value>);
 
+  /// CodeLens
+  void onCodeLens(const CodeLensParams &, Callback<std::vector<CodeLens>>);
+  void onCodeLensResolve(const CodeLens &, Callback<CodeLens>);
+
   /// Outgoing LSP calls.
   LSPBinder::OutgoingMethod<ApplyWorkspaceEditParams,
                             ApplyWorkspaceEditResponse>
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -625,6 +625,11 @@
        {"capabilities", std::move(ServerCaps)}}};
   if (Opts.Encoding)
     Result["offsetEncoding"] = *Opts.Encoding;
+  if (Opts.CodeLens)
+    Result.getObject("capabilities")
+        ->insert({"codeLensProvider", llvm::json::Object{
+                                          {"resolveProvider", true},
+                                      }});
   Reply(std::move(Result));
 
   // Apply settings after we're fully initialized.
@@ -1463,6 +1468,30 @@
   Reply(std::move(MT));
 }
 
+void ClangdLSPServer::onCodeLens(const CodeLensParams &Params,
+                                 Callback<std::vector<CodeLens>> Reply) {
+  URIForFile FileURI = Params.textDocument.uri;
+  Server->provideCodeLens(
+      FileURI.file(), Opts.ReferencesLimit,
+      [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.ReferencesLimit,
+      [Reply = std::move(Reply)](llvm::Expected<CodeLens> CodeLens) mutable {
+        if (!CodeLens)
+          return Reply(CodeLens.takeError());
+        return Reply(std::move(*CodeLens));
+      });
+}
+
 void ClangdLSPServer::onAST(const ASTParams &Params,
                             Callback<llvm::Optional<ASTNode>> CB) {
   Server->getAST(Params.textDocument.uri.file(), Params.range, std::move(CB));
@@ -1536,7 +1565,10 @@
     Bind.method("textDocument/foldingRange", this, &ClangdLSPServer::onFoldingRange);
   Bind.command(ApplyFixCommand, this, &ClangdLSPServer::onCommandApplyEdit);
   Bind.command(ApplyTweakCommand, this, &ClangdLSPServer::onCommandApplyTweak);
-
+  if (Opts.CodeLens) {
+    Bind.method("textDocument/codeLens",this, &ClangdLSPServer::onCodeLens);
+    Bind.method("codeLens/resolve",this, &ClangdLSPServer::onCodeLensResolve);
+  }
   ApplyWorkspaceEdit = Bind.outgoingMethod("workspace/applyEdit");
   PublishDiagnostics = Bind.outgoingNotification("textDocument/publishDiagnostics");
   ShowMessage = Bind.outgoingNotification("window/showMessage");
Index: clang-tools-extra/clangd/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/CMakeLists.txt
+++ clang-tools-extra/clangd/CMakeLists.txt
@@ -59,6 +59,7 @@
   ClangdServer.cpp
   CodeComplete.cpp
   CodeCompletionStrings.cpp
+  CodeLens.cpp
   CollectMacros.cpp
   CompileCommands.cpp
   Compiler.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to