From 7dfb2bd577bbfb009e61de262721a2e57c1bcf66 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 16 Nov 2010 13:54:50 +0100 Subject: [PATCH] QmlJS: Add a filter for functions to the locator. Task-number: QTCREATORBUG-2607 Reviewed-by: Erik Verbruggen --- .../qmljstools/QmlJSTools.pluginspec.in | 1 + .../qmljstools/qmljsfunctionfilter.cpp | 115 +++++++++ src/plugins/qmljstools/qmljsfunctionfilter.h | 61 +++++ src/plugins/qmljstools/qmljslocatordata.cpp | 218 ++++++++++++++++++ src/plugins/qmljstools/qmljslocatordata.h | 78 +++++++ src/plugins/qmljstools/qmljstools-lib.pri | 8 +- src/plugins/qmljstools/qmljstoolsplugin.cpp | 6 + 7 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmljstools/qmljsfunctionfilter.cpp create mode 100644 src/plugins/qmljstools/qmljsfunctionfilter.h create mode 100644 src/plugins/qmljstools/qmljslocatordata.cpp create mode 100644 src/plugins/qmljstools/qmljslocatordata.h diff --git a/src/plugins/qmljstools/QmlJSTools.pluginspec.in b/src/plugins/qmljstools/QmlJSTools.pluginspec.in index 0128ca3945b..169376d347e 100644 --- a/src/plugins/qmljstools/QmlJSTools.pluginspec.in +++ b/src/plugins/qmljstools/QmlJSTools.pluginspec.in @@ -16,5 +16,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General + diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp new file mode 100644 index 00000000000..4f097857f24 --- /dev/null +++ b/src/plugins/qmljstools/qmljsfunctionfilter.cpp @@ -0,0 +1,115 @@ +/************************************************************************** +** +** 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 "qmljsfunctionfilter.h" +#include "qmljslocatordata.h" + +#include +#include + +#include + +using namespace QmlJSTools::Internal; + +Q_DECLARE_METATYPE(LocatorData::Entry); + +FunctionFilter::FunctionFilter(LocatorData *data, QObject *parent) + : Locator::ILocatorFilter(parent) + , m_data(data) +{ + setShortcutString(QString(QLatin1Char('m'))); + setIncludedByDefault(false); +} + +FunctionFilter::~FunctionFilter() +{ } + +void FunctionFilter::refresh(QFutureInterface &) +{ +} + +static bool compareLexigraphically(const Locator::FilterEntry &a, + const Locator::FilterEntry &b) +{ + return a.displayName < b.displayName; +} + +QList FunctionFilter::matchesFor(QFutureInterface &future, const QString &origEntry) +{ + QString entry = trimWildcards(origEntry); + QList goodEntries; + QList betterEntries; + const QChar asterisk = QLatin1Char('*'); + QStringMatcher matcher(entry, Qt::CaseInsensitive); + const QRegExp regexp(asterisk + entry+ asterisk, Qt::CaseInsensitive, QRegExp::Wildcard); + if (!regexp.isValid()) + return goodEntries; + bool hasWildcard = (entry.contains(asterisk) || entry.contains('?')); + + QHashIterator > it(m_data->entries()); + while (it.hasNext()) { + if (future.isCanceled()) + break; + + it.next(); + + const QList items = it.value(); + foreach (const LocatorData::Entry &info, items) { + if (info.type != LocatorData::Function) + continue; + if ((hasWildcard && regexp.exactMatch(info.symbolName)) + || (!hasWildcard && matcher.indexIn(info.symbolName) != -1)) { + + QVariant id = qVariantFromValue(info); + Locator::FilterEntry filterEntry(this, info.displayName, id/*, info.icon*/); + filterEntry.extraInfo = info.extraInfo; + + if (info.symbolName.startsWith(entry)) + betterEntries.append(filterEntry); + else + goodEntries.append(filterEntry); + } + } + } + + if (goodEntries.size() < 1000) + qSort(goodEntries.begin(), goodEntries.end(), compareLexigraphically); + if (betterEntries.size() < 1000) + qSort(betterEntries.begin(), betterEntries.end(), compareLexigraphically); + + betterEntries += goodEntries; + return betterEntries; +} + +void FunctionFilter::accept(Locator::FilterEntry selection) const +{ + const LocatorData::Entry entry = qvariant_cast(selection.internalData); + TextEditor::BaseTextEditor::openEditorAt(entry.fileName, entry.line, entry.column, + QString(), Core::EditorManager::ModeSwitch); +} diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.h b/src/plugins/qmljstools/qmljsfunctionfilter.h new file mode 100644 index 00000000000..d79fed8eb95 --- /dev/null +++ b/src/plugins/qmljstools/qmljsfunctionfilter.h @@ -0,0 +1,61 @@ +/************************************************************************** +** +** 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 QMLJSFUNCTIONFILTER_H +#define QMLJSFUNCTIONFILTER_H + +#include + +namespace QmlJSTools { +namespace Internal { + +class LocatorData; + +class FunctionFilter : public Locator::ILocatorFilter +{ + Q_OBJECT +public: + explicit FunctionFilter(LocatorData *data, QObject *parent = 0); + ~FunctionFilter(); + + QString displayName() const { return tr("Functions"); } + QString id() const { return QLatin1String("Functions"); } + Priority priority() const { return Medium; } + QList matchesFor(QFutureInterface &future, const QString &entry); + void accept(Locator::FilterEntry selection) const; + void refresh(QFutureInterface &future); + +private: + LocatorData *m_data; +}; + +} // namespace Internal +} // namespace QmlJSTools + +#endif // QMLJSFUNCTIONFILTER_H diff --git a/src/plugins/qmljstools/qmljslocatordata.cpp b/src/plugins/qmljstools/qmljslocatordata.cpp new file mode 100644 index 00000000000..4b9b45e24ae --- /dev/null +++ b/src/plugins/qmljstools/qmljslocatordata.cpp @@ -0,0 +1,218 @@ +/************************************************************************** +** +** 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 "qmljslocatordata.h" + +#include +#include +#include +#include + +using namespace QmlJSTools::Internal; +using namespace QmlJS; +using namespace QmlJS::Interpreter; +using namespace QmlJS::AST; + +LocatorData::LocatorData(QObject *parent) + : QObject(parent) +{ + QmlJS::ModelManagerInterface *manager = QmlJS::ModelManagerInterface::instance(); + + connect(manager, SIGNAL(documentUpdated(QmlJS::Document::Ptr)), + this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr))); + connect(manager, SIGNAL(aboutToRemoveFiles(QStringList)), + this, SLOT(onAboutToRemoveFiles(QStringList))); +} + +LocatorData::~LocatorData() +{} + +namespace { +static QString findId(UiObjectInitializer *initializer) +{ + if (!initializer) + return QString(); + for (UiObjectMemberList *member = initializer->members; member; member = member->next) { + if (UiScriptBinding *script = cast(member->member)) { + if (!script->qualifiedId || !script->qualifiedId->name || script->qualifiedId->next) + continue; + if (script->qualifiedId->name->asString() != QLatin1String("id")) + continue; + if (ExpressionStatement *expStmt = cast(script->statement)) { + if (IdentifierExpression *identExp = cast(expStmt->expression)) { + if (identExp->name) + return identExp->name->asString(); + } + } + } + } + return QString(); +} + +class FunctionFinder : protected AST::Visitor +{ + QList m_entries; + Document::Ptr m_doc; + QString m_context; + QString m_documentContext; + +public: + FunctionFinder() + {} + + QList run(const Document::Ptr &doc) + { + m_doc = doc; + if (!doc->componentName().isEmpty()) { + m_documentContext = doc->componentName(); + } else { + m_documentContext = QFileInfo(doc->fileName()).fileName(); + } + accept(doc->ast(), m_documentContext); + return m_entries; + } + +protected: + QString contextString(const QString &extra) + { + return QString("%1, %2").arg(extra, m_documentContext); + } + + LocatorData::Entry basicEntry(SourceLocation loc) + { + LocatorData::Entry entry; + entry.type = LocatorData::Function; + entry.extraInfo = m_context; + entry.fileName = m_doc->fileName(); + entry.line = loc.startLine; + entry.column = loc.startColumn - 1; + return entry; + } + + void accept(Node *ast, const QString &context) + { + const QString old = m_context; + m_context = context; + Node::accept(ast, this); + m_context = old; + } + + bool visit(FunctionDeclaration *ast) + { + return visit(static_cast(ast)); + } + + bool visit(FunctionExpression *ast) + { + if (!ast->name) + return true; + + LocatorData::Entry entry = basicEntry(ast->identifierToken); + + entry.type = LocatorData::Function; + entry.displayName = ast->name->asString(); + entry.displayName += QLatin1Char('('); + for (FormalParameterList *it = ast->formals; it; it = it->next) { + if (it != ast->formals) + entry.displayName += QLatin1String(", "); + if (it->name) + entry.displayName += it->name->asString(); + } + entry.displayName += QLatin1Char(')'); + entry.symbolName = entry.displayName; + + m_entries += entry; + + accept(ast->body, contextString(QString("function %1").arg(entry.displayName))); + return false; + } + + bool visit(UiScriptBinding *ast) + { + if (!ast->qualifiedId) + return true; + const QString qualifiedIdString = Bind::toString(ast->qualifiedId); + + if (cast(ast->statement)) { + LocatorData::Entry entry = basicEntry(ast->qualifiedId->identifierToken); + entry.displayName = qualifiedIdString; + entry.symbolName = qualifiedIdString; + m_entries += entry; + } + + accept(ast->statement, contextString(Bind::toString(ast->qualifiedId))); + return false; + } + + bool visit(UiObjectBinding *ast) + { + if (!ast->qualifiedTypeNameId) + return true; + + QString context = Bind::toString(ast->qualifiedTypeNameId); + const QString id = findId(ast->initializer); + if (!id.isEmpty()) + context = QString("%1 (%2)").arg(id, context); + accept(ast->initializer, contextString(context)); + return false; + } + + bool visit(UiObjectDefinition *ast) + { + if (!ast->qualifiedTypeNameId) + return true; + + QString context = Bind::toString(ast->qualifiedTypeNameId); + const QString id = findId(ast->initializer); + if (!id.isEmpty()) + context = QString("%1 (%2)").arg(id, context); + accept(ast->initializer, contextString(context)); + return false; + } +}; +} // anonymous namespace + +QHash > LocatorData::entries() const +{ + return m_entries; +} + +void LocatorData::onDocumentUpdated(const QmlJS::Document::Ptr &doc) +{ + QList entries = FunctionFinder().run(doc); + m_entries.insert(doc->fileName(), entries); +} + +void LocatorData::onAboutToRemoveFiles(const QStringList &files) +{ + foreach (const QString &file, files) { + m_entries.remove(file); + } +} + diff --git a/src/plugins/qmljstools/qmljslocatordata.h b/src/plugins/qmljstools/qmljslocatordata.h new file mode 100644 index 00000000000..9357fca7e95 --- /dev/null +++ b/src/plugins/qmljstools/qmljslocatordata.h @@ -0,0 +1,78 @@ +/************************************************************************** +** +** 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 QMLJSLOCATORDATA_H +#define QMLJSLOCATORDATA_H + +#include + +#include +#include + +namespace QmlJSTools { +namespace Internal { + +class LocatorData : public QObject +{ + Q_OBJECT +public: + explicit LocatorData(QObject *parent = 0); + ~LocatorData(); + + enum EntryType + { + Function, + }; + + class Entry + { + public: + EntryType type; + QString symbolName; + QString displayName; + QString extraInfo; + QString fileName; + int line; + int column; + }; + + QHash > entries() const; + +private slots: + void onDocumentUpdated(const QmlJS::Document::Ptr &doc); + void onAboutToRemoveFiles(const QStringList &files); + +private: + QHash > m_entries; +}; + +} // namespace Internal +} // namespace QmlJSTools + +#endif // QMLJSLOCATORDATA_H diff --git a/src/plugins/qmljstools/qmljstools-lib.pri b/src/plugins/qmljstools/qmljstools-lib.pri index ca1997b6c9d..14e1c763de0 100644 --- a/src/plugins/qmljstools/qmljstools-lib.pri +++ b/src/plugins/qmljstools/qmljstools-lib.pri @@ -12,11 +12,15 @@ HEADERS += \ $$PWD/qmljsmodelmanager.h \ $$PWD/qmljsqtstylecodeformatter.h \ $$PWD/qmljsrefactoringchanges.h \ - $$PWD/qmljsplugindumper.h + $$PWD/qmljsplugindumper.h \ + $$PWD/qmljsfunctionfilter.h \ + $$PWD/qmljslocatordata.h SOURCES += \ $$PWD/qmljstoolsplugin.cpp \ $$PWD/qmljsmodelmanager.cpp \ $$PWD/qmljsqtstylecodeformatter.cpp \ $$PWD/qmljsrefactoringchanges.cpp \ - $$PWD/qmljsplugindumper.cpp + $$PWD/qmljsplugindumper.cpp \ + $$PWD/qmljsfunctionfilter.cpp \ + $$PWD/qmljslocatordata.cpp diff --git a/src/plugins/qmljstools/qmljstoolsplugin.cpp b/src/plugins/qmljstools/qmljstoolsplugin.cpp index b4b13c46fdc..7c6b30584ea 100644 --- a/src/plugins/qmljstools/qmljstoolsplugin.cpp +++ b/src/plugins/qmljstools/qmljstoolsplugin.cpp @@ -29,6 +29,8 @@ #include "qmljstoolsplugin.h" #include "qmljsmodelmanager.h" +#include "qmljsfunctionfilter.h" +#include "qmljslocatordata.h" #include @@ -74,6 +76,10 @@ bool QmlJSToolsPlugin::initialize(const QStringList &arguments, QString *error) // m_modelManager, SLOT(updateSourceFiles(QStringList))); addAutoReleasedObject(m_modelManager); + LocatorData *locatorData = new LocatorData; + addAutoReleasedObject(locatorData); + addAutoReleasedObject(new FunctionFilter(locatorData)); + return true; }