forked from qt-creator/qt-creator
CppEditor: Move "insert definition" quickfixes into dedicated files
Change-Id: Ib314f43dd44d34ab1e2d9a867e95de2261e6c86a Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -107,6 +107,7 @@ add_qtc_plugin(CppEditor
|
|||||||
quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
|
quickfixes/cppquickfixsettingspage.cpp quickfixes/cppquickfixsettingspage.h
|
||||||
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
quickfixes/cppquickfixsettingswidget.cpp quickfixes/cppquickfixsettingswidget.h
|
||||||
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
|
quickfixes/convertqt4connect.cpp quickfixes/convertqt4connect.h
|
||||||
|
quickfixes/insertfunctiondefinition.cpp quickfixes/insertfunctiondefinition.h
|
||||||
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
quickfixes/moveclasstoownfile.cpp quickfixes/moveclasstoownfile.h
|
||||||
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
|
quickfixes/movefunctiondefinition.cpp quickfixes/movefunctiondefinition.h
|
||||||
quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h
|
quickfixes/removeusingnamespace.cpp quickfixes/removeusingnamespace.h
|
||||||
|
@@ -243,6 +243,8 @@ QtcPlugin {
|
|||||||
"cppquickfixsettingspage.h",
|
"cppquickfixsettingspage.h",
|
||||||
"cppquickfixsettingswidget.cpp",
|
"cppquickfixsettingswidget.cpp",
|
||||||
"cppquickfixsettingswidget.h",
|
"cppquickfixsettingswidget.h",
|
||||||
|
"insertfunctiondefinition.cpp",
|
||||||
|
"insertfunctiondefinition.h",
|
||||||
"moveclasstoownfile.cpp",
|
"moveclasstoownfile.cpp",
|
||||||
"moveclasstoownfile.h",
|
"moveclasstoownfile.h",
|
||||||
"movefunctiondefinition.cpp",
|
"movefunctiondefinition.cpp",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -100,45 +100,6 @@ private slots:
|
|||||||
void testInsertMemberFromUse_data();
|
void testInsertMemberFromUse_data();
|
||||||
void testInsertMemberFromUse();
|
void testInsertMemberFromUse();
|
||||||
|
|
||||||
void testInsertDefFromDeclAfterClass();
|
|
||||||
void testInsertDefFromDeclHeaderSourceBasic1();
|
|
||||||
void testInsertDefFromDeclHeaderSourceBasic2();
|
|
||||||
void testInsertDefFromDeclHeaderSourceBasic3();
|
|
||||||
void testInsertDefFromDeclHeaderSourceNamespace1();
|
|
||||||
void testInsertDefFromDeclHeaderSourceNamespace2();
|
|
||||||
void testInsertDefFromDeclInsideClass();
|
|
||||||
void testInsertDefFromDeclNotTriggeringWhenDefinitionExists();
|
|
||||||
void testInsertDefFromDeclFindRightImplementationFile();
|
|
||||||
void testInsertDefFromDeclIgnoreSurroundingGeneratedDeclarations();
|
|
||||||
void testInsertDefFromDeclRespectWsInOperatorNames1();
|
|
||||||
void testInsertDefFromDeclRespectWsInOperatorNames2();
|
|
||||||
void testInsertDefFromDeclNoexceptSpecifier();
|
|
||||||
void testInsertDefFromDeclMacroUsesAtEndOfFile1();
|
|
||||||
void testInsertDefFromDeclMacroUsesAtEndOfFile2();
|
|
||||||
void testInsertDefFromDeclErroneousStatementAtEndOfFile();
|
|
||||||
void testInsertDefFromDeclRvalueReference();
|
|
||||||
void testInsertDefFromDeclFunctionTryBlock();
|
|
||||||
void testInsertDefFromDeclUsingDecl();
|
|
||||||
void testInsertDefFromDeclFindImplementationFile();
|
|
||||||
void testInsertDefFromDeclUnicodeIdentifier();
|
|
||||||
void testInsertDefFromDeclTemplateClass();
|
|
||||||
void testInsertDefFromDeclTemplateClassWithValueParam();
|
|
||||||
void testInsertDefFromDeclTemplateFunction();
|
|
||||||
void testInsertDefFromDeclTemplateClassAndTemplateFunction();
|
|
||||||
void testInsertDefFromDeclTemplateClassAndFunctionInsideNamespace();
|
|
||||||
void testInsertDefFromDeclFunctionWithSignedUnsignedArgument();
|
|
||||||
void testInsertDefFromDeclNotTriggeredForFriendFunc();
|
|
||||||
void testInsertDefFromDeclMinimalFunctionParameterType();
|
|
||||||
void testInsertDefFromDeclAliasTemplateAsReturnType();
|
|
||||||
void testInsertDefsFromDecls_data();
|
|
||||||
void testInsertDefsFromDecls();
|
|
||||||
void testInsertAndFormatDefsFromDecls();
|
|
||||||
|
|
||||||
void testInsertDefOutsideFromDeclTemplateClassAndTemplateFunction();
|
|
||||||
void testInsertDefOutsideFromDeclTemplateClass();
|
|
||||||
void testInsertDefOutsideFromDeclTemplateFunction();
|
|
||||||
void testInsertDefOutsideFromDeclFunction();
|
|
||||||
|
|
||||||
void testInsertDeclFromDef();
|
void testInsertDeclFromDef();
|
||||||
void testInsertDeclFromDefTemplateFuncTypename();
|
void testInsertDeclFromDefTemplateFuncTypename();
|
||||||
void testInsertDeclFromDefTemplateFuncInt();
|
void testInsertDeclFromDefTemplateFuncInt();
|
||||||
|
@@ -23,6 +23,7 @@
|
|||||||
#include "cppquickfixhelpers.h"
|
#include "cppquickfixhelpers.h"
|
||||||
#include "cppquickfixprojectsettings.h"
|
#include "cppquickfixprojectsettings.h"
|
||||||
#include "convertqt4connect.h"
|
#include "convertqt4connect.h"
|
||||||
|
#include "insertfunctiondefinition.h"
|
||||||
#include "moveclasstoownfile.h"
|
#include "moveclasstoownfile.h"
|
||||||
#include "movefunctiondefinition.h"
|
#include "movefunctiondefinition.h"
|
||||||
#include "removeusingnamespace.h"
|
#include "removeusingnamespace.h"
|
||||||
@@ -131,13 +132,6 @@ namespace Internal {
|
|||||||
// different quick fixes.
|
// different quick fixes.
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum DefPos {
|
|
||||||
DefPosInsideClass,
|
|
||||||
DefPosOutsideClass,
|
|
||||||
DefPosImplementationFile
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline bool isQtStringLiteral(const QByteArray &id)
|
inline bool isQtStringLiteral(const QByteArray &id)
|
||||||
{
|
{
|
||||||
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral"
|
return id == "QLatin1String" || id == "QLatin1Literal" || id == "QStringLiteral"
|
||||||
@@ -2591,280 +2585,6 @@ QString InsertDeclOperation::generateDeclaration(const Function *function)
|
|||||||
return decl;
|
return decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class InsertDefOperation: public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Make sure that either loc is valid or targetFileName is not empty.
|
|
||||||
InsertDefOperation(const CppQuickFixInterface &interface,
|
|
||||||
Declaration *decl, DeclaratorAST *declAST, const InsertionLocation &loc,
|
|
||||||
const DefPos defpos, const FilePath &targetFileName = {},
|
|
||||||
bool freeFunction = false)
|
|
||||||
: CppQuickFixOperation(interface, 0)
|
|
||||||
, m_decl(decl)
|
|
||||||
, m_declAST(declAST)
|
|
||||||
, m_loc(loc)
|
|
||||||
, m_defpos(defpos)
|
|
||||||
, m_targetFilePath(targetFileName)
|
|
||||||
{
|
|
||||||
if (m_defpos == DefPosImplementationFile) {
|
|
||||||
const FilePath declFile = decl->filePath();
|
|
||||||
const FilePath targetFile = m_loc.isValid() ? m_loc.filePath() : m_targetFilePath;
|
|
||||||
const FilePath resolved = targetFile.relativePathFrom(declFile.parentDir());
|
|
||||||
setPriority(2);
|
|
||||||
setDescription(Tr::tr("Add Definition in %1").arg(resolved.displayName()));
|
|
||||||
} else if (freeFunction) {
|
|
||||||
setDescription(Tr::tr("Add Definition Here"));
|
|
||||||
} else if (m_defpos == DefPosInsideClass) {
|
|
||||||
setDescription(Tr::tr("Add Definition Inside Class"));
|
|
||||||
} else if (m_defpos == DefPosOutsideClass) {
|
|
||||||
setPriority(1);
|
|
||||||
setDescription(Tr::tr("Add Definition Outside Class"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void insertDefinition(
|
|
||||||
const CppQuickFixOperation *op,
|
|
||||||
InsertionLocation loc,
|
|
||||||
DefPos defPos,
|
|
||||||
DeclaratorAST *declAST,
|
|
||||||
Declaration *decl,
|
|
||||||
const FilePath &targetFilePath,
|
|
||||||
ChangeSet *changeSet = nullptr)
|
|
||||||
{
|
|
||||||
CppRefactoringChanges refactoring(op->snapshot());
|
|
||||||
if (!loc.isValid())
|
|
||||||
loc = insertLocationForMethodDefinition(decl, true, NamespaceHandling::Ignore,
|
|
||||||
refactoring, targetFilePath);
|
|
||||||
QTC_ASSERT(loc.isValid(), return);
|
|
||||||
|
|
||||||
CppRefactoringFilePtr targetFile = refactoring.cppFile(loc.filePath());
|
|
||||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
oo.showFunctionSignatures = true;
|
|
||||||
oo.showReturnTypes = true;
|
|
||||||
oo.showArgumentNames = true;
|
|
||||||
oo.showEnclosingTemplate = true;
|
|
||||||
|
|
||||||
// What we really want is to show template parameters for the class, but not for the
|
|
||||||
// function, but we cannot express that. This is an approximation that will work
|
|
||||||
// as long as either the surrounding class or the function is not a template.
|
|
||||||
oo.showTemplateParameters = decl->enclosingClass()
|
|
||||||
&& decl->enclosingClass()->enclosingTemplate();
|
|
||||||
|
|
||||||
if (defPos == DefPosInsideClass) {
|
|
||||||
const int targetPos = targetFile->position(loc.line(), loc.column());
|
|
||||||
ChangeSet localChangeSet;
|
|
||||||
ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
|
|
||||||
target->replace(targetPos - 1, targetPos, QLatin1String("\n {\n\n}")); // replace ';'
|
|
||||||
|
|
||||||
if (!changeSet) {
|
|
||||||
targetFile->setChangeSet(*target);
|
|
||||||
targetFile->setOpenEditor(true, targetPos);
|
|
||||||
targetFile->apply();
|
|
||||||
|
|
||||||
// Move cursor inside definition
|
|
||||||
QTextCursor c = targetFile->cursor();
|
|
||||||
c.setPosition(targetPos);
|
|
||||||
c.movePosition(QTextCursor::Down);
|
|
||||||
c.movePosition(QTextCursor::EndOfLine);
|
|
||||||
op->editor()->setTextCursor(c);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// make target lookup context
|
|
||||||
Document::Ptr targetDoc = targetFile->cppDocument();
|
|
||||||
Scope *targetScope = targetDoc->scopeAt(loc.line(), loc.column());
|
|
||||||
|
|
||||||
// Correct scope in case of a function try-block. See QTCREATORBUG-14661.
|
|
||||||
if (targetScope && targetScope->asBlock()) {
|
|
||||||
if (Class * const enclosingClass = targetScope->enclosingClass())
|
|
||||||
targetScope = enclosingClass;
|
|
||||||
else
|
|
||||||
targetScope = targetScope->enclosingNamespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
LookupContext targetContext(targetDoc, op->snapshot());
|
|
||||||
ClassOrNamespace *targetCoN = targetContext.lookupType(targetScope);
|
|
||||||
if (!targetCoN)
|
|
||||||
targetCoN = targetContext.globalNamespace();
|
|
||||||
|
|
||||||
// setup rewriting to get minimally qualified names
|
|
||||||
SubstitutionEnvironment env;
|
|
||||||
env.setContext(op->context());
|
|
||||||
env.switchScope(decl->enclosingScope());
|
|
||||||
UseMinimalNames q(targetCoN);
|
|
||||||
env.enter(&q);
|
|
||||||
Control *control = op->context().bindings()->control().get();
|
|
||||||
|
|
||||||
// rewrite the function type
|
|
||||||
const FullySpecifiedType tn = rewriteType(decl->type(), &env, control);
|
|
||||||
|
|
||||||
// rewrite the function name
|
|
||||||
if (nameIncludesOperatorName(decl->name())) {
|
|
||||||
CppRefactoringFilePtr file = refactoring.cppFile(op->filePath());
|
|
||||||
const QString operatorNameText = file->textOf(declAST->core_declarator);
|
|
||||||
oo.includeWhiteSpaceInOperatorName = operatorNameText.contains(QLatin1Char(' '));
|
|
||||||
}
|
|
||||||
const QString name = oo.prettyName(LookupContext::minimalName(decl, targetCoN,
|
|
||||||
control));
|
|
||||||
|
|
||||||
const QString inlinePref = inlinePrefix(targetFilePath, [defPos] {
|
|
||||||
return defPos == DefPosOutsideClass;
|
|
||||||
});
|
|
||||||
|
|
||||||
const QString prettyType = oo.prettyType(tn, name);
|
|
||||||
|
|
||||||
QString input = prettyType;
|
|
||||||
int index = 0;
|
|
||||||
while (input.startsWith("template")) {
|
|
||||||
QRegularExpression templateRegex("template\\s*<[^>]*>");
|
|
||||||
QRegularExpressionMatch match = templateRegex.match(input);
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
index += match.captured().size() + 1;
|
|
||||||
input = input.mid(match.captured().size() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defText = prettyType;
|
|
||||||
defText.insert(index, inlinePref);
|
|
||||||
defText += QLatin1String("\n{\n\n}");
|
|
||||||
|
|
||||||
ChangeSet localChangeSet;
|
|
||||||
ChangeSet * const target = changeSet ? changeSet : &localChangeSet;
|
|
||||||
const int targetPos = targetFile->position(loc.line(), loc.column());
|
|
||||||
target->insert(targetPos, loc.prefix() + defText + loc.suffix());
|
|
||||||
|
|
||||||
if (!changeSet) {
|
|
||||||
targetFile->setChangeSet(*target);
|
|
||||||
targetFile->setOpenEditor(true, targetPos);
|
|
||||||
targetFile->apply();
|
|
||||||
|
|
||||||
// Move cursor inside definition
|
|
||||||
QTextCursor c = targetFile->cursor();
|
|
||||||
c.setPosition(targetPos);
|
|
||||||
c.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor,
|
|
||||||
loc.prefix().count(QLatin1String("\n")) + 2);
|
|
||||||
c.movePosition(QTextCursor::EndOfLine);
|
|
||||||
if (defPos == DefPosImplementationFile) {
|
|
||||||
if (targetFile->editor())
|
|
||||||
targetFile->editor()->setTextCursor(c);
|
|
||||||
} else {
|
|
||||||
op->editor()->setTextCursor(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
insertDefinition(this, m_loc, m_defpos, m_declAST, m_decl, m_targetFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
Declaration *m_decl;
|
|
||||||
DeclaratorAST *m_declAST;
|
|
||||||
InsertionLocation m_loc;
|
|
||||||
const DefPos m_defpos;
|
|
||||||
const FilePath m_targetFilePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
void InsertDefFromDecl::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const QList<AST *> &path = interface.path();
|
|
||||||
|
|
||||||
int idx = path.size() - 1;
|
|
||||||
for (; idx >= 0; --idx) {
|
|
||||||
AST *node = path.at(idx);
|
|
||||||
if (SimpleDeclarationAST *simpleDecl = node->asSimpleDeclaration()) {
|
|
||||||
if (idx > 0 && path.at(idx - 1)->asStatement())
|
|
||||||
return;
|
|
||||||
if (simpleDecl->symbols && !simpleDecl->symbols->next) {
|
|
||||||
if (Symbol *symbol = simpleDecl->symbols->value) {
|
|
||||||
if (Declaration *decl = symbol->asDeclaration()) {
|
|
||||||
if (Function *func = decl->type()->asFunctionType()) {
|
|
||||||
if (func->isSignal() || func->isPureVirtual() || func->isFriend())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if there is already a definition
|
|
||||||
SymbolFinder symbolFinder;
|
|
||||||
if (symbolFinder.findMatchingDefinition(decl, interface.snapshot(),
|
|
||||||
true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert Position: Implementation File
|
|
||||||
DeclaratorAST *declAST = simpleDecl->declarator_list->value;
|
|
||||||
InsertDefOperation *op = nullptr;
|
|
||||||
ProjectFile::Kind kind = ProjectFile::classify(interface.filePath().toString());
|
|
||||||
const bool isHeaderFile = ProjectFile::isHeader(kind);
|
|
||||||
if (isHeaderFile) {
|
|
||||||
CppRefactoringChanges refactoring(interface.snapshot());
|
|
||||||
InsertionPointLocator locator(refactoring);
|
|
||||||
// find appropriate implementation file, but do not use this
|
|
||||||
// location, because insertLocationForMethodDefinition() should
|
|
||||||
// be used in perform() to get consistent insert positions.
|
|
||||||
for (const InsertionLocation &location :
|
|
||||||
locator.methodDefinition(decl, false, {})) {
|
|
||||||
if (!location.isValid())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const FilePath filePath = location.filePath();
|
|
||||||
if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
|
|
||||||
const FilePath source = correspondingHeaderOrSource(filePath);
|
|
||||||
if (!source.isEmpty()) {
|
|
||||||
op = new InsertDefOperation(interface, decl, declAST,
|
|
||||||
InsertionLocation(),
|
|
||||||
DefPosImplementationFile,
|
|
||||||
source);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
op = new InsertDefOperation(interface, decl, declAST,
|
|
||||||
InsertionLocation(),
|
|
||||||
DefPosImplementationFile,
|
|
||||||
filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (op)
|
|
||||||
result << op;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if we are dealing with a free function
|
|
||||||
const bool isFreeFunction = func->enclosingClass() == nullptr;
|
|
||||||
|
|
||||||
// Insert Position: Outside Class
|
|
||||||
if (!isFreeFunction || m_defPosOutsideClass) {
|
|
||||||
result << new InsertDefOperation(interface, decl, declAST,
|
|
||||||
InsertionLocation(),
|
|
||||||
DefPosOutsideClass,
|
|
||||||
interface.filePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert Position: Inside Class
|
|
||||||
// Determine insert location direct after the declaration.
|
|
||||||
int line, column;
|
|
||||||
const CppRefactoringFilePtr file = interface.currentFile();
|
|
||||||
file->lineAndColumn(file->endOf(simpleDecl), &line, &column);
|
|
||||||
const InsertionLocation loc
|
|
||||||
= InsertionLocation(interface.filePath(), QString(),
|
|
||||||
QString(), line, column);
|
|
||||||
result << new InsertDefOperation(interface, decl, declAST, loc,
|
|
||||||
DefPosInsideClass, FilePath(),
|
|
||||||
isFreeFunction);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InsertMemberFromInitializationOp : public CppQuickFixOperation
|
class InsertMemberFromInitializationOp : public CppQuickFixOperation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -3253,259 +2973,6 @@ void AddDeclarationForUndeclaredIdentifier::maybeAddStaticMember(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MemberFunctionImplSetting
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Symbol *func = nullptr;
|
|
||||||
DefPos defPos = DefPosImplementationFile;
|
|
||||||
};
|
|
||||||
using MemberFunctionImplSettings = QList<MemberFunctionImplSetting>;
|
|
||||||
|
|
||||||
class AddImplementationsDialog : public QDialog
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AddImplementationsDialog(const QList<Symbol *> &candidates, const FilePath &implFile)
|
|
||||||
: QDialog(Core::ICore::dialogParent()), m_candidates(candidates)
|
|
||||||
{
|
|
||||||
setWindowTitle(Tr::tr("Member Function Implementations"));
|
|
||||||
|
|
||||||
const auto defaultImplTargetComboBox = new QComboBox;
|
|
||||||
QStringList implTargetStrings{Tr::tr("None"), Tr::tr("Inline"), Tr::tr("Outside Class")};
|
|
||||||
if (!implFile.isEmpty())
|
|
||||||
implTargetStrings.append(implFile.fileName());
|
|
||||||
defaultImplTargetComboBox->insertItems(0, implTargetStrings);
|
|
||||||
connect(defaultImplTargetComboBox, &QComboBox::currentIndexChanged, this,
|
|
||||||
[this](int index) {
|
|
||||||
for (int i = 0; i < m_implTargetBoxes.size(); ++i) {
|
|
||||||
if (!m_candidates.at(i)->type()->asFunctionType()->isPureVirtual())
|
|
||||||
static_cast<QComboBox *>(m_implTargetBoxes.at(i))->setCurrentIndex(index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const auto defaultImplTargetLayout = new QHBoxLayout;
|
|
||||||
defaultImplTargetLayout->addWidget(new QLabel(Tr::tr("Default implementation location:")));
|
|
||||||
defaultImplTargetLayout->addWidget(defaultImplTargetComboBox);
|
|
||||||
|
|
||||||
const auto candidatesLayout = new QGridLayout;
|
|
||||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
oo.showFunctionSignatures = true;
|
|
||||||
oo.showReturnTypes = true;
|
|
||||||
for (int i = 0; i < m_candidates.size(); ++i) {
|
|
||||||
const Function * const func = m_candidates.at(i)->type()->asFunctionType();
|
|
||||||
QTC_ASSERT(func, continue);
|
|
||||||
const auto implTargetComboBox = new QComboBox;
|
|
||||||
m_implTargetBoxes.append(implTargetComboBox);
|
|
||||||
implTargetComboBox->insertItems(0, implTargetStrings);
|
|
||||||
if (func->isPureVirtual())
|
|
||||||
implTargetComboBox->setCurrentIndex(0);
|
|
||||||
candidatesLayout->addWidget(new QLabel(oo.prettyType(func->type(), func->name())),
|
|
||||||
i, 0);
|
|
||||||
candidatesLayout->addWidget(implTargetComboBox, i, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto buttonBox
|
|
||||||
= new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
|
||||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
||||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
|
|
||||||
defaultImplTargetComboBox->setCurrentIndex(implTargetStrings.size() - 1);
|
|
||||||
const auto mainLayout = new QVBoxLayout(this);
|
|
||||||
mainLayout->addLayout(defaultImplTargetLayout);
|
|
||||||
mainLayout->addWidget(Layouting::createHr(this));
|
|
||||||
mainLayout->addLayout(candidatesLayout);
|
|
||||||
mainLayout->addWidget(buttonBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
MemberFunctionImplSettings settings() const
|
|
||||||
{
|
|
||||||
QTC_ASSERT(m_candidates.size() == m_implTargetBoxes.size(), return {});
|
|
||||||
MemberFunctionImplSettings settings;
|
|
||||||
for (int i = 0; i < m_candidates.size(); ++i) {
|
|
||||||
MemberFunctionImplSetting setting;
|
|
||||||
const int index = m_implTargetBoxes.at(i)->currentIndex();
|
|
||||||
const bool addImplementation = index != 0;
|
|
||||||
if (!addImplementation)
|
|
||||||
continue;
|
|
||||||
setting.func = m_candidates.at(i);
|
|
||||||
setting.defPos = static_cast<DefPos>(index - 1);
|
|
||||||
settings << setting;
|
|
||||||
}
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const QList<Symbol *> m_candidates;
|
|
||||||
QList<QComboBox *> m_implTargetBoxes;
|
|
||||||
};
|
|
||||||
|
|
||||||
class InsertDefsOperation: public CppQuickFixOperation
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
InsertDefsOperation(const CppQuickFixInterface &interface)
|
|
||||||
: CppQuickFixOperation(interface)
|
|
||||||
{
|
|
||||||
setDescription(Tr::tr("Create Implementations for Member Functions"));
|
|
||||||
|
|
||||||
m_classAST = astForClassOperations(interface);
|
|
||||||
if (!m_classAST)
|
|
||||||
return;
|
|
||||||
const Class * const theClass = m_classAST->symbol;
|
|
||||||
if (!theClass)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Collect all member functions.
|
|
||||||
for (auto it = theClass->memberBegin(); it != theClass->memberEnd(); ++it) {
|
|
||||||
Symbol * const s = *it;
|
|
||||||
if (!s->identifier() || !s->type() || !s->asDeclaration() || s->asFunction())
|
|
||||||
continue;
|
|
||||||
Function * const func = s->type()->asFunctionType();
|
|
||||||
if (!func || func->isSignal() || func->isFriend())
|
|
||||||
continue;
|
|
||||||
Overview oo = CppCodeStyleSettings::currentProjectCodeStyleOverview();
|
|
||||||
oo.showFunctionSignatures = true;
|
|
||||||
if (magicQObjectFunctions().contains(oo.prettyName(func->name())))
|
|
||||||
continue;
|
|
||||||
m_declarations << s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isApplicable() const { return !m_declarations.isEmpty(); }
|
|
||||||
void setMode(InsertDefsFromDecls::Mode mode) { m_mode = mode; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void perform() override
|
|
||||||
{
|
|
||||||
QList<Symbol *> unimplemented;
|
|
||||||
SymbolFinder symbolFinder;
|
|
||||||
for (Symbol * const s : std::as_const(m_declarations)) {
|
|
||||||
if (!symbolFinder.findMatchingDefinition(s, snapshot()))
|
|
||||||
unimplemented << s;
|
|
||||||
}
|
|
||||||
if (unimplemented.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
CppRefactoringChanges refactoring(snapshot());
|
|
||||||
const bool isHeaderFile = ProjectFile::isHeader(ProjectFile::classify(filePath().toString()));
|
|
||||||
FilePath cppFile; // Only set if the class is defined in a header file.
|
|
||||||
if (isHeaderFile) {
|
|
||||||
InsertionPointLocator locator(refactoring);
|
|
||||||
for (const InsertionLocation &location
|
|
||||||
: locator.methodDefinition(unimplemented.first(), false, {})) {
|
|
||||||
if (!location.isValid())
|
|
||||||
continue;
|
|
||||||
const FilePath filePath = location.filePath();
|
|
||||||
if (ProjectFile::isHeader(ProjectFile::classify(filePath.path()))) {
|
|
||||||
const FilePath source = correspondingHeaderOrSource(filePath);
|
|
||||||
if (!source.isEmpty())
|
|
||||||
cppFile = source;
|
|
||||||
} else {
|
|
||||||
cppFile = filePath;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MemberFunctionImplSettings settings;
|
|
||||||
switch (m_mode) {
|
|
||||||
case InsertDefsFromDecls::Mode::User: {
|
|
||||||
AddImplementationsDialog dlg(unimplemented, cppFile);
|
|
||||||
if (dlg.exec() == QDialog::Accepted)
|
|
||||||
settings = dlg.settings();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InsertDefsFromDecls::Mode::Impl: {
|
|
||||||
for (Symbol * const func : std::as_const(unimplemented)) {
|
|
||||||
MemberFunctionImplSetting setting;
|
|
||||||
setting.func = func;
|
|
||||||
setting.defPos = DefPosImplementationFile;
|
|
||||||
settings << setting;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InsertDefsFromDecls::Mode::Alternating: {
|
|
||||||
int defPos = DefPosImplementationFile;
|
|
||||||
const auto incDefPos = [&defPos] {
|
|
||||||
defPos = (defPos + 1) % (DefPosImplementationFile + 2);
|
|
||||||
};
|
|
||||||
for (Symbol * const func : std::as_const(unimplemented)) {
|
|
||||||
incDefPos();
|
|
||||||
if (defPos > DefPosImplementationFile)
|
|
||||||
continue;
|
|
||||||
MemberFunctionImplSetting setting;
|
|
||||||
setting.func = func;
|
|
||||||
setting.defPos = static_cast<DefPos>(defPos);
|
|
||||||
settings << setting;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InsertDefsFromDecls::Mode::Off:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
class DeclFinder : public ASTVisitor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeclFinder(const CppRefactoringFile *file, const Symbol *func)
|
|
||||||
: ASTVisitor(file->cppDocument()->translationUnit()), m_func(func) {}
|
|
||||||
|
|
||||||
SimpleDeclarationAST *decl() const { return m_decl; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool visit(SimpleDeclarationAST *decl) override
|
|
||||||
{
|
|
||||||
if (m_decl)
|
|
||||||
return false;
|
|
||||||
if (decl->symbols && decl->symbols->value == m_func)
|
|
||||||
m_decl = decl;
|
|
||||||
return !m_decl;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Symbol * const m_func;
|
|
||||||
SimpleDeclarationAST *m_decl = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash<FilePath, ChangeSet> changeSets;
|
|
||||||
for (const MemberFunctionImplSetting &setting : std::as_const(settings)) {
|
|
||||||
DeclFinder finder(currentFile().data(), setting.func);
|
|
||||||
finder.accept(m_classAST);
|
|
||||||
QTC_ASSERT(finder.decl(), continue);
|
|
||||||
InsertionLocation loc;
|
|
||||||
const FilePath targetFilePath = setting.defPos == DefPosImplementationFile
|
|
||||||
? cppFile : filePath();
|
|
||||||
QTC_ASSERT(!targetFilePath.isEmpty(), continue);
|
|
||||||
if (setting.defPos == DefPosInsideClass) {
|
|
||||||
int line, column;
|
|
||||||
currentFile()->lineAndColumn(currentFile()->endOf(finder.decl()), &line, &column);
|
|
||||||
loc = InsertionLocation(filePath(), QString(), QString(), line, column);
|
|
||||||
}
|
|
||||||
ChangeSet &changeSet = changeSets[targetFilePath];
|
|
||||||
InsertDefOperation::insertDefinition(
|
|
||||||
this, loc, setting.defPos, finder.decl()->declarator_list->value,
|
|
||||||
setting.func->asDeclaration(),targetFilePath, &changeSet);
|
|
||||||
}
|
|
||||||
for (auto it = changeSets.cbegin(); it != changeSets.cend(); ++it) {
|
|
||||||
const CppRefactoringFilePtr file = refactoring.cppFile(it.key());
|
|
||||||
file->setChangeSet(it.value());
|
|
||||||
file->apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassSpecifierAST *m_classAST = nullptr;
|
|
||||||
InsertDefsFromDecls::Mode m_mode;
|
|
||||||
QList<Symbol *> m_declarations;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void InsertDefsFromDecls::doMatch(const CppQuickFixInterface &interface, QuickFixOperations &result)
|
|
||||||
{
|
|
||||||
const auto op = QSharedPointer<InsertDefsOperation>::create(interface);
|
|
||||||
op->setMode(m_mode);
|
|
||||||
if (op->isApplicable())
|
|
||||||
result << op;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class ExtractFunctionOptions
|
class ExtractFunctionOptions
|
||||||
@@ -5994,9 +5461,7 @@ void createCppQuickFixes()
|
|||||||
new ExtractFunction;
|
new ExtractFunction;
|
||||||
new ExtractLiteralAsParameter;
|
new ExtractLiteralAsParameter;
|
||||||
new InsertDeclFromDef;
|
new InsertDeclFromDef;
|
||||||
new InsertDefFromDecl;
|
|
||||||
new AddDeclarationForUndeclaredIdentifier;
|
new AddDeclarationForUndeclaredIdentifier;
|
||||||
new InsertDefsFromDecls;
|
|
||||||
|
|
||||||
new AssignToLocalVariable;
|
new AssignToLocalVariable;
|
||||||
|
|
||||||
@@ -6006,6 +5471,7 @@ void createCppQuickFixes()
|
|||||||
registerCodeGenerationQuickfixes();
|
registerCodeGenerationQuickfixes();
|
||||||
registerConvertQt4ConnectQuickfix();
|
registerConvertQt4ConnectQuickfix();
|
||||||
registerMoveFunctionDefinitionQuickfixes();
|
registerMoveFunctionDefinitionQuickfixes();
|
||||||
|
registerInsertFunctionDefinitionQuickfixes();
|
||||||
|
|
||||||
new OptimizeForLoop;
|
new OptimizeForLoop;
|
||||||
|
|
||||||
|
@@ -353,16 +353,6 @@ public:
|
|||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
Adds a definition for a declaration.
|
|
||||||
*/
|
|
||||||
class InsertDefFromDecl: public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface, TextEditor::QuickFixOperations &result) override;
|
|
||||||
bool m_defPosOutsideClass = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
|
class AddDeclarationForUndeclaredIdentifier : public CppQuickFixFactory
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -395,27 +385,6 @@ private:
|
|||||||
bool m_membersOnly = false;
|
bool m_membersOnly = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
|
||||||
Adds a definition for any number of member function declarations.
|
|
||||||
*/
|
|
||||||
class InsertDefsFromDecls : public CppQuickFixFactory
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void doMatch(const CppQuickFixInterface &interface,
|
|
||||||
TextEditor::QuickFixOperations &result) override;
|
|
||||||
|
|
||||||
enum class Mode {
|
|
||||||
Off, // Testing: simulates user canceling the dialog
|
|
||||||
Impl, // Testing: simulates user choosing cpp file for every function
|
|
||||||
Alternating, // Testing: simulates user choosing a different DefPos for every function
|
|
||||||
User // Normal interactive mode
|
|
||||||
};
|
|
||||||
void setMode(Mode mode) { m_mode = mode; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Mode m_mode = Mode::User;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Extracts the selected code and puts it to a function
|
Extracts the selected code and puts it to a function
|
||||||
*/
|
*/
|
||||||
|
2129
src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
Normal file
2129
src/plugins/cppeditor/quickfixes/insertfunctiondefinition.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace CppEditor::Internal {
|
||||||
|
void registerInsertFunctionDefinitionQuickfixes();
|
||||||
|
} // namespace CppEditor::Internal
|
Reference in New Issue
Block a user