qmljsmodelmanager.cpp 12.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
** 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
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
25
26
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
27
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
30

Roberto Raggi's avatar
Roberto Raggi committed
31
#include "qmljsmodelmanager.h"
32
#include "qmljstoolsconstants.h"
33
#include "qmljssemanticinfo.h"
34
#include "qmljsbundleprovider.h"
35
36

#include <coreplugin/icore.h>
37
#include <coreplugin/messagemanager.h>
38
#include <coreplugin/progressmanager/progressmanager.h>
39
#include <cpptools/cppmodelmanager.h>
40
#include <extensionsystem/pluginmanager.h>
41
#include <projectexplorer/buildconfiguration.h>
42
#include <projectexplorer/project.h>
43
44
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projecttree.h>
45
#include <projectexplorer/session.h>
46
#include <projectexplorer/target.h>
47
48
49
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljsfindexportedcpptypes.h>
#include <qmljs/qmljsplugindumper.h>
50
#include <qtsupport/qmldumptool.h>
51
#include <qtsupport/qtkitinformation.h>
52
#include <qtsupport/qtsupportconstants.h>
53
#include <texteditor/textdocument.h>
54
#include <utils/hostosinfo.h>
55

56
57
58
#include <QDir>
#include <QFile>
#include <QFileInfo>
59
#include <utils/runextensions.h>
60
#include <QTextDocument>
61
62
#include <QTextStream>
#include <QTimer>
63
#include <QRegExp>
64
65
#include <QLibraryInfo>
#include <qglobal.h>
66

67
using namespace Core;
68
using namespace QmlJS;
69
70
using namespace QmlJSTools;
using namespace QmlJSTools::Internal;
71

72

73
74
ModelManagerInterface::ProjectInfo QmlJSTools::Internal::ModelManager::defaultProjectInfoForProject(
        ProjectExplorer::Project *project) const
75
76
77
78
{
    ModelManagerInterface::ProjectInfo projectInfo(project);
    ProjectExplorer::Target *activeTarget = 0;
    if (project) {
79
        QList<MimeGlobPattern> globs;
hjk's avatar
hjk committed
80
        foreach (const MimeType &mimeType, MimeDatabase::mimeTypes())
81
82
83
            if (mimeType.type() == QLatin1String(Constants::QML_MIMETYPE)
                    || mimeType.subClassesOf().contains(QLatin1String(Constants::QML_MIMETYPE)))
                globs << mimeType.globPatterns();
84
        if (globs.isEmpty()) {
85
86
87
88
            globs.append(MimeGlobPattern(QLatin1String("*.qbs")));
            globs.append(MimeGlobPattern(QLatin1String("*.qml")));
            globs.append(MimeGlobPattern(QLatin1String("*.qmltypes")));
            globs.append(MimeGlobPattern(QLatin1String("*.qmlproject")));
89
        }
90
91
        foreach (const QString &filePath,
                 project->files(ProjectExplorer::Project::ExcludeGeneratedFiles))
92
            foreach (const MimeGlobPattern &glob, globs)
93
                if (glob.matches(filePath))
94
95
96
97
                    projectInfo.sourceFiles << filePath;
        activeTarget = project->activeTarget();
    }
    ProjectExplorer::Kit *activeKit = activeTarget ? activeTarget->kit() :
98
                                           ProjectExplorer::KitManager::defaultKit();
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
    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));
117
118
        projectInfo.qtQmlPath = QFileInfo(qtVersion->qmakeProperty("QT_INSTALL_QML")).canonicalFilePath();
        projectInfo.qtImportsPath = QFileInfo(qtVersion->qmakeProperty("QT_INSTALL_IMPORTS")).canonicalFilePath();
119
        projectInfo.qtVersionString = qtVersion->qtVersionString();
120
    } else {
121
122
        projectInfo.qtQmlPath = QFileInfo(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)).canonicalFilePath();
        projectInfo.qtImportsPath = QFileInfo(QLibraryInfo::location(QLibraryInfo::ImportsPath)).canonicalFilePath();
