Add CompilerExplorer plugin

Change-Id: I534dea195a0b74f177fc227483752777485be177
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-07-06 14:11:36 +02:00
parent 92355bf40d
commit e34762c844
33 changed files with 2242 additions and 8 deletions

View File

@@ -86,10 +86,11 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
auto dee = static_cast<QDragEnterEvent *>(event);
if ((isFileDrop(dee) || isValueDrop(dee)) && (!m_filterFunction || m_filterFunction(dee, this))) {
event->accept();
} else {
event->ignore();
}
return true;
}
// event->ignore();
return false;
} else if (event->type() == QEvent::DragMove) {
event->accept();
return true;

View File

@@ -112,3 +112,4 @@ add_subdirectory(mcusupport)
add_subdirectory(saferenderer)
add_subdirectory(copilot)
add_subdirectory(terminal)
add_subdirectory(compilerexplorer)

View File

@@ -0,0 +1,30 @@
add_qtc_plugin(CompilerExplorer
PLUGIN_DEPENDS Core TextEditor
DEPENDS Qt::Network TerminalLib Spinner
SOURCES
api/config.h
api/compile.cpp
api/compile.h
api/compiler.cpp
api/compiler.h
api/compilerexplorerapi.h
api/language.cpp
api/language.h
api/library.cpp
api/library.h
api/request.h
compilerexploreraspects.cpp
compilerexploreraspects.h
compilerexplorerplugin.cpp
compilerexplorerplugin.h
compilerexplorertr.h
compilerexplorereditor.cpp
compilerexplorereditor.h
compilerexplorersettings.cpp
compilerexplorersettings.h
compilerexploreroptions.cpp
compilerexploreroptions.h
logos/logos.qrc
)

View File

@@ -0,0 +1,19 @@
{
"Name" : "CompilerExplorer",
"Version" : "${IDE_VERSION}",
"CompatVersion" : "${IDE_VERSION_COMPAT}",
"DisabledByDefault" : true,
"Vendor" : "The Qt Company Ltd",
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
"License" : [ "Commercial Usage",
"",
"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.",
"",
"GNU General Public License Usage",
"",
"Alternatively, this plugin may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this plugin. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html."
],
"Description" : "Integrates https://godbolt.org into Qt Creator.",
"Url" : "http://www.qt.io",
${IDE_PLUGIN_DEPENDENCIES}
}

View File

@@ -0,0 +1,29 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compile.h"
#include "request.h"
namespace CompilerExplorer::Api {
QFuture<CompileResult> compile(const Config &config, const CompileParameters &parameters)
{
const QUrl url = config.url({"api/compiler", parameters.compilerId, "compile"});
QNetworkRequest req(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
req.setRawHeader("Accept", "application/json");
return jsonRequest<CompileResult>(
config.networkManager,
req,
[](const QJsonDocument &response) {
CompileResult result;
QJsonObject obj = response.object();
return CompileResult::fromJson(obj);
},
QNetworkAccessManager::PostOperation,
parameters.toByteArray());
}
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,341 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "compiler.h"
#include "compilerexploreraspects.h"
#include "config.h"
#include <QList>
#include <QString>
#include <QFuture>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QNetworkAccessManager>
namespace CompilerExplorer::Api {
struct ExecuteParameters
{
ExecuteParameters &args(QStringList args)
{
obj["args"] = QJsonArray::fromStringList(args);
return *this;
}
ExecuteParameters &stdIn(const QString &stdIn)
{
obj["stdin"] = stdIn;
return *this;
}
QJsonObject obj;
};
struct CompileParameters
{
CompileParameters() = delete;
CompileParameters(const QString &cId)
: compilerId(cId)
{
obj["compiler"] = cId;
}
CompileParameters(const Compiler &compiler)
: compilerId(compiler.id)
{
obj["compiler"] = compiler.id;
}
QByteArray toByteArray() const { return QJsonDocument(obj).toJson(QJsonDocument::Compact); }
struct Options
{
Options &userArguments(const QString &userArguments)
{
obj["userArguments"] = userArguments;
return *this;
}
struct CompilerOptions
{
bool skipAsm{false};
bool executorRequest{false};
};
Options &compilerOptions(CompilerOptions compilerOptions)
{
QJsonObject co;
co["skipAsm"] = compilerOptions.skipAsm;
co["executorRequest"] = compilerOptions.executorRequest;
obj["compilerOptions"] = co;
return *this;
}
Options &executeParameters(ExecuteParameters executeParameters)
{
obj["executeParameters"] = executeParameters.obj;
return *this;
}
struct Filters
{
bool binary{false};
bool binaryObject{false};
bool commentOnly{true};
bool demangle{true};
bool directives{true};
bool execute{false};
bool intel{true};
bool labels{true};
bool libraryCode{false};
bool trim{false};
bool debugCalls{false};
};
Options &filters(Filters filters)
{
QJsonObject filter;
filter["binary"] = filters.binary;
filter["binaryObject"] = filters.binaryObject;
filter["commentOnly"] = filters.commentOnly;
filter["demangle"] = filters.demangle;
filter["directives"] = filters.directives;
filter["execute"] = filters.execute;
filter["intel"] = filters.intel;
filter["labels"] = filters.labels;
filter["libraryCode"] = filters.libraryCode;
filter["trim"] = filters.trim;
filter["debugCalls"] = filters.debugCalls;
obj["filters"] = filter;
return *this;
}
struct Libraries
{
QJsonArray array;
Libraries &add(const QString id, const QString version)
{
QJsonObject obj;
obj["id"] = id;
obj["version"] = version;
array.append(obj);
return *this;
}
};
Options &libraries(const Libraries &libraries)
{
obj["libraries"] = libraries.array;
return *this;
}
Options &libraries(const CompilerExplorer::LibrarySelectionAspect &aspect)
{
Libraries result;
for (const auto &key : aspect.value().keys()) {
result.add(key, aspect.value()[key]);
}
obj["libraries"] = result.array;
return *this;
}
QJsonObject obj;
};
CompileParameters &source(const QString &source)
{
obj["source"] = source;
return *this;
}
CompileParameters &language(const QString &languageId)
{
obj["lang"] = languageId;
return *this;
}
CompileParameters &options(Options options)
{
obj["options"] = options.obj;
return *this;
}
QJsonObject obj;
QString compilerId;
};
struct CompilerResult
{
struct Line
{
QString text;
struct Tag
{
int column;
QString file;
int line;
int severity;
QString text;
static Tag fromJson(const QJsonObject &obj)
{
Tag tag;
tag.column = obj["column"].toInt();
tag.file = obj["file"].toString();
tag.line = obj["line"].toInt();
tag.severity = obj["severity"].toInt();
tag.text = obj["text"].toString();
return tag;
}
};
std::optional<Tag> tag;
static Line fromJson(const QJsonObject &obj)
{
Line line;
line.text = obj["text"].toString();
if (obj.contains("tag")) {
line.tag = Tag::fromJson(obj["tag"].toObject());
}
return line;
}
};
int code;
bool timedOut;
bool truncated;
QList<Line> stdErr;
QList<Line> stdOut;
static CompilerResult fromJson(const QJsonObject &object)
{
CompilerResult result;
result.timedOut = object["timedOut"].toBool();
result.truncated = object["truncated"].toBool();
result.code = object["code"].toInt();
for (const auto &line : object["stderr"].toArray())
result.stdErr.append(Line::fromJson(line.toObject()));
for (const auto &line : object["stdout"].toArray())
result.stdOut.append(Line::fromJson(line.toObject()));
return result;
}
};
struct CompileResult : CompilerResult
{
struct Asm
{
// A part of the asm that is a (hyper)link to a label (the name references labelDefinitions)
struct Label
{
QString name;
struct Range
{
int startCol;
int endCol;
} range;
static Label fromJson(const QJsonObject &object)
{
Label label;
label.name = object["name"].toString();
label.range.startCol = object["range"]["startCol"].toInt();
label.range.endCol = object["range"]["endCol"].toInt();
return label;
}
};
QList<Label> labels;
// The part of the source that generated this asm
struct
{
int column;
QString file;
int line;
} source;
QString text;
QStringList opcodes;
static Asm fromJson(const QJsonObject &object)
{
Asm asm_;
asm_.text = object["text"].toString();
auto opcodes = object["opcodes"].toArray();
for (const auto &opcode : opcodes)
asm_.opcodes.append(opcode.toString());
asm_.source.column = object["source"]["column"].toInt();
asm_.source.file = object["source"]["file"].toString();
asm_.source.line = object["source"]["line"].toInt();
for (const auto &label : object["labels"].toArray()) {
asm_.labels.append(Label::fromJson(label.toObject()));
}
return asm_;
}
};
struct ExecResult
{
int code;
bool didExecute;
bool timedOut;
bool truncated;
QStringList stdOutLines;
QStringList stdErrLines;
CompilerResult buildResult;
static ExecResult fromJson(const QJsonObject &object)
{
ExecResult result;
result.code = object["code"].toInt();
result.didExecute = object["didExecute"].toBool();
result.timedOut = object["timedOut"].toBool();
result.truncated = object["truncated"].toBool();
for (const auto &line : object["stdout"].toArray())
result.stdOutLines.append(line.toObject()["text"].toString());
for (const auto &line : object["stderr"].toArray())
result.stdErrLines.append(line.toObject()["text"].toString());
result.buildResult = CompilerResult::fromJson(object["buildResult"].toObject());
return result;
}
};
QMap<QString, int> labelDefinitions;
QList<Asm> assemblyLines;
std::optional<ExecResult> execResult;
static CompileResult fromJson(const QJsonObject &object)
{
CompilerResult compilerResult = CompilerResult::fromJson(object);
CompileResult result;
result.code = compilerResult.code;
result.timedOut = compilerResult.timedOut;
result.truncated = compilerResult.truncated;
result.stdOut = compilerResult.stdOut;
result.stdErr = compilerResult.stdErr;
if (object.contains("labelDefinitions")) {
QJsonObject labelDefinitions = object["labelDefinitions"].toObject();
for (const QString &key : labelDefinitions.keys())
result.labelDefinitions[key] = labelDefinitions[key].toInt();
}
if (object.contains("asm")) {
for (const auto &asmLine : object["asm"].toArray())
result.assemblyLines.append(Asm::fromJson(asmLine.toObject()));
}
if (object.contains("execResult"))
result.execResult = ExecResult::fromJson(object["execResult"].toObject());
return result;
}
};
QFuture<CompileResult> compile(const Config &config, const CompileParameters &parameters);
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,65 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compiler.h"
#include "request.h"
#include <QFutureWatcher>
#include <QUrlQuery>
namespace CompilerExplorer::Api {
QFuture<Compilers> compilers(const Config &config,
const QString &languageId,
const QSet<QString> &extraFields)
{
QUrl url = config.url(QStringList{"api/compilers"}
<< (languageId.isEmpty() ? QString() : languageId));
QString fieldParam;
if (!extraFields.isEmpty()) {
QSet<QString> fields = {"id", "name", "lang", "compilerType", "semver"};
fields.unite(extraFields);
for (const auto &field : fields) {
if (!fieldParam.isEmpty())
fieldParam += ",";
fieldParam += field;
}
}
if (!fieldParam.isEmpty())
url.setQuery(QUrlQuery{{"fields", fieldParam}});
QNetworkRequest req(url);
req.setRawHeader("Accept", "application/json");
auto fromJson = [extraFields](const QJsonDocument &doc) {
QJsonArray compilers = doc.array();
Compilers result;
for (const auto &compiler : compilers) {
QJsonObject obj = compiler.toObject();
Compiler c;
c.id = obj["id"].toString();
c.name = obj["name"].toString();
c.languageId = obj["lang"].toString();
c.compilerType = obj["compilerType"].toString();
c.version = obj["semver"].toString();
c.instructionSet = obj["instructionSet"].toString();
for (const auto &field : extraFields) {
c.extraFields[field] = obj[field].toString();
}
result.append(c);
}
return result;
};
return jsonRequest<Compilers>(config.networkManager, req, fromJson);
}
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "config.h"
#include <QFuture>
#include <QList>
#include <QMap>
#include <QNetworkAccessManager>
#include <QSet>
#include <QString>
namespace CompilerExplorer::Api {
struct Compiler
{
QString id;
QString name;
QString languageId;
QString compilerType;
QString version;
QString instructionSet;
QMap<QString, QString> extraFields;
};
using Compilers = QList<Compiler>;
QFuture<Compilers> compilers(const Config &config,
const QString &languageId = {},
const QSet<QString> &extraFields = {});
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,9 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "compile.h"
#include "compiler.h"
#include "language.h"
#include "library.h"

View File

@@ -0,0 +1,30 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QNetworkAccessManager>
#include <QUrl>
namespace CompilerExplorer::Api {
struct Config
{
Config(QNetworkAccessManager *networkManager)
: networkManager(networkManager)
{}
Config(QNetworkAccessManager *networkManager, const QUrl &baseUrl)
: networkManager(networkManager)
, baseUrl(baseUrl){};
Config(QNetworkAccessManager *networkManager, const QString &baseUrl)
: networkManager(networkManager)
, baseUrl(QUrl::fromUserInput(baseUrl)){};
QNetworkAccessManager *networkManager;
QUrl baseUrl{"https://godbolt.org/"};
QUrl url(const QStringList &paths) const { return baseUrl.resolved(paths.join("/")); }
};
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,43 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "language.h"
#include "request.h"
#include <QUrlQuery>
namespace CompilerExplorer::Api {
QFuture<Languages> languages(const Config &config)
{
QUrl url = config.url({"api/languages"});
url.setQuery(QUrlQuery{{"fields", "id,name,extensions,logoUrl"}});
QNetworkRequest req(url);
req.setRawHeader("Accept", "application/json");
return jsonRequest<Languages>(config.networkManager, req, [](const QJsonDocument &doc) {
QJsonArray languages = doc.array();
Languages result;
for (const auto &language : languages) {
QJsonObject obj = language.toObject();
Language l;
l.id = obj["id"].toString();
l.name = obj["name"].toString();
l.logoUrl = obj["logoUrl"].toString();
QJsonArray extensions = obj["extensions"].toArray();
for (const auto &extension : extensions) {
l.extensions.append(extension.toString());
}
result.append(l);
}
std::sort(result.begin(), result.end(), [](const Language &a, const Language &b) {
return a.name < b.name;
});
return result;
});
}
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,27 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "config.h"
#include <QFuture>
#include <QNetworkAccessManager>
#include <QString>
#include <QStringList>
namespace CompilerExplorer::Api {
struct Language
{
QString id;
QString name;
QString logoUrl;
QStringList extensions;
QString monaco;
};
using Languages = QList<Language>;
QFuture<Languages> languages(const Config &config);
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "library.h"
#include "request.h"
#include <utils/qtcassert.h>
namespace CompilerExplorer::Api {
QFuture<Libraries> libraries(const Config &config, const QString &languageId)
{
QTC_ASSERT(!languageId.isEmpty(),
return QtFuture::makeExceptionalFuture<Libraries>(
std::make_exception_ptr(std::runtime_error("Language ID is empty."))));
const QUrl url = config.url({"api/libraries", languageId});
QNetworkRequest req(url);
req.setRawHeader("Accept", "application/json");
return jsonRequest<Libraries>(config.networkManager, req, [](const QJsonDocument &doc) {
QJsonArray libraries = doc.array();
Libraries result;
for (const auto &library : libraries) {
QJsonObject obj = library.toObject();
Library l;
l.id = obj["id"].toString();
l.name = obj["name"].toString();
l.url = QUrl::fromUserInput(obj["url"].toString());
QJsonArray versions = obj["versions"].toArray();
for (const auto &version : versions) {
l.versions.append({
version.toObject()["version"].toString(),
version.toObject()["id"].toString(),
});
}
result.append(l);
}
return result;
});
}
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,32 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "config.h"
#include <QFuture>
#include <QList>
#include <QNetworkAccessManager>
#include <QString>
#include <QUrl>
namespace CompilerExplorer::Api {
struct Library
{
QString id;
QString name;
QUrl url;
struct Version
{
QString version;
QString id;
};
QList<Version> versions;
};
using Libraries = QList<Library>;
QFuture<Libraries> libraries(const Config &config, const QString &languageId);
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,139 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QFuture>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QString>
static Q_LOGGING_CATEGORY(apiLog, "qtc.compilerexplorer.api", QtWarningMsg);
namespace CompilerExplorer::Api {
static inline QString toString(QNetworkAccessManager::Operation op)
{
switch (op) {
case QNetworkAccessManager::GetOperation:
return QStringLiteral("GET");
case QNetworkAccessManager::PostOperation:
return QStringLiteral("POST");
case QNetworkAccessManager::PutOperation:
return QStringLiteral("PUT");
case QNetworkAccessManager::DeleteOperation:
return QStringLiteral("DELETE");
case QNetworkAccessManager::HeadOperation:
return QStringLiteral("HEAD");
case QNetworkAccessManager::CustomOperation:
return QStringLiteral("CUSTOM");
case QNetworkAccessManager::UnknownOperation:
break;
}
return "<unknown>";
}
static int debugRequestId = 0;
template<typename Result>
QFuture<Result> request(
QNetworkAccessManager *networkManager,
const QNetworkRequest &req,
std::function<void(const QByteArray &, QSharedPointer<QPromise<Result>>)> callback,
QNetworkAccessManager::Operation op = QNetworkAccessManager::GetOperation,
const QByteArray &outData = {})
{
QSharedPointer<QPromise<Result>> p(new QPromise<Result>);
p->start();
// For logging purposes only
debugRequestId += 1;
const auto reqId = [r = debugRequestId] { return QString("[%1]").arg(r); };
if (outData.isEmpty())
qCDebug(apiLog).noquote() << reqId() << "Requesting" << toString(op)
<< req.url().toString();
else
qCDebug(apiLog).noquote() << reqId() << "Requesting" << toString(op) << req.url().toString()
<< "with payload:" << QString::fromUtf8(outData);
QNetworkReply *reply = nullptr;
switch (op) {
case QNetworkAccessManager::GetOperation:
reply = networkManager->get(req);
break;
case QNetworkAccessManager::PostOperation:
reply = networkManager->post(req, outData);
break;
case QNetworkAccessManager::PutOperation:
reply = networkManager->put(req, outData);
break;
case QNetworkAccessManager::DeleteOperation:
reply = networkManager->deleteResource(req);
break;
default:
return QtFuture::makeExceptionalFuture<Result>(
std::make_exception_ptr(std::runtime_error("Unsupported operation")));
}
QObject::connect(reply, &QNetworkReply::finished, [p, reply, callback, reqId] {
if (reply->error() != QNetworkReply::NoError) {
qCWarning(apiLog).noquote()
<< reqId() << "Request failed:" << reply->error() << reply->errorString();
QString errorMessage;
if (reply->error() == QNetworkReply::ContentNotFoundError) {
errorMessage = QObject::tr("Not found");
} else
errorMessage = reply->errorString();
p->setException(std::make_exception_ptr(std::runtime_error(errorMessage.toUtf8())));
reply->deleteLater();
p->finish();
return;
}
QByteArray data = reply->readAll();
qCDebug(apiLog).noquote() << reqId() << "Request finished:" << data;
callback(data, p);
reply->deleteLater();
p->finish();
});
return p->future();
}
template<typename Result>
QFuture<Result> jsonRequest(QNetworkAccessManager *networkManager,
const QNetworkRequest &req,
std::function<Result(QJsonDocument)> callback,
QNetworkAccessManager::Operation op
= QNetworkAccessManager::GetOperation,
const QByteArray &outData = {})
{
return request<Result>(
networkManager,
req,
[callback](const QByteArray &reply, auto promise) {
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(reply, &error);
if (error.error != QJsonParseError::NoError) {
promise->setException(
std::make_exception_ptr(std::runtime_error(error.errorString().toUtf8())));
return;
}
promise->addResult(callback(doc));
},
op,
outData);
}
} // namespace CompilerExplorer::Api

View File

@@ -0,0 +1,37 @@
import qbs 1.0
QtcPlugin {
name: "CompilerExplorer"
Depends { name: "Core" }
Depends { name: "Qt"; submodules: ["widgets", "network"] }
files: [
"api/compile.cpp",
"api/compile.h",
"api/compiler.cpp",
"api/compiler.h",
"api/compilerexplorerapi.h",
"api/config.h",
"api/language.cpp",
"api/language.h",
"api/library.cpp",
"api/library.h",
"api/request.h",
"compilerexploreraspects.cpp",
"compilerexploreraspects.h",
"compilerexplorerconstants.h",
"compilerexplorereditor.cpp",
"compilerexplorereditor.h",
"compilerexploreroptions.cpp",
"compilerexploreroptions.h",
"compilerexplorerplugin.cpp",
"compilerexplorerplugin.h",
"compilerexplorersettings.cpp",
"compilerexplorersettings.h",
"compilerexplorertr.h",
"logos/logos.qrc"
]
}

View File

@@ -0,0 +1,262 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compilerexploreraspects.h"
#include "compilerexplorertr.h"
#include "api/library.h"
#include <utils/layoutbuilder.h>
#include <QComboBox>
#include <QCompleter>
#include <QFutureWatcher>
#include <QPushButton>
#include <QStackedLayout>
using namespace Utils;
namespace CompilerExplorer {
StringSelectionAspect::StringSelectionAspect(AspectContainer *container)
: TypedAspect<QString>(container)
{}
void StringSelectionAspect::bufferToGui()
{
if (!m_model)
return;
for (int i = 0; i < m_model->rowCount(); ++i) {
auto cur = m_model->item(i);
if (cur->data() == m_buffer) {
m_selectionModel->setCurrentIndex(cur->index(),
QItemSelectionModel::SelectionFlag::ClearAndSelect);
return;
}
}
if (m_model->rowCount() > 0)
m_selectionModel->setCurrentIndex(m_model->item(0)->index(),
QItemSelectionModel::SelectionFlag::ClearAndSelect);
else
m_selectionModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::SelectionFlag::Clear);
handleGuiChanged();
}
bool StringSelectionAspect::guiToBuffer()
{
if (!m_model)
return false;
auto oldBuffer = m_buffer;
QModelIndex index = m_selectionModel->currentIndex();
if (index.isValid())
m_buffer = index.data(Qt::UserRole + 1).toString();
else
m_buffer.clear();
return oldBuffer != m_buffer;
}
void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{
QTC_ASSERT(m_fillCallback, return);
auto cb = [this](const QList<QStandardItem *> &items) {
m_model->clear();
for (QStandardItem *item : items)
m_model->appendRow(item);
bufferToGui();
};
if (!m_model) {
m_model = new QStandardItemModel(this);
m_selectionModel = new QItemSelectionModel(m_model);
connect(this, &StringSelectionAspect::refillRequested, this, [this, cb] {
m_fillCallback(cb);
});
m_fillCallback(cb);
}
QComboBox *comboBox = new QComboBox();
comboBox->setInsertPolicy(QComboBox::InsertPolicy::NoInsert);
comboBox->setEditable(true);
comboBox->completer()->setCompletionMode(QCompleter::PopupCompletion);
comboBox->completer()->setFilterMode(Qt::MatchContains);
comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon);
comboBox->setCurrentText(value());
comboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
comboBox->setModel(m_model);
connect(m_selectionModel,
&QItemSelectionModel::currentChanged,
comboBox,
[comboBox](QModelIndex currentIdx) {
if (currentIdx.isValid() && comboBox->currentIndex() != currentIdx.row())
comboBox->setCurrentIndex(currentIdx.row());
});
connect(comboBox, &QComboBox::activated, this, [this, comboBox] {
m_selectionModel->setCurrentIndex(m_model->index(comboBox->currentIndex(), 0),
QItemSelectionModel::SelectionFlag::ClearAndSelect);
handleGuiChanged();
});
if (m_selectionModel->currentIndex().isValid())
comboBox->setCurrentIndex(m_selectionModel->currentIndex().row());
return addLabeledItem(parent, comboBox);
}
LibrarySelectionAspect::LibrarySelectionAspect(AspectContainer *container)
: TypedAspect<QMap<QString, QString>>(container)
{}
void LibrarySelectionAspect::bufferToGui()
{
if (!m_model)
return;
for (int i = 0; i < m_model->rowCount(); i++) {
QModelIndex idx = m_model->index(i, 0);
if (m_buffer.contains(qvariant_cast<Api::Library>(idx.data(LibraryData)).id))
m_model->setData(idx,
m_buffer[qvariant_cast<Api::Library>(idx.data(LibraryData)).id],
SelectedVersion);
else
m_model->setData(idx, QVariant(), SelectedVersion);
}
handleGuiChanged();
}
bool LibrarySelectionAspect::guiToBuffer()
{
if (!m_model)
return false;
auto oldBuffer = m_buffer;
m_buffer.clear();
for (int i = 0; i < m_model->rowCount(); i++) {
if (m_model->item(i)->data(SelectedVersion).isValid()) {
m_buffer.insert(qvariant_cast<Api::Library>(m_model->item(i)->data(LibraryData)).id,
m_model->item(i)->data(SelectedVersion).toString());
}
}
return oldBuffer != m_buffer;
}
void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{
using namespace Layouting;
QTC_ASSERT(m_fillCallback, return);
auto cb = [this](const QList<QStandardItem *> &items) {
for (QStandardItem *item : items)
m_model->appendRow(item);
bufferToGui();
};
if (!m_model) {
m_model = new QStandardItemModel(this);
connect(this, &LibrarySelectionAspect::refillRequested, this, [this, cb] {
m_model->clear();
m_fillCallback(cb);
});
m_fillCallback(cb);
}
QComboBox *nameCombo = new QComboBox();
nameCombo->setInsertPolicy(QComboBox::InsertPolicy::NoInsert);
nameCombo->setEditable(true);
nameCombo->completer()->setCompletionMode(QCompleter::PopupCompletion);
nameCombo->completer()->setFilterMode(Qt::MatchContains);
nameCombo->setModel(m_model);
QComboBox *versionCombo = new QComboBox();
versionCombo->addItem("--");
auto refreshVersionCombo = [nameCombo, versionCombo] {
versionCombo->clear();
versionCombo->addItem("--");
QString selected = nameCombo->currentData(SelectedVersion).toString();
Api::Library lib = qvariant_cast<Api::Library>(nameCombo->currentData(LibraryData));
for (const auto &version : lib.versions) {
versionCombo->addItem(version.version, version.id);
if (version.id == selected)
versionCombo->setCurrentIndex(versionCombo->count() - 1);
}
};
refreshVersionCombo();
connect(nameCombo, &QComboBox::currentIndexChanged, this, refreshVersionCombo);
connect(versionCombo, &QComboBox::activated, this, [this, nameCombo, versionCombo] {
m_model->setData(m_model->index(nameCombo->currentIndex(), 0),
versionCombo->currentData(),
SelectedVersion);
handleGuiChanged();
});
QPushButton *clearBtn = new QPushButton("Clear All");
connect(clearBtn, &QPushButton::clicked, clearBtn, [this, refreshVersionCombo] {
for (int i = 0; i < m_model->rowCount(); i++)
m_model->setData(m_model->index(i, 0), QVariant(), SelectedVersion);
handleGuiChanged();
refreshVersionCombo();
});
ElidingLabel *displayLabel = new ElidingLabel();
auto updateLabel = [displayLabel, this] {
QStringList libs;
for (int i = 0; i < m_model->rowCount(); i++) {
QModelIndex idx = m_model->index(i, 0);
if (idx.data(SelectedVersion).isValid())
libs.append(QString("%1 %2")
.arg(idx.data().toString())
.arg(idx.data(SelectedVersion).toString()));
}
if (libs.empty())
displayLabel->setText(Tr::tr("No libraries selected"));
else
displayLabel->setText(libs.join(", "));
};
connect(m_model, &QStandardItemModel::itemChanged, displayLabel, updateLabel);
updateLabel();
QPushButton *editBtn = new QPushButton(Tr::tr("Edit"));
QStackedLayout *stack{nullptr};
// clang-format off
auto s = Stack {
bindTo(&stack),
noMargin,
Row { noMargin, displayLabel, editBtn }.emerge(),
Row { noMargin, nameCombo, versionCombo, clearBtn }.emerge()
}.emerge();
// clang-format on
connect(editBtn, &QPushButton::clicked, this, [stack] { stack->setCurrentIndex(1); });
addLabeledItem(parent, s);
}
} // namespace CompilerExplorer

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <utils/aspects.h>
#include <QComboBox>
#include <QItemSelectionModel>
#include <QStandardItemModel>
namespace CompilerExplorer {
class StringSelectionAspect : public Utils::TypedAspect<QString>
{
Q_OBJECT
public:
StringSelectionAspect(Utils::AspectContainer *container = nullptr);
void addToLayout(Layouting::LayoutItem &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *> items)>;
using FillCallback = std::function<void(ResultCallback)>;
void setFillCallback(FillCallback callback) { m_fillCallback = callback; }
void refill() { emit refillRequested(); }
void bufferToGui() override;
bool guiToBuffer() override;
signals:
void refillRequested();
private:
FillCallback m_fillCallback;
QStandardItemModel *m_model{nullptr};
QItemSelectionModel *m_selectionModel{nullptr};
};
// QMap<Library.Id, Library.Version.Id>
class LibrarySelectionAspect : public Utils::TypedAspect<QMap<QString, QString>>
{
Q_OBJECT
public:
enum Roles {
LibraryData = Qt::UserRole + 1,
SelectedVersion,
};
LibrarySelectionAspect(Utils::AspectContainer *container = nullptr);
void addToLayout(Layouting::LayoutItem &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *>)>;
using FillCallback = std::function<void(ResultCallback)>;
void setFillCallback(FillCallback callback) { m_fillCallback = callback; }
void refill() { emit refillRequested(); }
void bufferToGui() override;
bool guiToBuffer() override;
signals:
void refillRequested();
private:
FillCallback m_fillCallback;
QStandardItemModel *m_model{nullptr};
};
} // namespace CompilerExplorer

View File

@@ -0,0 +1,9 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
namespace CompilerExplorer::Constants {
const char CE_EDITOR_ID[] = "CompilerExplorer.Editor";
const char CE_EDITOR_CONTEXT_ID[] = "CompilerExplorer.Editor.Context";
}

View File

@@ -0,0 +1,391 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compilerexplorereditor.h"
#include "compilerexplorerconstants.h"
#include "compilerexploreroptions.h"
#include "compilerexplorersettings.h"
#include "compilerexplorertr.h"
#include <aggregation/aggregate.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <texteditor/textdocument.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/textmark.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/hostosinfo.h>
#include <utils/layoutbuilder.h>
#include <utils/mimeutils.h>
#include <utils/mimetypes2/mimetype.h>
#include <utils/utilsicons.h>
#include <QCompleter>
#include <QDockWidget>
#include <QNetworkAccessManager>
#include <QPushButton>
#include <QSplitter>
#include <QStackedLayout>
#include <QStandardItemModel>
#include <QTemporaryFile>
#include <QTimer>
#include <QToolButton>
#include <chrono>
#include <iostream>
using namespace std::chrono_literals;
using namespace Aggregation;
using namespace TextEditor;
namespace CompilerExplorer {
class CodeEditorWidget : public TextEditorWidget
{
public:
CodeEditorWidget(Settings *settings)
: m_settings(settings){};
void updateHighlighter()
{
const QString ext = m_settings->languageExtension();
if (ext.isEmpty())
return;
Utils::MimeType mimeType = Utils::mimeTypeForFile("foo" + ext);
configureGenericHighlighter(mimeType);
}
private:
Settings *m_settings;
};
CompilerWidget::CompilerWidget()
: m_compilerSettings(&settings())
{
using namespace Layouting;
QVariantMap map;
m_compilerSettings.setAutoApply(true);
m_delayTimer = new QTimer(this);
m_delayTimer->setSingleShot(true);
m_delayTimer->setInterval(500ms);
connect(m_delayTimer, &QTimer::timeout, this, &CompilerWidget::doCompile);
for (const auto &aspect : m_compilerSettings.aspects())
QTC_CHECK(
connect(aspect, &Utils::BaseAspect::changed, m_delayTimer, qOverload<>(&QTimer::start)));
m_asmEditor = new TextEditorWidget;
m_asmDocument = QSharedPointer<TextDocument>(new TextDocument);
m_asmDocument->setFilePath("asm.asm");
m_asmEditor->setTextDocument(m_asmDocument);
m_asmEditor->configureGenericHighlighter(Utils::mimeTypeForName("text/x-asm"));
auto advButton = new QPushButton;
QSplitter *splitter{nullptr};
auto advDlg = new QAction;
advDlg->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon());
advDlg->setIconText(Tr::tr("Advanced Options"));
connect(advDlg, &QAction::triggered, this, [advButton, this] {
CompilerExplorerOptions *dlg = new CompilerExplorerOptions(m_compilerSettings, advButton);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setWindowFlag(Qt::WindowType::Popup);
dlg->show();
QRect rect = dlg->geometry();
rect.moveTopRight(advButton->mapToGlobal(QPoint(advButton->width(), advButton->height())));
dlg->setGeometry(rect);
});
connect(advButton, &QPushButton::clicked, advDlg, &QAction::trigger);
advButton->setIcon(advDlg->icon());
// clang-format off
Column {
Row {
m_compilerSettings.compiler,
advButton,
},
Splitter {
bindTo(&splitter),
m_asmEditor,
createTerminal()
}
}.attachTo(this);
// clang-format on
m_spinner = new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Large, this);
}
Core::SearchableTerminal *CompilerWidget::createTerminal()
{
m_resultTerminal = new Core::SearchableTerminal();
m_resultTerminal->setAllowBlinkingCursor(false);
std::array<QColor, 20> colors{Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi0),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi1),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi2),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi3),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi4),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi5),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi6),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi7),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi8),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi9),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi10),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi11),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi12),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi13),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi14),
Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi15),
Utils::creatorTheme()->color(Utils::Theme::TerminalForeground),
Utils::creatorTheme()->color(Utils::Theme::TerminalBackground),
Utils::creatorTheme()->color(Utils::Theme::TerminalSelection),
Utils::creatorTheme()->color(Utils::Theme::TerminalFindMatch)};
m_resultTerminal->setColors(colors);
return m_resultTerminal;
}
void CompilerWidget::compile(const QString &source)
{
m_source = source;
m_delayTimer->start();
}
void CompilerWidget::doCompile()
{
using namespace Api;
QString compilerId = m_compilerSettings.compiler();
if (compilerId.isEmpty())
compilerId = "clang_trunk";
m_spinner->setVisible(true);
m_asmEditor->setEnabled(false);
CompileParameters params = CompileParameters(compilerId)
.source(m_source)
.language(settings().languageId())
.options(CompileParameters::Options()
.userArguments(m_compilerSettings.compilerOptions())
.compilerOptions({false, false})
.filters({false,
m_compilerSettings.compileToBinaryObject(),
true,
m_compilerSettings.demangleIdentifiers(),
true,
m_compilerSettings.executeCode(),
m_compilerSettings.intelAsmSyntax(),
true,
false,
false,
false})
.libraries(m_compilerSettings.libraries));
auto f = Api::compile(settings().apiConfig(), params);
m_compileWatcher.reset(new QFutureWatcher<CompileResult>);
connect(
m_compileWatcher.get(), &QFutureWatcher<CompileResult>::finished, this, [this] {
m_spinner->setVisible(false);
m_asmEditor->setEnabled(true);
try {
Api::CompileResult r = m_compileWatcher->result();
m_resultTerminal->restart();
m_resultTerminal->writeToTerminal("\x1b[?25l", false);
for (const auto &err : r.stdErr)
m_resultTerminal->writeToTerminal((err.text + "\r\n").toUtf8(), false);
for (const auto &out : r.stdOut)
m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false);
m_resultTerminal->writeToTerminal(
QString("ASM generation compiler returned: %1\r\n\r\n").arg(r.code).toUtf8(),
true);
if (r.execResult) {
for (const auto &err : r.execResult->buildResult.stdErr)
m_resultTerminal->writeToTerminal((err.text + "\r\n").toUtf8(), false);
for (const auto &out : r.execResult->buildResult.stdOut)
m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false);
m_resultTerminal
->writeToTerminal(QString("Execution build compiler returned: %1\r\n\r\n")
.arg(r.execResult->buildResult.code)
.toUtf8(),
true);
if (r.execResult->didExecute) {
m_resultTerminal->writeToTerminal(QString("Program returned: %1\r\n")
.arg(r.execResult->code)
.toUtf8(),
true);
for (const auto &err : r.execResult->stdErrLines)
m_resultTerminal
->writeToTerminal((" \033[0;31m" + err + "\033[0m\r\n").toUtf8(),
false);
for (const auto &out : r.execResult->stdOutLines)
m_resultTerminal->writeToTerminal((" " + out + "\r\n").toUtf8(), false);
}
}
for (auto mark : m_marks) {
delete mark;
}
m_marks.clear();
QString asmText;
for (auto l : r.assemblyLines)
asmText += l.text + "\n";
m_asmDocument->setPlainText(asmText);
int i = 0;
for (auto l : r.assemblyLines) {
i++;
if (l.opcodes.empty())
continue;
auto mark = new TextMark(m_asmDocument.get(),
i,
TextMarkCategory{"Bytes", "Bytes"});
mark->setLineAnnotation(l.opcodes.join(' '));
m_marks.append(mark);
}
} catch (const std::exception &e) {
qCritical() << "Exception: " << e.what();
}
});
m_compileWatcher->setFuture(f);
}
EditorWidget::EditorWidget(TextDocumentPtr document, QWidget *parent)
: Utils::FancyMainWindow(parent)
{
setAutoHideTitleBars(false);
setDockNestingEnabled(true);
setDocumentMode(true);
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::South);
using namespace Layouting;
m_codeEditor = new CodeEditorWidget(&settings());
m_codeEditor->setTextDocument(document);
m_codeEditor->updateHighlighter();
auto addCompilerButton = new QPushButton;
addCompilerButton->setText(Tr::tr("Add compiler"));
connect(addCompilerButton, &QPushButton::clicked, this, &EditorWidget::addCompiler);
// clang-format off
auto source =
Column {
Row {
settings().languageId,
settings().compilerExplorerUrl,
addCompilerButton,
},
m_codeEditor,
}.emerge();
// clang-format on
source->setWindowTitle("Source code");
source->setObjectName("source_code");
addDockWidget(Qt::LeftDockWidgetArea, addDockForWidget(source));
addCompiler();
Aggregate *agg = Aggregate::parentAggregate(m_codeEditor);
if (!agg) {
agg = new Aggregate;
agg->add(m_codeEditor);
}
agg->add(this);
m_context = new Core::IContext(this);
m_context->setWidget(this);
m_context->setContext(Core::Context(Constants::CE_EDITOR_CONTEXT_ID));
Core::ICore::addContextObject(m_context);
connect(m_codeEditor, &QPlainTextEdit::textChanged, this, &EditorWidget::sourceCodeChanged);
connect(&settings(),
&Settings::languagesChanged,
m_codeEditor,
&CodeEditorWidget::updateHighlighter);
connect(&settings().languageId,
&StringSelectionAspect::changed,
this,
&EditorWidget::onLanguageChanged);
setFocusProxy(m_codeEditor);
}
void EditorWidget::onLanguageChanged()
{
m_codeEditor->updateHighlighter();
}
void EditorWidget::addCompiler()
{
m_compilerCount++;
auto compiler = new CompilerWidget;
compiler->setWindowTitle("Compiler #" + QString::number(m_compilerCount));
compiler->setObjectName("compiler_" + QString::number(m_compilerCount));
addDockWidget(Qt::RightDockWidgetArea, addDockForWidget(compiler));
if (m_codeEditor && m_codeEditor->textDocument())
compiler->compile(QString::fromUtf8(m_codeEditor->textDocument()->contents()));
connect(this, &EditorWidget::sourceCodeChanged, compiler, [this, compiler] {
compiler->compile(QString::fromUtf8(m_codeEditor->textDocument()->contents()));
});
}
class Editor : public Core::IEditor
{
public:
Editor()
: m_document(new TextDocument(Constants::CE_EDITOR_ID))
{
setWidget(new EditorWidget(m_document));
}
Core::IDocument *document() const override { return m_document.data(); }
QWidget *toolBar() override { return nullptr; }
TextDocumentPtr m_document;
};
EditorFactory::EditorFactory()
: m_actionHandler(Constants::CE_EDITOR_ID,
Constants::CE_EDITOR_CONTEXT_ID,
TextEditor::TextEditorActionHandler::None,
[](Core::IEditor *editor) -> TextEditorWidget * {
return Aggregation::query<TextEditorWidget>(editor->widget());
})
{
setId(Constants::CE_EDITOR_ID);
setDisplayName(Tr::tr("Compiler Explorer Editor"));
setEditorCreator([]() { return new Editor(); });
}
} // namespace CompilerExplorer

