diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 7d9b6dcce907d3bfbc05014c6b144825757c8e77..4d365ae08336a6686da0f05c662ce2aefe78f23f 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -138,6 +138,43 @@ QList<const Name *> LookupContext::fullyQualifiedName(Symbol *symbol) return names; } +const Name *LookupContext::minimalName(const Name *name, + Scope *source, + ClassOrNamespace *target) const +{ + Q_ASSERT(name); + Q_ASSERT(source); + Q_ASSERT(target); + + QList<Symbol *> symbols = lookup(name, source); + if (symbols.isEmpty()) + return 0; + + Symbol *canonicalSymbol = symbols.first(); + std::vector<const Name *> fqNames = fullyQualifiedName(canonicalSymbol).toVector().toStdVector(); + if (const QualifiedNameId *qId = name->asQualifiedNameId()) + fqNames.push_back(qId->nameAt(qId->nameCount() - 1)); + else + fqNames.push_back(name); + + const QualifiedNameId *lastWorking = 0; + for (unsigned i = 0; i < fqNames.size(); ++i) { + const QualifiedNameId *newName = control()->qualifiedNameId(&fqNames[i], + fqNames.size() - i); + QList<Symbol *> candidates = target->lookup(newName); + if (candidates.contains(canonicalSymbol)) + lastWorking = newName; + else + break; + } + + if (lastWorking && lastWorking->nameCount() == 1) + return lastWorking->nameAt(0); + else + return lastWorking; +} + + QSharedPointer<CreateBindings> LookupContext::bindings() const { if (! _bindings) @@ -280,6 +317,19 @@ QList<Symbol *> LookupContext::lookup(const Name *name, Scope *scope) const return candidates; } +ClassOrNamespace *LookupContext::lookupParent(Symbol *symbol) const +{ + QList<const Name *> fqName = fullyQualifiedName(symbol); + ClassOrNamespace *binding = globalNamespace(); + foreach (const Name *name, fqName) { + binding = binding->findType(name); + if (!binding) + return 0; + } + + return binding; +} + ClassOrNamespace::ClassOrNamespace(CreateBindings *factory, ClassOrNamespace *parent) : _factory(factory), _parent(parent), _templateId(0) { diff --git a/src/libs/cplusplus/LookupContext.h b/src/libs/cplusplus/LookupContext.h index 73893a6670105106b236325c16b114a337a4bef6..82f180592ce0bf000e6d16717a53156599cdfd34 100644 --- a/src/libs/cplusplus/LookupContext.h +++ b/src/libs/cplusplus/LookupContext.h @@ -216,6 +216,7 @@ public: QList<Symbol *> lookup(const Name *name, Scope *scope) const; ClassOrNamespace *lookupType(const Name *name, Scope *scope) const; ClassOrNamespace *lookupType(Symbol *symbol) const; + ClassOrNamespace *lookupParent(Symbol *symbol) const; /// \internal QSharedPointer<CreateBindings> bindings() const; @@ -227,6 +228,9 @@ public: static QList<const Name *> fullyQualifiedName(Symbol *symbol); + const Name *minimalName(const Name *name, Scope *source, + ClassOrNamespace *target) const; + private: // The current expression. Document::Ptr _expressionDocument; diff --git a/src/libs/cplusplus/Overview.cpp b/src/libs/cplusplus/Overview.cpp index 37ec7d7e1db764bba517d263648584c89ac58281..48cdb009b75a0887477330de0ececb489373ec0e 100644 --- a/src/libs/cplusplus/Overview.cpp +++ b/src/libs/cplusplus/Overview.cpp @@ -30,6 +30,9 @@ #include "Overview.h" #include "NamePrettyPrinter.h" #include "TypePrettyPrinter.h" + +#include <Control.h> +#include <CoreTypes.h> #include <FullySpecifiedType.h> using namespace CPlusPlus; @@ -145,3 +148,23 @@ QString Overview::prettyType(const FullySpecifiedType &ty, TypePrettyPrinter pp(this); return pp(ty, name); } + +/** + * Pretty-prints the given fully-specified type, but replacing the contained + * type with the given name. + * + * \param type the fully-specified type to convert to string + * \param the name for the named-type to use + * \param the control where the name is registered + * \return the pretty-printed name + */ +QString Overview::prettyTypeWithName(const FullySpecifiedType &type, + const Name *name, + Control *control) +{ + NamedType *namedTy = control->namedType(name); + FullySpecifiedType newTy = type; + newTy.setType(namedTy); + + return prettyType(newTy); +} diff --git a/src/libs/cplusplus/Overview.h b/src/libs/cplusplus/Overview.h index 9cebefed01f456c81bab1a52a9fddb4c6f3edd03..7471f306a12d0903cec113bcfcfdbb14effd1294 100644 --- a/src/libs/cplusplus/Overview.h +++ b/src/libs/cplusplus/Overview.h @@ -79,6 +79,10 @@ public: QString prettyType(const FullySpecifiedType &type, const Name *name = 0) const; QString prettyType(const FullySpecifiedType &type, const QString &name) const; + QString prettyTypeWithName(const FullySpecifiedType &type, + const Name *name, + Control *control); + private: unsigned _markedArgument; int _markedArgumentBegin; diff --git a/src/plugins/cppeditor/cppdeclfromdef.cpp b/src/plugins/cppeditor/cppdeclfromdef.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3c32d6f1d6fdda569b6bc95010248e10de787c0 --- /dev/null +++ b/src/plugins/cppeditor/cppdeclfromdef.cpp @@ -0,0 +1,249 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "cppdeclfromdef.h" + +#include <Literals.h> //### remove +#include <QDebug> //###remove + +#include <AST.h> +#include <ASTVisitor.h> +#include <CoreTypes.h> +#include <Names.h> +#include <Symbols.h> +#include <TranslationUnit.h> +#include <cplusplus/ASTPath.h> +#include <cplusplus/LookupContext.h> +#include <cplusplus/Overview.h> + +#include <QtCore/QCoreApplication> + +using namespace CPlusPlus; +using namespace CppEditor::Internal; +using namespace CppTools; + +using CppEditor::CppRefactoringChanges; + +namespace { + +class InsertionPointFinder: public ASTVisitor +{ +public: + InsertionPointFinder(Document::Ptr doc, const QString &className) + : ASTVisitor(doc->translationUnit()) + , _doc(doc) + , _className(className) + {} + + void operator()(int *line, int *column) + { + if (!line && !column) + return; + _line = 0; + _column = 0; + + AST *ast = translationUnit()->ast(); + accept(ast); + + if (line) + *line = _line - 1; + if (column) + *column = _column - 1; + } + +protected: + using ASTVisitor::visit; + + bool visit(ClassSpecifierAST *ast) + { + if (!ast->symbol || _className != QLatin1String(ast->symbol->identifier()->chars())) + return true; + + unsigned currentVisibility = (tokenKind(ast->classkey_token) == T_CLASS) ? T_PUBLIC : T_PRIVATE; + unsigned insertBefore = 0; + + for (DeclarationListAST *iter = ast->member_specifier_list; iter; iter = iter->next) { + DeclarationAST *decl = iter->value; + if (AccessDeclarationAST *xsDecl = decl->asAccessDeclaration()) { + const unsigned token = xsDecl->access_specifier_token; + const int kind = tokenKind(token); + if (kind == T_PUBLIC) { + currentVisibility = T_PUBLIC; + } else if (kind == T_PROTECTED) { + if (currentVisibility == T_PUBLIC) { + insertBefore = token; + break; + } else { + currentVisibility = T_PROTECTED; + } + } else if (kind == T_PRIVATE) { + if (currentVisibility == T_PUBLIC + || currentVisibility == T_PROTECTED) { + insertBefore = token; + break; + } else { + currentVisibility = T_PRIVATE; + } + } + } + } + + if (!insertBefore) + insertBefore = ast->rbrace_token; + + getTokenStartPosition(insertBefore, &_line, &_column); + + return false; + } + +private: + Document::Ptr _doc; + QString _className; + + unsigned _line; + unsigned _column; +}; + +QString prettyMinimalType(const FullySpecifiedType &ty, + const LookupContext &context, + Scope *source, + ClassOrNamespace *target) +{ + Overview oo; + + if (const NamedType *namedTy = ty->asNamedType()) + return oo.prettyTypeWithName(ty, context.minimalName(namedTy->name(), + source, + target), + context.control().data()); + else + return oo(ty); +} + +} // anonymous namespace + +DeclFromDef::DeclFromDef(TextEditor::BaseTextEditor *editor) + : CppQuickFixOperation(editor) +{} + +QString DeclFromDef::description() const +{ + return QCoreApplication::tr("Create Declaration from Definition", "DeclFromDef"); +} + +int DeclFromDef::match(const QList<CPlusPlus::AST *> &path) +{ + m_targetFileName.clear(); + m_targetSymbolName.clear(); + m_decl.clear(); + + FunctionDefinitionAST *funDef = 0; + int idx = 0; + for (; idx < path.size(); ++idx) { + AST *node = path.at(idx); + if (FunctionDefinitionAST *candidate = node->asFunctionDefinition()) + if (!funDef) + funDef = candidate; + else if (node->asClassSpecifier()) + return -1; + } + + if (!funDef || !funDef->symbol) + return -1; + + Function *method = funDef->symbol; + LookupContext context(document(), snapshot()); + + if (ClassOrNamespace *targetBinding = context.lookupParent(method)) { + foreach (Symbol *s, targetBinding->symbols()) { + if (Class *clazz = s->asClass()) { + m_targetFileName = QLatin1String(clazz->fileName()); + m_targetSymbolName = QLatin1String(clazz->identifier()->chars()); + + m_decl = generateDeclaration(method, targetBinding); + + return idx; + } // ### TODO: support insertion into namespaces + } + } + + return -1; +} + +void DeclFromDef::createChanges() +{ + CppRefactoringChanges *changes = refactoringChanges(); + + Document::Ptr targetDoc = changes->document(m_targetFileName); + InsertionPointFinder findInsertionPoint(targetDoc, m_targetSymbolName); + int line = 0, column = 0; + findInsertionPoint(&line, &column); + + int targetPosition1 = changes->positionInFile(m_targetFileName, line, column); + int targetPosition2 = changes->positionInFile(m_targetFileName, line + 1, 0) - 1; + + Utils::ChangeSet target; + target.insert(targetPosition1, m_decl); + changes->changeFile(m_targetFileName, target); + + changes->reindent(m_targetFileName, + Utils::ChangeSet::Range(targetPosition1, targetPosition2)); + + changes->openEditor(m_targetFileName, line, column); +} + +QString DeclFromDef::generateDeclaration(Function *method, ClassOrNamespace *targetBinding) +{ + LookupContext context(document(), snapshot()); + Overview oo; + QString decl; + + decl.append(prettyMinimalType(method->returnType(), + context, + method->scope(), + targetBinding)); + + decl.append(QLatin1Char(' ')); + decl.append(QLatin1String(method->name()->identifier()->chars())); + decl.append(QLatin1Char('(')); + for (unsigned argIdx = 0; argIdx < method->argumentCount(); ++argIdx) { + if (argIdx > 0) + decl.append(QLatin1String(", ")); + Argument *arg = method->argumentAt(argIdx)->asArgument(); + decl.append(prettyMinimalType(arg->type(), + context, + method->members(), + targetBinding)); + decl.append(QLatin1Char(' ')); + decl.append(oo(arg->name())); + } + decl.append(QLatin1String(");\n\n")); + + return decl; +} diff --git a/src/plugins/cppeditor/cppdeclfromdef.h b/src/plugins/cppeditor/cppdeclfromdef.h new file mode 100644 index 0000000000000000000000000000000000000000..8cbebc9271b3cd77777601e4da3254ac92cb4eff --- /dev/null +++ b/src/plugins/cppeditor/cppdeclfromdef.h @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CPPDECLFROMDEF_H +#define CPPDECLFROMDEF_H + +#include "cppquickfix.h" + +#include <CPlusPlusForwardDeclarations.h> + +namespace CPlusPlus { +class ClassOrNamespace; +} // namespace CPlusPlus + +namespace CppEditor { +namespace Internal { + +class DeclFromDef: public CppQuickFixOperation +{ +public: + DeclFromDef(TextEditor::BaseTextEditor *editor); + + virtual int match(const QList<CPlusPlus::AST *> &path); + virtual QString description() const; + virtual void createChanges(); + +protected: + virtual QString generateDeclaration(CPlusPlus::Function *method, + CPlusPlus::ClassOrNamespace *targetBinding); + +private: + QString m_targetFileName; + QString m_targetSymbolName; + QString m_decl; +}; + + +} // namespace Internal +} // namespace CppEditor + +#endif // CPPDECLFROMDEF_H diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 49306644a2d5b80e856b96f1591d2fd79077c3fb..159f6442dec518d096ecfbf513f123ec52e2ffad 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -19,7 +19,8 @@ HEADERS += cppplugin.h \ cpprefactoringchanges.h \ cppcheckundefinedsymbols.h \ cppsemanticinfo.h \ - cppoutline.h + cppoutline.h \ + cppdeclfromdef.h SOURCES += cppplugin.cpp \ cppeditor.cpp \ @@ -31,7 +32,8 @@ SOURCES += cppplugin.cpp \ cpprefactoringchanges.cpp \ cppcheckundefinedsymbols.cpp \ cppsemanticinfo.cpp \ - cppoutline.cpp + cppoutline.cpp \ + cppdeclfromdef.cpp RESOURCES += cppeditor.qrc diff --git a/src/plugins/cppeditor/cppquickfix.cpp b/src/plugins/cppeditor/cppquickfix.cpp index 00c6eb84ba0a964ab95fffaf020fb3eaedc9b325..5237e35773fc638d8144caa2e7fc2f0946a06bf9 100644 --- a/src/plugins/cppeditor/cppquickfix.cpp +++ b/src/plugins/cppeditor/cppquickfix.cpp @@ -29,6 +29,7 @@ #include "cppquickfix.h" #include "cppeditor.h" +#include "cppdeclfromdef.h" #include <cplusplus/ASTPath.h> #include <cplusplus/CppDocument.h> @@ -1546,6 +1547,7 @@ QList<TextEditor::QuickFixOperation::Ptr> CppQuickFixFactory::quickFixOperations QSharedPointer<ConvertNumericToOctal> convertNumericToOctal(new ConvertNumericToOctal(editor)); QSharedPointer<ConvertNumericToDecimal> convertNumericToDecimal(new ConvertNumericToDecimal(editor)); QSharedPointer<CompleteSwitchCaseStatement> completeSwitchCaseStatement(new CompleteSwitchCaseStatement(editor)); + QSharedPointer<DeclFromDef> declFromDef(new DeclFromDef(editor)); quickFixOperations.append(rewriteLogicalAndOp); quickFixOperations.append(splitIfStatementOp); @@ -1561,6 +1563,7 @@ QList<TextEditor::QuickFixOperation::Ptr> CppQuickFixFactory::quickFixOperations quickFixOperations.append(convertNumericToOctal); quickFixOperations.append(convertNumericToDecimal); quickFixOperations.append(completeSwitchCaseStatement); + quickFixOperations.append(declFromDef); if (editor->mimeType() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE) quickFixOperations.append(wrapCString); diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 2bee7d79b0fee8318265754a1a3fcdbeb4105432..94e7a9d1e3741e4ad368833e7ffed36cd2068c4f 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -39,6 +39,11 @@ using namespace TextEditor; +RefactoringChanges::RefactoringChanges() + : m_lineToShow(0) + , m_columnToShow(0) +{} + RefactoringChanges::~RefactoringChanges() {} @@ -149,12 +154,20 @@ QStringList RefactoringChanges::apply() // ### } + if (!m_fileNameToShow.isEmpty()) { + Core::EditorManager *editorManager = Core::EditorManager::instance(); + BaseTextEditor *editor = editorForFile(m_fileNameToShow); + editorManager->activateEditor(editor->editableInterface()); + if (m_lineToShow != -1) + editor->gotoLine(m_lineToShow + 1, m_columnToShow + 1); + } + return changed.toList(); } int RefactoringChanges::positionInFile(const QString &fileName, int line, int column) const { - if (BaseTextEditor *editor = editorForFile(fileName)) { + if (BaseTextEditor *editor = editorForFile(fileName, true)) { return editor->document()->findBlockByNumber(line).position() + column; } else { return -1; @@ -191,3 +204,10 @@ BaseTextEditor *RefactoringChanges::editorForNewFile(const QString &fileName) f.close(); return editorForFile(fileName, true); } + +void RefactoringChanges::openEditor(const QString &fileName, int line, int column) +{ + m_fileNameToShow = fileName; + m_lineToShow = line; + m_columnToShow = column; +} diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h index 93b576496c427dff7c88ecfb6b560c05d95252ae..f111123acd75b524bb184c35bd3348e425423c17 100644 --- a/src/plugins/texteditor/refactoringchanges.h +++ b/src/plugins/texteditor/refactoringchanges.h @@ -46,6 +46,7 @@ public: typedef Utils::ChangeSet::Range Range; public: + RefactoringChanges(); virtual ~RefactoringChanges(); void createFile(const QString &fileName, const QString &contents); @@ -63,10 +64,16 @@ public: bool openIfClosed = false); static BaseTextEditor *editorForNewFile(const QString &fileName); + /** line and column are zero-based */ + void openEditor(const QString &fileName, int line, int column); + private: QMap<QString, QString> m_contentsByCreatedFile; QMap<QString, Utils::ChangeSet> m_changesByFile; QMap<QString, QList<Range> > m_indentRangesByFile; + QString m_fileNameToShow; + int m_lineToShow; + int m_columnToShow; }; } // namespace TextEditor