diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -145,6 +145,10 @@ $ ) +target_include_directories(clangDaemon PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/../pseudo/include +) + clang_target_link_libraries(clangDaemon PRIVATE clangAST @@ -170,6 +174,8 @@ clangTidy clangdSupport + + clangPseudo ) if(CLANGD_TIDY_CHECKS) target_link_libraries(clangDaemon PRIVATE ${ALL_CLANG_TIDY_CHECKS}) diff --git a/clang-tools-extra/clangd/SemanticSelection.h b/clang-tools-extra/clangd/SemanticSelection.h --- a/clang-tools-extra/clangd/SemanticSelection.h +++ b/clang-tools-extra/clangd/SemanticSelection.h @@ -15,6 +15,7 @@ #include "ParsedAST.h" #include "Protocol.h" #include "llvm/Support/Error.h" +#include #include namespace clang { namespace clangd { @@ -29,6 +30,10 @@ /// This should include large scopes, preprocessor blocks etc. llvm::Expected> getFoldingRanges(ParsedAST &AST); +/// FILL ME. +llvm::Expected> +getFoldingRanges(const std::string &Code); + } // namespace clangd } // namespace clang 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 @@ -11,15 +11,20 @@ #include "Protocol.h" #include "Selection.h" #include "SourceCode.h" +#include "clang-pseudo/Bracket.h" +#include "clang-pseudo/DirectiveTree.h" +#include "clang-pseudo/Token.h" #include "clang/AST/DeclBase.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Tooling/Syntax/BuildTree.h" #include "clang/Tooling/Syntax/Nodes.h" #include "clang/Tooling/Syntax/Tree.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -162,5 +167,38 @@ return collectFoldingRanges(SyntaxTree, AST.getSourceManager()); } +llvm::Expected> +getFoldingRanges(const std::string &Code) { + auto Stream = clang::pseudo::lex(Code, clang::pseudo::genericLangOpts()); + + auto DirectiveStructure = clang::pseudo::DirectiveTree::parse(Stream); + clang::pseudo::chooseConditionalBranches(DirectiveStructure, Stream); + + llvm::Optional Preprocessed; + if (true) { + Preprocessed = DirectiveStructure.stripDirectives(Stream); + Stream = *Preprocessed; + } + + auto ParseableStream = clang::pseudo::stripComments( + cook(Stream, clang::pseudo::genericLangOpts())); + pseudo::pairBrackets(ParseableStream); + + std::vector Result; + for (const auto &Tok : ParseableStream.tokens()) { + if (auto *Paired = Tok.pair()) { + if (Tok.Line < Paired->Line) { + FoldingRange FR; + FR.startLine = Tok.Line; + FR.startCharacter = Tok.StartColumn + 1; + FR.endLine = Paired->Line; + FR.endCharacter = Paired->StartColumn; + Result.push_back(FR); + } + } + } + return Result; +} + } // namespace clangd } // namespace clang 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 @@ -240,7 +240,7 @@ ]]} )cpp", R"cpp( - class Foo { + class Foo {[[ public: Foo() {[[ int X = 1; @@ -253,14 +253,15 @@ // Braces are located at the same line: no folding range here. void getFooBar() { } - }; + ]]}; )cpp", }; for (const char *Test : Tests) { auto T = Annotations(Test); auto AST = TestTU::withCode(T.code()).build(); - EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(AST))), - UnorderedElementsAreArray(T.ranges())) + EXPECT_THAT( + gatherFoldingRanges(llvm::cantFail(getFoldingRanges(T.code().str()))), + UnorderedElementsAreArray(T.ranges())) << Test; } } diff --git a/clang-tools-extra/pseudo/include/clang-pseudo/Token.h b/clang-tools-extra/pseudo/include/clang-pseudo/Token.h --- a/clang-tools-extra/pseudo/include/clang-pseudo/Token.h +++ b/clang-tools-extra/pseudo/include/clang-pseudo/Token.h @@ -63,6 +63,9 @@ /// Zero-based line number for the start of the token. /// This refers to the original source file as written. uint32_t Line = 0; + /// Zero-based column number for the start of the token. + /// This refers to the original source file as written. + uint8_t StartColumn = 0; /// Width of whitespace before the first token on this line. uint8_t Indent = 0; /// Flags have some meaning defined by the function that produced this stream. @@ -96,7 +99,7 @@ /// If this token is a paired bracket, the offset of the pair in the stream. int32_t Pair = 0; }; -static_assert(sizeof(Token) <= sizeof(char *) + 20, "Careful with layout!"); +static_assert(sizeof(Token) <= sizeof(char *) + 24, "Careful with layout!"); llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Token &); /// A half-open range of tokens within a stream. diff --git a/clang-tools-extra/pseudo/lib/Lex.cpp b/clang-tools-extra/pseudo/lib/Lex.cpp --- a/clang-tools-extra/pseudo/lib/Lex.cpp +++ b/clang-tools-extra/pseudo/lib/Lex.cpp @@ -27,6 +27,7 @@ TokenStream Result; clang::Token CT; unsigned LastOffset = 0; + unsigned LineStartOffset = 0; unsigned Line = 0; unsigned Indent = 0; for (Lexer.LexFromRawLexer(CT); CT.getKind() != clang::tok::eof; @@ -40,16 +41,17 @@ Tok.Kind = CT.getKind(); // Update current line number and indentation from raw source code. - unsigned NewLineStart = 0; + bool SawNewLine = 0; for (unsigned I = LastOffset; I < Offset; ++I) { if (Code[I] == '\n') { - NewLineStart = I + 1; + LineStartOffset = I + 1; + SawNewLine = true; ++Line; } } - if (NewLineStart || !LastOffset) { + if (SawNewLine || !LastOffset) { Indent = 0; - for (char C : StringRef(Code).slice(NewLineStart, Offset)) { + for (char C : StringRef(Code).slice(LineStartOffset, Offset)) { if (C == ' ') ++Indent; else if (C == '\t') @@ -60,6 +62,7 @@ } Tok.Indent = Indent; Tok.Line = Line; + Tok.StartColumn = Offset - LineStartOffset; if (CT.isAtStartOfLine()) Tok.setFlag(LexFlags::StartsPPLine);