123
        projectInfo.qtVersionString = QLatin1String(qVersion());
124
125
126
127
128
129
130
131
132
    }

    if (projectInfo.tryQmlDump) {
        ProjectExplorer::ToolChain *toolChain =
                ProjectExplorer::ToolChainKitInformation::toolChain(activeKit);
        QtSupport::QmlDumpTool::pathAndEnvironment(project, qtVersion,
                                                   toolChain,
                                                   preferDebugDump, &projectInfo.qmlDumpPath,
                                                   &projectInfo.qmlDumpEnvironment);
133
        projectInfo.qmlDumpHasRelocatableFlag = qtVersion->hasQmlDumpWithRelocatableFlag();
134
135
136
    } else {
        projectInfo.qmlDumpPath.clear();
        projectInfo.qmlDumpEnvironment.clear();
137
        projectInfo.qmlDumpHasRelocatableFlag = true;
138
139
140
141
142
143
144
145
    }
    setupProjectInfoQmlBundles(projectInfo);
    return projectInfo;
}

void QmlJSTools::setupProjectInfoQmlBundles(ModelManagerInterface::ProjectInfo &projectInfo)
{
    ProjectExplorer::Target *activeTarget = 0;
Orgad Shaneh's avatar
Orgad Shaneh committed
146
    if (projectInfo.project)
147
148
        activeTarget = projectInfo.project->activeTarget();
    ProjectExplorer::Kit *activeKit = activeTarget
149
            ? activeTarget->kit() : ProjectExplorer::KitManager::defaultKit();
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
    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);
        }
    }
}

177
QHash<QString,QmlJS::Dialect> ModelManager::languageForSuffix() const
178
{
179
    QHash<QString,QmlJS::Dialect> res = ModelManagerInterface::languageForSuffix();
180

181
    if (ICore::instance()) {
hjk's avatar
hjk committed
182
        MimeType jsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JS_MIMETYPE));
183
        foreach (const QString &suffix, jsSourceTy.suffixes())
184
            res[suffix] = Dialect::JavaScript;
hjk's avatar
hjk committed
185
        MimeType qmlSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QML_MIMETYPE));
186
        foreach (const QString &suffix, qmlSourceTy.suffixes())
187
            res[suffix] = Dialect::Qml;
hjk's avatar
hjk committed
188
        MimeType qbsSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QBS_MIMETYPE));
189
        foreach (const QString &suffix, qbsSourceTy.suffixes())
190
            res[suffix] = Dialect::QmlQbs;
hjk's avatar
hjk committed
191
        MimeType qmlProjectSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QMLPROJECT_MIMETYPE));
192
        foreach (const QString &suffix, qmlProjectSourceTy.suffixes())
193
            res[suffix] = Dialect::QmlProject;
194
195
196
        MimeType qmlUiSourceTy = MimeDatabase::findByType(QLatin1String(Constants::QMLUI_MIMETYPE));
        foreach (const QString &suffix, qmlUiSourceTy.suffixes())
            res[suffix] = Dialect::QmlQtQuick2Ui;
hjk's avatar
hjk committed
197
        MimeType jsonSourceTy = MimeDatabase::findByType(QLatin1String(Constants::JSON_MIMETYPE));
198
        foreach (const QString &suffix, jsonSourceTy.suffixes())
199
            res[suffix] = Dialect::Json;
200
    }
201
    return res;
202
203
}

Roberto Raggi's avatar
Roberto Raggi committed
204
ModelManager::ModelManager(QObject *parent):
205
        ModelManagerInterface(parent)
206
{
207
    qRegisterMetaType<QmlJSTools::SemanticInfo>("QmlJSTools::SemanticInfo");
208
    loadDefaultQmlTypeDescriptions();
209
210
}

211
212
213
214
ModelManager::~ModelManager()
{
}

