mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-25 19:32:52 +00:00
358 lines
9.9 KiB
C++
358 lines
9.9 KiB
C++
![]() |
/*
|
||
|
* 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 <http://www.gnu.org/licenses/>.
|
||
|
**/
|
||
|
|
||
|
#include "dcd.h"
|
||
|
#include <kdebug.h>
|
||
|
#include <QtCore/QFile>
|
||
|
|
||
|
|
||
|
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"
|