diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp index 4c5fa48ed7e5be0ce3928b182cd2072358d57450..cb4630d9c9c4810a6552f536dc5633efca71dc8d 100644 --- a/src/libs/cplusplus/CppDocument.cpp +++ b/src/libs/cplusplus/CppDocument.cpp @@ -50,6 +50,7 @@ #include <NameVisitor.h> #include <TypeVisitor.h> #include <CoreTypes.h> +#include <LookupContext.h> #include <QtCore/QByteArray> #include <QtCore/QBitArray> @@ -583,6 +584,8 @@ void Document::check(CheckMode mode) semantic(ast, _globalNamespace); } else if (ExpressionAST *ast = _translationUnit->ast()->asExpression()) { semantic(ast, _globalNamespace); + } else if (DeclarationAST *ast = translationUnit()->ast()->asDeclaration()) { + semantic(ast, _globalNamespace); } } @@ -972,7 +975,9 @@ public: }; } // end of anonymous namespace -Symbol *Snapshot::findMatchingDefinition(Symbol *declaration) const +// strict means the returned symbol has to match exactly, +// including argument count and argument types +Symbol *Snapshot::findMatchingDefinition(Symbol *declaration, bool strict) const { if (!declaration) return 0; @@ -1030,7 +1035,7 @@ Symbol *Snapshot::findMatchingDefinition(Symbol *declaration) const if (viableFunctions.isEmpty()) continue; - else if (viableFunctions.length() == 1) + else if (! strict && viableFunctions.length() == 1) return viableFunctions.first(); Function *best = 0; @@ -1039,7 +1044,7 @@ Symbol *Snapshot::findMatchingDefinition(Symbol *declaration) const if (! (fun->unqualifiedName() && fun->unqualifiedName()->isEqualTo(declaration->unqualifiedName()))) continue; else if (fun->argumentCount() == declarationTy->argumentCount()) { - if (! best) + if (! strict && ! best) best = fun; unsigned argc = 0; @@ -1055,7 +1060,7 @@ Symbol *Snapshot::findMatchingDefinition(Symbol *declaration) const } } - if (! best) + if (!strict && ! best) best = viableFunctions.first(); return best; @@ -1089,3 +1094,74 @@ Class *Snapshot::findMatchingClassDeclaration(Symbol *declaration) const return 0; } + +void CPlusPlus::findMatchingDeclaration(const LookupContext &context, + Function *functionType, + QList<Declaration *> *typeMatch, + QList<Declaration *> *argumentCountMatch, + QList<Declaration *> *nameMatch) +{ + Scope *enclosingScope = functionType->enclosingScope(); + while (! (enclosingScope->isNamespace() || enclosingScope->isClass())) + enclosingScope = enclosingScope->enclosingScope(); + Q_ASSERT(enclosingScope != 0); + + const Name *functionName = functionType->name(); + if (! functionName) + return; // anonymous function names are not valid c++ + + ClassOrNamespace *binding = 0; + const QualifiedNameId *qName = functionName->asQualifiedNameId(); + if (qName) { + if (qName->base()) + binding = context.lookupType(qName->base(), enclosingScope); + functionName = qName->name(); + } + + if (!binding) { // declaration for a global function + binding = context.lookupType(enclosingScope); + + if (!binding) + return; + } + + const Identifier *funcId = functionName->identifier(); + if (!funcId) // E.g. operator, which we might be able to handle in the future... + return; + + foreach (Symbol *s, binding->symbols()) { + Class *matchingClass = s->asClass(); + if (!matchingClass) + continue; + + for (Symbol *s = matchingClass->find(funcId); s; s = s->next()) { + if (! s->name()) + continue; + else if (! funcId->isEqualTo(s->identifier())) + continue; + else if (! s->type()->isFunctionType()) + continue; + else if (Declaration *decl = s->asDeclaration()) { + if (Function *declFunTy = decl->type()->asFunctionType()) { + if (functionType->isEqualTo(declFunTy)) + typeMatch->prepend(decl); + else if (functionType->argumentCount() == declFunTy->argumentCount()) + argumentCountMatch->prepend(decl); + else + nameMatch->append(decl); + } + } + } + } +} + +QList<Declaration *> CPlusPlus::findMatchingDeclaration(const LookupContext &context, Function *functionType) +{ + QList<Declaration *> result; + QList<Declaration *> nameMatch, argumentCountMatch, typeMatch; + findMatchingDeclaration(context, functionType, &typeMatch, &argumentCountMatch, &nameMatch); + result.append(typeMatch); + result.append(argumentCountMatch); + result.append(nameMatch); + return result; +} diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h index 92e1e902b305227ad4e6ffef6b416f3044172dd6..48d72cad3cb13bbd44ccbf53c791defbdbd93df0 100644 --- a/src/libs/cplusplus/CppDocument.h +++ b/src/libs/cplusplus/CppDocument.h @@ -45,6 +45,7 @@ namespace CPlusPlus { class Macro; class MacroArgumentReference; +class LookupContext; class CPLUSPLUS_EXPORT Document { @@ -386,7 +387,7 @@ public: Document::Ptr documentFromSource(const QByteArray &preprocessedCode, const QString &fileName) const; - Symbol *findMatchingDefinition(Symbol *symbol) const; + Symbol *findMatchingDefinition(Symbol *symbol, bool strict = false) const; Class *findMatchingClassDeclaration(Symbol *symbol) const; private: @@ -396,6 +397,15 @@ private: _Base _documents; }; +void CPLUSPLUS_EXPORT findMatchingDeclaration( + const LookupContext &context, + Function *functionType, + QList<Declaration *> *typeMatch, + QList<Declaration *> *argumentCountMatch, + QList<Declaration *> *nameMatch); +QList<Declaration *> CPLUSPLUS_EXPORT findMatchingDeclaration( + const LookupContext &context, Function *functionType); + } // namespace CPlusPlus #endif // CPLUSPLUS_CPPDOCUMENT_H diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp index 271303048d50566e8aa35cd07e1d850a3a4acc98..1433e55fafabe6071ffef8eb132ad72d756c8a8a 100644 --- a/src/libs/utils/changeset.cpp +++ b/src/libs/utils/changeset.cpp @@ -41,6 +41,11 @@ ChangeSet::ChangeSet() { } +ChangeSet::ChangeSet(const QList<EditOp> &operations) + : m_string(0), m_cursor(0), m_operationList(operations), m_error(false) +{ +} + static bool overlaps(int posA, int lengthA, int posB, int lengthB) { if (lengthB > 0) { return diff --git a/src/libs/utils/changeset.h b/src/libs/utils/changeset.h index 5b26c703b21633ef1f5df793992303dcc5ec3aa0..d184f379ebc18cabc62d52d703fc330180ee0428 100644 --- a/src/libs/utils/changeset.h +++ b/src/libs/utils/changeset.h @@ -81,6 +81,7 @@ public: public: ChangeSet(); + ChangeSet(const QList<EditOp> &operations); bool isEmpty() const; diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index bc07e4ffbc86e7ab7095b7593462c9a5075f4042..c15f1e04e7cd3a11f2cbb031bb29dc162433b76c 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -67,6 +67,7 @@ #include <cpptools/cppcompletionassist.h> #include <cpptools/cppqtstyleindenter.h> #include <cpptools/cppcodestylesettings.h> +#include <cpptools/cpprefactoringchanges.h> #include <coreplugin/icore.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -76,6 +77,7 @@ #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/mimedatabase.h> +#include <utils/qtcassert.h> #include <utils/uncommentselection.h> #include <extensionsystem/pluginmanager.h> #include <projectexplorer/projectexplorerconstants.h> @@ -84,6 +86,7 @@ #include <texteditor/fontsettings.h> #include <texteditor/tabsettings.h> #include <texteditor/texteditorconstants.h> +#include <texteditor/refactoroverlay.h> #include <texteditor/codeassist/basicproposalitemlistmodel.h> #include <texteditor/codeassist/basicproposalitem.h> #include <texteditor/codeassist/genericproposal.h> @@ -111,7 +114,8 @@ enum { UPDATE_OUTLINE_INTERVAL = 500, - UPDATE_USES_INTERVAL = 500 + UPDATE_USES_INTERVAL = 500, + UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL = 200 }; using namespace CPlusPlus; @@ -464,6 +468,13 @@ CPPEditorWidget::CPPEditorWidget(QWidget *parent) m_referencesRevision = 0; m_referencesCursorPosition = 0; connect(&m_referencesWatcher, SIGNAL(finished()), SLOT(markSymbolsNow())); + + connect(this, SIGNAL(refactorMarkerClicked(TextEditor::RefactorMarker)), + this, SLOT(onRefactorMarkerClicked(TextEditor::RefactorMarker))); + + m_declDefLinkFinder = new FunctionDeclDefLinkFinder(this); + connect(m_declDefLinkFinder, SIGNAL(foundLink(QSharedPointer<FunctionDeclDefLink>)), + this, SLOT(onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink>))); } CPPEditorWidget::~CPPEditorWidget() @@ -533,6 +544,11 @@ void CPPEditorWidget::createToolBar(CPPEditor *editor) m_updateUsesTimer->setInterval(UPDATE_USES_INTERVAL); connect(m_updateUsesTimer, SIGNAL(timeout()), this, SLOT(updateUsesNow())); + m_updateFunctionDeclDefLinkTimer = new QTimer(this); + m_updateFunctionDeclDefLinkTimer->setSingleShot(true); + m_updateFunctionDeclDefLinkTimer->setInterval(UPDATE_FUNCTION_DECL_DEF_LINK_INTERVAL); + connect(m_updateFunctionDeclDefLinkTimer, SIGNAL(timeout()), this, SLOT(updateFunctionDeclDefLinkNow())); + connect(m_outlineCombo, SIGNAL(activated(int)), this, SLOT(jumpToOutlineElement(int))); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateOutlineIndex())); connect(m_outlineCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOutlineToolTip())); @@ -540,6 +556,9 @@ void CPPEditorWidget::createToolBar(CPPEditor *editor) connect(file(), SIGNAL(changed()), this, SLOT(updateFileName())); + // set up function declaration - definition link + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateFunctionDeclDefLink())); + connect(this, SIGNAL(textChanged()), this, SLOT(updateFunctionDeclDefLink())); // set up the semantic highlighter connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateUses())); @@ -1170,77 +1189,6 @@ static inline LookupItem skipForwardDeclarations(const QList<LookupItem> &resolv return result; } -namespace { - -QList<Declaration *> findMatchingDeclaration(const LookupContext &context, - Function *functionType) -{ - QList<Declaration *> result; - - Scope *enclosingScope = functionType->enclosingScope(); - while (! (enclosingScope->isNamespace() || enclosingScope->isClass())) - enclosingScope = enclosingScope->enclosingScope(); - Q_ASSERT(enclosingScope != 0); - - const Name *functionName = functionType->name(); - if (! functionName) - return result; // anonymous function names are not valid c++ - - ClassOrNamespace *binding = 0; - const QualifiedNameId *qName = functionName->asQualifiedNameId(); - if (qName) { - if (qName->base()) - binding = context.lookupType(qName->base(), enclosingScope); - functionName = qName->name(); - } - - if (!binding) { // declaration for a global function - binding = context.lookupType(enclosingScope); - - if (!binding) - return result; - } - - const Identifier *funcId = functionName->identifier(); - if (!funcId) // E.g. operator, which we might be able to handle in the future... - return result; - - QList<Declaration *> good, better, best; - - foreach (Symbol *s, binding->symbols()) { - Class *matchingClass = s->asClass(); - if (!matchingClass) - continue; - - for (Symbol *s = matchingClass->find(funcId); s; s = s->next()) { - if (! s->name()) - continue; - else if (! funcId->isEqualTo(s->identifier())) - continue; - else if (! s->type()->isFunctionType()) - continue; - else if (Declaration *decl = s->asDeclaration()) { - if (Function *declFunTy = decl->type()->asFunctionType()) { - if (functionType->isEqualTo(declFunTy)) - best.prepend(decl); - else if (functionType->argumentCount() == declFunTy->argumentCount() && result.isEmpty()) - better.prepend(decl); - else - good.append(decl); - } - } - } - } - - result.append(best); - result.append(better); - result.append(good); - - return result; -} - -} // end of anonymous namespace - CPPEditorWidget::Link CPPEditorWidget::attemptFuncDeclDef(const QTextCursor &cursor, const Document::Ptr &doc, Snapshot snapshot) const { snapshot.insert(doc); @@ -1616,7 +1564,10 @@ bool CPPEditorWidget::event(QEvent *e) { switch (e->type()) { case QEvent::ShortcutOverride: - if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_currentRenameSelection != NoCurrentRenameSelection) { + // handle escape manually if a rename or func decl/def link is active + if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape + && (m_currentRenameSelection != NoCurrentRenameSelection + || m_declDefLink)) { e->accept(); return true; } @@ -1691,10 +1642,29 @@ void CPPEditorWidget::contextMenuEvent(QContextMenuEvent *e) void CPPEditorWidget::keyPressEvent(QKeyEvent *e) { if (m_currentRenameSelection == NoCurrentRenameSelection) { + // key handling for linked function declarations/definitions + if (m_declDefLink && m_declDefLink->isMarkerVisible()) { + switch (e->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + applyDeclDefLinkChanges(/*jump tp change*/ e->modifiers() & Qt::ShiftModifier); + e->accept(); + return; + case Qt::Key_Escape: + abortDeclDefLink(); + e->accept(); + return; + default: + break; + } + } + TextEditor::BaseTextEditorWidget::keyPressEvent(e); return; } + // key handling for renames + QTextCursor cursor = textCursor(); const QTextCursor::MoveMode moveMode = (e->modifiers() & Qt::ShiftModifier) ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor; @@ -1974,6 +1944,9 @@ void CPPEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo) } m_lastSemanticInfo.forced = false; // clear the forced flag + + // schedule a check for a decl/def link + updateFunctionDeclDefLink(); } namespace { @@ -2274,4 +2247,78 @@ TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface( return 0; } +void CPPEditorWidget::onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker) +{ + if (marker.data.canConvert<FunctionDeclDefLink::Marker>()) + applyDeclDefLinkChanges(true); +} + +void CPPEditorWidget::updateFunctionDeclDefLink() +{ + const int pos = textCursor().selectionStart(); + + // if there's already a link, abort it if the cursor is outside + if (m_declDefLink + && (pos < m_declDefLink->linkSelection.selectionStart() + || pos > m_declDefLink->linkSelection.selectionEnd())) { + abortDeclDefLink(); + return; + } + + // don't start a new scan if there's one active and the cursor is already in the scanned area + const QTextCursor scannedSelection = m_declDefLinkFinder->scannedSelection(); + if (!scannedSelection.isNull() + && scannedSelection.selectionStart() <= pos + && scannedSelection.selectionEnd() >= pos) { + return; + } + + m_updateFunctionDeclDefLinkTimer->start(); +} + +void CPPEditorWidget::updateFunctionDeclDefLinkNow() +{ + if (Core::EditorManager::instance()->currentEditor() != editor()) + return; + if (m_declDefLink) { + // update the change marker + const Utils::ChangeSet changes = m_declDefLink->changes(m_lastSemanticInfo.snapshot); + if (changes.isEmpty()) + m_declDefLink->hideMarker(this); + else + m_declDefLink->showMarker(this); + return; + } + if (!m_lastSemanticInfo.doc || isOutdated()) + return; + + Snapshot snapshot = CppModelManagerInterface::instance()->snapshot(); + snapshot.insert(m_lastSemanticInfo.doc); + + m_declDefLinkFinder->startFindLinkAt(textCursor(), m_lastSemanticInfo.doc, snapshot); +} + +void CPPEditorWidget::onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link) +{ + abortDeclDefLink(); + m_declDefLink = link; +} + +void CPPEditorWidget::applyDeclDefLinkChanges(bool jumpToMatch) +{ + if (!m_declDefLink) + return; + m_declDefLink->apply(this, jumpToMatch); + m_declDefLink.clear(); + updateFunctionDeclDefLink(); +} + +void CPPEditorWidget::abortDeclDefLink() +{ + if (!m_declDefLink) + return; + m_declDefLink->hideMarker(this); + m_declDefLink.clear(); +} + #include "cppeditor.moc" diff --git a/src/plugins/cppeditor/cppeditor.h b/src/plugins/cppeditor/cppeditor.h index a90026fcf40638fa7e005e609cdd903a71bb9fc2..d07e53d99fd5354e62f2934428ac23fb258682ed 100644 --- a/src/plugins/cppeditor/cppeditor.h +++ b/src/plugins/cppeditor/cppeditor.h @@ -35,6 +35,7 @@ #include "cppeditorenums.h" #include "cppsemanticinfo.h" +#include "cppfunctiondecldeflink.h" #include <cplusplus/ModelManagerInterface.h> #include <cplusplus/CppDocument.h> @@ -62,6 +63,7 @@ class Symbol; namespace CppTools { class CppModelManagerInterface; class CppCodeStyleSettings; +class CppRefactoringFile; } namespace TextEditor { @@ -211,7 +213,7 @@ public Q_SLOTS: protected: bool event(QEvent *e); void contextMenuEvent(QContextMenuEvent *); - void keyPressEvent(QKeyEvent *); + void keyPressEvent(QKeyEvent *e); TextEditor::BaseTextEditor *createEditor(); @@ -229,6 +231,9 @@ private Q_SLOTS: void updateOutlineToolTip(); void updateUses(); void updateUsesNow(); + void updateFunctionDeclDefLink(); + void updateFunctionDeclDefLinkNow(); + void onFunctionDeclDefLinkFound(QSharedPointer<FunctionDeclDefLink> link); void onDocumentUpdated(CPlusPlus::Document::Ptr doc); void onContentsChanged(int position, int charsRemoved, int charsAdded); @@ -241,6 +246,8 @@ private Q_SLOTS: void performQuickFix(int index); + void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker); + private: void markSymbols(const QTextCursor &tc, const SemanticInfo &info); bool sortedOutline() const; @@ -261,6 +268,9 @@ private: void finishRename(); void abortRename(); + void applyDeclDefLinkChanges(bool jumpToMatch); + void abortDeclDefLink(); + Link attemptFuncDeclDef(const QTextCursor &cursor, const CPlusPlus::Document::Ptr &doc, CPlusPlus::Snapshot snapshot) const; @@ -283,6 +293,7 @@ private: QTimer *m_updateOutlineTimer; QTimer *m_updateOutlineIndexTimer; QTimer *m_updateUsesTimer; + QTimer *m_updateFunctionDeclDefLinkTimer; QTextCharFormat m_occurrencesFormat; QTextCharFormat m_occurrencesUnusedFormat; QTextCharFormat m_occurrenceRenameFormat; @@ -315,6 +326,9 @@ private: QFutureWatcher<QList<int> > m_referencesWatcher; unsigned m_referencesRevision; int m_referencesCursorPosition; + + FunctionDeclDefLinkFinder *m_declDefLinkFinder; + QSharedPointer<FunctionDeclDefLink> m_declDefLink; }; diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 48e114ab826157f92cbac966964e57e61b20eb79..d90fc0a0523ae2944c38725672cf9a33b17e3df9 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -26,7 +26,8 @@ HEADERS += cppplugin.h \ cppsnippetprovider.h \ cppinsertqtpropertymembers.h \ cppquickfixassistant.h \ - cppquickfix.h + cppquickfix.h \ + cppfunctiondecldeflink.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ @@ -47,7 +48,10 @@ SOURCES += cppplugin.cpp \ cppsnippetprovider.cpp \ cppinsertqtpropertymembers.cpp \ cppquickfixassistant.cpp \ - cppquickfix.cpp + cppquickfix.cpp \ + cppfunctiondecldeflink.cpp RESOURCES += cppeditor.qrc OTHER_FILES += CppEditor.mimetypes.xml + + diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c43916054248b0534f854a2dd2acb97e3a6c78c --- /dev/null +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -0,0 +1,569 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#include "cppfunctiondecldeflink.h" + +#include "cppeditor.h" + +#include <cplusplus/CppRewriter.h> +#include <cplusplus/ASTPath.h> +#include <cplusplus/AST.h> +#include <cplusplus/Symbols.h> +#include <cplusplus/TypeOfExpression.h> +#include <cplusplus/TranslationUnit.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Overview.h> +#include <texteditor/refactoroverlay.h> +#include <texteditor/tooltip/tooltip.h> +#include <texteditor/tooltip/tipcontents.h> +#include <utils/qtcassert.h> + +#include <QtCore/QtConcurrentRun> + +using namespace CPlusPlus; +using namespace CppEditor; +using namespace CppEditor::Internal; + +FunctionDeclDefLinkFinder::FunctionDeclDefLinkFinder(QObject *parent) + : QObject(parent) +{ + connect(&m_watcher, SIGNAL(finished()), + this, SLOT(onFutureDone())); +} + +void FunctionDeclDefLinkFinder::onFutureDone() +{ + QSharedPointer<FunctionDeclDefLink> link = m_watcher.result(); + if (link) + link->linkSelection = m_scannedSelection; + m_scannedSelection = QTextCursor(); + if (link) + emit foundLink(link); +} + +QTextCursor FunctionDeclDefLinkFinder::scannedSelection() const +{ + return m_scannedSelection; +} + +// parent is either a FunctionDefinitionAST or a SimpleDeclarationAST +static bool findDeclOrDef(const Document::Ptr &doc, const QTextCursor &cursor, + DeclarationAST **parent, FunctionDeclaratorAST **funcDecl) +{ + QList<AST *> path = ASTPath(doc)(cursor); + + // for function definitions, simply scan for FunctionDefinitionAST not preceded + // by CompoundStatement/CtorInitializer + // for function declarations, look for SimpleDeclarations with a single Declarator + // with a FunctionDeclarator postfix + FunctionDefinitionAST *funcDef = 0; + SimpleDeclarationAST *simpleDecl = 0; + DeclaratorAST *decl = 0; + for (int i = path.size() - 1; i > 0; --i) { + AST *ast = path.at(i); + if (ast->asCompoundStatement() || ast->asCtorInitializer()) + break; + if ((funcDef = ast->asFunctionDefinition()) != 0) { + *parent = funcDef; + decl = funcDef->declarator; + break; + } + if ((simpleDecl = ast->asSimpleDeclaration()) != 0) { + *parent = simpleDecl; + if (!simpleDecl->declarator_list || !simpleDecl->declarator_list->value) + break; + decl = simpleDecl->declarator_list->value; + break; + } + } + if (!*parent || !decl) + return false; + if (!decl->postfix_declarator_list || !decl->postfix_declarator_list->value) + return false; + *funcDecl = decl->postfix_declarator_list->value->asFunctionDeclarator(); + return *funcDecl; +} + +static void declDefLinkStartEnd(const CppTools::CppRefactoringFile &file, + DeclarationAST *parent, FunctionDeclaratorAST *funcDecl, + int *start, int *end) +{ + *start = file.startOf(parent); + if (funcDecl->trailing_return_type) + *end = file.endOf(funcDecl->trailing_return_type); + else if (funcDecl->exception_specification) + *end = file.endOf(funcDecl->exception_specification); + else if (funcDecl->cv_qualifier_list) + *end = file.endOf(funcDecl->cv_qualifier_list->lastValue()); + else + *end = file.endOf(funcDecl->rparen_token); +} + +static QSharedPointer<FunctionDeclDefLink> findLinkHelper(QSharedPointer<FunctionDeclDefLink> link, CppTools::CppRefactoringChanges changes) +{ + QSharedPointer<FunctionDeclDefLink> noResult; + const Snapshot &snapshot = changes.snapshot(); + + // find the matching decl/def symbol + Symbol *target = 0; + if (FunctionDefinitionAST *funcDef = link->sourceDeclaration->asFunctionDefinition()) { + QList<Declaration *> nameMatch, argumentCountMatch, typeMatch; + findMatchingDeclaration(LookupContext(link->sourceDocument, snapshot), + funcDef->symbol, + &typeMatch, &argumentCountMatch, &nameMatch); + if (!typeMatch.isEmpty()) + target = typeMatch.first(); + } else if (link->sourceDeclaration->asSimpleDeclaration()) { + target = snapshot.findMatchingDefinition(link->sourceFunctionDeclarator->symbol, true); + } + if (!target) { + return noResult; + } + + // parse the target file to get the linked decl/def + const QString targetFileName = QString::fromUtf8( + target->fileName(), target->fileNameLength()); + CppTools::CppRefactoringFile targetFile = changes.file(targetFileName); + if (!targetFile.isValid()) + return noResult; + + QTextCursor tc(targetFile.cursor()); + tc.setPosition(targetFile.position(target->line(), target->column())); + DeclarationAST *targetParent = 0; + FunctionDeclaratorAST *targetFuncDecl = 0; + if (!findDeclOrDef(targetFile.cppDocument(), tc, &targetParent, &targetFuncDecl)) + return noResult; + + // the parens are necessary for finding good places for changes + if (!targetFuncDecl->lparen_token || !targetFuncDecl->rparen_token) + return noResult; + + int targetStart, targetEnd; + declDefLinkStartEnd(targetFile, targetParent, targetFuncDecl, &targetStart, &targetEnd); + QString targetInitial = targetFile.textOf( + targetFile.startOf(targetParent), + targetEnd); + + link->targetOffset = targetStart; + link->targetInitial = targetInitial; + + link->targetFile = QSharedPointer<CppTools::CppRefactoringFile>( + new CppTools::CppRefactoringFile(targetFile.document()->clone(), targetFile.fileName())); + link->targetFile->setCppDocument(targetFile.cppDocument()); + link->targetFunction = targetFuncDecl->symbol; + link->targetFunctionDeclarator = targetFuncDecl; + link->targetDeclaration = targetParent; + + return link; +} + +void FunctionDeclDefLinkFinder::startFindLinkAt( + QTextCursor cursor, const Document::Ptr &doc, const Snapshot &snapshot) +{ + // check if cursor is on function decl/def + DeclarationAST *parent = 0; + FunctionDeclaratorAST *funcDecl = 0; + if (!findDeclOrDef(doc, cursor, &parent, &funcDecl)) + return; + + // find the start/end offsets + CppTools::CppRefactoringChanges refactoringChanges(snapshot); + CppTools::CppRefactoringFile sourceFile = refactoringChanges.file(doc->fileName()); + sourceFile.setCppDocument(doc); + int start, end; + declDefLinkStartEnd(sourceFile, parent, funcDecl, &start, &end); + + // if already scanning, don't scan again + if (!m_scannedSelection.isNull() + && m_scannedSelection.selectionStart() == start + && m_scannedSelection.selectionEnd() == end) { + return; + } + + // build the selection for the currently scanned area + m_scannedSelection = cursor; + m_scannedSelection.setPosition(end); + m_scannedSelection.setPosition(start, QTextCursor::KeepAnchor); + m_scannedSelection.setKeepPositionOnInsert(true); + + // set up a base result + QSharedPointer<FunctionDeclDefLink> result(new FunctionDeclDefLink); + result->sourceDocument = doc; + result->sourceFunction = funcDecl->symbol; + result->sourceDeclaration = parent; + result->sourceFunctionDeclarator = funcDecl; + + // handle the rest in a thread + m_watcher.setFuture(QtConcurrent::run(&findLinkHelper, result, refactoringChanges)); +} + +FunctionDeclDefLink::FunctionDeclDefLink() +{ + hasMarker = false; + targetOffset = 0; + sourceFunction = 0; + targetFunction = 0; + targetDeclaration = 0; + targetFunctionDeclarator = 0; +} + +FunctionDeclDefLink::~FunctionDeclDefLink() +{ +} + +bool FunctionDeclDefLink::isValid() const +{ + return targetFile; +} + +bool FunctionDeclDefLink::isMarkerVisible() const +{ + return hasMarker; +} + +static bool namesEqual(const Name *n1, const Name *n2) +{ + return n1 == n2 || (n1 && n2 && n1->isEqualTo(n2)); +} + +static DeclaratorIdAST *getDeclaratorId(DeclaratorAST *declarator) +{ + if (!declarator || !declarator->core_declarator) + return 0; + if (DeclaratorIdAST *id = declarator->core_declarator->asDeclaratorId()) { + return id; + } + if (NestedDeclaratorAST *nested = declarator->core_declarator->asNestedDeclarator()) { + return getDeclaratorId(nested->declarator); + } + return 0; +} + +void FunctionDeclDefLink::apply(CPPEditorWidget *editor, bool jumpToMatch) +{ + Snapshot snapshot = editor->semanticInfo().snapshot; + + // first verify the interesting region of the target file is unchanged + CppTools::CppRefactoringChanges refactoringChanges(snapshot); + CppTools::CppRefactoringFile newTargetFile = refactoringChanges.file(targetFile->fileName()); + if (!newTargetFile.isValid()) + return; + const int targetEnd = targetOffset + targetInitial.size(); + if (targetInitial == newTargetFile.textOf(targetOffset, targetEnd)) { + const Utils::ChangeSet changeset = changes(snapshot); + newTargetFile.change(changeset, jumpToMatch); + if (jumpToMatch) { + QTextCursor tc = newTargetFile.cursor(); + tc.setPosition(targetOffset + targetInitial.size()); + refactoringChanges.activateEditor(newTargetFile.fileName(), tc.blockNumber(), tc.columnNumber()); + } + } else { + TextEditor::ToolTip::instance()->show( + editor->toolTipPosition(linkSelection), + TextEditor::TextContent( + tr("Target file was changed, could not apply changes"))); + } + + hideMarker(editor); +} + +template <class T> +static QList<TextEditor::RefactorMarker> removeMarkersOfType(const QList<TextEditor::RefactorMarker> &markers) +{ + QList<TextEditor::RefactorMarker> result; + foreach (const TextEditor::RefactorMarker &marker, markers) { + if (!marker.data.canConvert<T>()) + result += marker; + } + return result; +} + +void FunctionDeclDefLink::hideMarker(CPPEditorWidget *editor) +{ + if (!hasMarker) + return; + editor->setRefactorMarkers( + removeMarkersOfType<Marker>(editor->refactorMarkers())); + hasMarker = false; +} + +void FunctionDeclDefLink::showMarker(CPPEditorWidget *editor) +{ + if (hasMarker) + return; + + QList<TextEditor::RefactorMarker> markers = removeMarkersOfType<Marker>(editor->refactorMarkers()); + TextEditor::RefactorMarker marker; + + // show the marker at the end of the linked area, with a special case + // to avoid it overlapping with a trailing semicolon + marker.cursor = editor->textCursor(); + marker.cursor.setPosition(linkSelection.selectionEnd()); + const int endBlockNr = marker.cursor.blockNumber(); + marker.cursor.setPosition(linkSelection.selectionEnd() + 1, QTextCursor::KeepAnchor); + if (marker.cursor.blockNumber() != endBlockNr + || marker.cursor.selectedText() != QLatin1String(";")) { + marker.cursor.setPosition(linkSelection.selectionEnd()); + } + + QString message; + if (targetDeclaration->asFunctionDefinition()) + message = tr("Apply changes to definition"); + else + message = tr("Apply changes to declaration"); + marker.tooltip = message; + marker.data = QVariant::fromValue(Marker()); + markers += marker; + editor->setRefactorMarkers(markers); + + hasMarker = true; +} + +Utils::ChangeSet FunctionDeclDefLink::changes(const Snapshot &snapshot) +{ + Utils::ChangeSet changes; + + QSharedPointer<CppTools::CppRefactoringFile> matchFile = targetFile; + + // parse the current source declaration + TypeOfExpression typeOfExpression; // ### just need to preprocess... + typeOfExpression.init(sourceDocument, snapshot); + const QString newDecl = typeOfExpression.preprocess( + linkSelection.selectedText()) + QLatin1String("{}"); + Document::Ptr newDeclDoc = Document::create(QLatin1String("<decl>")); + newDeclDoc->setSource(newDecl.toUtf8()); + newDeclDoc->parse(Document::ParseDeclaration); + newDeclDoc->check(); + + // extract the function symbol + if (!newDeclDoc->translationUnit()->ast()) { + return changes; + } + FunctionDefinitionAST *newDef = newDeclDoc->translationUnit()->ast()->asFunctionDefinition(); + if (!newDef) { + return changes; + } + Function *newFunction = newDef->symbol; + QTC_ASSERT(newFunction, return changes); // check() should always create this symbol + + LookupContext sourceContext(sourceDocument, snapshot); + LookupContext targetContext(targetFile->cppDocument(), snapshot); + + Overview overview; + overview.setShowReturnTypes(true); + overview.setShowTemplateParameters(true); + overview.setShowArgumentNames(true); + overview.setShowFunctionSignatures(true); + + // sync return type + { + // set up for rewriting return type + SubstitutionEnvironment env; + env.setContext(sourceContext); + env.switchScope(sourceFunction->enclosingScope()); + ClassOrNamespace *targetCoN = targetContext.lookupType(targetFunction->enclosingScope()); + if (!targetCoN) + targetCoN = targetContext.globalNamespace(); + UseMinimalNames q(targetCoN); + env.enter(&q); + Control *control = sourceContext.control().data(); + + // get return type start position and declarator info from declaration + int returnTypeStart = 0; + DeclaratorAST *declarator = 0; + if (SimpleDeclarationAST *simple = targetDeclaration->asSimpleDeclaration()) { + declarator = simple->declarator_list->value; + if (simple->decl_specifier_list) + returnTypeStart = matchFile->startOf(simple->decl_specifier_list->value); + else + returnTypeStart = matchFile->startOf(declarator); + } else if (FunctionDefinitionAST *def = targetDeclaration->asFunctionDefinition()) { + declarator = def->declarator; + if (def->decl_specifier_list) + returnTypeStart = matchFile->startOf(def->decl_specifier_list->value); + else + returnTypeStart = matchFile->startOf(declarator); + } + + if (!newFunction->returnType().isEqualTo(sourceFunction->returnType()) + && !newFunction->returnType().isEqualTo(targetFunction->returnType())) { + FullySpecifiedType type = rewriteType(newFunction->returnType(), &env, control); + const QString replacement = overview(type, targetFunction->name()); + changes.replace(returnTypeStart, + matchFile->startOf(targetFunctionDeclarator->lparen_token), + replacement); + } + } + + // sync parameters + { + // set up for rewriting parameter types + SubstitutionEnvironment env; + env.setContext(sourceContext); + env.switchScope(sourceFunction); + ClassOrNamespace *targetCoN = targetContext.lookupType(targetFunction); + if (!targetCoN) + targetCoN = targetContext.globalNamespace(); + UseMinimalNames q(targetCoN); + env.enter(&q); + Control *control = sourceContext.control().data(); + Overview overview; + + // check if parameter types or names have changed + const unsigned existingArgs = qMin(targetFunction->argumentCount(), newFunction->argumentCount()); + ParameterDeclarationClauseAST *targetParameterDecl = + targetFunctionDeclarator->parameter_declaration_clause; + ParameterDeclarationListAST *firstTargetParameterDeclIt = + targetParameterDecl ? targetParameterDecl->parameter_declaration_list : 0; + ParameterDeclarationListAST *targetParameterDeclIt = firstTargetParameterDeclIt; + for (unsigned i = 0; + i < existingArgs && targetParameterDeclIt; + ++i, targetParameterDeclIt = targetParameterDeclIt->next) { + Symbol *targetParam = targetFunction->argumentAt(i); + Symbol *newParam = newFunction->argumentAt(i); + + // if new's name and type are the same as source's, forbid changes + bool allowChangeType = true; + const Name *replacementName = newParam->name(); + if (i < sourceFunction->argumentCount()) { + Symbol *sourceParam = sourceFunction->argumentAt(i); + if (newParam->type().isEqualTo(sourceParam->type())) + allowChangeType = false; + if (namesEqual(replacementName, sourceParam->name())) + replacementName = targetParam->name(); + } + + ParameterDeclarationAST *targetParamAst = targetParameterDeclIt->value; + int parameterNameEnd = 0; + if (targetParamAst->declarator) + parameterNameEnd = matchFile->endOf(targetParamAst->declarator); + else if (targetParamAst->type_specifier_list) + parameterNameEnd = matchFile->endOf(targetParamAst->type_specifier_list->lastToken() - 1); + else + parameterNameEnd = matchFile->startOf(targetParamAst); + + if (allowChangeType + && !targetParam->type().isEqualTo(newParam->type())) { + FullySpecifiedType replacementType = rewriteType(newParam->type(), &env, control); + const QString replacement = overview(replacementType, replacementName); + + changes.replace(matchFile->startOf(targetParamAst), + parameterNameEnd, + replacement); + } else if (!namesEqual(targetParam->name(), replacementName)) { + DeclaratorIdAST *id = getDeclaratorId(targetParamAst->declarator); + QString replacementNameStr = overview(replacementName); + if (id) { + changes.replace(matchFile->range(id), replacementNameStr); + } else { + // add name to unnamed parameter + replacementNameStr.prepend(QLatin1Char(' ')); + int end; + if (targetParamAst->equal_token) { + end = matchFile->startOf(targetParamAst->equal_token); + replacementNameStr.append(QLatin1Char(' ')); + } else { + // one past end on purpose + end = matchFile->startOf(targetParamAst->lastToken()); + } + changes.replace(parameterNameEnd, end, replacementNameStr); + } + } + } + if (newFunction->argumentCount() < targetFunction->argumentCount()) { + targetParameterDeclIt = firstTargetParameterDeclIt; + if (targetParameterDeclIt) { + if (newFunction->argumentCount() == 0) { + changes.remove(matchFile->startOf(targetParameterDeclIt->firstToken()), + matchFile->endOf(targetParameterDeclIt->lastToken() - 1)); + } else { + // get the last valid argument + for (unsigned i = 0; i < newFunction->argumentCount() - 1 && targetParameterDeclIt; ++i) + targetParameterDeclIt = targetParameterDeclIt->next; + if (targetParameterDeclIt) { + const int start = matchFile->endOf(targetParameterDeclIt->value); + const int end = matchFile->endOf(targetParameterDecl->lastToken() - 1); + changes.remove(start, end); + } + } + } + } else if (newFunction->argumentCount() > targetFunction->argumentCount()) { + QString newParams; + for (unsigned i = targetFunction->argumentCount(); i < newFunction->argumentCount(); ++i) { + Symbol *param = newFunction->argumentAt(i); + FullySpecifiedType type = rewriteType(param->type(), &env, control); + if (i != 0) + newParams += QLatin1String(", "); + newParams += overview(type, param->name()); + } + targetParameterDeclIt = firstTargetParameterDeclIt; + if (targetParameterDeclIt) { + while (targetParameterDeclIt->next) + targetParameterDeclIt = targetParameterDeclIt->next; + changes.insert(matchFile->endOf(targetParameterDeclIt->value), newParams); + } else { + changes.insert(matchFile->endOf(targetFunctionDeclarator->lparen_token), newParams); + } + } + } + + // sync cv qualification + if (targetFunction->isConst() != newFunction->isConst() + || targetFunction->isVolatile() != newFunction->isVolatile()) { + QString cvString; + if (newFunction->isConst()) + cvString += QLatin1String("const"); + if (newFunction->isVolatile()) { + if (!cvString.isEmpty()) + cvString += QLatin1Char(' '); + cvString += QLatin1String("volatile"); + } + const int rparenEnd = matchFile->endOf(targetFunctionDeclarator->rparen_token); + if (targetFunctionDeclarator->cv_qualifier_list) { + const int cvEnd = matchFile->endOf(targetFunctionDeclarator->cv_qualifier_list->lastToken() - 1); + // if the qualifies changed, replace + if (!cvString.isEmpty()) { + changes.replace(matchFile->startOf(targetFunctionDeclarator->cv_qualifier_list->firstToken()), + cvEnd, cvString); + } else { + // remove + changes.remove(rparenEnd, cvEnd); + } + } else { + // otherwise add + cvString.prepend(QLatin1Char(' ')); + changes.insert(rparenEnd, cvString); + } + } + + return changes; +} diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.h b/src/plugins/cppeditor/cppfunctiondecldeflink.h new file mode 100644 index 0000000000000000000000000000000000000000..5de47afb918000c9aa555b7ff2cb615fe7023c25 --- /dev/null +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.h @@ -0,0 +1,121 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef CPPFUNCTIONDECLDEFLINK_H +#define CPPFUNCTIONDECLDEFLINK_H + +#include <cplusplus/CppDocument.h> +#include <cplusplus/ASTfwd.h> + +#include <cpptools/cpprefactoringchanges.h> +#include <utils/changeset.h> + +#include <QtCore/QString> +#include <QtCore/QCoreApplication> +#include <QtCore/QSharedPointer> +#include <QtCore/QFutureWatcher> +#include <QtGui/QTextCursor> + +namespace CppEditor { +namespace Internal { + +class CPPEditorWidget; +class FunctionDeclDefLink; + +class FunctionDeclDefLinkFinder : public QObject +{ + Q_OBJECT +public: + FunctionDeclDefLinkFinder(QObject *parent = 0); + + void startFindLinkAt(QTextCursor cursor, + const CPlusPlus::Document::Ptr &doc, + const CPlusPlus::Snapshot &snapshot); + + QTextCursor scannedSelection() const; + +signals: + void foundLink(QSharedPointer<FunctionDeclDefLink> link); + +private slots: + void onFutureDone(); + +private: + QTextCursor m_scannedSelection; + QFutureWatcher<QSharedPointer<FunctionDeclDefLink> > m_watcher; +}; + +class FunctionDeclDefLink +{ + Q_DECLARE_TR_FUNCTIONS(FunctionDeclDefLink) + Q_DISABLE_COPY(FunctionDeclDefLink) +public: + ~FunctionDeclDefLink(); + + class Marker {}; + + bool isValid() const; + bool isMarkerVisible() const; + + void apply(CPPEditorWidget *editor, bool jumpToMatch); + void hideMarker(CPPEditorWidget *editor); + void showMarker(CPPEditorWidget *editor); + Utils::ChangeSet changes(const CPlusPlus::Snapshot &snapshot); + + QTextCursor linkSelection; + int targetOffset; + QString targetInitial; + + CPlusPlus::Document::Ptr sourceDocument; + CPlusPlus::Function *sourceFunction; + CPlusPlus::DeclarationAST *sourceDeclaration; + CPlusPlus::FunctionDeclaratorAST *sourceFunctionDeclarator; + + QSharedPointer<CppTools::CppRefactoringFile> targetFile; + CPlusPlus::Function *targetFunction; + CPlusPlus::DeclarationAST *targetDeclaration; + CPlusPlus::FunctionDeclaratorAST *targetFunctionDeclarator; + +private: + FunctionDeclDefLink(); + + bool hasMarker; + + friend class FunctionDeclDefLinkFinder; +}; + +} // namespace Internal +} // namespace CppEditor + +Q_DECLARE_METATYPE(CppEditor::Internal::FunctionDeclDefLink::Marker) + +#endif // CPPFUNCTIONDECLDEFLINK_H diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index 9b998c2db37287cd966b3c5f147a7908de951ddb..dccf0af80970213ad025c5f46d36a8ada265c1f3 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -138,7 +138,9 @@ CPPEditorWidget *CppQuickFixAssistInterface::editor() const const CppRefactoringFile CppQuickFixAssistInterface::currentFile() const { - return CppRefactoringFile(m_editor, m_semanticInfo.doc); + CppRefactoringFile file(m_editor); + file.setCppDocument(m_semanticInfo.doc); + return file; } bool CppQuickFixAssistInterface::isCursorOn(unsigned tokenIndex) const diff --git a/src/plugins/cpptools/cpprefactoringchanges.cpp b/src/plugins/cpptools/cpprefactoringchanges.cpp index 53883128869e11b7e5982cd98739d536b3e38903..c92aa862d209979a610cabdad17956ba493a6053 100644 --- a/src/plugins/cpptools/cpprefactoringchanges.cpp +++ b/src/plugins/cpptools/cpprefactoringchanges.cpp @@ -110,13 +110,13 @@ CppRefactoringFile::CppRefactoringFile(const QString &fileName, CppRefactoringCh m_cppDocument = snapshot.document(fileName); } -CppRefactoringFile::CppRefactoringFile(TextEditor::BaseTextEditorWidget *editor, CPlusPlus::Document::Ptr document) - : RefactoringFile() - , m_cppDocument(document) -{ - m_fileName = document->fileName(); - m_editor = editor; -} +CppRefactoringFile::CppRefactoringFile(QTextDocument *document, const QString &fileName) + : RefactoringFile(document, fileName) +{ } + +CppRefactoringFile::CppRefactoringFile(TextEditor::BaseTextEditorWidget *editor) + : RefactoringFile(editor) +{ } Document::Ptr CppRefactoringFile::cppDocument() const { @@ -134,6 +134,11 @@ Document::Ptr CppRefactoringFile::cppDocument() const return m_cppDocument; } +void CppRefactoringFile::setCppDocument(Document::Ptr document) +{ + m_cppDocument = document; +} + Scope *CppRefactoringFile::scopeAt(unsigned index) const { unsigned line, column; diff --git a/src/plugins/cpptools/cpprefactoringchanges.h b/src/plugins/cpptools/cpprefactoringchanges.h index 1e0ea6a898e32b7f3f72f0492883ef59f718754c..c32e4e5c99c2cdf5d0c63f9af874e2002a46ce1f 100644 --- a/src/plugins/cpptools/cpprefactoringchanges.h +++ b/src/plugins/cpptools/cpprefactoringchanges.h @@ -50,10 +50,11 @@ class CPPTOOLS_EXPORT CppRefactoringFile: public TextEditor::RefactoringFile { public: CppRefactoringFile(); - CppRefactoringFile(const QString &fileName, CppRefactoringChanges *refactoringChanges); - CppRefactoringFile(TextEditor::BaseTextEditorWidget *editor, CPlusPlus::Document::Ptr document); + CppRefactoringFile(QTextDocument *document, const QString &fileName = QString()); + CppRefactoringFile(TextEditor::BaseTextEditorWidget *editor); CPlusPlus::Document::Ptr cppDocument() const; + void setCppDocument(CPlusPlus::Document::Ptr document); CPlusPlus::Scope *scopeAt(unsigned index) const; @@ -76,10 +77,15 @@ public: using TextEditor::RefactoringFile::textOf; QString textOf(const CPlusPlus::AST *ast) const; +protected: + CppRefactoringFile(const QString &fileName, CppRefactoringChanges *refactoringChanges); + private: CppRefactoringChanges *refactoringChanges() const; mutable CPlusPlus::Document::Ptr m_cppDocument; + + friend class CppRefactoringChanges; // for access to constructor }; class CPPTOOLS_EXPORT CppRefactoringChanges: public TextEditor::RefactoringChanges diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index f5b44539077d479c165cb908da58d8bfe3a48c3c..60d5fb0862808c65f8515441d310fe8dee6cee25 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -186,6 +186,22 @@ RefactoringFile::RefactoringFile() , m_openEditor(false) { } +RefactoringFile::RefactoringFile(QTextDocument *document, const QString &fileName) + : m_fileName(fileName) + , m_refactoringChanges(0) + , m_document(document) + , m_editor(0) + , m_openEditor(false) +{ } + +RefactoringFile::RefactoringFile(BaseTextEditorWidget *editor) + : m_fileName(editor->file()->fileName()) + , m_refactoringChanges(0) + , m_document(0) + , m_editor(editor) + , m_openEditor(false) +{ } + RefactoringFile::RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges) : m_fileName(fileName) , m_refactoringChanges(refactoringChanges) diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h index f291d390c191a06c784a67e47ba2e75575470e56..6dae8c57d8b85649d312119bdb1bc0b9955a0ccf 100644 --- a/src/plugins/texteditor/refactoringchanges.h +++ b/src/plugins/texteditor/refactoringchanges.h @@ -52,7 +52,9 @@ public: public: RefactoringFile(); - RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges); + // takes ownership of document + RefactoringFile(QTextDocument *document, const QString &fileName = QString()); + RefactoringFile(BaseTextEditorWidget *editor); RefactoringFile(const RefactoringFile &other); virtual ~RefactoringFile(); @@ -76,6 +78,7 @@ protected: // not assignable //const RefactoringFile &operator=(const RefactoringFile &other); + RefactoringFile(const QString &fileName, RefactoringChanges *refactoringChanges); QTextDocument *mutableDocument() const; protected: @@ -86,6 +89,8 @@ protected: Utils::ChangeSet m_changes; QList<Range> m_indentRanges; bool m_openEditor; + + friend class RefactoringChanges; // access to constructor }; /*!