1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "qmljsdocument.h"
35 #include "qmljsbind.h"
36 #include <qmljs/parser/qmljsast_p.h>
37 #include <qmljs/parser/qmljslexer_p.h>
38 #include <qmljs/parser/qmljsparser_p.h>
39 #include <qmljs/parser/qmljsnodepool_p.h>
40 #include <qmljs/parser/qmljsastfwd_p.h>
41 #include <QtCore/QDir>
43 using namespace QmlJS;
44 using namespace QmlJS::AST;
47 \class QmlJS::Document
48 \brief A Qml or JavaScript document.
51 Documents are usually created by the \l{QmlJSEditor::Internal::ModelManager}
52 and stored in a \l{QmlJS::Snapshot}. They allow access to data such as
53 the file path, source code, abstract syntax tree and the \l{QmlJS::Bind}
54 instance for the document.
56 To make sure unused and outdated documents are removed correctly, Document
57 instances are usually accessed through a shared pointer, see \l{Document::Ptr}.
61 \class QmlJS::LibraryInfo
65 A LibraryInfo is created when the \l{QmlJSEditor::Internal::ModelManager} finds
66 a Qml library and parses the qmldir file. The instance holds information about
67 which Components the library provides and which plugins to load.
69 The ModelManager will try to extract detailed information about the types
70 defined in the plugins this library loads. Once it is done, the data will
71 be available through the metaObjects() function.
75 \class QmlJS::Snapshot
76 \brief A set of Document::Ptr and LibraryInfo instances.
77 \sa QmlJS::Document QmlJS::LibraryInfo
79 A Snapshot holds and offers access to a set of Document and LibraryInfo instances.
81 Usually Snapshots are copies of the snapshot maintained and updated by the
82 \l{QmlJSEditor::Internal::ModelManager} that updates its instance as parsing
83 threads finish and new information becomes available.
87 Document::Document(const QString &fileName)
92 , _isQmlDocument(false)
94 , _parsedCorrectly(false)
95 , _fileName(QDir::cleanPath(fileName))
97 QFileInfo fileInfo(fileName);
98 _path = QDir::cleanPath(fileInfo.absolutePath());
100 // ### Should use mime type
101 if (fileInfo.suffix() == QLatin1String("qml")
102 || fileInfo.suffix() == QLatin1String("qmlproject")) {
103 _isQmlDocument = true;
104 _componentName = fileInfo.baseName();
106 if (! _componentName.isEmpty()) {
107 // ### TODO: check the component name.
109 if (! _componentName.at(0).isUpper())
110 _componentName.clear();
115 Document::~Document()
127 Document::Ptr Document::create(const QString &fileName)
129 Document::Ptr doc(new Document(fileName));
133 bool Document::isQmlDocument() const
135 return _isQmlDocument;
138 bool Document::isJSDocument() const
140 return ! _isQmlDocument;
143 AST::UiProgram *Document::qmlProgram() const
145 return cast<UiProgram *>(_ast);
148 AST::Program *Document::jsProgram() const
150 return cast<Program *>(_ast);
153 AST::ExpressionNode *Document::expression() const
156 return _ast->expressionCast();
161 AST::Node *Document::ast() const
166 const QmlJS::Engine *Document::engine() const
171 QList<DiagnosticMessage> Document::diagnosticMessages() const
173 return _diagnosticMessages;
176 QString Document::source() const
181 void Document::setSource(const QString &source)
186 int Document::editorRevision() const
188 return _editorRevision;
191 void Document::setEditorRevision(int revision)
193 _editorRevision = revision;
196 QString Document::fileName() const
202 QString Document::path() const
207 QString Document::componentName() const
209 return _componentName;
212 bool Document::parse_helper(int startToken)
219 _engine = new Engine();
220 _pool = new NodePool(_fileName, _engine);
222 Lexer lexer(_engine);
223 Parser parser(_engine);
225 QString source = _source;
226 if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM)
227 extractPragmas(&source);
229 lexer.setCode(source, /*line = */ 1);
231 switch (startToken) {
232 case QmlJSGrammar::T_FEED_UI_PROGRAM:
233 _parsedCorrectly = parser.parse();
235 case QmlJSGrammar::T_FEED_JS_PROGRAM:
236 _parsedCorrectly = parser.parseProgram();
238 case QmlJSGrammar::T_FEED_JS_EXPRESSION:
239 _parsedCorrectly = parser.parseExpression();
245 _ast = parser.rootNode();
246 _diagnosticMessages = parser.diagnosticMessages();
248 _bind = new Bind(this, &_diagnosticMessages);
250 return _parsedCorrectly;
253 bool Document::parse()
258 return parseJavaScript();
261 bool Document::parseQml()
263 return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
266 bool Document::parseJavaScript()
268 return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
271 bool Document::parseExpression()
273 return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
276 Bind *Document::bind() const
281 // this is essentially a copy of QDeclarativeScriptParser::extractPragmas
282 void Document::extractPragmas(QString *source)
284 const QChar forwardSlash(QLatin1Char('/'));
285 const QChar star(QLatin1Char('*'));
286 const QChar newline(QLatin1Char('\n'));
287 const QChar dot(QLatin1Char('.'));
288 const QChar semicolon(QLatin1Char(';'));
289 const QChar space(QLatin1Char(' '));
290 const QString pragma(QLatin1String(".pragma "));
292 const QChar *pragmaData = pragma.constData();
294 QString &script = *source;
295 const QChar *data = script.constData();
296 const int length = script.count();
297 for (int ii = 0; ii < length; ++ii) {
298 const QChar &c = data[ii];
303 if (c == forwardSlash) {
308 const QChar &c = data[ii];
309 if (c == forwardSlash) {
311 while (ii < length && data[++ii] != newline) {};
312 } else if (c == star) {
315 while (ii < length && data[++ii] != star) {};
316 if (ii + 1 >= length)
319 if (data[ii + 1] == forwardSlash) {
327 } else if (c == dot) {
328 // Could be a pragma!
329 if (ii + pragma.length() >= length ||
330 0 != ::memcmp(data + ii, pragmaData, sizeof(QChar) * pragma.length()))
333 int pragmaStatementIdx = ii;
335 ii += pragma.length();
337 while (ii < length && data[ii].isSpace()) { ++ii; }
341 while (ii < length && data[ii].isLetter()) { ++ii; }
345 if (ii != length && data[ii] != forwardSlash && !data[ii].isSpace() && data[ii] != semicolon)
348 QString p(data + startIdx, endIdx - startIdx);
350 if (p != QLatin1String("library"))
353 for (int jj = pragmaStatementIdx; jj < endIdx; ++jj) script[jj] = space;
361 LibraryInfo::LibraryInfo()
363 , _dumpStatus(DumpNotStartedOrRunning)
367 LibraryInfo::LibraryInfo(const QmlDirParser &parser)
369 , _components(parser.components())
370 , _plugins(parser.plugins())
371 , _dumpStatus(DumpNotStartedOrRunning)
375 LibraryInfo::~LibraryInfo()
383 Snapshot::~Snapshot()
387 void Snapshot::insert(const Document::Ptr &document)
389 if (document && (document->qmlProgram() || document->jsProgram())) {
390 const QString fileName = document->fileName();
391 const QString path = document->path();
394 _documentsByPath[path].append(document);
395 _documents.insert(fileName, document);
399 void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
401 _libraries.insert(QDir::cleanPath(path), info);
404 void Snapshot::remove(const QString &fileName)
406 Document::Ptr doc = _documents.value(fileName);
408 const QString &path = doc->path();
410 QList<Document::Ptr> docs = _documentsByPath.value(path);
412 _documentsByPath[path] = docs;
414 _documents.remove(fileName);
418 Document::Ptr Snapshot::documentFromSource(const QString &code,
419 const QString &fileName) const
421 Document::Ptr newDoc = Document::create(fileName);
423 if (Document::Ptr thisDocument = document(fileName)) {
424 newDoc->_editorRevision = thisDocument->_editorRevision;
427 newDoc->setSource(code);
431 Document::Ptr Snapshot::document(const QString &fileName) const
433 return _documents.value(QDir::cleanPath(fileName));
436 QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
438 return _documentsByPath.value(QDir::cleanPath(path));
441 LibraryInfo Snapshot::libraryInfo(const QString &path) const
443 return _libraries.value(QDir::cleanPath(path));