qmljs: generalize language treatement

* better support qbs and similar non QtQuick qml languages
* begin to clean and migrate things from ModelManager to
  ModelManagerInterface

Change-Id: Ifa39dc1e4c568e9e21307f4913dcb5989e80420f
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
This commit is contained in:
Fawzi Mohamed
2014-01-22 18:38:45 +01:00
parent a8e8c5fe1d
commit a8ea0a0736
12 changed files with 118 additions and 80 deletions

View File

@@ -137,6 +137,34 @@ bool Document::isQmlLikeOrJsLanguage(Language::Enum language)
} }
} }
QList<Language::Enum> Document::companionLanguages(Language::Enum language)
{
QList<Language::Enum> langs;
langs << language;
switch (language) {
case Language::JavaScript:
case Language::Json:
case Language::QmlProject:
case Language::QmlTypeInfo:
break;
case Language::QmlQbs:
langs << Language::JavaScript;
break;
case Language::Qml:
langs << Language::QmlQtQuick1 << Language::QmlQtQuick2 << Language::JavaScript;
break;
case Language::QmlQtQuick1:
case Language::QmlQtQuick2:
langs << Language::Qml << Language::JavaScript;
break;
case Language::Unknown:
langs << Language::JavaScript << Language::Json << Language::QmlProject << Language:: QmlQbs
<< Language::QmlTypeInfo << Language::QmlQtQuick1 << Language::QmlQtQuick2 ;
break;
}
return langs;
}
Document::Document(const QString &fileName, Language::Enum language) Document::Document(const QString &fileName, Language::Enum language)
: _engine(0) : _engine(0)
, _ast(0) , _ast(0)
@@ -177,19 +205,6 @@ Document::MutablePtr Document::create(const QString &fileName, Language::Enum la
return doc; return doc;
} }
Language::Enum Document::guessLanguageFromSuffix(const QString &fileName)
{
if (fileName.endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
return Language::Qml;
if (fileName.endsWith(QLatin1String(".qbs"), Qt::CaseInsensitive))
return Language::QmlQbs;
if (fileName.endsWith(QLatin1String(".js"), Qt::CaseInsensitive))
return Language::JavaScript;
if (fileName.endsWith(QLatin1String(".json"), Qt::CaseInsensitive))
return Language::Json;
return Language::Unknown;
}
Document::Ptr Document::ptr() const Document::Ptr Document::ptr() const
{ {
return _ptr.toStrongRef(); return _ptr.toStrongRef();

View File

@@ -56,6 +56,7 @@ public:
static bool isQmlLikeLanguage(Language::Enum languge); static bool isQmlLikeLanguage(Language::Enum languge);
static bool isFullySupportedLanguage(Language::Enum language); static bool isFullySupportedLanguage(Language::Enum language);
static bool isQmlLikeOrJsLanguage(Language::Enum language); static bool isQmlLikeOrJsLanguage(Language::Enum language);
static QList<Language::Enum> companionLanguages(Language::Enum language);
protected: protected:
Document(const QString &fileName, Language::Enum language); Document(const QString &fileName, Language::Enum language);
@@ -63,7 +64,6 @@ public:
~Document(); ~Document();
static MutablePtr create(const QString &fileName, Language::Enum language); static MutablePtr create(const QString &fileName, Language::Enum language);
static Language::Enum guessLanguageFromSuffix(const QString &fileName);
Document::Ptr ptr() const; Document::Ptr ptr() const;

View File

@@ -330,7 +330,7 @@ Import LinkPrivate::importFileOrDirectory(Document::Ptr doc, const ImportInfo &i
->filesInQrcPath(path)); ->filesInQrcPath(path));
while (iter.hasNext()) { while (iter.hasNext()) {
iter.next(); iter.next();
if (Document::isQmlLikeLanguage(Document::guessLanguageFromSuffix(iter.key()))) { if (Document::isQmlLikeLanguage(ModelManagerInterface::guessLanguageOfFile(iter.key()))) {
Document::Ptr importedDoc = snapshot.document(iter.value().at(0)); Document::Ptr importedDoc = snapshot.document(iter.value().at(0));
if (importedDoc && importedDoc->bind()->rootObjectValue()) { if (importedDoc && importedDoc->bind()->rootObjectValue()) {
const QString targetName = QFileInfo(iter.key()).baseName(); const QString targetName = QFileInfo(iter.key()).baseName();

View File

@@ -29,6 +29,8 @@
#include "qmljsmodelmanagerinterface.h" #include "qmljsmodelmanagerinterface.h"
#include <QFileInfo>
using namespace QmlJS; using namespace QmlJS;
/*! /*!
@@ -65,8 +67,54 @@ ModelManagerInterface::~ModelManagerInterface()
g_instance = 0; g_instance = 0;
} }
static QHash<QString, Language::Enum> defaultLanguageMapping()
{
QHash<QString, Language::Enum> res;
res[QLatin1String("js")] = Language::JavaScript;
res[QLatin1String("qml")] = Language::Qml;
res[QLatin1String("qmltypes")] = Language::QmlTypeInfo;
res[QLatin1String("qmlproject")] = Language::QmlProject;
res[QLatin1String("json")] = Language::Json;
res[QLatin1String("qbs")] = Language::QmlQbs;
return res;
}
Language::Enum ModelManagerInterface::guessLanguageOfFile(const QString &fileName)
{
QHash<QString, Language::Enum> lMapping;
if (instance())
lMapping = instance()->languageForSuffix();
else
lMapping = defaultLanguageMapping();
const QFileInfo info(fileName);
const QString fileSuffix = info.suffix();
return lMapping.value(fileSuffix, Language::Unknown);
}
QStringList ModelManagerInterface::globPatternsForLanguages(const QList<Language::Enum> languages)
{
QHash<QString, Language::Enum> lMapping;
if (instance())
lMapping = instance()->languageForSuffix();
else
lMapping = defaultLanguageMapping();
QStringList patterns;
QHashIterator<QString,Language::Enum> i(lMapping);
while (i.hasNext()) {
i.next();
if (languages.contains(i.value()))
patterns << QLatin1String("*.") + i.key();
}
return patterns;
}
ModelManagerInterface *ModelManagerInterface::instance() ModelManagerInterface *ModelManagerInterface::instance()
{ {
return g_instance; return g_instance;
} }
QHash<QString, Language::Enum> ModelManagerInterface::languageForSuffix() const
{
return defaultLanguageMapping();
}

View File

@@ -139,6 +139,8 @@ public:
ModelManagerInterface(QObject *parent = 0); ModelManagerInterface(QObject *parent = 0);
virtual ~ModelManagerInterface(); virtual ~ModelManagerInterface();
static Language::Enum guessLanguageOfFile(const QString &fileName);
static QStringList globPatternsForLanguages(const QList<Language::Enum> languages);
static ModelManagerInterface *instance(); static ModelManagerInterface *instance();
virtual WorkingCopy workingCopy() const = 0; virtual WorkingCopy workingCopy() const = 0;
@@ -184,7 +186,6 @@ public:
// Blocks until all parsing threads are done. Used for testing. // Blocks until all parsing threads are done. Used for testing.
virtual void joinAllThreads() = 0; virtual void joinAllThreads() = 0;
public slots: public slots:
virtual void resetCodeModel() = 0; virtual void resetCodeModel() = 0;
@@ -194,6 +195,8 @@ signals:
void aboutToRemoveFiles(const QStringList &files); void aboutToRemoveFiles(const QStringList &files);
void libraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info); void libraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info);
void projectInfoUpdated(const ProjectInfo &pinfo); void projectInfoUpdated(const ProjectInfo &pinfo);
protected:
virtual QHash<QString,Language::Enum> languageForSuffix() const;
}; };
} // namespace QmlJS } // namespace QmlJS

View File

@@ -816,7 +816,7 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future,
if (oldDoc) if (oldDoc)
language = oldDoc->language(); language = oldDoc->language();
else else
language = QmlJSTools::languageOfFile(fileName); language = ModelManagerInterface::guessLanguageOfFile(fileName);
Document::MutablePtr newDoc = snapshot.documentFromSource( Document::MutablePtr newDoc = snapshot.documentFromSource(
it.value().first, fileName, language); it.value().first, fileName, language);

View File

@@ -172,62 +172,28 @@ void QmlJSTools::setupProjectInfoQmlBundles(ModelManagerInterface::ProjectInfo &
static QStringList environmentImportPaths(); static QStringList environmentImportPaths();
static void mergeSuffixes(QStringList &l1, const QStringList &l2) QHash<QString,QmlJS::Language::Enum> ModelManager::languageForSuffix() const
{ {
if (!l2.isEmpty()) QHash<QString,QmlJS::Language::Enum> res = ModelManagerInterface::languageForSuffix();
l1 = l2;
}
QmlJS::Language::Enum QmlJSTools::languageOfFile(const QString &fileName)
{
QStringList jsSuffixes(QLatin1String("js"));
QStringList qmlSuffixes(QLatin1String("qml"));
QStringList qmlProjectSuffixes(QLatin1String("qmlproject"));
QStringList jsonSuffixes(QLatin1String("json"));
QStringList qbsSuffixes(QLatin1String("qbs"));
if (ICore::instance()) { if (ICore::instance()) {
MimeType jsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JS_MIMETYPE)); MimeType jsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JS_MIMETYPE));
mergeSuffixes(jsSuffixes, jsSourceTy.suffixes()); foreach (const QString &suffix, jsSourceTy.suffixes())
res[suffix] = Language::JavaScript;
MimeType qmlSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QML_MIMETYPE)); MimeType qmlSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QML_MIMETYPE));
mergeSuffixes(qmlSuffixes, qmlSourceTy.suffixes()); foreach (const QString &suffix, qmlSourceTy.suffixes())
res[suffix] = Language::Qml;
MimeType qbsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QBS_MIMETYPE)); MimeType qbsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QBS_MIMETYPE));
mergeSuffixes(qbsSuffixes, qbsSourceTy.suffixes()); foreach (const QString &suffix, qbsSourceTy.suffixes())
res[suffix] = Language::QmlQbs;
MimeType qmlProjectSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QMLPROJECT_MIMETYPE)); MimeType qmlProjectSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QMLPROJECT_MIMETYPE));
mergeSuffixes(qmlProjectSuffixes, qmlProjectSourceTy.suffixes()); foreach (const QString &suffix, qmlProjectSourceTy.suffixes())
res[suffix] = Language::QmlProject;
MimeType jsonSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JSON_MIMETYPE)); MimeType jsonSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JSON_MIMETYPE));
mergeSuffixes(jsonSuffixes, jsonSourceTy.suffixes()); foreach (const QString &suffix, jsonSourceTy.suffixes())
res[suffix] = Language::Json;
} }
return res;
const QFileInfo info(fileName);
const QString fileSuffix = info.suffix();
if (jsSuffixes.contains(fileSuffix))
return QmlJS::Language::JavaScript;
if (qbsSuffixes.contains(fileSuffix))
return QmlJS::Language::QmlQbs;
if (qmlSuffixes.contains(fileSuffix) || qmlProjectSuffixes.contains(fileSuffix))
return QmlJS::Language::Qml;
if (jsonSuffixes.contains(fileSuffix))
return QmlJS::Language::Json;
return QmlJS::Language::Unknown;
}
QStringList QmlJSTools::qmlAndJsGlobPatterns()
{
QStringList pattern;
if (ICore::instance()) {
MimeType jsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JS_MIMETYPE));
MimeType qmlSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QML_MIMETYPE));
QStringList pattern;
foreach (const MimeGlobPattern &glob, jsSourceTy.globPatterns())
pattern << glob.pattern();
foreach (const MimeGlobPattern &glob, qmlSourceTy.globPatterns())
pattern << glob.pattern();
} else {
pattern << QLatin1String("*.qml") << QLatin1String("*.js");
}
return pattern;
} }
ModelManager::ModelManager(QObject *parent): ModelManager::ModelManager(QObject *parent):
@@ -652,9 +618,9 @@ void ModelManager::updateLibraryInfo(const QString &path, const LibraryInfo &inf
emit libraryInfoUpdated(path, info); emit libraryInfoUpdated(path, info);
} }
static QStringList qmlFilesInDirectory(const QString &path) static QStringList filesInDirectoryForLanguages(const QString &path, QList<Language::Enum> languages)
{ {
const QStringList pattern = qmlAndJsGlobPatterns(); const QStringList pattern = ModelManagerInterface::globPatternsForLanguages(languages);
QStringList files; QStringList files;
const QDir dir(path); const QDir dir(path);
@@ -671,7 +637,8 @@ static void findNewImplicitImports(const Document::Ptr &doc, const Snapshot &sna
// it's important we also do this for JS files, otherwise the isEmpty check will fail // it's important we also do this for JS files, otherwise the isEmpty check will fail
if (snapshot.documentsInDirectory(doc->path()).isEmpty()) { if (snapshot.documentsInDirectory(doc->path()).isEmpty()) {
if (! scannedPaths->contains(doc->path())) { if (! scannedPaths->contains(doc->path())) {
*importedFiles += qmlFilesInDirectory(doc->path()); *importedFiles += filesInDirectoryForLanguages(doc->path(),
Document::companionLanguages(doc->language()));
scannedPaths->insert(doc->path()); scannedPaths->insert(doc->path());
} }
} }
@@ -689,7 +656,8 @@ static void findNewFileImports(const Document::Ptr &doc, const Snapshot &snapsho
} else if (import.type() == ImportType::Directory) { } else if (import.type() == ImportType::Directory) {
if (snapshot.documentsInDirectory(importName).isEmpty()) { if (snapshot.documentsInDirectory(importName).isEmpty()) {
if (! scannedPaths->contains(importName)) { if (! scannedPaths->contains(importName)) {
*importedFiles += qmlFilesInDirectory(importName); *importedFiles += filesInDirectoryForLanguages(importName,
Document::companionLanguages(doc->language()));
scannedPaths->insert(importName); scannedPaths->insert(importName);
} }
} }
@@ -703,7 +671,7 @@ static void findNewFileImports(const Document::Ptr &doc, const Snapshot &snapsho
QMapIterator<QString,QStringList> dirContents(ModelManagerInterface::instance()->filesInQrcPath(importName)); QMapIterator<QString,QStringList> dirContents(ModelManagerInterface::instance()->filesInQrcPath(importName));
while (dirContents.hasNext()) { while (dirContents.hasNext()) {
dirContents.next(); dirContents.next();
if (Document::isQmlLikeOrJsLanguage(Document::guessLanguageFromSuffix(dirContents.key()))) { if (Document::isQmlLikeOrJsLanguage(ModelManagerInterface::guessLanguageOfFile(dirContents.key()))) {
foreach (const QString &filePath, dirContents.value()) { foreach (const QString &filePath, dirContents.value()) {
if (! snapshot.document(filePath)) if (! snapshot.document(filePath))
*importedFiles += filePath; *importedFiles += filePath;
@@ -763,7 +731,8 @@ static bool findNewQmlLibraryInPath(const QString &path,
const QFileInfo componentFileInfo(dir.filePath(component.fileName)); const QFileInfo componentFileInfo(dir.filePath(component.fileName));
const QString path = QDir::cleanPath(componentFileInfo.absolutePath()); const QString path = QDir::cleanPath(componentFileInfo.absolutePath());
if (! scannedPaths->contains(path)) { if (! scannedPaths->contains(path)) {
*importedFiles += qmlFilesInDirectory(path); *importedFiles += filesInDirectoryForLanguages(path,
Document::companionLanguages(Language::Unknown));
scannedPaths->insert(path); scannedPaths->insert(path);
} }
} }
@@ -845,7 +814,7 @@ void ModelManager::parseLoop(QSet<QString> &scannedPaths,
const QString fileName = files.at(i); const QString fileName = files.at(i);
Language::Enum language = languageOfFile(fileName); Language::Enum language = guessLanguageOfFile(fileName);
if (language == Language::Unknown) { if (language == Language::Unknown) {
if (fileName.endsWith(QLatin1String(".qrc"))) if (fileName.endsWith(QLatin1String(".qrc")))
modelManager->updateQrcFile(fileName); modelManager->updateQrcFile(fileName);
@@ -982,7 +951,8 @@ void ModelManager::importScan(QFutureInterface<void> &future,
if (!findNewQmlLibraryInPath(toScan.path, snapshot, modelManager, &importedFiles, if (!findNewQmlLibraryInPath(toScan.path, snapshot, modelManager, &importedFiles,
&scannedPaths, &newLibraries, true) &scannedPaths, &newLibraries, true)
&& !libOnly && snapshot.documentsInDirectory(toScan.path).isEmpty()) && !libOnly && snapshot.documentsInDirectory(toScan.path).isEmpty())
importedFiles += qmlFilesInDirectory(toScan.path); importedFiles += filesInDirectoryForLanguages(toScan.path,
Document::companionLanguages(language));
workDone += 1; workDone += 1;
future.setProgressValue(progressRange * workDone / totalWork); future.setProgressValue(progressRange * workDone / totalWork);
if (!importedFiles.isEmpty()) { if (!importedFiles.isEmpty()) {

View File

@@ -60,9 +60,6 @@ class QrcParser;
namespace QmlJSTools { namespace QmlJSTools {
QMLJSTOOLS_EXPORT QmlJS::Language::Enum languageOfFile(const QString &fileName);
QMLJSTOOLS_EXPORT QStringList qmlAndJsGlobPatterns();
namespace Internal { namespace Internal {
class PluginDumper; class PluginDumper;
@@ -197,6 +194,8 @@ private:
PluginDumper *m_pluginDumper; PluginDumper *m_pluginDumper;
QFutureSynchronizer<void> m_synchronizer; QFutureSynchronizer<void> m_synchronizer;
QHash<QString, QmlJS::Language::Enum> languageForSuffix() const QTC_OVERRIDE;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -124,7 +124,7 @@ QmlJSRefactoringFile::QmlJSRefactoringFile(const QString &fileName, const QShare
: RefactoringFile(fileName, data) : RefactoringFile(fileName, data)
{ {
// the RefactoringFile is invalid if its not for a file with qml or js code // the RefactoringFile is invalid if its not for a file with qml or js code
if (languageOfFile(fileName) == Language::Unknown) if (ModelManagerInterface::guessLanguageOfFile(fileName) == Language::Unknown)
m_fileName.clear(); m_fileName.clear();
} }
@@ -142,7 +142,8 @@ Document::Ptr QmlJSRefactoringFile::qmljsDocument() const
const QString name = fileName(); const QString name = fileName();
const Snapshot &snapshot = data()->m_snapshot; const Snapshot &snapshot = data()->m_snapshot;
Document::MutablePtr newDoc = snapshot.documentFromSource(source, name, languageOfFile(name)); Document::MutablePtr newDoc = snapshot.documentFromSource(source, name,
ModelManagerInterface::guessLanguageOfFile(name));
newDoc->parse(); newDoc->parse();
m_qmljsDocument = newDoc; m_qmljsDocument = newDoc;
} }

View File

@@ -129,7 +129,7 @@ void QmlProfilerDetailsRewriter::requestDetailsForLocation(int requestId,
QFileInfo fileInfo(localFile); QFileInfo fileInfo(localFile);
if (!fileInfo.exists() || !fileInfo.isReadable()) if (!fileInfo.exists() || !fileInfo.isReadable())
return; return;
if (!QmlJS::Document::isQmlLikeLanguage(QmlJSTools::languageOfFile(localFile))) if (!QmlJS::Document::isQmlLikeLanguage(QmlJS::ModelManagerInterface::guessLanguageOfFile(localFile)))
return; return;
PendingEvent ev = {location, localFile, requestId}; PendingEvent ev = {location, localFile, requestId};

View File

@@ -36,6 +36,7 @@
#include <QFileInfo> #include <QFileInfo>
#include <qmljs/qmljsdocument.h> #include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/qmljsreformatter.h> #include <qmljs/qmljsreformatter.h>
#include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsast_p.h>
@@ -78,7 +79,7 @@ void tst_Reformatter::test()
{ {
QFETCH(QString, path); QFETCH(QString, path);
Document::MutablePtr doc = Document::create(path, Document::guessLanguageFromSuffix(path)); Document::MutablePtr doc = Document::create(path, ModelManagerInterface::guessLanguageOfFile(path));
QFile file(doc->fileName()); QFile file(doc->fileName());
file.open(QFile::ReadOnly | QFile::Text); file.open(QFile::ReadOnly | QFile::Text);
QString source = QString::fromUtf8(file.readAll()); QString source = QString::fromUtf8(file.readAll());

View File

@@ -30,6 +30,7 @@
#include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljsastvisitor_p.h> #include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmljsdocument.h> #include <qmljs/qmljsdocument.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <QFile> #include <QFile>
#include <QList> #include <QList>
@@ -331,7 +332,7 @@ int main(int argc, char *argv[])
const QByteArray source = file.readAll(); const QByteArray source = file.readAll();
file.close(); file.close();
Document::MutablePtr doc = Document::create(fileName, Document::guessLanguageFromSuffix(fileName)); Document::MutablePtr doc = Document::create(fileName, ModelManagerInterface::guessLanguageOfFile(fileName));
doc->setSource(QString::fromUtf8(source)); doc->setSource(QString::fromUtf8(source));
doc->parse(); doc->parse();