mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Add @LSP-CALL to file_test (#4896)
Adds @LSP-CALL and refactors LSP keyword handling to implicitly handle a
little more of the LSP structure. This is coming out of textDocument
call handling, where this at least reduces some boilerplate of
`"params": {...}`.
This commit is contained in:
@@ -517,62 +517,81 @@ struct SplitState {
|
||||
int file_index = 0;
|
||||
};
|
||||
|
||||
// Reformats `[[@LSP:` and `[[LSP-NOTIFY:` as an LSP call with headers. For
|
||||
// notifications, `lsp_call_id` is null.
|
||||
// Reformats `[[@LSP:` and similar keyword as an LSP call with headers.
|
||||
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();
|
||||
int& lsp_call_id) -> ErrorOr<size_t> {
|
||||
llvm::StringRef content_at_keyword =
|
||||
llvm::StringRef(*content).substr(keyword_pos);
|
||||
|
||||
auto [keyword, body_start] = content_at_keyword.split(":");
|
||||
if (body_start.empty()) {
|
||||
return ErrorBuilder() << "Missing `:` for `"
|
||||
<< content_at_keyword.take_front(10) << "`";
|
||||
}
|
||||
|
||||
// Whether the first param is a method or id.
|
||||
llvm::StringRef method_or_id_label = "method";
|
||||
// Whether to attach the `lsp_call_id`.
|
||||
bool use_call_id = false;
|
||||
// The JSON label for extra content.
|
||||
llvm::StringRef extra_content_label;
|
||||
if (keyword == "[[@LSP-CALL") {
|
||||
use_call_id = true;
|
||||
extra_content_label = "params";
|
||||
} else if (keyword == "[[@LSP-NOTIFY") {
|
||||
extra_content_label = "params";
|
||||
} else if (keyword == "[[@LSP-REPLY") {
|
||||
method_or_id_label = "id";
|
||||
extra_content_label = "result";
|
||||
} else if (keyword != "[[@LSP") {
|
||||
return ErrorBuilder() << "Unrecognized @LSP keyword at `"
|
||||
<< keyword.take_front(10) << "`";
|
||||
}
|
||||
|
||||
static constexpr llvm::StringLiteral LspEnd = "]]";
|
||||
auto keyword_end = content->find("]]", method_or_id_start);
|
||||
if (keyword_end == std::string::npos) {
|
||||
auto body_end = body_start.find(LspEnd);
|
||||
if (body_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 += " ";
|
||||
}
|
||||
}
|
||||
llvm::StringRef body = body_start.take_front(body_end);
|
||||
auto [method_or_id, extra_content] = body.split(":");
|
||||
|
||||
// Form the JSON.
|
||||
std::string json = R"({"jsonrpc": "2.0", )";
|
||||
if (lsp_call_id) {
|
||||
json += llvm::formatv(R"("id": "{0}", )", ++(*lsp_call_id));
|
||||
std::string json = llvm::formatv(R"({{"jsonrpc": "2.0", "{0}": "{1}")",
|
||||
method_or_id_label, method_or_id);
|
||||
if (use_call_id) {
|
||||
// Omit quotes on the ID because we know it's an integer.
|
||||
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);
|
||||
if (!extra_content.empty()) {
|
||||
json += ",";
|
||||
if (extra_content_label.empty()) {
|
||||
if (!extra_content.starts_with("\n")) {
|
||||
json += " ";
|
||||
}
|
||||
json += extra_content;
|
||||
} else {
|
||||
json += llvm::formatv(R"( "{0}": {{{1}})", extra_content_label,
|
||||
extra_content);
|
||||
}
|
||||
}
|
||||
json += "}";
|
||||
|
||||
// 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);
|
||||
int keyword_len =
|
||||
(body_start.data() + body_end + LspEnd.size()) - keyword.data();
|
||||
content->replace(keyword_pos, keyword_len, 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_call_id)
|
||||
llvm::StringRef test_name, int& lsp_call_id)
|
||||
-> ErrorOr<size_t> {
|
||||
auto keyword = llvm::StringRef(*content).substr(keyword_pos);
|
||||
|
||||
@@ -590,20 +609,8 @@ static auto ReplaceContentKeywordAt(std::string* content, size_t keyword_pos,
|
||||
return keyword_pos + test_name.size();
|
||||
}
|
||||
|
||||
static constexpr llvm::StringLiteral Lsp = "[[@LSP:";
|
||||
if (keyword.starts_with(Lsp)) {
|
||||
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");
|
||||
if (keyword.starts_with("[[@LSP")) {
|
||||
return ReplaceLspKeywordAt(content, keyword_pos, lsp_call_id);
|
||||
}
|
||||
|
||||
return ErrorBuilder() << "Unexpected use of `[[@` at `"
|
||||
@@ -644,7 +651,7 @@ static auto ReplaceContentKeywords(llvm::StringRef filename,
|
||||
while (keyword_pos != std::string::npos) {
|
||||
CARBON_ASSIGN_OR_RETURN(
|
||||
auto keyword_end,
|
||||
ReplaceContentKeywordAt(content, keyword_pos, test_name, &lsp_call_id));
|
||||
ReplaceContentKeywordAt(content, keyword_pos, test_name, lsp_call_id));
|
||||
keyword_pos = content->find(Prefix, keyword_end);
|
||||
}
|
||||
return Success();
|
||||
|
||||
Reference in New Issue
Block a user