|
|
@@ -517,10 +517,62 @@ struct SplitState {
|
|
|
int file_index = 0;
|
|
|
};
|
|
|
|
|
|
+// Reformats `[[@LSP:` and `[[LSP-NOTIFY:` as an LSP call with headers. For
|
|
|
+// notifications, `lsp_call_id` is null.
|
|
|
+static auto ReplaceLspKeywordAt(std::string* content, size_t keyword_pos,
|
|
|
+ int* lsp_call_id, llvm::StringLiteral keyword,
|
|
|
+ llvm::StringLiteral method_or_id_label)
|
|
|
+ -> ErrorOr<size_t> {
|
|
|
+ auto method_or_id_start = keyword_pos + keyword.size();
|
|
|
+
|
|
|
+ static constexpr llvm::StringLiteral LspEnd = "]]";
|
|
|
+ auto keyword_end = content->find("]]", method_or_id_start);
|
|
|
+ if (keyword_end == std::string::npos) {
|
|
|
+ return ErrorBuilder() << "Missing `" << LspEnd << "` after `" << keyword
|
|
|
+ << "`";
|
|
|
+ }
|
|
|
+
|
|
|
+ auto method_or_id_end = content->find(":", method_or_id_start);
|
|
|
+ auto extra_content_start = method_or_id_end + 1;
|
|
|
+ if (method_or_id_end == std::string::npos || method_or_id_end > keyword_end) {
|
|
|
+ method_or_id_end = keyword_end;
|
|
|
+ extra_content_start = keyword_end;
|
|
|
+ }
|
|
|
+ auto method_or_id =
|
|
|
+ llvm::StringRef(*content).slice(method_or_id_start, method_or_id_end);
|
|
|
+
|
|
|
+ auto extra_content =
|
|
|
+ llvm::StringRef(*content).slice(extra_content_start, keyword_end);
|
|
|
+ std::string extra_content_sep;
|
|
|
+ if (!extra_content.empty()) {
|
|
|
+ extra_content_sep = ",";
|
|
|
+ if (!extra_content.starts_with("\n")) {
|
|
|
+ extra_content_sep += " ";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Form the JSON.
|
|
|
+ std::string json = R"({"jsonrpc": "2.0", )";
|
|
|
+ if (lsp_call_id) {
|
|
|
+ json += llvm::formatv(R"("id": "{0}", )", ++(*lsp_call_id));
|
|
|
+ }
|
|
|
+ json += llvm::formatv(R"("{0}": "{1}"{2}{3}})", method_or_id_label,
|
|
|
+ method_or_id, extra_content_sep, extra_content);
|
|
|
+
|
|
|
+ // Add the Content-Length header. The `2` accounts for extra newlines.
|
|
|
+ auto json_with_header =
|
|
|
+ llvm::formatv("Content-Length: {0}\n\n{1}\n", json.size() + 2, json)
|
|
|
+ .str();
|
|
|
+ // Insert the content.
|
|
|
+ content->replace(keyword_pos, keyword_end + 2 - keyword_pos,
|
|
|
+ json_with_header);
|
|
|
+ return keyword_pos + json_with_header.size();
|
|
|
+}
|
|
|
+
|
|
|
// Replaces the keyword at the given position. Returns the position to start a
|
|
|
// find for the next keyword.
|
|
|
static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
|
|
|
- llvm::StringRef test_name, int* lsp_id)
|
|
|
+ llvm::StringRef test_name, int* lsp_call_id)
|
|
|
-> ErrorOr<size_t> {
|
|
|
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
|
|
|
|
|
|
@@ -538,57 +590,20 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
|
|
|
return keyword_pos + test_name.size();
|
|
|
}
|
|
|
|
|
|
- // Reformatted as an LSP call with headers.
|
|
|
static constexpr llvm::StringLiteral Lsp = "[[@LSP:";
|
|
|
if (keyword.starts_with(Lsp)) {
|
|
|
- auto method_start = keyword_pos + Lsp.size();
|
|
|
-
|
|
|
- static constexpr llvm::StringLiteral LspEnd = "]]";
|
|
|
- auto keyword_end = content->find("]]", method_start);
|
|
|
- if (keyword_end == std::string::npos) {
|
|
|
- return ErrorBuilder()
|
|
|
- << "Missing `" << LspEnd << "` after `" << Lsp << "`";
|
|
|
- }
|
|
|
-
|
|
|
- auto method_end = content->find(":", method_start);
|
|
|
- auto extra_content_start = method_end + 1;
|
|
|
- if (method_end == std::string::npos || method_end > keyword_end) {
|
|
|
- method_end = keyword_end;
|
|
|
- extra_content_start = keyword_end;
|
|
|
- }
|
|
|
- auto method = content->substr(method_start, method_end - method_start);
|
|
|
-
|
|
|
- auto extra_content =
|
|
|
- content->substr(extra_content_start, keyword_end - extra_content_start);
|
|
|
- std::string extra_content_sep;
|
|
|
- if (!extra_content.empty()) {
|
|
|
- extra_content_sep = ",";
|
|
|
- if (!extra_content.starts_with("\n")) {
|
|
|
- extra_content_sep += " ";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Form the JSON.
|
|
|
- std::string json;
|
|
|
- if (method == "exit") {
|
|
|
- if (!extra_content.empty()) {
|
|
|
- return Error("`[[@LSP:exit:` cannot include extra content");
|
|
|
- }
|
|
|
- json = R"({"jsonrpc": "2.0", "method": "exit"})";
|
|
|
- } else {
|
|
|
- json = llvm::formatv(
|
|
|
- R"({{"jsonrpc": "2.0", "id": "{0}", "method": "{1}"{2}{3}})",
|
|
|
- ++(*lsp_id), method, extra_content_sep, extra_content)
|
|
|
- .str();
|
|
|
- }
|
|
|
- // Add the Content-Length header. The `2` accounts for extra newlines.
|
|
|
- auto json_with_header =
|
|
|
- llvm::formatv("Content-Length: {0}\n\n{1}\n", json.size() + 2, json)
|
|
|
- .str();
|
|
|
- // Insert the content.
|
|
|
- content->replace(keyword_pos, keyword_end + 2 - keyword_pos,
|
|
|
- json_with_header);
|
|
|
- return keyword_pos + json_with_header.size();
|
|
|
+ return ReplaceLspKeywordAt(content, keyword_pos, lsp_call_id, Lsp,
|
|
|
+ "method");
|
|
|
+ }
|
|
|
+ static constexpr llvm::StringLiteral LspNotify = "[[@LSP-NOTIFY:";
|
|
|
+ if (keyword.starts_with(LspNotify)) {
|
|
|
+ return ReplaceLspKeywordAt(content, keyword_pos,
|
|
|
+ /*lsp_call_id=*/nullptr, LspNotify, "method");
|
|
|
+ }
|
|
|
+ static constexpr llvm::StringLiteral LspReply = "[[@LSP-REPLY:";
|
|
|
+ if (keyword.starts_with(LspReply)) {
|
|
|
+ return ReplaceLspKeywordAt(content, keyword_pos,
|
|
|
+ /*lsp_call_id=*/nullptr, LspReply, "id");
|
|
|
}
|
|
|
|
|
|
return ErrorBuilder() << "Unexpected use of `[[@` at `"
|
|
|
@@ -625,11 +640,11 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
|
|
|
test_name.consume_front("todo_");
|
|
|
|
|
|
// A counter for LSP calls.
|
|
|
- int lsp_id = 0;
|
|
|
+ int lsp_call_id = 0;
|
|
|
while (keyword_pos != std::string::npos) {
|
|
|
CARBON_ASSIGN_OR_RETURN(
|
|
|
auto keyword_end,
|
|
|
- ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_id));
|
|
|
+ ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_call_id));
|
|
|
keyword_pos = content->find(Prefix, keyword_end);
|
|
|
}
|
|
|
return Success();
|