View File

@@ -0,0 +1,95 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "api/compile.h"
#include "compilerexplorersettings.h"
#include <coreplugin/terminal/searchableterminal.h>
#include <solutions/spinner/spinner.h>
#include <texteditor/texteditor.h>
#include <texteditor/texteditoractionhandler.h>
#include <utils/fancymainwindow.h>
#include <QFutureWatcher>
#include <QMainWindow>
#include <QSplitter>
#include <memory>
namespace CppEditor {
class CppEditorWidget;
}
namespace CompilerExplorer {
class CodeEditorWidget;
class CompilerWidget : public QWidget
{
Q_OBJECT
public:
CompilerWidget();
Core::SearchableTerminal *createTerminal();
void compile(const QString &source);
private:
void doCompile();
private:
TextEditor::TextEditorWidget *m_asmEditor{nullptr};
Core::SearchableTerminal *m_resultTerminal{nullptr};
SpinnerSolution::Spinner *m_spinner{nullptr};
QSharedPointer<TextEditor::TextDocument> m_asmDocument;
std::unique_ptr<QFutureWatcher<Api::CompileResult>> m_compileWatcher;
CompilerExplorer::CompilerSettings m_compilerSettings;
QString m_source;
QTimer *m_delayTimer{nullptr};
QList<TextEditor::TextMark *> m_marks;
};
class EditorWidget : public Utils::FancyMainWindow
{
Q_OBJECT
public:
EditorWidget(QSharedPointer<TextEditor::TextDocument> document = nullptr, QWidget *parent = nullptr);
void addCompiler();
protected:
Core::SearchableTerminal *createTerminal();
void onLanguageChanged();
signals:
void sourceCodeChanged();
private:
CodeEditorWidget *m_codeEditor;
CompilerExplorer::Settings m_currentSettings;
QSplitter *m_mainSplitter;
int m_compilerCount{0};
Core::IContext *m_context;
};
class EditorFactory : public Core::IEditorFactory
{
public:
EditorFactory();
private:
TextEditor::TextEditorActionHandler m_actionHandler;
};
} // namespace CompilerExplorer

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compilerexploreroptions.h"
#include "compilerexplorersettings.h"
#include <utils/layoutbuilder.h>
#include <QFutureWatcher>
#include <QScrollArea>
#include <QStandardItemModel>
namespace CompilerExplorer {
CompilerExplorerOptions::CompilerExplorerOptions(CompilerSettings &compilerSettings, QWidget *parent)
: QDialog(parent, Qt::Popup)
{
using namespace Layouting;
// clang-format off
Form {
compilerSettings.compiler, br,
compilerSettings.compilerOptions, br,
compilerSettings.libraries, br,
compilerSettings.compileToBinaryObject, br,
compilerSettings.executeCode, br,
compilerSettings.intelAsmSyntax, br,
compilerSettings.demangleIdentifiers, br,
}.attachTo(this);
// clang-format on
}
} // namespace CompilerExplorer

