Commit 9135c8cc authored by Erik Verbruggen's avatar Erik Verbruggen
Browse files

Quickfix for adding a declaration to a class from a definition.

parent 65eb1d55
......@@ -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)
{
......
......@@ -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;
......
......@@ -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);
}
......@@ -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;
......
/**************************************************************************
**
** 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;
}
/**************************************************************************
**
** 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
......@@ -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
......
......@@ -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);
......
......@@ -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;
}
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment