forked from OSchip/llvm-project
FoldingRanges: Handle LineFoldingsOnly clients.
Do not fold the endline which contains tokens after the end of range. Differential Revision: https://reviews.llvm.org/D131154
This commit is contained in:
parent
ba916c0cf6
commit
a11ec00afe
|
@ -514,6 +514,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
|
|||
Params.capabilities.HierarchicalDocumentSymbol;
|
||||
SupportFileStatus = Params.initializationOptions.FileStatus;
|
||||
HoverContentFormat = Params.capabilities.HoverContentFormat;
|
||||
Opts.LineFoldingOnly = Params.capabilities.LineFoldingOnly;
|
||||
SupportsOffsetsInSignatureHelp = Params.capabilities.OffsetsInSignatureHelp;
|
||||
if (Params.capabilities.WorkDoneProgress)
|
||||
BackgroundIndexProgressState = BackgroundIndexProgress::Empty;
|
||||
|
|
|
@ -177,6 +177,7 @@ ClangdServer::ClangdServer(const GlobalCompilationDatabase &CDB,
|
|||
DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex() : nullptr),
|
||||
ClangTidyProvider(Opts.ClangTidyProvider),
|
||||
UseDirtyHeaders(Opts.UseDirtyHeaders),
|
||||
LineFoldingOnly(Opts.LineFoldingOnly),
|
||||
PreambleParseForwardingFunctions(Opts.PreambleParseForwardingFunctions),
|
||||
WorkspaceRoot(Opts.WorkspaceRoot),
|
||||
Transient(Opts.ImplicitCancellation ? TUScheduler::InvalidateOnUpdate
|
||||
|
@ -855,8 +856,9 @@ void ClangdServer::foldingRanges(llvm::StringRef File,
|
|||
return CB(llvm::make_error<LSPError>(
|
||||
"trying to compute folding ranges for non-added document",
|
||||
ErrorCode::InvalidParams));
|
||||
auto Action = [CB = std::move(CB), Code = std::move(*Code)]() mutable {
|
||||
CB(clangd::getFoldingRanges(Code));
|
||||
auto Action = [LineFoldingOnly = LineFoldingOnly, CB = std::move(CB),
|
||||
Code = std::move(*Code)]() mutable {
|
||||
CB(clangd::getFoldingRanges(Code, LineFoldingOnly));
|
||||
};
|
||||
// We want to make sure folding ranges are always available for all the open
|
||||
// files, hence prefer runQuick to not wait for operations on other files.
|
||||
|
|
|
@ -164,6 +164,9 @@ public:
|
|||
/// Enable preview of FoldingRanges feature.
|
||||
bool FoldingRanges = false;
|
||||
|
||||
// Whether the client supports folding only complete lines.
|
||||
bool LineFoldingOnly = false;
|
||||
|
||||
FeatureModuleSet *FeatureModules = nullptr;
|
||||
/// If true, use the dirty buffer contents when building Preambles.
|
||||
bool UseDirtyHeaders = false;
|
||||
|
@ -429,6 +432,9 @@ private:
|
|||
|
||||
bool UseDirtyHeaders = false;
|
||||
|
||||
// Whether the client supports folding only complete lines.
|
||||
bool LineFoldingOnly = false;
|
||||
|
||||
bool PreambleParseForwardingFunctions = false;
|
||||
|
||||
// GUARDED_BY(CachedCompletionFuzzyFindRequestMutex)
|
||||
|
|
|
@ -391,6 +391,10 @@ bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (auto *Folding = TextDocument->getObject("foldingRange")) {
|
||||
if (auto LineFolding = Folding->getBoolean("lineFoldingOnly"))
|
||||
R.LineFoldingOnly = *LineFolding;
|
||||
}
|
||||
if (auto *Rename = TextDocument->getObject("rename")) {
|
||||
if (auto RenameSupport = Rename->getBoolean("prepareSupport"))
|
||||
R.RenamePrepareSupport = *RenameSupport;
|
||||
|
|
|
@ -437,6 +437,12 @@ struct ClientCapabilities {
|
|||
/// textDocument.signatureHelp
|
||||
bool HasSignatureHelp = false;
|
||||
|
||||
/// Client signals that it only supports folding complete lines.
|
||||
/// Client will ignore specified `startCharacter` and `endCharacter`
|
||||
/// properties in a FoldingRange.
|
||||
/// textDocument.foldingRange.lineFoldingOnly
|
||||
bool LineFoldingOnly = false;
|
||||
|
||||
/// Client supports processing label offsets instead of a simple label string.
|
||||
/// textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport
|
||||
bool OffsetsInSignatureHelp = false;
|
||||
|
|
|
@ -179,7 +179,7 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST) {
|
|||
// statement bodies).
|
||||
// Related issue: https://github.com/clangd/clangd/issues/310
|
||||
llvm::Expected<std::vector<FoldingRange>>
|
||||
getFoldingRanges(const std::string &Code) {
|
||||
getFoldingRanges(const std::string &Code, bool LineFoldingOnly) {
|
||||
auto OrigStream = pseudo::lex(Code, clang::pseudo::genericLangOpts());
|
||||
|
||||
auto DirectiveStructure = pseudo::DirectiveTree::parse(OrigStream);
|
||||
|
@ -192,15 +192,17 @@ getFoldingRanges(const std::string &Code) {
|
|||
pseudo::pairBrackets(ParseableStream);
|
||||
|
||||
std::vector<FoldingRange> Result;
|
||||
auto ToFoldingRange = [](Position Start, Position End,
|
||||
llvm::StringLiteral Kind) {
|
||||
auto AddFoldingRange = [&](Position Start, Position End,
|
||||
llvm::StringLiteral Kind) {
|
||||
if (Start.line >= End.line)
|
||||
return;
|
||||
FoldingRange FR;
|
||||
FR.startLine = Start.line;
|
||||
FR.startCharacter = Start.character;
|
||||
FR.endLine = End.line;
|
||||
FR.endCharacter = End.character;
|
||||
FR.kind = Kind.str();
|
||||
return FR;
|
||||
Result.push_back(FR);
|
||||
};
|
||||
auto OriginalToken = [&](const pseudo::Token &T) {
|
||||
return OrigStream.tokens()[T.OriginalIndex];
|
||||
|
@ -211,8 +213,11 @@ getFoldingRanges(const std::string &Code) {
|
|||
auto StartPosition = [&](const pseudo::Token &T) {
|
||||
return offsetToPosition(Code, StartOffset(T));
|
||||
};
|
||||
auto EndOffset = [&](const pseudo::Token &T) {
|
||||
return StartOffset(T) + OriginalToken(T).Length;
|
||||
};
|
||||
auto EndPosition = [&](const pseudo::Token &T) {
|
||||
return offsetToPosition(Code, StartOffset(T) + OriginalToken(T).Length);
|
||||
return offsetToPosition(Code, EndOffset(T));
|
||||
};
|
||||
auto Tokens = ParseableStream.tokens();
|
||||
// Brackets.
|
||||
|
@ -223,26 +228,43 @@ getFoldingRanges(const std::string &Code) {
|
|||
if (Tok.Line < Paired->Line) {
|
||||
Position Start = offsetToPosition(Code, 1 + StartOffset(Tok));
|
||||
Position End = StartPosition(*Paired);
|
||||
Result.push_back(ToFoldingRange(Start, End, FoldingRange::REGION_KIND));
|
||||
if (LineFoldingOnly)
|
||||
End.line--;
|
||||
AddFoldingRange(Start, End, FoldingRange::REGION_KIND);
|
||||
}
|
||||
}
|
||||
}
|
||||
auto IsBlockComment = [&](const pseudo::Token &T) {
|
||||
assert(T.Kind == tok::comment);
|
||||
return OriginalToken(T).Length >= 2 &&
|
||||
Code.substr(StartOffset(T), 2) == "/*";
|
||||
};
|
||||
// Multi-line comments.
|
||||
for (const auto *T = Tokens.begin(); T != Tokens.end();) {
|
||||
for (auto *T = Tokens.begin(); T != Tokens.end();) {
|
||||
if (T->Kind != tok::comment) {
|
||||
T++;
|
||||
continue;
|
||||
}
|
||||
Position Start = StartPosition(*T);
|
||||
Position LastCommentEnd = EndPosition(*T);
|
||||
pseudo::Token *FirstComment = T;
|
||||
// Show starting sentinals (// and /*) of the comment.
|
||||
Position Start = offsetToPosition(Code, 2 + StartOffset(*FirstComment));
|
||||
pseudo::Token *LastComment = T;
|
||||
Position End = EndPosition(*T);
|
||||
while (T != Tokens.end() && T->Kind == tok::comment &&
|
||||
StartPosition(*T).line <= LastCommentEnd.line + 1) {
|
||||
LastCommentEnd = EndPosition(*T);
|
||||
StartPosition(*T).line <= End.line + 1) {
|
||||
End = EndPosition(*T);
|
||||
LastComment = T;
|
||||
T++;
|
||||
}
|
||||
if (Start.line < LastCommentEnd.line)
|
||||
Result.push_back(
|
||||
ToFoldingRange(Start, LastCommentEnd, FoldingRange::COMMENT_KIND));
|
||||
if (IsBlockComment(*FirstComment)) {
|
||||
if (LineFoldingOnly)
|
||||
// Show last line of a block comment.
|
||||
End.line--;
|
||||
if (IsBlockComment(*LastComment))
|
||||
// Show ending sentinal "*/" of the block comment.
|
||||
End.character -= 2;
|
||||
}
|
||||
AddFoldingRange(Start, End, FoldingRange::COMMENT_KIND);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ llvm::Expected<std::vector<FoldingRange>> getFoldingRanges(ParsedAST &AST);
|
|||
/// Returns a list of ranges whose contents might be collapsible in an editor.
|
||||
/// This version uses the pseudoparser which does not require the AST.
|
||||
llvm::Expected<std::vector<FoldingRange>>
|
||||
getFoldingRanges(const std::string &Code);
|
||||
getFoldingRanges(const std::string &Code, bool LineFoldingOnly);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
|
@ -196,7 +196,7 @@ TEST(SemanticSelection, RunViaClangdServer) {
|
|||
ElementsAre(SourceAnnotations.range("empty")));
|
||||
}
|
||||
|
||||
TEST(FoldingRanges, All) {
|
||||
TEST(FoldingRanges, ASTAll) {
|
||||
const char *Tests[] = {
|
||||
R"cpp(
|
||||
#define FOO int foo() {\
|
||||
|
@ -265,7 +265,7 @@ TEST(FoldingRanges, All) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(FoldingRangesPseudoParser, All) {
|
||||
TEST(FoldingRanges, PseudoParserWithoutLineFoldings) {
|
||||
const char *Tests[] = {
|
||||
R"cpp(
|
||||
#define FOO int foo() {\
|
||||
|
@ -336,36 +336,123 @@ TEST(FoldingRangesPseudoParser, All) {
|
|||
]]};
|
||||
)cpp",
|
||||
R"cpp(
|
||||
[[/* Multi
|
||||
/*[[ Multi
|
||||
* line
|
||||
* comment
|
||||
*/]]
|
||||
]]*/
|
||||
)cpp",
|
||||
R"cpp(
|
||||
[[// Comment
|
||||
//[[ Comment
|
||||
// 1]]
|
||||
|
||||
[[// Comment
|
||||
//[[ Comment
|
||||
// 2]]
|
||||
|
||||
// No folding for single line comment.
|
||||
|
||||
[[/* comment 3
|
||||
*/]]
|
||||
/*[[ comment 3
|
||||
]]*/
|
||||
|
||||
[[/* comment 4
|
||||
*/]]
|
||||
/*[[ comment 4
|
||||
]]*/
|
||||
|
||||
/*[[ foo */
|
||||
/* bar ]]*/
|
||||
|
||||
/*[[ foo */
|
||||
// baz
|
||||
/* bar ]]*/
|
||||
|
||||
/*[[ foo */
|
||||
/* bar*/
|
||||
// baz]]
|
||||
|
||||
//[[ foo
|
||||
/* bar */]]
|
||||
)cpp",
|
||||
};
|
||||
for (const char *Test : Tests) {
|
||||
auto T = Annotations(Test);
|
||||
EXPECT_THAT(
|
||||
gatherFoldingRanges(llvm::cantFail(getFoldingRanges(T.code().str()))),
|
||||
UnorderedElementsAreArray(T.ranges()))
|
||||
EXPECT_THAT(gatherFoldingRanges(llvm::cantFail(getFoldingRanges(
|
||||
T.code().str(), /*LineFoldingsOnly=*/false))),
|
||||
UnorderedElementsAreArray(T.ranges()))
|
||||
<< Test;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FoldingRanges, PseudoParserLineFoldingsOnly) {
|
||||
const char *Tests[] = {
|
||||
R"cpp(
|
||||
void func(int a) {[[
|
||||
a++;]]
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
// Always exclude last line for brackets.
|
||||
void func(int a) {[[
|
||||
if(a == 1) {[[
|
||||
a++;]]
|
||||
} else if (a == 2){[[
|
||||
a--;]]
|
||||
} else { // No folding for 2 line bracketed ranges.
|
||||
}]]
|
||||
}
|
||||
)cpp",
|
||||
R"cpp(
|
||||
/*[[ comment
|
||||
* comment]]
|
||||
*/
|
||||
|
||||
/* No folding for this comment.
|
||||
*/
|
||||
|
||||
// No folding for this comment.
|
||||
|
||||
//[[ 2 single line comment.
|
||||
// 2 single line comment.]]
|
||||
|
||||
//[[ >=2 line comments.
|
||||
// >=2 line comments.
|
||||
// >=2 line comments.]]
|
||||
|
||||
//[[ foo\
|
||||
bar\
|
||||
baz]]
|
||||
|
||||
/*[[ foo */
|
||||
/* bar */]]
|
||||
/* baz */
|
||||
|
||||
/*[[ foo */
|
||||
/* bar]]
|
||||
* This does not fold me */
|
||||
|
||||
//[[ foo
|
||||
/* bar */]]
|
||||
)cpp",
|
||||
// FIXME: Support folding template arguments.
|
||||
// R"cpp(
|
||||
// template <[[typename foo, class bar]]> struct baz {};
|
||||
// )cpp",
|
||||
|
||||
};
|
||||
auto StripColumns = [](const std::vector<Range> &Ranges) {
|
||||
std::vector<Range> Res;
|
||||
for (Range R : Ranges) {
|
||||
R.start.character = R.end.character = 0;
|
||||
Res.push_back(R);
|
||||
}
|
||||
return Res;
|
||||
};
|
||||
for (const char *Test : Tests) {
|
||||
auto T = Annotations(Test);
|
||||
EXPECT_THAT(
|
||||
StripColumns(gatherFoldingRanges(llvm::cantFail(
|
||||
getFoldingRanges(T.code().str(), /*LineFoldingsOnly=*/true)))),
|
||||
UnorderedElementsAreArray(StripColumns(T.ranges())))
|
||||
<< Test;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
|
Loading…
Reference in New Issue