[clang-tools-extra] a11ec00 - FoldingRanges: Handle LineFoldingsOnly clients.

Utkarsh Saxena via cfe-commits cfe-commits at lists.llvm.org
Mon Aug 29 10:03:59 PDT 2022


Author: Utkarsh Saxena
Date: 2022-08-29T19:03:48+02:00
New Revision: a11ec00afea327419ec1ab7c78ba6818d6c5bbf7

URL: https://github.com/llvm/llvm-project/commit/a11ec00afea327419ec1ab7c78ba6818d6c5bbf7
DIFF: https://github.com/llvm/llvm-project/commit/a11ec00afea327419ec1ab7c78ba6818d6c5bbf7.diff

LOG: FoldingRanges: Handle LineFoldingsOnly clients.

Do not fold the endline which contains tokens after the end of range.

Differential Revision: https://reviews.llvm.org/D131154

Added: 
    

Modified: 
    clang-tools-extra/clangd/ClangdLSPServer.cpp
    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/SemanticSelection.cpp
    clang-tools-extra/clangd/SemanticSelection.h
    clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index 66bda05d00f71..e0129d99d9706 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -514,6 +514,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
       Params.capabilities.HierarchicalDocumentSymbol;
   SupportFileStatus = Params.initializationOptions.FileStatus;
   HoverContentFormat = Params.capabilities.HoverContentFormat;
+  Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
   SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
   if (Params.capabilities.WorkDoneProgress)
     BackgroundIndexProgressState = BackgroundIndexProgress::Empty;

diff  --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 2997823e75fc3..5c4263d0ee6cb 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -177,6 +177,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
       DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr),
       ClangTidyProvider(Opts.ClangTidyProvider),
       UseDirtyHeaders(Opts.UseDirtyHeaders),
+      LineFoldingOnly(Opts.LineFoldingOnly),
       PreambleParseForwardingFunctions(Opts.PreambleParseForwardingFunctions),
       WorkspaceRoot(Opts.WorkspaceRoot),
       Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate
