forked from qt-creator/qt-creator
CppEditor: Add Base Class Support for Generate Constructor QuickFix
Change-Id: Idd92229134609c0ac87aad030a6bb645ff4cce1b Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
27
src/libs/3rdparty/cplusplus/Bind.cpp
vendored
27
src/libs/3rdparty/cplusplus/Bind.cpp
vendored
@@ -1253,12 +1253,39 @@ const StringLiteral *Bind::asStringLiteral(const AST *ast)
|
||||
const int firstToken = ast->firstToken();
|
||||
const int lastToken = ast->lastToken();
|
||||
std::string buffer;
|
||||
|
||||
const auto token = tokenAt(ast->firstToken());
|
||||
|
||||
if (token.isCharLiteral()) {
|
||||
if (token.kind() == T_WIDE_CHAR_LITERAL)
|
||||
buffer += 'L';
|
||||
else if (token.kind() == T_UTF16_CHAR_LITERAL)
|
||||
buffer += 'u';
|
||||
else if (token.kind() == T_UTF32_CHAR_LITERAL)
|
||||
buffer += 'U';
|
||||
buffer += '\'';
|
||||
} else if (token.isStringLiteral()) {
|
||||
if (token.kind() == T_WIDE_STRING_LITERAL)
|
||||
buffer += 'L';
|
||||
else if (token.kind() == T_UTF16_STRING_LITERAL)
|
||||
buffer += 'u';
|
||||
else if (token.kind() == T_UTF32_STRING_LITERAL)
|
||||
buffer += 'U';
|
||||
else if (token.kind() == T_UTF8_STRING_LITERAL)
|
||||
buffer += "u8";
|
||||
buffer += '"';
|
||||
}
|
||||
for (int index = firstToken; index != lastToken; ++index) {
|
||||
const Token &tk = tokenAt(index);
|
||||
if (index != firstToken && (tk.whitespace() || tk.newline()))
|
||||
buffer += ' ';
|
||||
buffer += tk.spell();
|
||||
}
|
||||
if (token.isCharLiteral())
|
||||
buffer += '\'';
|
||||
else if (token.isStringLiteral())
|
||||
buffer += '"';
|
||||
|
||||
return control()->stringLiteral(buffer.c_str(), int(buffer.size()));
|
||||
}
|
||||
|
||||
|
1
src/libs/3rdparty/cplusplus/Symbols.h
vendored
1
src/libs/3rdparty/cplusplus/Symbols.h
vendored
@@ -544,6 +544,7 @@ public:
|
||||
int baseClassCount() const;
|
||||
BaseClass *baseClassAt(int index) const;
|
||||
void addBaseClass(BaseClass *baseClass);
|
||||
const std::vector<BaseClass *> &baseClasses() const { return _baseClasses; }
|
||||
|
||||
// Symbol's interface
|
||||
FullySpecifiedType type() const override;
|
||||
|
@@ -7878,6 +7878,75 @@ public:
|
||||
QTest::newRow("default parameters")
|
||||
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||
|
||||
header = R"--(
|
||||
struct Bar{
|
||||
Bar(int i);
|
||||
};
|
||||
class@ Foo : public Bar{
|
||||
int test;
|
||||
public:
|
||||
};
|
||||
)--";
|
||||
expected = R"--(
|
||||
struct Bar{
|
||||
Bar(int i);
|
||||
};
|
||||
class Foo : public Bar{
|
||||
int test;
|
||||
public:
|
||||
Foo(int test, int i) : Bar(i),
|
||||
test(test)
|
||||
{}
|
||||
};
|
||||
)--";
|
||||
QTest::newRow("parent constructor")
|
||||
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||
|
||||
header = R"--(
|
||||
struct Bar{
|
||||
Bar(int use_i = 6);
|
||||
};
|
||||
class@ Foo : public Bar{
|
||||
int test;
|
||||
public:
|
||||
};
|
||||
)--";
|
||||
expected = R"--(
|
||||
struct Bar{
|
||||
Bar(int use_i = 6);
|
||||
};
|
||||
class Foo : public Bar{
|
||||
int test;
|
||||
public:
|
||||
Foo(int test, int use_i = 6) : Bar(use_i),
|
||||
test(test)
|
||||
{}
|
||||
};
|
||||
)--";
|
||||
QTest::newRow("parent constructor with default")
|
||||
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||
|
||||
header = R"--(
|
||||
struct Bar{
|
||||
Bar(int use_i = L'A', int use_i2 = u8"B");
|
||||
};
|
||||
class@ Foo : public Bar{
|
||||
public:
|
||||
};
|
||||
)--";
|
||||
expected = R"--(
|
||||
struct Bar{
|
||||
Bar(int use_i = L'A', int use_i2 = u8"B");
|
||||
};
|
||||
class Foo : public Bar{
|
||||
public:
|
||||
Foo(int use_i = L'A', int use_i2 = u8"B") : Bar(use_i, use_i2)
|
||||
{}
|
||||
};
|
||||
)--";
|
||||
QTest::newRow("parent constructor with char/string default value")
|
||||
<< header << expected << QByteArray() << QByteArray() << Inside;
|
||||
|
||||
const QByteArray common = R"--(
|
||||
namespace N{
|
||||
template<typename T>
|
||||
|
@@ -67,6 +67,9 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include <QAbstractItemModelTester>
|
||||
#endif
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
@@ -86,6 +89,7 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QSharedPointer>
|
||||
#include <QStack>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTableView>
|
||||
#include <QTextCodec>
|
||||
#include <QTextCursor>
|
||||
@@ -8429,6 +8433,8 @@ void RemoveUsingNamespace::match(const CppQuickFixInterface &interface, QuickFix
|
||||
|
||||
namespace {
|
||||
|
||||
struct ParentClassConstructorInfo;
|
||||
|
||||
class ConstructorMemberInfo
|
||||
{
|
||||
public:
|
||||
@@ -8439,7 +8445,20 @@ public:
|
||||
, type(symbol->type())
|
||||
, numberOfMember(numberOfMember)
|
||||
{}
|
||||
|
||||
ConstructorMemberInfo(const QString &memberName,
|
||||
const QString ¶mName,
|
||||
const QString &defaultValue,
|
||||
Symbol *symbol,
|
||||
const ParentClassConstructorInfo *parentClassConstructor)
|
||||
: parentClassConstructor(parentClassConstructor)
|
||||
, memberVariableName(memberName)
|
||||
, parameterName(paramName)
|
||||
, defaultValue(defaultValue)
|
||||
, init(defaultValue.isEmpty())
|
||||
, symbol(symbol)
|
||||
, type(symbol->type())
|
||||
{}
|
||||
const ParentClassConstructorInfo *parentClassConstructor = nullptr;
|
||||
QString memberVariableName;
|
||||
QString parameterName;
|
||||
QString defaultValue;
|
||||
@@ -8449,12 +8468,12 @@ public:
|
||||
FullySpecifiedType type;
|
||||
int numberOfMember; // first member, second member, ...
|
||||
};
|
||||
using ConstructorMemberCandidates = std::vector<ConstructorMemberInfo>;
|
||||
|
||||
class ConstructorParams : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
std::vector<ConstructorMemberInfo *> &infos;
|
||||
std::list<ConstructorMemberInfo> candidates;
|
||||
std::vector<ConstructorMemberInfo *> infos;
|
||||
|
||||
void validateOrder()
|
||||
{
|
||||
@@ -8474,10 +8493,46 @@ class ConstructorParams : public QAbstractTableModel
|
||||
|
||||
public:
|
||||
enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
|
||||
ConstructorParams(QObject *parent, std::vector<ConstructorMemberInfo *> &infos)
|
||||
: QAbstractTableModel(parent)
|
||||
, infos(infos)
|
||||
{}
|
||||
template<typename... _Args>
|
||||
void emplaceBackParameter(_Args &&...__args)
|
||||
{
|
||||
candidates.emplace_back(std::forward<_Args>(__args)...);
|
||||
infos.push_back(&candidates.back());
|
||||
}
|
||||
const std::vector<ConstructorMemberInfo *> &getInfos() const { return infos; }
|
||||
void addRow(ConstructorMemberInfo *info)
|
||||
{
|
||||
beginInsertRows({}, rowCount(), rowCount());
|
||||
infos.push_back(info);
|
||||
endInsertRows();
|
||||
validateOrder();
|
||||
}
|
||||
void removeRow(ConstructorMemberInfo *info)
|
||||
{
|
||||
for (auto iter = infos.begin(); iter != infos.end(); ++iter) {
|
||||
if (*iter == info) {
|
||||
const auto index = iter - infos.begin();
|
||||
beginRemoveRows({}, index, index);
|
||||
infos.erase(iter);
|
||||
endRemoveRows();
|
||||
validateOrder();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int selectedCount() const
|
||||
{
|
||||
return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
|
||||
return mi->init && !mi->parentClassConstructor;
|
||||
});
|
||||
}
|
||||
int memberCount() const
|
||||
{
|
||||
return Utils::count(infos, [](const ConstructorMemberInfo *mi) {
|
||||
return !mi->parentClassConstructor;
|
||||
});
|
||||
}
|
||||
|
||||
int rowCount(const QModelIndex & /*parent*/ = {}) const override { return infos.size(); }
|
||||
int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
|
||||
@@ -8485,7 +8540,8 @@ public:
|
||||
{
|
||||
if (index.row() < 0 || index.row() >= rowCount())
|
||||
return {};
|
||||
if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn)
|
||||
if (role == Qt::CheckStateRole && index.column() == ShouldInitColumn
|
||||
&& !infos[index.row()]->parentClassConstructor)
|
||||
return infos[index.row()]->init ? Qt::Checked : Qt::Unchecked;
|
||||
if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
|
||||
return infos[index.row()]->memberVariableName;
|
||||
@@ -8495,15 +8551,17 @@ public:
|
||||
if ((role == Qt::DisplayRole || role == Qt::EditRole)
|
||||
&& index.column() == DefaultValueColumn)
|
||||
return infos[index.row()]->defaultValue;
|
||||
if ((role == Qt::ToolTipRole) && index.column() == DefaultValueColumn)
|
||||
if ((role == Qt::ToolTipRole) && index.column() > 0)
|
||||
return Overview{}.prettyType(infos[index.row()]->symbol->type());
|
||||
return {};
|
||||
}
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override
|
||||
{
|
||||
if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
|
||||
if (infos[index.row()]->parentClassConstructor)
|
||||
return false;
|
||||
infos[index.row()]->init = value.toInt() == Qt::Checked;
|
||||
emit dataChanged(this->index(index.row(), 1), this->index(index.row(), 2));
|
||||
emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
|
||||
validateOrder();
|
||||
return true;
|
||||
}
|
||||
@@ -8530,7 +8588,7 @@ public:
|
||||
f |= Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
if (index.column() == ShouldInitColumn)
|
||||
if (index.column() == ShouldInitColumn && !infos[index.row()]->parentClassConstructor)
|
||||
return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
||||
if (!infos[index.row()]->init)
|
||||
return f;
|
||||
@@ -8597,14 +8655,14 @@ public:
|
||||
class TableViewStyle : public QProxyStyle
|
||||
{
|
||||
public:
|
||||
TableViewStyle(QStyle *style = 0)
|
||||
TableViewStyle(QStyle *style)
|
||||
: QProxyStyle(style)
|
||||
{}
|
||||
|
||||
void drawPrimitive(PrimitiveElement element,
|
||||
const QStyleOption *option,
|
||||
QPainter *painter,
|
||||
const QWidget *widget = 0) const
|
||||
const QWidget *widget) const override
|
||||
{
|
||||
if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
|
||||
QStyleOption opt(*option);
|
||||
@@ -8621,23 +8679,250 @@ signals:
|
||||
void validOrder(bool valid);
|
||||
};
|
||||
|
||||
class TopMarginDelegate : public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
TopMarginDelegate(QObject *parent = nullptr)
|
||||
: QStyledItemDelegate(parent)
|
||||
{}
|
||||
|
||||
void paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override
|
||||
{
|
||||
Q_ASSERT(index.isValid());
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
const QWidget *widget = option.widget;
|
||||
QStyle *style = widget ? widget->style() : QApplication::style();
|
||||
if (opt.rect.height() > 20)
|
||||
opt.rect.adjust(0, 5, 0, 0);
|
||||
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
|
||||
}
|
||||
};
|
||||
|
||||
struct ParentClassConstructorParameter : public ConstructorMemberInfo
|
||||
{
|
||||
QString originalDefaultValue;
|
||||
QString declaration; // displayed in the treeView
|
||||
ParentClassConstructorParameter(const QString &name,
|
||||
const QString &defaultValue,
|
||||
Symbol *symbol,
|
||||
const ParentClassConstructorInfo *parentClassConstructor);
|
||||
|
||||
ParentClassConstructorParameter(const ParentClassConstructorParameter &) = delete;
|
||||
ParentClassConstructorParameter(ParentClassConstructorParameter &&) = default;
|
||||
};
|
||||
|
||||
struct ParentClassConstructorInfo
|
||||
{
|
||||
ParentClassConstructorInfo(const QString &name, ConstructorParams &model)
|
||||
: className(name)
|
||||
, model(model)
|
||||
{}
|
||||
bool useInConstructor = false;
|
||||
const QString className;
|
||||
QString declaration;
|
||||
std::vector<ParentClassConstructorParameter> parameters;
|
||||
ConstructorParams &model;
|
||||
|
||||
ParentClassConstructorInfo(const ParentClassConstructorInfo &) = delete;
|
||||
ParentClassConstructorInfo(ParentClassConstructorInfo &&) = default;
|
||||
|
||||
void addParameter(ParentClassConstructorParameter ¶m) { model.addRow(¶m); }
|
||||
void removeParameter(ParentClassConstructorParameter ¶m) { model.removeRow(¶m); }
|
||||
void removeAllParameters()
|
||||
{
|
||||
for (auto ¶m : parameters)
|
||||
model.removeRow(¶m);
|
||||
}
|
||||
};
|
||||
|
||||
ParentClassConstructorParameter::ParentClassConstructorParameter(
|
||||
const QString &name,
|
||||
const QString &defaultValue,
|
||||
Symbol *symbol,
|
||||
const ParentClassConstructorInfo *parentClassConstructor)
|
||||
: ConstructorMemberInfo(parentClassConstructor->className + "::" + name,
|
||||
name,
|
||||
defaultValue,
|
||||
symbol,
|
||||
parentClassConstructor)
|
||||
, originalDefaultValue(defaultValue)
|
||||
, declaration(Overview{}.prettyType(symbol->type(), name)
|
||||
+ (defaultValue.isEmpty() ? QString{} : " = " + defaultValue))
|
||||
{}
|
||||
|
||||
using ParentClassConstructors = std::vector<ParentClassConstructorInfo>;
|
||||
|
||||
class ParentClassesModel : public QAbstractItemModel
|
||||
{
|
||||
ParentClassConstructors &constructors;
|
||||
|
||||
public:
|
||||
ParentClassesModel(QObject *parent, ParentClassConstructors &constructors)
|
||||
: QAbstractItemModel(parent)
|
||||
, constructors(constructors)
|
||||
{}
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override
|
||||
{
|
||||
if (!parent.isValid())
|
||||
return createIndex(row, column, nullptr);
|
||||
if (parent.internalPointer())
|
||||
return {};
|
||||
auto index = createIndex(row, column, &constructors.at(parent.row()));
|
||||
return index;
|
||||
}
|
||||
QModelIndex parent(const QModelIndex &index) const override
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
auto *parent = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
|
||||
if (!parent)
|
||||
return {};
|
||||
int i = 0;
|
||||
for (const auto &info : constructors) {
|
||||
if (&info == parent)
|
||||
return createIndex(i, 0, nullptr);
|
||||
++i;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
int rowCount(const QModelIndex &parent = {}) const override
|
||||
{
|
||||
if (!parent.isValid())
|
||||
return static_cast<int>(constructors.size());
|
||||
auto info = static_cast<ParentClassConstructorInfo *>(parent.internalPointer());
|
||||
if (!info)
|
||||
return static_cast<int>(constructors.at(parent.row()).parameters.size());
|
||||
return 0;
|
||||
}
|
||||
int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 1; }
|
||||
QVariant data(const QModelIndex &index, int role) const override
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
|
||||
|
||||
if (info) {
|
||||
const auto ¶meter = info->parameters.at(index.row());
|
||||
if (role == Qt::CheckStateRole)
|
||||
return parameter.init ? Qt::Checked : Qt::Unchecked;
|
||||
if (role == Qt::DisplayRole)
|
||||
return parameter.declaration;
|
||||
return {};
|
||||
}
|
||||
const auto &constructor = constructors.at(index.row());
|
||||
if (role == Qt::CheckStateRole)
|
||||
return constructor.useInConstructor ? Qt::PartiallyChecked : Qt::Unchecked;
|
||||
if (role == Qt::DisplayRole)
|
||||
return constructor.declaration;
|
||||
|
||||
// Highlight the selected items
|
||||
if (role == Qt::FontRole && constructor.useInConstructor) {
|
||||
QFont font = QApplication::font();
|
||||
font.setBold(true);
|
||||
return font;
|
||||
}
|
||||
// Create a margin between sets of constructors for base classes
|
||||
if (role == Qt::SizeHintRole && index.row() > 0
|
||||
&& constructor.className != constructors.at(index.row() - 1).className) {
|
||||
return QSize(-1, 25);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int /*role*/) override
|
||||
{
|
||||
if (index.isValid() && index.column() == 0) {
|
||||
auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
|
||||
if (info) {
|
||||
const bool nowUse = value.toBool();
|
||||
auto ¶m = info->parameters.at(index.row());
|
||||
param.init = nowUse;
|
||||
if (nowUse)
|
||||
info->addParameter(param);
|
||||
else
|
||||
info->removeParameter(param);
|
||||
return true;
|
||||
}
|
||||
auto &newConstructor = constructors.at(index.row());
|
||||
// You have to select a base class constructor
|
||||
if (newConstructor.useInConstructor)
|
||||
return false;
|
||||
auto c = std::find_if(constructors.begin(), constructors.end(), [&](const auto &c) {
|
||||
return c.className == newConstructor.className && c.useInConstructor;
|
||||
});
|
||||
QTC_ASSERT(c == constructors.end(), return false;);
|
||||
c->useInConstructor = false;
|
||||
newConstructor.useInConstructor = true;
|
||||
emit dataChanged(this->index(index.row(), 0), this->index(index.row(), columnCount()));
|
||||
auto parentIndex = this->index(index.row(), 0);
|
||||
emit dataChanged(this->index(0, 0, parentIndex),
|
||||
this->index(rowCount(parentIndex), columnCount()));
|
||||
const int oldIndex = c - constructors.begin();
|
||||
emit dataChanged(this->index(oldIndex, 0), this->index(oldIndex, columnCount()));
|
||||
parentIndex = this->index(oldIndex, 0);
|
||||
emit dataChanged(this->index(0, 0, parentIndex),
|
||||
this->index(rowCount(parentIndex), columnCount()));
|
||||
// update other table
|
||||
c->removeAllParameters();
|
||||
for (auto &p : newConstructor.parameters)
|
||||
if (p.init)
|
||||
newConstructor.addParameter(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Base Class Constructors");
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override
|
||||
{
|
||||
if (index.isValid()) {
|
||||
Qt::ItemFlags f;
|
||||
auto info = static_cast<ParentClassConstructorInfo *>(index.internalPointer());
|
||||
if (!info || info->useInConstructor) {
|
||||
f |= Qt::ItemIsEnabled;
|
||||
}
|
||||
f |= Qt::ItemIsUserCheckable;
|
||||
|
||||
return f;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class GenerateConstructorDialog : public QDialog
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(GenerateConstructorDialog)
|
||||
public:
|
||||
GenerateConstructorDialog(std::vector<ConstructorMemberInfo *> &candidates)
|
||||
: QDialog()
|
||||
GenerateConstructorDialog(ConstructorParams *constructorParamsModel,
|
||||
ParentClassConstructors &constructors)
|
||||
{
|
||||
setWindowTitle(tr("Constructor"));
|
||||
const auto model = new ConstructorParams(this, candidates);
|
||||
|
||||
const auto treeModel = new ParentClassesModel(this, constructors);
|
||||
const auto treeView = new QTreeView(this);
|
||||
treeView->setModel(treeModel);
|
||||
treeView->setItemDelegate(new TopMarginDelegate(this));
|
||||
treeView->expandAll();
|
||||
|
||||
const auto view = new QTableView(this);
|
||||
view->setModel(model);
|
||||
view->setModel(constructorParamsModel);
|
||||
int optimalWidth = 0;
|
||||
for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
|
||||
for (int i = 0; i < constructorParamsModel->columnCount(QModelIndex{}); ++i) {
|
||||
view->resizeColumnToContents(i);
|
||||
optimalWidth += view->columnWidth(i);
|
||||
}
|
||||
view->resizeRowsToContents();
|
||||
view->verticalHeader()->setDefaultSectionSize(view->rowHeight(0));
|
||||
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
view->setDragEnabled(true);
|
||||
@@ -8659,7 +8944,7 @@ public:
|
||||
QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
|
||||
labelSizePolicy.setRetainSizeWhenHidden(true);
|
||||
errorLabel->setSizePolicy(labelSizePolicy);
|
||||
connect(model,
|
||||
connect(constructorParamsModel,
|
||||
&ConstructorParams::validOrder,
|
||||
[=, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
|
||||
button->setEnabled(valid);
|
||||
@@ -8669,7 +8954,7 @@ public:
|
||||
// setup select all/none checkbox
|
||||
QCheckBox *const checkBox = new QCheckBox(tr("Initialize all members"));
|
||||
checkBox->setChecked(true);
|
||||
connect(checkBox, &QCheckBox::stateChanged, [model](int state) {
|
||||
connect(checkBox, &QCheckBox::stateChanged, [model = constructorParamsModel](int state) {
|
||||
if (state != Qt::PartiallyChecked) {
|
||||
for (int i = 0; i < model->rowCount(); ++i)
|
||||
model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
|
||||
@@ -8681,20 +8966,19 @@ public:
|
||||
if (checkBox->checkState() == Qt::PartiallyChecked)
|
||||
checkBox->setCheckState(Qt::Checked);
|
||||
});
|
||||
connect(model, &QAbstractItemModel::dataChanged, this, [&candidates, checkBox] {
|
||||
const auto selectedCount = Utils::count(candidates, [](const ConstructorMemberInfo *mi) {
|
||||
return mi->init;
|
||||
});
|
||||
|
||||
const auto state = [&candidates, selectedCount]() {
|
||||
if (selectedCount == 0)
|
||||
return Qt::Unchecked;
|
||||
if (static_cast<int>(candidates.size()) == selectedCount)
|
||||
return Qt::Checked;
|
||||
return Qt::PartiallyChecked;
|
||||
}();
|
||||
checkBox->setCheckState(state);
|
||||
});
|
||||
connect(constructorParamsModel,
|
||||
&QAbstractItemModel::dataChanged,
|
||||
this,
|
||||
[model = constructorParamsModel, checkBox] {
|
||||
const auto state = [model, selectedCount = model->selectedCount()]() {
|
||||
if (selectedCount == 0)
|
||||
return Qt::Unchecked;
|
||||
if (static_cast<int>(model->memberCount() == selectedCount))
|
||||
return Qt::Checked;
|
||||
return Qt::PartiallyChecked;
|
||||
}();
|
||||
checkBox->setCheckState(state);
|
||||
});
|
||||
|
||||
using A = InsertionPointLocator::AccessSpec;
|
||||
auto accessCombo = new QComboBox;
|
||||
@@ -8716,6 +9000,7 @@ public:
|
||||
mainLayout->addLayout(row);
|
||||
mainLayout->addWidget(checkBox);
|
||||
mainLayout->addWidget(view);
|
||||
mainLayout->addWidget(treeView);
|
||||
mainLayout->addWidget(errorLabel);
|
||||
mainLayout->addWidget(buttonBox);
|
||||
int left, right;
|
||||
@@ -8756,29 +9041,108 @@ public:
|
||||
if (s->isDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
|
||||
const auto name = QString::fromUtf8(s->identifier()->chars(),
|
||||
s->identifier()->size());
|
||||
m_candidates.emplace_back(name, s, memberCounter++);
|
||||
parameterModel.emplaceBackParameter(name, s, memberCounter++);
|
||||
}
|
||||
}
|
||||
Overview o = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
||||
o.showArgumentNames = true;
|
||||
o.showReturnTypes = true;
|
||||
o.showDefaultArguments = true;
|
||||
o.showTemplateParameters = true;
|
||||
o.showFunctionSignatures = true;
|
||||
LookupContext context(currentFile()->cppDocument(), interface.snapshot());
|
||||
for (BaseClass *bc : theClass->baseClasses()) {
|
||||
const QString className = o.prettyName(bc->name());
|
||||
|
||||
ClassOrNamespace *localLookupType = context.lookupType(bc);
|
||||
QList<LookupItem> localLookup = localLookupType->lookup(bc->name());
|
||||
for (auto &li : localLookup) {
|
||||
Symbol *d = li.declaration();
|
||||
if (!d->asClass())
|
||||
continue;
|
||||
for (auto i = d->asClass()->memberBegin(); i != d->asClass()->memberEnd(); ++i) {
|
||||
Symbol *s = *i;
|
||||
if (s->isProtected() || s->isPublic()) {
|
||||
if (s->name()->match(d->name())) {
|
||||
// we have found a constructor
|
||||
Function *func = s->type().type()->asFunctionType();
|
||||
if (!func)
|
||||
continue;
|
||||
const bool isFirst = parentClassConstructors.empty()
|
||||
|| parentClassConstructors.back().className
|
||||
!= className;
|
||||
parentClassConstructors.emplace_back(className, parameterModel);
|
||||
ParentClassConstructorInfo &constructor = parentClassConstructors.back();
|
||||
constructor.declaration = className + o.prettyType(func->type());
|
||||
constructor.declaration.replace("std::__1::__get_nullptr_t()",
|
||||
"nullptr");
|
||||
constructor.useInConstructor = isFirst;
|
||||
for (auto arg = func->memberBegin(); arg != func->memberEnd(); ++arg) {
|
||||
Symbol *param = *arg;
|
||||
Argument *argument = param->asArgument();
|
||||
if (!argument) // can also be a block
|
||||
continue;
|
||||
const QString name = o.prettyName(param->name());
|
||||
const StringLiteral *ini = argument->initializer();
|
||||
QString defaultValue;
|
||||
if (ini)
|
||||
defaultValue = QString::fromUtf8(ini->chars(), ini->size())
|
||||
.replace("std::__1::__get_nullptr_t()",
|
||||
"nullptr");
|
||||
constructor.parameters.emplace_back(name,
|
||||
defaultValue,
|
||||
param,
|
||||
&constructor);
|
||||
// do not show constructors like QObject(QObjectPrivate & dd, ...)
|
||||
ReferenceType *ref = param->type()->asReferenceType();
|
||||
if (ref && name == "dd") {
|
||||
auto type = o.prettyType(ref->elementType());
|
||||
if (type.startsWith("Q") && type.endsWith("Private")) {
|
||||
parentClassConstructors.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add params to parameter lists
|
||||
for (auto &c : parentClassConstructors)
|
||||
if (c.useInConstructor)
|
||||
for (auto &p : c.parameters)
|
||||
if (p.init)
|
||||
c.addParameter(p);
|
||||
}
|
||||
|
||||
bool isApplicable() const { return !m_candidates.empty(); }
|
||||
bool isApplicable() const
|
||||
{
|
||||
return parameterModel.rowCount() > 0
|
||||
|| Utils::anyOf(parentClassConstructors,
|
||||
[](const auto &parent) { return !parent.parameters.empty(); });
|
||||
}
|
||||
|
||||
void setTest(bool isTest = true) { m_test = isTest; }
|
||||
|
||||
private:
|
||||
void perform() override
|
||||
{
|
||||
std::vector<ConstructorMemberInfo *> infos;
|
||||
for (auto &info : m_candidates)
|
||||
infos.push_back(&info);
|
||||
auto infos = parameterModel.getInfos();
|
||||
|
||||
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
|
||||
if (!m_test) {
|
||||
GenerateConstructorDialog dlg(infos);
|
||||
GenerateConstructorDialog dlg(¶meterModel, parentClassConstructors);
|
||||
if (dlg.exec() == QDialog::Rejected)
|
||||
return;
|
||||
accessSpec = dlg.accessSpec();
|
||||
infos = parameterModel.getInfos();
|
||||
} else {
|
||||
#ifdef WITH_TESTS
|
||||
ParentClassesModel model(nullptr, parentClassConstructors);
|
||||
QAbstractItemModelTester tester(&model);
|
||||
#endif
|
||||
if (infos.size() >= 3) {
|
||||
// if we are testing and have 3 or more members => change the order
|
||||
// move first element to the back
|
||||
@@ -8789,6 +9153,16 @@ private:
|
||||
if (info->memberVariableName.startsWith("di_"))
|
||||
info->defaultValue = "42";
|
||||
}
|
||||
for (auto &c : parentClassConstructors) {
|
||||
if (c.useInConstructor) {
|
||||
for (auto &p : c.parameters) {
|
||||
if (!p.init && p.parameterName.startsWith("use_")) {
|
||||
infos.push_back(&p);
|
||||
p.init = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (infos.empty())
|
||||
return;
|
||||
@@ -8805,7 +9179,8 @@ private:
|
||||
, m_classAST(classAST)
|
||||
, m_accessSpec(accessSpec)
|
||||
{}
|
||||
void generateConstructor(std::vector<ConstructorMemberInfo *> members)
|
||||
void generateConstructor(std::vector<ConstructorMemberInfo *> members,
|
||||
const ParentClassConstructors &parentClassConstructors)
|
||||
{
|
||||
auto constructorLocation = m_settings->determineSetterLocation(members.size());
|
||||
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
|
||||
@@ -8865,7 +9240,46 @@ private:
|
||||
}
|
||||
}
|
||||
Utils::sort(members, &ConstructorMemberInfo::numberOfMember);
|
||||
// first, do the base classes
|
||||
for (const auto &parent : parentClassConstructors) {
|
||||
if (!parent.useInConstructor)
|
||||
continue;
|
||||
// Check if we really need a constructor
|
||||
if (Utils::anyOf(parent.parameters, [](const auto ¶m) {
|
||||
return param.init || param.originalDefaultValue.isEmpty();
|
||||
})) {
|
||||
int defaultAtEndCount = 0;
|
||||
for (auto i = parent.parameters.crbegin(); i != parent.parameters.crend();
|
||||
++i) {
|
||||
if (i->init || i->originalDefaultValue.isEmpty())
|
||||
break;
|
||||
++defaultAtEndCount;
|
||||
}
|
||||
const int numberOfParameters = static_cast<int>(parent.parameters.size())
|
||||
- defaultAtEndCount;
|
||||
constructorBody += parent.className + "(";
|
||||
int counter = 0;
|
||||
for (const auto ¶m : parent.parameters) {
|
||||
if (++counter > numberOfParameters)
|
||||
break;
|
||||
if (param.init) {
|
||||
if (param.customValueType)
|
||||
constructorBody += "std::move(" + param.parameterName + ')';
|
||||
else
|
||||
constructorBody += param.parameterName;
|
||||
} else if (!param.originalDefaultValue.isEmpty())
|
||||
constructorBody += param.originalDefaultValue;
|
||||
else
|
||||
constructorBody += "/* insert value */";
|
||||
constructorBody += ", ";
|
||||
}
|
||||
constructorBody.resize(constructorBody.length() - 2);
|
||||
constructorBody += "),\n";
|
||||
}
|
||||
}
|
||||
for (auto &member : members) {
|
||||
if (member->parentClassConstructor)
|
||||
continue;
|
||||
QString param = member->parameterName;
|
||||
if (member->customValueType)
|
||||
param = "std::move(" + member->parameterName + ')';
|
||||
@@ -8909,12 +9323,15 @@ private:
|
||||
m_classAST,
|
||||
accessSpec);
|
||||
|
||||
auto members = Utils::filtered(infos, [](const auto mi) { return mi->init; });
|
||||
helper.generateConstructor(std::move(members));
|
||||
auto members = Utils::filtered(infos, [](const auto mi) {
|
||||
return mi->init || mi->parentClassConstructor;
|
||||
});
|
||||
helper.generateConstructor(std::move(members), parentClassConstructors);
|
||||
helper.applyChanges();
|
||||
}
|
||||
|
||||
ConstructorMemberCandidates m_candidates;
|
||||
ConstructorParams parameterModel;
|
||||
ParentClassConstructors parentClassConstructors;
|
||||
const ClassSpecifierAST *m_classAST = nullptr;
|
||||
bool m_test = false;
|
||||
};
|
||||
|
Reference in New Issue
Block a user