14#include "clang/AST/DeclBase.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Tooling/Syntax/BuildTree.h"
18#include "clang/Tooling/Syntax/Nodes.h"
19#include "clang/Tooling/Syntax/TokenBufferTokenManager.h"
20#include "clang/Tooling/Syntax/Tree.h"
21#include "llvm/ADT/ArrayRef.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Support/Casting.h"
24#include "llvm/Support/Error.h"
38void addIfDistinct(
const Range &R, std::vector<Range> &Result) {
39 if (Result.empty() || Result.back() != R) {
44std::optional<FoldingRange> toFoldingRange(SourceRange SR,
45 const SourceManager &SM) {
46 const auto Begin = SM.getDecomposedLoc(SR.getBegin()),
47 End = SM.getDecomposedLoc(SR.getEnd());
51 if ((Begin.first != SM.getMainFileID()) || (End.first != SM.getMainFileID()))
54 Range.startCharacter = SM.getColumnNumber(Begin.first, Begin.second) - 1;
55 Range.startLine = SM.getLineNumber(Begin.first, Begin.second) - 1;
56 Range.endCharacter = SM.getColumnNumber(End.first, End.second) - 1;
57 Range.endLine = SM.getLineNumber(End.first, End.second) - 1;
61std::optional<FoldingRange>
62extractFoldingRange(
const syntax::Node *Node,
63 const syntax::TokenBufferTokenManager &TM) {
64 if (
const auto *Stmt = dyn_cast<syntax::CompoundStatement>(Node)) {
65 const auto *LBrace = cast_or_null<syntax::Leaf>(
66 Stmt->findChild(syntax::NodeRole::OpenParen));
70 const auto *RBrace = cast_or_null<syntax::Leaf>(
71 Stmt->findChild(syntax::NodeRole::CloseParen));
72 if (!LBrace || !RBrace)
75 const SourceLocation LBraceLocInfo =
76 TM.getToken(LBrace->getTokenKey())->endLocation(),
78 TM.getToken(RBrace->getTokenKey())->location();
79 auto Range = toFoldingRange(SourceRange(LBraceLocInfo, RBraceLocInfo),
83 if (Range &&
Range->startLine !=
Range->endLine)
90std::vector<FoldingRange>
91collectFoldingRanges(
const syntax::Node *
Root,
92 const syntax::TokenBufferTokenManager &TM) {
93 std::queue<const syntax::Node *> Nodes;
95 std::vector<FoldingRange> Result;
96 while (!Nodes.empty()) {
97 const syntax::Node *
Node = Nodes.front();
99 const auto Range = extractFoldingRange(Node, TM);
101 Result.push_back(*Range);
102 if (
const auto *T = dyn_cast<syntax::Tree>(Node))
103 for (
const auto *NextNode =
T->getFirstChild(); NextNode;
104 NextNode = NextNode->getNextSibling())
105 Nodes.push(NextNode);
113 std::vector<Range> Ranges;
114 const auto &SM =
AST.getSourceManager();
115 const auto &LangOpts =
AST.getLangOpts();
117 auto FID = SM.getMainFileID();
120 return Offset.takeError();
129 if (llvm::isa<TranslationUnitDecl>(D)) {
135 if (!SR || SM.getFileID(SR->getBegin()) != SM.getMainFileID()) {
141 addIfDistinct(R, Ranges);
144 if (Ranges.empty()) {
149 return std::move(
Empty);
154 Head.range = std::move(Ranges.front());
157 llvm::MutableArrayRef(Ranges.data(), Ranges.size()).drop_front()) {
158 Tail->parent = std::make_unique<SelectionRange>();
163 return std::move(
Head);
172 syntax::TokenBufferTokenManager TM(
AST.getTokens(),
AST.getLangOpts(),
173 AST.getSourceManager());
174 const auto *SyntaxTree = syntax::buildSyntaxTree(A, TM,
AST.getASTContext());
175 return collectFoldingRanges(SyntaxTree, TM);
182llvm::Expected<std::vector<FoldingRange>>
190 auto Preprocessed = DirectiveStructure.stripDirectives(OrigStream);
195 std::vector<FoldingRange> Result;
197 llvm::StringLiteral
Kind) {
198 if (Start.line >= End.line)
206 Result.push_back(FR);
208 auto OriginalToken = [&](
const Token &T) {
209 return OrigStream.tokens()[T.OriginalIndex];
211 auto StartOffset = [&](
const Token &T) {
212 return OriginalToken(T).text().data() - Code.data();
214 auto StartPosition = [&](
const Token &T) {
217 auto EndOffset = [&](
const Token &T) {
218 return StartOffset(T) + OriginalToken(T).Length;
220 auto EndPosition = [&](
const Token &T) {
223 auto Tokens = ParseableStream.tokens();
225 for (
const auto &Tok : Tokens) {
226 if (
auto *Paired = Tok.
pair()) {
229 if (Tok.
Line < Paired->Line) {
231 Position End = StartPosition(*Paired);
238 auto IsBlockComment = [&](
const Token &T) {
239 assert(T.Kind == tok::comment);
240 return OriginalToken(T).Length >= 2 &&
241 Code.substr(StartOffset(T), 2) ==
"/*";
244 for (
auto *T = Tokens.begin(); T != Tokens.end();) {
245 if (T->Kind != tok::comment) {
249 Token *FirstComment = T;
252 Token *LastComment = T;
254 while (T != Tokens.end() && T->Kind == tok::comment &&
255 StartPosition(*T).line <= End.line + 1) {
256 End = EndPosition(*T);
260 if (IsBlockComment(*FirstComment)) {
264 if (IsBlockComment(*LastComment))
const FunctionDecl * Decl
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
Stores and provides access to parsed AST.
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
const Node * commonAncestor() const
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
static void lex(llvm::StringRef Code, const LangOptions &LangOpts, llvm::function_ref< void(const syntax::Token &, const SourceManager &SM)> Action)
llvm::Expected< std::vector< FoldingRange > > getFoldingRanges(ParsedAST &AST)
Returns a list of ranges whose contents might be collapsible in an editor.
Position sourceLocToPosition(const SourceManager &SM, SourceLocation Loc)
Turn a SourceLocation into a [line, column] pair.
llvm::Expected< SelectionRange > getSemanticRanges(ParsedAST &AST, Position Pos)
Returns the list of all interesting ranges around the Position Pos.
clang::LangOptions genericLangOpts(clang::Language Lang, clang::LangStandard::Kind Standard)
A generic lang options suitable for lexing/parsing a langage.
void chooseConditionalBranches(DirectiveTree &Tree, const TokenStream &Code)
Selects a "taken" branch for each conditional directive in the file.
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
TokenStream cook(const TokenStream &Code, const LangOptions &LangOpts)
void pairBrackets(TokenStream &Stream)
Identifies bracket token in the stream which should be paired.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static DirectiveTree parse(const TokenStream &)
Extract preprocessor structure by examining the raw tokens.
Stores information about a region of code that can be folded.
static const llvm::StringLiteral REGION_KIND
static const llvm::StringLiteral COMMENT_KIND
Position start
The range's start position.
Position end
The range's end position.
A single C++ or preprocessor token.
const Token * pair() const
Returns the bracket paired with this one, if any.
uint32_t Line
Zero-based line number for the start of the token.