2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2022-08-25 12:46:47 +02:00
|
|
|
#include "qmljseditortr.h"
|
2010-09-24 14:05:34 +02:00
|
|
|
#include "qmljsfindreferences.h"
|
|
|
|
|
|
2020-11-12 10:05:19 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2014-01-13 16:17:34 +01:00
|
|
|
#include <coreplugin/find/searchresultwindow.h>
|
2020-11-12 10:05:19 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
#include <coreplugin/progressmanager/futureprogress.h>
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
#include <coreplugin/progressmanager/progressmanager.h>
|
2020-11-12 10:05:19 +01:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
#include <texteditor/basefilefind.h>
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
#include <utils/algorithm.h>
|
2023-05-03 15:05:47 +02:00
|
|
|
#include <utils/async.h>
|
2020-11-12 10:05:19 +01:00
|
|
|
#include <utils/filesearch.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
|
|
|
|
#include <qmljs/qmljsbind.h>
|
|
|
|
|
#include <qmljs/qmljslink.h>
|
|
|
|
|
#include <qmljs/qmljsevaluate.h>
|
|
|
|
|
#include <qmljs/qmljsscopebuilder.h>
|
2011-05-04 11:12:45 +02:00
|
|
|
#include <qmljs/qmljsscopeastpath.h>
|
2011-07-01 13:51:53 +02:00
|
|
|
#include <qmljs/qmljscontext.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
#include <qmljs/parser/qmljsastvisitor_p.h>
|
|
|
|
|
#include <qmljs/parser/qmljsast_p.h>
|
2011-09-09 10:55:11 +02:00
|
|
|
#include <qmljstools/qmljsmodelmanager.h>
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
#include <QDebug>
|
2020-11-12 10:05:19 +01:00
|
|
|
#include <QFuture>
|
|
|
|
|
#include <QtConcurrentMap>
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2013-08-30 09:22:42 +02:00
|
|
|
using namespace Core;
|
2010-09-24 14:05:34 +02:00
|
|
|
using namespace QmlJS;
|
|
|
|
|
using namespace QmlJS::AST;
|
|
|
|
|
using namespace QmlJSEditor;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
// ### These visitors could be useful in general
|
|
|
|
|
|
|
|
|
|
class FindUsages: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-02-28 17:51:32 +01:00
|
|
|
using Result = QList<SourceLocation>;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
FindUsages(Document::Ptr doc, const ContextPtr &context)
|
2010-09-24 14:05:34 +02:00
|
|
|
: _doc(doc)
|
2011-07-12 14:55:27 +02:00
|
|
|
, _scopeChain(doc, context)
|
|
|
|
|
, _builder(&_scopeChain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result operator()(const QString &name, const ObjectValue *scope)
|
|
|
|
|
{
|
|
|
|
|
_name = name;
|
|
|
|
|
_scope = scope;
|
|
|
|
|
_usages.clear();
|
|
|
|
|
if (_doc)
|
|
|
|
|
Node::accept(_doc->ast(), this);
|
|
|
|
|
return _usages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2021-12-03 15:49:28 +01:00
|
|
|
void accept(AST::Node *node) { AST::Node::accept(node, this); }
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
using Visitor::visit;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiPublicMember *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name == _name
|
2011-07-12 14:55:27 +02:00
|
|
|
&& _scopeChain.qmlScopeObjects().contains(_scope)) {
|
2010-09-24 14:05:34 +02:00
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectDefinition *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 09:44:24 +01:00
|
|
|
bool visit(AST::TemplateLiteral *el) override
|
|
|
|
|
{
|
|
|
|
|
Node::accept(el->expression, this);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiScriptBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
Node::accept(node->qualifiedId, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiArrayBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (node->qualifiedId
|
|
|
|
|
&& !node->qualifiedId->next
|
2011-09-13 09:57:24 +02:00
|
|
|
&& node->qualifiedId->name == _name
|
2010-09-24 14:05:34 +02:00
|
|
|
&& checkQmlScope()) {
|
|
|
|
|
_usages.append(node->qualifiedId->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::IdentifierExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name.isEmpty() || node->name != _name)
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain.lookup(_name, &scope);
|
2010-09-24 14:05:34 +02:00
|
|
|
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
|
2011-07-12 14:55:27 +02:00
|
|
|
const ScopeChain &chain = _scopeChain;
|
|
|
|
|
if (chain.jsScopes().contains(scope)
|
|
|
|
|
|| chain.qmlScopeObjects().contains(scope)
|
|
|
|
|
|| chain.qmlTypes() == scope
|
|
|
|
|
|| chain.globalScope() == scope)
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
if (contains(chain.qmlComponentChain().data()))
|
2010-09-24 14:05:34 +02:00
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FieldMemberExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(&_scopeChain);
|
2010-09-24 14:05:34 +02:00
|
|
|
const Value *lhsValue = evaluate(node->base);
|
|
|
|
|
if (!lhsValue)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (check(lhsValue->asObjectValue())) // passing null is ok
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionDeclaration *node) override
|
2010-11-24 14:42:10 +01:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name == _name) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (checkLookup())
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
Node::accept(node->formals, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->body, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(AST::PatternElement *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration() && node->bindingIdentifier == _name) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (checkLookup())
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
void throwRecursionDepthError() override
|
|
|
|
|
{
|
|
|
|
|
qWarning("Warning: Hit maximum recursion depth while visitin AST in FindUsages");
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
private:
|
2011-07-12 14:55:27 +02:00
|
|
|
bool contains(const QmlComponentChain *chain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-11-29 12:53:06 +01:00
|
|
|
if (!chain || !chain->document() || !chain->document()->bind())
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
|
2011-11-29 12:53:06 +01:00
|
|
|
const ObjectValue *idEnv = chain->document()->bind()->idEnvironment();
|
|
|
|
|
if (idEnv && idEnv->lookupMember(_name, _scopeChain.context()))
|
|
|
|
|
return idEnv == _scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
const ObjectValue *root = chain->document()->bind()->rootObjectValue();
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (root && root->lookupMember(_name, _scopeChain.context()))
|
2010-09-24 14:05:34 +02:00
|
|
|
return check(root);
|
|
|
|
|
|
2022-05-17 16:11:03 +02:00
|
|
|
const QList<const QmlComponentChain *> parents = chain->instantiatingComponents();
|
|
|
|
|
for (const QmlComponentChain *parent : parents) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (contains(parent))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool check(const ObjectValue *s)
|
|
|
|
|
{
|
|
|
|
|
if (!s)
|
|
|
|
|
return false;
|
2010-11-17 14:20:06 +01:00
|
|
|
const ObjectValue *definingObject;
|
2011-07-12 14:55:27 +02:00
|
|
|
s->lookupMember(_name, _scopeChain.context(), &definingObject);
|
2010-11-17 14:20:06 +01:00
|
|
|
return definingObject == _scope;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkQmlScope()
|
|
|
|
|
{
|
2022-05-17 16:11:03 +02:00
|
|
|
const QList<const ObjectValue *> scopes = _scopeChain.qmlScopeObjects();
|
|
|
|
|
for (const ObjectValue *s : scopes) {
|
2010-09-24 14:05:34 +02:00
|
|
|
if (check(s))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool checkLookup()
|
|
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *scope = nullptr;
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain.lookup(_name, &scope);
|
2010-09-24 14:05:34 +02:00
|
|
|
return check(scope);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result _usages;
|
|
|
|
|
|
|
|
|
|
Document::Ptr _doc;
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeChain _scopeChain;
|
2010-09-24 14:05:34 +02:00
|
|
|
ScopeBuilder _builder;
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_scope = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
};
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
class FindTypeUsages: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2020-02-28 17:51:32 +01:00
|
|
|
using Result = QList<SourceLocation>;
|
2011-06-06 11:17:12 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
FindTypeUsages(Document::Ptr doc, const ContextPtr &context)
|
2011-06-06 11:17:12 +02:00
|
|
|
: _doc(doc)
|
|
|
|
|
, _context(context)
|
2011-07-12 14:55:27 +02:00
|
|
|
, _scopeChain(doc, context)
|
|
|
|
|
, _builder(&_scopeChain)
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result operator()(const QString &name, const ObjectValue *typeValue)
|
|
|
|
|
{
|
|
|
|
|
_name = name;
|
|
|
|
|
_typeValue = typeValue;
|
|
|
|
|
_usages.clear();
|
|
|
|
|
if (_doc)
|
|
|
|
|
Node::accept(_doc->ast(), this);
|
|
|
|
|
return _usages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2021-12-03 15:49:28 +01:00
|
|
|
void accept(AST::Node *node) { AST::Node::accept(node, this); }
|
2011-06-06 11:17:12 +02:00
|
|
|
|
|
|
|
|
using Visitor::visit;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiPublicMember *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2019-02-13 15:38:58 +01:00
|
|
|
if (UiQualifiedId *memberType = node->memberType) {
|
|
|
|
|
if (memberType->name == _name) {
|
|
|
|
|
const ObjectValue * tVal = _context->lookupType(_doc.data(), QStringList(_name));
|
|
|
|
|
if (tVal == _typeValue)
|
|
|
|
|
_usages.append(node->typeToken);
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
}
|
2011-06-06 14:13:50 +02:00
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectDefinition *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
checkTypeName(node->qualifiedTypeNameId);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiObjectBinding *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
checkTypeName(node->qualifiedTypeNameId);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->initializer, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::UiScriptBinding *node) override
|
2011-06-06 14:13:50 +02:00
|
|
|
{
|
|
|
|
|
if (AST::cast<Block *>(node->statement)) {
|
|
|
|
|
Node::accept(node->qualifiedId, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->statement, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::IdentifierExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2011-06-06 11:17:12 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope;
|
2011-07-12 14:55:27 +02:00
|
|
|
const Value *objV = _scopeChain.lookup(_name, &scope);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (objV == _typeValue)
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FieldMemberExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (node->name != _name)
|
2011-06-06 11:17:12 +02:00
|
|
|
return true;
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(&_scopeChain);
|
2011-06-06 11:17:12 +02:00
|
|
|
const Value *lhsValue = evaluate(node->base);
|
|
|
|
|
if (!lhsValue)
|
|
|
|
|
return true;
|
|
|
|
|
const ObjectValue *lhsObj = lhsValue->asObjectValue();
|
|
|
|
|
if (lhsObj && lhsObj->lookupMember(_name, _context) == _typeValue)
|
|
|
|
|
_usages.append(node->identifierToken);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 09:44:24 +01:00
|
|
|
bool visit(AST::TemplateLiteral *el) override
|
|
|
|
|
{
|
|
|
|
|
Node::accept(el->expression, this);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionDeclaration *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(AST::FunctionExpression *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
|
|
|
|
Node::accept(node->formals, this);
|
|
|
|
|
_builder.push(node);
|
|
|
|
|
Node::accept(node->body, this);
|
|
|
|
|
_builder.pop();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(AST::PatternElement *node) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration())
|
|
|
|
|
Node::accept(node->initializer, this);
|
2011-06-06 11:17:12 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiImport *ast) override
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (ast && ast->importId == _name) {
|
2011-06-06 11:17:12 +02:00
|
|
|
const Imports *imp = _context->imports(_doc.data());
|
|
|
|
|
if (!imp)
|
|
|
|
|
return false;
|
|
|
|
|
if (_context->lookupType(_doc.data(), QStringList(_name)) == _typeValue)
|
|
|
|
|
_usages.append(ast->importIdToken);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
void throwRecursionDepthError() override
|
|
|
|
|
{
|
|
|
|
|
qWarning("Warning: Hit maximum recursion depth while visitin AST in FindTypeUsages");
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
bool checkTypeName(UiQualifiedId *id)
|
|
|
|
|
{
|
|
|
|
|
for (UiQualifiedId *att = id; att; att = att->next){
|
2011-09-13 09:57:24 +02:00
|
|
|
if (att->name == _name) {
|
2011-06-06 11:17:12 +02:00
|
|
|
const ObjectValue *objectValue = _context->lookupType(_doc.data(), id, att->next);
|
|
|
|
|
if (_typeValue == objectValue){
|
|
|
|
|
_usages.append(att->identifierToken);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Result _usages;
|
|
|
|
|
|
|
|
|
|
Document::Ptr _doc;
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr _context;
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeChain _scopeChain;
|
2011-06-06 11:17:12 +02:00
|
|
|
ScopeBuilder _builder;
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_typeValue = nullptr;
|
2011-06-06 11:17:12 +02:00
|
|
|
};
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
class FindTargetExpression: protected Visitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
2011-06-06 11:17:12 +02:00
|
|
|
enum Kind {
|
|
|
|
|
ExpKind,
|
|
|
|
|
TypeKind
|
|
|
|
|
};
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
FindTargetExpression(Document::Ptr doc, const ScopeChain *scopeChain)
|
|
|
|
|
: _doc(doc), _scopeChain(scopeChain)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 14:43:08 +01:00
|
|
|
void operator()(quint32 offset)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-04-19 15:42:14 +02:00
|
|
|
_name.clear();
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
|
|
|
|
_objectNode = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
_offset = offset;
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = ExpKind;
|
2010-09-24 14:05:34 +02:00
|
|
|
if (_doc)
|
|
|
|
|
Node::accept(_doc->ast(), this);
|
2011-03-17 14:43:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString name() const
|
|
|
|
|
{ return _name; }
|
|
|
|
|
|
|
|
|
|
const ObjectValue *scope()
|
|
|
|
|
{
|
|
|
|
|
if (!_scope)
|
2011-07-12 14:55:27 +02:00
|
|
|
_scopeChain->lookup(_name, &_scope);
|
2011-03-17 14:43:08 +01:00
|
|
|
return _scope;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
Kind typeKind(){
|
|
|
|
|
return _typeKind;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Value *targetValue(){
|
|
|
|
|
return _targetValue;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
protected:
|
2021-12-03 15:49:28 +01:00
|
|
|
void accept(AST::Node *node) { AST::Node::accept(node, this); }
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
using Visitor::visit;
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool preVisit(Node *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (Statement *stmt = node->statementCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(stmt->firstSourceLocation(), stmt->lastSourceLocation());
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (ExpressionNode *exp = node->expressionCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(exp->firstSourceLocation(), exp->lastSourceLocation());
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (UiObjectMember *ui = node->uiObjectMemberCast())
|
2010-09-24 14:05:34 +02:00
|
|
|
return containsOffset(ui->firstSourceLocation(), ui->lastSourceLocation());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(IdentifierExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!_name.isEmpty()) && _name.at(0).isUpper()) {
|
|
|
|
|
// a possible type
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->lookup(_name, &_scope);
|
2011-10-10 10:55:37 +02:00
|
|
|
if (value_cast<ObjectValue>(_targetValue))
|
2011-06-06 14:13:50 +02:00
|
|
|
_typeKind = TypeKind;
|
2011-06-06 11:17:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FieldMemberExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
setScope(node->base);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!_name.isEmpty()) && _name.at(0).isUpper()) {
|
|
|
|
|
// a possible type
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(_scopeChain);
|
2011-06-06 11:17:12 +02:00
|
|
|
const Value *lhsValue = evaluate(node->base);
|
|
|
|
|
if (!lhsValue)
|
|
|
|
|
return true;
|
|
|
|
|
const ObjectValue *lhsObj = lhsValue->asObjectValue();
|
|
|
|
|
if (lhsObj) {
|
|
|
|
|
_scope = lhsObj;
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = lhsObj->lookupMember(_name, _scopeChain->context());
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-06 09:44:24 +01:00
|
|
|
bool visit(AST::TemplateLiteral *el) override
|
|
|
|
|
{
|
|
|
|
|
Node::accept(el->expression, this);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiScriptBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
return !checkBindingName(node->qualifiedId);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiArrayBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
return !checkBindingName(node->qualifiedId);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiObjectBinding *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if ((!checkTypeName(node->qualifiedTypeNameId)) &&
|
|
|
|
|
(!checkBindingName(node->qualifiedId))) {
|
2011-03-17 14:43:08 +01:00
|
|
|
Node *oldObjectNode = _objectNode;
|
|
|
|
|
_objectNode = node;
|
|
|
|
|
accept(node->initializer);
|
|
|
|
|
_objectNode = oldObjectNode;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiObjectDefinition *node) override
|
2011-03-17 14:43:08 +01:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!checkTypeName(node->qualifiedTypeNameId)) {
|
|
|
|
|
Node *oldObjectNode = _objectNode;
|
|
|
|
|
_objectNode = node;
|
|
|
|
|
accept(node->initializer);
|
|
|
|
|
_objectNode = oldObjectNode;
|
|
|
|
|
}
|
2011-03-17 14:43:08 +01:00
|
|
|
return false;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(UiPublicMember *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
if (containsOffset(node->typeToken)){
|
2021-12-03 15:49:28 +01:00
|
|
|
if (node->defaultToken().isValid()) {
|
2018-10-16 15:32:58 +02:00
|
|
|
_name = node->memberType->name.toString();
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->context()->lookupType(_doc.data(), QStringList(_name));
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} else if (containsOffset(node->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
_scope = _doc->bind()->findQmlObject(_objectNode);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FunctionDeclaration *node) override
|
2010-11-24 14:42:10 +01:00
|
|
|
{
|
|
|
|
|
return visit(static_cast<FunctionExpression *>(node));
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-11 07:31:38 +02:00
|
|
|
bool visit(FunctionExpression *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
if (containsOffset(node->identifierToken)) {
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = node->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 15:32:58 +02:00
|
|
|
bool visit(PatternElement *node) override
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-10-16 15:32:58 +02:00
|
|
|
if (node->isVariableDeclaration() && containsOffset(node->identifierToken)) {
|
|
|
|
|
_name = node->bindingIdentifier.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-28 17:51:32 +01:00
|
|
|
void throwRecursionDepthError() override
|
|
|
|
|
{
|
|
|
|
|
qWarning("Warning: Hit maximum recursion depth visiting AST in FindUsages");
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2011-09-13 09:57:24 +02:00
|
|
|
if (id && !id->name.isEmpty() && !id->next && containsOffset(id->identifierToken)) {
|
2011-03-17 14:43:08 +01:00
|
|
|
_scope = _doc->bind()->findQmlObject(_objectNode);
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = id->name.toString();
|
2010-09-24 14:05:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
bool checkTypeName(UiQualifiedId *id)
|
|
|
|
|
{
|
|
|
|
|
for (UiQualifiedId *att = id; att; att = att->next) {
|
2011-09-13 09:57:24 +02:00
|
|
|
if (!att->name.isEmpty() && containsOffset(att->identifierToken)) {
|
2011-07-12 14:55:27 +02:00
|
|
|
_targetValue = _scopeChain->context()->lookupType(_doc.data(), id, att->next);
|
2018-11-24 02:45:30 +01:00
|
|
|
_scope = nullptr;
|
2011-09-13 09:57:24 +02:00
|
|
|
_name = att->name.toString();
|
2011-06-06 11:17:12 +02:00
|
|
|
_typeKind = TypeKind;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 14:43:08 +01:00
|
|
|
void setScope(Node *node)
|
|
|
|
|
{
|
2011-07-12 14:55:27 +02:00
|
|
|
Evaluate evaluate(_scopeChain);
|
2011-03-17 14:43:08 +01:00
|
|
|
const Value *v = evaluate(node);
|
|
|
|
|
if (v)
|
|
|
|
|
_scope = v->asObjectValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString _name;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ObjectValue *_scope = nullptr;
|
|
|
|
|
const Value *_targetValue = nullptr;
|
|
|
|
|
Node *_objectNode = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
Document::Ptr _doc;
|
2018-11-24 02:45:30 +01:00
|
|
|
const ScopeChain *_scopeChain = nullptr;
|
|
|
|
|
quint32 _offset = 0;
|
|
|
|
|
Kind _typeKind = ExpKind;
|
2010-09-24 14:05:34 +02:00
|
|
|
};
|
|
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class ProcessFile
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context;
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
|
|
|
|
const QString name;
|
2010-09-24 14:05:34 +02:00
|
|
|
const ObjectValue *scope;
|
2023-03-09 17:43:08 +01:00
|
|
|
QPromise<Usage> &m_promise;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using argument_type = const QString &;
|
|
|
|
|
using result_type = QList<Usage>;
|
|
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
ProcessFile(const ContextPtr &context,
|
2018-11-24 02:45:30 +01:00
|
|
|
const QString &name,
|
2012-05-25 16:45:18 +02:00
|
|
|
const ObjectValue *scope,
|
2023-03-09 17:43:08 +01:00
|
|
|
QPromise<Usage> &promise)
|
|
|
|
|
: context(context), name(name), scope(scope), m_promise(promise)
|
2010-09-24 14:05:34 +02:00
|
|
|
{ }
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
QList<Usage> operator()(const Utils::FilePath &fileName)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
QList<Usage> usages;
|
2023-03-09 17:43:08 +01:00
|
|
|
m_promise.suspendIfRequested();
|
|
|
|
|
if (m_promise.isCanceled())
|
2012-05-25 16:45:18 +02:00
|
|
|
return usages;
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
2011-07-13 15:04:27 +02:00
|
|
|
Document::Ptr doc = context->snapshot().document(fileName);
|
2010-09-24 14:05:34 +02:00
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
// find all idenfifier expressions, try to resolve them and check if the result is in scope
|
2011-07-13 15:04:27 +02:00
|
|
|
FindUsages findUsages(doc, context);
|
2022-05-17 16:11:03 +02:00
|
|
|
const FindUsages::Result results = findUsages(name, scope);
|
|
|
|
|
for (const SourceLocation &loc : results)
|
2022-06-20 12:35:13 +02:00
|
|
|
usages.append(Usage(modelManager->fileToSource(fileName),
|
|
|
|
|
matchingLine(loc.offset, doc->source()),
|
|
|
|
|
loc.startLine,
|
|
|
|
|
loc.startColumn - 1,
|
|
|
|
|
loc.length));
|
2023-03-09 17:43:08 +01:00
|
|
|
m_promise.suspendIfRequested();
|
2010-09-24 14:05:34 +02:00
|
|
|
return usages;
|
|
|
|
|
}
|
2011-06-06 11:17:12 +02:00
|
|
|
};
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class SearchFileForType
|
2011-06-06 11:17:12 +02:00
|
|
|
{
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context;
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
|
|
|
|
const QString name;
|
2011-06-06 11:17:12 +02:00
|
|
|
const ObjectValue *scope;
|
2023-03-09 17:43:08 +01:00
|
|
|
QPromise<Usage> &m_promise;
|
2011-06-06 11:17:12 +02:00
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using argument_type = const QString &;
|
|
|
|
|
using result_type = QList<Usage>;
|
|
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
SearchFileForType(const ContextPtr &context,
|
2018-11-24 02:45:30 +01:00
|
|
|
const QString &name,
|
2012-05-25 16:45:18 +02:00
|
|
|
const ObjectValue *scope,
|
2023-03-09 17:43:08 +01:00
|
|
|
QPromise<Usage> &promise)
|
|
|
|
|
: context(context), name(name), scope(scope), m_promise(promise)
|
2011-06-06 11:17:12 +02:00
|
|
|
{ }
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
QList<Usage> operator()(const Utils::FilePath &fileName)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-06-06 11:17:12 +02:00
|
|
|
QList<Usage> usages;
|
2023-03-09 17:43:08 +01:00
|
|
|
m_promise.suspendIfRequested();
|
|
|
|
|
if (m_promise.isCanceled())
|
2012-05-25 16:45:18 +02:00
|
|
|
return usages;
|
2011-07-13 15:04:27 +02:00
|
|
|
Document::Ptr doc = context->snapshot().document(fileName);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
// find all idenfifier expressions, try to resolve them and check if the result is in scope
|
2011-07-13 15:04:27 +02:00
|
|
|
FindTypeUsages findUsages(doc, context);
|
2022-05-17 16:11:03 +02:00
|
|
|
const FindTypeUsages::Result results = findUsages(name, scope);
|
|
|
|
|
for (const SourceLocation &loc : results)
|
2011-06-06 11:17:12 +02:00
|
|
|
usages.append(Usage(fileName, matchingLine(loc.offset, doc->source()), loc.startLine, loc.startColumn - 1, loc.length));
|
2023-03-09 17:43:08 +01:00
|
|
|
m_promise.suspendIfRequested();
|
2011-06-06 11:17:12 +02:00
|
|
|
return usages;
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-31 11:31:59 +02:00
|
|
|
class UpdateUI
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
using Usage = FindReferences::Usage;
|
2023-03-09 17:43:08 +01:00
|
|
|
QPromise<Usage> &m_promise;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
public:
|
2018-05-31 11:31:59 +02:00
|
|
|
// needed by QtConcurrent
|
|
|
|
|
using first_argument_type = QList<Usage> &;
|
|
|
|
|
using second_argument_type = const QList<Usage> &;
|
|
|
|
|
using result_type = void;
|
|
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
UpdateUI(QPromise<Usage> &promise): m_promise(promise) {}
|
2010-09-24 14:05:34 +02:00
|
|
|
|
|
|
|
|
void operator()(QList<Usage> &, const QList<Usage> &usages)
|
|
|
|
|
{
|
2022-05-17 16:11:03 +02:00
|
|
|
for (const Usage &u : usages)
|
2023-03-09 17:43:08 +01:00
|
|
|
m_promise.addResult(u);
|
|
|
|
|
m_promise.setProgressValue(m_promise.future().progressValue() + 1);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
|
|
FindReferences::FindReferences(QObject *parent)
|
2011-09-09 16:10:57 +02:00
|
|
|
: QObject(parent)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
m_watcher.setPendingResultsLimit(1);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(&m_watcher, &QFutureWatcherBase::resultsReadyAt, this, &FindReferences::displayResults);
|
|
|
|
|
connect(&m_watcher, &QFutureWatcherBase::finished, this, &FindReferences::searchFinished);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
FindReferences::~FindReferences() = default;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
static void find_helper(QPromise<FindReferences::Usage> &promise,
|
2018-11-24 02:45:30 +01:00
|
|
|
const ModelManagerInterface::WorkingCopy &workingCopy,
|
2010-09-24 14:05:34 +02:00
|
|
|
Snapshot snapshot,
|
2022-06-20 12:35:13 +02:00
|
|
|
const Utils::FilePath &fileName,
|
2011-07-11 12:53:05 +02:00
|
|
|
quint32 offset,
|
|
|
|
|
QString replacement)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
// update snapshot from workingCopy to make sure it's up to date
|
|
|
|
|
// ### remove?
|
|
|
|
|
// ### this is a great candidate for map-reduce
|
2019-07-24 13:43:54 +02:00
|
|
|
const ModelManagerInterface::WorkingCopy::Table &all = workingCopy.all();
|
|
|
|
|
for (auto it = all.cbegin(), end = all.cend(); it != end; ++it) {
|
2022-06-20 12:35:13 +02:00
|
|
|
const Utils::FilePath fileName = it.key();
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Ptr oldDoc = snapshot.document(fileName);
|
2010-09-24 14:05:34 +02:00
|
|
|
if (oldDoc && oldDoc->editorRevision() == it.value().second)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-07-22 19:06:44 +02:00
|
|
|
Dialect language;
|
2011-09-09 10:55:11 +02:00
|
|
|
if (oldDoc)
|
|
|
|
|
language = oldDoc->language();
|
|
|
|
|
else
|
2014-01-22 18:38:45 +01:00
|
|
|
language = ModelManagerInterface::guessLanguageOfFile(fileName);
|
2014-07-22 19:06:44 +02:00
|
|
|
if (language == Dialect::NoLanguage) {
|
2014-06-02 19:36:32 +02:00
|
|
|
qCDebug(qmljsLog) << "NoLanguage in qmljsfindreferences.cpp find_helper for " << fileName;
|
2014-07-22 19:06:44 +02:00
|
|
|
language = Dialect::AnyLanguage;
|
2014-06-02 19:36:32 +02:00
|
|
|
}
|
2011-09-09 10:55:11 +02:00
|
|
|
|
2011-11-03 13:47:03 +01:00
|
|
|
Document::MutablePtr newDoc = snapshot.documentFromSource(
|
|
|
|
|
it.value().first, fileName, language);
|
2010-09-24 14:05:34 +02:00
|
|
|
newDoc->parse();
|
|
|
|
|
snapshot.insert(newDoc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the scope for the name we're searching
|
|
|
|
|
Document::Ptr doc = snapshot.document(fileName);
|
|
|
|
|
if (!doc)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
2011-06-28 12:01:56 +02:00
|
|
|
|
2014-04-11 23:07:52 +02:00
|
|
|
Link link(snapshot, modelManager->defaultVContext(doc->language(), doc), modelManager->builtins(doc));
|
2011-07-13 15:04:27 +02:00
|
|
|
ContextPtr context = link();
|
2011-05-06 13:31:30 +02:00
|
|
|
|
2011-07-13 15:04:27 +02:00
|
|
|
ScopeChain scopeChain(doc, context);
|
2011-07-12 14:55:27 +02:00
|
|
|
ScopeBuilder builder(&scopeChain);
|
2010-09-24 14:05:34 +02:00
|
|
|
ScopeAstPath astPath(doc);
|
|
|
|
|
builder.push(astPath(offset));
|
|
|
|
|
|
2011-07-12 14:55:27 +02:00
|
|
|
FindTargetExpression findTarget(doc, &scopeChain);
|
2011-03-17 14:43:08 +01:00
|
|
|
findTarget(offset);
|
|
|
|
|
const QString &name = findTarget.name();
|
2010-09-24 14:05:34 +02:00
|
|
|
if (name.isEmpty())
|
|
|
|
|
return;
|
2011-07-11 12:53:05 +02:00
|
|
|
if (!replacement.isNull() && replacement.isEmpty())
|
|
|
|
|
replacement = name;
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
Utils::FilePaths files;
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const Document::Ptr &doc : std::as_const(snapshot)) {
|
2010-09-24 14:05:34 +02:00
|
|
|
// ### skip files that don't contain the name token
|
2022-06-20 12:35:13 +02:00
|
|
|
files.append(modelManager->fileToSource(doc->fileName()));
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
files = Utils::filteredUnique(files);
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
promise.setProgressRange(0, files.size());
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2011-07-11 12:53:05 +02:00
|
|
|
// report a dummy usage to indicate the search is starting
|
2022-06-20 12:35:13 +02:00
|
|
|
FindReferences::Usage searchStarting(Utils::FilePath::fromString(replacement), name, 0, 0, 0);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
if (findTarget.typeKind() == findTarget.TypeKind){
|
2011-10-10 10:55:37 +02:00
|
|
|
const ObjectValue *typeValue = value_cast<ObjectValue>(findTarget.targetValue());
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!typeValue)
|
|
|
|
|
return;
|
2023-03-09 17:43:08 +01:00
|
|
|
promise.addResult(searchStarting);
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
SearchFileForType process(context, name, typeValue, promise);
|
|
|
|
|
UpdateUI reduce(promise);
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2011-06-06 11:17:12 +02:00
|
|
|
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
|
|
|
|
|
} else {
|
|
|
|
|
const ObjectValue *scope = findTarget.scope();
|
|
|
|
|
if (!scope)
|
|
|
|
|
return;
|
2011-07-13 15:04:27 +02:00
|
|
|
scope->lookupMember(name, context, &scope);
|
2011-06-06 11:17:12 +02:00
|
|
|
if (!scope)
|
|
|
|
|
return;
|
2011-09-07 20:28:04 +02:00
|
|
|
if (!scope->className().isEmpty())
|
|
|
|
|
searchStarting.lineText.prepend(scope->className() + QLatin1Char('.'));
|
2023-03-09 17:43:08 +01:00
|
|
|
promise.addResult(searchStarting);
|
2011-06-06 11:17:12 +02:00
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
ProcessFile process(context, name, scope, promise);
|
|
|
|
|
UpdateUI reduce(promise);
|
2011-06-06 11:17:12 +02:00
|
|
|
|
|
|
|
|
QtConcurrent::blockingMappedReduced<QList<FindReferences::Usage> > (files, process, reduce);
|
|
|
|
|
}
|
2023-03-09 17:43:08 +01:00
|
|
|
promise.setProgressValue(files.size());
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
void FindReferences::findUsages(const Utils::FilePath &fileName, quint32 offset)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2011-07-11 12:53:05 +02:00
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
|
2016-02-08 16:26:19 +01:00
|
|
|
modelManager->snapshot(), fileName, offset, QString());
|
2011-07-11 12:53:05 +02:00
|
|
|
m_watcher.setFuture(result);
|
2021-05-11 09:26:15 +02:00
|
|
|
m_synchronizer.addFuture(result);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
void FindReferences::renameUsages(const Utils::FilePath &fileName,
|
|
|
|
|
quint32 offset,
|
2011-07-11 12:53:05 +02:00
|
|
|
const QString &replacement)
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
|
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
2011-07-11 12:53:05 +02:00
|
|
|
// an empty non-null string asks the future to use the current name as base
|
|
|
|
|
QString newName = replacement;
|
|
|
|
|
if (newName.isNull())
|
|
|
|
|
newName = QLatin1String("");
|
2010-09-24 14:05:34 +02:00
|
|
|
|
2023-03-09 17:43:08 +01:00
|
|
|
QFuture<Usage> result = Utils::asyncRun(&find_helper, ModelManagerInterface::workingCopy(),
|
2016-02-08 16:26:19 +01:00
|
|
|
modelManager->snapshot(), fileName, offset, newName);
|
2010-09-24 14:05:34 +02:00
|
|
|
m_watcher.setFuture(result);
|
2021-05-11 09:26:15 +02:00
|
|
|
m_synchronizer.addFuture(result);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
QList<FindReferences::Usage> FindReferences::findUsageOfType(const Utils::FilePath &fileName,
|
|
|
|
|
const QString &typeName)
|
2015-02-17 12:10:04 +01:00
|
|
|
{
|
|
|
|
|
QList<Usage> usages;
|
|
|
|
|
ModelManagerInterface *modelManager = ModelManagerInterface::instance();
|
|
|
|
|
|
|
|
|
|
Document::Ptr doc = modelManager->snapshot().document(fileName);
|
|
|
|
|
if (!doc)
|
|
|
|
|
return usages;
|
|
|
|
|
|
|
|
|
|
Link link(modelManager->snapshot(), modelManager->defaultVContext(doc->language(), doc), modelManager->builtins(doc));
|
|
|
|
|
ContextPtr context = link();
|
|
|
|
|
ScopeChain scopeChain(doc, context);
|
|
|
|
|
|
|
|
|
|
const ObjectValue *targetValue = scopeChain.context()->lookupType(doc.data(), QStringList(typeName));
|
|
|
|
|
|
|
|
|
|
QmlJS::Snapshot snapshot = modelManager->snapshot();
|
|
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
QSet<Utils::FilePath> docDone;
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const QmlJS::Document::Ptr &doc : std::as_const(snapshot)) {
|
2022-06-20 12:35:13 +02:00
|
|
|
Utils::FilePath sourceFile = modelManager->fileToSource(doc->fileName());
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
if (docDone.contains(sourceFile))
|
|
|
|
|
continue;
|
|
|
|
|
docDone.insert(sourceFile);
|
|
|
|
|
QmlJS::Document::Ptr sourceDoc = doc;
|
|
|
|
|
if (sourceFile != doc->fileName())
|
|
|
|
|
sourceDoc = snapshot.document(sourceFile);
|
|
|
|
|
FindTypeUsages findUsages(sourceDoc, context);
|
2022-05-17 16:11:03 +02:00
|
|
|
const FindTypeUsages::Result results = findUsages(typeName, targetValue);
|
|
|
|
|
for (const SourceLocation &loc : results) {
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
usages.append(Usage(sourceFile,
|
|
|
|
|
matchingLine(loc.offset, doc->source()),
|
|
|
|
|
loc.startLine,
|
|
|
|
|
loc.startColumn - 1,
|
|
|
|
|
loc.length));
|
2015-02-17 12:10:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return usages;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 14:05:34 +02:00
|
|
|
void FindReferences::displayResults(int first, int last)
|
|
|
|
|
{
|
2011-03-17 15:08:25 +01:00
|
|
|
// the first usage is always a dummy to indicate we now start searching
|
|
|
|
|
if (first == 0) {
|
2011-07-11 12:53:05 +02:00
|
|
|
Usage dummy = m_watcher.future().resultAt(0);
|
2022-06-20 12:35:13 +02:00
|
|
|
const QString replacement = dummy.path.toString();
|
2011-09-09 16:10:57 +02:00
|
|
|
const QString symbolName = dummy.lineText;
|
2022-08-25 12:46:47 +02:00
|
|
|
const QString label = Tr::tr("QML/JS Usages:");
|
2011-07-11 12:53:05 +02:00
|
|
|
|
|
|
|
|
if (replacement.isEmpty()) {
|
2015-02-03 23:48:57 +02:00
|
|
|
m_currentSearch = SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
label, QString(), symbolName, SearchResultWindow::SearchOnly);
|
2011-07-11 12:53:05 +02:00
|
|
|
} else {
|
2015-02-03 23:48:57 +02:00
|
|
|
m_currentSearch = SearchResultWindow::instance()->startNewSearch(
|
|
|
|
|
label, QString(), symbolName, SearchResultWindow::SearchAndReplace,
|
|
|
|
|
SearchResultWindow::PreserveCaseDisabled);
|
2011-09-02 11:51:31 +02:00
|
|
|
m_currentSearch->setTextToReplace(replacement);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::replaceButtonClicked,
|
|
|
|
|
this, &FindReferences::onReplaceButtonClicked);
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::activated,
|
2023-05-05 19:12:47 +02:00
|
|
|
[](const Utils::SearchResultItem &item) {
|
2017-09-22 16:36:26 +02:00
|
|
|
Core::EditorManager::openEditorAtSearchResult(item);
|
|
|
|
|
});
|
2022-07-20 12:46:13 +02:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::canceled, this, &FindReferences::cancel);
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_currentSearch.data(), &SearchResult::paused, this, &FindReferences::setPaused);
|
2015-02-03 23:48:57 +02:00
|
|
|
SearchResultWindow::instance()->popup(IOutputPane::Flags(IOutputPane::ModeSwitch | IOutputPane::WithFocus));
|
2011-03-17 15:08:25 +01:00
|
|
|
|
2019-07-18 14:17:25 +02:00
|
|
|
FutureProgress *progress = ProgressManager::addTask(m_watcher.future(),
|
2022-08-25 12:46:47 +02:00
|
|
|
Tr::tr("Searching for Usages"),
|
2019-07-18 14:17:25 +02:00
|
|
|
"QmlJSEditor.TaskSearch");
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(progress, &FutureProgress::clicked, m_currentSearch.data(), &SearchResult::popup);
|
2011-03-17 15:08:25 +01:00
|
|
|
|
|
|
|
|
++first;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-09 16:10:57 +02:00
|
|
|
if (!m_currentSearch) {
|
|
|
|
|
m_watcher.cancel();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-24 14:05:34 +02:00
|
|
|
for (int index = first; index != last; ++index) {
|
|
|
|
|
Usage result = m_watcher.future().resultAt(index);
|
2023-05-05 15:51:11 +02:00
|
|
|
Utils::SearchResultItem item;
|
2022-06-20 12:35:13 +02:00
|
|
|
item.setFilePath(result.path);
|
2021-02-11 16:22:48 +01:00
|
|
|
item.setLineText(result.lineText);
|
|
|
|
|
item.setMainRange(result.line, result.col, result.len);
|
2021-03-02 09:10:08 +01:00
|
|
|
item.setUseTextEditorFont(true);
|
2021-02-11 16:22:48 +01:00
|
|
|
m_currentSearch->addResult(item);
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FindReferences::searchFinished()
|
|
|
|
|
{
|
2011-09-09 16:10:57 +02:00
|
|
|
if (m_currentSearch)
|
2012-05-16 15:59:16 +02:00
|
|
|
m_currentSearch->finishSearch(m_watcher.isCanceled());
|
2018-11-24 02:45:30 +01:00
|
|
|
m_currentSearch = nullptr;
|
2010-09-24 14:05:34 +02:00
|
|
|
emit changed();
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-09 08:46:51 +02:00
|
|
|
void FindReferences::cancel()
|
|
|
|
|
{
|
|
|
|
|
m_watcher.cancel();
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-25 16:45:18 +02:00
|
|
|
void FindReferences::setPaused(bool paused)
|
|
|
|
|
{
|
|
|
|
|
if (!paused || m_watcher.isRunning()) // guard against pausing when the search is finished
|
|
|
|
|
m_watcher.setPaused(paused);
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 15:51:11 +02:00
|
|
|
void FindReferences::onReplaceButtonClicked(const QString &text,
|
2023-05-05 19:12:47 +02:00
|
|
|
const Utils::SearchResultItems &items, bool preserveCase)
|
2011-07-11 12:53:05 +02:00
|
|
|
{
|
2021-06-22 08:57:36 +02:00
|
|
|
const Utils::FilePaths filePaths = TextEditor::BaseFileFind::replaceAll(text,
|
|
|
|
|
items,
|
|
|
|
|
preserveCase);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
|
|
|
|
// files that are opened in an editor are changed, but not saved
|
2022-06-20 12:35:13 +02:00
|
|
|
Utils::FilePaths changedOnDisk;
|
|
|
|
|
Utils::FilePaths changedUnsavedEditors;
|
2021-06-22 08:57:36 +02:00
|
|
|
for (const Utils::FilePath &filePath : filePaths) {
|
|
|
|
|
if (DocumentModel::documentForFilePath(filePath))
|
2022-06-20 12:35:13 +02:00
|
|
|
changedOnDisk += filePath;
|
2011-07-11 12:53:05 +02:00
|
|
|
else
|
2022-06-20 12:35:13 +02:00
|
|
|
changedUnsavedEditors += filePath;
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!changedOnDisk.isEmpty())
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface::instance()->updateSourceFiles(changedOnDisk, true);
|
2011-07-11 12:53:05 +02:00
|
|
|
if (!changedUnsavedEditors.isEmpty())
|
2015-02-03 23:48:57 +02:00
|
|
|
ModelManagerInterface::instance()->updateSourceFiles(changedUnsavedEditors, false);
|
2011-07-11 12:53:05 +02:00
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
SearchResultWindow::instance()->hide();
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|