View File

@@ -0,0 +1,18 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "compilerexplorersettings.h"
#include <QDialog>
namespace CompilerExplorer {
class CompilerExplorerOptions : public QDialog
{
public:
CompilerExplorerOptions(CompilerSettings &settings, QWidget *parent = nullptr);
};
} // namespace CompilerExplorer

View File

@@ -0,0 +1,56 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compilerexplorerplugin.h"
#include "compilerexplorerconstants.h"
#include "compilerexplorereditor.h"
#include "compilerexplorersettings.h"
#include "compilerexplorertr.h"
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <cppeditor/cppeditorconstants.h>
#include <QMenu>
namespace CompilerExplorer {
namespace Internal {
CompilerExplorerPlugin::CompilerExplorerPlugin() {}
CompilerExplorerPlugin::~CompilerExplorerPlugin() {}
void CompilerExplorerPlugin::initialize()
{
static CompilerExplorer::EditorFactory ceEditorFactory;
auto action = new QAction(Tr::tr("Open Compiler Explorer"), this);
connect(action, &QAction::triggered, this, [] {
CompilerExplorer::Settings settings;
const QString src = settings.source();
QString name("Compiler Explorer");
Core::EditorManager::openEditorWithContents(Constants::CE_EDITOR_ID, &name, src.toUtf8());
});
Core::ActionContainer *mtools = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
Core::ActionContainer *mCompilerExplorer = Core::ActionManager::createMenu(
"Tools.CompilerExplorer");
QMenu *menu = mCompilerExplorer->menu();
menu->setTitle(Tr::tr("Compiler Explorer"));
mtools->addMenu(mCompilerExplorer);
Core::Command *cmd
= Core::ActionManager::registerAction(action, "CompilerExplorer.CompilerExplorerAction");
mCompilerExplorer->addAction(cmd);
}
void CompilerExplorerPlugin::extensionsInitialized() {}
} // namespace Internal
} // namespace CompilerExplorer

