forked from qt-creator/qt-creator
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:
@@ -86,10 +86,11 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
|
|||||||
auto dee = static_cast<QDragEnterEvent *>(event);
|
auto dee = static_cast<QDragEnterEvent *>(event);
|
||||||
if ((isFileDrop(dee) || isValueDrop(dee)) && (!m_filterFunction || m_filterFunction(dee, this))) {
|
if ((isFileDrop(dee) || isValueDrop(dee)) && (!m_filterFunction || m_filterFunction(dee, this))) {
|
||||||
event->accept();
|
event->accept();
|
||||||
} else {
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
// event->ignore();
|
||||||
|
return false;
|
||||||
|
|
||||||
} else if (event->type() == QEvent::DragMove) {
|
} else if (event->type() == QEvent::DragMove) {
|
||||||
event->accept();
|
event->accept();
|
||||||
return true;
|
return true;
|
||||||
|
@@ -112,3 +112,4 @@ add_subdirectory(mcusupport)
|
|||||||
add_subdirectory(saferenderer)
|
add_subdirectory(saferenderer)
|
||||||
add_subdirectory(copilot)
|
add_subdirectory(copilot)
|
||||||
add_subdirectory(terminal)
|
add_subdirectory(terminal)
|
||||||
|
add_subdirectory(compilerexplorer)
|
||||||
|
30
src/plugins/compilerexplorer/CMakeLists.txt
Normal file
30
src/plugins/compilerexplorer/CMakeLists.txt
Normal 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
|
||||||
|
)
|
19
src/plugins/compilerexplorer/CompilerExplorer.json.in
Normal file
19
src/plugins/compilerexplorer/CompilerExplorer.json.in
Normal 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}
|
||||||
|
}
|
29
src/plugins/compilerexplorer/api/compile.cpp
Normal file
29
src/plugins/compilerexplorer/api/compile.cpp
Normal 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 ¶meters)
|
||||||
|
{
|
||||||
|
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
|
341
src/plugins/compilerexplorer/api/compile.h
Normal file
341
src/plugins/compilerexplorer/api/compile.h
Normal 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 ¶meters);
|
||||||
|
|
||||||
|
} // namespace CompilerExplorer::Api
|
65
src/plugins/compilerexplorer/api/compiler.cpp
Normal file
65
src/plugins/compilerexplorer/api/compiler.cpp
Normal 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
|
34
src/plugins/compilerexplorer/api/compiler.h
Normal file
34
src/plugins/compilerexplorer/api/compiler.h
Normal 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
|
9
src/plugins/compilerexplorer/api/compilerexplorerapi.h
Normal file
9
src/plugins/compilerexplorer/api/compilerexplorerapi.h
Normal 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"
|
30
src/plugins/compilerexplorer/api/config.h
Normal file
30
src/plugins/compilerexplorer/api/config.h
Normal 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
|
43
src/plugins/compilerexplorer/api/language.cpp
Normal file
43
src/plugins/compilerexplorer/api/language.cpp
Normal 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
|
27
src/plugins/compilerexplorer/api/language.h
Normal file
27
src/plugins/compilerexplorer/api/language.h
Normal 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
|
48
src/plugins/compilerexplorer/api/library.cpp
Normal file
48
src/plugins/compilerexplorer/api/library.cpp
Normal 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
|
32
src/plugins/compilerexplorer/api/library.h
Normal file
32
src/plugins/compilerexplorer/api/library.h
Normal 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
|
139
src/plugins/compilerexplorer/api/request.h
Normal file
139
src/plugins/compilerexplorer/api/request.h
Normal 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
|
37
src/plugins/compilerexplorer/compilerexplorer.qbs
Normal file
37
src/plugins/compilerexplorer/compilerexplorer.qbs
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
262
src/plugins/compilerexplorer/compilerexploreraspects.cpp
Normal file
262
src/plugins/compilerexplorer/compilerexploreraspects.cpp
Normal 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
|
70
src/plugins/compilerexplorer/compilerexploreraspects.h
Normal file
70
src/plugins/compilerexplorer/compilerexploreraspects.h
Normal 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
|
9
src/plugins/compilerexplorer/compilerexplorerconstants.h
Normal file
9
src/plugins/compilerexplorer/compilerexplorerconstants.h
Normal 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";
|
||||||
|
}
|
391
src/plugins/compilerexplorer/compilerexplorereditor.cpp
Normal file
391
src/plugins/compilerexplorer/compilerexplorereditor.cpp
Normal 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
|
95
src/plugins/compilerexplorer/compilerexplorereditor.h
Normal file
95
src/plugins/compilerexplorer/compilerexplorereditor.h
Normal 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
|
34
src/plugins/compilerexplorer/compilerexploreroptions.cpp
Normal file
34
src/plugins/compilerexplorer/compilerexploreroptions.cpp
Normal 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
|
18
src/plugins/compilerexplorer/compilerexploreroptions.h
Normal file
18
src/plugins/compilerexplorer/compilerexploreroptions.h
Normal 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
|
56
src/plugins/compilerexplorer/compilerexplorerplugin.cpp
Normal file
56
src/plugins/compilerexplorer/compilerexplorerplugin.cpp
Normal 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
|
24
src/plugins/compilerexplorer/compilerexplorerplugin.h
Normal file
24
src/plugins/compilerexplorer/compilerexplorerplugin.h
Normal 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
|
252
src/plugins/compilerexplorer/compilerexplorersettings.cpp
Normal file
252
src/plugins/compilerexplorer/compilerexplorersettings.cpp
Normal 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
|
70
src/plugins/compilerexplorer/compilerexplorersettings.h
Normal file
70
src/plugins/compilerexplorer/compilerexplorersettings.h
Normal 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
|
15
src/plugins/compilerexplorer/compilerexplorertr.h
Normal file
15
src/plugins/compilerexplorer/compilerexplorertr.h
Normal 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
|
3
src/plugins/compilerexplorer/logos/logos.qrc
Normal file
3
src/plugins/compilerexplorer/logos/logos.qrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/compilerexplorer/logos/"></qresource>
|
||||||
|
</RCC>
|
50
src/plugins/compilerexplorer/test.rest
Normal file
50
src/plugins/compilerexplorer/test.rest
Normal 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
|
@@ -114,7 +114,7 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) :
|
|||||||
m_container->addWidget(empty);
|
m_container->addWidget(empty);
|
||||||
m_widgetEditorMap.insert(empty, nullptr);
|
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)
|
// 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
|
// 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
|
// 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,
|
connect(dropSupport, &DropSupport::filesDropped,
|
||||||
this, &EditorView::openDroppedFiles);
|
this, &EditorView::openDroppedFiles);
|
||||||
|
*/
|
||||||
updateNavigatorActions();
|
updateNavigatorActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -203,12 +203,12 @@ MainWindow::MainWindow()
|
|||||||
|
|
||||||
statusBar()->setProperty("p_styled", true);
|
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)
|
return event->source() == nullptr; // only accept drops from the "outside" (e.g. file manager)
|
||||||
});
|
});
|
||||||
connect(dropSupport, &DropSupport::filesDropped,
|
connect(dropSupport, &DropSupport::filesDropped,
|
||||||
this, &MainWindow::openDroppedFiles);
|
this, &MainWindow::openDroppedFiles);
|
||||||
|
*/
|
||||||
if (HostOsInfo::isLinuxHost()) {
|
if (HostOsInfo::isLinuxHost()) {
|
||||||
m_trimTimer.setSingleShot(true);
|
m_trimTimer.setSingleShot(true);
|
||||||
m_trimTimer.setInterval(60000);
|
m_trimTimer.setInterval(60000);
|
||||||
|
Submodule src/shared/qbs updated: d8c97a5f0b...2e6eb75c76
Reference in New Issue
Block a user