hokein created this revision.
hokein added a reviewer: sammccall.
Herald added subscribers: kadircet, arphaman, jkorous, MaskRay, ilya-biryukov.
Herald added a project: clang.

"prepareRename" request is added in LSP v3.12.0.

No test yet, but want some early feedback.

Unfortunately, due to the bug in VSCode LSP[1], the rename dialog still
shows up when we return null in prepareRename. The fix is not released
in the latest version.

[1]: https://github.com/microsoft/vscode-languageserver-node/issues/447


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D63126

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/refactor/Rename.cpp
  clang-tools-extra/clangd/refactor/Rename.h

Index: clang-tools-extra/clangd/refactor/Rename.h
===================================================================
--- clang-tools-extra/clangd/refactor/Rename.h
+++ clang-tools-extra/clangd/refactor/Rename.h
@@ -16,6 +16,20 @@
 namespace clang {
 namespace clangd {
 
+// Contains information about the symbol being renamed.
+struct PrepareRename {
+  /// The unqualified symbol name.
+  std::string Name;
+  /// The range of the symbol at the Pos;
+  Range NameRange;
+  /// Whether the symbol is local (e.g. function-local).
+  bool IsLocal;
+};
+
+/// Test the validity of a rename operation at a specified \Pos.
+/// Returns null if the rename operation is not valid.
+llvm::Optional<PrepareRename> prepareRenameAt(ParsedAST &AST, Position Pos);
+
 /// Renames all occurrences of the symbol at \p Pos to \p NewName.
 /// Occurrences outside the current file are not modified.
 llvm::Expected<tooling::Replacements> renameWithinFile(ParsedAST &AST,
Index: clang-tools-extra/clangd/refactor/Rename.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Rename.cpp
+++ clang-tools-extra/clangd/refactor/Rename.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "refactor/Rename.h"
+#include "AST.h"
 #include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
 #include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
 
 namespace clang {
 namespace clangd {
@@ -84,5 +86,28 @@
   return FilteredChanges;
 }
 
+static Range getTokenRange(SourceLocation Loc, const ASTContext &Ctx) {
+  SourceLocation End = Lexer::getLocForEndOfToken(
+      Loc, 0, Ctx.getSourceManager(), Ctx.getLangOpts());
+  return halfOpenToRange(Ctx.getSourceManager(),
+                         CharSourceRange::getCharRange(Loc, End));
+}
+
+llvm::Optional<PrepareRename> prepareRenameAt(ParsedAST &AST, Position Pos) {
+  ASTContext &ASTCtx = AST.getASTContext();
+  SourceLocation SourceLocationBeg = clangd::getBeginningOfIdentifier(
+      AST, Pos, AST.getSourceManager().getMainFileID());
+  const NamedDecl *D =
+      clang::tooling::getNamedDeclAt(ASTCtx, SourceLocationBeg);
+  if (!D)
+    return llvm::None;
+  PrepareRename Result;
+  Result.Name = printName(ASTCtx, *D);
+  Result.NameRange = getTokenRange(SourceLocationBeg, ASTCtx);
+  /// FIXME: we should include the TU-scoped symbols (e.g. static function).
+  Result.IsLocal = D->getParentFunctionOrMethod();
+  return Result;
+}
+
 } // 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,10 @@
 
   /// The content format that should be used for Hover requests.
   MarkupKind HoverContentFormat = MarkupKind::PlainText;
+
+  /// The client supports testing for validity of rename operations
+  /// before execution.
+  bool RenamePrepareSupport = false;
 };
 bool fromJSON(const llvm::json::Value &, ClientCapabilities &);
 
Index: clang-tools-extra/clangd/Protocol.cpp
===================================================================
--- clang-tools-extra/clangd/Protocol.cpp
+++ clang-tools-extra/clangd/Protocol.cpp
@@ -339,6 +339,10 @@
     if (!fromJSON(*OffsetEncoding, *R.offsetEncoding))
       return false;
   }
+  if (auto *Rename = O->getObject("rename")) {
+    if (auto RenameSupport = Rename->getBoolean("prepareSupport"))
+      R.RenamePrepareSupport = *RenameSupport;
+  }
   return true;
 }
 
