Skip to content

Commit 7d2958a

Browse files
authored
Broaden file_test support for LSP requests (#4854)
Adds `@LSP-NOTIFY` and `@LSP-REPLY` to capture the slightly different formats versus a typical call. Removes special-casing for `exit`.
1 parent 4ecf914 commit 7d2958a

File tree

5 files changed

+85
-56
lines changed

5 files changed

+85
-56
lines changed

testing/file_test/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,13 @@ Some keywords can be inserted for content:
8888
8989
- ```
9090
[[@LSP:<method>:<extra content>]]
91+
[[@LSP-NOTIFY:<method>:<extra content>]]
92+
[[@LSP-REPLY:<id>:<extra content>]]
9193
```
9294
93-
Produces JSON for an LSP method call, complete with `Content-Length` header.
95+
Produces JSON for an LSP method call, notification, or reply. Each includes
96+
the `Content-Length` header. The `:<extra content>` is optional, and may be
97+
omitted.
9498
9599
- ```
96100
[[@TEST_NAME]]

testing/file_test/file_test_base.cpp

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,62 @@ struct SplitState {
517517
int file_index = 0;
518518
};
519519

520+
// Reformats `[[@LSP:` and `[[LSP-NOTIFY:` as an LSP call with headers. For
521+
// notifications, `lsp_call_id` is null.
522+
static auto ReplaceLspKeywordAt(std::string* content, size_t keyword_pos,
523+
int* lsp_call_id, llvm::StringLiteral keyword,
524+
llvm::StringLiteral method_or_id_label)
525+
-> ErrorOr<size_t> {
526+
auto method_or_id_start = keyword_pos + keyword.size();
527+
528+
static constexpr llvm::StringLiteral LspEnd = "]]";
529+
auto keyword_end = content->find("]]", method_or_id_start);
530+
if (keyword_end == std::string::npos) {
531+
return ErrorBuilder() << "Missing `" << LspEnd << "` after `" << keyword
532+
<< "`";
533+
}
534+
535+
auto method_or_id_end = content->find(":", method_or_id_start);
536+
auto extra_content_start = method_or_id_end + 1;
537+
if (method_or_id_end == std::string::npos || method_or_id_end > keyword_end) {
538+
method_or_id_end = keyword_end;
539+
extra_content_start = keyword_end;
540+
}
541+
auto method_or_id =
542+
llvm::StringRef(*content).slice(method_or_id_start, method_or_id_end);
543+
544+
auto extra_content =
545+
llvm::StringRef(*content).slice(extra_content_start, keyword_end);
546+
std::string extra_content_sep;
547+
if (!extra_content.empty()) {
548+
extra_content_sep = ",";
549+
if (!extra_content.starts_with("\n")) {
550+
extra_content_sep += " ";
551+
}
552+
}
553+
554+
// Form the JSON.
555+
std::string json = R"({"jsonrpc": "2.0", )";
556+
if (lsp_call_id) {
557+
json += llvm::formatv(R"("id": "{0}", )", ++(*lsp_call_id));
558+
}
559+
json += llvm::formatv(R"("{0}": "{1}"{2}{3}})", method_or_id_label,
560+
method_or_id, extra_content_sep, extra_content);
561+
562+
// Add the Content-Length header. The `2` accounts for extra newlines.
563+
auto json_with_header =
564+
llvm::formatv("Content-Length: {0}\n\n{1}\n", json.size() + 2, json)
565+
.str();
566+
// Insert the content.
567+
content->replace(keyword_pos, keyword_end + 2 - keyword_pos,
568+
json_with_header);
569+
return keyword_pos + json_with_header.size();
570+
}
571+
520572
// Replaces the keyword at the given position. Returns the position to start a
521573
// find for the next keyword.
522574
static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
523-
llvm::StringRef test_name, int* lsp_id)
575+
llvm::StringRef test_name, int* lsp_call_id)
524576
-> ErrorOr<size_t> {
525577
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
526578

@@ -538,57 +590,20 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
538590
return keyword_pos + test_name.size();
539591
}
540592

541-
// Reformatted as an LSP call with headers.
542593
static constexpr llvm::StringLiteral Lsp = "[[@LSP:";
543594
if (keyword.starts_with(Lsp)) {
544-
auto method_start = keyword_pos + Lsp.size();
545-
546-
static constexpr llvm::StringLiteral LspEnd = "]]";
547-
auto keyword_end = content->find("]]", method_start);
548-
if (keyword_end == std::string::npos) {
549-
return ErrorBuilder()
550-
<< "Missing `" << LspEnd << "` after `" << Lsp << "`";
551-
}
552-
553-
auto method_end = content->find(":", method_start);
554-
auto extra_content_start = method_end + 1;
555-
if (method_end == std::string::npos || method_end > keyword_end) {
556-
method_end = keyword_end;
557-
extra_content_start = keyword_end;
558-
}
559-
auto method = content->substr(method_start, method_end - method_start);
560-
561-
auto extra_content =
562-
content->substr(extra_content_start, keyword_end - extra_content_start);
563-
std::string extra_content_sep;
564-
if (!extra_content.empty()) {
565-
extra_content_sep = ",";
566-
if (!extra_content.starts_with("\n")) {
567-
extra_content_sep += " ";
568-
}
569-
}
570-
571-
// Form the JSON.
572-
std::string json;
573-
if (method == "exit") {
574-
if (!extra_content.empty()) {
575-
return Error("`[[@LSP:exit:` cannot include extra content");
576-
}
577-
json = R"({"jsonrpc": "2.0", "method": "exit"})";
578-
} else {
579-
json = llvm::formatv(
580-
R"({{"jsonrpc": "2.0", "id": "{0}", "method": "{1}"{2}{3}})",
581-
++(*lsp_id), method, extra_content_sep, extra_content)
582-
.str();
583-
}
584-
// Add the Content-Length header. The `2` accounts for extra newlines.
585-
auto json_with_header =
586-
llvm::formatv("Content-Length: {0}\n\n{1}\n", json.size() + 2, json)
587-
.str();
588-
// Insert the content.
589-
content->replace(keyword_pos, keyword_end + 2 - keyword_pos,
590-
json_with_header);
591-
return keyword_pos + json_with_header.size();
595+
return ReplaceLspKeywordAt(content, keyword_pos, lsp_call_id, Lsp,
596+
"method");
597+
}
598+
static constexpr llvm::StringLiteral LspNotify = "[[@LSP-NOTIFY:";
599+
if (keyword.starts_with(LspNotify)) {
600+
return ReplaceLspKeywordAt(content, keyword_pos,
601+
/*lsp_call_id=*/nullptr, LspNotify, "method");
602+
}
603+
static constexpr llvm::StringLiteral LspReply = "[[@LSP-REPLY:";
604+
if (keyword.starts_with(LspReply)) {
605+
return ReplaceLspKeywordAt(content, keyword_pos,
606+
/*lsp_call_id=*/nullptr, LspReply, "id");
592607
}
593608

594609
return ErrorBuilder() << "Unexpected use of `[[@` at `"
@@ -625,11 +640,11 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
625640
test_name.consume_front("todo_");
626641

627642
// A counter for LSP calls.
628-
int lsp_id = 0;
643+
int lsp_call_id = 0;
629644
while (keyword_pos != std::string::npos) {
630645
CARBON_ASSIGN_OR_RETURN(
631646
auto keyword_end,
632-
ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_id));
647+
ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_call_id));
633648
keyword_pos = content->find(Prefix, keyword_end);
634649
}
635650
return Success();

