/* * Copyright 2014 David Herberth kde@dav1d.de * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . **/ #include "dcd.h" #include #include char DCDCompletionItemType::toChar(DCDCompletionItemType e) { switch (e) { case Invalid: return 0; case Calltip: return 1; case ClassName: return 'c'; case InterfaceName: return 'i'; case StructName: return 's'; case UnionName: return 'u'; case VariableName: return 'v'; case MemberVariableName: return 'm'; case Keyword: return 'k'; case FunctionName: return 'f'; case EnumName: return 'g'; case EnumMember: return 'e'; case PackageName: return 'p'; case ModuleName: return 'M'; } return 0; } DCDCompletionItemType::DCDCompletionItemType DCDCompletionItemType::fromChar(char c) { switch (c) { case 0: return Invalid; case 1: return Calltip; case 'c': return ClassName; case 'i': return InterfaceName; case 's': return StructName; case 'u': return UnionName; case 'v': return VariableName; case 'm': return MemberVariableName; case 'k': return Keyword; case 'f': return FunctionName; case 'g': return EnumName; case 'e': return EnumMember; case 'p': return PackageName; case 'M': return ModuleName; } return Invalid; } DCDCompletionItem::DCDCompletionItem(DCDCompletionItemType::DCDCompletionItemType t, QString s): type(t), name(s) { } #define RETURN_CACHED_ICON(name) {static QIcon icon(KIcon(name).pixmap(QSize(16, 16))); return icon;} QIcon DCDCompletionItem::icon() const { using namespace DCDCompletionItemType; switch (type) { case Invalid: break; case Calltip: RETURN_CACHED_ICON("code-function") case ClassName: RETURN_CACHED_ICON("code-class") case InterfaceName: RETURN_CACHED_ICON("code-class") case StructName: RETURN_CACHED_ICON("struct") case UnionName: RETURN_CACHED_ICON("union") case VariableName: RETURN_CACHED_ICON("code-variable") case MemberVariableName: RETURN_CACHED_ICON("field") case Keyword: RETURN_CACHED_ICON("field") case FunctionName: RETURN_CACHED_ICON("code-function") case EnumName: RETURN_CACHED_ICON("enum") case EnumMember: RETURN_CACHED_ICON("enum") case PackageName: RETURN_CACHED_ICON("field") case ModuleName: RETURN_CACHED_ICON("field") } return KIcon(); } QString DCDCompletionItem::typeLong() const { using namespace DCDCompletionItemType; switch (type) { case Invalid: return "invalid"; case Calltip: return "calltip"; case ClassName: return "class"; case InterfaceName: return "interface"; case StructName: return "struct"; case UnionName: return "union"; case VariableName: return "variable"; case MemberVariableName: return "member"; case Keyword: return "keyword"; case FunctionName: return "function"; case EnumName: return "enum"; case EnumMember: return "enum member"; case PackageName: return "package"; case ModuleName: return "module"; } return "completion"; } static const int TIMEOUT_START_SERVER = 200; static const int TIMEOUT_COMPLETE = 200; static const int TIMEOUT_IMPORTPATH = 200; static const int TIMEOUT_SHUTDOWN = 350; static const int TIMEOUT_SHUTDOWN_SERVER = 200; DCD::DCD(int port, const QString& server, const QString& client) { m_port = port; m_server = server; m_client = client; } int DCD::port() { return m_port; } bool DCD::running() { return m_sproc.state() == QProcess::Running; } bool DCD::startServer() { m_sproc.setProcessChannelMode(QProcess::MergedChannels); m_sproc.start(m_server, QStringList(QString("-p%1").arg(m_port))); bool started = m_sproc.waitForStarted(TIMEOUT_START_SERVER); bool finished = m_sproc.waitForFinished(TIMEOUT_START_SERVER); if (!started || finished || m_sproc.state() == QProcess::NotRunning) { kWarning() << "unable to start completion-server:" << m_sproc.exitCode(); kWarning() << m_sproc.readAll(); return false; } kDebug() << "started completion-server"; return true; } DCDCompletion DCD::complete(QString file, int offset) { QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(m_client, QStringList() << QString("-p%1").arg(m_port) << QString("-c%1").arg(offset) << file ); if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { kWarning() << "unable to complete:" << proc.exitCode(); kWarning() << proc.readAll(); return DCDCompletion(); } return processCompletion(proc.readAllStandardOutput()); } DCDCompletion DCD::complete(QByteArray data, int offset) { QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(m_client, QStringList() << QString("-p%1").arg(m_port) << QString("-c%1").arg(offset) ); proc.write(data); proc.closeWriteChannel(); if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { kWarning() << "unable to complete: client didn't finish in time"; proc.close(); } else if (proc.exitCode() != 0) { kWarning() << "unable to complete:" << proc.exitCode(); kWarning() << proc.readAll(); } else { // everything Ok return processCompletion(proc.readAllStandardOutput()); } return DCDCompletion(); } QString DCD::doc(QByteArray data, int offset) { QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(m_client, QStringList() << QString("-p%1").arg(m_port) << QString("-c%1").arg(offset) << QString("--doc") ); proc.write(data); proc.closeWriteChannel(); if (!proc.waitForFinished(TIMEOUT_COMPLETE)) { kWarning() << "unable to lookup documentation: client didn't finish in time"; proc.close(); } else if (proc.exitCode() != 0) { kWarning() << "unable to lookup documentation:" << proc.exitCode(); kWarning() << proc.readAll(); } else { return proc.readAllStandardOutput(); } return QString(""); } DCDCompletion DCD::processCompletion(QString data) { DCDCompletion completion; QStringList lines = data.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); if (lines.length() == 0) { return completion; } QString type = lines.front(); if (type == "identifiers") { completion.type = DCDCompletionType::Identifiers; } else if (type == "calltips") { completion.type = DCDCompletionType::Calltips; } else { kWarning() << "Invalid type:" << type; return completion; } lines.pop_front(); foreach(QString line, lines) { if (line.trimmed().length() == 0) { continue; } QStringList kv = line.split(QRegExp("\\s+"), QString::SkipEmptyParts); if (kv.length() != 2 && completion.type != DCDCompletionType::Calltips) { kWarning() << "invalid completion data:" << kv.length() << completion.type; continue; } if (completion.type == DCDCompletionType::Identifiers) { completion.completions.append(DCDCompletionItem( DCDCompletionItemType::fromChar(kv[1].at(0).toAscii()), kv[0] )); } else { completion.completions.append(DCDCompletionItem( DCDCompletionItemType::Calltip, line )); } } return completion; } void DCD::addImportPath(QString path) { addImportPath(QStringList(path)); } void DCD::addImportPath(QStringList paths) { if (paths.isEmpty()) { return; } QStringList arguments = QStringList(QString("-p%1").arg(m_port)); foreach(QString path, paths) { if (QFile::exists(path)) arguments << QString("-I%1").arg(path); } kDebug() << "ARGUMENTS:" << arguments; QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(m_client, arguments); if (!proc.waitForFinished(TIMEOUT_IMPORTPATH)) { kWarning() << "unable to add importpath(s)" << paths << ":" << proc.exitCode(); kWarning() << proc.readAll(); } } void DCD::shutdown() { QProcess proc; proc.setProcessChannelMode(QProcess::MergedChannels); proc.start(m_client, QStringList() << QString("-p%1").arg(m_port) << QString("--shutdown") ); if (!proc.waitForFinished(TIMEOUT_SHUTDOWN)) { kWarning() << "unable to shutdown dcd:" << proc.exitCode(); kWarning() << proc.readAll(); } } bool DCD::stopServer() { if (m_sproc.state() == QProcess::Running) { kDebug() << "shutting down dcd"; shutdown(); if(!m_sproc.waitForFinished(TIMEOUT_SHUTDOWN_SERVER)) m_sproc.terminate(); if(!m_sproc.waitForFinished(TIMEOUT_SHUTDOWN_SERVER)) m_sproc.kill(); return true; } return false; } DCD::~DCD() { if (running()) { stopServer(); } } #include "moc_dcd.cpp"