Index: clang-tools-extra/clangd/ClangdServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdServer.h
+++ clang-tools-extra/clangd/ClangdServer.h
@@ -23,6 +23,7 @@
 #include "index/Background.h"
 #include "index/FileIndex.h"
 #include "index/Index.h"
+#include "refactor/Rename.h"
 #include "refactor/Tweak.h"
 #include "clang/Tooling/CompilationDatabase.h"
 #include "clang/Tooling/Core/Replacement.h"
@@ -219,6 +220,10 @@
                                                      PathRef File, Position Pos,
                                                      StringRef TriggerText);
 
+  /// Test the validity of a rename operation.
+  void prepareRename(PathRef File, Position Pos,
+                     Callback<llvm::Optional<PrepareRename>> CB);
+
   /// Rename all occurrences of the symbol at the \p Pos in \p File to
   /// \p NewName.
   void rename(PathRef File, Position Pos, llvm::StringRef NewName,
Index: clang-tools-extra/clangd/ClangdServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdServer.cpp
+++ clang-tools-extra/clangd/ClangdServer.cpp
@@ -270,6 +270,17 @@
   return Result;
 }
 
+void ClangdServer::prepareRename(PathRef File, Position Pos,
+                                 Callback<llvm::Optional<PrepareRename>> CB) {
+  auto Action = [Pos](Callback<llvm::Optional<PrepareRename>> CB,
+                      llvm::Expected<InputsAndAST> InpAST) {
+    if (!InpAST)
+      return CB(InpAST.takeError());
+    CB(clangd::prepareRenameAt(InpAST->AST, Pos));
+  };
+  WorkScheduler.runWithAST("PrepareRename", File, Bind(Action, std::move(CB)));
+}
+
 void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
                           Callback<std::vector<TextEdit>> CB) {
   auto Action = [Pos](Path File, std::string NewName,
Index: clang-tools-extra/clangd/ClangdLSPServer.h
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.h
+++ clang-tools-extra/clangd/ClangdLSPServer.h
@@ -92,6 +92,8 @@
   void onCommand(const ExecuteCommandParams &, Callback<llvm::json::Value>);
   void onWorkspaceSymbol(const WorkspaceSymbolParams &,
                          Callback<std::vector<SymbolInformation>>);
+  void onPrepareRename(const TextDocumentPositionParams &,
+                       Callback<llvm::Optional<Range>>);
   void onRename(const RenameParams &, Callback<WorkspaceEdit>);
   void onHover(const TextDocumentPositionParams &,
                Callback<llvm::Optional<Hover>>);
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -390,6 +390,8 @@
              }},
             {"typeHierarchyProvider", true},
         }}}};
+  if (Params.capabilities.RenamePrepareSupport)
+    Result["renameProvider"] = llvm::json::Object{{"prepareProvider", true}};
   if (NegotiatedOffsetEncoding)
     Result["offsetEncoding"] = *NegotiatedOffsetEncoding;
   Reply(std::move(Result));
@@ -525,6 +527,25 @@
           std::move(Reply)));
 }
 
+void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params,
+                                      Callback<llvm::Optional<Range>> Reply) {
+  Server->prepareRename(
+      Params.textDocument.uri.file(), Params.position,
+      Bind(
+          [](decltype(Reply) Reply,
+             llvm::Expected<llvm::Optional<PrepareRename>> R) {
+            if (!R)
+              return Reply(R.takeError());
+            if (!*R)
+              return Reply(llvm::None);
+
+            Range Result;
+            Result = (*R)->NameRange;
+            Reply(std::move(Result));
+          },
+          std::move(Reply)));
+}
+
 void ClangdLSPServer::onRename(const RenameParams &Params,
                                Callback<WorkspaceEdit> Reply) {
   Path File = Params.textDocument.uri.file();
@@ -960,6 +981,7 @@
   MsgHandler->bind("textDocument/declaration", &ClangdLSPServer::onGoToDeclaration);
   MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference);
   MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader);
+  MsgHandler->bind("textDocument/prepareRename", &ClangdLSPServer::onPrepareRename);
   MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename);
   MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover);
   MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to