@@ -855,8 +856,9 @@ void ClangdServer::foldingRanges(llvm::StringRef File,
     return CB(llvm::make_error<LSPError>(
         "trying to compute folding ranges for non-added document",
         ErrorCode::InvalidParams));
-  auto Action = [CB = std::move(CB), Code = std::move(*Code)]() mutable {
-    CB(clangd::getFoldingRanges(Code));
+  auto Action = [LineFoldingOnly = LineFoldingOnly, CB = std::move(CB),
+                 Code = std::move(*Code)]() mutable {
+    CB(clangd::getFoldingRanges(Code, LineFoldingOnly));
   };
   // We want to make sure folding ranges are always available for all the open
   // files, hence prefer runQuick to not wait for operations on other files.

diff  --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index b14391aab6680..6be0c930ded47 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -164,6 +164,9 @@ class ClangdServer {
     /// Enable preview of FoldingRanges feature.
     bool FoldingRanges = false;
 
+    // Whether the client supports folding only complete lines.
+    bool LineFoldingOnly = false;
+
     FeatureModuleSet *FeatureModules = nullptr;
     /// If true, use the dirty buffer contents when building Preambles.
     bool UseDirtyHeaders = false;
@@ -429,6 +432,9 @@ class ClangdServer {
 
   bool UseDirtyHeaders = false;
 
+  // Whether the client supports folding only complete lines.
+  bool LineFoldingOnly = false;
+
   bool PreambleParseForwardingFunctions = false;
 
   // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)

diff  --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 0109fa01e34e5..8b833c0f3f8b2 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -391,6 +391,10 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
         }
       }
     }
+    if (auto *Folding = TextDocument->getObject("foldingRange")) {
+      if (auto LineFolding = Folding->getBoolean("lineFoldingOnly"))
+        R.LineFoldingOnly = *LineFolding;
+    }
     if (auto *Rename = TextDocument->getObject("rename")) {
       if (auto RenameSupport = Rename->getBoolean("prepareSupport"))
         R.RenamePrepareSupport = *RenameSupport;

diff  --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index 9a68a33cfb334..aa7c753a9f916 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -437,6 +437,12 @@ struct ClientCapabilities {
   /// textDocument.signatureHelp
   bool HasSignatureHelp = false;
 
+  /// Client signals that it only supports folding complete lines.
+  /// Client will ignore specified `startCharacter` and `endCharacter`
+  /// properties in a FoldingRange.
+  /// textDocument.foldingRange.lineFoldingOnly
+  bool LineFoldingOnly = false;
+
   /// Client supports processing label offsets instead of a simple label string.
   /// textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport
   bool OffsetsInSignatureHelp = false;

diff  --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp
index b82df8ebc2595..b00f9acf696c2 100644
--- a/clang-tools-extra/clangd/SemanticSelection.cpp
+++ b/clang-tools-extra/clangd/SemanticSelection.cpp
@@ -179,7 +179,7 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) {
 // statement bodies).
 // Related issue: https://github.com/clangd/clangd/issues/310
 llvm::Expected<std::vector<FoldingRange>>
-getFoldingRanges(const std::string &Code) {
+getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
   auto OrigStream = pseudo::lex(Code, clang::pseudo::genericLangOpts());
 
   auto DirectiveStructure = pseudo::DirectiveTree::parse(OrigStream);
@@ -192,15 +192,17 @@ getFoldingRanges(const std::string &Code) {
   pseudo::pairBrackets(ParseableStream);
 
   std::vector<FoldingRange> Result;
-  auto ToFoldingRange = [](Position Start, Position End,
-                           llvm::StringLiteral Kind) {
+  auto AddFoldingRange = [&](Position Start, Position End,
+                             llvm::StringLiteral Kind) {
+    if (Start.line >= End.line)
+      return;
     FoldingRange FR;
     FR.startLine = Start.line;
     FR.startCharacter = Start.character;
     FR.endLine = End.line;
     FR.endCharacter = End.character;
     FR.kind = Kind.str();
-    return FR;
+    Result.push_back(FR);
   };
   auto OriginalToken = [&](const pseudo::Token &T) {
     return OrigStream.tokens()[T.OriginalIndex];
@@ -211,8 +213,11 @@ getFoldingRanges(const std::string &Code) {
   auto StartPosition = [&](const pseudo::Token &T) {
     return offsetToPosition(Code, StartOffset(T));
   };
+  auto EndOffset = [&](const pseudo::Token &T) {
+    return StartOffset(T) + OriginalToken(T).Length;
+  };
   auto EndPosition = [&](const pseudo::Token &T) {
-    return offsetToPosition(Code, StartOffset(T) + OriginalToken(T).Length);
+    return offsetToPosition(Code, EndOffset(T));
   };
   auto Tokens = ParseableStream.tokens();
   // Brackets.
@@ -223,26 +228,43 @@ getFoldingRanges(const std::string &Code) {
       if (Tok.Line < Paired->Line) {
         Position Start = offsetToPosition(Code, 1 + StartOffset(Tok));
         Position End = StartPosition(*Paired);
-        Result.push_back(ToFoldingRange(Start, End, FoldingRange::REGION_KIND));
+        if (LineFoldingOnly)
+          End.line--;
+        AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
       }
     }
   }
+  auto IsBlockComment = [&](const pseudo::Token &T) {
+    assert(T.Kind == tok::comment);
+    return OriginalToken(T).Length >= 2 &&
+           Code.substr(StartOffset(T), 2) == "/*";
+  };
   // Multi-line comments.
-  for (const auto *T = Tokens.begin(); T != Tokens.end();) {
+  for (auto *T = Tokens.begin(); T != Tokens.end();) {
     if (T->Kind != tok::comment) {
       T++;
       continue;
     }
-    Position Start = StartPosition(*T);
-    Position LastCommentEnd = EndPosition(*T);
+    pseudo::Token *FirstComment = T;
+    // Show starting sentinals (// and /*) of the comment.
+    Position Start = offsetToPosition(Code, 2 + StartOffset(*FirstComment));
+    pseudo::Token *LastComment = T;
+    Position End = EndPosition(*T);
     while (T != Tokens.end() && T->Kind == tok::comment &&
-           StartPosition(*T).line <= LastCommentEnd.line + 1) {
-      LastCommentEnd = EndPosition(*T);
+           StartPosition(*T).line <= End.line + 1) {
+      End = EndPosition(*T);
+      LastComment = T;
       T++;
     }
-    if (Start.line < LastCommentEnd.line)
-      Result.push_back(
-          ToFoldingRange(Start, LastCommentEnd, FoldingRange::COMMENT_KIND));
+    if (IsBlockComment(*FirstComment)) {
+      if (LineFoldingOnly)
+        // Show last line of a block comment.
+        End.line--;
+      if (IsBlockComment(*LastComment))
+        // Show ending sentinal "*/" of the block comment.
+        End.character -= 2;
+    }
+    AddFoldingRange(Start, End, FoldingRange::COMMENT_KIND);
   }
   return Result;
 }

diff  --git a/clang-tools-extra/clangd/SemanticSelection.h b/clang-tools-extra/clangd/SemanticSelection.h
index 337d8d38a0e0d..dd9d3ea7e81d3 100644
--- a/clang-tools-extra/clangd/SemanticSelection.h
+++ b/clang-tools-extra/clangd/SemanticSelection.h
@@ -33,7 +33,7 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST);
 /// Returns a list of ranges whose contents might be collapsible in an editor.
 /// This version uses the pseudoparser which does not require the AST.
 llvm::Expected<std::vector<FoldingRange>>
-getFoldingRanges(const std::string &Code);
+getFoldingRanges(const std::string &Code, bool LineFoldingOnly);
 
 } // namespace clangd
 } // namespace clang

diff  --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
index de2960869b1e9..7faef6f95d8f9 100644
--- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
+++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
@@ -196,7 +196,7 @@ TEST(SemanticSelection, RunViaClangdServer) {
               ElementsAre(SourceAnnotations.range("empty")));
 }
 
-TEST(FoldingRanges, All) {
+TEST(FoldingRanges, ASTAll) {
   const char *Tests[] = {
       R"cpp(
         #define FOO int foo() {\
@@ -265,7 +265,7 @@ TEST(FoldingRanges, All) {
   }
 }
 
-TEST(FoldingRangesPseudoParser, All) {
+TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
   const char *Tests[] = {
       R"cpp(
         #define FOO int foo() {\
@@ -336,36 +336,123 @@ TEST(FoldingRangesPseudoParser, All) {
         ]]};
       )cpp",
       R"cpp(
-        [[/* Multi 
+        /*[[ Multi 
           * line
           *  comment 
-          */]]
+          ]]*/
       )cpp",
       R"cpp(
-        [[// Comment
+        //[[ Comment
         // 1]]
         
-        [[// Comment
+        //[[ Comment
         // 2]]
         
         // No folding for single line comment.
 
-        [[/* comment 3
-        */]]
+        /*[[ comment 3
+        ]]*/
 
-        [[/* comment 4
-        */]]
+        /*[[ comment 4
+        ]]*/
+
+        /*[[ foo */
+        /* bar ]]*/
+
+        /*[[ foo */
+        // baz
+        /* bar ]]*/
+
+        /*[[ foo */
+        /* bar*/
+        // baz]]
+
+        //[[ foo
+        /* bar */]]
       )cpp",
   };
   for (const char *Test : Tests) {
     auto T = Annotations(Test);
-    EXPECT_THAT(
-        gatherFoldingRanges(llvm::cantFail(getFoldingRanges(T.code().str()))),
-        UnorderedElementsAreArray(T.ranges()))
+    EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(
+                    T.code().str(), /*LineFoldingsOnly=*/false))),
+                UnorderedElementsAreArray(T.ranges()))
         << Test;
   }
 }
 
+TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
+  const char *Tests[] = {
+      R"cpp(
+        void func(int a) {[[
+            a++;]]
+        }
+      )cpp",
+      R"cpp(
+        // Always exclude last line for brackets.
+        void func(int a) {[[
+          if(a == 1) {[[
+            a++;]]
+          } else if (a == 2){[[
+            a--;]]
+          } else {  // No folding for 2 line bracketed ranges.
+          }]]
+        }
+      )cpp",
+      R"cpp(
+        /*[[ comment
+        * comment]]
+        */
+
+        /* No folding for this comment.
+        */
+
+        // No folding for this comment.
+
+        //[[ 2 single line comment.
+        // 2 single line comment.]]
+
+        //[[ >=2 line comments.
+        // >=2 line comments.
+        // >=2 line comments.]]
+
+        //[[ foo\
+        bar\
+        baz]]
+
+        /*[[ foo */
+        /* bar */]]
+        /* baz */
+
+        /*[[ foo */
+        /* bar]]
+        * This does not fold me */
+
+        //[[ foo
+        /* bar */]]
+      )cpp",
+      // FIXME: Support folding template arguments.
+      // R"cpp(
+      // template <[[typename foo, class bar]]> struct baz {};
+      // )cpp",
+
+  };
+  auto StripColumns = [](const std::vector<Range> &Ranges) {
+    std::vector<Range> Res;
+    for (Range R : Ranges) {
+      R.start.character = R.end.character = 0;
+      Res.push_back(R);
+    }
+    return Res;
+  };
+  for (const char *Test : Tests) {
+    auto T = Annotations(Test);
+    EXPECT_THAT(
+        StripColumns(gatherFoldingRanges(llvm::cantFail(
+            getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),
+        UnorderedElementsAreArray(StripColumns(T.ranges())))
+        << Test;
+  }
+}
 } // namespace
 } // namespace clangd
 } // namespace clang


        


More information about the cfe-commits mailing list