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 @@ -530,7 +530,10 @@ {"save", true}, }}, {"documentFormattingProvider", true}, - {"documentRangeFormattingProvider", true}, + {"documentRangeFormattingProvider", + llvm::json::Object{ + {"multiRange", true}, + }}, {"documentOnTypeFormattingProvider", llvm::json::Object{ {"firstTriggerCharacter", "\n"}, @@ -872,9 +875,14 @@ void ClangdLSPServer::onDocumentRangeFormatting( const DocumentRangeFormattingParams &Params, Callback> Reply) { + if (!Params.range && !Params.ranges) + Reply(llvm::make_error( + "Neither \"range\", nor \"ranges\" was provided", + ErrorCode::InvalidParams)); + auto File = Params.textDocument.uri.file(); auto Code = Server->getDraft(File); - Server->formatFile(File, Params.range, + Server->formatFile(File, Params.ranges.value_or(std::vector{*Params.range}), [Code = std::move(Code), Reply = std::move(Reply)]( llvm::Expected Result) mutable { if (Result) @@ -890,7 +898,7 @@ auto File = Params.textDocument.uri.file(); auto Code = Server->getDraft(File); Server->formatFile(File, - /*Rng=*/std::nullopt, + /*Rngs=*/{}, [Code = std::move(Code), Reply = std::move(Reply)]( llvm::Expected Result) mutable { if (Result) 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 @@ -306,8 +306,8 @@ bool AddContainer, Callback CB); /// Run formatting for the \p File with content \p Code. - /// If \p Rng is non-null, formats only that region. - void formatFile(PathRef File, std::optional Rng, + /// If \p Rng is non-empty, formats only those regions. + void formatFile(PathRef File, const std::vector &Rngs, Callback CB); /// Run formatting after \p TriggerText was typed at \p Pos in \p File with 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 @@ -472,29 +472,32 @@ std::move(Action)); } -void ClangdServer::formatFile(PathRef File, std::optional Rng, +void ClangdServer::formatFile(PathRef File, const std::vector &Rngs, Callback CB) { auto Code = getDraft(File); if (!Code) return CB(llvm::make_error("trying to format non-added document", ErrorCode::InvalidParams)); - tooling::Range RequestedRange; - if (Rng) { - llvm::Expected Begin = positionToOffset(*Code, Rng->start); - if (!Begin) - return CB(Begin.takeError()); - llvm::Expected End = positionToOffset(*Code, Rng->end); - if (!End) - return CB(End.takeError()); - RequestedRange = tooling::Range(*Begin, *End - *Begin); + std::vector RequestedRanges; + if (!Rngs.empty()) { + RequestedRanges.reserve(Rngs.size()); + for (const auto &Rng : Rngs) { + llvm::Expected Begin = positionToOffset(*Code, Rng.start); + if (!Begin) + return CB(Begin.takeError()); + llvm::Expected End = positionToOffset(*Code, Rng.end); + if (!End) + return CB(End.takeError()); + RequestedRanges.emplace_back(*Begin, *End - *Begin); + } } else { - RequestedRange = tooling::Range(0, Code->size()); + RequestedRanges = {tooling::Range(0, Code->size())}; } // Call clang-format. auto Action = [File = File.str(), Code = std::move(*Code), - Ranges = std::vector{RequestedRange}, - CB = std::move(CB), this]() mutable { + Ranges = std::move(RequestedRanges), CB = std::move(CB), + this]() mutable { format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS); tooling::Replacements IncludeReplaces = format::sortIncludes(Style, Code, Ranges, File); 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 @@ -802,7 +802,10 @@ TextDocumentIdentifier textDocument; /// The range to format - Range range; + std::optional range; + + /// The list of ranges to format + std::optional> ranges; }; bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &, llvm::json::Path); 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 @@ -584,7 +584,9 @@ bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R, llvm::json::Path P) { llvm::json::ObjectMapper O(Params, P); - return O && O.map("textDocument", R.textDocument) && O.map("range", R.range); + return O && O.map("textDocument", R.textDocument) && + O.map("range", R.range) && O.map("ranges", R.ranges); + ; } bool fromJSON(const llvm::json::Value &Params, 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 @@ -35,7 +35,9 @@ # CHECK-NEXT: "firstTriggerCharacter": "\n", # CHECK-NEXT: "moreTriggerCharacter": [] # CHECK-NEXT: }, -# CHECK-NEXT: "documentRangeFormattingProvider": true, +# CHECK-NEXT: "documentRangeFormattingProvider": { +# CHECK-NEXT: "multiRange": true +# CHECK-NEXT: }, # CHECK-NEXT: "documentSymbolProvider": true, # CHECK-NEXT: "executeCommandProvider": { # CHECK-NEXT: "commands": [ diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -942,7 +942,7 @@ FS.Files[Path] = Code; runAddDocument(Server, Path, Code); - auto Replaces = runFormatFile(Server, Path, /*Rng=*/std::nullopt); + auto Replaces = runFormatFile(Server, Path, /*Rngs=*/{}); EXPECT_TRUE(static_cast(Replaces)); auto Changed = tooling::applyAllReplacements(Code, *Replaces); EXPECT_TRUE(static_cast(Changed)); diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h --- a/clang-tools-extra/clangd/unittests/SyncAPI.h +++ b/clang-tools-extra/clangd/unittests/SyncAPI.h @@ -53,7 +53,7 @@ const clangd::RenameOptions &RenameOpts); llvm::Expected -runFormatFile(ClangdServer &Server, PathRef File, std::optional); +runFormatFile(ClangdServer &Server, PathRef File, const std::vector &); SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query); SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req); diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -116,9 +116,10 @@ } llvm::Expected -runFormatFile(ClangdServer &Server, PathRef File, std::optional Rng) { +runFormatFile(ClangdServer &Server, PathRef File, + const std::vector &Rngs) { std::optional> Result; - Server.formatFile(File, Rng, capture(Result)); + Server.formatFile(File, Rngs, capture(Result)); return std::move(*Result); }