qmljs: add infrastructure handling qml dialects better

QmlBundles enables us to treat the different qml
dialects differently.

Add imports completion.

Change-log: [Qml/JS Support] Corrected handling of QtQuick2 only features.
Change-log: [Qml/JS Support] Added import completion in editor.
Task-number: QTCREATORBUG-8750
Task-number: QTCREATORBUG-8624
Task-number: QTCREATORBUG-8584
Task-number: QTCREATORBUG-8583
Task-number: QTCREATORBUG-8429

Change-Id: I1384b1b23136a85b4d077895ea86f92960da9e71
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
This commit is contained in:
Fawzi Mohamed
2012-12-06 17:20:58 +01:00
parent 6d3c271d53
commit 942326ae9b
34 changed files with 1184 additions and 122 deletions
@@ -0,0 +1,14 @@
{
"name": "qbs",
"searchPaths": [
"$(QBS_IMPORT_PATH)"],
"installPaths": [
"$(QBS_IMPORT_PATH)"],
"implicitImports": [
"__javascriptQt5__"],
"supportedImports": [
"qbs.base 1.0",
"qbs 1.0",
"qbs.fileinfo 1.0",
"qbs.probe 1.0"]
}
@@ -0,0 +1,12 @@
{
"name": "qmlproject",
"searchPaths": [
""],
"installPaths": [
""],
"implicitImports": [
""],
"supportedImports": [
"QmlProject 1.0",
"QmlProject 1.1"]
}
@@ -0,0 +1,9 @@
{
"name":"qmltypes",
"searchPaths":[],
"installPaths":[],
"implicitImports":[],
"supportedImports":[
"QtQuick.tooling 1.1",
"QtQuick.tooling 1.0"]
}
@@ -0,0 +1,17 @@
{
"name": "QtQuick1",
"searchPaths": [
"$(QT_INSTALL_IMPORTS)"],
"installPaths": [
"$(QT_INSTALL_IMPORTS)"],
"implicitImports": [
"__javascriptQt4__"],
"supportedImports": [
"QtQuick 1.0",
"QtQuick 1.1",
"Qt.labs.gestures 1.0",
"Qt.labs.particles 1.0",
"Qt.labs.shaders 1.0",
"Qt.labs.folderlistmodel 1.0",
"QtWebKit 1.0"]
}
@@ -0,0 +1,16 @@
{
"name": "QtQuick1",
"searchPaths": [
"$(QT_INSTALL_IMPORTS)"],
"installPaths": [
"$(QT_INSTALL_IMPORTS)"],
"implicitImports": [
"__javascriptQt5__"],
"supportedImports": [
"QtQuick 1.1",
"Qt.labs.gestures 1.0",
"Qt.labs.particles 1.0",
"Qt.labs.shaders 1.0",
"Qt.labs.folderlistmodel 1.0",
"QtWebKit 1.0"]
}
@@ -0,0 +1,20 @@
{
"name": "QtQuick2",
"searchPaths": [
"$(QT_INSTALL_QML)"],
"installPaths": [
"$(QT_INSTALL_QML)"],
"implicitImports": [
"__javascriptQt5__"],
"supportedImports": [
"Qt.labs.folderlistmodel 2.0",
"QtAudioEngine 1.0",
"QtMultimedia 5.0",
"QtQuick.LocalStorage 2.0",
"QtQuick.Particles 2.0",
"QtQuick.Window 2.0",
"QtQuick.XmlListModel 2.0",
"QtQuick 2.0",
"QtTest 1.0",
"QtWebKit 3.0"]
}
+77 -1
View File
@@ -261,6 +261,54 @@ QStringList TrieNode::stringList(const TrieNode::Ptr &trie)
return a.res;
}
bool TrieNode::isSame(const TrieNode::Ptr &trie1, const TrieNode::Ptr &trie2)
{
if (trie1.data() == trie2.data())
return true;
if (trie1.isNull() || trie2.isNull())
return false; // assume never to generate non null empty tries
if (trie1->prefix != trie2->prefix)
return false; // assume no excess splitting
QList<TrieNode::Ptr> t1 = trie1->postfixes, t2 =trie2->postfixes;
int nEl = t1.size();
if (nEl != t2.size()) return false;
// different order = different trie
for (int i = 0; i < nEl; ++i) {
if (!isSame(t1.value(i), t2.value(i)))
return false;
}
return true;
}
namespace {
class ReplaceInTrie
{
public:
TrieNode::Ptr trie;
QHash<QString, QString> replacements;
ReplaceInTrie() { }
void operator()(QString s)
{
QHashIterator<QString, QString> i(replacements);
QString res = s;
while (i.hasNext()) {
i.next();
res.replace(i.key(), i.value());
}
trie = TrieNode::insertF(trie,res);
}
};
}
TrieNode::Ptr TrieNode::replaceF(const TrieNode::Ptr &trie, const QHash<QString, QString> &replacements)
{
// inefficient...
ReplaceInTrie rep;
rep.replacements = replacements;
enumerateTrieNode<ReplaceInTrie>(trie, rep, QString());
return rep.trie;
}
std::pair<TrieNode::Ptr,int> TrieNode::intersectF(
const TrieNode::Ptr &v1, const TrieNode::Ptr &v2, int index1)
{
@@ -553,6 +601,26 @@ void Trie::merge(const Trie &v)
trie = TrieNode::mergeF(trie, v.trie).first;
}
void Trie::replace(const QHash<QString, QString> &replacements)
{
trie = TrieNode::replaceF(trie, replacements);
}
bool Trie::isEmpty() const
{
return trie.isNull(); // assuming to never generate an empty non null trie
}
bool Trie::operator==(const Trie &o)
{
return TrieNode::isSame(trie,o.trie);
}
bool Trie::operator!=(const Trie &o)
{
return !TrieNode::isSame(trie,o.trie);
}
Trie Trie::insertF(const QString &value) const
{
return Trie(TrieNode::insertF(trie, value));
@@ -568,13 +636,19 @@ Trie Trie::mergeF(const Trie &v) const
return Trie(TrieNode::mergeF(trie, v.trie).first);
}
Trie Trie::replaceF(const QHash<QString, QString> &replacements) const
{
return Trie(TrieNode::replaceF(trie, replacements));
}
/*!
\fn int matchStrength(const QString &searchStr, const QString &str)
Returns a number defining how well the serachStr matches str.
Quite simplistic, looks only at the first match, and prefers contiguos
matches, or matches to ca capitalized or separated word.
matches, or matches to capitalized or separated words.
Match to the last char is also preferred.
*/
int matchStrength(const QString &searchStr, const QString &str)
{
@@ -601,6 +675,8 @@ int matchStrength(const QString &searchStr, const QString &str)
}
if (i != iEnd)
return iEnd - i;
if (j == jEnd)
++res;
return res;
}
+9
View File
@@ -32,6 +32,7 @@
#include <qmljs/qmljs_global.h>
#include <QHash>
#include <QList>
#include <QSharedPointer>
@@ -67,7 +68,9 @@ public:
const QString &base = QString(), LookupFlags flags = LookupFlags(CaseInsensitive|Partial));
static bool contains(const Ptr &trie, const QString &value, LookupFlags flags = LookupFlags(0));
static QStringList stringList(const Ptr &trie);
static bool isSame(const Ptr &trie1, const Ptr &trie2);
static Ptr replaceF(const Ptr &trie, const QHash<QString, QString> &replacements);
static Ptr insertF(const Ptr &trie, const QString &value);
static std::pair<Ptr,int> intersectF(const Ptr &v1, const Ptr &v2, int index1=0);
static std::pair<Ptr,int> mergeF(const Ptr &v1, const Ptr &v2);
@@ -91,10 +94,16 @@ public:
Trie insertF(const QString &value) const;
Trie intersectF(const Trie &v) const;
Trie mergeF(const Trie &v) const;
Trie replaceF(const QHash<QString, QString> &replacements) const;
void insert(const QString &value);
void intersect(const Trie &v);
void merge(const Trie &v);
void replace(const QHash<QString, QString> &replacements);
bool isEmpty() const;
bool operator==(const Trie &o);
bool operator!=(const Trie &o);
friend QMLJS_EXPORT QDebug &operator<<(QDebug &dbg, const TrieNode::Ptr &trie);
friend QMLJS_EXPORT QDebug &operator<<(QDebug &dbg, const Trie &trie);
+2
View File
@@ -11,6 +11,7 @@ INCLUDEPATH += $$PWD/..
HEADERS += \
$$PWD/qmljs_global.h \
$$PWD/qmljsbind.h \
$$PWD/qmljsbundle.h \
$$PWD/qmljsevaluate.h \
$$PWD/qmljsdocument.h \
$$PWD/qmljsscanner.h \
@@ -42,6 +43,7 @@ HEADERS += \
SOURCES += \
$$PWD/qmljsbind.cpp \
$$PWD/qmljsbundle.cpp \
$$PWD/qmljsevaluate.cpp \
$$PWD/qmljsdocument.cpp \
$$PWD/qmljsscanner.cpp \
+2
View File
@@ -23,6 +23,8 @@ QtcLibrary {
"qmljs_global.h",
"qmljsbind.cpp",
"qmljsbind.h",
"qmljsbundle.cpp",
"qmljsbundle.h",
"qmljscheck.cpp",
"qmljscheck.h",
"qmljscodeformatter.cpp",
+15 -2
View File
@@ -31,6 +31,7 @@
#include "qmljsbind.h"
#include "qmljsutils.h"
#include "qmljsdocument.h"
#include "qmljsmodelmanagerinterface.h"
#include <languageutils/componentversion.h>
@@ -204,15 +205,27 @@ bool Bind::visit(UiImport *ast)
_diagnosticMessages->append(
errorMessage(ast, tr("package import requires a version number")));
}
_imports += ImportInfo::moduleImport(toString(ast->importUri), version,
ImportInfo import = ImportInfo::moduleImport(toString(ast->importUri), version,
ast->importId.toString(), ast);
if (_doc->language() == Document::QmlLanguage) {
QString importStr = import.name() + ast->importId.toString();
QmlLanguageBundles langBundles = ModelManagerInterface::instance()->extendedBundles();
QmlBundle qq1 = langBundles.bundleForLanguage(Document::QmlQtQuick1Language);
QmlBundle qq2 = langBundles.bundleForLanguage(Document::QmlQtQuick2Language);
bool isQQ1 = qq1.supportedImports().contains(importStr);
bool isQQ2 = qq2.supportedImports().contains(importStr);
if (isQQ1 && ! isQQ2)
_doc->setLanguage(Document::QmlQtQuick1Language);
if (isQQ2 && ! isQQ1)
_doc->setLanguage(Document::QmlQtQuick2Language);
}
_imports += import;
} else if (!ast->fileName.isEmpty()) {
_imports += ImportInfo::pathImport(_doc->path(), ast->fileName.toString(),
version, ast->importId.toString(), ast);
} else {
_imports += ImportInfo::invalidImport(ast);
}
return false;
}
+314
View File
@@ -0,0 +1,314 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmljsbundle.h"
#include <utils/json.h>
#include <QString>
#include <QFile>
#include <QTextStream>
#include <QHash>
namespace QmlJS {
typedef PersistentTrie::Trie Trie;
QmlBundle::QmlBundle(const QmlBundle &o)
: m_name(o.m_name), m_searchPaths(o.searchPaths()), m_installPaths(o.installPaths()),
m_supportedImports(o.m_supportedImports), m_implicitImports(o.m_implicitImports)
{ }
QmlBundle::QmlBundle()
{ }
QmlBundle::QmlBundle(const QString &bundleName, const Trie &searchPaths,
const Trie &installPaths, const Trie &supportedImports,
const Trie &implicitImports)
: m_name(bundleName), m_searchPaths(searchPaths), m_installPaths(installPaths),
m_supportedImports(supportedImports), m_implicitImports(implicitImports)
{ }
QString QmlBundle::name() const
{
return m_name;
}
Trie QmlBundle::installPaths() const
{
return m_installPaths;
}
Trie QmlBundle::searchPaths() const
{
return m_searchPaths;
}
Trie QmlBundle::implicitImports() const
{
return m_implicitImports;
}
Trie QmlBundle::supportedImports() const
{
return m_supportedImports;
}
void QmlBundle::merge(const QmlBundle &o)
{
*this = mergeF(o);
}
void QmlBundle::intersect(const QmlBundle &o)
{
*this = intersectF(o);
}
QmlBundle QmlBundle::mergeF(const QmlBundle &o) const
{
return QmlBundle(QString::fromLatin1("(%1)||(%2)").arg(name()).arg(o.name()),
searchPaths().mergeF(o.searchPaths()),
installPaths().mergeF(o.installPaths()),
supportedImports().mergeF(o.supportedImports()),
implicitImports().mergeF(o.implicitImports()));
}
QmlBundle QmlBundle::intersectF(const QmlBundle &o) const
{
return QmlBundle(QString::fromLatin1("(%1)&&(%2)").arg(name()).arg(o.name()),
searchPaths().mergeF(o.searchPaths()),
installPaths().mergeF(o.installPaths()),
supportedImports().intersectF(o.supportedImports()),
implicitImports().mergeF(o.implicitImports()));
}
bool QmlBundle::isEmpty() const
{
return m_implicitImports.isEmpty() && m_installPaths.isEmpty()
&& m_searchPaths.isEmpty() && m_supportedImports.isEmpty();
}
void QmlBundle::replaceVars(const QHash<QString, QString> &replacements)
{
m_implicitImports.replace(replacements);
m_installPaths.replace(replacements);
m_searchPaths.replace(replacements);
m_supportedImports.replace(replacements);
}
QmlBundle QmlBundle::replaceVarsF(const QHash<QString, QString> &replacements) const
{
QmlBundle res(*this);
res.replaceVars(replacements);
return res;
}
bool QmlBundle::writeTo(const QString &path) const
{
QFile f(path);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream stream(&f);
return writeTo(stream);
}
bool QmlBundle::operator==(const QmlBundle &o) const
{
return o.implicitImports() == implicitImports()
&& o.installPaths() == installPaths()
&& o.supportedImports() == supportedImports(); // name is not considered
}
bool QmlBundle::operator!=(const QmlBundle &o) const
{
return !((*this) == o);
}
void QmlBundle::printEscaped(QTextStream &s, const QString &str)
{
s << QLatin1Char('"');
QString::const_iterator i = str.constBegin(), iLast = str.constBegin(),
iEnd = str.constEnd();
while (i != iEnd) {
if ((*i) != QLatin1Char('"')) {
s << QStringRef(&str, static_cast<int>(iLast - str.constBegin())
, static_cast<int>(i - iLast) ).toString()
<< QLatin1Char('\\');
iLast = i;
}
++i;
}
s << QStringRef(&str, static_cast<int>(iLast - str.constBegin())
, static_cast<int>(i - iLast) ).toString();
}
void QmlBundle::writeTrie(QTextStream &stream, const Trie &t, const QString &indent) {
stream << QLatin1Char('[');
bool firstLine = true;
foreach (const QString &i, t.stringList()) {
if (firstLine)
firstLine = false;
else
stream << QLatin1Char(',');
stream << QLatin1String("\n") << indent << QLatin1String(" ");
printEscaped(stream, i);
}
stream << QLatin1Char(']');
}
bool QmlBundle::writeTo(QTextStream &stream, const QString &indent) const
{
QString innerIndent = QString::fromLatin1(" ").append(indent);
stream << indent << QLatin1String("{\n")
<< indent << QLatin1String(" \"name\": ");
printEscaped(stream, name());
stream << QLatin1String(",\n")
<< indent << QLatin1String(" \"searchPaths\": ");
writeTrie(stream, searchPaths(), innerIndent);
stream << QLatin1String(",\n")
<< indent << QLatin1String(" \"installPaths\": ");
writeTrie(stream, installPaths(), innerIndent);
stream << QLatin1String(",\n")
<< indent << QLatin1String(" \"supportedImports\": ");
writeTrie(stream, supportedImports(), innerIndent);
stream << QLatin1String(",\n")
<< QLatin1String(" \"implicitImports\": ");
writeTrie(stream, implicitImports(), innerIndent);
stream << QLatin1String("\n")
<< indent << QLatin1Char('}');
return true;
}
QString QmlBundle::toString(const QString &indent)
{
QString res;
QTextStream s(&res);
writeTo(s, indent);
return res;
}
QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config,
const QString &path, const QString &propertyName, bool required)
{
QStringList res;
if (!config->hasMember(propertyName)) {
if (required)
res << tr("Missing required property \"%1\" from %2").arg(propertyName, path);
return res;
}
Utils::JsonValue *imp0 = config->member(propertyName);
Utils::JsonArrayValue *imp = ((imp0 != 0) ? imp0->toArray() : 0);
if (imp != 0) {
foreach (Utils::JsonValue *v, imp->elements()) {
Utils::JsonStringValue *impStr = ((v != 0) ? v->toString() : 0);
if (impStr != 0) {
trie.insert(impStr->value());
} else {
res.append(tr("Expected all elements of array in property \"%1\" to be strings in QmlBundle at %2.")
.arg(propertyName).arg(path));
break;
}
}
} else {
res.append(tr("Expected string array in property \"%1\" in QmlBundle at %2.")
.arg(propertyName).arg(path));
}
return res;
}
bool QmlBundle::readFrom(QString path, QStringList *errors)
{
using namespace Utils;
QFile f(path);
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (errors)
(*errors) << tr("Could not open QmlBundle at %1 .").arg(path);
return false;
}
JsonObjectValue *config = JsonValue::create(QString::fromUtf8(f.readAll()))->toObject();
if (config == 0) {
if (errors)
(*errors) << tr("Could not parse json object in QmlBundle at %1 .").arg(path);
return false;
}
QStringList errs;
if (config->hasMember(QLatin1String("name"))) {
JsonValue *n0 = config->member(QLatin1String("name"));
JsonStringValue *n = ((n0 != 0) ? n0->toString() : 0);
if (n != 0)
m_name = n->value();
else
errs.append(tr("Property \"name\" in QmlBundle at %1 is expected to be a string.")
.arg(path));
} else {
errs.append(tr("Missing required property \"name\" in QmlBundle at %1 .").arg(path));
}
errs << maybeReadTrie(m_searchPaths, config, path, QLatin1String("searchPaths"));
errs << maybeReadTrie(m_installPaths, config, path, QLatin1String("installPaths"));
errs << maybeReadTrie(m_supportedImports, config, path, QLatin1String("supportedImports")
, true);
errs << maybeReadTrie(m_implicitImports, config, path, QLatin1String("implicitImports"));
if (errors)
(*errors) << errs;
return errs.isEmpty();
}
QmlBundle QmlLanguageBundles::bundleForLanguage(Document::Language l) const
{
if (m_bundles.contains(l))
return m_bundles.value(l);
return QmlBundle();
}
void QmlLanguageBundles::mergeBundleForLanguage(Document::Language l, const QmlBundle &bundle)
{
if (bundle.isEmpty())
return;
if (m_bundles.contains(l))
m_bundles[l].merge(bundle);
else
m_bundles.insert(l,bundle);
}
QList<Document::Language> QmlLanguageBundles::languages() const
{
return m_bundles.keys();
}
void QmlLanguageBundles::mergeLanguageBundles(const QmlLanguageBundles &o)
{
foreach (Document::Language l, o.languages())
mergeBundleForLanguage(l, o.bundleForLanguage(l));
}
} // end namespace QmlJS
+116
View File
@@ -0,0 +1,116 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLJSBUNDLE_H
#define QMLJSBUNDLE_H
#include <qmljs/qmljs_global.h>
#include <qmljs/persistenttrie.h>
#include <qmljs/qmljsdocument.h>
#include <QString>
#include <QCoreApplication>
#include <QHash>
QT_FORWARD_DECLARE_CLASS(QTextStream)
namespace Utils {
class JsonObjectValue;
}
namespace QmlJS {
/* !
\class QmlJS::QmlBundle
A Qmlbundle represents a set of qml libraries, with a list of their exports
Note that searchPaths, installPaths and implicitImports are PersistentTries
and not QStringLists.
This makes merging easier, and the order is not important for our use case.
*/
class QMLJS_EXPORT QmlBundle
{
Q_DECLARE_TR_FUNCTIONS(QmlJS::QmlBundle)
typedef PersistentTrie::Trie Trie;
public:
QmlBundle(const QmlBundle &o);
QmlBundle();
QmlBundle(const QString &name,
const Trie &searchPaths,
const Trie &installPaths,
const Trie &supportedImports,
const Trie &implicitImports);
QString name() const;
Trie installPaths() const;
Trie searchPaths() const;
Trie implicitImports() const;
Trie supportedImports() const;
void merge(const QmlBundle &o);
void intersect(const QmlBundle &o);
QmlBundle mergeF(const QmlBundle &o) const;
QmlBundle intersectF(const QmlBundle &o) const;
bool isEmpty() const;
void replaceVars(const QHash<QString, QString> &replacements);
QmlBundle replaceVarsF(const QHash<QString, QString> &replacements) const;
bool writeTo(const QString &path) const;
bool writeTo(QTextStream &stream, const QString &indent = QString()) const;
QString toString(const QString &indent = QString());
bool readFrom(QString path, QStringList *errors);
bool operator==(const QmlBundle &o) const;
bool operator!=(const QmlBundle &o) const;
private:
static void printEscaped(QTextStream &s, const QString &str);
static void writeTrie(QTextStream &stream, const Trie &t, const QString &indent);
QStringList maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, const QString &path,
const QString &propertyName, bool required = false);
QString m_name;
Trie m_searchPaths;
Trie m_installPaths;
Trie m_supportedImports;
Trie m_implicitImports;
};
class QMLJS_EXPORT QmlLanguageBundles
{
public:
QmlBundle bundleForLanguage(Document::Language l) const;
void mergeBundleForLanguage(Document::Language l, const QmlBundle &bundle);
QList<Document::Language> languages() const;
void mergeLanguageBundles(const QmlLanguageBundles &);
private:
QHash<Document::Language,QmlBundle> m_bundles;
};
} // namespace QmlJS
#endif // QMLJSBUNDLE_H
@@ -31,6 +31,7 @@
#include <QDebug>
#include <QTextDocument>
#include <QStringList>
using namespace QmlJS;
@@ -204,6 +205,8 @@ void CompletionContextFinder::checkImport()
//qDebug() << "Start line:" << *yyLine << m_startTokenIndex;
QStringList libVersionImport;
int isInLibVersionImport;
int i = m_startTokenIndex;
bool stop = false;
enum State {
@@ -216,6 +219,7 @@ void CompletionContextFinder::checkImport()
ExpectAs = 1 << 5
};
State state = Unknown;
isInLibVersionImport = -1;
while (!stop) {
if (i < 0) {
@@ -233,17 +237,27 @@ void CompletionContextFinder::checkImport()
case Token::Identifier: {
const QStringRef tokenString = yyLine->midRef(token.begin(), token.length);
if (tokenString == QLatin1String("as")) {
isInLibVersionImport = 0;
if (state == Unknown) {
state = State(ExpectAnyTarget | ExpectVersion);
break;
}
} else if (tokenString == QLatin1String("import")) {
if (state == Unknown || (state & ExpectImport))
if (state == Unknown || (state & ExpectImport)) {
if (isInLibVersionImport == -1 && token.end() < m_cursor.position())
isInLibVersionImport = 1;
m_inImport = true;
}
} else {
if (state == Unknown || (state & ExpectAnyTarget)
|| (state & ExpectTargetIdentifier)) {
state = State(ExpectImport | ExpectTargetDot);
libVersionImport.prepend(tokenString.toString());
if (isInLibVersionImport == -1) {
if (token.end() < m_cursor.position())
libVersionImport.append(QLatin1String(" "));
isInLibVersionImport = 1;
}
break;
}
}
@@ -260,6 +274,10 @@ void CompletionContextFinder::checkImport()
case Token::Number:
if (state == Unknown || (state & ExpectVersion)) {
state = ExpectAnyTarget;
libVersionImport.prepend(yyLine->midRef(token.begin(), token.length).toString());
libVersionImport.prepend(QLatin1String(" "));
if (isInLibVersionImport == -1)
isInLibVersionImport = 1;
break;
}
stop = true;
@@ -267,6 +285,9 @@ void CompletionContextFinder::checkImport()
case Token::Dot:
if (state == Unknown || (state & ExpectTargetDot)) {
state = ExpectTargetIdentifier;
libVersionImport.prepend(QLatin1String("."));
if (isInLibVersionImport == -1)
isInLibVersionImport = 1;
break;
}
stop = true;
@@ -276,11 +297,19 @@ void CompletionContextFinder::checkImport()
stop = true;
break;
}
if (isInLibVersionImport == -1)
isInLibVersionImport = 0;
--i;
}
YY_RESTORE();
if (m_inImport && isInLibVersionImport == 1) {
m_libVersion = libVersionImport.join(QLatin1String(""));
if (m_libVersion.isNull())
m_libVersion = QLatin1String("");
} else {
m_libVersion = QString();
}
}
QStringList CompletionContextFinder::qmlObjectTypeName() const
@@ -327,6 +356,11 @@ bool QmlJS::CompletionContextFinder::isInImport() const
return m_inImport;
}
QString CompletionContextFinder::libVersionImport() const
{
return m_libVersion;
}
int CompletionContextFinder::findOpeningBrace(int startTokenIndex)
{
YY_SAVE();
@@ -54,6 +54,7 @@ public:
bool isInStringLiteral() const;
bool isInImport() const;
QString libVersionImport() const;
private:
int findOpeningBrace(int startTokenIndex);
@@ -69,6 +70,7 @@ private:
bool m_behaviorBinding;
bool m_inStringLiteral;
bool m_inImport;
QString m_libVersion;
};
} // namespace QmlJS
+5
View File
@@ -166,6 +166,11 @@ Document::Language Document::language() const
return _language;
}
void Document::setLanguage(Document::Language l)
{
_language = l;
}
AST::UiProgram *Document::qmlProgram() const
{
return cast<UiProgram *>(_ast);
+1
View File
@@ -78,6 +78,7 @@ public:
bool isQmlDocument() const;
Language language() const;
void setLanguage(Language l);
AST::UiProgram *qmlProgram() const;
AST::Program *jsProgram() const;
@@ -68,3 +68,4 @@ ModelManagerInterface *ModelManagerInterface::instance()
{
return g_instance;
}
@@ -32,6 +32,7 @@
#include "qmljs_global.h"
#include "qmljsdocument.h"
#include "qmljsbundle.h"
#include <utils/environment.h>
@@ -73,6 +74,8 @@ public:
bool isNull() const
{ return project.isNull(); }
QStringList completeImportPaths();
public: // attributes
QPointer<ProjectExplorer::Project> project;
QStringList sourceFiles;
@@ -86,6 +89,8 @@ public:
QString qtImportsPath;
QString qtQmlPath;
QString qtVersionString;
QmlJS::QmlLanguageBundles activeBundle;
QmlJS::QmlLanguageBundles extendedBundle;
};
class WorkingCopy
@@ -141,8 +146,11 @@ public:
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const = 0;
virtual void updateProjectInfo(const ProjectInfo &pinfo) = 0;
Q_SLOT virtual void removeProjectInfo(ProjectExplorer::Project *project) = 0;
virtual ProjectInfo projectInfoForPath(QString path) = 0;
virtual QStringList importPaths() const = 0;
virtual QmlJS::QmlLanguageBundles activeBundles() const = 0;
virtual QmlJS::QmlLanguageBundles extendedBundles() const = 0;
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
const QString &importUri, const QString &importVersion) = 0;
+3 -53
View File
@@ -54,6 +54,7 @@
#include <projectexplorer/target.h>
#include <projectexplorer/taskhub.h>
#include <qtsupport/qtkitinformation.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -603,61 +604,10 @@ void QbsProject::updateCppCodeModel(const qbs::ProjectData *prj)
void QbsProject::updateQmlJsCodeModel(const qbs::ProjectData *prj)
{
// FIXME: No information about import directories, so ignore this for now.
#if 1
Q_UNUSED(prj);
#else
QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
if (!modelManager)
return;
QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(this);
projectInfo.sourceFiles = m_projectFiles->files[QMLType];
FindQt4ProFiles findQt4ProFiles;
QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
projectInfo.importPaths.clear();
foreach (Qt4ProFileNode *node, proFiles) {
projectInfo.importPaths.append(node->variableValue(QmlImportPathVar));
}
bool preferDebugDump = false;
projectInfo.tryQmlDump = false;
ProjectExplorer::Target *t = activeTarget();
ProjectExplorer::Kit *k = t ? t->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
if (t) {
if (Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(t->activeBuildConfiguration()))
preferDebugDump = bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild;
} else {
if (qtVersion)
preferDebugDump = qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::DebugBuild;
}
if (qtVersion && qtVersion->isValid()) {
projectInfo.tryQmlDump = qtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
|| qtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT);
projectInfo.qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
if (!projectInfo.qtImportsPath.isEmpty())
projectInfo.importPaths += projectInfo.qtImportsPath;
projectInfo.qtVersionString = qtVersion->qtVersionString();
}
projectInfo.importPaths.removeDuplicates();
if (projectInfo.tryQmlDump) {
QtSupport::QmlDumpTool::pathAndEnvironment(this, qtVersion,
ToolChainKitInformation::toolChain(k),
preferDebugDump, &projectInfo.qmlDumpPath,
&projectInfo.qmlDumpEnvironment);
} else {
projectInfo.qmlDumpPath.clear();
projectInfo.qmlDumpEnvironment.clear();
}
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
QmlJSTools::defaultProjectInfoForProject(this);
modelManager->updateProjectInfo(projectInfo);
#endif
}
} // namespace Internal
@@ -17,6 +17,7 @@ QtcPlugin {
Depends { name: "TextEditor" }
Depends { name: "QtSupport" }
Depends { name: "QmlJS" }
Depends { name: "QmlJSTools" }
Depends { name: "cpp" }
@@ -2,3 +2,4 @@ include(../../plugins/projectexplorer/projectexplorer.pri)
include(../../plugins/cpptools/cpptools.pri)
include(../../plugins/texteditor/texteditor.pri)
include(../../plugins/qtsupport/qtsupport.pri)
include(../../plugins/qmljstools/qmljstools.pri)
@@ -51,7 +51,9 @@
#include <qmljs/qmljsscanner.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljscompletioncontextfinder.h>
#include <qmljs/qmljsbundle.h>
#include <qmljs/qmljsscopebuilder.h>
#include <projectexplorer/projectexplorer.h>
#include <QFile>
#include <QFileInfo>
@@ -61,6 +63,7 @@
#include <QDirIterator>
#include <QStringList>
#include <QIcon>
#include <QTextDocumentFragment>
using namespace QmlJS;
using namespace QmlJSEditor;
@@ -645,8 +648,37 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface
}
// currently path-in-stringliteral is the only completion available in imports
if (contextFinder.isInImport())
if (contextFinder.isInImport()) {
QmlJS::ModelManagerInterface::ProjectInfo pInfo = QmlJS::ModelManagerInterface::instance()
->projectInfo(ProjectExplorer::ProjectExplorerPlugin::currentProject());
QmlBundle platform = pInfo.extendedBundle.bundleForLanguage(document->language());
if (!platform.supportedImports().isEmpty()) {
QTextCursor tc(qmlInterface->textDocument());
tc.setPosition(qmlInterface->position());
QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(tc);
QString libVersion = contextFinder.libVersionImport();
if (!libVersion.isNull()) {
QStringList completions=platform.supportedImports().complete(libVersion, QString(), QmlJS::PersistentTrie::LookupFlags(QmlJS::PersistentTrie::CaseInsensitive|QmlJS::PersistentTrie::SkipChars|QmlJS::PersistentTrie::SkipSpaces));
completions = QmlJS::PersistentTrie::matchStrengthSort(libVersion, completions);
int toSkip = qMax(libVersion.lastIndexOf(QLatin1Char(' '))
, libVersion.lastIndexOf(QLatin1Char('.')));
if (++toSkip > 0) {
QStringList nCompletions;
QString prefix(libVersion.left(toSkip));
nCompletions.reserve(completions.size());
foreach (QString completion, completions)
if (completion.startsWith(prefix))
nCompletions.append(completion.right(completion.size()-toSkip));
completions = nCompletions;
}
addCompletions(&m_completions, completions, m_interface->fileNameIcon(), KeywordOrder);
return createContentProposal();
}
}
return 0;
}
// member "a.bc<complete>" or function "foo(<complete>" completion
if (completionOperator == QLatin1Char('.')
@@ -0,0 +1,201 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmljsbundleprovider.h"
#include <coreplugin/icore.h>
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/target.h>
#include <qmljs/qmljsbundle.h>
#include <qmljs/qmljsdocument.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
#include <QDir>
#include <QFile>
#include <QHash>
#include <QHashIterator>
#include <QList>
#include <QMutex>
#include <QMutexLocker>
#include <QtAlgorithms>
namespace QmlJSTools {
namespace {
typedef QmlJS::Document::Language Language;
typedef QmlJS::QmlBundle QmlBundle;
typedef QmlJS::QmlLanguageBundles QmlLanguageBundles;
}
/*!
\class QmlJSEditor::BasicBundleProvider
\brief a class that sets up the default bundles for qt and various qml states.
*/
BasicBundleProvider::BasicBundleProvider(QObject *parent) :
IBundleProvider(parent)
{ }
QmlBundle BasicBundleProvider::defaultBundle(const QString &bundleInfoName)
{
static bool wroteErrors = false;
QmlBundle res;
QString defaultBundlePath = Core::ICore::resourcePath()
+ QLatin1String("/qml-type-descriptions/")
+ bundleInfoName;
if (!QFileInfo(defaultBundlePath).exists()) {
qWarning() << "BasicBundleProvider: ERROR " << defaultBundlePath
<< " not found";
return res;
}
QStringList errors;
if (!res.readFrom(defaultBundlePath, &errors) && ! wroteErrors) {
qWarning() << "BasicBundleProvider: ERROR reading " << defaultBundlePath
<< " : " << errors;
wroteErrors = true;
}
return res;
}
QmlBundle BasicBundleProvider::defaultQt4QtQuick1Bundle()
{
return defaultBundle(QLatin1String("qt4QtQuick1-bundle.json"));
}
QmlBundle BasicBundleProvider::defaultQt5QtQuick1Bundle()
{
return defaultBundle(QLatin1String("qt5QtQuick1-bundle.json"));
}
QmlBundle BasicBundleProvider::defaultQt5QtQuick2Bundle()
{
return defaultBundle(QLatin1String("qt5QtQuick2-bundle.json"));
}
QmlBundle BasicBundleProvider::defaultQbsBundle()
{
return defaultBundle(QLatin1String("qbs-bundle.json"));
}
QmlBundle BasicBundleProvider::defaultQmltypesBundle()
{
return defaultBundle(QLatin1String("qmltypes-bundle.json"));
}
QmlBundle BasicBundleProvider::defaultQmlprojectBundle()
{
return defaultBundle(QLatin1String("qmlproject-bundle.json"));
}
void BasicBundleProvider::mergeBundlesForKit(ProjectExplorer::Kit *kit
, QmlJS::QmlLanguageBundles &bundles
, const QHash<QString,QString> &replacements)
{
typedef QmlJS::Document Doc;
QHash<QString,QString> myReplacements = replacements;
bundles.mergeBundleForLanguage(Doc::QmlQbsLanguage, defaultQbsBundle());
bundles.mergeBundleForLanguage(Doc::QmlTypeInfoLanguage, defaultQmltypesBundle());
bundles.mergeBundleForLanguage(Doc::QmlProjectLanguage, defaultQmlprojectBundle());
QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(kit);
if (!qtVersion) {
QmlBundle b1(defaultQt4QtQuick1Bundle());
bundles.mergeBundleForLanguage(Doc::QmlLanguage, b1);
bundles.mergeBundleForLanguage(Doc::QmlQtQuick1Language, b1);
QmlBundle b11(defaultQt5QtQuick1Bundle());
bundles.mergeBundleForLanguage(Doc::QmlLanguage, b11);
bundles.mergeBundleForLanguage(Doc::QmlQtQuick1Language, b11);
QmlBundle b2(defaultQt5QtQuick2Bundle());
bundles.mergeBundleForLanguage(Doc::QmlLanguage, b2);
bundles.mergeBundleForLanguage(Doc::QmlQtQuick2Language, b2);
return;
}
QString qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
QString qtQmlPath = qtVersion->qmakeProperty("QT_INSTALL_QML");
Core::FeatureSet features = qtVersion->availableFeatures();
if (features.contains(Core::Feature(QtSupport::Constants::FEATURE_QT_QUICK))
|| features.contains(Core::Feature(QtSupport::Constants::FEATURE_QT_QUICK_1))
|| features.contains(Core::Feature(QtSupport::Constants::FEATURE_QT_QUICK_1_1))) {
myReplacements.insert(QLatin1String("$(CURRENT_DIRECTORY)"), qtImportsPath);
QDir qtQuick1Bundles(qtImportsPath);
qtQuick1Bundles.setNameFilters(QStringList(QLatin1String("*-bundle.json")));
QmlBundle qtQuick1Bundle;
QFileInfoList list = qtQuick1Bundles.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QmlBundle bAtt;
QStringList errors;
if (!bAtt.readFrom(list.value(i).filePath(), &errors))
qWarning() << "BasicBundleProvider: ERROR reading " << list[i].filePath() << " : "
<< errors;
qtQuick1Bundle.merge(bAtt);
}
if (!qtQuick1Bundle.supportedImports().contains(QLatin1String("QtQuick 1."),
QmlJS::PersistentTrie::Partial)) {
if (qtVersion->qtVersion().majorVersion == 4)
qtQuick1Bundle.merge(defaultQt4QtQuick1Bundle());
else if (qtVersion->qtVersion().majorVersion > 4)
qtQuick1Bundle.merge(defaultQt5QtQuick1Bundle());
}
qtQuick1Bundle.replaceVars(myReplacements);
bundles.mergeBundleForLanguage(Doc::QmlLanguage, qtQuick1Bundle);
bundles.mergeBundleForLanguage(Doc::QmlQtQuick1Language, qtQuick1Bundle);
}
if (features.contains(Core::Feature(QtSupport::Constants::FEATURE_QT_QUICK_2))) {
myReplacements.insert(QLatin1String("$(CURRENT_DIRECTORY)"), qtQmlPath);
QDir qtQuick2Bundles(qtQmlPath);
qtQuick2Bundles.setNameFilters(QStringList(QLatin1String("*-bundle.json")));
QmlBundle qtQuick2Bundle;
QFileInfoList list = qtQuick2Bundles.entryInfoList();
for (int i = 0; i < list.size(); ++i) {
QmlBundle bAtt;
QStringList errors;
if (!bAtt.readFrom(list.value(i).filePath(), &errors))
qWarning() << "BasicBundleProvider: ERROR reading " << list[i].filePath() << " : "
<< errors;
qtQuick2Bundle.merge(bAtt);
}
if (!qtQuick2Bundle.supportedImports().contains(QLatin1String("QtQuick 2."),
QmlJS::PersistentTrie::Partial)) {
qtQuick2Bundle.merge(defaultQt5QtQuick2Bundle());
}
qtQuick2Bundle.replaceVars(myReplacements);
bundles.mergeBundleForLanguage(Doc::QmlLanguage, qtQuick2Bundle);
bundles.mergeBundleForLanguage(Doc::QmlQtQuick2Language, qtQuick2Bundle);
}
}
} // end namespace QmlJSTools
@@ -0,0 +1,80 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** 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.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLJSBUNDLEPROVIDER_H
#define QMLJSBUNDLEPROVIDER_H
#include <QObject>
#include <qmljs/qmljsbundle.h>
#include <qmljs/qmljsdocument.h>
#include "qmljstools_global.h"
namespace ProjectExplorer {
class Kit;
class Project;
class Target;
}
namespace QmlJSTools {
class QMLJSTOOLS_EXPORT IBundleProvider : public QObject
{
Q_OBJECT
public:
explicit IBundleProvider(QObject *parent = 0)
: QObject(parent)
{ }
virtual void mergeBundlesForKit(ProjectExplorer::Kit *kit, QmlJS::QmlLanguageBundles &bundles
, const QHash<QString,QString> &replacements) = 0;
};
class QMLJSTOOLS_EXPORT BasicBundleProvider : public IBundleProvider
{
Q_OBJECT
public:
explicit BasicBundleProvider(QObject *parent = 0);
virtual void mergeBundlesForKit(ProjectExplorer::Kit *kit, QmlJS::QmlLanguageBundles &bundles
, const QHash<QString,QString> &replacements);
static QmlJS::QmlBundle defaultBundle(const QString &bundleInfoName);
static QmlJS::QmlBundle defaultQt4QtQuick1Bundle();
static QmlJS::QmlBundle defaultQt5QtQuick1Bundle();
static QmlJS::QmlBundle defaultQt5QtQuick2Bundle();
static QmlJS::QmlBundle defaultQbsBundle();
static QmlJS::QmlBundle defaultQmltypesBundle();
static QmlJS::QmlBundle defaultQmlprojectBundle();
};
} // end QmlJSTools namespace
#endif // QMLJSBUNDLEPROVIDER_H
@@ -32,6 +32,7 @@
#include "qmljsplugindumper.h"
#include "qmljsfindexportedcpptypes.h"
#include "qmljssemanticinfo.h"
#include "qmljsbundleprovider.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
@@ -42,15 +43,25 @@
#include <cplusplus/CppDocument.h>
#include <qmljs/qmljscontext.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljsbundle.h>
#include <qmljs/parser/qmldirparser_p.h>
#include <texteditor/itexteditor.h>
#include <texteditor/basetexteditor.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <projectexplorer/toolchain.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qmldumptool.h>
#include <qtsupport/qtsupportconstants.h>
#include <utils/hostosinfo.h>
#include <extensionsystem/pluginmanager.h>
#include <QDir>
#include <QFile>
@@ -61,6 +72,7 @@
#include <QTextStream>
#include <QCoreApplication>
#include <QTimer>
#include <QRegExp>
#include <QDebug>
@@ -68,6 +80,104 @@ using namespace QmlJS;
using namespace QmlJSTools;
using namespace QmlJSTools::Internal;
ModelManagerInterface::ProjectInfo QmlJSTools::defaultProjectInfoForProject(
ProjectExplorer::Project *project)
{
ModelManagerInterface::ProjectInfo projectInfo(project);
ProjectExplorer::Target *activeTarget = 0;
if (project) {
Core::MimeDatabase *db = Core::ICore::mimeDatabase();
QList<Core::MimeGlobPattern> globs;
QList<Core::MimeType> mimeTypes = db->mimeTypes();
foreach (const Core::MimeType &mimeType, mimeTypes)
if (mimeType.type() == QLatin1String(Constants::QML_MIMETYPE)
|| mimeType.subClassesOf().contains(QLatin1String(Constants::QML_MIMETYPE)))
globs << mimeType.globPatterns();
if (globs.isEmpty())
globs << Core::MimeGlobPattern(QRegExp(QLatin1String(".*\\.(?:qbs|qml|qmltypes|qmlproject)$")));
foreach (const QString &filePath
, project->files(ProjectExplorer::Project::ExcludeGeneratedFiles))
foreach (const Core::MimeGlobPattern &glob, globs)
if (glob.regExp().exactMatch(filePath))
projectInfo.sourceFiles << filePath;
activeTarget = project->activeTarget();
}
ProjectExplorer::Kit *activeKit = activeTarget ? activeTarget->kit() :
ProjectExplorer::KitManager::instance()->defaultKit();
QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(activeKit);
bool preferDebugDump = false;
bool setPreferDump = false;
projectInfo.tryQmlDump = false;
if (activeTarget) {
if (ProjectExplorer::BuildConfiguration *bc = activeTarget->activeBuildConfiguration()) {
preferDebugDump = bc->buildType() == ProjectExplorer::BuildConfiguration::Debug;
setPreferDump = true;
}
}
if (!setPreferDump && qtVersion)
preferDebugDump = (qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::DebugBuild);
if (qtVersion && qtVersion->isValid()) {
projectInfo.tryQmlDump = project && (
qtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
|| qtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT));
projectInfo.qtQmlPath = qtVersion->qmakeProperty("QT_INSTALL_QML");
projectInfo.qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
projectInfo.qtVersionString = qtVersion->qtVersionString();
}
if (projectInfo.tryQmlDump) {
ProjectExplorer::ToolChain *toolChain =
ProjectExplorer::ToolChainKitInformation::toolChain(activeKit);
QtSupport::QmlDumpTool::pathAndEnvironment(project, qtVersion,
toolChain,
preferDebugDump, &projectInfo.qmlDumpPath,
&projectInfo.qmlDumpEnvironment);
} else {
projectInfo.qmlDumpPath.clear();
projectInfo.qmlDumpEnvironment.clear();
}
setupProjectInfoQmlBundles(projectInfo);
return projectInfo;
}
void QmlJSTools::setupProjectInfoQmlBundles(ModelManagerInterface::ProjectInfo &projectInfo)
{
ProjectExplorer::Target *activeTarget = 0;
if (projectInfo.project) {
activeTarget = projectInfo.project->activeTarget();
}
ProjectExplorer::Kit *activeKit = activeTarget
? activeTarget->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
QHash<QString, QString> replacements;
replacements.insert(QLatin1String("$(QT_INSTALL_IMPORTS)"), projectInfo.qtImportsPath);
replacements.insert(QLatin1String("$(QT_INSTALL_QML)"), projectInfo.qtQmlPath);
QList<IBundleProvider *> bundleProviders =
ExtensionSystem::PluginManager::getObjects<IBundleProvider>();
foreach (IBundleProvider *bp, bundleProviders) {
if (bp)
bp->mergeBundlesForKit(activeKit, projectInfo.activeBundle, replacements);
}
projectInfo.extendedBundle = projectInfo.activeBundle;
if (projectInfo.project) {
QSet<ProjectExplorer::Kit *> currentKits;
foreach (const ProjectExplorer::Target *t, projectInfo.project->targets())
if (t->kit())
currentKits.insert(t->kit());
currentKits.remove(activeKit);
foreach (ProjectExplorer::Kit *kit, currentKits) {
foreach (IBundleProvider *bp, bundleProviders)
if (bp)
bp->mergeBundlesForKit(kit, projectInfo.extendedBundle, replacements);
}
}
}
static QStringList environmentImportPaths();
static void mergeSuffixes(QStringList &l1, const QStringList &l2)
@@ -373,6 +483,7 @@ void ModelManager::updateProjectInfo(const ProjectInfo &pinfo)
void ModelManager::removeProjectInfo(ProjectExplorer::Project *project)
{
ProjectInfo info(project);
info.sourceFiles.clear();
// update with an empty project info to clear data
updateProjectInfo(info);
@@ -382,6 +493,16 @@ void ModelManager::removeProjectInfo(ProjectExplorer::Project *project)
}
}
ModelManagerInterface::ProjectInfo ModelManager::projectInfoForPath(QString path)
{
QMutexLocker locker(&m_mutex);
foreach (const ProjectInfo &p, m_projects)
if (p.sourceFiles.contains(path))
return p;
return ProjectInfo();
}
void ModelManager::emitDocumentChangedOnDisk(Document::Ptr doc)
{ emit documentChangedOnDisk(doc); }
@@ -660,6 +781,18 @@ QStringList ModelManager::importPaths() const
return m_allImportPaths;
}
QmlLanguageBundles ModelManager::activeBundles() const
{
QMutexLocker l(&m_mutex);
return m_activeBundles;
}
QmlLanguageBundles ModelManager::extendedBundles() const
{
QMutexLocker l(&m_mutex);
return m_extendedBundles;
}
static QStringList environmentImportPaths()
{
QStringList paths;
@@ -679,6 +812,8 @@ static QStringList environmentImportPaths()
void ModelManager::updateImportPaths()
{
QStringList allImportPaths;
QmlLanguageBundles activeBundles;
QmlLanguageBundles extendedBundles;
QMapIterator<ProjectExplorer::Project *, ProjectInfo> it(m_projects);
while (it.hasNext()) {
it.next();
@@ -688,12 +823,40 @@ void ModelManager::updateImportPaths()
allImportPaths += canonicalPath;
}
}
it.toFront();
while (it.hasNext()) {
it.next();
activeBundles.mergeLanguageBundles(it.value().activeBundle);
foreach (Document::Language l, it.value().activeBundle.languages()) {
foreach (const QString &path, it.value().activeBundle.bundleForLanguage(l)
.searchPaths().stringList()) {
const QString canonicalPath = QFileInfo(path).canonicalFilePath();
if (!canonicalPath.isEmpty())
allImportPaths += canonicalPath;
}
}
}
it.toFront();
while (it.hasNext()) {
it.next();
extendedBundles.mergeLanguageBundles(it.value().extendedBundle);
foreach (Document::Language l, it.value().extendedBundle.languages()) {
foreach (const QString &path, it.value().extendedBundle.bundleForLanguage(l)
.searchPaths().stringList()) {
const QString canonicalPath = QFileInfo(path).canonicalFilePath();
if (!canonicalPath.isEmpty())
allImportPaths += canonicalPath;
}
}
}
allImportPaths += m_defaultImportPaths;
allImportPaths.removeDuplicates();
{
QMutexLocker l(&m_mutex);
m_allImportPaths = allImportPaths;
m_activeBundles = activeBundles;
m_extendedBundles = extendedBundles;
}
@@ -82,12 +82,15 @@ public:
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
virtual void updateProjectInfo(const ProjectInfo &pinfo);
Q_SLOT virtual void removeProjectInfo(ProjectExplorer::Project *project);
virtual ProjectInfo projectInfoForPath(QString path);
void updateDocument(QmlJS::Document::Ptr doc);
void updateLibraryInfo(const QString &path, const QmlJS::LibraryInfo &info);
void emitDocumentChangedOnDisk(QmlJS::Document::Ptr doc);
virtual QStringList importPaths() const;
virtual QmlJS::QmlLanguageBundles activeBundles() const;
virtual QmlJS::QmlLanguageBundles extendedBundles() const;
virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath,
const QString &importUri, const QString &importVersion);
@@ -136,6 +139,8 @@ private:
QmlJS::Snapshot _newestSnapshot;
QStringList m_allImportPaths;
QStringList m_defaultImportPaths;
QmlJS::QmlLanguageBundles m_activeBundles;
QmlJS::QmlLanguageBundles m_extendedBundles;
QFutureSynchronizer<void> m_synchronizer;
@@ -153,6 +158,11 @@ private:
};
} // namespace Internal
QMLJSTOOLS_EXPORT QmlJS::ModelManagerInterface::ProjectInfo defaultProjectInfoForProject(
ProjectExplorer::Project *project);
QMLJSTOOLS_EXPORT void setupProjectInfoQmlBundles(QmlJS::ModelManagerInterface::ProjectInfo &projectInfo);
} // namespace QmlJSTools
#endif // QMLJSMODELMANAGER_H
+2
View File
@@ -10,6 +10,7 @@ DEFINES += QMLJSTOOLS_LIBRARY
}
HEADERS += \
$$PWD/qmljsbundleprovider.h \
$$PWD/qmljstoolsplugin.h \
$$PWD/qmljstoolsconstants.h \
$$PWD/qmljstoolssettings.h \
@@ -36,6 +37,7 @@ HEADERS += \
$$PWD/qmlconsoleproxymodel.h
SOURCES += \
$$PWD/qmljsbundleprovider.cpp \
$$PWD/qmljstoolsplugin.cpp \
$$PWD/qmljstoolssettings.cpp \
$$PWD/qmljscodestylepreferencesfactory.cpp \
+2
View File
@@ -21,6 +21,8 @@ QtcPlugin {
cpp.includePaths: base.concat("../../libs/3rdparty")
files: [
"qmljsbundleprovider.cpp",
"qmljsbundleprovider.h",
"qmljscodestylepreferencesfactory.cpp",
"qmljscodestylepreferencesfactory.h",
"qmljscodestylesettingspage.cpp",
@@ -35,6 +35,7 @@
#include "qmljstoolsconstants.h"
#include "qmljstoolssettings.h"
#include "qmlconsolemanager.h"
#include "qmljsbundleprovider.h"
#include <extensionsystem/pluginmanager.h>
@@ -94,6 +95,7 @@ bool QmlJSToolsPlugin::initialize(const QStringList &arguments, QString *error)
addAutoReleasedObject(locatorData);
addAutoReleasedObject(new FunctionFilter(locatorData));
addAutoReleasedObject(new QmlJSCodeStyleSettingsPage);
addAutoReleasedObject(new BasicBundleProvider);
// Menus
Core::ActionContainer *mtools = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
+3 -24
View File
@@ -45,6 +45,7 @@
#include <qtsupport/qtversionmanager.h>
#include <qtsupport/qtkitinformation.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <utils/fileutils.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
@@ -184,32 +185,10 @@ void QmlProject::refresh(RefreshOptions options)
if (options & Files)
m_rootNode->refresh();
QmlJS::ModelManagerInterface::ProjectInfo projectInfo(this);
projectInfo.sourceFiles = files();
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
QmlJSTools::defaultProjectInfoForProject(this);
projectInfo.importPaths = customImportPaths();
QtSupport::BaseQtVersion *qtVersion = 0;
{
ProjectExplorer::Target *target = activeTarget();
ProjectExplorer::Kit *kit = target ? target->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
ProjectExplorer::ToolChain *toolChain = ProjectExplorer::ToolChainKitInformation::toolChain(kit);
qtVersion = QtSupport::QtKitInformation::qtVersion(kit);
QtSupport::QmlDumpTool::pathAndEnvironment(this, qtVersion, toolChain, false,
&projectInfo.qmlDumpPath, &projectInfo.qmlDumpEnvironment);
}
if (qtVersion) {
projectInfo.tryQmlDump = true;
projectInfo.qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
projectInfo.qtQmlPath = qtVersion->qmakeProperty("QT_INSTALL_QML");
projectInfo.qtVersionString = qtVersion->qtVersionString();
if (!projectInfo.qtQmlPath.isEmpty())
projectInfo.importPaths += projectInfo.qtQmlPath;
if (!projectInfo.qtImportsPath.isEmpty())
projectInfo.importPaths += projectInfo.qtImportsPath;
}
m_modelManager->updateProjectInfo(projectInfo);
}
+4 -38
View File
@@ -51,6 +51,7 @@
#include <extensionsystem/pluginmanager.h>
#include <cpptools/ModelManagerInterface.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h>
#include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/toolchain.h>
@@ -592,8 +593,8 @@ void Qt4Project::updateQmlJSCodeModel()
if (!modelManager)
return;
QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->projectInfo(this);
projectInfo.sourceFiles = m_projectFiles->files[QMLType];
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
QmlJSTools::defaultProjectInfoForProject(this);
FindQt4ProFiles findQt4ProFiles;
QList<Qt4ProFileNode *> proFiles = findQt4ProFiles(rootProjectNode());
@@ -616,47 +617,12 @@ void Qt4Project::updateQmlJSCodeModel()
// This assumption fails when there are no QDeclarativeEngine/QDeclarativeView (QtQuick 1)
// or QQmlEngine/QQuickView (QtQuick 2) instances.
Core::Context pl(ProjectExplorer::Constants::LANG_CXX);
if (projectInfo.sourceFiles.count() && hasQmlLib)
if (m_projectFiles->files[QMLType].count() && hasQmlLib)
pl.add(ProjectExplorer::Constants::LANG_QMLJS);
setProjectLanguages(pl);
bool preferDebugDump = false;
projectInfo.tryQmlDump = false;
ProjectExplorer::Target *t = activeTarget();
ProjectExplorer::Kit *k = t ? t->kit() : ProjectExplorer::KitManager::instance()->defaultKit();
QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k);
if (t) {
if (Qt4BuildConfiguration *bc = qobject_cast<Qt4BuildConfiguration *>(t->activeBuildConfiguration()))
preferDebugDump = bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild;
} else {
if (qtVersion)
preferDebugDump = qtVersion->defaultBuildConfig() & QtSupport::BaseQtVersion::DebugBuild;
}
if (qtVersion && qtVersion->isValid()) {
projectInfo.tryQmlDump = qtVersion->type() == QLatin1String(QtSupport::Constants::DESKTOPQT)
|| qtVersion->type() == QLatin1String(QtSupport::Constants::SIMULATORQT);
projectInfo.qtQmlPath = qtVersion->qmakeProperty("QT_INSTALL_QML");
if (!projectInfo.qtQmlPath.isEmpty())
projectInfo.importPaths += projectInfo.qtQmlPath;
projectInfo.qtImportsPath = qtVersion->qmakeProperty("QT_INSTALL_IMPORTS");
if (!projectInfo.qtImportsPath.isEmpty())
projectInfo.importPaths += projectInfo.qtImportsPath;
projectInfo.qtVersionString = qtVersion->qtVersionString();
}
projectInfo.importPaths.removeDuplicates();
if (projectInfo.tryQmlDump) {
QtSupport::QmlDumpTool::pathAndEnvironment(this, qtVersion,
ToolChainKitInformation::toolChain(k),
preferDebugDump, &projectInfo.qmlDumpPath,
&projectInfo.qmlDumpEnvironment);
} else {
projectInfo.qmlDumpPath.clear();
projectInfo.qmlDumpEnvironment.clear();
}
modelManager->updateProjectInfo(projectInfo);
}
@@ -14,6 +14,7 @@ QtcPlugin {
Depends { name: "QmlJS" }
Depends { name: "CPlusPlus" }
Depends { name: "TextEditor" }
Depends { name: "QmlJSTools" }
Depends { name: "cpp" }
cpp.defines: base.concat([
@@ -3,3 +3,4 @@ include(../../plugins/qtsupport/qtsupport.pri)
include(../../plugins/cpptools/cpptools.pri)
include(../../plugins/debugger/debugger.pri)
include(../../libs/qmljs/qmljs.pri)
include(../../plugins/qmljstools/qmljstools.pri)