diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp index dd7116e619e6d..57e7ff8b97c65 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 Token &T) { return offsetToPosition(Code, EndOffset(T)); }; + + // Preprocessor directives + auto PPRanges = 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/clangd/support/DirectiveTree.cpp b/clang-tools-extra/clangd/support/DirectiveTree.cpp index 7ea08add7a107..25ef1dcffd750 100644 --- a/clang-tools-extra/clangd/support/DirectiveTree.cpp +++ b/clang-tools-extra/clangd/support/DirectiveTree.cpp @@ -356,5 +356,59 @@ TokenStream DirectiveTree::stripDirectives(const TokenStream &In) const { return Out; } +namespace { +class RangePairer { + std::vector &Ranges; + +public: + RangePairer(std::vector &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 pairDirectiveRanges(const DirectiveTree &Tree, + const TokenStream &Code) { + std::vector 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 Ranges; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/support/DirectiveTree.h b/clang-tools-extra/clangd/support/DirectiveTree.h index 34f5a888863f2..373af322bca0c 100644 --- a/clang-tools-extra/clangd/support/DirectiveTree.h +++ b/clang-tools-extra/clangd/support/DirectiveTree.h @@ -124,6 +124,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &, /// The choices are stored in Conditional::Taken nodes. void chooseConditionalBranches(DirectiveTree &, const TokenStream &Code); +/// Pairs preprocessor conditional directives and computes their token ranges. +std::vector pairDirectiveRanges(const DirectiveTree &Tree, + const TokenStream &Code); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp index 7faef6f95d8f9..b4249590c7f66 100644 --- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -370,6 +370,26 @@ TEST(FoldingRanges, PseudoParserWithoutLineFoldings) { //[[ foo /* bar */]] )cpp", + R"cpp( + //Ignore non-conditional directives + #define A 1 + + void func() {[[ + int Variable = 100; + + #ifdef FOO[[ + Variable = 1; + ]]#else[[ + Variable = 2; + //handle nested directives + #if 1[[ + Variable = 3; + ]]#endif + ]]#endif + + + ]]} + )cpp", }; for (const char *Test : Tests) { auto T = Annotations(Test);