Commit 103ba359 authored by Jarek Kobus's avatar Jarek Kobus
Browse files

Finish "Go to slot" functionality. Currently it inserts declaration and...

Finish "Go to slot" functionality. Currently it inserts declaration and definition if it doesn't exist, adds parameter names taken from designer, deal with namespaced ui class name. There are couple of TODOs left in code with hints on how to make it better.
parent 58827feb
......@@ -68,8 +68,8 @@ WorkbenchIntegration::WorkbenchIntegration(QDesignerFormEditorInterface *core, F
setResourceFileWatcherBehaviour(QDesignerIntegration::ReloadSilently);
setResourceEditingEnabled(false);
setSlotNavigationEnabled(true);
connect(this, SIGNAL(navigateToSlot(QString, QString)),
this, SLOT(slotNavigateToSlot(QString, QString)));
connect(this, SIGNAL(navigateToSlot(QString, QString, QStringList)),
this, SLOT(slotNavigateToSlot(QString, QString, QStringList)));
}
void WorkbenchIntegration::updateSelection()
......@@ -87,7 +87,7 @@ QWidget *WorkbenchIntegration::containerWindow(QWidget * /*widget*/) const
return fw->integrationContainer();
}
QList<Document::Ptr> WorkbenchIntegration::findDocuments(const QString &uiFileName) const
static QList<Document::Ptr> findDocumentsIncluding(const QString &fileName, bool checkFileNameOnly)
{
Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>();
CppTools::CppModelManagerInterface *cppModelManager =
......@@ -99,19 +99,34 @@ QList<Document::Ptr> WorkbenchIntegration::findDocuments(const QString &uiFileNa
foreach (Document::Ptr doc, docTable) { // we go through all documents
QStringList includes = doc->includedFiles();
foreach (QString include, includes) {
const QFileInfo fi(include); // TODO: we should match an absolute path. Currently uiFileName is a file name only.
if (fi.fileName() == uiFileName) { // we are only interested in docs which includes our ui file
docList.append(doc);
if (checkFileNameOnly) {
const QFileInfo fi(include);
if (fi.fileName() == fileName) { // we are only interested in docs which includes fileName only
docList.append(doc);
}
} else {
if (include == fileName)
docList.append(doc);
}
}
}
return docList;
}
Class *WorkbenchIntegration::findClass(Namespace *parentNameSpace, const QString &uiClassName) const
static Class *findClass(Namespace *parentNameSpace, const QString &uiClassName, QString *namespaceName)
{
// construct proper ui class name, take into account namespaced ui class name
QString className1;
QString className2;
int indexOfScope = uiClassName.lastIndexOf(QLatin1String("::"));
if (indexOfScope < 0) {
className1 = QLatin1String("Ui::") + uiClassName;
className2 = QLatin1String("Ui_") + uiClassName;
} else {
className1 = uiClassName.left(indexOfScope + 2) + QLatin1String("Ui::") + uiClassName.mid(indexOfScope + 2);
className2 = uiClassName.left(indexOfScope + 2) + QLatin1String("Ui_") + uiClassName.mid(indexOfScope + 2);
}
for (unsigned i = 0; i < parentNameSpace->memberCount(); i++) { // we go through all namespace members
if (Class *cl = parentNameSpace->memberAt(i)->asClass()) { // we have found a class - we are interested in classes only
Overview o;
......@@ -119,9 +134,6 @@ Class *WorkbenchIntegration::findClass(Namespace *parentNameSpace, const QString
for (unsigned j = 0; j < cl->memberCount(); j++) { // we go through class members
const Declaration *decl = cl->memberAt(j)->asDeclaration();
if (decl) { // we want to know if the class contains a member (so we look into a declaration) of uiClassName type
const QString v1 = QLatin1String("Ui::") + uiClassName; // TODO: handle also the case of namespaced class name
const QString v2 = QLatin1String("Ui_") + uiClassName;
NamedType *nt = decl->type()->asNamedType();
// handle pointers to member variables
......@@ -131,35 +143,56 @@ Class *WorkbenchIntegration::findClass(Namespace *parentNameSpace, const QString
if (nt) {
Overview typeOverview;
const QString memberClass = typeOverview.prettyName(nt->name());
if (memberClass == v1 || memberClass == v2) // simple match here
if (memberClass == className1 || memberClass == className2) // names match
return cl;
// memberClass can be shorter (can have some namespaces cut because of e.g. "using namespace" declaration)
if (memberClass == className1.right(memberClass.length())) { // memberClass lenght <= className length
const QString namespacePrefix = className1.left(className1.length() - memberClass.length());
if (namespacePrefix.right(2) == QLatin1String("::"))
return cl;
}
// the same as above but for className2
if (memberClass == className2.right(memberClass.length())) { // memberClass lenght <= className length
const QString namespacePrefix = className2.left(className1.length() - memberClass.length());
if (namespacePrefix.right(2) == QLatin1String("::"))
return cl;
}
}
}
}
} else if (Namespace *ns = parentNameSpace->memberAt(i)->asNamespace()) {
return findClass(ns, uiClassName);
Overview o;
QString tempNS = *namespaceName + o.prettyName(ns->name()) + QLatin1String("::");
Class *cl = findClass(ns, uiClassName, &tempNS);
if (cl) {
*namespaceName = tempNS;
return cl;
}
}
}
return 0;
}
Function *WorkbenchIntegration::findFunction(Class *cl, const QString &functionName) const
static Function *findDeclaration(Class *cl, const QString &functionName)
{
// TODO: match properly function name and argument list, for now we match only function name
// (Roberto's suggestion: use QtMethodAST for that, enclosing function with SIGNAL(<fun>)
// then it becames an expression). Robetro's also proposed he can add public methods to parse declarations
// Quick implementation start
QString funName = functionName.left(functionName.indexOf(QLatin1Char('(')));
// Quick implementation end
const QString funName = QString::fromUtf8(QMetaObject::normalizedSignature(functionName.toUtf8()));
for (unsigned j = 0; j < cl->memberCount(); j++) { // go through all members
const Declaration *decl = cl->memberAt(j)->asDeclaration();
if (decl) { // we are interested only in declarations (can be decl of method or of a field)
Function *fun = decl->type()->asFunction();
if (fun) { // we are only interested in declarations of methods
Overview typeOverview;
const QString memberFunction = typeOverview.prettyName(fun->name());
if (memberFunction == funName) // simple match (we match only fun name, we should match also arguments)
Overview overview;
QString memberFunction = overview.prettyName(fun->name()) + QLatin1Char('(');
for (uint i = 0; i < fun->argumentCount(); i++) { // we build argument types string
Argument *arg = fun->argumentAt(i)->asArgument();
if (i > 0)
memberFunction += QLatin1Char(',');
memberFunction += overview.prettyType(arg->type());
}
memberFunction += QLatin1Char(')');
// we compare normalized signatures
memberFunction = QString::fromUtf8(QMetaObject::normalizedSignature(memberFunction.toUtf8()));
if (memberFunction == funName) // we match function names and argument lists
return fun;
}
}
......@@ -222,7 +255,7 @@ static bool isCompatible(Function *definition, Symbol *declaration, QualifiedNam
}
// TODO: remove me, this is taken from cppeditor.cpp. Find some common place for this method
Document::Ptr WorkbenchIntegration::findDefinition(Function *functionDeclaration, int *line) const
static Document::Ptr findDefinition(Function *functionDeclaration, int *line)
{
Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>();
CppTools::CppModelManagerInterface *cppModelManager =
......@@ -283,9 +316,47 @@ Document::Ptr WorkbenchIntegration::findDefinition(Function *functionDeclaration
}
void WorkbenchIntegration::addDeclaration(const QString &docFileName, Class *cl, const QString &functionName) const
// TODO: Wait for robust Roberto's code using AST or whatever for that. Current implementation is hackish.
static int findClassEndPosition(const QString &headerContents, int classStartPosition)
{
const QString contents = headerContents.mid(classStartPosition); // we start serching from the beginning of class declaration
// We need to find the position of class closing "}"
int openedBlocksCount = 0; // counter of nested {} blocks
int idx = 0; // index of current position in the contents
while (true) {
if (idx < 0 || idx >= contents.length()) // indexOf returned -1, that means we don't have closing comment mark
break;
if (contents.mid(idx, 2) == QLatin1String("//")) {
idx = contents.indexOf(QLatin1Char('\n'), idx + 2) + 1; // drop everything up to the end of line
} else if (contents.mid(idx, 2) == QLatin1String("/*")) {
idx = contents.indexOf(QLatin1String("*/"), idx + 2) + 1; // drop everything up to the nearest */
} else if (contents.mid(idx, 4) == QLatin1String("'\\\"'")) {
idx += 4; // drop it
} else if (contents.at(idx) == QLatin1Char('\"')) {
do {
idx = contents.indexOf(QLatin1Char('\"'), idx + 1); // drop everything up to the nearest "
} while (idx > 0 && contents.at(idx - 1) == QLatin1Char('\\')); // if the nearest " is preceeded by \ we find next one
if (idx < 0)
break;
idx++;
} else {
if (contents.at(idx) == QLatin1Char('{')) {
openedBlocksCount++;
} else if (contents.at(idx) == QLatin1Char('}')) {
openedBlocksCount--;
if (openedBlocksCount == 0) {
return classStartPosition + idx;
}
}
idx++;
}
}
return -1;
}
static void addDeclaration(const QString &docFileName, Class *cl, const QString &functionName)
{
// TODO: add argument names (from designer we get only argument types)
// functionName comes already with argument names (if designer managed to do that)
for (unsigned j = 0; j < cl->memberCount(); j++) { // go through all members
const Declaration *decl = cl->memberAt(j)->asDeclaration();
if (decl) { // we want to find any method which is a private slot (then we don't need to add "private slots:" statement)
......@@ -293,7 +364,9 @@ void WorkbenchIntegration::addDeclaration(const QString &docFileName, Class *cl,
if (fun) { // we are only interested in declarations of methods
if (fun->isSlot() && fun->isPrivate()) {
ITextEditable *editable = qobject_cast<ITextEditable *>(
TextEditor::BaseTextEditor::openEditorAt(docFileName, fun->line()/*, fun->column()*/)); // TODO: fun->column() gives me weird number...
TextEditor::BaseTextEditor::openEditorAt(docFileName, fun->line(), fun->column()));
// fun->column() raturns always 0, what can cause trouble in case in one
// line there is: "private slots: void foo();"
if (editable) {
editable->insert(QLatin1String("void ") + functionName + QLatin1String(";\n "));
}
......@@ -303,24 +376,87 @@ void WorkbenchIntegration::addDeclaration(const QString &docFileName, Class *cl,
}
}
// TODO: we didn't find "private slots:", let's add it
// We didn't find any method under "private slots:", let's add "private slots:". Below code
// adds "private slots:" by the end of the class definition.
ITextEditable *editable = qobject_cast<ITextEditable *>(
TextEditor::BaseTextEditor::openEditorAt(docFileName, cl->line(), cl->column()));
if (editable) {
int classEndPosition = findClassEndPosition(editable->contents(), editable->position());
if (classEndPosition >= 0) {
int line, column;
editable->convertPosition(classEndPosition, &line, &column); // converts back position into a line and column
editable->gotoLine(line, column); // go to position (we should be just before closing } of the class)
editable->insert(QLatin1String("\nprivate slots:\n ")
+ QLatin1String("void ") + functionName + QLatin1String(";\n"));
}
}
}
static Document::Ptr addDefinition(const QString &headerFileName, const QString &className,
const QString &functionName, int *line)
{
// we find all documents which include headerFileName
QList<Document::Ptr> docList = findDocumentsIncluding(headerFileName, false);
if (docList.isEmpty())
return Document::Ptr();
QFileInfo headerFI(headerFileName);
const QString headerBaseName = headerFI.baseName();
const QString headerAbsolutePath = headerFI.absolutePath();
foreach (Document::Ptr doc, docList) {
QFileInfo sourceFI(doc->fileName());
// we take only those documents which has the same filename and path (maybe we don't need to compare the path???)
if (headerBaseName == sourceFI.baseName() && headerAbsolutePath == sourceFI.absolutePath()) {
ITextEditable *editable = qobject_cast<ITextEditable *>( // TODO: add the code into appropriate namespace
TextEditor::BaseTextEditor::openEditorAt(doc->fileName(), 0));
if (editable) {
const QString contents = editable->contents();
int column;
editable->convertPosition(contents.length(), line, &column);
editable->gotoLine(*line, column);
editable->insert(QLatin1String("\nvoid ") + className + QLatin1String("::") +
functionName + QLatin1String("\n {\n\n }\n"));
*line += 1;
return;
}
return doc;
}
}
return Document::Ptr();
}
void WorkbenchIntegration::slotNavigateToSlot(const QString &objectName, const QString &signalSignature)
static QString addParameterNames(const QString &functionSignature, const QStringList &parameterNames)
{
QString functionName = functionSignature.left(functionSignature.indexOf(QLatin1Char('(')) + 1);
QString argumentsString = functionSignature.mid(functionSignature.indexOf(QLatin1Char('(')) + 1);
argumentsString = argumentsString.left(argumentsString.indexOf(QLatin1Char(')')));
const QStringList arguments = argumentsString.split(QLatin1Char(','), QString::SkipEmptyParts);
for (int i = 0; i < arguments.count(); ++i) {
if (i > 0)
functionName += QLatin1String(", ");
functionName += arguments.at(i);
if (i < parameterNames.count())
functionName += QLatin1Char(' ') + parameterNames.at(i);
}
functionName += QLatin1Char(')');
return functionName;
}
void WorkbenchIntegration::slotNavigateToSlot(const QString &objectName, const QString &signalSignature,
const QStringList &parameterNames)
{
const QString currentUiFile = m_few->activeFormWindow()->file()->fileName();
// TODO: we should pass to findDocuments an absolute path to generated .h file from ui.
// Currently we are guessing the name of ui_<>.h file and pass the file name only to the findDocuments().
// TODO: we should pass to findDocumentsIncluding an absolute path to generated .h file from ui.
// Currently we are guessing the name of ui_<>.h file and pass the file name only to the findDocumentsIncluding().
// The idea is that the .pro file knows if the .ui files is inside, and the .pro file knows it will
// be generating the ui_<>.h file for it, and the .pro file knows what the generated file's name and its absolute path will be.
// So we should somehow get that info from project manager (?)
const QFileInfo fi(currentUiFile);
const QString uicedName = QLatin1String("ui_") + fi.baseName() + QLatin1String(".h");
QList<Document::Ptr> docList = findDocuments(uicedName);
QList<Document::Ptr> docList = findDocumentsIncluding(uicedName, true); // change to false when we know the absolute path to generated ui_<>.h file
if (docList.isEmpty())
return;
......@@ -329,22 +465,33 @@ void WorkbenchIntegration::slotNavigateToSlot(const QString &objectName, const Q
const QString uiClassName = fwi->mainContainer()->objectName();
foreach (Document::Ptr doc, docList) {
Class *cl = findClass(doc->globalNamespace(), uiClassName);
QString namespaceName; // namespace of the class found
Class *cl = findClass(doc->globalNamespace(), uiClassName, &namespaceName);
if (cl) {
Overview o;
const QString className = namespaceName + o.prettyName(cl->name());
QString functionName = QLatin1String("on_") + objectName + QLatin1Char('_') + signalSignature;
Function *fun = findFunction(cl, functionName);
QString functionNameWithParameterNames = addParameterNames(functionName, parameterNames);
Function *fun = findDeclaration(cl, functionName);
int line = 0;
Document::Ptr sourceDoc;
if (!fun) {
// add function declaration to cl
addDeclaration(doc->fileName(), cl, functionName);
// TODO: add function definition to cpp file
addDeclaration(doc->fileName(), cl, functionNameWithParameterNames);
// add function definition to cpp file
sourceDoc = addDefinition(doc->fileName(), className, functionNameWithParameterNames, &line);
} else {
doc = findDefinition(fun, &line);
sourceDoc = findDefinition(fun, &line);
if (!sourceDoc) {
// add function definition to cpp file
sourceDoc = addDefinition(doc->fileName(), className, functionNameWithParameterNames, &line);
}
}
if (doc) {
if (sourceDoc) {
// jump to function definition
TextEditor::BaseTextEditor::openEditorAt(doc->fileName(), line);
TextEditor::BaseTextEditor::openEditorAt(sourceDoc->fileName(), line);
}
return;
}
......
......@@ -40,7 +40,7 @@
namespace Designer {
namespace Internal {
class FormEditorW;
class WorkbenchIntegration : public qdesigner_internal::QDesignerIntegration {
......@@ -55,14 +55,8 @@ public:
public slots:
void updateSelection();
private slots:
void slotNavigateToSlot(const QString &objectName, const QString &signalSignature);
void slotNavigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList &parameterNames);
private:
QList<CPlusPlus::Document::Ptr> findDocuments(const QString &uiFileName) const;
CPlusPlus::Class *findClass(CPlusPlus::Namespace *parentNameSpace, const QString &uiClassName) const;
CPlusPlus::Function *findFunction(CPlusPlus::Class *cl, const QString &functionName) const;
CPlusPlus::Document::Ptr findDefinition(CPlusPlus::Function *functionDeclaration, int *line) const;
void addDeclaration(const QString &docFileName, CPlusPlus::Class *cl, const QString &functionName) const;
FormEditorW *m_few;
};
......
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