Source

pynoto / src / PythonCode / PythonCode.cpp

Full commit
#include <QDebug>
#include <QApplication>
#include <QFile>
#include <QDataStream>
#include <QXmlStreamWriter>
#include <QDomDocument>
#include "PythonCode.h"

namespace PythonCode {

PythonCode::PythonCode():
    PythonCode::IPythonCode(),
    _proc(NULL),
    _soc(NULL),
    _cmdNum(0)
{
}

PythonCode::~PythonCode()
{
    close();
}


void PythonCode::init(const QString& interpret, const QString& projectPath, const QString& mainScript, const QString& projectFile, const QStringList& extraPathes)
{
    QString path = QApplication::applicationDirPath() + "/python3/rope.py";
    if (!QFile::exists(path))
        path = "/usr/share/pynoto/python3/rope.py";

    close();
    qDebug() << path << interpret;

    _proc = new QProcess(this);
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    env.insert("PYTHONPATH", extraPathes.join(":"));
    _proc->setProcessEnvironment(env);
    connect(_proc, SIGNAL(readyReadStandardError()), SLOT(onError()));
    connect(_proc, SIGNAL(error(QProcess::ProcessError)), SLOT(onRunError(QProcess::ProcessError)));

    _proc->start(interpret, QStringList() << "-u" << path);
    QString ip;
    int port;
    _proc->waitForStarted();
    _proc->waitForReadyRead();
    QStringList out = QString(_proc->readAll()).split(' ');
    if (out.length() == 2){
        ip = out[0];
        port = out[1].toInt();
    }

    connect(_proc, SIGNAL(readyReadStandardOutput()), SLOT(onRead()));
    connect(_proc, SIGNAL(readyRead()), SLOT(onRead()));
    connect(this, SIGNAL(cmdRead(int)), SLOT(onCmdRead(int)));

    _soc = new QTcpSocket(this);
    _soc->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
    connect(_soc, SIGNAL(readyRead()), SLOT(onSocReadyRead()));
    _soc->connectToHost(ip, port);

    sendAsyncRequest(
        createRequest(
            "init",
            QVariantList() << projectPath
                << mainScript
                << projectFile
        )
    );
}

void PythonCode::sendSyncRequest(const QString& req)
{
    QDataStream data(_soc);
    data << req;
    waitForResponce(_cmdNum);
}

void PythonCode::sendAsyncRequest(const QString& req)
{
    QDataStream data(_soc);
    data << req;
}

QString PythonCode::createRequest(const QString& name, const QVariantList& params)
{
    return createRequest(name, "", params);
}

QString PythonCode::createRequest(const QString& name, const QString& inst, const QVariantList& params)
{
    QString out;
    QXmlStreamWriter st(&out);
    st.setAutoFormatting(true);
    st.writeStartDocument();
    st.writeStartElement("request");
    st.writeAttribute("cmd", name);
    st.writeAttribute("cmdNum", QString::number(++_cmdNum));
    st.writeAttribute("inst", inst);
    foreach(QVariant val, params){
        st.writeTextElement("param", val.toString());
    }
    st.writeEndElement();
    st.writeEndDocument();
    return out;
}


void PythonCode::close()
{
    if (_soc){
        _soc->close();
        delete _soc;
        _soc = NULL;
    }
    if (_proc){
        _proc->waitForFinished(1000);
        delete _proc;
        _proc = NULL;
        qDebug() << "python closed";
    }
}

void PythonCode::codeComplit(const QString& fileName, const QString& cnt, int pos)
{
    sendAsyncRequest(
        createRequest(
            "codeComplit",
            "Rope",
            QVariantList() << fileName
                << cnt
                << pos
        )
    );
}

void PythonCode::onRunError(QProcess::ProcessError err)
{
    qDebug() << err;
}

void PythonCode::onError()
{
    //qDebug() << _proc->readAllStandardError();
    emit systemError(_proc->readAllStandardError());
    if (_loop.isRunning()){
        _loop.exit();
    }
}

void PythonCode::onRead()
{
    qDebug() << _proc->readAllStandardOutput();
}

void PythonCode::onSocReadyRead()
{
    QDataStream st(_soc);
    QString data;
    st >> data;
    QDomDocument doc;
    doc.setContent(data);
    QDomNodeList lst = doc.elementsByTagName("responce");
    for(uint i = 0; i < lst.length(); ++i){
        QDomElement e = lst.at(i).toElement();
        if (e.hasAttribute("cmdId")){
            emit cmdRead(e.attribute("cmdId").toInt());
            return;
        }

    }
    lst = doc.elementsByTagName("signal");
    if (lst.length()){
        QDomElement e = lst.at(0).toElement();
        parseAndEmitSignal(e.attribute("name"), e.childNodes());
    }
    if (_soc->bytesAvailable())
        onSocReadyRead();
}

void PythonCode::waitForResponce(int cmdNum)
{
    _loop.setProperty("waitfor", cmdNum);
    _loop.exec();
}

void PythonCode::onCmdRead(int cmdId)
{
    if (_loop.isRunning() && _loop.property("waitfor").toInt() == cmdId)
        _loop.exit();
}

void PythonCode::errorList(const QString& fileName, const QString & content)
{
    sendAsyncRequest(createRequest("errorList", "PyLint", QVariantList() << fileName << content));
}

void PythonCode::parseAndEmitSignal(const QString& name, const QDomNodeList& items)
{
    if (name == "errorList")
        parseAndEmitErrorList(items);
    if (name == "codeComplit")
        parseAndEmitCodeComplit(items);
}

void PythonCode::parseAndEmitErrorList(const QDomNodeList& items)
{
    QString fileName = items.at(0).firstChild().toText().nodeValue();
    QList<IPythonCode::ErrorItem> elist;
    QDomElement err = items.at(1).childNodes().at(0).toElement();
    for(uint i = 0; i < err.childNodes().length(); ++i){
        QDomElement e = err.childNodes().at(i).toElement();
        IPythonCode::ErrorItem it;
        if (e.tagName() == "error")
            it.severity = IPythonCode::Error;
        if (e.tagName() == "warning")
            it.severity = IPythonCode::Warning;
        if (e.tagName() == "suggestion")
            it.severity = IPythonCode::Suggestion;
        if (e.tagName() == "style")
            it.severity = IPythonCode::CodeStyle;
        it.line = e.attribute("line").toInt()-1;
        it.message = e.firstChild().toText().nodeValue();
        elist.append(it);
    }
    emit errors(fileName, elist);
}

void PythonCode::parseAndEmitCodeComplit(const QDomNodeList& items)
{
    QList<IPythonCode::Completition> clist;
    QString fileName = items.at(0).firstChild().toText().nodeValue();
    QDomElement comp = items.at(1).childNodes().at(0).toElement();
    QString word = comp.attribute("word");
    int pos = comp.attribute("pos").toInt();
    for(uint i = 0; i < comp.childNodes().length(); ++i){
        QDomElement c = comp.childNodes().at(i).toElement();
        IPythonCode::Completition it;
        it.name = c.namedItem("name").firstChild().toText().nodeValue();
        it.scope = c.namedItem("scope").firstChild().toText().nodeValue();
        it.type = getType(c.namedItem("type").firstChild().toText().nodeValue());
        clist.append(it);
    }
    emit complitition(fileName, pos, word, clist);
}

IPythonCode::CompletitionType PythonCode::getType(const QString & named)
{
    if (named == "function")
        return IPythonCode::Function;
    if (named == "module")
        return IPythonCode::Module;
    if (named == "class")
        return IPythonCode::Class;
    return IPythonCode::Instance;
}

}