OSDN Git Service

QmlJS: Add a filter for functions to the locator.
authorChristian Kamm <christian.d.kamm@nokia.com>
Tue, 16 Nov 2010 12:54:50 +0000 (13:54 +0100)
committerChristian Kamm <christian.d.kamm@nokia.com>
Tue, 16 Nov 2010 14:27:26 +0000 (15:27 +0100)
Task-number: QTCREATORBUG-2607
Reviewed-by: Erik Verbruggen
src/plugins/qmljstools/QmlJSTools.pluginspec.in
src/plugins/qmljstools/qmljsfunctionfilter.cpp [new file with mode: 0644]
src/plugins/qmljstools/qmljsfunctionfilter.h [new file with mode: 0644]
src/plugins/qmljstools/qmljslocatordata.cpp [new file with mode: 0644]
src/plugins/qmljstools/qmljslocatordata.h [new file with mode: 0644]
src/plugins/qmljstools/qmljstools-lib.pri
src/plugins/qmljstools/qmljstoolsplugin.cpp

index 0128ca3..169376d 100644 (file)
@@ -16,5 +16,6 @@ Alternatively, this plugin may be used under the terms of the GNU Lesser General
     <dependencyList>
         <dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/>
         <dependency name=\"ProjectExplorer\" version=\"$$QTCREATOR_VERSION\"/>
+        <dependency name=\"Locator\" version=\"$$QTCREATOR_VERSION\"/>
     </dependencyList>
 </plugin>
diff --git a/src/plugins/qmljstools/qmljsfunctionfilter.cpp b/src/plugins/qmljstools/qmljsfunctionfilter.cpp
new file mode 100644 (file)
index 0000000..4f09785
--- /dev/null
@@ -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 <texteditor/itexteditor.h>
+#include <texteditor/basetexteditor.h>
+
+#include <QtCore/QStringMatcher>
+
+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<void> &)
+{
+}
+
+static bool compareLexigraphically(const Locator::FilterEntry &a,
+                                   const Locator::FilterEntry &b)
+{
+    return a.displayName < b.displayName;
+}
+
+QList<Locator::FilterEntry> FunctionFilter::matchesFor(QFutureInterface<Locator::FilterEntry> &future, const QString &origEntry)
+{
+    QString entry = trimWildcards(origEntry);
+    QList<Locator::FilterEntry> goodEntries;
+    QList<Locator::FilterEntry> 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<QString, QList<LocatorData::Entry> > it(m_data->entries());
+    while (it.hasNext()) {
+        if (future.isCanceled())
+            break;
+
+        it.next();
+
+        const QList<LocatorData::Entry> 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<LocatorData::Entry>(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 (file)
index 0000000..d79fed8
--- /dev/null
@@ -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 <locator/ilocatorfilter.h>
+
+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<Locator::FilterEntry> matchesFor(QFutureInterface<Locator::FilterEntry> &future, const QString &entry);
+    void accept(Locator::FilterEntry selection) const;
+    void refresh(QFutureInterface<void> &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 (file)
index 0000000..4b9b45e
--- /dev/null
@@ -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 <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljs/qmljsbind.h>
+#include <qmljs/qmljsinterpreter.h>
+#include <qmljs/parser/qmljsast_p.h>
+
+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<UiScriptBinding *>(member->member)) {
+            if (!script->qualifiedId || !script->qualifiedId->name || script->qualifiedId->next)
+                continue;
+            if (script->qualifiedId->name->asString() != QLatin1String("id"))
+                continue;
+            if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(script->statement)) {
+                if (IdentifierExpression *identExp = cast<IdentifierExpression *>(expStmt->expression)) {
+                    if (identExp->name)
+                        return identExp->name->asString();
+                }
+            }
+        }
+    }
+    return QString();
+}
+
+class FunctionFinder : protected AST::Visitor
+{
+    QList<LocatorData::Entry> m_entries;
+    Document::Ptr m_doc;
+    QString m_context;
+    QString m_documentContext;
+
+public:
+    FunctionFinder()
+    {}
+
+    QList<LocatorData::Entry> 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<FunctionExpression *>(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<Block *>(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<QString, QList<LocatorData::Entry> > LocatorData::entries() const
+{
+    return m_entries;
+}
+
+void LocatorData::onDocumentUpdated(const QmlJS::Document::Ptr &doc)
+{
+    QList<Entry> 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 (file)
index 0000000..9357fca
--- /dev/null
@@ -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 <qmljs/qmljsdocument.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QHash>
+
+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<QString, QList<Entry> > entries() const;
+
+private slots:
+    void onDocumentUpdated(const QmlJS::Document::Ptr &doc);
+    void onAboutToRemoveFiles(const QStringList &files);
+
+private:
+    QHash<QString, QList<Entry> > m_entries;
+};
+
+} // namespace Internal
+} // namespace QmlJSTools
+
+#endif // QMLJSLOCATORDATA_H
index ca1997b..14e1c76 100644 (file)
@@ -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
index b4b13c4..7c6b305 100644 (file)
@@ -29,6 +29,8 @@
 
 #include "qmljstoolsplugin.h"
 #include "qmljsmodelmanager.h"
+#include "qmljsfunctionfilter.h"
+#include "qmljslocatordata.h"
 
 #include <extensionsystem/pluginmanager.h>
 
@@ -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;
 }