1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "qmljsdocument.h"
34 #include "qmljsbind.h"
35 #include <qmljs/parser/qmljsast_p.h>
36 #include <qmljs/parser/qmljslexer_p.h>
37 #include <qmljs/parser/qmljsparser_p.h>
38 #include <qmljs/parser/qmljsnodepool_p.h>
39 #include <qmljs/parser/qmljsastfwd_p.h>
40 #include <QtCore/QDir>
42 using namespace QmlJS;
43 using namespace QmlJS::AST;
46 \class QmlJS::Document
47 \brief A Qml or JavaScript document.
50 Documents are usually created by the \l{QmlJSEditor::Internal::ModelManager}
51 and stored in a \l{QmlJS::Snapshot}. They allow access to data such as
52 the file path, source code, abstract syntax tree and the \l{QmlJS::Bind}
53 instance for the document.
55 To make sure unused and outdated documents are removed correctly, Document
56 instances are usually accessed through a shared pointer, see \l{Document::Ptr}.
60 \class QmlJS::LibraryInfo
64 A LibraryInfo is created when the \l{QmlJSEditor::Internal::ModelManager} finds
65 a Qml library and parses the qmldir file. The instance holds information about
66 which Components the library provides and which plugins to load.
68 The ModelManager will try to extract detailed information about the types
69 defined in the plugins this library loads. Once it is done, the data will
70 be available through the metaObjects() function.
74 \class QmlJS::Snapshot
75 \brief A set of Document::Ptr and LibraryInfo instances.
76 \sa QmlJS::Document QmlJS::LibraryInfo
78 A Snapshot holds and offers access to a set of Document and LibraryInfo instances.
80 Usually Snapshots are copies of the snapshot maintained and updated by the
81 \l{QmlJSEditor::Internal::ModelManager} that updates its instance as parsing
82 threads finish and new information becomes available.
86 Document::Document(const QString &fileName)
91 , _isQmlDocument(false)
93 , _parsedCorrectly(false)
94 , _fileName(QDir::cleanPath(fileName))
96 QFileInfo fileInfo(fileName);
97 _path = QDir::cleanPath(fileInfo.absolutePath());
99 // ### Should use mime type
100 if (fileInfo.suffix() == QLatin1String("qml")
101 || fileInfo.suffix() == QLatin1String("qmlproject")) {
102 _isQmlDocument = true;
103 _componentName = fileInfo.baseName();
105 if (! _componentName.isEmpty()) {
106 // ### TODO: check the component name.
108 if (! _componentName.at(0).isUpper())
109 _componentName.clear();
114 Document::~Document()
126 Document::Ptr Document::create(const QString &fileName)
128 Document::Ptr doc(new Document(fileName));
132 bool Document::isQmlDocument() const
134 return _isQmlDocument;
137 bool Document::isJSDocument() const
139 return ! _isQmlDocument;
142 AST::UiProgram *Document::qmlProgram() const
144 return cast<UiProgram *>(_ast);
147 AST::Program *Document::jsProgram() const
149 return cast<Program *>(_ast);
152 AST::ExpressionNode *Document::expression() const
155 return _ast->expressionCast();
160 AST::Node *Document::ast() const
165 const QmlJS::Engine *Document::engine() const
170 QList<DiagnosticMessage> Document::diagnosticMessages() const
172 return _diagnosticMessages;
175 QString Document::source() const
180 void Document::setSource(const QString &source)
185 int Document::editorRevision() const
187 return _editorRevision;
190 void Document::setEditorRevision(int revision)
192 _editorRevision = revision;
195 QString Document::fileName() const
201 QString Document::path() const
206 QString Document::componentName() const
208 return _componentName;
211 bool Document::parse_helper(int startToken)
218 _engine = new Engine();
219 _pool = new NodePool(_fileName, _engine);
221 Lexer lexer(_engine);
222 Parser parser(_engine);
224 QString source = _source;
225 if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM)
226 extractPragmas(&source);
228 lexer.setCode(source, /*line = */ 1);
230 switch (startToken) {
231 case QmlJSGrammar::T_FEED_UI_PROGRAM:
232 _parsedCorrectly = parser.parse();
234 case QmlJSGrammar::T_FEED_JS_PROGRAM:
235 _parsedCorrectly = parser.parseProgram();
237 case QmlJSGrammar::T_FEED_JS_EXPRESSION:
238 _parsedCorrectly = parser.parseExpression();
244 _ast = parser.rootNode();
245 _diagnosticMessages = parser.diagnosticMessages();
247 _bind = new Bind(this, &_diagnosticMessages);
249 return _parsedCorrectly;
252 bool Document::parse()
257 return parseJavaScript();
260 bool Document::parseQml()
262 return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
265 bool Document::parseJavaScript()
267 return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
270 bool Document::parseExpression()
272 return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
275 Bind *Document::bind() const
280 // this is essentially a copy of QDeclarativeScriptParser::extractPragmas
281 void Document::extractPragmas(QString *source)
283 const QChar forwardSlash(QLatin1Char('/'));
284 const QChar star(QLatin1Char('*'));
285 const QChar newline(QLatin1Char('\n'));
286 const QChar dot(QLatin1Char('.'));
287 const QChar semicolon(QLatin1Char(';'));
288 const QChar space(QLatin1Char(' '));
289 const QString pragma(QLatin1String(".pragma "));
291 const QChar *pragmaData = pragma.constData();
293 QString &script = *source;
294 const QChar *data = script.constData();
295 const int length = script.count();
296 for (int ii = 0; ii < length; ++ii) {
297 const QChar &c = data[ii];
302 if (c == forwardSlash) {
307 const QChar &c = data[ii];
308 if (c == forwardSlash) {
310 while (ii < length && data[++ii] != newline) {};
311 } else if (c == star) {
314 while (ii < length && data[++ii] != star) {};
315 if (ii + 1 >= length)
318 if (data[ii + 1] == forwardSlash) {
326 } else if (c == dot) {
327 // Could be a pragma!
328 if (ii + pragma.length() >= length ||
329 0 != ::memcmp(data + ii, pragmaData, sizeof(QChar) * pragma.length()))
332 int pragmaStatementIdx = ii;
334 ii += pragma.length();
336 while (ii < length && data[ii].isSpace()) { ++ii; }
340 while (ii < length && data[ii].isLetter()) { ++ii; }
344 if (ii != length && data[ii] != forwardSlash && !data[ii].isSpace() && data[ii] != semicolon)
347 QString p(data + startIdx, endIdx - startIdx);
349 if (p != QLatin1String("library"))
352 for (int jj = pragmaStatementIdx; jj < endIdx; ++jj) script[jj] = space;
360 LibraryInfo::LibraryInfo()
362 , _dumpStatus(DumpNotStartedOrRunning)
366 LibraryInfo::LibraryInfo(const QmlDirParser &parser)
368 , _components(parser.components())
369 , _plugins(parser.plugins())
370 , _dumpStatus(DumpNotStartedOrRunning)
374 LibraryInfo::~LibraryInfo()
382 Snapshot::~Snapshot()
386 void Snapshot::insert(const Document::Ptr &document)
388 if (document && (document->qmlProgram() || document->jsProgram())) {
389 const QString fileName = document->fileName();
390 const QString path = document->path();
393 _documentsByPath[path].append(document);
394 _documents.insert(fileName, document);
398 void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
400 _libraries.insert(QDir::cleanPath(path), info);
403 void Snapshot::remove(const QString &fileName)
405 Document::Ptr doc = _documents.value(fileName);
407 const QString &path = doc->path();
409 QList<Document::Ptr> docs = _documentsByPath.value(path);
411 _documentsByPath[path] = docs;
413 _documents.remove(fileName);
417 Document::Ptr Snapshot::documentFromSource(const QString &code,
418 const QString &fileName) const
420 Document::Ptr newDoc = Document::create(fileName);
422 if (Document::Ptr thisDocument = document(fileName)) {
423 newDoc->_editorRevision = thisDocument->_editorRevision;
426 newDoc->setSource(code);
430 Document::Ptr Snapshot::document(const QString &fileName) const
432 return _documents.value(QDir::cleanPath(fileName));
435 QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
437 return _documentsByPath.value(QDir::cleanPath(path));
440 LibraryInfo Snapshot::libraryInfo(const QString &path) const
442 return _libraries.value(QDir::cleanPath(path));