Commit ba60a91d authored by Daniel Mensinger's avatar Daniel Mensinger
Browse files

meson: Added default options rewriter support

parent 6387588d
......@@ -39,6 +39,7 @@ ki18n_wrap_ui(mesonbuilder_SRCS
settings/mesonoptionbaseview.ui
settings/mesonoptionsview.ui
settings/mesonrewriterinput.ui
settings/mesonrewriteroptioncontainer.ui
settings/mesonrewriterpage.ui
)
ecm_qt_declare_logging_category(mesonbuilder_SRCS
......
......@@ -20,6 +20,8 @@
#include "mesonkwargsinfo.h"
#include "debug.h"
#include <QJsonArray>
MesonKWARGSInfo::MesonKWARGSInfo(MesonKWARGSInfo::Function fn, QString id)
: m_func(fn)
, m_id(id)
......@@ -98,6 +100,15 @@ QString MesonKWARGSInfo::getString(QString kwarg) const
return get(kwarg).toString();
}
QStringList MesonKWARGSInfo::getArray(QString kwarg) const
{
QStringList result;
for (auto i : get(kwarg).toArray()) {
result += i.toString();
}
return result;
}
// Constructors
MesonKWARGSProjectInfo::MesonKWARGSProjectInfo()
......
......@@ -51,6 +51,7 @@ public:
bool hasKWARG(QString kwarg) const;
QJsonValue get(QString kwarg) const;
QString getString(QString kwarg) const;
QStringList getArray(QString kwarg) const;
private:
Function m_func;
......
......@@ -18,9 +18,11 @@
*/
#include "mesonrewriterinput.h"
#include "mesonoptionbaseview.h"
#include "rewriter/mesonkwargsinfo.h"
#include "rewriter/mesonkwargsmodify.h"
#include "ui_mesonrewriterinput.h"
#include "ui_mesonrewriteroptioncontainer.h"
#include <KColorScheme>
#include <QLineEdit>
......@@ -173,3 +175,37 @@ QJsonValue MesonRewriterInputString::value()
{
return QJsonValue(m_lineEdit->text());
}
// Options container
MesonRewriterOptionContainer::MesonRewriterOptionContainer(MesonOptViewPtr optView, QWidget* parent)
: QWidget(parent)
, m_optView(optView)
{
m_ui = new Ui::MesonRewriterOptionContainer;
m_ui->setupUi(this);
m_ui->h_layout->insertWidget(0, m_optView.get());
connect(optView.get(), &MesonOptionBaseView::configChanged, this, [this]() { emit configChanged(); });
}
void MesonRewriterOptionContainer::deleteMe()
{
m_markedForDeletion = true;
emit configChanged();
}
bool MesonRewriterOptionContainer::shouldDelete() const
{
return m_markedForDeletion;
}
bool MesonRewriterOptionContainer::hasChanged() const
{
return m_optView->option()->isUpdated();
}
MesonOptViewPtr MesonRewriterOptionContainer::view()
{
return m_optView;
}
......@@ -21,10 +21,17 @@
#include <QJsonValue>
#include <QWidget>
#include <memory>
class MesonOptionBaseView;
class MesonRewriterOptionContainer;
using MesonOptViewPtr = std::shared_ptr<MesonOptionBaseView>;
using MesonOptContainerPtr = std::shared_ptr<MesonRewriterOptionContainer>;
namespace Ui
{
class MesonRewriterInputBase;
class MesonRewriterOptionContainer;
}
class QLineEdit;
......@@ -100,3 +107,27 @@ private:
QString m_initialValue;
QLineEdit* m_lineEdit = nullptr;
};
class MesonRewriterOptionContainer : public QWidget
{
Q_OBJECT
public:
MesonRewriterOptionContainer(MesonOptViewPtr optView, QWidget* parent);
bool shouldDelete() const;
bool hasChanged() const;
MesonOptViewPtr view();
public Q_SLOTS:
void deleteMe();
Q_SIGNALS:
void configChanged();
private:
Ui::MesonRewriterOptionContainer* m_ui = nullptr;
MesonOptViewPtr m_optView = nullptr;
bool m_markedForDeletion = false;
};
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MesonRewriterOptionContainer</class>
<widget class="QWidget" name="MesonRewriterOptionContainer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>32</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="h_layout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="b_delete">
<property name="icon">
<iconset theme="edit-delete"/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>b_delete</sender>
<signal>clicked()</signal>
<receiver>MesonRewriterOptionContainer</receiver>
<slot>deleteMe()</slot>
<hints>
<hint type="sourcelabel">
<x>249</x>
<y>15</y>
</hint>
<hint type="destinationlabel">
<x>249</x>
<y>15</y>
</hint>
</hints>
</connection>
</connections>
<slots>
<slot>deleteMe()</slot>
</slots>
</ui>
......@@ -20,9 +20,11 @@
#include "mesonrewriterpage.h"
#include "mesonconfig.h"
#include "mesonmanager.h"
#include "mesonoptionbaseview.h"
#include "mesonrewriterinput.h"
#include "mintro/mesonintrospectjob.h"
#include "mintro/mesonprojectinfo.h"
#include "rewriter/mesondefaultopts.h"
#include "rewriter/mesonkwargsinfo.h"
#include "rewriter/mesonkwargsmodify.h"
#include "rewriter/mesonrewriterjob.h"
......@@ -35,6 +37,7 @@
#include <KColorScheme>
#include <QIcon>
#include <QInputDialog>
#include <algorithm>
#include <debug.h>
......@@ -70,26 +73,13 @@ MesonRewriterPage::MesonRewriterPage(IPlugin* plugin, IProject* project, QWidget
m_projectKwargs = constructPojectInputs();
// Calculate the maximum name width to align all widgets
vector<int> lengths;
int maxWidth = 50;
lengths.reserve(m_projectKwargs.size());
auto transform_op = [](MesonRewriterInputBase* x) -> int { return x->nameWidth(); };
transform(begin(m_projectKwargs), end(m_projectKwargs), back_inserter(lengths), transform_op);
maxWidth = accumulate(begin(lengths), end(lengths), maxWidth, [](int a, int b) -> int { return max(a, b); });
// Initialize widgets
for (auto* i : m_projectKwargs) {
m_ui->c_project->addWidget(i);
i->setMinNameWidth(maxWidth);
connect(i, &MesonRewriterInputBase::configChanged, this, &MesonRewriterPage::emitChanged);
}
m_ui->l_dispProject->setMinimumWidth(maxWidth);
recalculateLengths();
reset();
}
......@@ -105,11 +95,70 @@ QVector<MesonRewriterInputBase*> MesonRewriterPage::constructPojectInputs()
};
}
MesonOptContainerPtr MesonRewriterPage::constructDefaultOpt(QString name, QString value)
{
if (!m_opts) {
return nullptr;
}
for (auto& i : m_opts->options()) {
if (i->name() != name) {
continue;
}
if (!value.isNull()) {
i->setFromString(value);
}
auto optView = MesonOptionBaseView::fromOption(i, this);
if (!optView) {
continue;
}
auto opt = std::make_shared<MesonRewriterOptionContainer>(optView, this);
if (!opt) {
continue;
}
connect(opt.get(), &MesonRewriterOptionContainer::configChanged, this, &MesonRewriterPage::emitChanged);
return opt;
}
return nullptr;
}
void MesonRewriterPage::setWidgetsDisabled(bool disabled)
{
m_ui->c_tabs->setDisabled(disabled);
}
void MesonRewriterPage::recalculateLengths()
{
// Calculate the maximum name width to align all widgets
vector<int> lengths;
int maxWidth = 50;
lengths.reserve(m_projectKwargs.size() + m_defaultOpts.size());
auto input_op = [](MesonRewriterInputBase* x) -> int { return x->nameWidth(); };
auto optCont_op = [](MesonOptContainerPtr x) -> int { return x->view()->nameWidth(); };
transform(begin(m_projectKwargs), end(m_projectKwargs), back_inserter(lengths), input_op);
transform(begin(m_defaultOpts), end(m_defaultOpts), back_inserter(lengths), optCont_op);
maxWidth = accumulate(begin(lengths), end(lengths), maxWidth, [](int a, int b) -> int { return max(a, b); });
// Set widgets width
for (auto* i : m_projectKwargs) {
i->setMinNameWidth(maxWidth);
}
for (auto& i : m_defaultOpts) {
i->view()->setMinNameWidth(maxWidth);
}
m_ui->l_dispProject->setMinimumWidth(maxWidth);
}
void MesonRewriterPage::checkStatus()
{
// Get the config build dir status
......@@ -156,11 +205,16 @@ void MesonRewriterPage::checkStatus()
break;
}
// Remove old default options
m_defaultOpts.erase(remove_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->shouldDelete(); }),
end(m_defaultOpts));
KColorScheme scheme(QPalette::Normal);
KColorScheme::ForegroundRole role;
int numChanged = 0;
numChanged += count_if(begin(m_projectKwargs), end(m_projectKwargs), [](auto* x) { return x->hasChanged(); });
numChanged += count_if(begin(m_defaultOpts), end(m_defaultOpts), [](auto x) { return x->hasChanged(); });
if (numChanged == 0) {
role = KColorScheme::NormalText;
......@@ -187,6 +241,8 @@ void MesonRewriterPage::apply()
auto projectSet = make_shared<MesonKWARGSProjectModify>(MesonKWARGSProjectModify::SET);
auto projectDel = make_shared<MesonKWARGSProjectModify>(MesonKWARGSProjectModify::DELETE);
auto defOptsSet = make_shared<MesonRewriterDefaultOpts>(MesonRewriterDefaultOpts::SET);
auto defOptsDel = make_shared<MesonRewriterDefaultOpts>(MesonRewriterDefaultOpts::DELETE);
auto writer = [](MesonRewriterInputBase* widget, MesonKWARGSModifyPtr set, MesonKWARGSModifyPtr del) {
if (!widget->hasChanged()) {
......@@ -202,7 +258,24 @@ void MesonRewriterPage::apply()
for_each(begin(m_projectKwargs), end(m_projectKwargs), [&](auto* w) { writer(w, projectSet, projectDel); });
QVector<MesonRewriterActionPtr> actions = { projectSet, projectDel };
QStringList deletedOptions = m_initialDefaultOpts;
for (auto &i : m_defaultOpts) {
auto opt = i->view()->option();
// Detect deleted options by removing all current present options from the initial option list
deletedOptions.removeAll(opt->name());
if (opt->isUpdated() || !m_initialDefaultOpts.contains(opt->name())) {
defOptsSet->set(opt->name(), opt->value());
}
}
for (auto i : deletedOptions) {
defOptsDel->set(i, QString());
}
QVector<MesonRewriterActionPtr> actions = { projectSet, projectDel, defOptsSet, defOptsDel };
KJob* rewriterJob = new MesonRewriterJob(m_project, actions, this);
......@@ -227,7 +300,7 @@ void MesonRewriterPage::reset()
QVector<MesonRewriterActionPtr> actions = { projectInfo };
QVector<MesonIntrospectJob::Type> types = { MesonIntrospectJob::PROJECTINFO };
QVector<MesonIntrospectJob::Type> types = { MesonIntrospectJob::PROJECTINFO, MesonIntrospectJob::BUILDOPTIONS };
MesonIntrospectJob::Mode mode = MesonIntrospectJob::MESON_FILE;
auto introspectJob = new MesonIntrospectJob(m_project, buildDir, types, mode, this);
......@@ -246,7 +319,8 @@ void MesonRewriterPage::reset()
JobDeleter deleter(jobs); // Make sure to free all jobs with RAII
auto prInfo = introspectJob->projectInfo();
if (!prInfo) {
m_opts = introspectJob->buildOptions();
if (!prInfo || !m_opts) {
setStatus(ERROR);
return;
}
......@@ -258,6 +332,34 @@ void MesonRewriterPage::reset()
for_each(begin(m_projectKwargs), end(m_projectKwargs), [=](auto* x) { setter(x, projectInfo); });
// Updated the default options
m_defaultOpts.clear();
m_initialDefaultOpts.clear();
if (projectInfo->hasKWARG(QStringLiteral("default_options"))) {
auto rawValues = projectInfo->getArray(QStringLiteral("default_options"));
auto options = m_opts->options();
for (auto i : rawValues) {
int idx = i.indexOf(QChar::fromLatin1('='));
if (idx < 0) {
continue;
}
QString name = i.left(idx);
QString val = i.mid(idx + 1);
auto opt = constructDefaultOpt(name, val);
if (!opt) {
continue;
}
m_defaultOpts += opt;
m_initialDefaultOpts += name;
m_ui->c_defOpts->addWidget(opt.get());
}
}
recalculateLengths();
setStatus(READY);
return;
});
......@@ -266,6 +368,72 @@ void MesonRewriterPage::reset()
job->start();
}
void MesonRewriterPage::newOption()
{
// Sort by section
QStringList core;
QStringList backend;
QStringList base;
QStringList compiler;
QStringList directory;
QStringList user;
QStringList test;
for (auto& i : m_opts->options()) {
switch (i->section()) {
case MesonOptionBase::CORE:
core += i->name();
break;
case MesonOptionBase::BACKEND:
backend += i->name();
break;
case MesonOptionBase::BASE:
base += i->name();
break;
case MesonOptionBase::COMPILER:
compiler += i->name();
break;
case MesonOptionBase::DIRECTORY:
directory += i->name();
break;
case MesonOptionBase::USER:
user += i->name();
break;
case MesonOptionBase::TEST:
test += i->name();
break;
}
}
QStringList total = core + backend + base + compiler + directory + user + test;
// Remove already existing options
for (auto& i : m_defaultOpts) {
total.removeAll(i->view()->option()->name());
}
QInputDialog dialog(this);
dialog.setOption(QInputDialog::UseListViewForComboBoxItems, true);
dialog.setInputMode(QInputDialog::TextInput);
dialog.setWindowTitle(i18n("Select meson option to add"));
dialog.setLabelText(i18n("Select one new meson option to add"));
dialog.setComboBoxItems(total);
if (dialog.exec() != QDialog::Accepted) {
return;
}
auto opt = constructDefaultOpt(dialog.textValue(), QString());
if (!opt) {
return;
}
m_defaultOpts += opt;
m_ui->c_defOpts->addWidget(opt.get());
recalculateLengths();
}
void MesonRewriterPage::defaults()
{
reset();
......
......@@ -23,6 +23,9 @@
#include <QVector>
#include <interfaces/configpage.h>
#include "mintro/mesonoptions.h"
#include "mesonrewriterinput.h"
namespace KDevelop
{
class IPlugin;
......@@ -34,8 +37,6 @@ namespace Ui
class MesonRewriterPage;
}
class MesonRewriterInputBase;
class MesonRewriterPage : public KDevelop::ConfigPage
{
Q_OBJECT
......@@ -55,19 +56,26 @@ public Q_SLOTS:
void reset() override;
void emitChanged();
QVector<MesonRewriterInputBase*> constructPojectInputs();
void recalculateLengths();
void newOption();
private:
void setWidgetsDisabled(bool disabled);
void checkStatus();
void setStatus(State s);
QVector<MesonRewriterInputBase*> constructPojectInputs();
MesonOptContainerPtr constructDefaultOpt(QString name, QString value);
private:
KDevelop::IProject* m_project = nullptr;
Ui::MesonRewriterPage* m_ui = nullptr;
bool m_configChanged = false;
State m_state = START;
MesonOptsPtr m_opts = nullptr;
QVector<MesonRewriterInputBase*> m_projectKwargs;
QVector<MesonOptContainerPtr> m_defaultOpts;
QStringList m_initialDefaultOpts;
};
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<height>543</height>
<width>977</width>
<height>657</height>
</rect>
</property>
<property name="windowTitle">
......@@ -26,90 +26,142 @@
<attribute name="toolTip">
<string>Project settings</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QVBoxLayout" name="c_projectRoot">
<item>
<widget class="QLabel" name="l_dispProject">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
<layout class="QHBoxLayout" name="c_projectName">
<item>
<widget class="QLabel" name="l_dispProject">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="l_project">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;h3&gt;projectName&lt;/h3&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>