testing/file_test/testdata/lsp_calls.carbon

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
multi
1717
line
1818
]]
19-
[[@LSP:exit]]
19+
[[@LSP-REPLY:7]]
20+
[[@LSP-REPLY:8:bar]]
21+
[[@LSP-NOTIFY:exit]]
2022

2123
// --- AUTOUPDATE-SPLIT
2224

@@ -40,6 +42,14 @@ line
4042
// CHECK:STDERR: line
4143
// CHECK:STDERR: }
4244
// CHECK:STDERR:
45+
// CHECK:STDERR: Content-Length: 31
46+
// CHECK:STDERR:
47+
// CHECK:STDERR: {"jsonrpc": "2.0", "id": "7"}
48+
// CHECK:STDERR:
49+
// CHECK:STDERR: Content-Length: 36
50+
// CHECK:STDERR:
51+
// CHECK:STDERR: {"jsonrpc": "2.0", "id": "8", bar}
52+
// CHECK:STDERR:
4353
// CHECK:STDERR: Content-Length: 38
4454
// CHECK:STDERR:
4555
// CHECK:STDERR: {"jsonrpc": "2.0", "method": "exit"}

toolchain/language_server/testdata/exit.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/exit.carbon
1010

1111
// --- STDIN
12-
[[@LSP:exit]]
12+
[[@LSP-NOTIFY:exit]]
1313

1414
// --- AUTOUPDATE-SPLIT
1515

toolchain/language_server/testdata/initialize.carbon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// --- STDIN
1212
[[@LSP:initialize]]
13-
[[@LSP:exit]]
13+
[[@LSP-NOTIFY:exit]]
1414

1515
// --- AUTOUPDATE-SPLIT
1616

0 commit comments

Comments
 (0)