From dc2ee5bd5efb9be75cf79f889f7cf7ca2380e2ab Mon Sep 17 00:00:00 2001 From: DavidLoftus <43099493+DavidLoftus@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:38:38 +0000 Subject: [PATCH] Implement `lsp/formatting` for Carbon::LanguageServer (#7688) Implements formatting support within LSP. Current implementation performs while file formatting, we just replace file with output of Carbon::Format() https://github.com/carbon-language/carbon-lang/pull/7687 independently improves formatting so that this output is somewhat decent. Assisted-With: Gemini / Antigravity --- toolchain/language_server/BUILD | 1 + toolchain/language_server/handle.h | 7 + .../language_server/handle_formatting.cpp | 57 ++++++++ .../language_server/handle_initialize.cpp | 1 + .../language_server/incoming_messages.cpp | 1 + .../basics/fail_shutdown_without_exit.carbon | 3 +- .../testdata/basics/initialize.carbon | 9 +- .../testdata/basics/notify_parse_error.carbon | 3 +- .../testdata/document_symbol/basics.carbon | 3 +- .../testdata/document_symbol/choice.carbon | 3 +- .../decl_with_parse_error.carbon | 3 +- .../document_symbol/fn_definition.carbon | 3 +- .../document_symbol/incomplete.carbon | 3 +- .../testdata/document_symbol/language.carbon | 3 +- .../testdata/document_symbol/namespace.carbon | 3 +- .../testdata/document_symbol/unknown.carbon | 3 +- .../testdata/formatting/basic.carbon | 137 ++++++++++++++++++ .../testdata/formatting/errors.carbon | 79 ++++++++++ .../testdata/formatting/unknown.carbon | 46 ++++++ .../testdata/position/bad_params.carbon | 3 +- .../testdata/position/hover_and_goto.carbon | 3 +- .../text_document/change_unknown.carbon | 3 +- .../text_document/close_unknown.carbon | 3 +- .../testdata/text_document/diagnostics.carbon | 3 +- .../text_document/import_prelude.carbon | 3 +- .../text_document/incremental_sync.carbon | 3 +- .../incremental_sync_multiline.carbon | 3 +- .../text_document/multiple_files.carbon | 3 +- .../text_document/open_change_close.carbon | 3 +- .../text_document/open_duplicate.carbon | 3 +- .../open_with_cpp_nonexistent.carbon | 3 +- 31 files changed, 379 insertions(+), 25 deletions(-) create mode 100644 toolchain/language_server/handle_formatting.cpp create mode 100644 toolchain/language_server/testdata/formatting/basic.carbon create mode 100644 toolchain/language_server/testdata/formatting/errors.carbon create mode 100644 toolchain/language_server/testdata/formatting/unknown.carbon diff --git a/toolchain/language_server/BUILD b/toolchain/language_server/BUILD index 60579df93ade..7ae247a31382 100644 --- a/toolchain/language_server/BUILD +++ b/toolchain/language_server/BUILD @@ -102,6 +102,7 @@ cc_library( "//common:check", "//common:raw_string_ostream", "//toolchain/base:shared_value_stores", + "//toolchain/format", "//toolchain/lex", "//toolchain/lex:token_index", "//toolchain/lex:token_kind", diff --git a/toolchain/language_server/handle.h b/toolchain/language_server/handle.h index 9964d4c5e634..27b634e736c7 100644 --- a/toolchain/language_server/handle.h +++ b/toolchain/language_server/handle.h @@ -44,6 +44,13 @@ auto HandleDocumentSymbol( auto(llvm::Expected>)->void> on_done) -> void; +// Formats a document. +auto HandleFormatting( + Context& context, const clang::clangd::DocumentFormattingParams& params, + llvm::function_ref< + auto(llvm::Expected>)->void> + on_done) -> void; + // Provides the type of the entity at a position. auto HandleHover( Context& context, const clang::clangd::TextDocumentPositionParams& params, diff --git a/toolchain/language_server/handle_formatting.cpp b/toolchain/language_server/handle_formatting.cpp new file mode 100644 index 000000000000..4ea836ac0d15 --- /dev/null +++ b/toolchain/language_server/handle_formatting.cpp @@ -0,0 +1,57 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +#include "common/raw_string_ostream.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "toolchain/format/format.h" +#include "toolchain/language_server/handle.h" + +namespace Carbon::LanguageServer { + +// Returns the range covering the entire document text. +static auto GetFullDocumentRange(llvm::StringRef text) -> clang::clangd::Range { + int line_count = llvm::count(text, '\n') + 1; + return clang::clangd::Range{ + .start = {.line = 0, .character = 0}, + .end = {.line = line_count, .character = 0}, + }; +} + +auto HandleFormatting( + Context& context, const clang::clangd::DocumentFormattingParams& params, + llvm::function_ref< + auto(llvm::Expected>)->void> + on_done) -> void { + auto* file = context.LookupFile(params.textDocument.uri.file()); + if (!file) { + return; + } + + RawStringOstream out; + if (!Format::Format(file->tokens(), out)) { + out.clear(); + on_done(std::vector()); + return; + } + + std::string formatted_text = out.TakeStr(); + if (formatted_text == file->text()) { + on_done(std::vector()); + return; + } + + std::vector edits; + edits.push_back(clang::clangd::TextEdit{ + .range = GetFullDocumentRange(file->text()), + .newText = std::move(formatted_text), + }); + on_done(std::move(edits)); +} + +} // namespace Carbon::LanguageServer diff --git a/toolchain/language_server/handle_initialize.cpp b/toolchain/language_server/handle_initialize.cpp index 0c48b70542ed..422f834aefc6 100644 --- a/toolchain/language_server/handle_initialize.cpp +++ b/toolchain/language_server/handle_initialize.cpp @@ -32,6 +32,7 @@ auto HandleInitialize( llvm::json::Object capabilities{{"declarationProvider", true}, {"definitionProvider", true}, + {"documentFormattingProvider", true}, {"documentSymbolProvider", true}, {"hoverProvider", true}, {"positionEncoding", encoding}, diff --git a/toolchain/language_server/incoming_messages.cpp b/toolchain/language_server/incoming_messages.cpp index 36420077dca2..807a38de9725 100644 --- a/toolchain/language_server/incoming_messages.cpp +++ b/toolchain/language_server/incoming_messages.cpp @@ -76,6 +76,7 @@ IncomingMessages::IncomingMessages(clang::clangd::Transport* transport, AddCallHandler("textDocument/declaration", &HandleDefinition); AddCallHandler("textDocument/definition", &HandleDefinition); AddCallHandler("textDocument/documentSymbol", &HandleDocumentSymbol); + AddCallHandler("textDocument/formatting", &HandleFormatting); AddCallHandler("textDocument/hover", &HandleHover); AddCallHandler("textDocument/references", &HandleReferences); AddCallHandler("textDocument/typeDefinition", &HandleTypeDefinition); diff --git a/toolchain/language_server/testdata/basics/fail_shutdown_without_exit.carbon b/toolchain/language_server/testdata/basics/fail_shutdown_without_exit.carbon index 0ce55800a706..3077121e0447 100644 --- a/toolchain/language_server/testdata/basics/fail_shutdown_without_exit.carbon +++ b/toolchain/language_server/testdata/basics/fail_shutdown_without_exit.carbon @@ -15,7 +15,7 @@ // CHECK:STDERR: error: Input/output error [LanguageServerTransportError] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -24,6 +24,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/basics/initialize.carbon b/toolchain/language_server/testdata/basics/initialize.carbon index ee6a2286fe7a..ca52a4fc7a57 100644 --- a/toolchain/language_server/testdata/basics/initialize.carbon +++ b/toolchain/language_server/testdata/basics/initialize.carbon @@ -20,7 +20,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 351{{\r}} +// CHECK:STDOUT: Content-Length: 393{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -29,6 +29,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-8", @@ -37,7 +38,7 @@ // CHECK:STDOUT: "typeDefinitionProvider": true // CHECK:STDOUT: } // CHECK:STDOUT: } -// CHECK:STDOUT: }Content-Length: 352{{\r}} +// CHECK:STDOUT: }Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 2, @@ -46,6 +47,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", @@ -54,7 +56,7 @@ // CHECK:STDOUT: "typeDefinitionProvider": true // CHECK:STDOUT: } // CHECK:STDOUT: } -// CHECK:STDOUT: }Content-Length: 352{{\r}} +// CHECK:STDOUT: }Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 3, @@ -63,6 +65,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/basics/notify_parse_error.carbon b/toolchain/language_server/testdata/basics/notify_parse_error.carbon index 671edae02112..2ad6f8484df4 100644 --- a/toolchain/language_server/testdata/basics/notify_parse_error.carbon +++ b/toolchain/language_server/testdata/basics/notify_parse_error.carbon @@ -19,7 +19,7 @@ // CHECK:STDERR: warning: -32602: in call to `textDocument/didOpen`, JSON parse failed: missing value at (root).textDocument [LanguageServerNotificationParseError] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -28,6 +28,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/basics.carbon b/toolchain/language_server/testdata/document_symbol/basics.carbon index f8244340c091..5bd33bbe8bc1 100644 --- a/toolchain/language_server/testdata/document_symbol/basics.carbon +++ b/toolchain/language_server/testdata/document_symbol/basics.carbon @@ -36,7 +36,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -45,6 +45,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/choice.carbon b/toolchain/language_server/testdata/document_symbol/choice.carbon index 43b070672bc7..4367b27fdfe2 100644 --- a/toolchain/language_server/testdata/document_symbol/choice.carbon +++ b/toolchain/language_server/testdata/document_symbol/choice.carbon @@ -30,7 +30,7 @@ choice Colors { [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -39,6 +39,7 @@ choice Colors { // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/decl_with_parse_error.carbon b/toolchain/language_server/testdata/document_symbol/decl_with_parse_error.carbon index c1736a0201ca..6faa37819ef5 100644 --- a/toolchain/language_server/testdata/document_symbol/decl_with_parse_error.carbon +++ b/toolchain/language_server/testdata/document_symbol/decl_with_parse_error.carbon @@ -33,7 +33,7 @@ fn G() {} [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -42,6 +42,7 @@ fn G() {} // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/fn_definition.carbon b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon index 97a523be93cd..9035ae515a15 100644 --- a/toolchain/language_server/testdata/document_symbol/fn_definition.carbon +++ b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon @@ -29,7 +29,7 @@ fn Builtin() = "int.make_type_32"; [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -38,6 +38,7 @@ fn Builtin() = "int.make_type_32"; // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/incomplete.carbon b/toolchain/language_server/testdata/document_symbol/incomplete.carbon index 7a7eb64868d9..307a7ace01d9 100644 --- a/toolchain/language_server/testdata/document_symbol/incomplete.carbon +++ b/toolchain/language_server/testdata/document_symbol/incomplete.carbon @@ -27,7 +27,7 @@ class Incomplete { [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -36,6 +36,7 @@ class Incomplete { // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/language.carbon b/toolchain/language_server/testdata/document_symbol/language.carbon index b62f6ec855aa..1b830c40bb32 100644 --- a/toolchain/language_server/testdata/document_symbol/language.carbon +++ b/toolchain/language_server/testdata/document_symbol/language.carbon @@ -20,7 +20,7 @@ // CHECK:STDERR: /test.cpp: warning: non-Carbon file requested [LanguageServerFileUnsupported] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -29,6 +29,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/namespace.carbon b/toolchain/language_server/testdata/document_symbol/namespace.carbon index a5826a4d4a68..dadd15c8aaca 100644 --- a/toolchain/language_server/testdata/document_symbol/namespace.carbon +++ b/toolchain/language_server/testdata/document_symbol/namespace.carbon @@ -28,7 +28,7 @@ fn Bar() {} [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -37,6 +37,7 @@ fn Bar() {} // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/document_symbol/unknown.carbon b/toolchain/language_server/testdata/document_symbol/unknown.carbon index 4b6ba8713354..f215fcc86464 100644 --- a/toolchain/language_server/testdata/document_symbol/unknown.carbon +++ b/toolchain/language_server/testdata/document_symbol/unknown.carbon @@ -20,7 +20,7 @@ // CHECK:STDERR: /test.carbon: warning: unknown file requested [LanguageServerFileUnknown] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -29,6 +29,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/formatting/basic.carbon b/toolchain/language_server/testdata/formatting/basic.carbon new file mode 100644 index 000000000000..11ee85eeb27d --- /dev/null +++ b/toolchain/language_server/testdata/formatting/basic.carbon @@ -0,0 +1,137 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/formatting/basic.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/formatting/basic.carbon + +// --- STDIN +[[@LSP-CALL:initialize:"capabilities": {}]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": {"uri": "file:/unformatted.carbon", "languageId": "carbon", + "text": "fn F( ) { return ; }"} +]] +[[@LSP-CALL:textDocument/formatting: + "textDocument": {"uri": "file:/unformatted.carbon"}, + "options": {"tabSize": 2, "insertSpaces": true} +]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": {"uri": "file:/formatted.carbon", "languageId": "carbon", + "text": "fn F() {\n return;\n}\n"} +]] +[[@LSP-CALL:textDocument/formatting: + "textDocument": {"uri": "file:/formatted.carbon"}, + "options": {"tabSize": 2, "insertSpaces": true} +]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": {"uri": "file:/empty.carbon", "languageId": "carbon", + "text": ""} +]] +[[@LSP-CALL:textDocument/formatting: + "textDocument": {"uri": "file:/empty.carbon"}, + "options": {"tabSize": 2, "insertSpaces": true} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 394{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "declarationProvider": true, +// CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "hoverProvider": true, +// CHECK:STDOUT: "positionEncoding": "utf-16", +// CHECK:STDOUT: "referencesProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2, +// CHECK:STDOUT: "typeDefinitionProvider": true +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 151{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [], +// CHECK:STDOUT: "uri": "file:///unformatted.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 284{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "newText": "fn F () {\n return;\n}\n", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 149{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [], +// CHECK:STDOUT: "uri": "file:///formatted.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 284{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "newText": "fn F () {\n return;\n}\n", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 4 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 145{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [], +// CHECK:STDOUT: "uri": "file:///empty.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 49{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 4, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 5, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/formatting/errors.carbon b/toolchain/language_server/testdata/formatting/errors.carbon new file mode 100644 index 000000000000..fbb9c4c68c39 --- /dev/null +++ b/toolchain/language_server/testdata/formatting/errors.carbon @@ -0,0 +1,79 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/formatting/errors.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/formatting/errors.carbon + +// --- STDIN +[[@LSP-CALL:initialize:"capabilities": {}]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": {"uri": "file:/error.carbon", "languageId": "carbon", + "text": "fn F() {"} +]] +[[@LSP-CALL:textDocument/formatting: + "textDocument": {"uri": "file:/error.carbon"}, + "options": {"tabSize": 2, "insertSpaces": true} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 394{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "declarationProvider": true, +// CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "hoverProvider": true, +// CHECK:STDOUT: "positionEncoding": "utf-16", +// CHECK:STDOUT: "referencesProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2, +// CHECK:STDOUT: "typeDefinitionProvider": true +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 487{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "opening symbol without a corresponding closing symbol", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 8, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 7, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: ], +// CHECK:STDOUT: "uri": "file:///error.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 49{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/formatting/unknown.carbon b/toolchain/language_server/testdata/formatting/unknown.carbon new file mode 100644 index 000000000000..c5b28cfb7fde --- /dev/null +++ b/toolchain/language_server/testdata/formatting/unknown.carbon @@ -0,0 +1,46 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/formatting/unknown.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/formatting/unknown.carbon +// CHECK:STDERR: /test.carbon: warning: unknown file requested [LanguageServerFileUnknown] +// CHECK:STDERR: + +// --- STDIN +[[@LSP-CALL:initialize:"capabilities": {}]] +[[@LSP-CALL:textDocument/formatting: + "textDocument": {"uri": "file:/test.carbon"}, + "options": {"tabSize": 2, "insertSpaces": true} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 394{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "declarationProvider": true, +// CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "hoverProvider": true, +// CHECK:STDOUT: "positionEncoding": "utf-16", +// CHECK:STDOUT: "referencesProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2, +// CHECK:STDOUT: "typeDefinitionProvider": true +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/position/bad_params.carbon b/toolchain/language_server/testdata/position/bad_params.carbon index c7a763af292d..8fffdb21072b 100644 --- a/toolchain/language_server/testdata/position/bad_params.carbon +++ b/toolchain/language_server/testdata/position/bad_params.carbon @@ -51,7 +51,7 @@ [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -60,6 +60,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/position/hover_and_goto.carbon b/toolchain/language_server/testdata/position/hover_and_goto.carbon index 8edfd5181706..739fd5030c35 100644 --- a/toolchain/language_server/testdata/position/hover_and_goto.carbon +++ b/toolchain/language_server/testdata/position/hover_and_goto.carbon @@ -59,7 +59,7 @@ fn Run() { [[@LSP-CALL:shutdown]] [[@LSP-NOTIFY:exit]] -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -68,6 +68,7 @@ fn Run() { // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/change_unknown.carbon b/toolchain/language_server/testdata/text_document/change_unknown.carbon index 3c1817730fb1..0042117bedff 100644 --- a/toolchain/language_server/testdata/text_document/change_unknown.carbon +++ b/toolchain/language_server/testdata/text_document/change_unknown.carbon @@ -21,7 +21,7 @@ // CHECK:STDERR: /test.carbon: warning: unknown file requested [LanguageServerFileUnknown] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -30,6 +30,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/close_unknown.carbon b/toolchain/language_server/testdata/text_document/close_unknown.carbon index 20b219daa1ea..8bc8e2b186e8 100644 --- a/toolchain/language_server/testdata/text_document/close_unknown.carbon +++ b/toolchain/language_server/testdata/text_document/close_unknown.carbon @@ -20,7 +20,7 @@ // CHECK:STDERR: /test.carbon: warning: tried closing unknown file; ignoring request [LanguageServerCloseUnknownFile] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -29,6 +29,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/diagnostics.carbon b/toolchain/language_server/testdata/text_document/diagnostics.carbon index 51cc196acb79..e4f0d06359c9 100644 --- a/toolchain/language_server/testdata/text_document/diagnostics.carbon +++ b/toolchain/language_server/testdata/text_document/diagnostics.carbon @@ -26,7 +26,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -35,6 +35,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/import_prelude.carbon b/toolchain/language_server/testdata/text_document/import_prelude.carbon index 84297a4c262c..714f3d8281ed 100644 --- a/toolchain/language_server/testdata/text_document/import_prelude.carbon +++ b/toolchain/language_server/testdata/text_document/import_prelude.carbon @@ -28,7 +28,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -37,6 +37,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/incremental_sync.carbon b/toolchain/language_server/testdata/text_document/incremental_sync.carbon index 3eb27fe5243c..99d8db499e30 100644 --- a/toolchain/language_server/testdata/text_document/incremental_sync.carbon +++ b/toolchain/language_server/testdata/text_document/incremental_sync.carbon @@ -107,7 +107,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -116,6 +116,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/incremental_sync_multiline.carbon b/toolchain/language_server/testdata/text_document/incremental_sync_multiline.carbon index a86eba23fd8d..434c3bea986b 100644 --- a/toolchain/language_server/testdata/text_document/incremental_sync_multiline.carbon +++ b/toolchain/language_server/testdata/text_document/incremental_sync_multiline.carbon @@ -35,7 +35,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -44,6 +44,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/multiple_files.carbon b/toolchain/language_server/testdata/text_document/multiple_files.carbon index 8716a5cffaaf..c79b491a528e 100644 --- a/toolchain/language_server/testdata/text_document/multiple_files.carbon +++ b/toolchain/language_server/testdata/text_document/multiple_files.carbon @@ -25,7 +25,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -34,6 +34,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/open_change_close.carbon b/toolchain/language_server/testdata/text_document/open_change_close.carbon index 0c3751a01dd6..2cfb9c5fa5a2 100644 --- a/toolchain/language_server/testdata/text_document/open_change_close.carbon +++ b/toolchain/language_server/testdata/text_document/open_change_close.carbon @@ -26,7 +26,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -35,6 +35,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/open_duplicate.carbon b/toolchain/language_server/testdata/text_document/open_duplicate.carbon index 6768a2f1f272..76639f2c00ac 100644 --- a/toolchain/language_server/testdata/text_document/open_duplicate.carbon +++ b/toolchain/language_server/testdata/text_document/open_duplicate.carbon @@ -25,7 +25,7 @@ // CHECK:STDERR: /test.carbon: warning: duplicate open file request; updating content [LanguageServerOpenDuplicateFile] // CHECK:STDERR: -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -34,6 +34,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16", diff --git a/toolchain/language_server/testdata/text_document/open_with_cpp_nonexistent.carbon b/toolchain/language_server/testdata/text_document/open_with_cpp_nonexistent.carbon index 4519f9e1db5a..8a122c62cb4b 100644 --- a/toolchain/language_server/testdata/text_document/open_with_cpp_nonexistent.carbon +++ b/toolchain/language_server/testdata/text_document/open_with_cpp_nonexistent.carbon @@ -22,7 +22,7 @@ // --- AUTOUPDATE-SPLIT -// CHECK:STDOUT: Content-Length: 352{{\r}} +// CHECK:STDOUT: Content-Length: 394{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "id": 1, @@ -31,6 +31,7 @@ // CHECK:STDOUT: "capabilities": { // CHECK:STDOUT: "declarationProvider": true, // CHECK:STDOUT: "definitionProvider": true, +// CHECK:STDOUT: "documentFormattingProvider": true, // CHECK:STDOUT: "documentSymbolProvider": true, // CHECK:STDOUT: "hoverProvider": true, // CHECK:STDOUT: "positionEncoding": "utf-16",