-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[clangd] Add support for textDocument/rangesFormatting (LSP 3.18) #80180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clangd @llvm/pr-subscribers-clang-tools-extra Author: Tom Praschan (tom-anders) ChangesUses the protocol changes proposed in microsoft/language-server-protocol#1556 and microsoft/vscode#163190 Related issue: clangd/clangd#1635 Old Phabricator review: https://reviews.llvm.org/D150852 Relevant LSP 3.18 spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#documentRangesFormattingParams Full diff: https://github.com/llvm/llvm-project/pull/80180.diff 9 Files Affected:
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index a87da252b7a7e..455d265ba0987 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -566,7 +566,10 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
{"save", true},
}},
{"documentFormattingProvider", true},
- {"documentRangeFormattingProvider", true},
+ {"documentRangeFormattingProvider",
+ llvm::json::Object{
+ {"multiRange", true},
+ }},
{"documentOnTypeFormattingProvider",
llvm::json::Object{
{"firstTriggerCharacter", "\n"},
@@ -914,9 +917,14 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
void ClangdLSPServer::onDocumentRangeFormatting(
const DocumentRangeFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) {
+ if (!Params.range && !Params.ranges)
+ Reply(llvm::make_error<LSPError>(
+ "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<tooling::Replacements> Result) mutable {
if (Result)
@@ -932,7 +940,7 @@ void ClangdLSPServer::onDocumentFormatting(
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<tooling::Replacements> Result) mutable {
if (Result)
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 6fb2641e8793d..4c18f049c566e 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -500,29 +500,32 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos,
std::move(Action));
}
-void ClangdServer::formatFile(PathRef File, std::optional<Range> Rng,
+void ClangdServer::formatFile(PathRef File, const std::vector<Range> &Rngs,
Callback<tooling::Replacements> CB) {
auto Code = getDraft(File);
if (!Code)
return CB(llvm::make_error<LSPError>("trying to format non-added document",
ErrorCode::InvalidParams));
- tooling::Range RequestedRange;
- if (Rng) {
- llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start);
- if (!Begin)
- return CB(Begin.takeError());
- llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end);
- if (!End)
- return CB(End.takeError());
- RequestedRange = tooling::Range(*Begin, *End - *Begin);
+ std::vector<tooling::Range> RequestedRanges;
+ if (!Rngs.empty()) {
+ RequestedRanges.reserve(Rngs.size());
+ for (const auto &Rng : Rngs) {
+ llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng.start);
+ if (!Begin)
+ return CB(Begin.takeError());
+ llvm::Expected<size_t> 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<tooling::Range>{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/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h
index a416602251428..c37857c55f931 100644
--- a/clang-tools-extra/clangd/ClangdServer.h
+++ b/clang-tools-extra/clangd/ClangdServer.h
@@ -316,8 +316,8 @@ class ClangdServer {
bool AddContainer, Callback<ReferencesResult> 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<Range> Rng,
+ /// If \p Rng is non-empty, formats only those regions.
+ void formatFile(PathRef File, const std::vector<Range> &Rngs,
Callback<tooling::Replacements> CB);
/// Run formatting after \p TriggerText was typed at \p Pos in \p File with
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index a6370649f5ad1..c69a55b759f8c 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -632,7 +632,9 @@ bool fromJSON(const llvm::json::Value &Params,
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/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index e88c804692568..d1bce57fe245c 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -850,7 +850,10 @@ struct DocumentRangeFormattingParams {
TextDocumentIdentifier textDocument;
/// The range to format
- Range range;
+ std::optional<Range> range;
+
+ /// The list of ranges to format
+ std::optional<std::vector<Range>> ranges;
};
bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &,
llvm::json::Path);
diff --git a/clang-tools-extra/clangd/test/initialize-params.test b/clang-tools-extra/clangd/test/initialize-params.test
index a1fdae9870ab6..379f5f92018df 100644
--- 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
index 864337b98f446..4c4b34e742eb8 100644
--- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp
+++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp
@@ -943,7 +943,7 @@ void f() {}
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<bool>(Replaces));
auto Changed = tooling::applyAllReplacements(Code, *Replaces);
EXPECT_TRUE(static_cast<bool>(Changed));
diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp
index d48622eba5378..7e8c8e22acf95 100644
--- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp
+++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp
@@ -116,9 +116,10 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
}
llvm::Expected<tooling::Replacements>
-runFormatFile(ClangdServer &Server, PathRef File, std::optional<Range> Rng) {
+runFormatFile(ClangdServer &Server, PathRef File,
+ const std::vector<Range> &Rngs) {
std::optional<llvm::Expected<tooling::Replacements>> Result;
- Server.formatFile(File, Rng, capture(Result));
+ Server.formatFile(File, Rngs, capture(Result));
return std::move(*Result);
}
diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.h b/clang-tools-extra/clangd/unittests/SyncAPI.h
index cf3de4f742e84..35ebd2574dda3 100644
--- a/clang-tools-extra/clangd/unittests/SyncAPI.h
+++ b/clang-tools-extra/clangd/unittests/SyncAPI.h
@@ -53,7 +53,7 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
const clangd::RenameOptions &RenameOpts);
llvm::Expected<tooling::Replacements>
-runFormatFile(ClangdServer &Server, PathRef File, std::optional<Range>);
+runFormatFile(ClangdServer &Server, PathRef File, const std::vector<Range> &);
SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query);
SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req);
|
Hmm so it looks like there have been some changes to the proposed LSP spec since my original phabricator diff, so I think I need to adapt this PR a bit. Will convert to a draft for now. |
5a807c6
to
0377dc3
Compare
Adapted to spec, it's now a separate request (textDocument/rangesFormatting). Will add some tests if this gets positive feedback |
0377dc3
to
d7a498d
Compare
(ping) |
As part of the upcoming 3.18 spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#documentRangesFormattingParams Issue: clangd/clangd#1635 Differential Revision: https://reviews.llvm.org/D150852
Rebased to fixed merge conflict, extend lit test with test for new |
ping - Support for textDocument/rangesFormatting is about to be merged into neovim, so we'll soon have at least two editors (nvim, vscode) with support for this |
…#27323 While this relies on a proposed LSP 3.18 feature, it's fully backwards compatible, so IMO there's no harm in adding this already. Looks like some servers already support for this e.g. - gopls: https://go-review.googlesource.com/c/tools/+/510235 - clangd: llvm/llvm-project#80180 Fixes #27293
Update: neovim PR has been merged |
ping |
Uses the protocol changes proposed in microsoft/language-server-protocol#1556 and microsoft/vscode#163190
Related issue: clangd/clangd#1635
Old Phabricator review: https://reviews.llvm.org/D150852
Relevant LSP 3.18 spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#documentRangesFormattingParams