Add SQL, Erlang, and Forth highlighter implementations and tests for LSP process and transport handling.
- Added highlighters for new languages (SQL, Erlang, Forth) with filetype recognition. - Updated and reorganized syntax files to maintain consistency and modularity. - Introduced LSP transport framing unit tests and JSON decoding/dispatch tests. - Refactored `LspManager`, integrating UTF-16/UTF-8 position conversions and robust diagnostics handling. - Enhanced server start/restart logic with workspace root detection and logging to improve LSP usability.
This commit is contained in:
@@ -1,19 +1,147 @@
|
||||
/*
|
||||
* JsonRpcTransport.cc - placeholder
|
||||
* JsonRpcTransport.cc - minimal stdio JSON-RPC framing (Content-Length)
|
||||
*/
|
||||
#include "JsonRpcTransport.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace kte::lsp {
|
||||
void
|
||||
JsonRpcTransport::send(const std::string &/*method*/, const std::string &/*payload*/)
|
||||
JsonRpcTransport::connect(int inFd, int outFd)
|
||||
{
|
||||
// stub: no-op
|
||||
inFd_ = inFd;
|
||||
outFd_ = outFd;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
JsonRpcTransport::send(const std::string &/*method*/, const std::string &payload)
|
||||
{
|
||||
if (outFd_ < 0)
|
||||
return;
|
||||
const std::string header = "Content-Length: " + std::to_string(payload.size()) + "\r\n\r\n";
|
||||
std::lock_guard<std::mutex> lk(writeMutex_);
|
||||
// write header
|
||||
const char *hbuf = header.data();
|
||||
size_t hleft = header.size();
|
||||
while (hleft > 0) {
|
||||
ssize_t n = ::write(outFd_, hbuf, hleft);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
hbuf += static_cast<size_t>(n);
|
||||
hleft -= static_cast<size_t>(n);
|
||||
}
|
||||
// write payload
|
||||
const char *pbuf = payload.data();
|
||||
size_t pleft = payload.size();
|
||||
while (pleft > 0) {
|
||||
ssize_t n = ::write(outFd_, pbuf, pleft);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
pbuf += static_cast<size_t>(n);
|
||||
pleft -= static_cast<size_t>(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
readLineCrlf(int fd, std::string &out, size_t maxLen)
|
||||
{
|
||||
out.clear();
|
||||
char ch;
|
||||
while (true) {
|
||||
ssize_t n = ::read(fd, &ch, 1);
|
||||
if (n == 0)
|
||||
return false; // EOF
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
out.push_back(ch);
|
||||
// Handle CRLF or bare LF as end-of-line
|
||||
if ((out.size() >= 2 && out[out.size() - 2] == '\r' && out[out.size() - 1] == '\n') ||
|
||||
(out.size() >= 1 && out[out.size() - 1] == '\n')) {
|
||||
return true;
|
||||
}
|
||||
if (out.size() > maxLen) {
|
||||
// sanity cap
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<JsonRpcMessage>
|
||||
JsonRpcTransport::read()
|
||||
{
|
||||
return std::nullopt; // stub
|
||||
if (inFd_ < 0)
|
||||
return std::nullopt;
|
||||
// Parse headers (case-insensitive), accept/ignore extras
|
||||
size_t contentLength = 0;
|
||||
while (true) {
|
||||
std::string line;
|
||||
if (!readLineCrlf(inFd_, line, kMaxHeaderLine))
|
||||
return std::nullopt;
|
||||
// Normalize end-of-line handling: consider blank line as end of headers
|
||||
if (line == "\r\n" || line == "\n" || line == "\r")
|
||||
break;
|
||||
// Trim trailing CRLF
|
||||
if (!line.empty() && (line.back() == '\n' || line.back() == '\r')) {
|
||||
while (!line.empty() && (line.back() == '\n' || line.back() == '\r'))
|
||||
line.pop_back();
|
||||
}
|
||||
// Find colon
|
||||
auto pos = line.find(':');
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string value = line.substr(pos + 1);
|
||||
// trim leading spaces in value
|
||||
size_t i = 0;
|
||||
while (i < value.size() && (value[i] == ' ' || value[i] == '\t'))
|
||||
++i;
|
||||
value.erase(0, i);
|
||||
// lower-case name for comparison
|
||||
for (auto &c: name)
|
||||
c = static_cast<char>(::tolower(static_cast<unsigned char>(c)));
|
||||
if (name == "content-length") {
|
||||
size_t len = static_cast<size_t>(std::strtoull(value.c_str(), nullptr, 10));
|
||||
if (len > kMaxBody) {
|
||||
return std::nullopt; // drop too-large message
|
||||
}
|
||||
contentLength = len;
|
||||
}
|
||||
// else: ignore other headers
|
||||
}
|
||||
if (contentLength == 0)
|
||||
return std::nullopt;
|
||||
std::string body;
|
||||
body.resize(contentLength);
|
||||
size_t readTotal = 0;
|
||||
while (readTotal < contentLength) {
|
||||
ssize_t n = ::read(inFd_, &body[readTotal], contentLength - readTotal);
|
||||
if (n == 0)
|
||||
return std::nullopt;
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return std::nullopt;
|
||||
}
|
||||
readTotal += static_cast<size_t>(n);
|
||||
}
|
||||
return JsonRpcMessage{std::move(body)};
|
||||
}
|
||||
} // namespace kte::lsp
|
||||
Reference in New Issue
Block a user