View File

@@ -0,0 +1,24 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <extensionsystem/iplugin.h>
namespace CompilerExplorer::Internal {
class CompilerExplorerPlugin : public ExtensionSystem::IPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CompilerExplorer.json")
public:
CompilerExplorerPlugin();
~CompilerExplorerPlugin() override;
void initialize() override;
void extensionsInitialized() override;
};
} // namespace CompilerExplorer::Internal

View File

@@ -0,0 +1,252 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "compilerexplorersettings.h"
#include "compilerexplorertr.h"
#include "api/compiler.h"
#include "api/language.h"
#include "api/library.h"
#include <QComboBox>
#include <QFutureWatcher>
#include <QNetworkAccessManager>
namespace CompilerExplorer {
Settings &settings()
{
static Settings instance;
return instance;
}
static Api::Languages &cachedLanguages()
{
static Api::Languages instance;
return instance;
}
static QMap<QString, Api::Libraries> &cachedLibraries()
{
static QMap<QString, Api::Libraries> instance;
return instance;
}
static Api::Libraries &cachedLibraries(const QString &languageId)
{
return cachedLibraries()[languageId];
}
static QMap<QString, QMap<QString, QString>> &cachedCompilers()
{
static QMap<QString, QMap<QString, QString>> instance;
return instance;
}
Settings::Settings()
{
static QNetworkAccessManager networkManager;
m_networkAccessManager = &networkManager;
setSettingsGroup("CompilerExplorer");
source.setDefaultValue(R"(
int main()
{
return 0;
}
)");
compilerExplorerUrl.setLabelText(Tr::tr("Compiler Explorer URL:"));
compilerExplorerUrl.setToolTip(Tr::tr("URL of the Compiler Explorer instance to use"));
compilerExplorerUrl.setDefaultValue("https://godbolt.org/");
compilerExplorerUrl.setDisplayStyle(Utils::StringAspect::DisplayStyle::LineEditDisplay);
compilerExplorerUrl.setHistoryCompleter("CompilerExplorer.Url.History");
languageId.setDefaultValue("c++");
languageId.setLabelText(Tr::tr("Language:"));
languageId.setFillCallback([this](auto cb) { fillLanguageIdModel(cb); });
connect(&compilerExplorerUrl, &Utils::StringAspect::changed, this, [this] {
languageId.setValue(languageId.defaultValue());
cachedLanguages().clear();
languageId.refill();
});
readSettings();
}
QString Settings::languageExtension() const
{
auto it = std::find_if(std::begin(cachedLanguages()),
std::end(cachedLanguages()),
[this](const Api::Language &lang) { return lang.id == languageId(); });
if (it != cachedLanguages().end())
return it->extensions.first();
return ".cpp";
}
CompilerSettings::CompilerSettings(Settings *settings)
: m_parent(settings)
{
setAutoApply(true);
compilerOptions.setDefaultValue("-O3");
compilerOptions.setLabelText(Tr::tr("Compiler options:"));
compilerOptions.setToolTip(Tr::tr("Arguments passed to the compiler"));
compilerOptions.setDisplayStyle(Utils::StringAspect::DisplayStyle::LineEditDisplay);
compiler.setDefaultValue("clang_trunk");
compiler.setLabelText(Tr::tr("Compiler:"));
compiler.setFillCallback([this](auto cb) { fillCompilerModel(cb); });
libraries.setLabelText(Tr::tr("Libraries:"));
libraries.setFillCallback([this](auto cb) { fillLibraries(cb); });
executeCode.setLabelText(Tr::tr("Execute the code"));
compileToBinaryObject.setLabelText(Tr::tr("Compile to binary object"));
intelAsmSyntax.setLabelText(Tr::tr("Intel asm syntax"));
intelAsmSyntax.setDefaultValue(true);
demangleIdentifiers.setLabelText(Tr::tr("Demangle identifiers"));
demangleIdentifiers.setDefaultValue(true);
connect(&settings->compilerExplorerUrl, &Utils::StringAspect::changed, this, [this] {
cachedCompilers().clear();
cachedLibraries().clear();
compiler.refill();
libraries.refill();
});
connect(&settings->languageId, &StringSelectionAspect::changed, this, [this] {
compiler.refill();
libraries.refill();
if (m_parent->languageId() == "c++")
compilerOptions.setValue("-O3");
else
compilerOptions.setValue("");
});
}
void CompilerSettings::fillLibraries(LibrarySelectionAspect::ResultCallback cb)
{
const QString lang = m_parent->languageId();
auto fillFromCache = [cb, lang] {
QList<QStandardItem *> items;
for (const Api::Library &lib : cachedLibraries(lang)) {
QStandardItem *newItem = new QStandardItem(lib.name);
newItem->setData(QVariant::fromValue(lib), LibrarySelectionAspect::LibraryData);
items.append(newItem);
}
cb(items);
};
if (!cachedLibraries(lang).isEmpty()) {
fillFromCache();
return;
}
auto future = Api::libraries(m_parent->apiConfig(), lang);
auto watcher = new QFutureWatcher<Api::Libraries>(this);
watcher->setFuture(future);
QObject::connect(watcher,
&QFutureWatcher<Api::Libraries>::finished,
this,
[watcher, fillFromCache, lang]() {
try {
cachedLibraries(lang) = watcher->result();
fillFromCache();
} catch (const std::exception &e) {
qCritical() << e.what();
return;
}
});
}
void Settings::fillLanguageIdModel(StringSelectionAspect::ResultCallback cb)
{
auto fillFromCache = [cb, this] {
QList<QStandardItem *> items;
for (const Api::Language &language : cachedLanguages()) {
auto *newItem = new QStandardItem(language.name);
newItem->setData(language.id);
if (QFile::exists(":/compilerexplorer/logos/" + language.logoUrl)) {
QIcon icon(":/compilerexplorer/logos/" + language.logoUrl);
newItem->setIcon(icon);
}
items.append(newItem);
}
cb(items);
emit languagesChanged();
};
if (!cachedLanguages().isEmpty()) {
fillFromCache();
return;
}
auto future = Api::languages(apiConfig());
auto watcher = new QFutureWatcher<Api::Languages>(this);
watcher->setFuture(future);
QObject::connect(watcher,
&QFutureWatcher<Api::Languages>::finished,
this,
[watcher, fillFromCache]() {
try {
cachedLanguages() = watcher->result();
fillFromCache();
} catch (const std::exception &e) {
qCritical() << e.what();
return;
}
});
}
void CompilerSettings::fillCompilerModel(StringSelectionAspect::ResultCallback cb)
{
auto fillFromCache = [cb](auto it) {
QList<QStandardItem *> items;
for (const QString &key : it->keys()) {
QStandardItem *newItem = new QStandardItem(key);
newItem->setData(it->value(key));
items.append(newItem);
}
cb(items);
};
auto it = cachedCompilers().find(m_parent->languageId());
if (it != cachedCompilers().end()) {
fillFromCache(it);
return;
}
auto future = Api::compilers(m_parent->apiConfig(), m_parent->languageId());
auto watcher = new QFutureWatcher<Api::Compilers>(this);
watcher->setFuture(future);
QObject::connect(watcher,
&QFutureWatcher<Api::Compilers>::finished,
this,
[watcher, this, fillFromCache]() {
try {
auto result = watcher->result();
auto itCache = cachedCompilers().insert(m_parent->languageId(), {});
for (const Api::Compiler &compiler : result)
itCache->insert(compiler.name, compiler.id);
fillFromCache(itCache);
} catch (const std::exception &e) {
qCritical() << e.what();
return;
}
});
}
} // namespace CompilerExplorer

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include "api/config.h"
#include "compilerexploreraspects.h"
#include <utils/aspects.h>
#include <QNetworkAccessManager>
namespace CompilerExplorer {
class Settings;
class CompilerSettings : public Utils::AspectContainer
{
public:
CompilerSettings(Settings *settings);
StringSelectionAspect compiler{this};
Utils::StringAspect compilerOptions{this};
LibrarySelectionAspect libraries{this};
// "Filters"
Utils::BoolAspect executeCode{this};
Utils::BoolAspect compileToBinaryObject{this};
Utils::BoolAspect intelAsmSyntax{this};
Utils::BoolAspect demangleIdentifiers{this};
private:
void fillCompilerModel(StringSelectionAspect::ResultCallback cb);
void fillLibraries(LibrarySelectionAspect::ResultCallback cb);
Settings *m_parent;
};
class Settings : public Utils::AspectContainer
{
Q_OBJECT
public:
Settings();
StringSelectionAspect languageId{this};
Utils::StringAspect compilerExplorerUrl{this};
Utils::StringAspect source{this};
QNetworkAccessManager *networkAccessManager() const { return m_networkAccessManager; }
Api::Config apiConfig() const
{
return Api::Config(m_networkAccessManager, compilerExplorerUrl());
}
QString languageExtension() const;
signals:
void languagesChanged();
private:
void fillLanguageIdModel(StringSelectionAspect::ResultCallback cb);
QNetworkAccessManager *m_networkAccessManager{nullptr};
};
Settings &settings();
} // namespace CompilerExplorer

