2010-01-18 13:13:34 +01:00
|
|
|
/**************************************************************************
|
|
|
|
**
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
**
|
2011-01-11 16:28:15 +01:00
|
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Contact: Nokia Corporation (info@qt.nokia.com)
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** 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.
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2011-04-13 08:42:33 +02:00
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Other Usage
|
|
|
|
**
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** If you have questions regarding the use of this file, please contact
|
2011-05-06 15:05:37 +02:00
|
|
|
** Nokia at info@qt.nokia.com.
|
2010-01-18 13:13:34 +01:00
|
|
|
**
|
|
|
|
**************************************************************************/
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
#include "qmljsdocument.h"
|
2010-02-02 15:55:17 +01:00
|
|
|
#include "qmljsbind.h"
|
2010-01-18 13:13:34 +01:00
|
|
|
#include <qmljs/parser/qmljsast_p.h>
|
|
|
|
#include <qmljs/parser/qmljslexer_p.h>
|
|
|
|
#include <qmljs/parser/qmljsparser_p.h>
|
|
|
|
#include <qmljs/parser/qmljsastfwd_p.h>
|
2010-01-26 17:23:18 +01:00
|
|
|
#include <QtCore/QDir>
|
2010-01-18 13:13:34 +01:00
|
|
|
|
|
|
|
using namespace QmlJS;
|
|
|
|
using namespace QmlJS::AST;
|
|
|
|
|
2010-09-15 15:25:59 +02:00
|
|
|
/*!
|
|
|
|
\class QmlJS::Document
|
|
|
|
\brief A Qml or JavaScript document.
|
|
|
|
\sa QmlJS::Snapshot
|
|
|
|
|
2011-11-01 14:01:07 +01:00
|
|
|
Documents are usually created by the \l{QmlJS::ModelManagerInterface}
|
2010-09-15 15:25:59 +02:00
|
|
|
and stored in a \l{QmlJS::Snapshot}. They allow access to data such as
|
|
|
|
the file path, source code, abstract syntax tree and the \l{QmlJS::Bind}
|
|
|
|
instance for the document.
|
|
|
|
|
|
|
|
To make sure unused and outdated documents are removed correctly, Document
|
|
|
|
instances are usually accessed through a shared pointer, see \l{Document::Ptr}.
|
2011-11-01 14:01:07 +01:00
|
|
|
|
|
|
|
Documents in a Snapshot are immutable: They, or anything reachable through them,
|
|
|
|
must not be changed. This allows Documents to be shared freely among threads
|
|
|
|
without extra synchronization.
|
2010-09-15 15:25:59 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class QmlJS::LibraryInfo
|
|
|
|
\brief A Qml library.
|
|
|
|
\sa QmlJS::Snapshot
|
|
|
|
|
2011-11-01 14:01:07 +01:00
|
|
|
A LibraryInfo is created when the \l{QmlJS::ModelManagerInterface} finds
|
2010-09-15 15:25:59 +02:00
|
|
|
a Qml library and parses the qmldir file. The instance holds information about
|
|
|
|
which Components the library provides and which plugins to load.
|
|
|
|
|
|
|
|
The ModelManager will try to extract detailed information about the types
|
|
|
|
defined in the plugins this library loads. Once it is done, the data will
|
|
|
|
be available through the metaObjects() function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class QmlJS::Snapshot
|
|
|
|
\brief A set of Document::Ptr and LibraryInfo instances.
|
|
|
|
\sa QmlJS::Document QmlJS::LibraryInfo
|
|
|
|
|
|
|
|
A Snapshot holds and offers access to a set of Document and LibraryInfo instances.
|
|
|
|
|
|
|
|
Usually Snapshots are copies of the snapshot maintained and updated by the
|
2011-11-01 14:01:07 +01:00
|
|
|
\l{QmlJS::ModelManagerInterface} that updates its instance as parsing
|
2010-09-15 15:25:59 +02:00
|
|
|
threads finish and new information becomes available.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Document(const QString &fileName, Language language)
|
2010-01-18 13:13:34 +01:00
|
|
|
: _engine(0)
|
2010-01-22 10:26:25 +01:00
|
|
|
, _ast(0)
|
2010-02-03 15:59:15 +01:00
|
|
|
, _bind(0)
|
2011-09-09 10:55:11 +02:00
|
|
|
, _fileName(QDir::cleanPath(fileName))
|
2010-07-07 17:52:02 +02:00
|
|
|
, _editorRevision(0)
|
2011-09-09 10:55:11 +02:00
|
|
|
, _language(language)
|
2010-01-18 13:13:34 +01:00
|
|
|
, _parsedCorrectly(false)
|
|
|
|
{
|
2010-01-26 17:23:18 +01:00
|
|
|
QFileInfo fileInfo(fileName);
|
2010-04-01 14:42:39 +02:00
|
|
|
_path = QDir::cleanPath(fileInfo.absolutePath());
|
2010-01-27 09:24:49 +01:00
|
|
|
|
2011-09-09 10:55:11 +02:00
|
|
|
if (language == QmlLanguage) {
|
2010-01-27 09:24:49 +01:00
|
|
|
_componentName = fileInfo.baseName();
|
|
|
|
|
|
|
|
if (! _componentName.isEmpty()) {
|
|
|
|
// ### TODO: check the component name.
|
|
|
|
|
|
|
|
if (! _componentName.at(0).isUpper())
|
|
|
|
_componentName.clear();
|
|
|
|
}
|
2010-01-26 17:23:18 +01:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
Document::~Document()
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2010-02-03 15:59:15 +01:00
|
|
|
if (_bind)
|
|
|
|
delete _bind;
|
|
|
|
|
2010-01-18 13:13:34 +01:00
|
|
|
if (_engine)
|
|
|
|
delete _engine;
|
|
|
|
}
|
|
|
|
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Ptr Document::create(const QString &fileName, Language language)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Ptr doc(new Document(fileName, language));
|
2011-05-04 11:12:45 +02:00
|
|
|
doc->_ptr = doc;
|
2010-01-18 13:13:34 +01:00
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
2011-10-20 11:03:03 +02:00
|
|
|
Document::Language Document::guessLanguageFromSuffix(const QString &fileName)
|
|
|
|
{
|
|
|
|
if (fileName.endsWith(".qml", Qt::CaseInsensitive))
|
|
|
|
return QmlLanguage;
|
|
|
|
if (fileName.endsWith(".js", Qt::CaseInsensitive))
|
|
|
|
return JavaScriptLanguage;
|
|
|
|
if (fileName.endsWith(".json", Qt::CaseInsensitive))
|
|
|
|
return JsonLanguage;
|
|
|
|
return UnknownLanguage;
|
|
|
|
}
|
|
|
|
|
2011-05-04 11:12:45 +02:00
|
|
|
Document::Ptr Document::ptr() const
|
|
|
|
{
|
|
|
|
return _ptr.toStrongRef();
|
|
|
|
}
|
|
|
|
|
2010-03-29 12:56:25 +02:00
|
|
|
bool Document::isQmlDocument() const
|
|
|
|
{
|
2011-09-09 10:55:11 +02:00
|
|
|
return _language == QmlLanguage;
|
2010-03-29 12:56:25 +02:00
|
|
|
}
|
|
|
|
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Language Document::language() const
|
|
|
|
{
|
|
|
|
return _language;
|
2010-03-29 12:56:25 +02:00
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
AST::UiProgram *Document::qmlProgram() const
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2010-01-22 10:26:25 +01:00
|
|
|
return cast<UiProgram *>(_ast);
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
AST::Program *Document::jsProgram() const
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2010-01-22 10:26:25 +01:00
|
|
|
return cast<Program *>(_ast);
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
2010-01-26 14:53:11 +01:00
|
|
|
AST::ExpressionNode *Document::expression() const
|
|
|
|
{
|
|
|
|
if (_ast)
|
|
|
|
return _ast->expressionCast();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-19 10:16:57 +01:00
|
|
|
AST::Node *Document::ast() const
|
|
|
|
{
|
2010-01-22 10:26:25 +01:00
|
|
|
return _ast;
|
2010-01-19 10:16:57 +01:00
|
|
|
}
|
|
|
|
|
2010-11-23 12:57:48 +01:00
|
|
|
const QmlJS::Engine *Document::engine() const
|
|
|
|
{
|
|
|
|
return _engine;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
QList<DiagnosticMessage> Document::diagnosticMessages() const
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
|
|
|
return _diagnosticMessages;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
QString Document::source() const
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
|
|
|
return _source;
|
|
|
|
}
|
|
|
|
|
2010-01-18 16:15:23 +01:00
|
|
|
void Document::setSource(const QString &source)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
|
|
|
_source = source;
|
|
|
|
}
|
|
|
|
|
2010-07-07 17:52:02 +02:00
|
|
|
int Document::editorRevision() const
|
2010-01-25 14:18:53 +01:00
|
|
|
{
|
2010-07-07 17:52:02 +02:00
|
|
|
return _editorRevision;
|
2010-01-25 14:18:53 +01:00
|
|
|
}
|
|
|
|
|
2010-07-07 17:52:02 +02:00
|
|
|
void Document::setEditorRevision(int revision)
|
2010-01-25 14:18:53 +01:00
|
|
|
{
|
2010-07-07 17:52:02 +02:00
|
|
|
_editorRevision = revision;
|
2010-01-25 14:18:53 +01:00
|
|
|
}
|
|
|
|
|
2010-02-11 10:19:41 +01:00
|
|
|
QString Document::fileName() const
|
|
|
|
{
|
|
|
|
return _fileName;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Document::path() const
|
|
|
|
{
|
|
|
|
return _path;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Document::componentName() const
|
|
|
|
{
|
|
|
|
return _componentName;
|
|
|
|
}
|
|
|
|
|
2011-09-27 15:12:22 +02:00
|
|
|
namespace {
|
|
|
|
class CollectDirectives : public Directives
|
|
|
|
{
|
|
|
|
QString documentPath;
|
|
|
|
public:
|
|
|
|
CollectDirectives(const QString &documentPath)
|
|
|
|
: documentPath(documentPath)
|
|
|
|
, isLibrary(false)
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
virtual void pragmaLibrary() { isLibrary = true; }
|
|
|
|
virtual void importFile(const QString &jsfile, const QString &module)
|
|
|
|
{
|
|
|
|
imports += ImportInfo::pathImport(
|
|
|
|
documentPath, jsfile, LanguageUtils::ComponentVersion(), module);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void importModule(const QString &uri, const QString &version, const QString &module)
|
|
|
|
{
|
|
|
|
imports += ImportInfo::moduleImport(uri, LanguageUtils::ComponentVersion(version), module);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isLibrary;
|
|
|
|
QList<ImportInfo> imports;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2010-02-08 21:37:59 +01:00
|
|
|
bool Document::parse_helper(int startToken)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
|
|
|
Q_ASSERT(! _engine);
|
2010-01-22 10:26:25 +01:00
|
|
|
Q_ASSERT(! _ast);
|
2010-02-03 15:59:15 +01:00
|
|
|
Q_ASSERT(! _bind);
|
2010-01-18 13:13:34 +01:00
|
|
|
|
|
|
|
_engine = new Engine();
|
|
|
|
|
|
|
|
Lexer lexer(_engine);
|
|
|
|
Parser parser(_engine);
|
|
|
|
|
2010-06-15 14:50:16 +02:00
|
|
|
QString source = _source;
|
2011-09-13 09:57:24 +02:00
|
|
|
lexer.setCode(source, /*line = */ 1, /*qmlMode = */_language == QmlLanguage);
|
2010-01-18 13:13:34 +01:00
|
|
|
|
2011-09-27 15:12:22 +02:00
|
|
|
CollectDirectives collectDirectives(path());
|
|
|
|
_engine->setDirectives(&collectDirectives);
|
2011-09-19 14:16:25 +02:00
|
|
|
|
2010-02-08 21:37:59 +01:00
|
|
|
switch (startToken) {
|
|
|
|
case QmlJSGrammar::T_FEED_UI_PROGRAM:
|
|
|
|
_parsedCorrectly = parser.parse();
|
|
|
|
break;
|
|
|
|
case QmlJSGrammar::T_FEED_JS_PROGRAM:
|
|
|
|
_parsedCorrectly = parser.parseProgram();
|
|
|
|
break;
|
|
|
|
case QmlJSGrammar::T_FEED_JS_EXPRESSION:
|
|
|
|
_parsedCorrectly = parser.parseExpression();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Q_ASSERT(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
_ast = parser.rootNode();
|
2010-01-18 13:13:34 +01:00
|
|
|
_diagnosticMessages = parser.diagnosticMessages();
|
|
|
|
|
2011-09-27 15:12:22 +02:00
|
|
|
_bind = new Bind(this, &_diagnosticMessages, collectDirectives.isLibrary, collectDirectives.imports);
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-01-18 13:13:34 +01:00
|
|
|
return _parsedCorrectly;
|
|
|
|
}
|
|
|
|
|
2010-03-29 12:56:25 +02:00
|
|
|
bool Document::parse()
|
|
|
|
{
|
|
|
|
if (isQmlDocument())
|
|
|
|
return parseQml();
|
|
|
|
|
|
|
|
return parseJavaScript();
|
|
|
|
}
|
|
|
|
|
2010-02-08 21:37:59 +01:00
|
|
|
bool Document::parseQml()
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2010-02-08 21:37:59 +01:00
|
|
|
return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
|
|
|
|
}
|
2010-02-02 15:55:17 +01:00
|
|
|
|
2010-02-08 21:37:59 +01:00
|
|
|
bool Document::parseJavaScript()
|
|
|
|
{
|
|
|
|
return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
|
2010-01-22 10:26:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Document::parseExpression()
|
|
|
|
{
|
2010-02-08 21:37:59 +01:00
|
|
|
return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
2010-02-03 15:59:15 +01:00
|
|
|
Bind *Document::bind() const
|
2010-02-02 15:55:17 +01:00
|
|
|
{
|
|
|
|
return _bind;
|
|
|
|
}
|
|
|
|
|
2011-05-27 14:51:30 +02:00
|
|
|
LibraryInfo::LibraryInfo(Status status)
|
|
|
|
: _status(status)
|
2011-05-12 15:29:00 +02:00
|
|
|
, _dumpStatus(NoTypeInfo)
|
2010-03-18 15:43:33 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
LibraryInfo::LibraryInfo(const QmlDirParser &parser)
|
2011-05-27 14:51:30 +02:00
|
|
|
: _status(Found)
|
2010-03-18 15:43:33 +01:00
|
|
|
, _components(parser.components())
|
|
|
|
, _plugins(parser.plugins())
|
2011-09-02 13:25:08 +02:00
|
|
|
, _typeinfos(parser.typeInfos())
|
2011-05-12 15:29:00 +02:00
|
|
|
, _dumpStatus(NoTypeInfo)
|
2010-03-18 15:43:33 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
LibraryInfo::~LibraryInfo()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-01-18 13:13:34 +01:00
|
|
|
Snapshot::Snapshot()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Snapshot::~Snapshot()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-08-16 14:11:30 +02:00
|
|
|
void Snapshot::insert(const Document::Ptr &document, bool allowInvalid)
|
2010-01-18 13:13:34 +01:00
|
|
|
{
|
2011-08-16 14:11:30 +02:00
|
|
|
if (document && (allowInvalid || document->qmlProgram() || document->jsProgram())) {
|
2010-05-17 12:01:54 +02:00
|
|
|
const QString fileName = document->fileName();
|
|
|
|
const QString path = document->path();
|
|
|
|
|
2010-05-18 13:40:35 +02:00
|
|
|
remove(fileName);
|
2010-08-31 10:23:48 +02:00
|
|
|
_documentsByPath[path].append(document);
|
2010-05-17 12:01:54 +02:00
|
|
|
_documents.insert(fileName, document);
|
2010-04-01 11:27:49 +02:00
|
|
|
}
|
2010-01-18 13:13:34 +01:00
|
|
|
}
|
|
|
|
|
2010-03-18 15:43:33 +01:00
|
|
|
void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
|
|
|
|
{
|
2010-04-01 14:42:39 +02:00
|
|
|
_libraries.insert(QDir::cleanPath(path), info);
|
2010-03-18 15:43:33 +01:00
|
|
|
}
|
|
|
|
|
2010-05-18 13:40:35 +02:00
|
|
|
void Snapshot::remove(const QString &fileName)
|
|
|
|
{
|
|
|
|
Document::Ptr doc = _documents.value(fileName);
|
|
|
|
if (!doc.isNull()) {
|
2010-08-31 10:23:48 +02:00
|
|
|
const QString &path = doc->path();
|
|
|
|
|
|
|
|
QList<Document::Ptr> docs = _documentsByPath.value(path);
|
|
|
|
docs.removeAll(doc);
|
|
|
|
_documentsByPath[path] = docs;
|
|
|
|
|
2010-05-18 13:40:35 +02:00
|
|
|
_documents.remove(fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-16 10:36:09 +01:00
|
|
|
Document::Ptr Snapshot::documentFromSource(const QString &code,
|
2011-09-09 10:55:11 +02:00
|
|
|
const QString &fileName,
|
|
|
|
Document::Language language) const
|
2010-02-16 10:36:09 +01:00
|
|
|
{
|
2011-09-09 10:55:11 +02:00
|
|
|
Document::Ptr newDoc = Document::create(fileName, language);
|
2010-02-16 10:36:09 +01:00
|
|
|
|
|
|
|
if (Document::Ptr thisDocument = document(fileName)) {
|
2010-07-07 17:52:02 +02:00
|
|
|
newDoc->_editorRevision = thisDocument->_editorRevision;
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
newDoc->setSource(code);
|
|
|
|
return newDoc;
|
|
|
|
}
|
|
|
|
|
2010-04-01 14:42:39 +02:00
|
|
|
Document::Ptr Snapshot::document(const QString &fileName) const
|
|
|
|
{
|
|
|
|
return _documents.value(QDir::cleanPath(fileName));
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
|
|
|
|
{
|
2010-08-31 10:23:48 +02:00
|
|
|
return _documentsByPath.value(QDir::cleanPath(path));
|
2010-04-01 14:42:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LibraryInfo Snapshot::libraryInfo(const QString &path) const
|
|
|
|
{
|
|
|
|
return _libraries.value(QDir::cleanPath(path));
|
|
|
|
}
|