CompilerExplorer: Enable undo/redo

Change-Id: I06bba06181784de07f89f01a3bfe586513d63c66
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Marcus Tillmanns
2023-09-21 09:32:07 +02:00
parent 3c5de27af4
commit 4bdc023e32
6 changed files with 443 additions and 150 deletions

View File

@@ -6,6 +6,7 @@
#include "api/library.h" #include "api/library.h"
#include <utils/algorithm.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <QComboBox> #include <QComboBox>
@@ -58,6 +59,35 @@ bool LibrarySelectionAspect::guiToBuffer()
return oldBuffer != m_buffer; return oldBuffer != m_buffer;
} }
QVariantMap toVariantMap(const QMap<QString, QString> &map)
{
QVariantMap variant;
for (const auto &key : map.keys())
variant.insert(key, map[key]);
return variant;
}
QVariant LibrarySelectionAspect::variantValue() const
{
return toVariantMap(m_internal);
}
QVariant LibrarySelectionAspect::volatileVariantValue() const
{
return toVariantMap(m_buffer);
}
void LibrarySelectionAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce)
{
QMap<QString, QString> map;
QVariantMap variant = value.toMap();
for (const auto &key : variant.keys())
map[key] = variant[key].toString();
setValue(map, howToAnnounce);
}
void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent) void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
{ {
using namespace Layouting; using namespace Layouting;
@@ -110,6 +140,18 @@ void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
connect(nameCombo, &QComboBox::currentIndexChanged, this, refreshVersionCombo); connect(nameCombo, &QComboBox::currentIndexChanged, this, refreshVersionCombo);
connect(versionCombo, &QComboBox::activated, this, [this, nameCombo, versionCombo] { connect(versionCombo, &QComboBox::activated, this, [this, nameCombo, versionCombo] {
if (undoStack()) {
QVariant old = m_model->data(m_model->index(nameCombo->currentIndex(), 0),
SelectedVersion);
undoStack()->push(new SelectLibraryVersionCommand(this,
nameCombo->currentIndex(),
versionCombo->currentData(),
old));
handleGuiChanged();
return;
}
m_model->setData(m_model->index(nameCombo->currentIndex(), 0), m_model->setData(m_model->index(nameCombo->currentIndex(), 0),
versionCombo->currentData(), versionCombo->currentData(),
SelectedVersion); SelectedVersion);
@@ -118,6 +160,23 @@ void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
QPushButton *clearBtn = new QPushButton("Clear All"); QPushButton *clearBtn = new QPushButton("Clear All");
connect(clearBtn, &QPushButton::clicked, clearBtn, [this, refreshVersionCombo] { connect(clearBtn, &QPushButton::clicked, clearBtn, [this, refreshVersionCombo] {
if (undoStack()) {
undoStack()->beginMacro(Tr::tr("Reset used libraries"));
for (int i = 0; i < m_model->rowCount(); i++) {
QModelIndex idx = m_model->index(i, 0);
if (idx.data(SelectedVersion).isValid())
undoStack()->push(new SelectLibraryVersionCommand(this,
i,
QVariant(),
idx.data(SelectedVersion)));
}
undoStack()->endMacro();
handleGuiChanged();
refreshVersionCombo();
return;
}
for (int i = 0; i < m_model->rowCount(); i++) for (int i = 0; i < m_model->rowCount(); i++)
m_model->setData(m_model->index(i, 0), QVariant(), SelectedVersion); m_model->setData(m_model->index(i, 0), QVariant(), SelectedVersion);
handleGuiChanged(); handleGuiChanged();
@@ -130,11 +189,12 @@ void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
QStringList libs; QStringList libs;
for (int i = 0; i < m_model->rowCount(); i++) { for (int i = 0; i < m_model->rowCount(); i++) {
QModelIndex idx = m_model->index(i, 0); QModelIndex idx = m_model->index(i, 0);
if (idx.data(SelectedVersion).isValid()) if (idx.data(SelectedVersion).isValid()) {
libs.append(QString("%1 %2") libs.append(QString("%1 %2")
.arg(idx.data().toString()) .arg(idx.data().toString())
.arg(idx.data(SelectedVersion).toString())); .arg(idx.data(SelectedVersion).toString()));
} }
}
if (libs.empty()) if (libs.empty())
displayLabel->setText(Tr::tr("No libraries selected")); displayLabel->setText(Tr::tr("No libraries selected"));
else else
@@ -157,7 +217,10 @@ void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent)
Row { noMargin, nameCombo, versionCombo, clearBtn }.emerge() Row { noMargin, nameCombo, versionCombo, clearBtn }.emerge()
}.emerge(); }.emerge();
// clang-format on // clang-format on
connect(editBtn, &QPushButton::clicked, this, [stack] { stack->setCurrentIndex(1); }); connect(editBtn, &QPushButton::clicked, stack, [stack] { stack->setCurrentIndex(1); });
connect(this, &LibrarySelectionAspect::returnToDisplay, stack, [stack] {
stack->setCurrentIndex(0);
});
addLabeledItem(parent, s); addLabeledItem(parent, s);
} }

View File

@@ -24,6 +24,48 @@ public:
SelectedVersion, SelectedVersion,
}; };
class SelectLibraryVersionCommand : public QUndoCommand
{
public:
SelectLibraryVersionCommand(LibrarySelectionAspect *aspect,
int libraryIndex,
const QVariant &versionId,
const QVariant &oldVersionId = QVariant())
: m_aspect(aspect)
, m_libraryIndex(libraryIndex)
, m_versionId(versionId)
, m_oldVersionId(oldVersionId)
{}
void undo() override
{
m_aspect->m_model->setData(m_aspect->m_model->index(m_libraryIndex, 0),
m_oldVersionId,
LibrarySelectionAspect::SelectedVersion);
m_aspect->handleGuiChanged();
emit m_aspect->returnToDisplay();
}
void redo() override
{
m_aspect->m_model->setData(m_aspect->m_model->index(m_libraryIndex, 0),
m_versionId,
LibrarySelectionAspect::SelectedVersion);
if (!m_firstTime) {
emit m_aspect->returnToDisplay();
m_aspect->handleGuiChanged();
}
m_firstTime = false;
}
private:
LibrarySelectionAspect *m_aspect;
int m_libraryIndex;
QVariant m_versionId;
QVariant m_oldVersionId;
bool m_firstTime{true};
};
LibrarySelectionAspect(Utils::AspectContainer *container = nullptr); LibrarySelectionAspect(Utils::AspectContainer *container = nullptr);
void addToLayout(Layouting::LayoutItem &parent) override; void addToLayout(Layouting::LayoutItem &parent) override;
@@ -36,8 +78,14 @@ public:
void bufferToGui() override; void bufferToGui() override;
bool guiToBuffer() override; bool guiToBuffer() override;
QVariant variantValue() const override;
QVariant volatileVariantValue() const override;
void setVariantValue(const QVariant &value, Announcement howToAnnounce = DoEmit) override;
signals: signals:
void refillRequested(); void refillRequested();
void returnToDisplay();
private: private:
FillCallback m_fillCallback; FillCallback m_fillCallback;

View File

@@ -9,6 +9,7 @@
#include <aggregation/aggregate.h> #include <aggregation/aggregate.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -25,6 +26,7 @@
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/mimetypes2/mimetype.h> #include <utils/mimetypes2/mimetype.h>
#include <utils/mimeutils.h> #include <utils/mimeutils.h>
#include <utils/store.h>
#include <utils/utilsicons.h> #include <utils/utilsicons.h>
#include <QCompleter> #include <QCompleter>
@@ -37,6 +39,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QTimer> #include <QTimer>
#include <QToolButton> #include <QToolButton>
#include <QUndoStack>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
@@ -48,30 +51,36 @@ using namespace Utils;
namespace CompilerExplorer { namespace CompilerExplorer {
class CodeEditorWidget : public TextEditorWidget CodeEditorWidget::CodeEditorWidget(const std::shared_ptr<SourceSettings> &settings,
{ QUndoStack *undoStack)
public:
CodeEditorWidget(const std::shared_ptr<SourceSettings> &settings)
: m_settings(settings) : m_settings(settings)
{} , m_undoStack(undoStack){};
void updateHighlighter() void CodeEditorWidget::updateHighlighter()
{ {
const QString ext = m_settings->languageExtension(); const QString ext = m_settings->languageExtension();
if (ext.isEmpty()) if (ext.isEmpty())
return; return;
Utils::MimeType mimeType = Utils::mimeTypeForFile("foo" + ext); Utils::MimeType mimeType = Utils::mimeTypeForFile("foo" + ext);
configureGenericHighlighter(mimeType); configureGenericHighlighter(mimeType);
} }
std::shared_ptr<SourceSettings> m_settings;
};
class SourceTextDocument : public TextDocument class SourceTextDocument : public TextDocument
{ {
public: public:
SourceTextDocument(const std::shared_ptr<SourceSettings> &settings) class OpaqueUndoCommand : public QUndoCommand
{
public:
OpaqueUndoCommand(SourceTextDocument *doc)
: m_doc(doc)
{}
void undo() override { m_doc->undo(); }
void redo() override { m_doc->redo(); }
SourceTextDocument *m_doc;
};
SourceTextDocument(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack)
{ {
setPlainText(settings->source()); setPlainText(settings->source());
@@ -83,18 +92,26 @@ public:
if (settings->source.volatileValue() != plainText()) if (settings->source.volatileValue() != plainText())
setPlainText(settings->source.volatileValue()); setPlainText(settings->source.volatileValue());
}); });
connect(this->document(), &QTextDocument::undoCommandAdded, this, [this, undoStack] {
undoStack->push(new OpaqueUndoCommand(this));
});
} }
void undo() { document()->undo(); }
void redo() { document()->redo(); }
}; };
JsonSettingsDocument::JsonSettingsDocument() JsonSettingsDocument::JsonSettingsDocument(QUndoStack *undoStack)
: m_undoStack(undoStack)
{ {
setId(Constants::CE_EDITOR_ID); setId(Constants::CE_EDITOR_ID);
setMimeType("application/compiler-explorer"); setMimeType("application/compiler-explorer");
connect(&m_ceSettings, &CompilerExplorerSettings::changed, this, [this] { emit changed(); }); connect(&m_ceSettings, &CompilerExplorerSettings::changed, this, [this] { emit changed(); });
m_ceSettings.setAutoApply(true);
m_ceSettings.setUndoStack(undoStack);
} }
JsonSettingsDocument::~JsonSettingsDocument() {}
Core::IDocument::OpenResult JsonSettingsDocument::open(QString *errorString, Core::IDocument::OpenResult JsonSettingsDocument::open(QString *errorString,
const FilePath &filePath, const FilePath &filePath,
const FilePath &realFilePath) const FilePath &realFilePath)
@@ -121,23 +138,21 @@ Core::IDocument::OpenResult JsonSettingsDocument::open(QString *errorString,
return OpenResult::Success; return OpenResult::Success;
} }
bool JsonSettingsDocument::saveImpl(QString *errorString, bool JsonSettingsDocument::saveImpl(QString *errorString, const FilePath &newFilePath, bool autoSave)
const FilePath &newFilePath,
bool autoSave)
{ {
Store map; Store store;
if (autoSave) { if (autoSave) {
if (m_windowStateCallback) if (m_windowStateCallback)
m_ceSettings.windowState.setVolatileValue(m_windowStateCallback()); m_ceSettings.windowState.setVolatileValue(m_windowStateCallback());
m_ceSettings.volatileToMap(map); m_ceSettings.volatileToMap(store);
} else { } else {
if (m_windowStateCallback) if (m_windowStateCallback)
m_ceSettings.windowState.setValue(m_windowStateCallback()); m_ceSettings.windowState.setValue(m_windowStateCallback());
m_ceSettings.apply(); m_ceSettings.apply();
m_ceSettings.toMap(map); m_ceSettings.toMap(store);
} }
Utils::FilePath path = newFilePath.isEmpty() ? filePath() : newFilePath; Utils::FilePath path = newFilePath.isEmpty() ? filePath() : newFilePath;
@@ -145,7 +160,7 @@ bool JsonSettingsDocument::saveImpl(QString *errorString,
if (!newFilePath.isEmpty() && !autoSave) if (!newFilePath.isEmpty() && !autoSave)
setFilePath(newFilePath); setFilePath(newFilePath);
auto result = path.writeFileContents(jsonFromStore(map)); auto result = path.writeFileContents(jsonFromStore(store));
if (!result && errorString) { if (!result && errorString) {
*errorString = result.error(); *errorString = result.error();
return false; return false;
@@ -170,12 +185,15 @@ bool JsonSettingsDocument::setContents(const QByteArray &contents)
return true; return true;
} }
SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings) SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings,
QUndoStack *undoStack)
: m_sourceSettings(settings) : m_sourceSettings(settings)
{ {
m_codeEditor = new CodeEditorWidget(m_sourceSettings); m_codeEditor = new CodeEditorWidget(m_sourceSettings, undoStack);
TextDocumentPtr document = TextDocumentPtr(new SourceTextDocument(m_sourceSettings)); connect(m_codeEditor, &CodeEditorWidget::gotFocus, this, &SourceEditorWidget::gotFocus);
TextDocumentPtr document = TextDocumentPtr(new SourceTextDocument(m_sourceSettings, undoStack));
connect(document.get(), connect(document.get(),
&SourceTextDocument::changed, &SourceTextDocument::changed,
@@ -187,11 +205,12 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &se
auto addCompilerButton = new QPushButton; auto addCompilerButton = new QPushButton;
addCompilerButton->setText(Tr::tr("Add compiler")); addCompilerButton->setText(Tr::tr("Add compiler"));
connect(addCompilerButton, &QPushButton::clicked, this, [this] { connect(addCompilerButton, &QPushButton::clicked, this, &SourceEditorWidget::addCompiler);
auto newCompiler = std::make_shared<CompilerSettings>(m_sourceSettings->apiConfigFunction());
newCompiler->setLanguageId(m_sourceSettings->languageId()); auto removeSourceButton = new QPushButton;
m_sourceSettings->compilers.addItem(newCompiler); removeSourceButton->setIcon(Utils::Icons::EDIT_CLEAR.icon());
}); removeSourceButton->setToolTip(Tr::tr("Remove source"));
connect(removeSourceButton, &QPushButton::clicked, this, &SourceEditorWidget::remove);
// clang-format off // clang-format off
using namespace Layouting; using namespace Layouting;
@@ -200,6 +219,7 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &se
Row { Row {
settings->languageId, settings->languageId,
addCompilerButton, addCompilerButton,
removeSourceButton,
}, },
m_codeEditor, m_codeEditor,
}.attachTo(this); }.attachTo(this);
@@ -208,13 +228,6 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &se
setWindowTitle("Source code"); setWindowTitle("Source code");
setObjectName("source_code"); setObjectName("source_code");
Aggregate *agg = Aggregate::parentAggregate(m_codeEditor);
if (!agg) {
agg = new Aggregate;
agg->add(m_codeEditor);
}
agg->add(this);
setFocusProxy(m_codeEditor); setFocusProxy(m_codeEditor);
} }
@@ -243,11 +256,14 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
m_delayTimer, m_delayTimer,
qOverload<>(&QTimer::start)); qOverload<>(&QTimer::start));
m_asmEditor = new TextEditorWidget; m_asmEditor = new AsmEditorWidget;
m_asmDocument = QSharedPointer<TextDocument>(new TextDocument); m_asmDocument = QSharedPointer<TextDocument>(new TextDocument);
m_asmDocument->setFilePath("asm.asm"); m_asmDocument->setFilePath("asm.asm");
m_asmEditor->setTextDocument(m_asmDocument); m_asmEditor->setTextDocument(m_asmDocument);
m_asmEditor->configureGenericHighlighter(Utils::mimeTypeForName("text/x-asm")); m_asmEditor->configureGenericHighlighter(Utils::mimeTypeForName("text/x-asm"));
m_asmEditor->setReadOnly(true);
connect(m_asmEditor, &AsmEditorWidget::gotFocus, this, &CompilerWidget::gotFocus);
auto advButton = new QPushButton; auto advButton = new QPushButton;
QSplitter *splitter{nullptr}; QSplitter *splitter{nullptr};
@@ -268,6 +284,11 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
connect(advButton, &QPushButton::clicked, advDlg, &QAction::trigger); connect(advButton, &QPushButton::clicked, advDlg, &QAction::trigger);
advButton->setIcon(advDlg->icon()); advButton->setIcon(advDlg->icon());
auto removeCompilerBtn = new QPushButton;
removeCompilerBtn->setIcon(Utils::Icons::EDIT_CLEAR.icon());
removeCompilerBtn->setToolTip(Tr::tr("Remove compiler"));
connect(removeCompilerBtn, &QPushButton::clicked, this, &CompilerWidget::remove);
compile(m_sourceSettings->source()); compile(m_sourceSettings->source());
connect(&m_sourceSettings->source, &Utils::StringAspect::volatileValueChanged, this, [this] { connect(&m_sourceSettings->source, &Utils::StringAspect::volatileValueChanged, this, [this] {
@@ -279,6 +300,7 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
Row { Row {
m_compilerSettings->compiler, m_compilerSettings->compiler,
advButton, advButton,
removeCompilerBtn,
}, },
Splitter { Splitter {
bindTo(&splitter), bindTo(&splitter),
@@ -368,8 +390,7 @@ void CompilerWidget::doCompile()
m_compileWatcher.reset(new QFutureWatcher<CompileResult>); m_compileWatcher.reset(new QFutureWatcher<CompileResult>);
connect( connect(m_compileWatcher.get(), &QFutureWatcher<CompileResult>::finished, this, [this] {
m_compileWatcher.get(), &QFutureWatcher<CompileResult>::finished, this, [this] {
m_spinner->setVisible(false); m_spinner->setVisible(false);
m_asmEditor->setEnabled(true); m_asmEditor->setEnabled(true);
@@ -385,8 +406,7 @@ void CompilerWidget::doCompile()
m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false); m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false);
m_resultTerminal->writeToTerminal( m_resultTerminal->writeToTerminal(
QString("ASM generation compiler returned: %1\r\n\r\n").arg(r.code).toUtf8(), QString("ASM generation compiler returned: %1\r\n\r\n").arg(r.code).toUtf8(), true);
true);
if (r.execResult) { if (r.execResult) {
for (const auto &err : r.execResult->buildResult.stdErr) for (const auto &err : r.execResult->buildResult.stdErr)
@@ -408,15 +428,13 @@ void CompilerWidget::doCompile()
for (const auto &err : r.execResult->stdErrLines) for (const auto &err : r.execResult->stdErrLines)
m_resultTerminal m_resultTerminal
->writeToTerminal((" \033[0;31m" + err + "\033[0m\r\n").toUtf8(), ->writeToTerminal((" \033[0;31m" + err + "\033[0m\r\n\r\n").toUtf8(),
false); false);
for (const auto &out : r.execResult->stdOutLines) for (const auto &out : r.execResult->stdOutLines)
m_resultTerminal->writeToTerminal((" " + out + "\r\n").toUtf8(), false); m_resultTerminal->writeToTerminal((out + "\r\n").toUtf8(), false);
} }
} }
for (auto mark : m_marks) { qDeleteAll(m_marks);
delete mark;
}
m_marks.clear(); m_marks.clear();
QString asmText; QString asmText;
@@ -431,9 +449,7 @@ void CompilerWidget::doCompile()
if (l.opcodes.empty()) if (l.opcodes.empty())
continue; continue;
auto mark = new TextMark(m_asmDocument.get(), auto mark = new TextMark(m_asmDocument.get(), i, TextMarkCategory{"Bytes", "Bytes"});
i,
TextMarkCategory{"Bytes", "Bytes"});
mark->setLineAnnotation(l.opcodes.join(' ')); mark->setLineAnnotation(l.opcodes.join(' '));
m_marks.append(mark); m_marks.append(mark);
} }
@@ -445,7 +461,10 @@ void CompilerWidget::doCompile()
m_compileWatcher->setFuture(f); m_compileWatcher->setFuture(f);
} }
EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document, QWidget *parent) EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
QUndoStack *undoStack,
TextEditorActionHandler &actionHandler,
QWidget *parent)
: Utils::FancyMainWindow(parent) : Utils::FancyMainWindow(parent)
, m_document(document) , m_document(document)
{ {
@@ -473,7 +492,8 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
return result; return result;
}); });
auto addCompiler = [this](const std::shared_ptr<SourceSettings> &sourceSettings, auto addCompiler = [this,
&actionHandler](const std::shared_ptr<SourceSettings> &sourceSettings,
const std::shared_ptr<CompilerSettings> &compilerSettings, const std::shared_ptr<CompilerSettings> &compilerSettings,
int idx) { int idx) {
auto compiler = new CompilerWidget(sourceSettings, compilerSettings); auto compiler = new CompilerWidget(sourceSettings, compilerSettings);
@@ -482,23 +502,43 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
QDockWidget *dockWidget = addDockForWidget(compiler); QDockWidget *dockWidget = addDockForWidget(compiler);
addDockWidget(Qt::RightDockWidgetArea, dockWidget); addDockWidget(Qt::RightDockWidgetArea, dockWidget);
m_compilerWidgets.append(dockWidget); m_compilerWidgets.append(dockWidget);
connect(compiler,
&CompilerWidget::remove,
this,
[sourceSettings = sourceSettings.get(), compilerSettings = compilerSettings.get()] {
sourceSettings->compilers.removeItem(compilerSettings->shared_from_this());
});
connect(compiler, &CompilerWidget::gotFocus, this, [&actionHandler] {
actionHandler.updateCurrentEditor();
});
}; };
auto addSourceEditor = [this, document = document.get(), addCompiler]( auto addSourceEditor = [this, &actionHandler, document = document.get(), addCompiler, undoStack](
const std::shared_ptr<SourceSettings> &sourceSettings) { const std::shared_ptr<SourceSettings> &sourceSettings) {
auto sourceEditor = new SourceEditorWidget(sourceSettings); auto sourceEditor = new SourceEditorWidget(sourceSettings, undoStack);
sourceEditor->setWindowTitle("Source Code #" + QString::number(m_sourceWidgets.size() + 1)); sourceEditor->setWindowTitle("Source Code #" + QString::number(m_sourceWidgets.size() + 1));
sourceEditor->setObjectName("source_code_editor_" sourceEditor->setObjectName("source_code_editor_"
+ QString::number(m_sourceWidgets.size() + 1)); + QString::number(m_sourceWidgets.size() + 1));
QDockWidget *dockWidget = addDockForWidget(sourceEditor); QDockWidget *dockWidget = addDockForWidget(sourceEditor);
connect(dockWidget, connect(sourceEditor,
&QDockWidget::visibilityChanged, &SourceEditorWidget::remove,
this, this,
[document, sourceSettings = sourceSettings.get(), dockWidget] { [document, sourceSettings = sourceSettings.get()] {
if (!dockWidget->isVisible()) document->settings()->m_sources.removeItem(sourceSettings->shared_from_this());
document->settings()->m_sources.removeItem( });
sourceSettings->shared_from_this());
connect(sourceEditor, &SourceEditorWidget::addCompiler, this, [sourceSettings] {
auto newCompiler = std::make_shared<CompilerSettings>(
sourceSettings->apiConfigFunction());
newCompiler->setLanguageId(sourceSettings->languageId());
sourceSettings->compilers.addItem(newCompiler);
});
connect(sourceEditor, &SourceEditorWidget::gotFocus, this, [&actionHandler] {
actionHandler.updateCurrentEditor();
}); });
addDockWidget(Qt::LeftDockWidgetArea, dockWidget); addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
@@ -519,13 +559,19 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
sourceSettings->compilers.setItemRemovedCallback<CompilerSettings>( sourceSettings->compilers.setItemRemovedCallback<CompilerSettings>(
[this](const std::shared_ptr<CompilerSettings> &compilerSettings) { [this](const std::shared_ptr<CompilerSettings> &compilerSettings) {
m_compilerWidgets.removeIf([compilerSettings](const QDockWidget *c) { auto it = std::find_if(m_compilerWidgets.begin(),
return static_cast<CompilerWidget *>(c->widget())->m_compilerSettings m_compilerWidgets.end(),
[compilerSettings](const QDockWidget *c) {
return static_cast<CompilerWidget *>(c->widget())
->m_compilerSettings
== compilerSettings; == compilerSettings;
}); });
QTC_ASSERT(it != m_compilerWidgets.end(), return);
delete *it;
m_compilerWidgets.erase(it);
}); });
Aggregate *agg = Aggregate::parentAggregate(sourceEditor); /*Aggregate *agg = Aggregate::parentAggregate(sourceEditor);
if (!agg) { if (!agg) {
agg = new Aggregate; agg = new Aggregate;
agg->add(sourceEditor); agg->add(sourceEditor);
@@ -533,15 +579,21 @@ EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
agg->add(this); agg->add(this);
setFocusProxy(sourceEditor); setFocusProxy(sourceEditor);
*/
m_sourceWidgets.append(dockWidget); m_sourceWidgets.append(dockWidget);
}; };
auto removeSourceEditor = [this](const std::shared_ptr<SourceSettings> &sourceSettings) { auto removeSourceEditor = [this](const std::shared_ptr<SourceSettings> &sourceSettings) {
m_sourceWidgets.removeIf([sourceSettings = sourceSettings.get()](const QDockWidget *c) { auto it = std::find_if(m_sourceWidgets.begin(),
return static_cast<SourceEditorWidget *>(c->widget())->m_sourceSettings m_sourceWidgets.end(),
== sourceSettings->shared_from_this(); [sourceSettings](const QDockWidget *c) {
return static_cast<SourceEditorWidget *>(c->widget())
->sourceSettings()
== sourceSettings.get();
}); });
QTC_ASSERT(it != m_sourceWidgets.end(), return);
delete *it;
m_sourceWidgets.erase(it);
}; };
auto recreateEditors = [this, addSourceEditor]() { auto recreateEditors = [this, addSourceEditor]() {
@@ -593,21 +645,63 @@ EditorWidget::~EditorWidget()
m_sourceWidgets.clear(); m_sourceWidgets.clear();
} }
TextEditor::TextEditorWidget *EditorWidget::focusedEditorWidget() const
{
for (const QDockWidget *sourceWidget : m_sourceWidgets) {
TextEditorWidget *textEditor
= qobject_cast<SourceEditorWidget *>(sourceWidget->widget())->textEditor();
if (textEditor->hasFocus())
return textEditor;
}
for (const QDockWidget *compilerWidget : m_compilerWidgets) {
TextEditorWidget *textEditor
= qobject_cast<CompilerWidget *>(compilerWidget->widget())->textEditor();
if (textEditor->hasFocus())
return textEditor;
}
return nullptr;
}
class Editor : public Core::IEditor class Editor : public Core::IEditor
{ {
public: public:
Editor() Editor(TextEditorActionHandler &actionHandler)
: m_document(new JsonSettingsDocument()) : m_document(new JsonSettingsDocument(&m_undoStack))
{ {
setWidget(new EditorWidget(m_document)); setWidget(new EditorWidget(m_document, &m_undoStack, actionHandler));
connect(&m_undoStack, &QUndoStack::canUndoChanged, this, [&actionHandler] {
actionHandler.updateActions();
});
connect(&m_undoStack, &QUndoStack::canRedoChanged, this, [&actionHandler] {
actionHandler.updateActions();
});
} }
~Editor() { delete widget(); } ~Editor()
{
if (m_document->isModified()) {
auto settings = m_document->settings();
if (settings->isDirty()) {
settings->apply();
Utils::Store store;
settings->toMap(store);
QJsonDocument doc = QJsonDocument::fromVariant(Utils::mapFromStore(store));
CompilerExplorer::settings().defaultDocument.setValue(
QString::fromUtf8(doc.toJson()));
}
}
delete widget();
}
Core::IDocument *document() const override { return m_document.data(); } Core::IDocument *document() const override { return m_document.data(); }
QWidget *toolBar() override { return nullptr; } QWidget *toolBar() override { return nullptr; }
QSharedPointer<JsonSettingsDocument> m_document; QSharedPointer<JsonSettingsDocument> m_document;
QUndoStack m_undoStack;
}; };
EditorFactory::EditorFactory() EditorFactory::EditorFactory()
@@ -615,14 +709,32 @@ EditorFactory::EditorFactory()
Constants::CE_EDITOR_CONTEXT_ID, Constants::CE_EDITOR_CONTEXT_ID,
TextEditor::TextEditorActionHandler::None, TextEditor::TextEditorActionHandler::None,
[](Core::IEditor *editor) -> TextEditorWidget * { [](Core::IEditor *editor) -> TextEditorWidget * {
return Aggregation::query<TextEditorWidget>(editor->widget()); return static_cast<EditorWidget *>(editor->widget())->focusedEditorWidget();
}) })
{ {
setId(Constants::CE_EDITOR_ID); setId(Constants::CE_EDITOR_ID);
setDisplayName(Tr::tr("Compiler Explorer Editor")); setDisplayName(Tr::tr("Compiler Explorer Editor"));
setMimeTypes({"application/compiler-explorer"}); setMimeTypes({"application/compiler-explorer"});
setEditorCreator([]() { return new Editor(); }); auto undoStackFromEditor = [](Core::IEditor *editor) -> QUndoStack * {
if (!editor)
return nullptr;
return &static_cast<Editor *>(editor)->m_undoStack;
};
m_actionHandler.setCanUndoCallback([undoStackFromEditor](Core::IEditor *editor) {
if (auto undoStack = undoStackFromEditor(editor))
return undoStack->canUndo();
return false;
});
m_actionHandler.setCanRedoCallback([undoStackFromEditor](Core::IEditor *editor) {
if (auto undoStack = undoStackFromEditor(editor))
return undoStack->canRedo();
return false;
});
setEditorCreator([this]() { return new Editor(m_actionHandler); });
} }
} // namespace CompilerExplorer } // namespace CompilerExplorer