View File

@@ -0,0 +1,15 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <QCoreApplication>
namespace CompilerExplorer {
struct Tr
{
Q_DECLARE_TR_FUNCTIONS(QtC::CompilerExplorer)
};
} // namespace CompilerExplorer

View File

@@ -0,0 +1,3 @@
<RCC>
<qresource prefix="/compilerexplorer/logos/"></qresource>
</RCC>

View File

@@ -0,0 +1,50 @@
POST https://godbolt.org/api/compiler/clang_trunk/compile HTTP/1.1
content-type: application/json
Accept: application/json
{
"source": "int test() {return 20;};\nint main()\n{ return test();}",
"compiler": "clang_trunk",
"options": {
"userArguments": "",
"compilerOptions": {
"producePp": null,
"produceGccDump": {},
"produceOptInfo": false,
"produceCfg": false,
"produceIr": null,
"produceLLVMOptPipeline": null,
"produceDevice": false,
"overrides": []
},
"filters": {
"binaryObject": false,
"binary": false,
"execute": true,
"intel": false,
"demangle": true,
"labels": true,
"libraryCode": false,
"directives": true,
"commentOnly": true,
"trim": false,
"debugCalls": false
},
"tools": [],
"libraries": [],
"executeParameters": {
"args": "",
"stdin": ""
}
},
"lang": "c++",
"files": [],
"bypassCache": 0,
"allowStoreCodeDebug": true
}
###
GET https://godbolt.org/api/compilers/c++
Accept: application/json

