summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2009-05-26 01:17:20 (GMT)
committerAaron Kennedy <aaron.kennedy@nokia.com>2009-05-26 01:17:20 (GMT)
commit4058b501914216bf28ab62c02b78abcbf7f5a3c9 (patch)
tree5dc502f69aa8f2151c9adfee3580f1d1aa0833aa
parent334e406ff6441d3980741683714087c59c7dd123 (diff)
downloadQt-4058b501914216bf28ab62c02b78abcbf7f5a3c9.zip
Qt-4058b501914216bf28ab62c02b78abcbf7f5a3c9.tar.gz
Qt-4058b501914216bf28ab62c02b78abcbf7f5a3c9.tar.bz2
roberto: Added support for CSS like numeric literals e.g. 10p
-rw-r--r--src/declarative/qml/parser/javascript.g6
-rw-r--r--src/declarative/qml/parser/javascriptast.cpp38
-rw-r--r--src/declarative/qml/parser/javascriptast_p.h37
-rw-r--r--src/declarative/qml/parser/javascriptlexer.cpp57
-rw-r--r--src/declarative/qml/parser/javascriptlexer_p.h19
-rw-r--r--src/declarative/qml/parser/javascriptparser.cpp6
-rw-r--r--src/declarative/qml/parser/parser.pri2
-rw-r--r--src/declarative/qml/qml.pri13
-rw-r--r--src/declarative/qml/qmlscriptparser.cpp74
-rw-r--r--src/declarative/qml/rewriter/rewriter.cpp55
-rw-r--r--src/declarative/qml/rewriter/rewriter.pri4
-rw-r--r--src/declarative/qml/rewriter/rewriter_p.h112
-rw-r--r--src/declarative/qml/rewriter/textwriter.cpp176
-rw-r--r--src/declarative/qml/rewriter/textwriter_p.h60
14 files changed, 624 insertions, 35 deletions
diff --git a/src/declarative/qml/parser/javascript.g b/src/declarative/qml/parser/javascript.g
index 155630b..8cabeea 100644
--- a/src/declarative/qml/parser/javascript.g
+++ b/src/declarative/qml/parser/javascript.g
@@ -875,7 +875,7 @@ case $rule_number: {
PrimaryExpression: T_NUMERIC_LITERAL ;
/.
case $rule_number: {
- AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval);
+ AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval, lexer->flags);
node->literalToken = loc(1);
sym(1).Node = node;
} break;
@@ -2830,7 +2830,7 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ;
yytoken = *tk;
yylval = 0;
yylloc = token_buffer[0].loc;
- yylloc.length = 0;
+ yylloc.length = 0;
first_token = &token_buffer[0];
last_token = &token_buffer[2];
@@ -2852,7 +2852,7 @@ PropertyNameAndValueListOpt: PropertyNameAndValueList ;
yytoken = tk;
yylval = 0;
yylloc = token_buffer[0].loc;
- yylloc.length = 0;
+ yylloc.length = 0;
action = errorState;
goto _Lcheck_token;
diff --git a/src/declarative/qml/parser/javascriptast.cpp b/src/declarative/qml/parser/javascriptast.cpp
index 083dd28..130229b 100644
--- a/src/declarative/qml/parser/javascriptast.cpp
+++ b/src/declarative/qml/parser/javascriptast.cpp
@@ -49,6 +49,44 @@ QT_BEGIN_NAMESPACE
namespace JavaScript { namespace AST {
+int NumericLiteral::suffixLength[] = {
+ 0, // noSuffix
+ 2, // emSuffix
+ 2, // exSuffix
+ 2, // pxSuffix
+ 2, // cmSuffix
+ 2, // mmSuffix
+ 2, // inSuffix
+ 2, // ptSuffix
+ 2, // pcSuffix
+ 3, // degSuffix
+ 3, // radSuffix
+ 4, // gradSuffix
+ 2, // msSuffix
+ 1, // sSuffix
+ 2, // hzSuffix
+ 3 // khzSuffix
+};
+
+const char *const NumericLiteral::suffixSpell[] = {
+ "",
+ "em",
+ "ex",
+ "px",
+ "cm",
+ "mm",
+ "in",
+ "pt",
+ "pc",
+ "deg",
+ "rad",
+ "grad",
+ "ms",
+ "s",
+ "hz",
+ "khz"
+};
+
ExpressionNode *Node::expressionCast()
{
return 0;
diff --git a/src/declarative/qml/parser/javascriptast_p.h b/src/declarative/qml/parser/javascriptast_p.h
index 134f3cc..b5fd922 100644
--- a/src/declarative/qml/parser/javascriptast_p.h
+++ b/src/declarative/qml/parser/javascriptast_p.h
@@ -398,8 +398,30 @@ class NumericLiteral: public ExpressionNode
public:
JAVASCRIPT_DECLARE_AST_NODE(NumericLiteral)
- NumericLiteral(double v):
- value (v) { kind = K; }
+ enum Suffix { // ### keep it in sync with the Suffix enum in javascriptlexer_p.h
+ noSuffix,
+ emSuffix,
+ exSuffix,
+ pxSuffix,
+ cmSuffix,
+ mmSuffix,
+ inSuffix,
+ ptSuffix,
+ pcSuffix,
+ degSuffix,
+ radSuffix,
+ gradSuffix,
+ msSuffix,
+ sSuffix,
+ hzSuffix,
+ khzSuffix
+ };
+
+ static int suffixLength[];
+ static const char *const suffixSpell[];
+
+ NumericLiteral(double v, int suffix):
+ value(v), suffix(suffix) { kind = K; }
virtual ~NumericLiteral() {}
virtual void accept0(Visitor *visitor);
@@ -412,6 +434,7 @@ public:
// attributes:
double value;
+ int suffix;
SourceLocation literalToken;
};
@@ -2314,7 +2337,7 @@ public:
virtual SourceLocation firstSourceLocation() const
{
if (defaultToken.isValid())
- return defaultToken;
+ return defaultToken;
return propertyToken;
}
@@ -2375,9 +2398,9 @@ public:
virtual SourceLocation firstSourceLocation() const
{
if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
- return funDecl->firstSourceLocation();
+ return funDecl->firstSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
- return varStmt->firstSourceLocation();
+ return varStmt->firstSourceLocation();
return SourceLocation();
}
@@ -2385,9 +2408,9 @@ public:
virtual SourceLocation lastSourceLocation() const
{
if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
- return funDecl->lastSourceLocation();
+ return funDecl->lastSourceLocation();
else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
- return varStmt->lastSourceLocation();
+ return varStmt->lastSourceLocation();
return SourceLocation();
}
diff --git a/src/declarative/qml/parser/javascriptlexer.cpp b/src/declarative/qml/parser/javascriptlexer.cpp
index 7455b87..fda6ad2 100644
--- a/src/declarative/qml/parser/javascriptlexer.cpp
+++ b/src/declarative/qml/parser/javascriptlexer.cpp
@@ -377,7 +377,7 @@ int Lexer::findReservedWord(const QChar *c, int size) const
else if (c[0] == QLatin1Char('p') && c[1] == QLatin1Char('r')
&& c[2] == QLatin1Char('o') && c[3] == QLatin1Char('p')
&& c[4] == QLatin1Char('e') && c[5] == QLatin1Char('r')
- && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y'))
+ && c[6] == QLatin1Char('t') && c[7] == QLatin1Char('y'))
return JavaScriptGrammar::T_PROPERTY;
else if (check_reserved) {
if (c[0] == QLatin1Char('a') && c[1] == QLatin1Char('b')
@@ -753,12 +753,65 @@ int Lexer::lex()
bol = false;
}
+ if (state == Number) {
+ // CSS-style suffix for numeric literals
+
+ flags = noSuffix;
+
+ if (current == 'e' && next1 == 'm') {
+ flags = emSuffix;
+ shift(2);
+ } else if (current == 'e' && next1 == 'x') {
+ flags = exSuffix;
+ shift(2);
+ } else if (current == 'p' && next1 == 'x') {
+ flags = pxSuffix;
+ shift(2);
+ } else if (current == 'c' && next1 == 'm') {
+ flags = cmSuffix;
+ shift(2);
+ } else if (current == 'm' && next1 == 'm') {
+ flags = mmSuffix;
+ shift(2);
+ } else if (current == 'i' && next1 == 'n') {
+ flags = inSuffix;
+ shift(2);
+ } else if (current == 'p' && next1 == 't') {
+ flags = ptSuffix;
+ shift(2);
+ } else if (current == 'p' && next1 == 'c') {
+ flags = pcSuffix;
+ shift(1);
+ } else if (current == 'd' && next1 == 'e' && next2 == 'g') {
+ flags = degSuffix;
+ shift(3);
+ } else if (current == 'r' && next1 == 'a' && next2 == 'd') {
+ flags = radSuffix;
+ shift(3);
+ } else if (current == 'g' && next1 == 'r' && next2 == 'a' && next3 == 'd') {
+ flags = gradSuffix;
+ shift(4);
+ } else if (current == 'm' && next1 == 's') {
+ flags = msSuffix;
+ shift(2);
+ } else if (current == 's') {
+ flags = sSuffix;
+ shift(1);
+ } else if (current == 'h' && next1 == 'z') {
+ flags = hzSuffix;
+ shift(2);
+ } else if (current == 'k' && next1 == 'h' && next2 == 'z') {
+ flags = khzSuffix;
+ shift(3);
+ }
+ }
+
// no identifiers allowed directly after numeric literal, e.g. "3in" is bad
if ((state == Number || state == Octal || state == Hex)
&& isIdentLetter(current)) {
state = Bad;
err = IllegalIdentifier;
- errmsg = QLatin1String("Identifier cannot start with numeric literal");
+ errmsg = QLatin1String("Identifier cannot start with numeric literal `%1'");
}
// terminate string
diff --git a/src/declarative/qml/parser/javascriptlexer_p.h b/src/declarative/qml/parser/javascriptlexer_p.h
index 092609c..a47c1ae 100644
--- a/src/declarative/qml/parser/javascriptlexer_p.h
+++ b/src/declarative/qml/parser/javascriptlexer_p.h
@@ -112,6 +112,25 @@ public:
Other,
Bad };
+ enum Suffix {
+ noSuffix,
+ emSuffix,
+ exSuffix,
+ pxSuffix,
+ cmSuffix,
+ mmSuffix,
+ inSuffix,
+ ptSuffix,
+ pcSuffix,
+ degSuffix,
+ radSuffix,
+ gradSuffix,
+ msSuffix,
+ sSuffix,
+ hzSuffix,
+ khzSuffix
+ };
+
enum Error {
NoError,
IllegalCharacter,
diff --git a/src/declarative/qml/parser/javascriptparser.cpp b/src/declarative/qml/parser/javascriptparser.cpp
index 7ff438e..34ecd0e 100644
--- a/src/declarative/qml/parser/javascriptparser.cpp
+++ b/src/declarative/qml/parser/javascriptparser.cpp
@@ -430,7 +430,7 @@ case 51: {
} break;
case 52: {
- AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval);
+ AST::NumericLiteral *node = makeAstNode<AST::NumericLiteral> (driver->nodePool(), sym(1).dval, lexer->flags);
node->literalToken = loc(1);
sym(1).Node = node;
} break;
@@ -1675,7 +1675,7 @@ case 320: {
yytoken = *tk;
yylval = 0;
yylloc = token_buffer[0].loc;
- yylloc.length = 0;
+ yylloc.length = 0;
first_token = &token_buffer[0];
last_token = &token_buffer[2];
@@ -1697,7 +1697,7 @@ case 320: {
yytoken = tk;
yylval = 0;
yylloc = token_buffer[0].loc;
- yylloc.length = 0;
+ yylloc.length = 0;
action = errorState;
goto _Lcheck_token;
diff --git a/src/declarative/qml/parser/parser.pri b/src/declarative/qml/parser/parser.pri
index b4d226a..72bd46c 100644
--- a/src/declarative/qml/parser/parser.pri
+++ b/src/declarative/qml/parser/parser.pri
@@ -1,4 +1,6 @@
+INCLUDEPATH += $$PWD
+
HEADERS += $$PWD/javascriptast_p.h \
$$PWD/javascriptastfwd_p.h \
$$PWD/javascriptastvisitor_p.h \
diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri
index 99e30e4..b6ac30e 100644
--- a/src/declarative/qml/qml.pri
+++ b/src/declarative/qml/qml.pri
@@ -22,7 +22,8 @@ SOURCES += qml/qmlparser.cpp \
qml/qmlparserstatus.cpp \
qml/qmlcompositetypemanager.cpp \
qml/qmlinfo.cpp \
- qml/qmlerror.cpp
+ qml/qmlerror.cpp \
+ qml/qmlscriptparser.cpp
HEADERS += qml/qmlparser_p.h \
qml/qmlinstruction_p.h \
@@ -59,15 +60,13 @@ HEADERS += qml/qmlparser_p.h \
qml/qmlcompositetypemanager_p.h \
qml/qmllist.h \
qml/qmldeclarativedata_p.h \
- qml/qmlerror.h
+ qml/qmlerror.h \
+ qml/qmlscriptparser_p.h
# for qtscript debugger
QT += scripttools
-include(script/script.pri)
-# new language front-end
+include(script/script.pri)
include(parser/parser.pri)
+include(rewriter/rewriter.pri)
-HEADERS += qml/qmlscriptparser_p.h
-
-SOURCES += qml/qmlscriptparser.cpp
diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp
index 5b3564f..07f6b17 100644
--- a/src/declarative/qml/qmlscriptparser.cpp
+++ b/src/declarative/qml/qmlscriptparser.cpp
@@ -9,6 +9,8 @@
#include "parser/javascriptastvisitor_p.h"
#include "parser/javascriptast_p.h"
+#include "rewriter/textwriter_p.h"
+
#include <QStack>
#include <QCoreApplication>
#include <QtDebug>
@@ -22,6 +24,46 @@ using namespace QmlParser;
namespace {
+class RewriteNumericLiterals: protected AST::Visitor
+{
+ unsigned _position;
+ TextWriter _writer;
+
+public:
+ QString operator()(QString code, unsigned position, AST::Node *node)
+ {
+ _position = position;
+
+ AST::Node::acceptChild(node, this);
+
+ _writer.write(&code);
+
+ return code;
+ }
+
+protected:
+ using AST::Visitor::visit;
+
+ virtual bool visit(AST::NumericLiteral *node)
+ {
+ if (node->suffix != AST::NumericLiteral::noSuffix) {
+ const int suffixLength = AST::NumericLiteral::suffixLength[node->suffix];
+ const char *suffixSpell = AST::NumericLiteral::suffixSpell[node->suffix];
+ QString pre;
+ pre += QLatin1String("qmlNumberFrom");
+ pre += QChar(QLatin1Char(suffixSpell[0])).toUpper();
+ pre += QLatin1String(&suffixSpell[1]);
+ pre += QLatin1Char('(');
+ _writer.replace(node->literalToken.begin() - _position, 0, pre);
+ _writer.replace(node->literalToken.end() - _position - suffixLength,
+ suffixLength,
+ QLatin1String(")"));
+ }
+
+ return false;
+ }
+};
+
class ProcessAST: protected AST::Visitor
{
struct State {
@@ -107,24 +149,30 @@ protected:
QString textAt(const AST::SourceLocation &loc) const
{ return _contents.mid(loc.offset, loc.length); }
+
QString textAt(const AST::SourceLocation &first,
const AST::SourceLocation &last) const
{ return _contents.mid(first.offset, last.offset + last.length - first.offset); }
- QString asString(AST::ExpressionNode *expr) const
+ RewriteNumericLiterals rewriteNumericLiterals;
+
+ QString asString(AST::ExpressionNode *expr)
{
if (! expr)
return QString();
- return textAt(expr->firstSourceLocation(), expr->lastSourceLocation());
+ return rewriteNumericLiterals(textAt(expr->firstSourceLocation(), expr->lastSourceLocation()),
+ expr->firstSourceLocation().offset, expr);
}
- QString asString(AST::Statement *stmt) const
+ QString asString(AST::Statement *stmt)
{
if (! stmt)
return QString();
- QString s = textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation());
+ QString s = rewriteNumericLiterals(textAt(stmt->firstSourceLocation(), stmt->lastSourceLocation()),
+ stmt->firstSourceLocation().offset, stmt);
+
s += QLatin1Char('\n');
return s;
}
@@ -214,7 +262,7 @@ ProcessAST::defineObjectBinding_helper(AST::UiQualifiedId *propertyName,
int propertyCount = 0;
for (; propertyName; propertyName = propertyName->next){
++propertyCount;
- _stateStack.pushProperty(propertyName->name->asString(),
+ _stateStack.pushProperty(propertyName->name->asString(),
this->location(propertyName));
}
@@ -322,7 +370,7 @@ Object *ProcessAST::defineObjectBinding(AST::UiQualifiedId *qualifiedId,
script = asString(scriptBinding->statement);
}
- LocationSpan l = this->location(scriptBinding->statement->firstSourceLocation(),
+ LocationSpan l = this->location(scriptBinding->statement->firstSourceLocation(),
scriptBinding->statement->lastSourceLocation());
_stateStack.pushProperty(QLatin1String("script"), l);
@@ -414,7 +462,7 @@ bool ProcessAST::visit(AST::UiPublicMember *node)
{ "var", Object::DynamicProperty::Variant },
{ "variant", Object::DynamicProperty::Variant }
};
- const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
+ const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
sizeof(propTypeNameToTypes[0]);
bool typeFound = false;
@@ -425,7 +473,7 @@ bool ProcessAST::visit(AST::UiPublicMember *node)
typeFound = true;
}
}
-
+
if(!typeFound) {
QmlError error;
error.setDescription(QCoreApplication::translate("QmlParser","Expected property type"));
@@ -515,7 +563,7 @@ bool ProcessAST::visit(AST::UiScriptBinding *node)
AST::UiQualifiedId *propertyName = node->qualifiedId;
for (; propertyName; propertyName = propertyName->next){
++propertyCount;
- _stateStack.pushProperty(propertyName->name->asString(),
+ _stateStack.pushProperty(propertyName->name->asString(),
location(propertyName));
}
@@ -526,7 +574,7 @@ bool ProcessAST::visit(AST::UiScriptBinding *node)
if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement *>(node->statement)) {
primitive = getVariant(stmt->expression);
} else { // do binding
- primitive = QmlParser::Variant(asString(node->statement),
+ primitive = QmlParser::Variant(asString(node->statement),
QmlParser::Variant::Script);
}
@@ -550,7 +598,7 @@ bool ProcessAST::visit(AST::UiArrayBinding *node)
AST::UiQualifiedId *propertyName = node->qualifiedId;
for (; propertyName; propertyName = propertyName->next){
++propertyCount;
- _stateStack.pushProperty(propertyName->name->asString(),
+ _stateStack.pushProperty(propertyName->name->asString(),
location(propertyName));
}
@@ -608,7 +656,7 @@ bool ProcessAST::visit(AST::UiSourceElement *node)
}
Value *value = new Value;
- value->location = location(node->firstSourceLocation(),
+ value->location = location(node->firstSourceLocation(),
node->lastSourceLocation());
value->value = QmlParser::Variant(source);
@@ -673,7 +721,7 @@ bool QmlScriptParser::parse(const QByteArray &data, const QUrl &url)
process(code, parser.ast());
// Set the url for process errors
- for(int ii = 0; ii < _errors.count(); ++ii)
+ for(int ii = 0; ii < _errors.count(); ++ii)
_errors[ii].setUrl(url);
}
diff --git a/src/declarative/qml/rewriter/rewriter.cpp b/src/declarative/qml/rewriter/rewriter.cpp
new file mode 100644
index 0000000..ec504fa
--- /dev/null
+++ b/src/declarative/qml/rewriter/rewriter.cpp
@@ -0,0 +1,55 @@
+#include "rewriter_p.h"
+#include "javascriptast_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace JavaScript;
+
+void Rewriter::replace(const AST::SourceLocation &loc, const QString &text)
+{ replace(loc.offset, loc.length, text); }
+
+void Rewriter::remove(const AST::SourceLocation &loc)
+{ return replace(loc.offset, loc.length, QString()); }
+
+void Rewriter::remove(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc)
+{ return replace(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, QString()); }
+
+void Rewriter::insertTextBefore(const AST::SourceLocation &loc, const QString &text)
+{ replace(loc.offset, 0, text); }
+
+void Rewriter::insertTextAfter(const AST::SourceLocation &loc, const QString &text)
+{ replace(loc.offset + loc.length, 0, text); }
+
+void Rewriter::replace(int offset, int length, const QString &text)
+{ textWriter.replace(offset, length, text); }
+
+void Rewriter::insertText(int offset, const QString &text)
+{ replace(offset, 0, text); }
+
+void Rewriter::removeText(int offset, int length)
+{ replace(offset, length, QString()); }
+
+QString Rewriter::textAt(const AST::SourceLocation &loc) const
+{ return _code.mid(loc.offset, loc.length); }
+
+QString Rewriter::textAt(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc) const
+{ return _code.mid(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset); }
+
+void Rewriter::accept(JavaScript::AST::Node *node)
+{ JavaScript::AST::Node::acceptChild(node, this); }
+
+void Rewriter::moveTextBefore(const AST::SourceLocation &firstLoc,
+ const AST::SourceLocation &lastLoc,
+ const AST::SourceLocation &loc)
+{
+ textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset);
+}
+
+void Rewriter::moveTextAfter(const AST::SourceLocation &firstLoc,
+ const AST::SourceLocation &lastLoc,
+ const AST::SourceLocation &loc)
+{
+ textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/qml/rewriter/rewriter.pri b/src/declarative/qml/rewriter/rewriter.pri
new file mode 100644
index 0000000..987e26d
--- /dev/null
+++ b/src/declarative/qml/rewriter/rewriter.pri
@@ -0,0 +1,4 @@
+
+INCLUDEPATH += $$PWD
+HEADERS += $$PWD/rewriter_p.h $$PWD/textwriter_p.h
+SOURCES += $$PWD/rewriter.cpp $$PWD/textwriter.cpp
diff --git a/src/declarative/qml/rewriter/rewriter_p.h b/src/declarative/qml/rewriter/rewriter_p.h
new file mode 100644
index 0000000..892c006
--- /dev/null
+++ b/src/declarative/qml/rewriter/rewriter_p.h
@@ -0,0 +1,112 @@
+#ifndef REWRITER_H
+#define REWRITER_H
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+#include "textwriter_p.h"
+#include "javascriptastvisitor_p.h"
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+namespace JavaScript {
+
+////////////////////////////////////////////////////////////////////////////////
+// Replacement
+////////////////////////////////////////////////////////////////////////////////
+class Replacement
+{
+ int _offset;
+ int _length;
+ QString _text;
+
+public:
+ Replacement(int offset = 0, int length = 0, const QString &text = QString())
+ : _offset(offset), _length(length), _text(text)
+ { }
+
+ bool isNull() const { return _offset == _length; }
+ operator bool() const { return ! isNull(); }
+
+ int offset() const { return _offset; }
+ int length() const { return _length; }
+ QString text() const { return _text; }
+};
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Rewriter
+////////////////////////////////////////////////////////////////////////////////
+class Rewriter: public AST::Visitor
+{
+protected:
+ TextWriter textWriter;
+public:
+ //
+ // Token based API
+ //
+
+ /// Returns the text of the token at the given \a location.
+ QString textAt(const AST::SourceLocation &location) const;
+
+ QString textAt(const AST::SourceLocation &firstLoc,
+ const AST::SourceLocation &lastLoc) const;
+
+ /// Replace the token at \a loc with the given \a text.
+ void replace(const AST::SourceLocation &loc, const QString &text);
+
+ /// Remove the token at the given \a location.
+ void remove(const AST::SourceLocation &location);
+
+ /// Remove all tokens in the range [\a firstLoc, \a lastLoc].
+ void remove(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc);
+
+ /// Insert \a text before the token at the given \a location.
+ void insertTextBefore(const AST::SourceLocation &location, const QString &text);
+
+ /// Insert \a text after the token at the given \a location.
+ void insertTextAfter(const AST::SourceLocation &loc, const QString &text);
+
+ void moveTextBefore(const AST::SourceLocation &firstLoc,
+ const AST::SourceLocation &lastLoc,
+ const AST::SourceLocation &loc);
+
+ void moveTextAfter(const AST::SourceLocation &firstLoc,
+ const AST::SourceLocation &lastLoc,
+ const AST::SourceLocation &loc);
+
+ //
+ // low-level offset based API
+ //
+ void replace(int offset, int length, const QString &text);
+ void insertText(int offset, const QString &text);
+ void removeText(int offset, int length);
+
+ /// Visit the given \a node.
+ void accept(AST::Node *node);
+
+ /// Returns the original unchanged source code.
+ QString code() const { return _code; }
+
+ /// Returns the list of replacements.
+ QList<Replacement> replacementList() const { return _replacementList; }
+
+protected:
+ /// \internal
+ void setCode(const QString &code) { _code = code; }
+
+private:
+ QString _code;
+ QList<Replacement> _replacementList;
+};
+
+} // end of namespace JavaScript
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // REWRITER_H
diff --git a/src/declarative/qml/rewriter/textwriter.cpp b/src/declarative/qml/rewriter/textwriter.cpp
new file mode 100644
index 0000000..d56c9a1
--- /dev/null
+++ b/src/declarative/qml/rewriter/textwriter.cpp
@@ -0,0 +1,176 @@
+#include "textwriter_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace JavaScript;
+
+TextWriter::TextWriter()
+ :string(0), cursor(0)
+{
+}
+
+static bool overlaps(int posA, int lengthA, int posB, int lengthB) {
+ return (posA < posB + lengthB && posA + lengthA > posB + lengthB)
+ || (posA < posB && posA + lengthA > posB);
+}
+
+bool TextWriter::hasOverlap(int pos, int length)
+{
+ {
+ QListIterator<Replace> i(replaceList);
+ while (i.hasNext()) {
+ const Replace &cmd = i.next();
+ if (overlaps(pos, length, cmd.pos, cmd.length))
+ return true;
+ }
+ }
+ {
+ QListIterator<Move> i(moveList);
+ while (i.hasNext()) {
+ const Move &cmd = i.next();
+ if (overlaps(pos, length, cmd.pos, cmd.length))
+ return true;
+ }
+ return false;
+ }
+}
+
+bool TextWriter::hasMoveInto(int pos, int length)
+{
+ QListIterator<Move> i(moveList);
+ while (i.hasNext()) {
+ const Move &cmd = i.next();
+ if (cmd.to >= pos && cmd.to < pos + length)
+ return true;
+ }
+ return false;
+}
+
+void TextWriter::replace(int pos, int length, const QString &replacement)
+{
+ Q_ASSERT(!hasOverlap(pos, length));
+ Q_ASSERT(!hasMoveInto(pos, length));
+
+ Replace cmd;
+ cmd.pos = pos;
+ cmd.length = length;
+ cmd.replacement = replacement;
+ replaceList += cmd;
+}
+
+void TextWriter::move(int pos, int length, int to)
+{
+ Q_ASSERT(!hasOverlap(pos, length));
+
+ Move cmd;
+ cmd.pos = pos;
+ cmd.length = length;
+ cmd.to = to;
+ moveList += cmd;
+}
+
+void TextWriter::doReplace(const Replace &replace)
+{
+ int diff = replace.replacement.size() - replace.length;
+ {
+ QMutableListIterator<Replace> i(replaceList);
+ while (i.hasNext()) {
+ Replace &c = i.next();
+ if (replace.pos < c.pos)
+ c.pos += diff;
+ else if (replace.pos + replace.length < c.pos + c.length)
+ c.length += diff;
+ }
+ }
+ {
+ QMutableListIterator<Move> i(moveList);
+ while (i.hasNext()) {
+ Move &c = i.next();
+ if (replace.pos < c.pos)
+ c.pos += diff;
+ else if (replace.pos + replace.length < c.pos + c.length)
+ c.length += diff;
+
+ if (replace.pos < c.to)
+ c.to += diff;
+ }
+ }
+
+ if (string) {
+ string->replace(replace.pos, replace.length, replace.replacement);
+ } else if (cursor) {
+ cursor->setPosition(replace.pos);
+ cursor->setPosition(replace.pos + replace.length, QTextCursor::KeepAnchor);
+ cursor->insertText(replace.replacement);
+ }
+}
+
+void TextWriter::doMove(const Move &move)
+{
+ QString text;
+ if (string) {
+ text = string->mid(move.pos, move.length);
+ } else if (cursor) {
+ cursor->setPosition(move.pos);
+ cursor->setPosition(move.pos + move.length, QTextCursor::KeepAnchor);
+ text = cursor->selectedText();
+ }
+
+ Replace cut;
+ cut.pos = move.pos;
+ cut.length = move.length;
+ Replace paste;
+ paste.pos = move.to;
+ paste.length = 0;
+ paste.replacement = text;
+
+ replaceList.append(cut);
+ replaceList.append(paste);
+
+ Replace cmd;
+ while (!replaceList.isEmpty()) {
+ cmd = replaceList.first();
+ replaceList.removeFirst();
+ doReplace(cmd);
+ }
+}
+
+void TextWriter::write(QString *s)
+{
+ string = s;
+ write_helper();
+ string = 0;
+}
+
+void TextWriter::write(QTextCursor *textCursor)
+{
+ cursor = textCursor;
+ write_helper();
+ cursor = 0;
+}
+
+void TextWriter::write_helper()
+{
+ if (cursor)
+ cursor->beginEditBlock();
+ {
+ Replace cmd;
+ while (!replaceList.isEmpty()) {
+ cmd = replaceList.first();
+ replaceList.removeFirst();
+ doReplace(cmd);
+ }
+ }
+ {
+ Move cmd;
+ while (!moveList.isEmpty()) {
+ cmd = moveList.first();
+ moveList.removeFirst();
+ doMove(cmd);
+ }
+ }
+ if (cursor)
+ cursor->endEditBlock();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/qml/rewriter/textwriter_p.h b/src/declarative/qml/rewriter/textwriter_p.h
new file mode 100644
index 0000000..52d18d3
--- /dev/null
+++ b/src/declarative/qml/rewriter/textwriter_p.h
@@ -0,0 +1,60 @@
+#ifndef TEXTWRITER_H
+#define TEXTWRITER_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtGui/QTextCursor>
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+namespace JavaScript {
+
+class TextWriter
+{
+ QString *string;
+ QTextCursor *cursor;
+
+ struct Replace {
+ int pos;
+ int length;
+ QString replacement;
+ };
+
+ QList<Replace> replaceList;
+
+ struct Move {
+ int pos;
+ int length;
+ int to;
+ };
+
+ QList<Move> moveList;
+
+ bool hasOverlap(int pos, int length);
+ bool hasMoveInto(int pos, int length);
+
+ void doReplace(const Replace &replace);
+ void doMove(const Move &move);
+
+ void write_helper();
+
+public:
+ TextWriter();
+
+ void replace(int pos, int length, const QString &replacement);
+ void move(int pos, int length, int to);
+
+ void write(QString *s);
+ void write(QTextCursor *textCursor);
+
+};
+
+} // end of namespace JavaScript
+
+QT_END_NAMESPACE
+QT_END_HEADER
+
+#endif // TEXTWRITER_H