Commit 02bdf30f authored by Fawzi Mohamed's avatar Fawzi Mohamed
Browse files

qmljs: improve handling of qml dialects



Language::Enum -> QmlDialect
 * class instead of enum
 * moved Language specific operations to it (from Document)
 * nicer handling
QStringList -> PathsAndLanguages
 * store language along with path, to perform a correct scan and improve
   path handling

Change-Id: If69d35c63cfeb48aa670b51870916cd0c40f1916
Reviewed-by: default avatarThomas Hartmann <Thomas.Hartmann@digia.com>
parent faa0e5b9
......@@ -44,7 +44,8 @@ HEADERS += \
$$PWD/qmljsconstants.h \
$$PWD/qmljsimportdependencies.h \
$$PWD/qmljsviewercontext.h \
$$PWD/qmljsdescribevalue.h
$$PWD/qmljsdescribevalue.h \
$$PWD/qmljsdialect.h
SOURCES += \
$$PWD/qmljsbind.cpp \
......@@ -80,7 +81,8 @@ SOURCES += \
$$PWD/qmljsqrcparser.cpp \
$$PWD/qmljsimportdependencies.cpp \
$$PWD/qmljsviewercontext.cpp \
$$PWD/qmljsdescribevalue.cpp
$$PWD/qmljsdescribevalue.cpp \
$$PWD/qmljsdialect.cpp
RESOURCES += \
......
......@@ -41,6 +41,7 @@ QtcLibrary {
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
"qmljsindenter.cpp", "qmljsindenter.h",
"qmljsinterpreter.cpp", "qmljsinterpreter.h",
"qmljsdialect.cpp", "qmljsdialect.h",
"qmljslineinfo.cpp", "qmljslineinfo.h",
"qmljslink.cpp", "qmljslink.h",
"qmljsmodelmanagerinterface.cpp", "qmljsmodelmanagerinterface.h",
......
......@@ -202,18 +202,18 @@ bool Bind::visit(UiImport *ast)
const QString importId = ast->importId.toString();
ImportInfo import = ImportInfo::moduleImport(toString(ast->importUri), version,
importId, ast);
if (_doc->language() == Language::Qml) {
if (_doc->language() == Dialect::Qml) {
const QString importStr = import.name() + importId;
if (ModelManagerInterface::instance()) {
QmlLanguageBundles langBundles = ModelManagerInterface::instance()->extendedBundles();
QmlBundle qq1 = langBundles.bundleForLanguage(Language::QmlQtQuick1);
QmlBundle qq2 = langBundles.bundleForLanguage(Language::QmlQtQuick2);
QmlBundle qq1 = langBundles.bundleForLanguage(Dialect::QmlQtQuick1);
QmlBundle qq2 = langBundles.bundleForLanguage(Dialect::QmlQtQuick2);
bool isQQ1 = qq1.supportedImports().contains(importStr);
bool isQQ2 = qq2.supportedImports().contains(importStr);
if (isQQ1 && ! isQQ2)
_doc->setLanguage(Language::QmlQtQuick1);
_doc->setLanguage(Dialect::QmlQtQuick1);
if (isQQ2 && ! isQQ1)
_doc->setLanguage(Language::QmlQtQuick2);
_doc->setLanguage(Dialect::QmlQtQuick2);
}
}
_imports += import;
......
......@@ -288,14 +288,14 @@ bool QmlBundle::readFrom(QString path, QStringList *errors)
return errs.isEmpty();
}
QmlBundle QmlLanguageBundles::bundleForLanguage(Language::Enum l) const
QmlBundle QmlLanguageBundles::bundleForLanguage(Dialect l) const
{
if (m_bundles.contains(l))
return m_bundles.value(l);
return QmlBundle();
}
void QmlLanguageBundles::mergeBundleForLanguage(Language::Enum l, const QmlBundle &bundle)
void QmlLanguageBundles::mergeBundleForLanguage(Dialect l, const QmlBundle &bundle)
{
if (bundle.isEmpty())
return;
......@@ -305,14 +305,14 @@ void QmlLanguageBundles::mergeBundleForLanguage(Language::Enum l, const QmlBundl
m_bundles.insert(l,bundle);
}
QList<Language::Enum> QmlLanguageBundles::languages() const
QList<Dialect> QmlLanguageBundles::languages() const
{
return m_bundles.keys();
}
void QmlLanguageBundles::mergeLanguageBundles(const QmlLanguageBundles &o)
{
foreach (Language::Enum l, o.languages())
foreach (Dialect l, o.languages())
mergeBundleForLanguage(l, o.bundleForLanguage(l));
}
......
......@@ -100,12 +100,12 @@ private:
class QMLJS_EXPORT QmlLanguageBundles
{
public:
QmlBundle bundleForLanguage(Language::Enum l) const;
void mergeBundleForLanguage(Language::Enum l, const QmlBundle &bundle);
QList<Language::Enum> languages() const;
QmlBundle bundleForLanguage(Dialect l) const;
void mergeBundleForLanguage(Dialect l, const QmlBundle &bundle);
QList<Dialect> languages() const;
void mergeLanguageBundles(const QmlLanguageBundles &);
private:
QHash<Language::Enum,QmlBundle> m_bundles;
QHash<Dialect,QmlBundle> m_bundles;
};
} // namespace QmlJS
......
/****************************************************************************
**
** Copyright (C) 2014 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 "qmljsdialect.h"
namespace QmlJS {
bool Dialect::isQmlLikeLanguage() const
{
switch (m_dialect) {
case Dialect::Qml:
case Dialect::QmlQtQuick1:
case Dialect::QmlQtQuick2:
case Dialect::QmlQbs:
case Dialect::QmlProject:
case Dialect::QmlTypeInfo:
case Dialect::AnyLanguage:
return true;
default:
return false;
}
}
bool Dialect::isFullySupportedLanguage() const
{
switch (m_dialect) {
case Dialect::JavaScript:
case Dialect::Json:
case Dialect::Qml:
case Dialect::QmlQtQuick1:
case Dialect::QmlQtQuick2:
return true;
case Dialect::NoLanguage:
case Dialect::AnyLanguage:
case Dialect::QmlQbs:
case Dialect::QmlProject:
case Dialect::QmlTypeInfo:
break;
}
return false;
}
bool Dialect::isQmlLikeOrJsLanguage() const
{
switch (m_dialect) {
case Dialect::Qml:
case Dialect::QmlQtQuick1:
case Dialect::QmlQtQuick2:
case Dialect::QmlQbs:
case Dialect::QmlProject:
case Dialect::QmlTypeInfo:
case Dialect::JavaScript:
case Dialect::AnyLanguage:
return true;
default:
return false;
}
}
QString Dialect::toString() const
{
switch (m_dialect) {
case Dialect::JavaScript:
return QLatin1String("JavaScript");
case Dialect::Json:
return QLatin1String("Json");
case Dialect::Qml:
return QLatin1String("Qml");
case Dialect::QmlQtQuick1:
return QLatin1String("QmlQtQuick1");
case Dialect::QmlQtQuick2:
return QLatin1String("QmlQtQuick2");
case Dialect::NoLanguage:
return QLatin1String("NoLanguage");
case Dialect::AnyLanguage:
return QLatin1String("AnyLanguage");
case Dialect::QmlQbs:
return QLatin1String("QmlQbs");
case Dialect::QmlProject:
return QLatin1String("QmlProject");
case Dialect::QmlTypeInfo:
break;
}
return QLatin1String("QmlTypeInfo");
}
bool Dialect::operator ==(const Dialect &o) const {
return m_dialect == o.m_dialect;
}
bool Dialect::operator <(const Dialect &o) const {
return m_dialect < o.m_dialect;
}
Dialect Dialect::mergeLanguages(const Dialect &l1, const Dialect &l2)
{
if (l1 == Dialect::NoLanguage)
return l2;
if (l2 == Dialect::NoLanguage)
return l1;
QList<Dialect> ll1 = l1.companionLanguages();
QList<Dialect> ll2 = l2.companionLanguages();
bool i1 = ll1.contains(l2);
bool i2 = ll2.contains(l1);
if (i1 && i2) {
if (ll1.size() > ll2.size())
return l1;
if (ll2.size() > ll1.size())
return l2;
if (l1 < l2)
return l1;
return l2;
}
if (i1 && !i2)
return l1;
if (i2 && !i1)
return l2;
QList<Dialect> qmlLangs = Dialect(Qml).companionLanguages();
if (qmlLangs.contains(l1) && qmlLangs.contains(l2))
return Dialect::Qml;
return Dialect::AnyLanguage;
}
void Dialect::mergeLanguage(const Dialect &l2) {
*this = mergeLanguages(*this, l2);
}
bool Dialect::restrictLanguage(const Dialect &l2)
{
if (*this == l2)
return true;
QList<Dialect> ll1 = companionLanguages();
QList<Dialect> ll2 = l2.companionLanguages();
bool i1 = ll1.contains(l2);
bool i2 = ll2.contains(*this);
if (i1 && i2) {
if (ll1.size() < ll2.size())
return true;
if (ll2.size() < ll1.size()) {
*this = l2;
return true;
}
if (m_dialect < l2.m_dialect)
return true;
*this = l2;
return true;
}
if (i1 && !i2) {
*this = l2;
return true;
}
if (i2 && !i1)
return true;
QList<Dialect> qmlLangs = Dialect(Qml).companionLanguages();
if (qmlLangs.contains(*this) && qmlLangs.contains(l2))
*this = Dialect::Qml;
*this = Dialect::AnyLanguage;
return false;
}
QList<Dialect> Dialect::companionLanguages() const
{
QList<Dialect> langs;
langs << *this;
switch (m_dialect) {
case Dialect::JavaScript:
case Dialect::Json:
case Dialect::QmlProject:
case Dialect::QmlTypeInfo:
break;
case Dialect::QmlQbs:
langs << Dialect::JavaScript;
break;
case Dialect::Qml:
langs << Dialect::QmlQtQuick1 << Dialect::QmlQtQuick2 << Dialect::JavaScript;
break;
case Dialect::QmlQtQuick1:
case Dialect::QmlQtQuick2:
langs << Dialect::Qml << Dialect::JavaScript;
break;
case Dialect::AnyLanguage:
langs << Dialect::JavaScript << Dialect::Json << Dialect::QmlProject << Dialect:: QmlQbs
<< Dialect::QmlTypeInfo << Dialect::QmlQtQuick1 << Dialect::QmlQtQuick2
<< Dialect::Qml;
break;
case Dialect::NoLanguage:
return QList<Dialect>(); // return at least itself?
}
if (*this != Dialect::AnyLanguage)
langs << Dialect::AnyLanguage;
return langs;
}
uint qHash(const Dialect &o)
{
return uint(o.dialect());
}
QDebug operator << (QDebug &dbg, const Dialect &dialect)
{
dbg << dialect.toString();
return dbg;
}
PathAndLanguage::PathAndLanguage(const Utils::FileName &path, Dialect language)
: m_path(path), m_language(language)
{ }
bool PathAndLanguage::operator ==(const PathAndLanguage &other) const
{
return path() == other.path() && language() == other.language();
}
bool PathAndLanguage::operator <(const PathAndLanguage &other) const
{
if (path() < other.path())
return true;
if (path() > other.path())
return false;
if (language() == other.language())
return false;
bool i1 = other.language().companionLanguages().contains(language());
bool i2 = language().companionLanguages().contains(other.language());
if (i1 && !i2)
return true;
if (i2 && !i1)
return false;
return language() < other.language();
}
QDebug operator << (QDebug &dbg, const PathAndLanguage &pathAndLanguage)
{
dbg << "{ path:" << pathAndLanguage.path() << " language:" << pathAndLanguage.language().toString() << "}";
return dbg;
}
bool PathsAndLanguages::maybeInsert(const PathAndLanguage &pathAndLanguage) {
for (int i = 0; i < m_list.size(); ++i) {
PathAndLanguage currentElement = m_list.at(i);
if (currentElement.path() == pathAndLanguage.path()) {
int j = i;
do {
if (pathAndLanguage.language() < currentElement.language()) {
if (currentElement.language() == pathAndLanguage.language())
return false;
break;
}
++j;
if (j == m_list.length())
break;
currentElement = m_list.at(j);
} while (currentElement.path() == pathAndLanguage.path());
m_list.insert(j, pathAndLanguage);
return true;
}
}
m_list.append(pathAndLanguage);
return true;
}
void PathsAndLanguages::compact() {
int oldCompactionPlace = 0;
Utils::FileName oldPath = m_list.first().path();
QList<PathAndLanguage> compactedList;
bool restrictFailed = false;
for (int i = 1; i < m_list.length(); ++i) {
Utils::FileName newPath = m_list.at(i).path();
if (newPath == oldPath) {
int newCompactionPlace = i - 1;
compactedList << m_list.mid(oldCompactionPlace, newCompactionPlace - oldCompactionPlace);
LanguageMerger merger;
merger.merge(m_list.at(i - 1).language());
do {
merger.merge(m_list.at(i++).language());
if (++i == m_list.length())
break;
newPath = m_list.at(i).path();
} while (newPath == oldPath);
oldCompactionPlace = i;
compactedList << PathAndLanguage(oldPath, merger.mergedLanguage());
if (merger.restrictFailed())
restrictFailed = true;
}
oldPath = newPath;
}
if (oldCompactionPlace == 0)
return;
if (restrictFailed)
qCWarning(qmljsLog) << "failed to restrict PathAndLanguages " << m_list;
m_list = compactedList;
}
void LanguageMerger::merge(Dialect l)
{
bool restrictSuccedeed = m_specificLanguage.restrictLanguage(l);
m_specificLanguage.mergeLanguage(m_minimalSpecificLanguage);
if (!restrictSuccedeed) {
m_minimalSpecificLanguage = m_specificLanguage;
m_restrictFailed = true;
}
}
} // namespace QmlJS
/****************************************************************************
**
** Copyright (C) 2014 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 QMLJSDIALECT_H
#define QMLJSDIALECT_H
#include "qmljsconstants.h"
#include <utils/fileutils.h>
#include <QString>
namespace QmlJS {
class QMLJS_EXPORT Dialect {
public:
enum Enum
{
NoLanguage = 0,
JavaScript = 1,
Json = 2,
Qml = 3,
QmlQtQuick1 = 4,
QmlQtQuick2 = 5,
QmlQbs = 6,
QmlProject = 7,
QmlTypeInfo = 8,
AnyLanguage = 9,
};
Dialect(Enum dialect = NoLanguage)
: m_dialect(dialect)
{ }
static Dialect mergeLanguages(const Dialect &l1, const Dialect &l2);
bool isQmlLikeLanguage() const;
bool isFullySupportedLanguage() const;
bool isQmlLikeOrJsLanguage() const;
QList<Dialect> companionLanguages() const;
bool restrictLanguage(const Dialect &l2);
QString toString() const;
bool operator ==(const Dialect &o) const;
bool operator !=(const Dialect &o) const {
return !(*this == o);
}
bool operator <(const Dialect &o) const;
Enum dialect() const {
return m_dialect;
}
void mergeLanguage(const Dialect &l2);
private:
Enum m_dialect;
};
QMLJS_EXPORT uint qHash(const Dialect &o);
QMLJS_EXPORT QDebug operator << (QDebug &dbg, const Dialect &dialect);
class QMLJS_EXPORT PathAndLanguage {
public:
PathAndLanguage(const Utils::FileName &path = Utils::FileName(), Dialect language = Dialect::AnyLanguage);
PathAndLanguage(const PathAndLanguage &o)
: m_path(o.path()), m_language(o.language())
{ }
Utils::FileName path() const {
return m_path;
}
Dialect language() const {
return m_language;
}
bool operator ==(const PathAndLanguage &other) const;
bool operator < (const PathAndLanguage &other) const;
private:
Utils::FileName m_path;
Dialect m_language;
};
// tries to find the "most specific" language still compatible with all requested ones
class QMLJS_EXPORT LanguageMerger
{
public:
LanguageMerger()
: m_specificLanguage(Dialect::AnyLanguage),
m_minimalSpecificLanguage(Dialect::NoLanguage),
m_restrictFailed(false)
{ }
void merge(Dialect l);
Dialect mergedLanguage() const {
return m_specificLanguage;
}
bool restrictFailed() const {
return m_restrictFailed;
}
private:
Dialect m_specificLanguage;
Dialect m_minimalSpecificLanguage;
bool m_restrictFailed;
};
QMLJS_EXPORT QDebug operator << (QDebug &dbg, const PathAndLanguage &pathAndLanguage);
class QMLJS_EXPORT PathsAndLanguages
{
public:
explicit PathsAndLanguages()
{ }
explicit PathsAndLanguages(const QList<PathAndLanguage> &list)
: m_list(list)
{ }
PathsAndLanguages(const PathsAndLanguages &o)
: m_list(o.m_list)
{ }
bool maybeInsert(const Utils::FileName &path, Dialect language = Dialect::AnyLanguage) {
return maybeInsert(PathAndLanguage(path, language));
}
bool maybeInsert(const PathAndLanguage &pathAndLanguage);
PathAndLanguage at(int i) const {
return m_list.at(i);
}
int size() const {
return m_list.size();
}
int length() const {
return m_list.length();
}
void clear() {
m_list.clear();
}
// foreach support
typedef QList<PathAndLanguage>::const_iterator const_iterator;
const_iterator begin() const {
return m_list.begin();