OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmldesigner / designercore / model / rewriterview.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 <filemanager/astobjecttextextractor.h>
35 #include <filemanager/objectlengthcalculator.h>
36 #include <filemanager/firstdefinitionfinder.h>
37 #include <customnotifications.h>
38
39 #include <qmljs/parser/qmljsengine_p.h>
40
41 #include "rewriterview.h"
42 #include "rewritingexception.h"
43 #include "textmodifier.h"
44 #include "texttomodelmerger.h"
45 #include "modelnodepositionstorage.h"
46 #include "modeltotextmerger.h"
47 #include "nodelistproperty.h"
48 #include "nodeproperty.h"
49 #include "invalidmodelnodeexception.h"
50
51
52
53 using namespace QmlDesigner::Internal;
54
55 namespace QmlDesigner {
56
57 RewriterView::Error::Error():
58         m_type(NoError),
59         m_line(-1),
60         m_column(-1)
61 {
62 }
63
64 RewriterView::Error::Error(Exception *exception):
65         m_type(InternalError),
66         m_line(exception->line()),
67         m_column(-1),
68         m_description(exception->description()),
69         m_url(exception->file())
70 {
71 }
72
73 RewriterView::Error::Error(const QmlJS::DiagnosticMessage &qmlError, const QUrl &document):
74         m_type(ParseError),
75         m_line(qmlError.loc.startLine),
76         m_column(qmlError.loc.startColumn),
77         m_description(qmlError.message),
78         m_url(document)
79 {
80 }
81
82 RewriterView::Error::Error(const QString &shortDescription) :
83     m_type(ParseError),
84     m_line(1),
85     m_column(0),
86     m_description(shortDescription),
87     m_url()
88 {
89 }
90
91
92 QString RewriterView::Error::toString() const
93 {
94     QString str;
95
96     if (m_type == ParseError)
97         str += tr("Error parsing");
98     else if (m_type == InternalError)
99         str += tr("Internal error");
100
101     if (url().isValid()) {
102         if (!str.isEmpty())
103             str += QLatin1Char(' ');
104
105         str += tr("\"%1\"").arg(url().toString());
106     }
107
108     if (line() != -1) {
109         if (!str.isEmpty())
110             str += QLatin1Char(' ');
111         str += tr("line %1").arg(line());
112     }
113
114     if(column() != -1) {
115         if (!str.isEmpty())
116             str += QLatin1Char(' ');
117
118         str += tr("column %1").arg(column());
119     }
120
121     if (!str.isEmpty())
122         QLatin1String(": ");
123     str += description();
124
125     return str;
126 }
127
128 RewriterView::RewriterView(DifferenceHandling differenceHandling, QObject *parent):
129         AbstractView(parent),
130         m_differenceHandling(differenceHandling),
131         m_modificationGroupActive(false),
132         m_positionStorage(new ModelNodePositionStorage),
133         m_modelToTextMerger(new Internal::ModelToTextMerger(this)),
134         m_textToModelMerger(new Internal::TextToModelMerger(this)),
135         m_textModifier(0),
136         transactionLevel(0)
137 {
138 }
139
140 RewriterView::~RewriterView()
141 {
142     delete m_positionStorage;
143 }
144
145 Internal::ModelToTextMerger *RewriterView::modelToTextMerger() const
146 {
147     return m_modelToTextMerger.data();
148 }
149
150 Internal::TextToModelMerger *RewriterView::textToModelMerger() const
151 {
152     return m_textToModelMerger.data();
153 }
154
155 void RewriterView::modelAttached(Model *model)
156 {
157     AbstractView::modelAttached(model);
158
159     ModelAmender differenceHandler(m_textToModelMerger.data());
160     const QString qmlSource = m_textModifier->text();
161     if (m_textToModelMerger->load(qmlSource.toUtf8(), differenceHandler)) {
162         lastCorrectQmlSource = qmlSource;
163     }
164 }
165
166 void RewriterView::modelAboutToBeDetached(Model * /*model*/)
167 {
168     m_positionStorage->clear();
169 }
170
171 void RewriterView::nodeCreated(const ModelNode &createdNode)
172 {
173     Q_ASSERT(textModifier());
174     m_positionStorage->setNodeOffset(createdNode, ModelNodePositionStorage::INVALID_LOCATION);
175     if (textToModelMerger()->isActive())
176         return;
177
178     modelToTextMerger()->nodeCreated(createdNode);
179
180     if (!isModificationGroupActive())
181         applyChanges();
182 }
183
184 void RewriterView::nodeAboutToBeRemoved(const ModelNode &/*removedNode*/)
185 {
186 }
187
188 void RewriterView::nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange)
189 {
190     Q_ASSERT(textModifier());
191     if (textToModelMerger()->isActive())
192         return;
193
194     modelToTextMerger()->nodeRemoved(removedNode, parentProperty, propertyChange);
195
196     if (!isModificationGroupActive())
197         applyChanges();
198 }
199
200 void RewriterView::propertiesAdded(const ModelNode &/*node*/, const QList<AbstractProperty>& /*propertyList*/)
201 {
202     Q_ASSERT(0);
203 }
204
205 void RewriterView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
206 {
207     Q_ASSERT(textModifier());
208     if (textToModelMerger()->isActive())
209         return;
210
211
212     foreach (const AbstractProperty &property, propertyList) {
213         if (property.isDefaultProperty() && property.isNodeListProperty()) {
214             m_removeDefaultPropertyTransaction = beginRewriterTransaction();
215
216             foreach (const ModelNode &node, property.toNodeListProperty().toModelNodeList()) {
217                 modelToTextMerger()->nodeRemoved(node, property.toNodeAbstractProperty(), AbstractView::NoAdditionalChanges);
218             }
219         }
220     }
221 }
222
223 void RewriterView::propertiesRemoved(const QList<AbstractProperty>& propertyList)
224 {
225     Q_ASSERT(textModifier());
226     if (textToModelMerger()->isActive())
227         return;
228
229     modelToTextMerger()->propertiesRemoved(propertyList);
230
231     if (m_removeDefaultPropertyTransaction.isValid())
232         m_removeDefaultPropertyTransaction.commit();
233
234     if (!isModificationGroupActive())
235         applyChanges();
236 }
237
238 void RewriterView::variantPropertiesChanged(const QList<VariantProperty>& propertyList, PropertyChangeFlags propertyChange)
239 {
240     Q_ASSERT(textModifier());
241     if (textToModelMerger()->isActive())
242         return;
243
244     QList<AbstractProperty> usefulPropertyList;
245     foreach (const VariantProperty &property, propertyList)
246         usefulPropertyList.append(property);
247
248     modelToTextMerger()->propertiesChanged(usefulPropertyList, propertyChange);
249
250     if (!isModificationGroupActive())
251         applyChanges();
252 }
253
254 void RewriterView::bindingPropertiesChanged(const QList<BindingProperty>& propertyList, PropertyChangeFlags propertyChange)
255 {
256     Q_ASSERT(textModifier());
257     if (textToModelMerger()->isActive())
258         return;
259
260     QList<AbstractProperty> usefulPropertyList;
261     foreach (const BindingProperty &property, propertyList)
262         usefulPropertyList.append(property);
263
264     modelToTextMerger()->propertiesChanged(usefulPropertyList, propertyChange);
265
266     if (!isModificationGroupActive())
267         applyChanges();
268 }
269
270 void RewriterView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange)
271 {
272     Q_ASSERT(textModifier());
273     if (textToModelMerger()->isActive())
274         return;
275
276     modelToTextMerger()->nodeReparented(node, newPropertyParent, oldPropertyParent, propertyChange);
277
278     if (!isModificationGroupActive())
279         applyChanges();
280 }
281
282 void RewriterView::nodeAboutToBeReparented(const ModelNode &/*node*/, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/)
283 {
284 }
285
286
287 void RewriterView::importAdded(const Import &import)
288 {
289     Q_ASSERT(textModifier());
290     if (textToModelMerger()->isActive())
291         return;
292
293     if (import.url() == "Qt")
294         foreach (const Import &import, model()->imports()) {
295             if (import.url() == "QtQuick")
296                 return; //QtQuick magic we do not have to add an import for Qt
297         }
298
299     modelToTextMerger()->addImport(import);
300
301     if (!isModificationGroupActive())
302         applyChanges();
303 }
304
305 void RewriterView::importRemoved(const Import &import)
306 {
307     Q_ASSERT(textModifier());
308     if (textToModelMerger()->isActive())
309         return;
310
311     modelToTextMerger()->removeImport(import);
312
313     if (!isModificationGroupActive())
314         applyChanges();
315 }
316
317 void RewriterView::fileUrlChanged(const QUrl &/*oldUrl*/, const QUrl &/*newUrl*/)
318 {
319 }
320
321 void RewriterView::nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId)
322 {
323     Q_ASSERT(textModifier());
324     if (textToModelMerger()->isActive())
325         return;
326
327     modelToTextMerger()->nodeIdChanged(node, newId, oldId);
328
329     if (!isModificationGroupActive())
330         applyChanges();
331 }
332
333 void RewriterView::nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int /*oldIndex*/)
334 {
335     Q_ASSERT(textModifier());
336     if (textToModelMerger()->isActive())
337         return;
338
339     const QList<ModelNode> nodes = listProperty.toModelNodeList();
340
341     ModelNode trailingNode;
342     int newIndex = nodes.indexOf(movedNode);
343     if (newIndex + 1 < nodes.size())
344         trailingNode = nodes.at(newIndex + 1);
345     modelToTextMerger()->nodeSlidAround(movedNode, trailingNode);
346
347     if (!isModificationGroupActive())
348         applyChanges();
349 }
350
351 void RewriterView::rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion)
352 {
353      Q_ASSERT(textModifier());
354     if (textToModelMerger()->isActive())
355         return;
356
357     modelToTextMerger()->nodeTypeChanged(rootModelNode(), type, majorVersion, minorVersion);
358
359     if (!isModificationGroupActive())
360         applyChanges();
361 }
362
363 void RewriterView::customNotification(const AbstractView * /*view*/, const QString &identifier, const QList<ModelNode> & /* nodeList */, const QList<QVariant> & /*data */)
364 {
365     if (identifier == StartRewriterAmend || identifier == EndRewriterAmend)
366         return; // we emitted this ourselves, so just ignore these notifications.
367
368     if (identifier == ("__start rewriter transaction__")) {
369         transactionLevel++;
370         setModificationGroupActive(true);
371     }
372     else if (identifier == ("__end rewriter transaction__")) {
373         transactionLevel--;
374         Q_ASSERT(transactionLevel >= 0);
375
376     }
377     if (transactionLevel == 0)
378     {
379         setModificationGroupActive(false);
380         applyModificationGroupChanges();
381     }
382 }
383
384 void RewriterView::scriptFunctionsChanged(const ModelNode &/*node*/, const QStringList &/*scriptFunctionList*/)
385 {
386 }
387
388 void RewriterView::instancePropertyChange(const QList<QPair<ModelNode, QString> > &/*propertyList*/)
389 {
390 }
391
392 void RewriterView::instancesCompleted(const QVector<ModelNode> &/*completedNodeList*/)
393 {
394 }
395
396
397 void RewriterView::selectedNodesChanged(const QList<ModelNode> & /* selectedNodeList, */, const QList<ModelNode> & /*lastSelectedNodeList */)
398 {
399 }
400
401 bool RewriterView::isModificationGroupActive() const
402 {
403     return m_modificationGroupActive;
404 }
405
406 void RewriterView::setModificationGroupActive(bool active)
407 {
408     m_modificationGroupActive = active;
409 }
410
411 TextModifier *RewriterView::textModifier() const
412 {
413     return m_textModifier;
414 }
415
416 void RewriterView::setTextModifier(TextModifier *textModifier)
417 {
418     if (m_textModifier)
419         disconnect(m_textModifier, SIGNAL(textChanged()), this, SLOT(qmlTextChanged()));
420
421     m_textModifier = textModifier;
422
423     if (m_textModifier)
424         connect(m_textModifier, SIGNAL(textChanged()), this, SLOT(qmlTextChanged()));
425 }
426
427 QString RewriterView::textModifierContent() const
428 {
429     if (textModifier())
430         return textModifier()->text();
431
432     return QString();
433 }
434
435 void RewriterView::applyModificationGroupChanges()
436 {
437     Q_ASSERT(transactionLevel == 0);
438     applyChanges();
439 }
440
441 void RewriterView::applyChanges()
442 {
443     if (modelToTextMerger()->hasNoPendingChanges())
444         return; // quick exit: nothing to be done.
445
446     clearErrors();
447
448     if (inErrorState()) {
449         const QString content = textModifierContent();
450         qDebug() << "RewriterView::applyChanges() got called while in error state. Will do a quick-exit now.";
451         qDebug() << "Content:" << content;
452         throw RewritingException(__LINE__, __FUNCTION__, __FILE__, "RewriterView::applyChanges() already in error state", content);
453     }
454
455     try {
456         modelToTextMerger()->applyChanges();
457         if (!errors().isEmpty()) {
458             enterErrorState(errors().first().description());
459         }
460     } catch (Exception &e) {
461         const QString content = textModifierContent();
462         qDebug() << "RewriterException:" << m_rewritingErrorMessage;
463         qDebug() << "Content:" << content;
464         enterErrorState(e.description());
465     }
466
467     if (inErrorState()) {
468         const QString content = textModifierContent();
469         qDebug() << "RewriterException:" << m_rewritingErrorMessage;
470         qDebug() << "Content:" << content;
471         if (!errors().isEmpty())
472             qDebug() << "Error:" << errors().first().description();
473         throw RewritingException(__LINE__, __FUNCTION__, __FILE__, m_rewritingErrorMessage, content);
474     }
475 }
476
477 QList<RewriterView::Error> RewriterView::errors() const
478 {
479     return m_errors;
480 }
481
482 void RewriterView::clearErrors()
483 {
484     m_errors.clear();
485     emit errorsChanged(m_errors);
486 }
487
488 void RewriterView::setErrors(const QList<RewriterView::Error> &errors)
489 {
490     m_errors = errors;
491     emit errorsChanged(m_errors);
492 }
493
494 void RewriterView::addError(const RewriterView::Error &error)
495 {
496     m_errors.append(error);
497     emit errorsChanged(m_errors);
498 }
499
500 void RewriterView::enterErrorState(const QString &errorMessage)
501 {
502     m_rewritingErrorMessage = errorMessage;
503 }
504
505 void RewriterView::resetToLastCorrectQml()
506 {
507     m_textModifier->textDocument()->undo();
508     m_textModifier->textDocument()->clearUndoRedoStacks(QTextDocument::RedoStack);
509     ModelAmender differenceHandler(m_textToModelMerger.data());
510     m_textToModelMerger->load(m_textModifier->text().toUtf8(), differenceHandler);
511
512     leaveErrorState();
513 }
514
515 QMap<ModelNode, QString> RewriterView::extractText(const QList<ModelNode> &nodes) const
516 {
517     QmlDesigner::ASTObjectTextExtractor extract(m_textModifier->text());
518     QMap<ModelNode, QString> result;
519
520     foreach (const ModelNode &node, nodes) {
521         const int nodeLocation = m_positionStorage->nodeOffset(node);
522
523         if (nodeLocation == ModelNodePositionStorage::INVALID_LOCATION)
524             result.insert(node, QString());
525         else
526             result.insert(node, extract(nodeLocation));
527     }
528
529     return result;
530 }
531
532 int RewriterView::nodeOffset(const ModelNode &node) const
533 {
534     return m_positionStorage->nodeOffset(node);
535 }
536
537 /**
538  * \return the length of the node's text, or -1 if it wasn't found or if an error
539  *         occurred.
540  */
541 int RewriterView::nodeLength(const ModelNode &node) const
542 {
543     ObjectLengthCalculator objectLengthCalculator;
544     unsigned length;
545     if (objectLengthCalculator(m_textModifier->text(), nodeOffset(node), length))
546         return (int) length;
547     else
548         return -1;
549 }
550
551 int RewriterView::firstDefinitionInsideOffset(const ModelNode &node) const
552 {
553     FirstDefinitionFinder firstDefinitionFinder(m_textModifier->text());
554     return firstDefinitionFinder(nodeOffset(node));
555 }
556
557 int RewriterView::firstDefinitionInsideLength(const ModelNode &node) const
558 {
559     FirstDefinitionFinder firstDefinitionFinder(m_textModifier->text());
560     const int offset =  firstDefinitionFinder(nodeOffset(node));
561
562     ObjectLengthCalculator objectLengthCalculator;
563     unsigned length;
564     if (objectLengthCalculator(m_textModifier->text(), offset, length))
565         return length;
566     else
567         return -1;
568 }
569
570 bool RewriterView::modificationGroupActive()
571 {
572     return m_modificationGroupActive;
573 }
574
575 bool RewriterView::renameId(const QString& oldId, const QString& newId)
576 {
577     if (textModifier())
578         return textModifier()->renameId(oldId, newId);
579
580     return false;
581 }
582
583 QmlJS::LookupContext *RewriterView::lookupContext() const
584 {
585     return textToModelMerger()->lookupContext();
586 }
587
588 QmlJS::Document *RewriterView::document() const
589 {
590     return textToModelMerger()->document();
591 }
592
593 void RewriterView::qmlTextChanged()
594 {
595     if (inErrorState())
596         return;
597
598     if (m_textToModelMerger && m_textModifier) {
599         const QString newQmlText = m_textModifier->text();
600
601 //        qDebug() << "qmlTextChanged:" << newQmlText;
602
603         switch (m_differenceHandling) {
604             case Validate: {
605                 ModelValidator differenceHandler(m_textToModelMerger.data());
606                 if (m_textToModelMerger->load(newQmlText.toUtf8(), differenceHandler)) {
607                     lastCorrectQmlSource = newQmlText;
608                 }
609                 break;
610             }
611
612             case Amend:
613             default: {
614                 emitCustomNotification(StartRewriterAmend);
615                 ModelAmender differenceHandler(m_textToModelMerger.data());
616                 if (m_textToModelMerger->load(newQmlText, differenceHandler)) {
617                     lastCorrectQmlSource = newQmlText;
618                 }
619                 emitCustomNotification(EndRewriterAmend);
620                 break;
621             }
622         }
623     }
624 }
625
626 } //QmlDesigner