215
216
void ModelManager::delayedInitialization()
{
217
218
    CppTools::CppModelManager *cppModelManager =
            CppTools::CppModelManager::instance();
219
    if (cppModelManager) {
220
221
        // It's important to have a direct connection here so we can prevent
        // the source and AST of the cpp document being cleaned away.
222
        connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
223
                this, SLOT(maybeQueueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)), Qt::DirectConnection);
224
    }
225

hjk's avatar
hjk committed
226
    connect(ProjectExplorer::SessionManager::instance(), SIGNAL(projectRemoved(ProjectExplorer::Project*)),
227
            this, SLOT(removeProjectInfo(ProjectExplorer::Project*)));
228
229
    connect(ProjectExplorer::ProjectTree::instance(), &ProjectExplorer::ProjectTree::currentProjectChanged,
            this, &ModelManager::updateDefaultProjectInfo);
Fawzi Mohamed's avatar
Fawzi Mohamed committed
230
231

    QmlJS::ViewerContext qbsVContext;
232
    qbsVContext.language = Dialect::QmlQbs;
Fawzi Mohamed's avatar
Fawzi Mohamed committed
233
234
    qbsVContext.maybeAddPath(ICore::resourcePath() + QLatin1String("/qbs"));
    setDefaultVContext(qbsVContext);
235
236
}

237
void ModelManager::loadDefaultQmlTypeDescriptions()
238
{
239
    if (ICore::instance()) {
240
241
        loadQmlTypeDescriptionsInternal(ICore::resourcePath());
        loadQmlTypeDescriptionsInternal(ICore::userResourcePath());
242
    }
243
244
}

245
void ModelManager::writeMessageInternal(const QString &msg) const
246
{
247
    MessageManager::write(msg, MessageManager::Flash);
248
249
}

250
ModelManagerInterface::WorkingCopy ModelManager::workingCopyInternal() const
251
252
{
    WorkingCopy workingCopy;
hjk's avatar
hjk committed
253
    foreach (IDocument *document, DocumentModel::openedDocuments()) {
254
        const QString key = document->filePath().toString();
255
        if (TextEditor::TextDocument *textDocument = qobject_cast<TextEditor::TextDocument *>(document)) {
256
            // TODO the language should be a property on the document, not the editor
hjk's avatar
hjk committed
257
            if (DocumentModel::editorsForDocument(document).first()->context().contains(ProjectExplorer::Constants::LANG_QMLJS))
258
                workingCopy.insert(key, textDocument->plainText(), textDocument->document()->revision());
259
260
261
262
263
264
        }
    }

    return workingCopy;
}

265
void ModelManager::updateDefaultProjectInfo()
Fawzi Mohamed's avatar
Fawzi Mohamed committed
266
{
267
    // needs to be performed in the ui thread
268
    ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectTree::currentProject();
269
270
    ProjectInfo newDefaultProjectInfo = projectInfo(currentProject,
                                                    defaultProjectInfoForProject(currentProject));
271
    setDefaultProject(projectInfo(currentProject,newDefaultProjectInfo), currentProject);
272
273
}

274

275
// Check whether fileMimeType is the same or extends knownMimeType
276
bool ModelManager::matchesMimeType(const MimeType &fileMimeType, const MimeType &knownMimeType)
277
278
279
{
    const QStringList knownTypeNames = QStringList(knownMimeType.type()) + knownMimeType.aliases();

Friedemann Kleint's avatar
Friedemann Kleint committed
280
    foreach (const QString &knownTypeName, knownTypeNames)
281
282
283
284
        if (fileMimeType.matchesType(knownTypeName))
            return true;

    // recursion to parent types of fileMimeType
hjk's avatar
hjk committed
285
286
    foreach (const QString &parentMimeType, fileMimeType.subClassesOf())
        if (matchesMimeType(MimeDatabase::findByType(parentMimeType), knownMimeType))
287
288
289
290
            return true;

    return false;
}
291

292
void ModelManager::addTaskInternal(QFuture<void> result, const QString &msg, const char *taskId) const
293
{
294
    ProgressManager::addTask(result, msg, taskId);
295
}