[clang-tools-extra] [clangd] Implement simple folding of preprocessor branches (PR #80592)

Ruihua Dong via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 22 09:26:14 PST 2024


https://github.com/144026 updated https://github.com/llvm/llvm-project/pull/80592

>From 01a9cefb0c8400a5b8f1bab73f6307e5ff5e62c1 Mon Sep 17 00:00:00 2001
From: Ruihua Dong <dongruihua.drh at alibaba-inc.com>
Date: Sun, 4 Feb 2024 16:39:22 +0800
Subject: [PATCH] [clangd] Implement simple folding of preprocessor branches

Extract directive branches information from DirectiveTree, fold branches
that don't end with eof.

Fixes https://github.com/clangd/clangd/issues/1661
---
 .../clangd/SemanticSelection.cpp              | 18 +++++++
 .../include/clang-pseudo/DirectiveTree.h      |  3 ++
 .../pseudo/lib/DirectiveTree.cpp              | 54 +++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp
index 3d687173b2be99..d2aefb51fd49ac 100644
--- a/clang-tools-extra/clangd/SemanticSelection.cpp
+++ b/clang-tools-extra/clangd/SemanticSelection.cpp
@@ -220,6 +220,24 @@ getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
   auto EndPosition = [&](const pseudo::Token &T) {
     return offsetToPosition(Code, EndOffset(T));
   };
+
+  // Preprocessor directives
+  auto PPRanges = pseudo::pairDirectiveRanges(DirectiveStructure, OrigStream);
+  for (const auto &R : PPRanges) {
+    auto BTok = OrigStream.tokens()[R.Begin];
+    auto ETok = OrigStream.tokens()[R.End];
+    if (ETok.Kind == tok::eof)
+      continue;
+    if (BTok.Line >= ETok.Line)
+      continue;
+
+    Position Start = EndPosition(BTok);
+    Position End = StartPosition(ETok);
+    if (LineFoldingOnly)
+      End.line--;
+    AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
+  }
+
   auto Tokens = ParseableStream.tokens();
   // Brackets.
   for (const auto &Tok : Tokens) {
diff --git a/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h b/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h
index 2b6cb63297915d..2e5f007f94a764 100644
--- a/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h
+++ b/clang-tools-extra/pseudo/include/clang-pseudo/DirectiveTree.h
@@ -124,6 +124,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &,
 /// The choices are stored in Conditional::Taken nodes.
 void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code);
 
+std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
+                                              const TokenStream &Code);
+
 } // namespace pseudo
 } // namespace clang
 
diff --git a/clang-tools-extra/pseudo/lib/DirectiveTree.cpp b/clang-tools-extra/pseudo/lib/DirectiveTree.cpp
index 9e853e46edc232..4882f018906548 100644
--- a/clang-tools-extra/pseudo/lib/DirectiveTree.cpp
+++ b/clang-tools-extra/pseudo/lib/DirectiveTree.cpp
@@ -353,5 +353,59 @@ TokenStream DirectiveTree::stripDirectives(const TokenStream &In) const {
   return Out;
 }
 
+namespace {
+class RangePairer {
+  std::vector<Token::Range> &Ranges;
+
+public:
+  RangePairer(std::vector<Token::Range> &Ranges) : Ranges(Ranges) {}
+
+  void walk(const DirectiveTree &T) {
+    for (const auto &C : T.Chunks)
+      std::visit(*this, C);
+  }
+
+  void operator()(const DirectiveTree::Code &C) {}
+
+  void operator()(const DirectiveTree::Directive &) {}
+
+  void operator()(const DirectiveTree::Conditional &C) {
+    Token::Range Range;
+    Token::Index Last;
+    auto First = true;
+    for (const auto &B : C.Branches) {
+      if (First) {
+        First = false;
+      } else {
+        Range = {Last, B.first.Tokens.Begin};
+        Ranges.push_back(Range);
+      }
+      Last = B.first.Tokens.Begin;
+    }
+    Range = {Last, C.End.Tokens.Begin};
+    Ranges.push_back(Range);
+
+    for (const auto &B : C.Branches)
+      walk(B.second);
+  }
+};
+} // namespace
+
+std::vector<Token::Range> pairDirectiveRanges(const DirectiveTree &Tree,
+                                              const TokenStream &Code) {
+  std::vector<Token::Range> Ranges;
+  RangePairer(Ranges).walk(Tree);
+
+  // Transform paired ranges to start with last token in its logical line
+  for (auto &R : Ranges) {
+    const Token *Tok = &Code.tokens()[R.Begin + 1];
+    while (Tok->Kind != tok::eof && !Tok->flag(LexFlags::StartsPPLine))
+      ++Tok;
+    Tok = Tok - 1;
+    R.Begin = Tok->OriginalIndex;
+  }
+  return std::move(Ranges);
+}
+
 } // namespace pseudo
 } // namespace clang



More information about the cfe-commits mailing list