// Copyright (C) Filippo Cucchetto // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "client.h" #include "sexprparser.h" #include namespace Nim { namespace Suggest { NimSuggestClient::NimSuggestClient(QObject *parent) : QObject(parent) { connect(&m_socket, &QTcpSocket::readyRead, this, &NimSuggestClient::onReadyRead); connect(&m_socket, &QTcpSocket::connected, this, &NimSuggestClient::connected); connect(&m_socket, &QTcpSocket::disconnected, this, &NimSuggestClient::disconnected); } bool NimSuggestClient::connectToServer(quint16 port) { m_port = port; m_socket.connectToHost("localhost", m_port); return true; } bool NimSuggestClient::disconnectFromServer() { m_socket.disconnectFromHost(); clear(); return true; } std::shared_ptr NimSuggestClient::sug(const QString &nimFile, int line, int column, const QString &dirtyFile) { return sendRequest(QLatin1String("sug"), nimFile, line, column, dirtyFile); } std::shared_ptr NimSuggestClient::def(const QString &nimFile, int line, int column, const QString &dirtyFile) { return sendRequest(QLatin1String("def"), nimFile, line, column, dirtyFile); } std::shared_ptr NimSuggestClient::sendRequest(const QString& type, const QString &nimFile, int line, int column, const QString &dirtyFile) { if (!m_socket.isOpen()) return nullptr; auto result = std::make_shared(m_lastMessageId++); m_requests.emplace(result->id(), result); QByteArray body = QString(R"((call %1 %2 ("%3" %4 %5 "%6"))\n)") .arg(result->id()) .arg(type) .arg(nimFile) .arg(line).arg(column) .arg(dirtyFile) .toUtf8(); QByteArray length = QString::number(body.size(), 16).rightJustified(6, '0').toUtf8(); QByteArray message = length + body; m_socket.write(message); m_socket.waitForBytesWritten(); return result; } void NimSuggestClient::clear() { for (const auto &pair : m_requests) { if (auto req = pair.second.lock()) { emit req->finished(); } } m_lines.clear(); m_readBuffer.clear(); m_requests.clear(); m_lastMessageId = 0; } void NimSuggestClient::onDisconnectedFromServer() { clear(); } void NimSuggestClient::onReadyRead() { std::array buffer; for (;;) { qint64 num_read = m_socket.read(buffer.data(), buffer.size()); m_readBuffer.insert(m_readBuffer.end(), buffer.begin(), buffer.begin() + num_read); if (num_read <= 0) { break; } } while (m_readBuffer.size() >= 6) { const size_t payload_size = QByteArray::fromRawData(m_readBuffer.data(), 6).toUInt(nullptr, 16); if (payload_size <= m_readBuffer.size() - 6) { parsePayload(m_readBuffer.data() + 6, payload_size); m_readBuffer.erase(m_readBuffer.begin(), m_readBuffer.begin() + 6u + payload_size); } else { break; } } } void NimSuggestClient::parsePayload(const char *payload, std::size_t size) { SExprParser::Node root_ast; SExprParser parser(payload, size); if (!parser.parse(root_ast)) return; if (root_ast.nodes.at(0).value != "return") return; const quint64 uid = std::stoull(root_ast.nodes.at(1).value, nullptr, 0); auto it = m_requests.find(uid); if (it == m_requests.end()) return; auto req = std::dynamic_pointer_cast((*it).second.lock()); if (!req) return; std::vector suggestions; suggestions.reserve(root_ast.nodes.at(2).nodes.size()); for (const SExprParser::Node &row : root_ast.nodes.at(2).nodes) { Line line; if (!Line::fromString(line.line_type, row.nodes.at(0).value)) continue; if (!Line::fromString(line.symbol_kind, row.nodes.at(1).value)) continue; line.data.reserve(row.nodes.at(2).nodes.size()); for (const SExprParser::Node &field : row.nodes.at(2).nodes) line.data.push_back(QString::fromStdString(field.value)); line.abs_path = QString::fromStdString(row.nodes.at(3).value); line.symbol_type = QString::fromStdString(row.nodes.at(4).value); line.row = std::stoi(row.nodes.at(5).value); line.column = std::stoi(row.nodes.at(6).value); line.doc = QString::fromStdString(row.nodes.at(7).value); suggestions.push_back(std::move(line)); } req->setFinished(std::move(suggestions)); m_requests.erase(it); } } // namespace Suggest } // namespace Nim