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,7 @@ // 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 @@ -620,6 +620,7 @@ {"renameProvider", std::move(RenameProvider)}, {"selectionRangeProvider", true}, {"documentSymbolProvider", true}, + {"foldingRangeProvider", true}, {"workspaceSymbolProvider", true}, {"referencesProvider", true}, {"executeCommandProvider", @@ -922,7 +923,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 +961,19 @@ }); } +void ClangdLSPServer::onFoldingRange(const FoldingRangeParams &Params, + Callback Reply) { + URIForFile FileURI = Params.textDocument.uri; + Server->foldingRanges( + Params.textDocument.uri.file(), + [FileURI, Reply = std::move(Reply)]( + llvm::Expected> Items) mutable { + if (!Items) + return Reply(Items.takeError()); + return Reply(std::move(*Items)); + }); +} + static llvm::Optional asCommand(const CodeAction &Action) { Command Cmd; if (Action.command && Action.edit) @@ -1372,6 +1385,7 @@ MsgHandler->bind("textDocument/rename", &ClangdLSPServer::onRename); MsgHandler->bind("textDocument/hover", &ClangdLSPServer::onHover); MsgHandler->bind("textDocument/documentSymbol", &ClangdLSPServer::onDocumentSymbol); + MsgHandler->bind("textDocument/foldingRange", &ClangdLSPServer::onFoldingRange); MsgHandler->bind("workspace/executeCommand", &ClangdLSPServer::onCommand); MsgHandler->bind("textDocument/documentHighlight", &ClangdLSPServer::onDocumentHighlight); MsgHandler->bind("workspace/symbol", &ClangdLSPServer::onWorkspaceSymbol); 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 @@ -242,6 +242,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/FindSymbols.h b/clang-tools-extra/clangd/FindSymbols.h --- a/clang-tools-extra/clangd/FindSymbols.h +++ b/clang-tools-extra/clangd/FindSymbols.h @@ -47,6 +47,10 @@ /// same order that they appear. llvm::Expected> getDocumentSymbols(ParsedAST &AST); +/// Retrieves folding ranges using Document Symbols in the "main file" section +/// of given AST. +llvm::Expected> getFoldingRanges(ParsedAST &AST); + } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -271,11 +271,35 @@ std::vector collectDocSymbols(ParsedAST &AST) { return DocumentOutline(AST).build(); } + +// 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> getDocumentSymbols(ParsedAST &AST) { return collectDocSymbols(AST); } +// FIXME(kirillbobyrev): Collect commenets, PP definitions and other code +// regions (e.g. public/private sections of classes, control flow statement +// bodies). +llvm::Expected> getFoldingRanges(ParsedAST &AST) { + auto DocumentSymbols = collectDocSymbols(AST); + std::vector Result; + for (const auto &Symbol : DocumentSymbols) + collectFoldingRanges(Symbol, Result); + return Result; +} + } // namespace clangd } // namespace clang 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,22 @@ }; llvm::json::Value toJSON(const DocumentLink &DocumentLink); +struct FoldingRangeParams { + TextDocumentIdentifier textDocument; +}; +bool fromJSON(const llvm::json::Value &, FoldingRangeParams &); + +/// Stores information about a region of code that can be folded. +/// FIXME(kirillbobyrev): Implement FoldingRangeClientCapabilities. +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/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test --- a/clang-tools-extra/clangd/test/initialize-params.test +++ b/clang-tools-extra/clangd/test/initialize-params.test @@ -37,6 +37,7 @@ # CHECK-NEXT: "clangd.applyTweak" # CHECK-NEXT: ] # CHECK-NEXT: }, +# CHECK-NEXT: "foldingRangeProvider": true, # CHECK-NEXT: "hoverProvider": true, # CHECK-NEXT: "referencesProvider": true, # CHECK-NEXT: "renameProvider": true,