Files
qt-creator/src/libs/qmljs/qmljsdocument.cpp

393 lines
9.1 KiB
C++
Raw Normal View History

/**************************************************************************
**
** This file is part of Qt Creator
**
2010-03-05 11:25:49 +01:00
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "qmljsdocument.h"
#include "qmljsbind.h"
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/parser/qmljslexer_p.h>
#include <qmljs/parser/qmljsparser_p.h>
#include <qmljs/parser/qmljsnodepool_p.h>
#include <qmljs/parser/qmljsastfwd_p.h>
#include <QtCore/QDir>
using namespace QmlJS;
using namespace QmlJS::AST;
Document::Document(const QString &fileName)
: _engine(0)
, _pool(0)
, _ast(0)
, _bind(0)
, _isQmlDocument(false)
, _editorRevision(0)
, _parsedCorrectly(false)
, _fileName(QDir::cleanPath(fileName))
{
QFileInfo fileInfo(fileName);
_path = QDir::cleanPath(fileInfo.absolutePath());
// ### Should use mime type
if (fileInfo.suffix() == QLatin1String("qml")
|| fileInfo.suffix() == QLatin1String("qmlproject")) {
_isQmlDocument = true;
_componentName = fileInfo.baseName();
if (! _componentName.isEmpty()) {
// ### TODO: check the component name.
if (! _componentName.at(0).isUpper())
_componentName.clear();
}
}
}
Document::~Document()
{
if (_bind)
delete _bind;
if (_engine)
delete _engine;
if (_pool)
delete _pool;
}
Document::Ptr Document::create(const QString &fileName)
{
Document::Ptr doc(new Document(fileName));
return doc;
}
bool Document::isQmlDocument() const
{
return _isQmlDocument;
}
bool Document::isJSDocument() const
{
return ! _isQmlDocument;
}
AST::UiProgram *Document::qmlProgram() const
{
return cast<UiProgram *>(_ast);
}
AST::Program *Document::jsProgram() const
{
return cast<Program *>(_ast);
}
AST::ExpressionNode *Document::expression() const
{
if (_ast)
return _ast->expressionCast();
return 0;
}
AST::Node *Document::ast() const
{
return _ast;
}
QList<DiagnosticMessage> Document::diagnosticMessages() const
{
return _diagnosticMessages;
}
QString Document::source() const
{
return _source;
}
void Document::setSource(const QString &source)
{
_source = source;
}
int Document::editorRevision() const
{
return _editorRevision;
}
void Document::setEditorRevision(int revision)
{
_editorRevision = revision;
}
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;
}
bool Document::parse_helper(int startToken)
{
Q_ASSERT(! _engine);
Q_ASSERT(! _pool);
Q_ASSERT(! _ast);
Q_ASSERT(! _bind);
_engine = new Engine();
_pool = new NodePool(_fileName, _engine);
Lexer lexer(_engine);
Parser parser(_engine);
QString source = _source;
if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM)
extractPragmas(&source);
lexer.setCode(source, /*line = */ 1);
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();
_diagnosticMessages = parser.diagnosticMessages();
_bind = new Bind(this);
return _parsedCorrectly;
}
bool Document::parse()
{
if (isQmlDocument())
return parseQml();
return parseJavaScript();
}
bool Document::parseQml()
{
return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
}
bool Document::parseJavaScript()
{
return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
}
bool Document::parseExpression()
{
return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
}
Bind *Document::bind() const
{
return _bind;
}
// this is essentially a copy of QDeclarativeScriptParser::extractPragmas
void Document::extractPragmas(QString *source)
{
const QChar forwardSlash(QLatin1Char('/'));
const QChar star(QLatin1Char('*'));
const QChar newline(QLatin1Char('\n'));
const QChar dot(QLatin1Char('.'));
const QChar semicolon(QLatin1Char(';'));
const QChar space(QLatin1Char(' '));
const QString pragma(QLatin1String(".pragma "));
const QChar *pragmaData = pragma.constData();
QString &script = *source;
const QChar *data = script.constData();
const int length = script.count();
for (int ii = 0; ii < length; ++ii) {
const QChar &c = data[ii];
if (c.isSpace())
continue;
if (c == forwardSlash) {
++ii;
if (ii >= length)
return;
const QChar &c = data[ii];
if (c == forwardSlash) {
// Find next newline
while (ii < length && data[++ii] != newline) {};
} else if (c == star) {
// Find next star
while (true) {
while (ii < length && data[++ii] != star) {};
if (ii + 1 >= length)
return;
if (data[ii + 1] == forwardSlash) {
++ii;
break;
}
}
} else {
return;
}
} else if (c == dot) {
// Could be a pragma!
if (ii + pragma.length() >= length ||
0 != ::memcmp(data + ii, pragmaData, sizeof(QChar) * pragma.length()))
return;
int pragmaStatementIdx = ii;
ii += pragma.length();
while (ii < length && data[ii].isSpace()) { ++ii; }
int startIdx = ii;
while (ii < length && data[ii].isLetter()) { ++ii; }
int endIdx = ii;
if (ii != length && data[ii] != forwardSlash && !data[ii].isSpace() && data[ii] != semicolon)
return;
QString p(data + startIdx, endIdx - startIdx);
if (p != QLatin1String("library"))
return;
for (int jj = pragmaStatementIdx; jj < endIdx; ++jj) script[jj] = space;
} else {
return;
}
}
}
LibraryInfo::LibraryInfo()
: _valid(false)
{
}
LibraryInfo::LibraryInfo(const QmlDirParser &parser)
: _valid(true)
, _components(parser.components())
, _plugins(parser.plugins())
{
}
LibraryInfo::~LibraryInfo()
{
}
Snapshot::Snapshot()
{
}
Snapshot::~Snapshot()
{
}
void Snapshot::insert(const Document::Ptr &document)
{
if (document && (document->qmlProgram() || document->jsProgram())) {
const QString fileName = document->fileName();
const QString path = document->path();
remove(fileName);
_documentsByPath[path].append(document);
_documents.insert(fileName, document);
}
}
void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
{
_libraries.insert(QDir::cleanPath(path), info);
}
void Snapshot::remove(const QString &fileName)
{
Document::Ptr doc = _documents.value(fileName);
if (!doc.isNull()) {
const QString &path = doc->path();
QList<Document::Ptr> docs = _documentsByPath.value(path);
docs.removeAll(doc);
_documentsByPath[path] = docs;
_documents.remove(fileName);
}
}
Document::Ptr Snapshot::documentFromSource(const QString &code,
const QString &fileName) const
{
Document::Ptr newDoc = Document::create(fileName);
if (Document::Ptr thisDocument = document(fileName)) {
newDoc->_editorRevision = thisDocument->_editorRevision;
}
newDoc->setSource(code);
return newDoc;
}
Document::Ptr Snapshot::document(const QString &fileName) const
{
return _documents.value(QDir::cleanPath(fileName));
}
QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
{
return _documentsByPath.value(QDir::cleanPath(path));
}
LibraryInfo Snapshot::libraryInfo(const QString &path) const
{
return _libraries.value(QDir::cleanPath(path));
}