Tom Praschan 2025-05-26 10:08:18 +02:00 committed by GitHub
parent 3f29acb517
commit 926c201323
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 119 additions and 25 deletions

View File

@ -591,7 +591,10 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
{"save", true}, {"save", true},
}}, }},
{"documentFormattingProvider", true}, {"documentFormattingProvider", true},
{"documentRangeFormattingProvider", true}, {"documentRangeFormattingProvider",
llvm::json::Object{
{"rangesSupport", true},
}},
{"documentOnTypeFormattingProvider", {"documentOnTypeFormattingProvider",
llvm::json::Object{ llvm::json::Object{
{"firstTriggerCharacter", "\n"}, {"firstTriggerCharacter", "\n"},
@ -952,9 +955,17 @@ void ClangdLSPServer::onDocumentOnTypeFormatting(
void ClangdLSPServer::onDocumentRangeFormatting( void ClangdLSPServer::onDocumentRangeFormatting(
const DocumentRangeFormattingParams &Params, const DocumentRangeFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) { Callback<std::vector<TextEdit>> Reply) {
onDocumentRangesFormatting(
DocumentRangesFormattingParams{Params.textDocument, {Params.range}},
std::move(Reply));
}
void ClangdLSPServer::onDocumentRangesFormatting(
const DocumentRangesFormattingParams &Params,
Callback<std::vector<TextEdit>> Reply) {
auto File = Params.textDocument.uri.file(); auto File = Params.textDocument.uri.file();
auto Code = Server->getDraft(File); auto Code = Server->getDraft(File);
Server->formatFile(File, Params.range, Server->formatFile(File, Params.ranges,
[Code = std::move(Code), Reply = std::move(Reply)]( [Code = std::move(Code), Reply = std::move(Reply)](
llvm::Expected<tooling::Replacements> Result) mutable { llvm::Expected<tooling::Replacements> Result) mutable {
if (Result) if (Result)
@ -970,7 +981,7 @@ void ClangdLSPServer::onDocumentFormatting(
auto File = Params.textDocument.uri.file(); auto File = Params.textDocument.uri.file();
auto Code = Server->getDraft(File); auto Code = Server->getDraft(File);
Server->formatFile(File, Server->formatFile(File,
/*Rng=*/std::nullopt, /*Rngs=*/{},
[Code = std::move(Code), Reply = std::move(Reply)]( [Code = std::move(Code), Reply = std::move(Reply)](
llvm::Expected<tooling::Replacements> Result) mutable { llvm::Expected<tooling::Replacements> Result) mutable {
if (Result) if (Result)
@ -1666,6 +1677,7 @@ void ClangdLSPServer::bindMethods(LSPBinder &Bind,
Bind.method("shutdown", this, &ClangdLSPServer::onShutdown); Bind.method("shutdown", this, &ClangdLSPServer::onShutdown);
Bind.method("sync", this, &ClangdLSPServer::onSync); Bind.method("sync", this, &ClangdLSPServer::onSync);
Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting); Bind.method("textDocument/rangeFormatting", this, &ClangdLSPServer::onDocumentRangeFormatting);
Bind.method("textDocument/rangesFormatting", this, &ClangdLSPServer::onDocumentRangesFormatting);
Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting); Bind.method("textDocument/onTypeFormatting", this, &ClangdLSPServer::onDocumentOnTypeFormatting);
Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting); Bind.method("textDocument/formatting", this, &ClangdLSPServer::onDocumentFormatting);
Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction); Bind.method("textDocument/codeAction", this, &ClangdLSPServer::onCodeAction);

View File

@ -110,6 +110,8 @@ private:
Callback<std::vector<TextEdit>>); Callback<std::vector<TextEdit>>);
void onDocumentRangeFormatting(const DocumentRangeFormattingParams &, void onDocumentRangeFormatting(const DocumentRangeFormattingParams &,
Callback<std::vector<TextEdit>>); Callback<std::vector<TextEdit>>);
void onDocumentRangesFormatting(const DocumentRangesFormattingParams &,
Callback<std::vector<TextEdit>>);
void onDocumentFormatting(const DocumentFormattingParams &, void onDocumentFormatting(const DocumentFormattingParams &,
Callback<std::vector<TextEdit>>); Callback<std::vector<TextEdit>>);
// The results are serialized 'vector<DocumentSymbol>' if // The results are serialized 'vector<DocumentSymbol>' if

View File

@ -521,29 +521,32 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos,
std::move(Action)); 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) { Callback<tooling::Replacements> CB) {
auto Code = getDraft(File); auto Code = getDraft(File);
if (!Code) if (!Code)
return CB(llvm::make_error<LSPError>("trying to format non-added document", return CB(llvm::make_error<LSPError>("trying to format non-added document",
ErrorCode::InvalidParams)); ErrorCode::InvalidParams));
tooling::Range RequestedRange; std::vector<tooling::Range> RequestedRanges;
if (Rng) { if (!Rngs.empty()) {
llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng->start); RequestedRanges.reserve(Rngs.size());
if (!Begin) for (const auto &Rng : Rngs) {
return CB(Begin.takeError()); llvm::Expected<size_t> Begin = positionToOffset(*Code, Rng.start);
llvm::Expected<size_t> End = positionToOffset(*Code, Rng->end); if (!Begin)
if (!End) return CB(Begin.takeError());
return CB(End.takeError()); llvm::Expected<size_t> End = positionToOffset(*Code, Rng.end);
RequestedRange = tooling::Range(*Begin, *End - *Begin); if (!End)
return CB(End.takeError());
RequestedRanges.emplace_back(*Begin, *End - *Begin);
}
} else { } else {
RequestedRange = tooling::Range(0, Code->size()); RequestedRanges = {tooling::Range(0, Code->size())};
} }
// Call clang-format. // Call clang-format.
auto Action = [File = File.str(), Code = std::move(*Code), auto Action = [File = File.str(), Code = std::move(*Code),
Ranges = std::vector<tooling::Range>{RequestedRange}, Ranges = std::move(RequestedRanges), CB = std::move(CB),
CB = std::move(CB), this]() mutable { this]() mutable {
format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS, true); format::FormatStyle Style = getFormatStyleForFile(File, Code, TFS, true);
tooling::Replacements IncludeReplaces = tooling::Replacements IncludeReplaces =
format::sortIncludes(Style, Code, Ranges, File); format::sortIncludes(Style, Code, Ranges, File);

View File

@ -329,8 +329,8 @@ public:
bool AddContainer, Callback<ReferencesResult> CB); bool AddContainer, Callback<ReferencesResult> CB);
/// Run formatting for the \p File with content \p Code. /// Run formatting for the \p File with content \p Code.
/// If \p Rng is non-null, formats only that region. /// If \p Rng is non-empty, formats only those regions.
void formatFile(PathRef File, std::optional<Range> Rng, void formatFile(PathRef File, const std::vector<Range> &Rngs,
Callback<tooling::Replacements> CB); Callback<tooling::Replacements> CB);
/// Run formatting after \p TriggerText was typed at \p Pos in \p File with /// Run formatting after \p TriggerText was typed at \p Pos in \p File with

View File

@ -672,6 +672,15 @@ bool fromJSON(const llvm::json::Value &Params, DocumentRangeFormattingParams &R,
llvm::json::Path P) { llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, 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);
;
}
bool fromJSON(const llvm::json::Value &Params,
DocumentRangesFormattingParams &R, llvm::json::Path P) {
llvm::json::ObjectMapper O(Params, P);
return O && O.map("textDocument", R.textDocument) &&
O.map("ranges", R.ranges);
;
} }
bool fromJSON(const llvm::json::Value &Params, bool fromJSON(const llvm::json::Value &Params,

View File

@ -858,6 +858,16 @@ struct DocumentRangeFormattingParams {
bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &, bool fromJSON(const llvm::json::Value &, DocumentRangeFormattingParams &,
llvm::json::Path); llvm::json::Path);
struct DocumentRangesFormattingParams {
/// The document to format.
TextDocumentIdentifier textDocument;
/// The list of ranges to format
std::vector<Range> ranges;
};
bool fromJSON(const llvm::json::Value &, DocumentRangesFormattingParams &,
llvm::json::Path);
struct DocumentOnTypeFormattingParams { struct DocumentOnTypeFormattingParams {
/// The document to format. /// The document to format.
TextDocumentIdentifier textDocument; TextDocumentIdentifier textDocument;

View File

@ -135,10 +135,65 @@
# CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": [] # CHECK-NEXT: "result": []
--- ---
{"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}"}]}}
---
{"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}}]}}
---
# CHECK: "id": 5,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": [
# CHECK-NEXT: {
# CHECK-NEXT: "newText": "",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 10,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 8,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "newText": " ",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 16,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 16,
# CHECK-NEXT: "line": 0
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: },
# CHECK-NEXT: {
# CHECK-NEXT: "newText": "\n ",
# CHECK-NEXT: "range": {
# CHECK-NEXT: "end": {
# CHECK-NEXT: "character": 0,
# CHECK-NEXT: "line": 2
# CHECK-NEXT: },
# CHECK-NEXT: "start": {
# CHECK-NEXT: "character": 8,
# CHECK-NEXT: "line": 1
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: }
# CHECK-NEXT: ]
---
{"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}"}]}}
---
{"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}}]}}
# CHECK: "id": 6,
# CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": []
---
{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":5},"contentChanges":[{"text":"int x=\n"}]}} {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":5},"contentChanges":[{"text":"int x=\n"}]}}
--- ---
{"jsonrpc":"2.0","id":5,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"position":{"line":1,"character":0},"ch":"\n"}} {"jsonrpc":"2.0","id":7,"method":"textDocument/onTypeFormatting","params":{"textDocument":{"uri":"test:///foo.c"},"position":{"line":1,"character":0},"ch":"\n"}}
# CHECK: "id": 5, # CHECK: "id": 7,
# CHECK-NEXT: "jsonrpc": "2.0", # CHECK-NEXT: "jsonrpc": "2.0",
# CHECK-NEXT: "result": [ # CHECK-NEXT: "result": [
# CHECK-NEXT: { # CHECK-NEXT: {

View File

@ -35,7 +35,9 @@
# CHECK-NEXT: "firstTriggerCharacter": "\n", # CHECK-NEXT: "firstTriggerCharacter": "\n",
# CHECK-NEXT: "moreTriggerCharacter": [] # CHECK-NEXT: "moreTriggerCharacter": []
# CHECK-NEXT: }, # CHECK-NEXT: },
# CHECK-NEXT: "documentRangeFormattingProvider": true, # CHECK-NEXT: "documentRangeFormattingProvider": {
# CHECK-NEXT: "rangesSupport": true
# CHECK-NEXT: },
# CHECK-NEXT: "documentSymbolProvider": true, # CHECK-NEXT: "documentSymbolProvider": true,
# CHECK-NEXT: "executeCommandProvider": { # CHECK-NEXT: "executeCommandProvider": {
# CHECK-NEXT: "commands": [ # CHECK-NEXT: "commands": [

View File

@ -944,7 +944,7 @@ void f() {}
FS.Files[Path] = Code; FS.Files[Path] = Code;
runAddDocument(Server, 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)); EXPECT_TRUE(static_cast<bool>(Replaces));
auto Changed = tooling::applyAllReplacements(Code, *Replaces); auto Changed = tooling::applyAllReplacements(Code, *Replaces);
EXPECT_TRUE(static_cast<bool>(Changed)); EXPECT_TRUE(static_cast<bool>(Changed));

View File

@ -116,9 +116,10 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
} }
llvm::Expected<tooling::Replacements> 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; std::optional<llvm::Expected<tooling::Replacements>> Result;
Server.formatFile(File, Rng, capture(Result)); Server.formatFile(File, Rngs, capture(Result));
return std::move(*Result); return std::move(*Result);
} }

View File

@ -53,7 +53,7 @@ runPrepareRename(ClangdServer &Server, PathRef File, Position Pos,
const clangd::RenameOptions &RenameOpts); const clangd::RenameOptions &RenameOpts);
llvm::Expected<tooling::Replacements> 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, StringRef Query);
SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req); SymbolSlab runFuzzyFind(const SymbolIndex &Index, const FuzzyFindRequest &Req);