Skip to content

Commit 926c201

Browse files
authored
[clangd] Add support for textDocument/rangesFormatting (#141208)
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 Old PR: #80180, #141052
1 parent 3f29acb commit 926c201

File tree

11 files changed

+119
-25
lines changed

11 files changed

+119
-25
lines changed

clang-tools-extra/clangd/ClangdLSPServer.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,10 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
591591
{"save", true},
592592
}},
593593
{"documentFormattingProvider", true},
594-
{"documentRangeFormattingProvider", true},
594+
{"documentRangeFormattingProvider",
595+
llvm::json::Object{
596+
{"rangesSupport", true},
597+
}},
595598
{"documentOnTypeFormattingProvider",
596599
llvm::json::Object{
597600
{"firstTriggerCharacter", "\n"},
@@ -952,9 +955,17 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
952955
void ClangdLSPServer::onDocumentRangeFormatting(
953956
const DocumentRangeFormattingParams &Params,
954957
Callback<std::vector<TextEdit>> Reply) {
958+
onDocumentRangesFormatting(
959+
DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
960+
std::move(Reply));
961+
}
962+
963+
void ClangdLSPServer::onDocumentRangesFormatting(
964+
const DocumentRangesFormattingParams &Params,
965+
Callback<std::vector<TextEdit>> Reply) {
955966
auto File = Params.textDocument.uri.file();
956967
auto Code = Server->getDraft(File);
957-
Server->formatFile(File, Params.range,
968+
Server->formatFile(File, Params.ranges,
958969
[Code = std::move(Code), Reply = std::move(Reply)](
959970
llvm::Expected<tooling::Replacements> Result) mutable {
960971
if (Result)
@@ -970,7 +981,7 @@ void ClangdLSPServer::onDocumentFormatting(
970981
auto File = Params.textDocument.uri.file();
971982
auto Code = Server->getDraft(File);
972983
Server->formatFile(File,
973-
/*Rng=*/std::nullopt,
984+
/*Rngs=*/{},
974985
[Code = std::move(Code), Reply = std::move(Reply)](
975986
llvm::Expected<tooling::Replacements> Result) mutable {
976987
if (Result)
@@ -1666,6 +1677,7 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
16661677
Bind.method("shutdown", this, &ClangdLSPServer::onShutdown);
16671678
Bind.method("sync", this, &ClangdLSPServer::onSync);
16681679
Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting);
1680+
Bind.method("textDocument/rangesFormatting", this, &ClangdLSPServer::onDocumentRangesFormatting);
16691681
Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting);
16701682
Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting);
16711683
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
@@ -110,6 +110,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
110110
Callback<std::vector<TextEdit>>);
111111
void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
112112
Callback<std::vector<TextEdit>>);
113+
void onDocumentRangesFormatting(const DocumentRangesFormattingParams &,
114+
Callback<std::vector<TextEdit>>);
113115
void onDocumentFormatting(const DocumentFormattingParams &,
114116
Callback<std::vector<TextEdit>>);
115117
// 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
@@ -521,29 +521,32 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos,
521521
std::move(Action));
522522
}
523523

524-
void ClangdServer::formatFile(PathRef File, std::optional<Range> Rng,
524+
void ClangdServer::formatFile(PathRef File, const std::vector<Range> &Rngs,
525525
Callback<tooling::Replacements> CB) {
526526
auto Code = getDraft(File);
527527
if (!Code)
528528
return CB(llvm::make_error<LSPError>("trying to format non-added document",
529529
ErrorCode::InvalidParams));
530-
tooling::Range RequestedRange;
531-
if (Rng) {
532-
llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start);
533-
if (!Begin)
534-
return CB(Begin.takeError());
535-
llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end);
536-
if (!End)
537-
return CB(End.takeError());
538-
RequestedRange = tooling::Range(*Begin, *End - *Begin);
530+
std::vector<tooling::Range> RequestedRanges;
531+
if (!Rngs.empty()) {
532+
RequestedRanges.reserve(Rngs.size());
533+
for (const auto &Rng : Rngs) {
534+
llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng.start);
535+
if (!Begin)
536+
return CB(Begin.takeError());
537+
llvm::Expected<size_t> End = positionToOffset(*Code, Rng.end);
538+
if (!End)
539+
return CB(End.takeError());
540+
RequestedRanges.emplace_back(*Begin, *End - *Begin);
541+
}
539542
} else {
540-
RequestedRange = tooling::Range(0, Code->size());
543+
RequestedRanges = {tooling::Range(0, Code->size())};
541544
}
542545

543546
// Call clang-format.
544547
auto Action = [File = File.str(), Code = std::move(*Code),
545-
Ranges = std::vector<tooling::Range>{RequestedRange},
546-
CB = std::move(CB), this]() mutable {
548+
Ranges = std::move(RequestedRanges), CB = std::move(CB),
549+
this]() mutable {
547550
format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS, true);
548551
tooling::Replacements IncludeReplaces =
549552
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
@@ -329,8 +329,8 @@ class ClangdServer {
329329
bool AddContainer, Callback<ReferencesResult> CB);
330330

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

336336
/// 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
@@ -672,6 +672,15 @@ bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R,
672672
llvm::json::Path P) {
673673
llvm::json::ObjectMapper O(Params, P);
674674
return O && O.map("textDocument", R.textDocument) && O.map("range", R.range);
675+
;
676+
}
677+
678+
bool fromJSON(const llvm::json::Value &Params,
679+
DocumentRangesFormattingParams &R, llvm::json::Path P) {
680+
llvm::json::ObjectMapper O(Params, P);
681+
return O && O.map("textDocument", R.textDocument) &&
682+
O.map("ranges", R.ranges);
683+
;
675684
}
676685

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

clang-tools-extra/clangd/Protocol.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,16 @@ struct DocumentRangeFormattingParams {
858858
bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &,
859859
llvm::json::Path);
860860

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+
861871
struct DocumentOnTypeFormattingParams {
862872
/// The document to format.
863873
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
@@ -944,7 +944,7 @@ void f() {}
944944
FS.Files[Path] = Code;
945945
runAddDocument(Server, Path, Code);
946946

947-
auto Replaces = runFormatFile(Server, Path, /*Rng=*/std::nullopt);
947+
auto Replaces = runFormatFile(Server, Path, /*Rngs=*/{});
948948
EXPECT_TRUE(static_cast<bool>(Replaces));
949949
auto Changed = tooling::applyAllReplacements(Code, *Replaces);
950950
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)