QmlJS: Add initial 'Find Usages' support.

This commit is contained in:
Christian Kamm
2010-09-24 14:05:34 +02:00
parent 6755edc351
commit 3f505e9982
20 changed files with 967 additions and 142 deletions

View File

@@ -35,6 +35,7 @@
#include "qmljseditorcodeformatter.h"
#include "qmljsquickfix.h"
#include "qmloutlinemodel.h"
#include "qmljsfindreferences.h"
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljscheck.h>
@@ -676,7 +677,8 @@ QmlJSTextEditor::QmlJSTextEditor(QWidget *parent) :
m_outlineModel(new QmlOutlineModel(this)),
m_modelManager(0),
m_contextPane(0),
m_updateSelectedElements(false)
m_updateSelectedElements(false),
m_findReferences(new FindReferences(this))
{
qRegisterMetaType<QmlJSEditor::Internal::SemanticInfo>("QmlJSEditor::Internal::SemanticInfo");
@@ -1434,6 +1436,11 @@ void QmlJSTextEditor::followSymbolUnderCursor()
openLink(findLinkAt(textCursor()));
}
void QmlJSTextEditor::findUsages()
{
m_findReferences->findUsages(file()->fileName(), textCursor().position());
}
void QmlJSTextEditor::showContextPane()
{
if (m_contextPane) {

View File

@@ -63,6 +63,7 @@ namespace QmlJS {
*/
namespace QmlJSEditor {
class Highlighter;
class FindReferences;
namespace Internal {
@@ -244,6 +245,7 @@ public:
public slots:
void followSymbolUnderCursor();
void findUsages();
void showContextPane();
virtual void setFontSettings(const TextEditor::FontSettings &);
@@ -331,6 +333,8 @@ private:
QmlJS::IContextPane *m_contextPane;
int m_oldCursorPosition;
bool m_updateSelectedElements;
FindReferences *m_findReferences;
};
} // namespace Internal

View File

@@ -31,7 +31,8 @@ HEADERS += \
qmljsoutlinetreeview.h \
quicktoolbarsettingspage.h \
quicktoolbar.h \
qmljscomponentnamedialog.h
qmljscomponentnamedialog.h \
qmljsfindreferences.h
SOURCES += \
qmljscodecompletion.cpp \
@@ -56,7 +57,8 @@ SOURCES += \
qmljsoutlinetreeview.cpp \
quicktoolbarsettingspage.cpp \
quicktoolbar.cpp \
qmljscomponentnamedialog.cpp
qmljscomponentnamedialog.cpp \
qmljsfindreferences.cpp
RESOURCES += qmljseditor.qrc
OTHER_FILES += QmlJSEditor.pluginspec QmlJSEditor.mimetypes.xml

View File

@@ -47,8 +47,10 @@ const char * const RUN_SEP = "QmlJSEditor.Run.Separator";
const char * const C_QMLJSEDITOR_ID = "QMLProjectManager.QMLJSEditor";
const char * const C_QMLJSEDITOR_DISPLAY_NAME = QT_TRANSLATE_NOOP("OpenWith::Editors", "QMLJS Editor");
const char * const TASK_INDEX = "QmlJSEditor.TaskIndex";
const char * const TASK_SEARCH = "QmlJSEditor.TaskSearch";
const char * const FOLLOW_SYMBOL_UNDER_CURSOR = "QmlJSEditor.FollowSymbolUnderCursor";
const char * const FIND_USAGES = "QmlJSEditor.FindUsages";
const char * const SHOW_QT_QUICK_HELPER = "QmlJSEditor.ShowQtQuickHelper";
const char * const QML_MIMETYPE = "application/x-qml";

View File

@@ -164,6 +164,13 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
contextMenu->addAction(cmd);
qmlToolsMenu->addAction(cmd);
QAction *findUsagesAction = new QAction(tr("Find Usages"), this);
cmd = am->registerAction(findUsagesAction, Constants::FIND_USAGES, context);
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+U")));
connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(findUsages()));
contextMenu->addAction(cmd);
qmlToolsMenu->addAction(cmd);
QAction *showQuickToolbar = new QAction(tr("Show Qt Quick Toolbar"), this);
cmd = am->registerAction(showQuickToolbar, Constants::SHOW_QT_QUICK_HELPER, context);
cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Space));
@@ -261,6 +268,13 @@ void QmlJSEditorPlugin::followSymbolUnderCursor()
editor->followSymbolUnderCursor();
}
void QmlJSEditorPlugin::findUsages()
{
Core::EditorManager *em = Core::EditorManager::instance();
if (QmlJSTextEditor *editor = qobject_cast<QmlJSTextEditor*>(em->currentEditor()->widget()))
editor->findUsages();
}
void QmlJSEditorPlugin::showContextPane()
{
Core::EditorManager *em = Core::EditorManager::instance();

View File

@@ -89,6 +89,7 @@ public:
public Q_SLOTS:
void followSymbolUnderCursor();
void findUsages();
void showContextPane();
private Q_SLOTS:

View File

@@ -0,0 +1,685 @@
/**************************************************************************
**
** 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 "qmljsfindreferences.h"
#include <texteditor/basetexteditor.h>
#include <texteditor/basefilefind.h>
#include <find/searchresultwindow.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/filesearch.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljslink.h>
#include <qmljs/qmljsevaluate.h>
#include <qmljs/qmljsscopebuilder.h>
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/parser/qmljsast_p.h>
#include "qmljseditorconstants.h"
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtCore/QtConcurrentRun>
#include <QtCore/QtConcurrentMap>
#include <QtCore/QDir>
#include <QtGui/QApplication>
#include <qtconcurrent/runextensions.h>
#include <functional>
using namespace QmlJS;
using namespace QmlJS::Interpreter;
using namespace QmlJS::AST;
using namespace QmlJSEditor;
static const ObjectValue *prototypeWithMember(const Context *context, const ObjectValue *object, const QString &name)
{
if (!object)
return 0;
const Value *value = object->property(name, context);
if (!value)
return 0;
forever {
const ObjectValue *prototype = object->prototype(context);
if (!prototype || prototype->property(name, context) != value)
return object;
object = prototype;
}
}
namespace {
// ### These visitors could be useful in general
class FindUsages: protected Visitor
{
public:
typedef QList<AST::SourceLocation> Result;
FindUsages(Document::Ptr doc, const Snapshot &snapshot, Context *context)
: _doc(doc)
, _snapshot(snapshot)
, _context(context)
, _builder(context, doc, snapshot)
{
}
Result operator()(const QString &name, const ObjectValue *scope)
{
_name = name;
_scope = scope;
_usages.clear();
if (_doc)
Node::accept(_doc->ast(), this);
return _usages;
}
protected:
void accept(AST::Node *node)
{ AST::Node::acceptChild(node, this); }
using Visitor::visit;
virtual bool visit(AST::UiPublicMember *node)
{
if (node->name
&& node->name->asString() == _name
&& _context->scopeChain().qmlScopeObjects.contains(_scope)) {
_usages.append(node->identifierToken);
}
return true;
}
virtual bool visit(AST::UiObjectDefinition *node)
{
_builder.push(node);
Node::accept(node->initializer, this);
_builder.pop();
return false;
}
virtual bool visit(AST::UiObjectBinding *node)
{
if (node->qualifiedId
&& !node->qualifiedId->next
&& node->qualifiedId->name->asString() == _name
&& checkQmlScope()) {
_usages.append(node->qualifiedId->identifierToken);
}
_builder.push(node);
Node::accept(node->initializer, this);
_builder.pop();
return false;
}
virtual bool visit(AST::UiScriptBinding *node)
{
if (node->qualifiedId
&& !node->qualifiedId->next
&& node->qualifiedId->name->asString() == _name
&& checkQmlScope()) {
_usages.append(node->qualifiedId->identifierToken);
}
return true;
}
virtual bool visit(AST::UiArrayBinding *node)
{
if (node->qualifiedId
&& !node->qualifiedId->next
&& node->qualifiedId->name->asString() == _name
&& checkQmlScope()) {
_usages.append(node->qualifiedId->identifierToken);
}
return true;
}
virtual bool visit(AST::IdentifierExpression *node)
{
if (!node->name || node->name->asString() != _name)
return false;
const ObjectValue *scope;
_context->lookup(_name, &scope);
if (!scope)
return false;
if (check(scope)) {
_usages.append(node->identifierToken);
return false;
}
// the order of scopes in 'instantiatingComponents' is undefined,
// so it might still be a use - we just found a different value in a different scope first
// if scope is one of these, our match wasn't inside the instantiating components list
const ScopeChain &chain = _context->scopeChain();
if (chain.jsScopes.contains(scope)
|| chain.qmlScopeObjects.contains(scope)
|| chain.qmlTypes == scope
|| chain.globalScope == scope)
return false;
if (contains(chain.qmlComponentScope.data()))
_usages.append(node->identifierToken);
return false;
}
virtual bool visit(AST::FieldMemberExpression *node)
{
if (!node->name || node->name->asString() != _name)
return true;
Evaluate evaluate(_context);
const Value *lhsValue = evaluate(node->base);
if (!lhsValue)
return true;
if (check(lhsValue->asObjectValue())) // passing null is ok
_usages.append(node->identifierToken);
return true;
}
virtual bool visit(AST::FunctionDeclaration *node)
{
if (node->name && node->name->asString() == _name) {
if (checkLookup())
_usages.append(node->identifierToken);
}
Node::accept(node->formals, this);
_builder.push(node);
Node::accept(node->body, this);
_builder.pop();
return false;
}
virtual bool visit(AST::VariableDeclaration *node)
{
if (node->name && node->name->asString() == _name) {
if (checkLookup())
_usages.append(node->identifierToken);
}
return true;
}
private:
bool contains(const ScopeChain::QmlComponentChain *chain)
{
if (!chain || !chain->document)
return false;
if (chain->document->bind()->idEnvironment()->property(_name, _context))
return chain->document->bind()->idEnvironment() == _scope;
const ObjectValue *root = chain->document->bind()->rootObjectValue();
if (root->property(_name, _context)) {
return check(root);
}
foreach (const ScopeChain::QmlComponentChain *parent, chain->instantiatingComponents) {
if (contains(parent))
return true;
}
return false;
}
bool check(const ObjectValue *s)
{
if (!s)
return false;
return prototypeWithMember(_context, s, _name) == _scope;
}
bool checkQmlScope()
{
foreach (const ObjectValue *s, _context->scopeChain().qmlScopeObjects) {
if (check(s))
return true;
}
return false;
}
bool checkLookup()
{
const ObjectValue *scope = 0;
_context->lookup(_name, &scope);
return check(scope);
}
Result _usages;
Document::Ptr _doc;
Snapshot _snapshot;
Context *_context;
ScopeBuilder _builder;
QString _name;
const ObjectValue *_scope;
};
class ScopeAstPath: protected Visitor
{
public:
ScopeAstPath(Document::Ptr doc)
: _doc(doc)
{
}
QList<Node *> operator()(quint32 offset)
{
_result.clear();
_offset = offset;
if (_doc)
Node::accept(_doc->ast(), this);
return _result;
}
protected:
void accept(AST::Node *node)
{ AST::Node::acceptChild(node, this); }
using Visitor::visit;
virtual bool preVisit(Node *node)
{
if (Statement *stmt = node->statementCast()) {
return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
} else if (ExpressionNode *exp = node->expressionCast()) {
return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
} else if (UiObjectMember *ui = node->uiObjectMemberCast()) {
return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
}
return true;
}
virtual bool visit(AST::UiObjectDefinition *node)
{
_result.append(node);
Node::accept(node->initializer, this);
return false;
}
virtual bool visit(AST::UiObjectBinding *node)
{
_result.append(node);
Node::accept(node->initializer, this);
return false;
}
virtual bool visit(AST::FunctionDeclaration *node)
{
Node::accept(node->formals, this);
_result.append(node);
Node::accept(node->body, this);
return false;
}
private:
bool containsOffset(SourceLocation start, SourceLocation end)
{
return _offset >= start.begin() && _offset <= end.end();
}
QList<Node *> _result;
Document::Ptr _doc;
quint32 _offset;
};
class FindTargetExpression: protected Visitor
{
public:
FindTargetExpression(Document::Ptr doc)
: _doc(doc)
{
}
QPair<Node *, QString> operator()(quint32 offset)
{
_result = qMakePair((Node *)0, QString());
_offset = offset;
if (_doc)
Node::accept(_doc->ast(), this);
return _result;
}
protected:
void accept(AST::Node *node)
{ AST::Node::acceptChild(node, this); }
using Visitor::visit;
virtual bool preVisit(Node *node)
{
if (Statement *stmt = node->statementCast()) {
return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
} else if (ExpressionNode *exp = node->expressionCast()) {
return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
} else if (UiObjectMember *ui = node->uiObjectMemberCast()) {
return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
}
return true;
}
virtual bool visit(IdentifierExpression *node)
{
if (containsOffset(node->identifierToken))
_result.second = node->name->asString();
return true;
}
virtual bool visit(FieldMemberExpression *node)
{
if (containsOffset(node->identifierToken)) {
_result.first = node->base;
_result.second = node->name->asString();
return false;
}
return true;
}
virtual bool visit(UiScriptBinding *node)
{
return !checkBindingName(node->qualifiedId);
}
virtual bool visit(UiArrayBinding *node)
{
return !checkBindingName(node->qualifiedId);
}
virtual bool visit(UiObjectBinding *node)
{
return !checkBindingName(node->qualifiedId);
}
virtual bool visit(UiPublicMember *node)
{
if (containsOffset(node->identifierToken)) {
_result.second = node->name->asString();
return false;
}
return true;
}
// ### Misses function declaration, var declaration
virtual bool visit(FunctionDeclaration *node)
{
if (containsOffset(node->identifierToken)) {
_result.second = node->name->asString();
return false;
}
return true;
}
virtual bool visit(VariableDeclaration *node)
{
if (containsOffset(node->identifierToken)) {
_result.second = node->name->asString();
return false;
}
return true;
}
private:
bool containsOffset(SourceLocation start, SourceLocation end)
{
return _offset >= start.begin() && _offset <= end.end();
}
bool containsOffset(SourceLocation loc)
{
return _offset >= loc.begin() && _offset <= loc.end();
}
bool checkBindingName(UiQualifiedId *id)
{
if (id && !id->next && containsOffset(id->identifierToken)) {
_result.second = id->name->asString();
return true;
}
return false;
}
QPair<Node *, QString> _result;
Document::Ptr _doc;
quint32 _offset;
};
class ProcessFile: public std::unary_function<QString, QList<FindReferences::Usage> >
{
const Snapshot &snapshot;
const Context &context;
typedef FindReferences::Usage Usage;
QString name;
const ObjectValue *scope;
public:
ProcessFile(const Snapshot &snapshot,
const Context &context,
QString name,
const ObjectValue *scope)
: snapshot(snapshot), context(context), name(name), scope(scope)
{ }
QList<Usage> operator()(const QString &fileName)
{
QList<Usage> usages;
Document::Ptr doc = snapshot.document(fileName);
if (!doc)
return usages;
Context contextCopy(context);
// find all idenfifier expressions, try to resolve them and check if the result is in scope
FindUsages findUsages(doc, snapshot, &contextCopy);
FindUsages::Result results = findUsages(name, scope);
foreach (AST::SourceLocation loc, results)
usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
return usages;
}
static QString matchingLine(unsigned position, const QString &source)
{
int start = source.lastIndexOf(QLatin1Char('\n'), position);
start += 1;
int end = source.indexOf(QLatin1Char('\n'), position);
return source.mid(start, end - start);
}
};
class UpdateUI: public std::binary_function<QList<FindReferences::Usage> &, QList<FindReferences::Usage>, void>
{
typedef FindReferences::Usage Usage;
QFutureInterface<Usage> *future;
public:
UpdateUI(QFutureInterface<Usage> *future): future(future) {}
void operator()(QList<Usage> &, const QList<Usage> &usages)
{
foreach (const Usage &u, usages)
future->reportResult(u);
future->setProgressValue(future->progressValue() + 1);
}
};
} // end of anonymous namespace
FindReferences::FindReferences(QObject *parent)
: QObject(parent)
, _resultWindow(Find::SearchResultWindow::instance())
{
m_watcher.setPendingResultsLimit(1);
connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
}
FindReferences::~FindReferences()
{
}
static void find_helper(QFutureInterface<FindReferences::Usage> &future,
const ModelManagerInterface::WorkingCopy workingCopy,
Snapshot snapshot,
const QString fileName,
quint32 offset)
{
// update snapshot from workingCopy to make sure it's up to date
// ### remove?
// ### this is a great candidate for map-reduce
QHashIterator< QString, QPair<QString, int> > it(workingCopy.all());
while (it.hasNext()) {
it.next();
Document::Ptr oldDoc = snapshot.document(it.key());
if (oldDoc && oldDoc->editorRevision() == it.value().second)
continue;
Document::Ptr newDoc = snapshot.documentFromSource(it.key(), it.value().first);
newDoc->parse();
snapshot.insert(newDoc);
}
// find the scope for the name we're searching
Context context;
Document::Ptr doc = snapshot.document(fileName);
if (!doc)
return;
Link link(&context, doc, snapshot, ModelManagerInterface::instance()->importPaths());
ScopeBuilder builder(&context, doc, snapshot);
ScopeAstPath astPath(doc);
builder.push(astPath(offset));
FindTargetExpression findTarget(doc);
QPair<Node *, QString> target = findTarget(offset);
const QString &name = target.second;
if (name.isEmpty())
return;
const ObjectValue *scope = 0;
if (target.first) {
Evaluate evaluate(&context);
const Value *v = evaluate(target.first);
if (v)
scope = v->asObjectValue();
} else {
context.lookup(name, &scope);
}
if (!scope)
return;
scope = prototypeWithMember(&context, scope, name);
if (!scope)
return;
QStringList files;
foreach (const Document::Ptr &doc, snapshot) {
// ### skip files that don't contain the name token
files.append(doc->fileName());
}
future.setProgressRange(0, files.size());
ProcessFile process(snapshot, context, name, scope);
UpdateUI reduce(&future);
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
future.setProgressValue(files.size());
}
void FindReferences::findUsages(const QString &fileName, quint32 offset)
{
Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);
connect(search, SIGNAL(activated(Find::SearchResultItem)),
this, SLOT(openEditor(Find::SearchResultItem)));
findAll_helper(fileName, offset);
}
void FindReferences::findAll_helper(const QString &fileName, quint32 offset)
{
_resultWindow->popup(true);
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
QFuture<Usage> result = QtConcurrent::run(
&find_helper, modelManager->workingCopy(),
modelManager->snapshot(), fileName, offset);
m_watcher.setFuture(result);
Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
QmlJSEditor::Constants::TASK_SEARCH);
connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
}
void FindReferences::displayResults(int first, int last)
{
for (int index = first; index != last; ++index) {
Usage result = m_watcher.future().resultAt(index);
_resultWindow->addResult(result.path,
result.line,
result.lineText,
result.col,
result.len);
}
}
void FindReferences::searchFinished()
{
_resultWindow->finishSearch();
emit changed();
}
void FindReferences::openEditor(const Find::SearchResultItem &item)
{
if (item.path.size() > 0) {
TextEditor::BaseTextEditor::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos,
QString(),
Core::EditorManager::ModeSwitch);
} else {
Core::EditorManager::instance()->openEditor(item.text, QString(), Core::EditorManager::ModeSwitch);
}
}

View File

@@ -0,0 +1,97 @@
/**************************************************************************
**
** 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 QMLJSFINDREFERENCES_H
#define QMLJSFINDREFERENCES_H
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QPointer>
#include <QtCore/QFuture>
#include <QtCore/QFutureWatcher>
#include <utils/filesearch.h>
#include <qmljs/qmljsdocument.h>
#include <qmljs/qmljslookupcontext.h>
QT_FORWARD_DECLARE_CLASS(QTimer)
namespace Find {
class SearchResultWindow;
struct SearchResultItem;
} // end of namespace Find
namespace QmlJSEditor {
class FindReferences: public QObject
{
Q_OBJECT
public:
class Usage
{
public:
Usage()
: line(0), col(0), len(0) {}
Usage(const QString &path, const QString &lineText, int line, int col, int len)
: path(path), lineText(lineText), line(line), col(col), len(len) {}
public:
QString path;
QString lineText;
int line;
int col;
int len;
};
public:
FindReferences(QObject *parent = 0);
virtual ~FindReferences();
Q_SIGNALS:
void changed();
public:
void findUsages(const QString &fileName, quint32 offset);
private Q_SLOTS:
void displayResults(int first, int last);
void searchFinished();
void openEditor(const Find::SearchResultItem &item);
private:
void findAll_helper(const QString &fileName, quint32 offset);
private:
Find::SearchResultWindow *_resultWindow;
QFutureWatcher<Usage> m_watcher;
};
} // end of namespace QmlJSEditor
#endif // QMLJSFINDREFERENCES_H

View File

@@ -103,6 +103,24 @@ void ModelManager::loadQmlTypeDescriptions(const QString &resourcePath)
//loadQmlPluginTypes(QString());
}
ModelManagerInterface::WorkingCopy ModelManager::workingCopy() const
{
WorkingCopy workingCopy;
Core::EditorManager *editorManager = m_core->editorManager();
foreach (Core::IEditor *editor, editorManager->openedEditors()) {
const QString key = editor->file()->fileName();
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) {
workingCopy.insert(key, ed->toPlainText(), ed->document()->revision());
}
}
}
return workingCopy;
}
Snapshot ModelManager::snapshot() const
{
QMutexLocker locker(&m_mutex);
@@ -123,10 +141,8 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles,
return QFuture<void>();
}
const QMap<QString, WorkingCopy> workingCopy = buildWorkingCopyList();
QFuture<void> result = QtConcurrent::run(&ModelManager::parse,
workingCopy, sourceFiles,
workingCopy(), sourceFiles,
this,
emitDocumentOnDiskChanged);
@@ -151,29 +167,10 @@ QFuture<void> ModelManager::refreshSourceFiles(const QStringList &sourceFiles,
return result;
}
QMap<QString, ModelManager::WorkingCopy> ModelManager::buildWorkingCopyList()
{
QMap<QString, WorkingCopy> workingCopy;
Core::EditorManager *editorManager = m_core->editorManager();
foreach (Core::IEditor *editor, editorManager->openedEditors()) {
const QString key = editor->file()->fileName();
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) {
workingCopy[key].contents = ed->toPlainText();
workingCopy[key].documentRevision = ed->document()->revision();
}
}
}
return workingCopy;
}
void ModelManager::fileChangedOnDisk(const QString &path)
{
QtConcurrent::run(&ModelManager::parse,
buildWorkingCopyList(), QStringList() << path,
workingCopy(), QStringList() << path,
this, true);
}
@@ -340,7 +337,7 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap
}
void ModelManager::parse(QFutureInterface<void> &future,
QMap<QString, WorkingCopy> workingCopy,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
bool emitDocChangedOnDisk)
@@ -377,9 +374,9 @@ void ModelManager::parse(QFutureInterface<void> &future,
int documentRevision = 0;
if (workingCopy.contains(fileName)) {
WorkingCopy wc = workingCopy.value(fileName);
contents = wc.contents;
documentRevision = wc.documentRevision;
QPair<QString, int> entry = workingCopy.get(fileName);
contents = entry.first;
documentRevision = entry.second;
} else {
QFile inFile(fileName);

View File

@@ -53,7 +53,9 @@ class ModelManager: public QmlJS::ModelManagerInterface
public:
ModelManager(QObject *parent = 0);
virtual WorkingCopy workingCopy() const;
virtual QmlJS::Snapshot snapshot() const;
virtual void updateSourceFiles(const QStringList &files,
bool emitDocumentOnDiskChanged);
virtual void fileChangedOnDisk(const QString &path);
@@ -84,19 +86,11 @@ private Q_SLOTS:
void qmlPluginTypeDumpError(QProcess::ProcessError error);
protected:
struct WorkingCopy
{
WorkingCopy(int revision = 0): documentRevision(revision) {}
int documentRevision;
QString contents;
};
QFuture<void> refreshSourceFiles(const QStringList &sourceFiles,
bool emitDocumentOnDiskChanged);
QMap<QString, WorkingCopy> buildWorkingCopyList();
static void parse(QFutureInterface<void> &future,
QMap<QString, WorkingCopy> workingCopy,
WorkingCopy workingCopy,
QStringList files,
ModelManager *modelManager,
bool emitDocChangedOnDisk);