View File

@@ -114,7 +114,7 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) :
m_container->addWidget(empty);
m_widgetEditorMap.insert(empty, nullptr);
const auto dropSupport = new DropSupport(this, [this](QDropEvent *event, DropSupport *) {
/*const auto dropSupport = new DropSupport(this, [this](QDropEvent *event, DropSupport *) {
// do not accept move events except from other editor views (i.e. their tool bars)
// otherwise e.g. item views that support moving items within themselves would
// also "move" the item into the editor view, i.e. the item would be removed from the
@@ -127,7 +127,7 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) :
});
connect(dropSupport, &DropSupport::filesDropped,
this, &EditorView::openDroppedFiles);
*/
updateNavigatorActions();
}

View File

@@ -203,12 +203,12 @@ MainWindow::MainWindow()
statusBar()->setProperty("p_styled", true);
auto dropSupport = new DropSupport(this, [](QDropEvent *event, DropSupport *) {
/*auto dropSupport = new DropSupport(this, [](QDropEvent *event, DropSupport *) {
return event->source() == nullptr; // only accept drops from the "outside" (e.g. file manager)
});
connect(dropSupport, &DropSupport::filesDropped,
this, &MainWindow::openDroppedFiles);
*/
if (HostOsInfo::isLinuxHost()) {
m_trimTimer.setSingleShot(true);
m_trimTimer.setInterval(60000);