OSDN Git Service

183b9ec2ff2f3ec62b405f1ae401e7a1a6414c10
[qt-creator-jp/qt-creator-jp.git] / src / libs / qmljs / qmljsdocument.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
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>
42
43 using namespace QmlJS;
44 using namespace QmlJS::AST;
45
46 /*!
47     \class QmlJS::Document
48     \brief A Qml or JavaScript document.
49     \sa QmlJS::Snapshot
50
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.
55
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}.
58 */
59
60 /*!
61     \class QmlJS::LibraryInfo
62     \brief A Qml library.
63     \sa QmlJS::Snapshot
64
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.
68
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.
72 */
73
74 /*!
75     \class QmlJS::Snapshot
76     \brief A set of Document::Ptr and LibraryInfo instances.
77     \sa QmlJS::Document QmlJS::LibraryInfo
78
79     A Snapshot holds and offers access to a set of Document and LibraryInfo instances.
80
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.
84 */
85
86
87 Document::Document(const QString &fileName)
88     : _engine(0)
89     , _pool(0)
90     , _ast(0)
91     , _bind(0)
92     , _isQmlDocument(false)
93     , _editorRevision(0)
94     , _parsedCorrectly(false)
95     , _fileName(QDir::cleanPath(fileName))
96 {
97     QFileInfo fileInfo(fileName);
98     _path = QDir::cleanPath(fileInfo.absolutePath());
99
100     // ### Should use mime type
101     if (fileInfo.suffix() == QLatin1String("qml")
102             || fileInfo.suffix() == QLatin1String("qmlproject")) {
103         _isQmlDocument = true;
104         _componentName = fileInfo.baseName();
105
106         if (! _componentName.isEmpty()) {
107             // ### TODO: check the component name.
108
109             if (! _componentName.at(0).isUpper())
110                 _componentName.clear();
111         }
112     }
113 }
114
115 Document::~Document()
116 {
117     if (_bind)
118         delete _bind;
119
120     if (_engine)
121         delete _engine;
122
123     if (_pool)
124         delete _pool;
125 }
126
127 Document::Ptr Document::create(const QString &fileName)
128 {
129     Document::Ptr doc(new Document(fileName));
130     return doc;
131 }
132
133 bool Document::isQmlDocument() const
134 {
135     return _isQmlDocument;
136 }
137
138 bool Document::isJSDocument() const
139 {
140     return ! _isQmlDocument;
141 }
142
143 AST::UiProgram *Document::qmlProgram() const
144 {
145     return cast<UiProgram *>(_ast);
146 }
147
148 AST::Program *Document::jsProgram() const
149 {
150     return cast<Program *>(_ast);
151 }
152
153 AST::ExpressionNode *Document::expression() const
154 {
155     if (_ast)
156         return _ast->expressionCast();
157
158     return 0;
159 }
160
161 AST::Node *Document::ast() const
162 {
163     return _ast;
164 }
165
166 const QmlJS::Engine *Document::engine() const
167 {
168     return _engine;
169 }
170
171 QList<DiagnosticMessage> Document::diagnosticMessages() const
172 {
173     return _diagnosticMessages;
174 }
175
176 QString Document::source() const
177 {
178     return _source;
179 }
180
181 void Document::setSource(const QString &source)
182 {
183     _source = source;
184 }
185
186 int Document::editorRevision() const
187 {
188     return _editorRevision;
189 }
190
191 void Document::setEditorRevision(int revision)
192 {
193     _editorRevision = revision;
194 }
195
196 QString Document::fileName() const
197 {
198     return _fileName;
199
200 }
201
202 QString Document::path() const
203 {
204     return _path;
205 }
206
207 QString Document::componentName() const
208 {
209     return _componentName;
210 }
211
212 bool Document::parse_helper(int startToken)
213 {
214     Q_ASSERT(! _engine);
215     Q_ASSERT(! _pool);
216     Q_ASSERT(! _ast);
217     Q_ASSERT(! _bind);
218
219     _engine = new Engine();
220     _pool = new NodePool(_fileName, _engine);
221
222     Lexer lexer(_engine);
223     Parser parser(_engine);
224
225     QString source = _source;
226     if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM)
227         extractPragmas(&source);
228
229     lexer.setCode(source, /*line = */ 1);
230
231     switch (startToken) {
232     case QmlJSGrammar::T_FEED_UI_PROGRAM:
233         _parsedCorrectly = parser.parse();
234         break;
235     case QmlJSGrammar::T_FEED_JS_PROGRAM:
236         _parsedCorrectly = parser.parseProgram();
237         break;
238     case QmlJSGrammar::T_FEED_JS_EXPRESSION:
239         _parsedCorrectly = parser.parseExpression();
240         break;
241     default:
242         Q_ASSERT(0);
243     }
244
245     _ast = parser.rootNode();
246     _diagnosticMessages = parser.diagnosticMessages();
247
248     _bind = new Bind(this, &_diagnosticMessages);
249
250     return _parsedCorrectly;
251 }
252
253 bool Document::parse()
254 {
255     if (isQmlDocument())
256         return parseQml();
257
258     return parseJavaScript();
259 }
260
261 bool Document::parseQml()
262 {
263     return parse_helper(QmlJSGrammar::T_FEED_UI_PROGRAM);
264 }
265
266 bool Document::parseJavaScript()
267 {
268     return parse_helper(QmlJSGrammar::T_FEED_JS_PROGRAM);
269 }
270
271 bool Document::parseExpression()
272 {
273     return parse_helper(QmlJSGrammar::T_FEED_JS_EXPRESSION);
274 }
275
276 Bind *Document::bind() const
277 {
278     return _bind;
279 }
280
281 // this is essentially a copy of QDeclarativeScriptParser::extractPragmas
282 void Document::extractPragmas(QString *source)
283 {
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 "));
291
292     const QChar *pragmaData = pragma.constData();
293
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];
299
300         if (c.isSpace())
301             continue;
302
303         if (c == forwardSlash) {
304             ++ii;
305             if (ii >= length)
306                 return;
307
308             const QChar &c = data[ii];
309             if (c == forwardSlash) {
310                 // Find next newline
311                 while (ii < length && data[++ii] != newline) {};
312             } else if (c == star) {
313                 // Find next star
314                 while (true) {
315                     while (ii < length && data[++ii] != star) {};
316                     if (ii + 1 >= length)
317                         return;
318
319                     if (data[ii + 1] == forwardSlash) {
320                         ++ii;
321                         break;
322                     }
323                 }
324             } else {
325                 return;
326             }
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()))
331                 return;
332
333             int pragmaStatementIdx = ii;
334
335             ii += pragma.length();
336
337             while (ii < length && data[ii].isSpace()) { ++ii; }
338
339             int startIdx = ii;
340
341             while (ii < length && data[ii].isLetter()) { ++ii; }
342
343             int endIdx = ii;
344
345             if (ii != length && data[ii] != forwardSlash && !data[ii].isSpace() && data[ii] != semicolon)
346                 return;
347
348             QString p(data + startIdx, endIdx - startIdx);
349
350             if (p != QLatin1String("library"))
351                 return;
352
353             for (int jj = pragmaStatementIdx; jj < endIdx; ++jj) script[jj] = space;
354
355         } else {
356             return;
357         }
358     }
359 }
360
361 LibraryInfo::LibraryInfo()
362     : _valid(false)
363     , _dumpStatus(DumpNotStartedOrRunning)
364 {
365 }
366
367 LibraryInfo::LibraryInfo(const QmlDirParser &parser)
368     : _valid(true)
369     , _components(parser.components())
370     , _plugins(parser.plugins())
371     , _dumpStatus(DumpNotStartedOrRunning)
372 {
373 }
374
375 LibraryInfo::~LibraryInfo()
376 {
377 }
378
379 Snapshot::Snapshot()
380 {
381 }
382
383 Snapshot::~Snapshot()
384 {
385 }
386
387 void Snapshot::insert(const Document::Ptr &document)
388 {
389     if (document && (document->qmlProgram() || document->jsProgram())) {
390         const QString fileName = document->fileName();
391         const QString path = document->path();
392
393         remove(fileName);
394         _documentsByPath[path].append(document);
395         _documents.insert(fileName, document);
396     }
397 }
398
399 void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
400 {
401     _libraries.insert(QDir::cleanPath(path), info);
402 }
403
404 void Snapshot::remove(const QString &fileName)
405 {
406     Document::Ptr doc = _documents.value(fileName);
407     if (!doc.isNull()) {
408         const QString &path = doc->path();
409
410         QList<Document::Ptr> docs = _documentsByPath.value(path);
411         docs.removeAll(doc);
412         _documentsByPath[path] = docs;
413
414         _documents.remove(fileName);
415     }
416 }
417
418 Document::Ptr Snapshot::documentFromSource(const QString &code,
419                                            const QString &fileName) const
420 {
421     Document::Ptr newDoc = Document::create(fileName);
422
423     if (Document::Ptr thisDocument = document(fileName)) {
424         newDoc->_editorRevision = thisDocument->_editorRevision;
425     }
426
427     newDoc->setSource(code);
428     return newDoc;
429 }
430
431 Document::Ptr Snapshot::document(const QString &fileName) const
432 {
433     return _documents.value(QDir::cleanPath(fileName));
434 }
435
436 QList<Document::Ptr> Snapshot::documentsInDirectory(const QString &path) const
437 {
438     return _documentsByPath.value(QDir::cleanPath(path));
439 }
440
441 LibraryInfo Snapshot::libraryInfo(const QString &path) const
442 {
443     return _libraries.value(QDir::cleanPath(path));
444 }