Skip to content

Commit 19ae86f

Browse files
committed
[clangd] Add support for textDocument/rangesFormatting
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
1 parent da5a86b commit 19ae86f

File tree

11 files changed

+122
-25
lines changed

11 files changed

+122
-25
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,10 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
583583
{"save", true},
584584
}},
585585
{"documentFormattingProvider", true},
586-
{"documentRangeFormattingProvider", true},
586+
{"documentRangeFormattingProvider",
587+
llvm::json::Object{
588+
{"rangesSupport", true},
589+
}},
587590
{"documentOnTypeFormattingProvider",
588591
llvm::json::Object{
589592
{"firstTriggerCharacter", "\n"},
@@ -944,9 +947,17 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
944947
void ClangdLSPServer::onDocumentRangeFormatting(
945948
const DocumentRangeFormattingParams &Params,
946949
Callback<std::vector<TextEdit>> Reply) {
950+
onDocumentRangesFormatting(
951+
DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
952+
std::move(Reply));
953+
}
954+
955+
void ClangdLSPServer::onDocumentRangesFormatting(
956+
const DocumentRangesFormattingParams &Params,
957+
Callback<std::vector<TextEdit>> Reply) {
947958
auto File = Params.textDocument.uri.file();
948959
auto Code = Server->getDraft(File);
949-
Server->formatFile(File, Params.range,
960+
Server->formatFile(File, Params.ranges,
950961
[Code = std::move(Code), Reply = std::move(Reply)](
951962
llvm::Expected<tooling::Replacements> Result) mutable {
952963
if (Result)
@@ -962,7 +973,7 @@ void ClangdLSPServer::onDocumentFormatting(
962973
auto File = Params.textDocument.uri.file();
963974
auto Code = Server->getDraft(File);
964975
Server->formatFile(File,
965-
/*Rng=*/std::nullopt,
976+
/*Rngs=*/{},
966977
[Code = std::move(Code), Reply = std::move(Reply)](
967978
llvm::Expected<tooling::Replacements> Result) mutable {
968979
if (Result)
@@ -1655,6 +1666,7 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
16551666
Bind.method("shutdown", this, &ClangdLSPServer::onShutdown);
16561667
Bind.method("sync", this, &ClangdLSPServer::onSync);
16571668
Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting);
1669+
Bind.method("textDocument/rangesFormatting", this, &ClangdLSPServer::onDocumentRangesFormatting);
16581670
Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting);
16591671
Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting);
16601672
Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction);

clang-tools-extra/clangd/ClangdLSPServer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
104104
Callback<std::vector<TextEdit>>);
105105
void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
106106
Callback<std::vector<TextEdit>>);
107+
void onDocumentRangesFormatting(const DocumentRangesFormattingParams &,
108+
Callback<std::vector<TextEdit>>);
107109
void onDocumentFormatting(const DocumentFormattingParams &,
108110
Callback<std::vector<TextEdit>>);
109111
// The results are serialized 'vector<DocumentSymbol>' if

clang-tools-extra/clangd/ClangdServer.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -500,29 +500,32 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos,
500500
std::move(Action));
501501
}
502502

503-
void ClangdServer::formatFile(PathRef File, std::optional<Range> Rng,
503+
void ClangdServer::formatFile(PathRef File, const std::vector<Range> &Rngs,
504504
Callback<tooling::Replacements> CB) {
505505
auto Code = getDraft(File);
506506
if (!Code)
507507
return CB(llvm::make_error<LSPError>("trying to format non-added document",
508508
ErrorCode::InvalidParams));
509-
tooling::Range RequestedRange;
510-
if (Rng) {
511-
llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start);
512-
if (!Begin)
513-
return CB(Begin.takeError());
514-
llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end);
515-
if (!End)
516-
return CB(End.takeError());
517-
RequestedRange = tooling::Range(*Begin, *End - *Begin);
509+
std::vector<tooling::Range> RequestedRanges;
510+
if (!Rngs.empty()) {
511+
RequestedRanges.reserve(Rngs.size());
512+
for (const auto &Rng : Rngs) {
513+
llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng.start);
514+
if (!Begin)
515+
return CB(Begin.takeError());
516+
llvm::Expected<size_t> End = positionToOffset(*Code, Rng.end);
517+
if (!End)
518+
return CB(End.takeError());
519+
RequestedRanges.emplace_back(*Begin, *End - *Begin);
520+
}
518521
} else {
519-
RequestedRange = tooling::Range(0, Code->size());
522+
RequestedRanges = {tooling::Range(0, Code->size())};
520523
}
521524

522525
// Call clang-format.
523526
auto Action = [File = File.str(), Code = std::move(*Code),
524-
Ranges = std::vector<tooling::Range>{RequestedRange},
525-
CB = std::move(CB), this]() mutable {
527+
Ranges = std::move(RequestedRanges), CB = std::move(CB),
528+
this]() mutable {
526529
format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS, true);
527530
tooling::Replacements IncludeReplaces =
528531
format::sortIncludes(Style, Code, Ranges, File);

clang-tools-extra/clangd/ClangdServer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,8 @@ class ClangdServer {
316316
bool AddContainer, Callback<ReferencesResult> CB);
317317

318318
/// Run formatting for the \p File with content \p Code.
319-
/// If \p Rng is non-null, formats only that region.
320-
void formatFile(PathRef File, std::optional<Range> Rng,
319+
/// If \p Rng is non-empty, formats only those regions.
320+
void formatFile(PathRef File, const std::vector<Range> &Rngs,
321321
Callback<tooling::Replacements> CB);
322322

323323
/// Run formatting after \p TriggerText was typed at \p Pos in \p File with

clang-tools-extra/clangd/Protocol.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,15 @@ bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R,
633633
llvm::json::Path P) {
634634
llvm::json::ObjectMapper O(Params, P);
635635
return O && O.map("textDocument", R.textDocument) && O.map("range", R.range);
636+
;
637+
}
638+
639+
bool fromJSON(const llvm::json::Value &Params,
640+
DocumentRangesFormattingParams &R, llvm::json::Path P) {
641+
llvm::json::ObjectMapper O(Params, P);
642+
return O && O.map("textDocument", R.textDocument) &&
643+
O.map("ranges", R.ranges);
644+
;
636645
}
637646

638647
bool fromJSON(const llvm::json::Value &Params,

clang-tools-extra/clangd/Protocol.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,23 @@ struct DocumentRangeFormattingParams {
851851

852852
/// The range to format
853853
Range range;
854+
855+
/// The list of ranges to format
856+
std::optional<std::vector<Range>> ranges;
854857
};
855858
bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &,
856859
llvm::json::Path);
857860

861+
struct DocumentRangesFormattingParams {
862+
/// The document to format.
863+
TextDocumentIdentifier textDocument;
864+
865+
/// The list of ranges to format
866+
std::vector<Range> ranges;
867+
};
868+
bool fromJSON(const llvm::json::Value &, DocumentRangesFormattingParams &,
869+
llvm::json::Path);
870+
858871
struct DocumentOnTypeFormattingParams {
859872
/// The document to format.
860873
TextDocumentIdentifier textDocument;

clang-tools-extra/clangd/test/formatting.test

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,65 @@
135135
# CHECK-NEXT: "jsonrpc": "2.0",
136136
# CHECK-NEXT: "result": []
137137
---
138+
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":9},"contentChanges":[{"text":"int foo( int x){\n x=x+1;\nreturn x;\n}"}]}}
139+
---
140+
{"jsonrpc":"2.0","id":5,"method":"textDocument/rangesFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"ranges":[{"start":{"line":0,"character":0},"end":{"line":0,"character":15}}, {"start":{"line":2,"character":0},"end":{"line":2,"character":5}}]}}
141+
---
142+
# CHECK: "id": 5,
143+
# CHECK-NEXT: "jsonrpc": "2.0",
144+
# CHECK-NEXT: "result": [
145+
# CHECK-NEXT: {
146+
# CHECK-NEXT: "newText": "",
147+
# CHECK-NEXT: "range": {
148+
# CHECK-NEXT: "end": {
149+
# CHECK-NEXT: "character": 10,
150+
# CHECK-NEXT: "line": 0
151+
# CHECK-NEXT: },
152+
# CHECK-NEXT: "start": {
153+
# CHECK-NEXT: "character": 8,
154+
# CHECK-NEXT: "line": 0
155+
# CHECK-NEXT: }
156+
# CHECK-NEXT: }
157+
# CHECK-NEXT: },
158+
# CHECK-NEXT: {
159+
# CHECK-NEXT: "newText": " ",
160+
# CHECK-NEXT: "range": {
161+
# CHECK-NEXT: "end": {
162+
# CHECK-NEXT: "character": 16,
163+
# CHECK-NEXT: "line": 0
164+
# CHECK-NEXT: },
165+
# CHECK-NEXT: "start": {
166+
# CHECK-NEXT: "character": 16,
167+
# CHECK-NEXT: "line": 0
168+
# CHECK-NEXT: }
169+
# CHECK-NEXT: }
170+
# CHECK-NEXT: },
171+
# CHECK-NEXT: {
172+
# CHECK-NEXT: "newText": "\n ",
173+
# CHECK-NEXT: "range": {
174+
# CHECK-NEXT: "end": {
175+
# CHECK-NEXT: "character": 0,
176+
# CHECK-NEXT: "line": 2
177+
# CHECK-NEXT: },
178+
# CHECK-NEXT: "start": {
179+
# CHECK-NEXT: "character": 8,
180+
# CHECK-NEXT: "line": 1
181+
# CHECK-NEXT: }
182+
# CHECK-NEXT: }
183+
# CHECK-NEXT: }
184+
# CHECK-NEXT: ]
185+
---
186+
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":9},"contentChanges":[{"text":"int foo(int x) {\n x=x+1;\n return x;\n}"}]}}
187+
---
188+
{"jsonrpc":"2.0","id":6,"method":"textDocument/rangesFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"ranges":[{"start":{"line":0,"character":0},"end":{"line":0,"character":15}}, {"start":{"line":2,"character":0},"end":{"line":2,"character":5}}]}}
189+
# CHECK: "id": 6,
190+
# CHECK-NEXT: "jsonrpc": "2.0",
191+
# CHECK-NEXT: "result": []
192+
---
138193
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":5},"contentChanges":[{"text":"int x=\n"}]}}
139194
---
140-
{"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"position":{"line":1,"character":0},"ch":"\n"}}
141-
# CHECK: "id": 5,
195+
{"jsonrpc":"2.0","id":7,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"position":{"line":1,"character":0},"ch":"\n"}}
196+
# CHECK: "id": 7,
142197
# CHECK-NEXT: "jsonrpc": "2.0",
143198
# CHECK-NEXT: "result": [
144199
# CHECK-NEXT: {

clang-tools-extra/clangd/test/initialize-params.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
# CHECK-NEXT: "firstTriggerCharacter": "\n",
3636
# CHECK-NEXT: "moreTriggerCharacter": []
3737
# CHECK-NEXT: },
38-
# CHECK-NEXT: "documentRangeFormattingProvider": true,
38+
# CHECK-NEXT: "documentRangeFormattingProvider": {
39+
# CHECK-NEXT: "rangesSupport": true
40+
# CHECK-NEXT: },
3941
# CHECK-NEXT: "documentSymbolProvider": true,
4042
# CHECK-NEXT: "executeCommandProvider": {
4143
# CHECK-NEXT: "commands": [

clang-tools-extra/clangd/unittests/ClangdTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ void f() {}
943943
FS.Files[Path] = Code;
944944
runAddDocument(Server, Path, Code);
945945

946-
auto Replaces = runFormatFile(Server, Path, /*Rng=*/std::nullopt);
946+
auto Replaces = runFormatFile(Server, Path, /*Rngs=*/{});
947947
EXPECT_TRUE(static_cast<bool>(Replaces));
948948
auto Changed = tooling::applyAllReplacements(Code, *Replaces);
949949
EXPECT_TRUE(static_cast<bool>(Changed));

clang-tools-extra/clangd/unittests/SyncAPI.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,10 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
116116
}
117117

118118
llvm::Expected<tooling::Replacements>
119-
runFormatFile(ClangdServer &Server, PathRef File, std::optional<Range> Rng) {
119+
runFormatFile(ClangdServer &Server, PathRef File,
120+
const std::vector<Range> &Rngs) {
120121
std::optional<llvm::Expected<tooling::Replacements>> Result;
121-
Server.formatFile(File, Rng, capture(Result));
122+
Server.formatFile(File, Rngs, capture(Result));
122123
return std::move(*Result);
123124
}
124125

clang-tools-extra/clangd/unittests/SyncAPI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
5353
const clangd::RenameOptions &RenameOpts);
5454

5555
llvm::Expected<tooling::Replacements>
56-
runFormatFile(ClangdServer &Server, PathRef File, std::optional<Range>);
56+
runFormatFile(ClangdServer &Server, PathRef File, const std::vector<Range> &);
5757

5858
SymbolSlab runFuzzyFind(const SymbolIndex &Index, StringRef Query);
5959
SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req);

0 commit comments

Comments
 (0)