- 增加了对 Lambda 表达式的初步解析支持,包括参数处理和返回类型。Lambda闭包尚未支持。 - 引入了用于对象初始化的新的表达式,支持可选的命名参数。 - 改进了表达式语法错误的错误报告。 - 更新了解析器和分析器以处理新的表达式类型并验证其语义。 - 修改了现有测试以涵盖新功能并确保其正确性。 - 改进了各种解析和语义错误的诊断。
180 lines
5.9 KiB
C++
180 lines
5.9 KiB
C++
#pragma once
|
|
|
|
#include <Ast/Ast.hpp>
|
|
#include <Lexer/Lexer.hpp>
|
|
#include <Parser/Parser.hpp>
|
|
#include <Sema/Analyzer.hpp>
|
|
|
|
#include <Error/Error.hpp>
|
|
#include <SourceManager/SourceManager.hpp>
|
|
|
|
#include <charconv> // C++17/20 字符串转数字
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include <Utils/json/json.hpp>
|
|
using json = nlohmann::json;
|
|
|
|
namespace Fig
|
|
{
|
|
|
|
class LspServer
|
|
{
|
|
public:
|
|
void Run()
|
|
{
|
|
std::ios_base::sync_with_stdio(false);
|
|
std::cin.tie(NULL);
|
|
|
|
while (true)
|
|
{
|
|
std::string line;
|
|
if (!std::getline(std::cin, line))
|
|
break; // 退出循环
|
|
|
|
// 解析 HTTP 风格的 Header: "Content-Length: 123\r"
|
|
if (line.starts_with("Content-Length: "))
|
|
{
|
|
std::size_t length = 0;
|
|
// 使用 C++ 极速的 from_chars 替代 stoi
|
|
auto res = std::from_chars(line.data() + 16, line.data() + line.size(), length);
|
|
if (res.ec != std::errc())
|
|
continue;
|
|
|
|
// 消费 Header 和 Body 之间那行空的 "\r\n"
|
|
std::string emptyLine;
|
|
std::getline(std::cin, emptyLine);
|
|
|
|
// 读取对应字节的 JSON Body
|
|
std::string jsonBody(length, '\0');
|
|
std::cin.read(jsonBody.data(), length);
|
|
|
|
// 派发给路由
|
|
HandleMessage(jsonBody);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
void HandleMessage(const std::string &rawJson)
|
|
{
|
|
json req = json::parse(rawJson, nullptr, false);
|
|
if (req.is_discarded())
|
|
return; // 忽略解析失败的报文
|
|
|
|
std::string method = req.value("method", "");
|
|
|
|
if (method == "initialize")
|
|
{
|
|
Respond(req["id"], R"({
|
|
"capabilities": {
|
|
"textDocumentSync": 1,
|
|
"hoverProvider": true
|
|
}
|
|
})");
|
|
}
|
|
else if (method == "textDocument/didOpen")
|
|
{
|
|
std::string uri = req["params"]["textDocument"]["uri"];
|
|
std::string text = req["params"]["textDocument"]["text"];
|
|
PublishDiagnostics(uri, text);
|
|
}
|
|
else if (method == "textDocument/didChange")
|
|
{
|
|
std::string uri = req["params"]["textDocument"]["uri"];
|
|
std::string text = req["params"]["contentChanges"][0]["text"];
|
|
PublishDiagnostics(uri, text);
|
|
}
|
|
}
|
|
|
|
void Respond(int id, const std::string &resultJsonString)
|
|
{
|
|
std::string response = "{\"jsonrpc\":\"2.0\",\"id\":" + std::to_string(id)
|
|
+ ",\"result\":" + resultJsonString + "}";
|
|
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
|
<< response << std::flush;
|
|
}
|
|
|
|
void SendDiagnostics(const std::string &uri, const Error *err = nullptr)
|
|
{
|
|
json diagnostics = json::array(); // 默认空数组,代码无错时擦除红线
|
|
|
|
if (err)
|
|
{
|
|
// LSP 规定行列号必须从 0 开始算
|
|
int startLine = err->location.sp.line - 1;
|
|
int startChar = err->location.sp.column - 1;
|
|
int endLine = startLine;
|
|
int endChar = startChar + err->location.sp.tok_length;
|
|
|
|
std::string fullMessage = err->message.toStdString();
|
|
if (!err->suggestion.empty())
|
|
{
|
|
fullMessage += " 💡suggestion: " + err->suggestion.toStdString();
|
|
}
|
|
|
|
diagnostics.push_back(
|
|
{{"range",
|
|
{{"start", {{"line", startLine}, {"character", startChar}}},
|
|
{"end", {{"line", endLine}, {"character", endChar}}}}},
|
|
{"severity", 1}, // 1 = 致命错误红线
|
|
{"source", "Fig LSP Server"},
|
|
{"message", fullMessage}});
|
|
}
|
|
|
|
// 组装 Notification
|
|
json notification = {{"jsonrpc", "2.0"},
|
|
{"method", "textDocument/publishDiagnostics"},
|
|
{"params", {{"uri", uri}, {"diagnostics", diagnostics}}}};
|
|
|
|
std::string response = notification.dump();
|
|
std::cout << "Content-Length: " << response.size() << "\r\n\r\n"
|
|
<< response << std::flush;
|
|
}
|
|
|
|
void PublishDiagnostics(const std::string &uri, const std::string &sourceCode)
|
|
{
|
|
SourceManager manager;
|
|
manager.LoadFromMemory(sourceCode);
|
|
|
|
Lexer lexer(sourceCode, "");
|
|
|
|
Diagnostics diagnostics;
|
|
|
|
Parser parser(lexer, manager, "", diagnostics);
|
|
|
|
// 语法检查拦截
|
|
auto parserResult = parser.Parse();
|
|
if (!parserResult)
|
|
{
|
|
SendDiagnostics(uri, &parserResult.error());
|
|
return;
|
|
}
|
|
|
|
for (auto &diag : diagnostics.GetErrors())
|
|
{
|
|
SendDiagnostics(uri, &diag);
|
|
}
|
|
|
|
Program *program = *parserResult;
|
|
|
|
Analyzer analyzer(manager);
|
|
|
|
// 语义检查拦截
|
|
auto analyzerResult = analyzer.Analyze(program);
|
|
if (!analyzerResult)
|
|
{
|
|
SendDiagnostics(uri, &analyzerResult.error());
|
|
return;
|
|
}
|
|
|
|
for (auto &diag : analyzer.GetDiagnostics().GetErrors())
|
|
{
|
|
SendDiagnostics(uri, &diag);
|
|
}
|
|
|
|
// 一切完美,发射空数组清空过去的错误红线
|
|
SendDiagnostics(uri, nullptr);
|
|
}
|
|
};
|
|
} // namespace Fig
|