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 firstToken = ast->firstToken();
|
||||||
const int lastToken = ast->lastToken();
|
const int lastToken = ast->lastToken();
|
||||||
std::string buffer;
|
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) {
|
for (int index = firstToken; index != lastToken; ++index) {
|
||||||
const Token &tk = tokenAt(index);
|
const Token &tk = tokenAt(index);
|
||||||
if (index != firstToken && (tk.whitespace() || tk.newline()))
|
if (index != firstToken && (tk.whitespace() || tk.newline()))
|
||||||
buffer += ' ';
|
buffer += ' ';
|
||||||
buffer += tk.spell();
|
buffer += tk.spell();
|
||||||
}
|
}
|
||||||
|
if (token.isCharLiteral())
|
||||||
|
buffer += '\'';
|
||||||
|
else if (token.isStringLiteral())
|
||||||
|
buffer += '"';
|
||||||
|
|
||||||
return control()->stringLiteral(buffer.c_str(), int(buffer.size()));
|
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;
|
int baseClassCount() const;
|
||||||
BaseClass *baseClassAt(int index) const;
|
BaseClass *baseClassAt(int index) const;
|
||||||
void addBaseClass(BaseClass *baseClass);
|
void addBaseClass(BaseClass *baseClass);
|
||||||
|
const std::vector<BaseClass *> &baseClasses() const { return _baseClasses; }
|
||||||
|
|
||||||
// Symbol's interface
|
// Symbol's interface
|
||||||
FullySpecifiedType type() const override;
|
FullySpecifiedType type() const override;
|
||||||
|
@@ -7878,6 +7878,75 @@ public:
|
|||||||
QTest::newRow("default parameters")
|
QTest::newRow("default parameters")
|
||||||
<< header << expected << QByteArray() << QByteArray() << Inside;
|
<< 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"--(
|
const QByteArray common = R"--(
|
||||||
namespace N{
|
namespace N{
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@@ -67,6 +67,9 @@
|
|||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/treemodel.h>
|
#include <utils/treemodel.h>
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
#include <QAbstractItemModelTester>
|
||||||
|
#endif
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
@@ -86,6 +89,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QStack>
|
#include <QStack>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
@@ -8429,6 +8433,8 @@ void RemoveUsingNamespace::match(const CppQuickFixInterface &interface, QuickFix
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
struct ParentClassConstructorInfo;
|
||||||
|
|
||||||
class ConstructorMemberInfo
|
class ConstructorMemberInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -8439,7 +8445,20 @@ public:
|
|||||||
, type(symbol->type())
|
, type(symbol->type())
|
||||||
, numberOfMember(numberOfMember)
|
, 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 memberVariableName;
|
||||||
QString parameterName;
|
QString parameterName;
|
||||||
QString defaultValue;
|
QString defaultValue;
|
||||||
@@ -8449,12 +8468,12 @@ public:
|
|||||||
FullySpecifiedType type;
|
FullySpecifiedType type;
|
||||||
int numberOfMember; // first member, second member, ...
|
int numberOfMember; // first member, second member, ...
|
||||||
};
|
};
|
||||||
using ConstructorMemberCandidates = std::vector<ConstructorMemberInfo>;
|
|
||||||
|
|
||||||
class ConstructorParams : public QAbstractTableModel
|
class ConstructorParams : public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
std::vector<ConstructorMemberInfo *> &infos;
|
std::list<ConstructorMemberInfo> candidates;
|
||||||
|
std::vector<ConstructorMemberInfo *> infos;
|
||||||
|
|
||||||
void validateOrder()
|
void validateOrder()
|
||||||
{
|
{
|
||||||
@@ -8474,10 +8493,46 @@ class ConstructorParams : public QAbstractTableModel
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
|
enum Column { ShouldInitColumn, MemberNameColumn, ParameterNameColumn, DefaultValueColumn };
|
||||||
ConstructorParams(QObject *parent, std::vector<ConstructorMemberInfo *> &infos)
|
template<typename... _Args>
|
||||||
: QAbstractTableModel(parent)
|
void emplaceBackParameter(_Args &&...__args)
|
||||||
, infos(infos)
|
{
|
||||||
{}
|
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 rowCount(const QModelIndex & /*parent*/ = {}) const override { return infos.size(); }
|
||||||
int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
|
int columnCount(const QModelIndex & /*parent*/ = {}) const override { return 4; }
|
||||||
@@ -8485,7 +8540,8 @@ public:
|
|||||||
{
|
{
|
||||||
if (index.row() < 0 || index.row() >= rowCount())
|
if (index.row() < 0 || index.row() >= rowCount())
|
||||||
return {};
|
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;
|
return infos[index.row()]->init ? Qt::Checked : Qt::Unchecked;
|
||||||
if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
|
if (role == Qt::DisplayRole && index.column() == MemberNameColumn)
|
||||||
return infos[index.row()]->memberVariableName;
|
return infos[index.row()]->memberVariableName;
|
||||||
@@ -8495,15 +8551,17 @@ public:
|
|||||||
if ((role == Qt::DisplayRole || role == Qt::EditRole)
|
if ((role == Qt::DisplayRole || role == Qt::EditRole)
|
||||||
&& index.column() == DefaultValueColumn)
|
&& index.column() == DefaultValueColumn)
|
||||||
return infos[index.row()]->defaultValue;
|
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 Overview{}.prettyType(infos[index.row()]->symbol->type());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override
|
||||||
{
|
{
|
||||||
if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
|
if (index.column() == ShouldInitColumn && role == Qt::CheckStateRole) {
|
||||||
|
if (infos[index.row()]->parentClassConstructor)
|
||||||
|
return false;
|
||||||
infos[index.row()]->init = value.toInt() == Qt::Checked;
|
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();
|
validateOrder();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -8530,7 +8588,7 @@ public:
|
|||||||
f |= Qt::ItemIsSelectable;
|
f |= Qt::ItemIsSelectable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index.column() == ShouldInitColumn)
|
if (index.column() == ShouldInitColumn && !infos[index.row()]->parentClassConstructor)
|
||||||
return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
return f | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
|
||||||
if (!infos[index.row()]->init)
|
if (!infos[index.row()]->init)
|
||||||
return f;
|
return f;
|
||||||
@@ -8597,14 +8655,14 @@ public:
|
|||||||
class TableViewStyle : public QProxyStyle
|
class TableViewStyle : public QProxyStyle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TableViewStyle(QStyle *style = 0)
|
TableViewStyle(QStyle *style)
|
||||||
: QProxyStyle(style)
|
: QProxyStyle(style)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void drawPrimitive(PrimitiveElement element,
|
void drawPrimitive(PrimitiveElement element,
|
||||||
const QStyleOption *option,
|
const QStyleOption *option,
|
||||||
QPainter *painter,
|
QPainter *painter,
|
||||||
const QWidget *widget = 0) const
|
const QWidget *widget) const override
|
||||||
{
|
{
|
||||||
if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
|
if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
|
||||||
QStyleOption opt(*option);
|
QStyleOption opt(*option);
|
||||||
@@ -8621,23 +8679,250 @@ signals:
|
|||||||
void validOrder(bool valid);
|
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
|
class GenerateConstructorDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS(GenerateConstructorDialog)
|
Q_DECLARE_TR_FUNCTIONS(GenerateConstructorDialog)
|
||||||
public:
|
public:
|
||||||
GenerateConstructorDialog(std::vector<ConstructorMemberInfo *> &candidates)
|
GenerateConstructorDialog(ConstructorParams *constructorParamsModel,
|
||||||
: QDialog()
|
ParentClassConstructors &constructors)
|
||||||
{
|
{
|
||||||
setWindowTitle(tr("Constructor"));
|
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);
|
const auto view = new QTableView(this);
|
||||||
view->setModel(model);
|
view->setModel(constructorParamsModel);
|
||||||
int optimalWidth = 0;
|
int optimalWidth = 0;
|
||||||
for (int i = 0; i < model->columnCount(QModelIndex{}); ++i) {
|
for (int i = 0; i < constructorParamsModel->columnCount(QModelIndex{}); ++i) {
|
||||||
view->resizeColumnToContents(i);
|
view->resizeColumnToContents(i);
|
||||||
optimalWidth += view->columnWidth(i);
|
optimalWidth += view->columnWidth(i);
|
||||||
}
|
}
|
||||||
view->resizeRowsToContents();
|
view->resizeRowsToContents();
|
||||||
|
view->verticalHeader()->setDefaultSectionSize(view->rowHeight(0));
|
||||||
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
view->setDragEnabled(true);
|
view->setDragEnabled(true);
|
||||||
@@ -8659,7 +8944,7 @@ public:
|
|||||||
QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
|
QSizePolicy labelSizePolicy = errorLabel->sizePolicy();
|
||||||
labelSizePolicy.setRetainSizeWhenHidden(true);
|
labelSizePolicy.setRetainSizeWhenHidden(true);
|
||||||
errorLabel->setSizePolicy(labelSizePolicy);
|
errorLabel->setSizePolicy(labelSizePolicy);
|
||||||
connect(model,
|
connect(constructorParamsModel,
|
||||||
&ConstructorParams::validOrder,
|
&ConstructorParams::validOrder,
|
||||||
[=, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
|
[=, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) {
|
||||||
button->setEnabled(valid);
|
button->setEnabled(valid);
|
||||||
@@ -8669,7 +8954,7 @@ public:
|
|||||||
// setup select all/none checkbox
|
// setup select all/none checkbox
|
||||||
QCheckBox *const checkBox = new QCheckBox(tr("Initialize all members"));
|
QCheckBox *const checkBox = new QCheckBox(tr("Initialize all members"));
|
||||||
checkBox->setChecked(true);
|
checkBox->setChecked(true);
|
||||||
connect(checkBox, &QCheckBox::stateChanged, [model](int state) {
|
connect(checkBox, &QCheckBox::stateChanged, [model = constructorParamsModel](int state) {
|
||||||
if (state != Qt::PartiallyChecked) {
|
if (state != Qt::PartiallyChecked) {
|
||||||
for (int i = 0; i < model->rowCount(); ++i)
|
for (int i = 0; i < model->rowCount(); ++i)
|
||||||
model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
|
model->setData(model->index(i, ConstructorParams::ShouldInitColumn),
|
||||||
@@ -8681,20 +8966,19 @@ public:
|
|||||||
if (checkBox->checkState() == Qt::PartiallyChecked)
|
if (checkBox->checkState() == Qt::PartiallyChecked)
|
||||||
checkBox->setCheckState(Qt::Checked);
|
checkBox->setCheckState(Qt::Checked);
|
||||||
});
|
});
|
||||||
connect(model, &QAbstractItemModel::dataChanged, this, [&candidates, checkBox] {
|
connect(constructorParamsModel,
|
||||||
const auto selectedCount = Utils::count(candidates, [](const ConstructorMemberInfo *mi) {
|
&QAbstractItemModel::dataChanged,
|
||||||
return mi->init;
|
this,
|
||||||
});
|
[model = constructorParamsModel, checkBox] {
|
||||||
|
const auto state = [model, selectedCount = model->selectedCount()]() {
|
||||||
const auto state = [&candidates, selectedCount]() {
|
if (selectedCount == 0)
|
||||||
if (selectedCount == 0)
|
return Qt::Unchecked;
|
||||||
return Qt::Unchecked;
|
if (static_cast<int>(model->memberCount() == selectedCount))
|
||||||
if (static_cast<int>(candidates.size()) == selectedCount)
|
return Qt::Checked;
|
||||||
return Qt::Checked;
|
return Qt::PartiallyChecked;
|
||||||
return Qt::PartiallyChecked;
|
}();
|
||||||
}();
|
checkBox->setCheckState(state);
|
||||||
checkBox->setCheckState(state);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
using A = InsertionPointLocator::AccessSpec;
|
using A = InsertionPointLocator::AccessSpec;
|
||||||
auto accessCombo = new QComboBox;
|
auto accessCombo = new QComboBox;
|
||||||
@@ -8716,6 +9000,7 @@ public:
|
|||||||
mainLayout->addLayout(row);
|
mainLayout->addLayout(row);
|
||||||
mainLayout->addWidget(checkBox);
|
mainLayout->addWidget(checkBox);
|
||||||
mainLayout->addWidget(view);
|
mainLayout->addWidget(view);
|
||||||
|
mainLayout->addWidget(treeView);
|
||||||
mainLayout->addWidget(errorLabel);
|
mainLayout->addWidget(errorLabel);
|
||||||
mainLayout->addWidget(buttonBox);
|
mainLayout->addWidget(buttonBox);
|
||||||
int left, right;
|
int left, right;
|
||||||
@@ -8756,29 +9041,108 @@ public:
|
|||||||
if (s->isDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
|
if (s->isDeclaration() && (s->isPrivate() || s->isProtected()) && !s->isStatic()) {
|
||||||
const auto name = QString::fromUtf8(s->identifier()->chars(),
|
const auto name = QString::fromUtf8(s->identifier()->chars(),
|
||||||
s->identifier()->size());
|
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; }
|
void setTest(bool isTest = true) { m_test = isTest; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void perform() override
|
void perform() override
|
||||||
{
|
{
|
||||||
std::vector<ConstructorMemberInfo *> infos;
|
auto infos = parameterModel.getInfos();
|
||||||
for (auto &info : m_candidates)
|
|
||||||
infos.push_back(&info);
|
|
||||||
|
|
||||||
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
|
InsertionPointLocator::AccessSpec accessSpec = InsertionPointLocator::Public;
|
||||||
if (!m_test) {
|
if (!m_test) {
|
||||||
GenerateConstructorDialog dlg(infos);
|
GenerateConstructorDialog dlg(¶meterModel, parentClassConstructors);
|
||||||
if (dlg.exec() == QDialog::Rejected)
|
if (dlg.exec() == QDialog::Rejected)
|
||||||
return;
|
return;
|
||||||
accessSpec = dlg.accessSpec();
|
accessSpec = dlg.accessSpec();
|
||||||
|
infos = parameterModel.getInfos();
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
ParentClassesModel model(nullptr, parentClassConstructors);
|
||||||
|
QAbstractItemModelTester tester(&model);
|
||||||
|
#endif
|
||||||
if (infos.size() >= 3) {
|
if (infos.size() >= 3) {
|
||||||
// if we are testing and have 3 or more members => change the order
|
// if we are testing and have 3 or more members => change the order
|
||||||
// move first element to the back
|
// move first element to the back
|
||||||
@@ -8789,6 +9153,16 @@ private:
|
|||||||
if (info->memberVariableName.startsWith("di_"))
|
if (info->memberVariableName.startsWith("di_"))
|
||||||
info->defaultValue = "42";
|
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())
|
if (infos.empty())
|
||||||
return;
|
return;
|
||||||
@@ -8805,7 +9179,8 @@ private:
|
|||||||
, m_classAST(classAST)
|
, m_classAST(classAST)
|
||||||
, m_accessSpec(accessSpec)
|
, 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());
|
auto constructorLocation = m_settings->determineSetterLocation(members.size());
|
||||||
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
|
if (constructorLocation == CppQuickFixSettings::FunctionLocation::CppFile
|
||||||
@@ -8865,7 +9240,46 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Utils::sort(members, &ConstructorMemberInfo::numberOfMember);
|
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) {
|
for (auto &member : members) {
|
||||||
|
if (member->parentClassConstructor)
|
||||||
|
continue;
|
||||||
QString param = member->parameterName;
|
QString param = member->parameterName;
|
||||||
if (member->customValueType)
|
if (member->customValueType)
|
||||||
param = "std::move(" + member->parameterName + ')';
|
param = "std::move(" + member->parameterName + ')';
|
||||||
@@ -8909,12 +9323,15 @@ private:
|
|||||||
m_classAST,
|
m_classAST,
|
||||||
accessSpec);
|
accessSpec);
|
||||||
|
|
||||||
auto members = Utils::filtered(infos, [](const auto mi) { return mi->init; });
|
auto members = Utils::filtered(infos, [](const auto mi) {
|
||||||
helper.generateConstructor(std::move(members));
|
return mi->init || mi->parentClassConstructor;
|
||||||
|
});
|
||||||
|
helper.generateConstructor(std::move(members), parentClassConstructors);
|
||||||
helper.applyChanges();
|
helper.applyChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructorMemberCandidates m_candidates;
|
ConstructorParams parameterModel;
|
||||||
|
ParentClassConstructors parentClassConstructors;
|
||||||
const ClassSpecifierAST *m_classAST = nullptr;
|
const ClassSpecifierAST *m_classAST = nullptr;
|
||||||
bool m_test = false;
|
bool m_test = false;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user