diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -87,6 +87,8 @@ // otherwise. void onDocumentSymbol(const DocumentSymbolParams &, Callback); + void onFoldingRange(const FoldingRangeParams &, + Callback>); void onCodeAction(const CodeActionParams &, Callback); void onCompletion(const CompletionParams &, Callback); void onSignatureHelp(const TextDocumentPositionParams &, diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -637,6 +637,8 @@ ->insert( {"semanticHighlighting", llvm::json::Object{{"scopes", buildHighlightScopeLookupTable()}}}); + if (ClangdServerOpts.FoldingRanges) + Result.getObject("capabilities")->insert({"foldingRangeProvider", true}); Reply(std::move(Result)); } @@ -922,7 +924,6 @@ static std::vector flattenSymbolHierarchy(llvm::ArrayRef Symbols, const URIForFile &FileURI) { - std::vector Results; std::function Process = [&](const DocumentSymbol &S, llvm::Optional ParentName) { @@ -961,6 +962,12 @@ }); } +void ClangdLSPServer::onFoldingRange( + const FoldingRangeParams &Params, + Callback> Reply) { + Server->foldingRanges(Params.textDocument.uri.file(), std::move(Reply)); +} + static llvm::Optional asCommand(const CodeAction &Action) { Command Cmd; if (Action.command && Action.edit) @@ -1388,6 +1395,8 @@ MsgHandler->bind("textDocument/documentLink", &ClangdLSPServer::onDocumentLink); MsgHandler->bind("textDocument/semanticTokens", &ClangdLSPServer::onSemanticTokens); MsgHandler->bind("textDocument/semanticTokens/edits", &ClangdLSPServer::onSemanticTokensEdits); + if (Opts.FoldingRanges) + MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange); // clang-format on } diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -153,6 +153,9 @@ /// Enable notification-based semantic highlighting. bool TheiaSemanticHighlighting = false; + /// Enable preview of FoldingRanges feature. + bool FoldingRanges = false; + /// Returns true if the tweak should be enabled. std::function TweakFilter = [](const Tweak &T) { return !T.hidden(); // only enable non-hidden tweaks. @@ -242,6 +245,9 @@ void documentSymbols(StringRef File, Callback> CB); + /// Retrieve ranges that can be used to fold code within the specified file. + void foldingRanges(StringRef File, Callback> CB); + /// Retrieve locations for symbol references. void findReferences(PathRef File, Position Pos, uint32_t Limit, Callback CB); diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -655,6 +655,18 @@ TUScheduler::InvalidateOnUpdate); } +void ClangdServer::foldingRanges(llvm::StringRef File, + Callback> CB) { + auto Action = + [CB = std::move(CB)](llvm::Expected InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + CB(clangd::getFoldingRanges(InpAST->AST)); + }; + WorkScheduler.runWithAST("foldingRanges", File, std::move(Action), + TUScheduler::InvalidateOnUpdate); +} + void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, Callback CB) { auto Action = [Pos, Limit, CB = std::move(CB), 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 @@ -1510,6 +1510,23 @@ }; llvm::json::Value toJSON(const DocumentLink &DocumentLink); +// FIXME(kirillbobyrev): Add FoldingRangeClientCapabilities so we can support +// per-line-folding editors. +struct FoldingRangeParams { + TextDocumentIdentifier textDocument; +}; +bool fromJSON(const llvm::json::Value &, FoldingRangeParams &); + +/// Stores information about a region of code that can be folded. +struct FoldingRange { + unsigned startLine; + llvm::Optional startCharacter; + unsigned endLine; + llvm::Optional endCharacter; + llvm::Optional kind; +}; +llvm::json::Value toJSON(const FoldingRange &Range); + } // namespace clangd } // namespace clang 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 @@ -1241,5 +1241,24 @@ }; } +bool fromJSON(const llvm::json::Value &Params, FoldingRangeParams &R) { + llvm::json::ObjectMapper O(Params); + return O && O.map("textDocument", R.textDocument); +} + +llvm::json::Value toJSON(const FoldingRange &Range) { + llvm::json::Object Result{ + {"startLine", Range.startLine}, + {"endLine", Range.endLine}, + }; + if (Range.startCharacter) + Result["startCharacter"] = *Range.startCharacter; + if (Range.endCharacter) + Result["endCharacter"] = *Range.endCharacter; + if (Range.kind) + Result["kind"] = *Range.kind; + return Result; +} + } // namespace clangd } // namespace clang 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 @@ -25,6 +25,9 @@ /// If pos is not in any interesting range, return [Pos, Pos). llvm::Expected getSemanticRanges(ParsedAST &AST, Position Pos); +/// Retrieves folding ranges in the "main file" section of given AST. +llvm::Expected> getFoldingRanges(ParsedAST &AST); + } // 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 @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// #include "SemanticSelection.h" +#include "FindSymbols.h" #include "ParsedAST.h" #include "Protocol.h" #include "Selection.h" @@ -18,6 +19,7 @@ namespace clang { namespace clangd { namespace { + // Adds Range \p R to the Result if it is distinct from the last added Range. // Assumes that only consecutive ranges can coincide. void addIfDistinct(const Range &R, std::vector &Result) { @@ -25,6 +27,20 @@ Result.push_back(R); } } + +// Recursively collects FoldingRange from a symbol and its children. +void collectFoldingRanges(DocumentSymbol Symbol, + std::vector &Result) { + FoldingRange Range; + Range.startLine = Symbol.range.start.line; + Range.startCharacter = Symbol.range.start.character; + Range.endLine = Symbol.range.end.line; + Range.endCharacter = Symbol.range.end.character; + Result.push_back(Range); + for (const auto &Child : Symbol.children) + collectFoldingRanges(Child, Result); +} + } // namespace llvm::Expected getSemanticRanges(ParsedAST &AST, Position Pos) { @@ -81,5 +97,23 @@ return std::move(Head); } +// FIXME(kirillbobyrev): Collect comments, 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(ParsedAST &AST) { + // FIXME(kirillbobyrev): getDocumentSymbols() is conveniently available but + // limited (e.g. doesn't yield blocks inside functions). Replace this with a + // more general RecursiveASTVisitor implementation instead. + auto DocumentSymbols = getDocumentSymbols(AST); + if (!DocumentSymbols) + return DocumentSymbols.takeError(); + std::vector Result; + for (const auto &Symbol : *DocumentSymbols) + collectFoldingRanges(Symbol, Result); + return Result; +} + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -296,6 +296,14 @@ Hidden, }; +opt FoldingRanges{ + "folding-rangees", + cat(Features), + desc("Enable preview of FoldingRanges feature"), + init(false), + Hidden, +}; + opt WorkerThreadsCount{ "j", cat(Misc), @@ -659,6 +667,7 @@ Opts.AsyncThreadsCount = WorkerThreadsCount; Opts.BuildRecoveryAST = RecoveryAST; Opts.PreserveRecoveryASTType = RecoveryASTType; + Opts.FoldingRanges = FoldingRanges; clangd::CodeCompleteOptions CCOpts; CCOpts.IncludeIneligibleResults = IncludeIneligibleResults;