mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 20:03:10 +00:00
296 lines
10 KiB
C++
296 lines
10 KiB
C++
![]() |
/*
|
||
|
Copyright 2009 David Nolden <david.nolden.kdevelop@art-master.de>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License version 2 as published by the Free Software Foundation.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#include "adaptsignatureassistant.h"
|
||
|
|
||
|
#include <interfaces/icore.h>
|
||
|
#include <interfaces/ilanguagecontroller.h>
|
||
|
#include <language/assistant/renameaction.h>
|
||
|
#include <language/duchain/duchainutils.h>
|
||
|
#include <language/backgroundparser/backgroundparser.h>
|
||
|
#include <language/backgroundparser/parsejob.h>
|
||
|
#include <language/duchain/functiondefinition.h>
|
||
|
#include <language/duchain/types/functiontype.h>
|
||
|
|
||
|
#include <KTextEditor/Document>
|
||
|
#include <KTextEditor/View>
|
||
|
|
||
|
#include "cppduchain.h"
|
||
|
#include "qtfunctiondeclaration.h"
|
||
|
|
||
|
using namespace KDevelop;
|
||
|
using namespace Cpp;
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
Declaration *getDeclarationAtCursor(const SimpleCursor &cursor, const KUrl &documentUrl)
|
||
|
{
|
||
|
ENSURE_CHAIN_READ_LOCKED
|
||
|
ReferencedTopDUContext top(DUChainUtils::standardContextForUrl(documentUrl));
|
||
|
if(!top)
|
||
|
return 0;
|
||
|
|
||
|
Declaration* functionDecl = DUChainUtils::declarationInLine(cursor, top.data());
|
||
|
return functionDecl;
|
||
|
}
|
||
|
|
||
|
Signature getDeclarationSignature(const Declaration *functionDecl, const DUContext *functionCtxt, bool includeDefaults)
|
||
|
{
|
||
|
ENSURE_CHAIN_READ_LOCKED
|
||
|
int pos = 0;
|
||
|
Signature signature;
|
||
|
const AbstractFunctionDeclaration* abstractFunDecl = dynamic_cast<const AbstractFunctionDeclaration*>(functionDecl);
|
||
|
foreach(Declaration* parameter, functionCtxt->localDeclarations()) {
|
||
|
signature.defaultParams << (includeDefaults ? abstractFunDecl->defaultParameterForArgument(pos).str() : "");
|
||
|
signature.parameters << qMakePair(parameter->indexedType(), parameter->identifier().identifier().str());
|
||
|
++pos;
|
||
|
}
|
||
|
signature.isConst = functionDecl->abstractType() && functionDecl->abstractType()->modifiers() & AbstractType::ConstModifier;
|
||
|
|
||
|
FunctionType::Ptr funType = functionDecl->type<FunctionType>();
|
||
|
if(funType)
|
||
|
signature.returnType = funType->returnType()->indexed();
|
||
|
|
||
|
return signature;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
AdaptSignatureAssistant::AdaptSignatureAssistant(ILanguageSupport* supportedLanguage)
|
||
|
: StaticAssistant(supportedLanguage)
|
||
|
{
|
||
|
connect(ICore::self()->languageController()->backgroundParser(),
|
||
|
SIGNAL(parseJobFinished(KDevelop::ParseJob*)),
|
||
|
SLOT(parseJobFinished(KDevelop::ParseJob*)));
|
||
|
}
|
||
|
|
||
|
QString AdaptSignatureAssistant::title() const
|
||
|
{
|
||
|
return tr("Adapt Signature");
|
||
|
}
|
||
|
|
||
|
void AdaptSignatureAssistant::reset()
|
||
|
{
|
||
|
doHide();
|
||
|
clearActions();
|
||
|
|
||
|
m_editingDefinition = {};
|
||
|
m_declarationName = {};
|
||
|
m_otherSideId = {};
|
||
|
m_otherSideTopContext = {};
|
||
|
m_otherSideContext = {};
|
||
|
m_oldSignature = Signature();
|
||
|
m_document = {};
|
||
|
m_view.clear();
|
||
|
}
|
||
|
|
||
|
void AdaptSignatureAssistant::textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText)
|
||
|
{
|
||
|
reset();
|
||
|
|
||
|
m_view = view;
|
||
|
|
||
|
//FIXME: update signature assistant to play well with the rename assistant
|
||
|
KTextEditor::Range sigAssistRange = invocationRange;
|
||
|
if (!removedText.isEmpty()) {
|
||
|
sigAssistRange.setRange(sigAssistRange.start(), sigAssistRange.start());
|
||
|
}
|
||
|
|
||
|
m_document = view->document()->url();
|
||
|
|
||
|
DUChainReadLocker lock(DUChain::lock(), 300);
|
||
|
if(!lock.locked()) {
|
||
|
kDebug() << "failed to lock duchain in time";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SimpleRange simpleInvocationRange = SimpleRange(sigAssistRange);
|
||
|
Declaration* funDecl = getDeclarationAtCursor(simpleInvocationRange.start, m_document);
|
||
|
if(!funDecl || !funDecl->type<FunctionType>())
|
||
|
return;
|
||
|
|
||
|
if(QtFunctionDeclaration* classFun = dynamic_cast<QtFunctionDeclaration*>(funDecl)) {
|
||
|
if (classFun->isSignal()) {
|
||
|
// do not offer to change signature of a signal, as the implementation will be generated by moc
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Declaration* otherSide = 0;
|
||
|
FunctionDefinition* definition = dynamic_cast<FunctionDefinition*>(funDecl);
|
||
|
if (definition)
|
||
|
{
|
||
|
m_editingDefinition = true;
|
||
|
otherSide = definition->declaration();
|
||
|
}
|
||
|
else if ((definition = FunctionDefinition::definition(funDecl)))
|
||
|
{
|
||
|
m_editingDefinition = false;
|
||
|
otherSide = definition;
|
||
|
}
|
||
|
|
||
|
if (!otherSide)
|
||
|
return;
|
||
|
|
||
|
m_otherSideContext = DUContextPointer(DUChainUtils::getFunctionContext(otherSide));
|
||
|
if (!m_otherSideContext)
|
||
|
return;
|
||
|
|
||
|
m_declarationName = funDecl->identifier();
|
||
|
m_otherSideId = otherSide->id();
|
||
|
m_otherSideTopContext = ReferencedTopDUContext(otherSide->topContext());
|
||
|
m_oldSignature = getDeclarationSignature(otherSide, m_otherSideContext.data(), true);
|
||
|
|
||
|
//Schedule an update, to make sure the ranges match
|
||
|
DUChain::self()->updateContextForUrl(m_otherSideTopContext->url(), TopDUContext::AllDeclarationsAndContexts);
|
||
|
}
|
||
|
|
||
|
bool AdaptSignatureAssistant::isUseful() const
|
||
|
{
|
||
|
return !m_declarationName.isEmpty() && m_otherSideId.isValid();
|
||
|
}
|
||
|
|
||
|
bool AdaptSignatureAssistant::getSignatureChanges(const Signature& newSignature, QList< int >& oldPositions) const
|
||
|
{
|
||
|
bool changed = false;
|
||
|
for (int i = 0; i < newSignature.parameters.size(); ++i)
|
||
|
oldPositions.append(-1);
|
||
|
for (int curNewParam = newSignature.parameters.size() - 1; curNewParam >= 0 ; --curNewParam)
|
||
|
{
|
||
|
int foundAt = -1;
|
||
|
|
||
|
for (int curOldParam = m_oldSignature.parameters.size() - 1; curOldParam >= 0 ; --curOldParam)
|
||
|
{
|
||
|
if (newSignature.parameters[curNewParam].first != m_oldSignature.parameters[curOldParam].first)
|
||
|
continue; //Different type == different parameters
|
||
|
|
||
|
if (newSignature.parameters[curNewParam].second == m_oldSignature.parameters[curOldParam].second || curOldParam == curNewParam)
|
||
|
{
|
||
|
//given the same type and either the same position or the same name, it's (probably) the same argument
|
||
|
foundAt = curOldParam;
|
||
|
|
||
|
if (newSignature.parameters[curNewParam].second != m_oldSignature.parameters[curOldParam].second || curOldParam != curNewParam)
|
||
|
changed = true; //Either the name changed at this position, or position of this name has changed
|
||
|
|
||
|
if (newSignature.parameters[curNewParam].second == m_oldSignature.parameters[curOldParam].second)
|
||
|
break; //Found an argument with the same name and type, no need to look further
|
||
|
//else: position/type match, but name match will trump, allowing: (int i=0, int j=1) => (int j=1, int i=0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (foundAt < 0)
|
||
|
changed = true;
|
||
|
|
||
|
oldPositions[curNewParam] = foundAt;
|
||
|
}
|
||
|
|
||
|
if(newSignature.parameters.size() != m_oldSignature.parameters.size())
|
||
|
changed = true;
|
||
|
|
||
|
if(newSignature.isConst != m_oldSignature.isConst)
|
||
|
changed = true;
|
||
|
|
||
|
if(newSignature.returnType != m_oldSignature.returnType)
|
||
|
changed = true;
|
||
|
|
||
|
return changed;
|
||
|
}
|
||
|
|
||
|
void AdaptSignatureAssistant::setDefaultParams(Signature& newSignature, const QList< int >& oldPositions) const
|
||
|
{
|
||
|
for(int i = newSignature.parameters.size() - 1; i >= 0; --i)
|
||
|
{
|
||
|
if (oldPositions[i] == -1)
|
||
|
return; //this param is new, no further defaults possible
|
||
|
|
||
|
if (i == newSignature.defaultParams.size() - 1 || !newSignature.defaultParams[i+1].isEmpty())
|
||
|
newSignature.defaultParams[i] = m_oldSignature.defaultParams[oldPositions[i]];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QList< RenameAction* > AdaptSignatureAssistant::getRenameActions(const Signature &newSignature, const QList<int> &oldPositions) const
|
||
|
{
|
||
|
Q_ASSERT(DUChain::lock()->currentThreadHasReadLock());
|
||
|
QList<RenameAction*> renameActions;
|
||
|
if (!m_otherSideContext)
|
||
|
return renameActions;
|
||
|
|
||
|
for(int i = newSignature.parameters.size() - 1; i >= 0; --i)
|
||
|
{
|
||
|
if (oldPositions[i] == -1)
|
||
|
continue; //new parameter
|
||
|
|
||
|
Declaration *renamedDecl = m_otherSideContext->localDeclarations()[oldPositions[i]];
|
||
|
if (newSignature.parameters[i].second != m_oldSignature.parameters[oldPositions[i]].second) {
|
||
|
QMap< IndexedString, QList< RangeInRevision > > uses = renamedDecl->uses();
|
||
|
if (!uses.isEmpty()) {
|
||
|
renameActions << new RenameAction(renamedDecl->identifier(), newSignature.parameters[i].second,
|
||
|
RevisionedFileRanges::convert(uses));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return renameActions;
|
||
|
}
|
||
|
|
||
|
void AdaptSignatureAssistant::parseJobFinished(KDevelop::ParseJob* job)
|
||
|
{
|
||
|
if (job->document().toUrl() != m_document || !m_view)
|
||
|
return;
|
||
|
|
||
|
clearActions();
|
||
|
|
||
|
DUChainReadLocker lock;
|
||
|
|
||
|
Declaration *functionDecl = getDeclarationAtCursor(SimpleCursor(m_view.data()->cursorPosition()), m_document);
|
||
|
if (!functionDecl || functionDecl->identifier() != m_declarationName)
|
||
|
return;
|
||
|
DUContext *functionCtxt = DUChainUtils::getFunctionContext(functionDecl);
|
||
|
if (!functionCtxt)
|
||
|
return;
|
||
|
if(QtFunctionDeclaration* classFun = dynamic_cast<QtFunctionDeclaration*>(functionDecl)) {
|
||
|
if (classFun->isSignal()) {
|
||
|
// do not offer to change signature of a signal, as the implementation will be generated by moc
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
//ParseJob having finished, get the signature that was modified
|
||
|
Signature newSignature = getDeclarationSignature(functionDecl, functionCtxt, false);
|
||
|
|
||
|
//Check for changes between m_oldSignature and newSignature, use oldPositions to store old<->new param index mapping
|
||
|
QList<int> oldPositions;
|
||
|
if (!getSignatureChanges(newSignature, oldPositions)) {
|
||
|
reset();
|
||
|
return; //No changes to signature
|
||
|
}
|
||
|
|
||
|
QList<RenameAction*> renameActions;
|
||
|
if (m_editingDefinition)
|
||
|
setDefaultParams(newSignature, oldPositions); //restore default parameters before updating the declarations
|
||
|
else
|
||
|
renameActions = getRenameActions(newSignature, oldPositions); //rename as needed when updating the definition
|
||
|
|
||
|
IAssistantAction::Ptr action(new AdaptSignatureAction(m_otherSideId, m_otherSideTopContext,
|
||
|
m_oldSignature, newSignature,
|
||
|
m_editingDefinition, renameActions));
|
||
|
connect(action.data(), SIGNAL(executed(IAssistantAction*)), SLOT(reset()));
|
||
|
addAction(action);
|
||
|
emit actionsChanged();
|
||
|
}
|
||
|
|
||
|
#include "moc_adaptsignatureassistant.cpp"
|