View File

@@ -18,6 +18,7 @@
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QMainWindow> #include <QMainWindow>
#include <QSplitter> #include <QSplitter>
#include <QUndoStack>
#include <memory> #include <memory>
@@ -29,14 +30,54 @@ namespace CompilerExplorer {
class JsonSettingsDocument; class JsonSettingsDocument;
class SourceEditorWidget; class SourceEditorWidget;
class CodeEditorWidget;
class CodeEditorWidget : public TextEditor::TextEditorWidget
{
Q_OBJECT
public:
CodeEditorWidget(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack);
void updateHighlighter();
void undo() override { m_undoStack->undo(); }
void redo() override { m_undoStack->redo(); }
void focusInEvent(QFocusEvent *event) override
{
TextEditorWidget::focusInEvent(event);
emit gotFocus();
}
signals:
void gotFocus();
private:
std::shared_ptr<SourceSettings> m_settings;
QUndoStack *m_undoStack;
};
class AsmEditorWidget : public TextEditor::TextEditorWidget
{
Q_OBJECT
public:
using TextEditor::TextEditorWidget::TextEditorWidget;
void focusInEvent(QFocusEvent *event) override
{
TextEditorWidget::focusInEvent(event);
emit gotFocus();
}
signals:
void gotFocus();
};
class JsonSettingsDocument : public Core::IDocument class JsonSettingsDocument : public Core::IDocument
{ {
Q_OBJECT Q_OBJECT
public: public:
JsonSettingsDocument(); JsonSettingsDocument(QUndoStack *undoStack);
~JsonSettingsDocument() override;
OpenResult open(QString *errorString, OpenResult open(QString *errorString,
const Utils::FilePath &filePath, const Utils::FilePath &filePath,
@@ -65,22 +106,31 @@ signals:
private: private:
mutable CompilerExplorerSettings m_ceSettings; mutable CompilerExplorerSettings m_ceSettings;
std::function<QVariantMap()> m_windowStateCallback; std::function<QVariantMap()> m_windowStateCallback;
QUndoStack *m_undoStack;
}; };
class SourceEditorWidget : public QWidget class SourceEditorWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings); SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack);
QString sourceCode(); QString sourceCode();
SourceSettings *sourceSettings() { return m_sourceSettings.get(); }
void focusInEvent(QFocusEvent *) override { emit gotFocus(); }
TextEditor::TextEditorWidget *textEditor() { return m_codeEditor; }
std::shared_ptr<SourceSettings> m_sourceSettings;
signals: signals:
void sourceCodeChanged(); void sourceCodeChanged();
void addCompiler();
void remove();
void gotFocus();
private: private:
CodeEditorWidget *m_codeEditor{nullptr}; CodeEditorWidget *m_codeEditor{nullptr};
std::shared_ptr<SourceSettings> m_sourceSettings;
}; };
class CompilerWidget : public QWidget class CompilerWidget : public QWidget
@@ -99,11 +149,19 @@ public:
std::shared_ptr<SourceSettings> m_sourceSettings; std::shared_ptr<SourceSettings> m_sourceSettings;
std::shared_ptr<CompilerSettings> m_compilerSettings; std::shared_ptr<CompilerSettings> m_compilerSettings;
void focusInEvent(QFocusEvent *) override { emit gotFocus(); }
TextEditor::TextEditorWidget *textEditor() { return m_asmEditor; }
private: private:
void doCompile(); void doCompile();
signals:
void remove();
void gotFocus();
private: private:
TextEditor::TextEditorWidget *m_asmEditor{nullptr}; AsmEditorWidget *m_asmEditor{nullptr};
Core::SearchableTerminal *m_resultTerminal{nullptr}; Core::SearchableTerminal *m_resultTerminal{nullptr};
SpinnerSolution::Spinner *m_spinner{nullptr}; SpinnerSolution::Spinner *m_spinner{nullptr};
@@ -120,9 +178,14 @@ class EditorWidget : public Utils::FancyMainWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
EditorWidget(const QSharedPointer<JsonSettingsDocument> &document, QWidget *parent = nullptr); EditorWidget(const QSharedPointer<JsonSettingsDocument> &document,
QUndoStack *undoStack,
TextEditor::TextEditorActionHandler &actionHandler,
QWidget *parent = nullptr);
~EditorWidget() override; ~EditorWidget() override;
TextEditor::TextEditorWidget *focusedEditorWidget() const;
signals: signals:
void sourceCodeChanged(); void sourceCodeChanged();
@@ -144,6 +207,9 @@ public:
private: private:
TextEditor::TextEditorActionHandler m_actionHandler; TextEditor::TextEditorActionHandler m_actionHandler;
QAction m_undoAction;
QAction m_redoAction;
}; };
} // namespace CompilerExplorer } // namespace CompilerExplorer

View File

@@ -87,11 +87,12 @@ SourceSettings::SourceSettings(const ApiConfigFunction &apiConfigFunction)
return result; return result;
}); });
for (const auto &aspect : this->aspects()) for (const auto &aspect : this->aspects()) {
connect(aspect, connect(aspect,
&Utils::BaseAspect::volatileValueChanged, &Utils::BaseAspect::volatileValueChanged,
this, this,
&CompilerExplorerSettings::changed); &Utils::AspectContainer::changed);
}
} }
void SourceSettings::refresh() void SourceSettings::refresh()
@@ -146,11 +147,12 @@ CompilerSettings::CompilerSettings(const ApiConfigFunction &apiConfigFunction)
demangleIdentifiers.setLabelText(Tr::tr("Demangle identifiers")); demangleIdentifiers.setLabelText(Tr::tr("Demangle identifiers"));
demangleIdentifiers.setDefaultValue(true); demangleIdentifiers.setDefaultValue(true);
for (const auto &aspect : this->aspects()) for (const auto &aspect : this->aspects()) {
connect(aspect, connect(aspect,
&Utils::BaseAspect::volatileValueChanged, &Utils::BaseAspect::volatileValueChanged,
this, this,
&CompilerExplorerSettings::changed); &Utils::AspectContainer::changed);
}
} }
void CompilerSettings::refresh() void CompilerSettings::refresh()
@@ -325,11 +327,12 @@ CompilerExplorerSettings::CompilerExplorerSettings()
m_sources.forEachItem<SourceSettings>(&SourceSettings::refresh); m_sources.forEachItem<SourceSettings>(&SourceSettings::refresh);
}); });
for (const auto &aspect : this->aspects()) for (const auto &aspect : this->aspects()) {
connect(aspect, connect(aspect,
&Utils::BaseAspect::volatileValueChanged, &Utils::BaseAspect::volatileValueChanged,
this, this,
&CompilerExplorerSettings::changed); &CompilerExplorerSettings::changed);
}
} }
CompilerExplorerSettings::~CompilerExplorerSettings() = default; CompilerExplorerSettings::~CompilerExplorerSettings() = default;

View File

@@ -77,7 +77,8 @@ private:
ApiConfigFunction m_apiConfigFunction; ApiConfigFunction m_apiConfigFunction;
}; };
class CompilerSettings : public Utils::AspectContainer class CompilerSettings : public Utils::AspectContainer,
public std::enable_shared_from_this<CompilerSettings>
{ {
public: public:
CompilerSettings(const ApiConfigFunction &apiConfigFunction); CompilerSettings(const ApiConfigFunction &apiConfigFunction);