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 @@ -1766,13 +1766,20 @@ bool fromJSON(const llvm::json::Value &, FoldingRangeParams &, llvm::json::Path); +enum class FoldingRangeKind { + unspecified = 0, + region = 1, + comment = 2, + import = 3, +}; + /// Stores information about a region of code that can be folded. struct FoldingRange { unsigned startLine = 0; unsigned startCharacter; unsigned endLine = 0; unsigned endCharacter; - std::string kind; + FoldingRangeKind 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 @@ -1435,6 +1435,21 @@ return O && O.map("textDocument", R.textDocument); } +llvm::json::Value toJSON(FoldingRangeKind Kind) { + switch (Kind) { + case FoldingRangeKind::comment: + return "comment"; + case FoldingRangeKind::region: + return "region"; + case FoldingRangeKind::import: + return "import"; + case FoldingRangeKind::unspecified: + // Not part of LSP, don't serialize. + return nullptr; + } + llvm_unreachable("Unknown clang.clangd.FoldingRangeKind"); +} + llvm::json::Value toJSON(const FoldingRange &Range) { llvm::json::Object Result{ {"startLine", Range.startLine}, @@ -1444,8 +1459,10 @@ Result["startCharacter"] = Range.startCharacter; if (Range.endCharacter) Result["endCharacter"] = Range.endCharacter; - if (!Range.kind.empty()) - Result["kind"] = Range.kind; + auto Kind = toJSON(Range.kind); + if (!Kind.getAsNull()) { + Result["kind"] = std::move(Kind); + } return Result; } 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 @@ -173,16 +173,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 +191,53 @@ pseudo::pairBrackets(ParseableStream); std::vector Result; - for (const auto &Tok : ParseableStream.tokens()) { + auto ToFoldingRange = [](Position Start, Position End, + FoldingRangeKind Kind) { + FoldingRange FR; + FR.startLine = Start.line; + FR.startCharacter = Start.character; + FR.endLine = End.line; + FR.endCharacter = End.character; + FR.kind = Kind; + 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 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 = offsetToPosition(Code, StartOffset(*Paired)); + Result.push_back(ToFoldingRange(Start, End, FoldingRangeKind::region)); } } } + // Multi-line comments. + for (const auto *T = Tokens.begin(); T != Tokens.end();) { + if (T->Kind != tok::comment) { + T++; + continue; + } + Position Start = offsetToPosition(Code, StartOffset(*T)); + const pseudo::Token *LastComment = T; + while (T != Tokens.end() && T->Kind == tok::comment && + LastComment->Line == T->Line + 1) { + LastComment = T; + T++; + } + Position End = offsetToPosition( + Code, StartOffset(*LastComment) + OriginalToken(*LastComment).Length); + if (Start.line < End.line) + Result.push_back(ToFoldingRange(Start, End, FoldingRangeKind::comment)); + } 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( + [[// Multiple continuous comments + // are + + // collapsed + + // into one.]] + + int a; + [[/* comment 1 + */ + + /* comment 1 + */]] + )cpp", }; for (const char *Test : Tests) { auto T = Annotations(Test);