diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1776,6 +1776,10 @@ unsigned startCharacter; unsigned endLine = 0; unsigned endCharacter; + + const static llvm::StringLiteral REGION_KIND; + const static llvm::StringLiteral COMMENT_KIND; + const static llvm::StringLiteral IMPORT_KIND; std::string kind; }; llvm::json::Value toJSON(const FoldingRange &Range); diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp --- a/clang-tools-extra/clangd/Protocol.cpp +++ b/clang-tools-extra/clangd/Protocol.cpp @@ -1443,6 +1443,10 @@ return O && O.map("textDocument", R.textDocument); } +const llvm::StringLiteral FoldingRange::REGION_KIND = "region"; +const llvm::StringLiteral FoldingRange::COMMENT_KIND = "comment"; +const llvm::StringLiteral FoldingRange::IMPORT_KIND = "import"; + llvm::json::Value toJSON(const FoldingRange &Range) { llvm::json::Object Result{ {"startLine", Range.startLine}, diff --git a/clang-tools-extra/clangd/SemanticSelection.cpp b/clang-tools-extra/clangd/SemanticSelection.cpp --- a/clang-tools-extra/clangd/SemanticSelection.cpp +++ b/clang-tools-extra/clangd/SemanticSelection.cpp @@ -22,6 +22,7 @@ #include "clang/Tooling/Syntax/TokenBufferTokenManager.h" #include "clang/Tooling/Syntax/Tree.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include @@ -173,16 +174,16 @@ return collectFoldingRanges(SyntaxTree, TM); } -// FIXME(kirillbobyrev): Collect comments, PP conditional regions, includes and -// other code regions (e.g. public/private/protected sections of classes, -// control flow statement bodies). +// FIXME( usaxena95): Collect PP conditional regions, includes and other code +// regions (e.g. public/private/protected sections of classes, control flow +// statement bodies). // Related issue: https://github.com/clangd/clangd/issues/310 llvm::Expected> getFoldingRanges(const std::string &Code) { - auto OrigStream = clang::pseudo::lex(Code, clang::pseudo::genericLangOpts()); + auto OrigStream = pseudo::lex(Code, clang::pseudo::genericLangOpts()); - auto DirectiveStructure = clang::pseudo::DirectiveTree::parse(OrigStream); - clang::pseudo::chooseConditionalBranches(DirectiveStructure, OrigStream); + auto DirectiveStructure = pseudo::DirectiveTree::parse(OrigStream); + pseudo::chooseConditionalBranches(DirectiveStructure, OrigStream); // FIXME: Provide ranges in the disabled-PP regions as well. auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream); @@ -191,26 +192,58 @@ pseudo::pairBrackets(ParseableStream); std::vector Result; - for (const auto &Tok : ParseableStream.tokens()) { + auto ToFoldingRange = [](Position Start, Position End, + llvm::StringLiteral Kind) { + FoldingRange FR; + FR.startLine = Start.line; + FR.startCharacter = Start.character; + FR.endLine = End.line; + FR.endCharacter = End.character; + FR.kind = Kind.str(); + return FR; + }; + auto OriginalToken = [&](const pseudo::Token &T) { + return OrigStream.tokens()[T.OriginalIndex]; + }; + auto StartOffset = [&](const pseudo::Token &T) { + return OriginalToken(T).text().data() - Code.data(); + }; + auto StartPosition = [&](const pseudo::Token &T) { + return offsetToPosition(Code, StartOffset(T)); + }; + auto EndPosition = [&](const pseudo::Token &T) { + return offsetToPosition(Code, StartOffset(T) + OriginalToken(T).Length); + }; + auto Tokens = ParseableStream.tokens(); + // Brackets. + for (const auto &Tok : Tokens) { if (auto *Paired = Tok.pair()) { // Process only token at the start of the range. Avoid ranges on a single // line. if (Tok.Line < Paired->Line) { - Position Start = offsetToPosition( - Code, - OrigStream.tokens()[Tok.OriginalIndex].text().data() - Code.data()); - Position End = offsetToPosition( - Code, OrigStream.tokens()[Paired->OriginalIndex].text().data() - - Code.data()); - FoldingRange FR; - FR.startLine = Start.line; - FR.startCharacter = Start.character + 1; - FR.endLine = End.line; - FR.endCharacter = End.character; - Result.push_back(FR); + Position Start = offsetToPosition(Code, 1 + StartOffset(Tok)); + Position End = StartPosition(*Paired); + Result.push_back(ToFoldingRange(Start, End, FoldingRange::REGION_KIND)); } } } + // Multi-line comments. + for (const auto *T = Tokens.begin(); T != Tokens.end();) { + if (T->Kind != tok::comment) { + T++; + continue; + } + Position Start = StartPosition(*T); + Position LastCommentEnd = EndPosition(*T); + while (T != Tokens.end() && T->Kind == tok::comment && + StartPosition(*T).line <= LastCommentEnd.line + 1) { + LastCommentEnd = EndPosition(*T); + T++; + } + if (Start.line < LastCommentEnd.line) + Result.push_back( + ToFoldingRange(Start, LastCommentEnd, FoldingRange::COMMENT_KIND)); + } return Result; } diff --git a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp @@ -335,6 +335,27 @@ ]]} \ ]]}; )cpp", + R"cpp( + [[/* Multi + * line + * comment + */]] + )cpp", + R"cpp( + [[// Comment + // 1]] + + [[// Comment + // 2]] + + // No folding for single line comment. + + [[/* comment 3 + */]] + + [[/* comment 4 + */]] + )cpp", }; for (const char *Test : Tests) { auto T = Annotations(Test);