Fixed the incremental scanner for the JS/QML highlighter.

This commit is contained in:
Erik Verbruggen
2009-10-07 16:31:08 +02:00
parent 9a7e02946a
commit d241ef831f
5 changed files with 256 additions and 320 deletions

View File

@@ -31,92 +31,28 @@
#include <QtCore/QSet>
#include <QtCore/QtAlgorithms>
#include <QtCore/QDebug>
using namespace SharedTools;
QSet<QString> QScriptHighlighter::m_keywords;
QScriptHighlighter::QScriptHighlighter(bool duiEnabled, QTextDocument *parent):
QSyntaxHighlighter(parent),
m_scanner(m_duiEnabled),
m_duiEnabled(duiEnabled)
{
setFormats(defaultFormats());
QVector<QTextCharFormat> rc;
rc.resize(NumFormats);
rc[NumberFormat].setForeground(Qt::blue);
rc[StringFormat].setForeground(Qt::darkGreen);
rc[TypeFormat].setForeground(Qt::darkMagenta);
rc[KeywordFormat].setForeground(Qt::darkYellow);
rc[LabelFormat].setForeground(Qt::darkRed);
rc[CommentFormat].setForeground(Qt::red); rc[CommentFormat].setFontItalic(true);
rc[PreProcessorFormat].setForeground(Qt::darkBlue);
rc[VisualWhitespace].setForeground(Qt::lightGray); // for debug: rc[VisualWhitespace].setBackground(Qt::red);
setFormats(rc);
m_scanner.setKeywords(keywords());
}
QSet<QString> QScriptHighlighter::keywords()
{
if (m_keywords.isEmpty()) {
m_keywords << QLatin1String("Infinity");
m_keywords << QLatin1String("NaN");
m_keywords << QLatin1String("abstract");
m_keywords << QLatin1String("boolean");
m_keywords << QLatin1String("break");
m_keywords << QLatin1String("byte");
m_keywords << QLatin1String("case");
m_keywords << QLatin1String("catch");
m_keywords << QLatin1String("char");
m_keywords << QLatin1String("class");
m_keywords << QLatin1String("const");
m_keywords << QLatin1String("constructor");
m_keywords << QLatin1String("continue");
m_keywords << QLatin1String("debugger");
m_keywords << QLatin1String("default");
m_keywords << QLatin1String("delete");
m_keywords << QLatin1String("do");
m_keywords << QLatin1String("double");
m_keywords << QLatin1String("else");
m_keywords << QLatin1String("enum");
m_keywords << QLatin1String("export");
m_keywords << QLatin1String("extends");
m_keywords << QLatin1String("false");
m_keywords << QLatin1String("final");
m_keywords << QLatin1String("finally");
m_keywords << QLatin1String("float");
m_keywords << QLatin1String("for");
m_keywords << QLatin1String("function");
m_keywords << QLatin1String("goto");
m_keywords << QLatin1String("if");
m_keywords << QLatin1String("implements");
m_keywords << QLatin1String("import");
m_keywords << QLatin1String("in");
m_keywords << QLatin1String("instanceof");
m_keywords << QLatin1String("int");
m_keywords << QLatin1String("interface");
m_keywords << QLatin1String("long");
m_keywords << QLatin1String("native");
m_keywords << QLatin1String("new");
m_keywords << QLatin1String("package");
m_keywords << QLatin1String("private");
m_keywords << QLatin1String("protected");
m_keywords << QLatin1String("public");
m_keywords << QLatin1String("return");
m_keywords << QLatin1String("short");
m_keywords << QLatin1String("static");
m_keywords << QLatin1String("super");
m_keywords << QLatin1String("switch");
m_keywords << QLatin1String("synchronized");
m_keywords << QLatin1String("this");
m_keywords << QLatin1String("throw");
m_keywords << QLatin1String("throws");
m_keywords << QLatin1String("transient");
m_keywords << QLatin1String("true");
m_keywords << QLatin1String("try");
m_keywords << QLatin1String("typeof");
m_keywords << QLatin1String("undefined");
m_keywords << QLatin1String("var");
m_keywords << QLatin1String("void");
m_keywords << QLatin1String("volatile");
m_keywords << QLatin1String("while");
m_keywords << QLatin1String("with");
}
return m_keywords;
}
bool QScriptHighlighter::isDuiEnabled() const
{ return m_duiEnabled; }
@@ -125,20 +61,19 @@ void QScriptHighlighter::highlightBlock(const QString &text)
m_scanner(onBlockStart(), text);
QTextCharFormat emptyFormat;
foreach (const QScriptIncrementalScanner::Token &token, m_scanner.tokens()) {
int lastEnd = 0;
const QList<QScriptIncrementalScanner::Token> tokens = m_scanner.tokens();
for (int i = 0; i < tokens.size(); ++i) {
const QScriptIncrementalScanner::Token token = tokens.at(i);
if (token.offset != lastEnd)
setFormat(lastEnd, token.offset - lastEnd, m_formats[VisualWhitespace]);
switch (token.kind) {
case QScriptIncrementalScanner::Token::Keyword:
setFormat(token.offset, token.length, m_formats[KeywordFormat]);
break;
case QScriptIncrementalScanner::Token::Type:
setFormat(token.offset, token.length, m_formats[TypeFormat]);
break;
case QScriptIncrementalScanner::Token::Label:
setFormat(token.offset, token.length, m_formats[LabelFormat]);
break;
case QScriptIncrementalScanner::Token::String:
setFormat(token.offset, token.length, m_formats[StringFormat]);
break;
@@ -175,44 +110,123 @@ void QScriptHighlighter::highlightBlock(const QString &text)
onClosingParenthesis(']', token.offset);
break;
case QScriptIncrementalScanner::Token::PreProcessor:
setFormat(token.offset, token.length, m_formats[PreProcessorFormat]);
case QScriptIncrementalScanner::Token::Identifier:
if (m_duiEnabled && (i + 1 != tokens.size()) && tokens.at(i + 1).kind == QScriptIncrementalScanner::Token::Colon) {
setFormat(token.offset, token.length, m_formats[LabelFormat]);
} else {
const QChar c = text.at(token.offset);
if (m_duiEnabled && c.isUpper() || !m_duiEnabled && c == QLatin1Char('Q'))
setFormat(token.offset, token.length, m_formats[TypeFormat]);
else
setFormat(token.offset, token.length, emptyFormat);
}
break;
case QScriptIncrementalScanner::Token::Empty:
default:
case QScriptIncrementalScanner::Token::Colon:
if (m_duiEnabled && i > 0 && tokens.at(i - 1).kind == QScriptIncrementalScanner::Token::Identifier)
setFormat(token.offset, token.length, m_formats[LabelFormat]);
else
setFormat(token.offset, token.length, emptyFormat);
break;
case QScriptIncrementalScanner::Token::Operator:
case QScriptIncrementalScanner::Token::Dot:
setFormat(token.offset, token.length, emptyFormat);
break;
default:
break;
}
lastEnd = token.end();
}
onBlockEnd(m_scanner.endState(), m_scanner.firstNonSpace());
}
const int firstNonSpace = m_scanner.firstNonSpace();
if (firstNonSpace > lastEnd)
setFormat(lastEnd, firstNonSpace - lastEnd, m_formats[VisualWhitespace]);
else if (text.length() > lastEnd)
setFormat(lastEnd, text.length() - lastEnd, m_formats[VisualWhitespace]);
const QVector<QTextCharFormat> &QScriptHighlighter::defaultFormats()
{
static QVector<QTextCharFormat> rc;
if (rc.empty()) {
rc.resize(NumFormats);
rc[NumberFormat].setForeground(Qt::blue);
rc[StringFormat].setForeground(Qt::darkGreen);
rc[TypeFormat].setForeground(Qt::darkMagenta);
rc[KeywordFormat].setForeground(Qt::darkYellow);
rc[LabelFormat].setForeground(Qt::darkRed);
rc[CommentFormat].setForeground(Qt::red);
rc[CommentFormat].setFontItalic(true);
rc[PreProcessorFormat].setForeground(Qt::darkBlue);
rc[VisualWhitespace].setForeground(Qt::lightGray);
}
return rc;
onBlockEnd(m_scanner.endState(), firstNonSpace);
}
void QScriptHighlighter::setFormats(const QVector<QTextCharFormat> &s)
{
Q_ASSERT(s.size() == NumFormats);
qCopy(s.constBegin(), s.constEnd(), m_formats);
}
QSet<QString> QScriptHighlighter::keywords()
{
QSet<QString> keywords;
keywords << QLatin1String("Infinity");
keywords << QLatin1String("NaN");
keywords << QLatin1String("abstract");
keywords << QLatin1String("boolean");
keywords << QLatin1String("break");
keywords << QLatin1String("byte");
keywords << QLatin1String("case");
keywords << QLatin1String("catch");
keywords << QLatin1String("char");
keywords << QLatin1String("class");
keywords << QLatin1String("const");
keywords << QLatin1String("constructor");
keywords << QLatin1String("continue");
keywords << QLatin1String("debugger");
keywords << QLatin1String("default");
keywords << QLatin1String("delete");
keywords << QLatin1String("do");
keywords << QLatin1String("double");
keywords << QLatin1String("else");
keywords << QLatin1String("enum");
keywords << QLatin1String("export");
keywords << QLatin1String("extends");
keywords << QLatin1String("false");
keywords << QLatin1String("final");
keywords << QLatin1String("finally");
keywords << QLatin1String("float");
keywords << QLatin1String("for");
keywords << QLatin1String("function");
keywords << QLatin1String("goto");
keywords << QLatin1String("if");
keywords << QLatin1String("implements");
keywords << QLatin1String("import");
keywords << QLatin1String("in");
keywords << QLatin1String("instanceof");
keywords << QLatin1String("int");
keywords << QLatin1String("interface");
keywords << QLatin1String("long");
keywords << QLatin1String("native");
keywords << QLatin1String("new");
keywords << QLatin1String("package");
keywords << QLatin1String("private");
keywords << QLatin1String("protected");
keywords << QLatin1String("public");
keywords << QLatin1String("return");
keywords << QLatin1String("short");
keywords << QLatin1String("static");
keywords << QLatin1String("super");
keywords << QLatin1String("switch");
keywords << QLatin1String("synchronized");
keywords << QLatin1String("this");
keywords << QLatin1String("throw");
keywords << QLatin1String("throws");
keywords << QLatin1String("transient");
keywords << QLatin1String("true");
keywords << QLatin1String("try");
keywords << QLatin1String("typeof");
keywords << QLatin1String("undefined");
keywords << QLatin1String("var");
keywords << QLatin1String("void");
keywords << QLatin1String("volatile");
keywords << QLatin1String("while");
keywords << QLatin1String("with");
return keywords;
}
int QScriptHighlighter::onBlockStart()
{
int state = 0;

View File

@@ -54,12 +54,11 @@ public:
// MS VC 6 compatible, still.
void setFormats(const QVector<QTextCharFormat> &s);
static const QVector<QTextCharFormat> &defaultFormats();
QTextCharFormat labelTextCharFormat() const
{ return m_formats[LabelFormat]; }
static QSet<QString> keywords();
QSet<QString> keywords();
protected:
// The functions are notified whenever parentheses are encountered.
@@ -74,7 +73,6 @@ protected:
QScriptIncrementalScanner m_scanner;
private:
static QSet<QString> m_keywords;
bool m_duiEnabled;
QTextCharFormat m_formats[NumFormats];
};

View File

@@ -4,8 +4,7 @@
using namespace SharedTools;
QScriptIncrementalScanner::QScriptIncrementalScanner(bool duiEnabled):
m_duiEnabled(duiEnabled)
QScriptIncrementalScanner::QScriptIncrementalScanner()
{
reset();
}
@@ -30,9 +29,7 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
InputNumber,
InputAsterix,
InputSlash,
InputParen,
InputSpace,
InputHash,
InputQuotation,
InputApostrophe,
InputSep,
@@ -42,13 +39,13 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
// states
enum {
StateStandard,
StateCommentStart1,
StateCCommentStart2,
StateCppCommentStart2,
StateCComment,
StateCppComment,
StateCCommentEnd1,
StateCCommentEnd2,
StateCommentStart1, // '/'
StateCCommentStart2, // '*' after a '/'
StateCppCommentStart2, // '/' after a '/'
StateCComment, // after a "/*"
StateCppComment, // after a "//"
StateCCommentEnd1, // '*' in a CppComment
StateCCommentEnd2, // '/' after a '*' in a CppComment
StateStringStart,
StateString,
StateStringEnd,
@@ -56,32 +53,28 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
StateString2,
StateString2End,
StateNumber,
StatePreProcessor,
NumStates
};
static const uchar table[NumStates][NumInputs] = {
{ StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStandard
{ StateStandard, StateNumber, StateCCommentStart2, StateCppCommentStart2, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCommentStart1
{ StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentStart2
{ StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // CppCommentStart2
{ StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCComment
{ StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // StateCppComment
{ StateCComment, StateCComment, StateCCommentEnd1, StateCCommentEnd2, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentEnd1
{ StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCCommentEnd2
{ StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateStringStart
{ StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateString
{ StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStringEnd
{ StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2Start
{ StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2
{ StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateString2End
{ StateNumber, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateNumber
{ StatePreProcessor, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard } // StatePreProcessor
// InputAlpha InputNumber InputAsterix InputSlash InputSpace InputQuotation InputApostrophe InputSep
{ StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStringStart, StateString2Start, StateStandard }, // StateStandard
{ StateStandard, StateNumber, StateCCommentStart2, StateCppCommentStart2, StateStandard, StateStringStart, StateString2Start, StateStandard }, // StateCommentStart1
{ StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentStart2
{ StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // StateCppCommentStart2
{ StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCComment
{ StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // StateCppComment
{ StateCComment, StateCComment, StateCCommentEnd1, StateCCommentEnd2, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentEnd1
{ StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStringStart, StateString2Start, StateStandard }, // StateCCommentEnd2
{ StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateStringStart
{ StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateString
{ StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStringStart, StateString2Start, StateStandard }, // StateStringEnd
{ StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2Start
{ StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2
{ StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStringStart, StateString2Start, StateStandard }, // StateString2End
{ StateNumber, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStringStart, StateString2Start, StateStandard } // StateNumber
};
QString buffer;
buffer.reserve(text.length());
int state = startState;
if (text.isEmpty()) {
blockEnd(state, 0);
@@ -96,7 +89,6 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
static const QString alphabeth = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
static const QString mathChars = QString::fromLatin1("xXeE");
static const QString numbers = QString::fromLatin1("0123456789");
bool questionMark = false;
QChar lastChar;
int firstNonSpace = -1;
@@ -104,13 +96,11 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
forever {
const QChar qc = text.at(i);
bool lookAtBinding = false;
const char c = qc.toLatin1();
if (lastWasBackSlash) {
input = InputSep;
} else {
const char c = qc.toLatin1();
switch (c) {
case '*':
input = InputAsterix;
@@ -118,34 +108,6 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
case '/':
input = InputSlash;
break;
case '(': case '[': case '{':
input = InputParen;
if (state == StateStandard
|| state == StateNumber
|| state == StatePreProcessor
|| state == StateCCommentEnd2
|| state == StateCCommentEnd1
|| state == StateString2End
|| state == StateStringEnd
)
openingParenthesis(c, i);
break;
case ')': case ']': case '}':
input = InputParen;
if (state == StateStandard
|| state == StateNumber
|| state == StatePreProcessor
|| state == StateCCommentEnd2
|| state == StateCCommentEnd1
|| state == StateString2End
|| state == StateStringEnd
) {
closingParenthesis(c, i);
}
break;
case '#':
input = InputHash;
break;
case '"':
input = InputQuotation;
break;
@@ -166,45 +128,14 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
input = InputNumber;
}
break;
case ':': {
input = InputSep;
QChar nextChar = ' ';
if (i < text.length() - 1)
nextChar = text.at(i + 1);
if (state == StateStandard && !questionMark && lastChar != ':' && nextChar != ':') {
int start = i - 1;
// skip white spaces
for (; start != -1; --start) {
if (! text.at(start).isSpace())
break;
}
int lastNonSpace = start + 1;
for (; start != -1; --start) {
const QChar ch = text.at(start);
if (! (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.')))
break;
}
++start;
lookAtBinding = true;
if (m_duiEnabled && text.midRef(start, lastNonSpace - start) == QLatin1String("id")) {
setFormat(start, i - start, Token::Keyword);
} else {
setFormat(start, i - start, Token::Label);
}
}
case '.':
if (state == StateNumber)
input = InputNumber;
else
input = InputSep;
break;
}
default: {
if (!questionMark && qc == QLatin1Char('?'))
questionMark = true;
if (qc.isLetter() || qc == QLatin1Char('_'))
if (qc.isLetter() || c == '_')
input = InputAlpha;
else
input = InputSep;
@@ -219,23 +150,20 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
lastNonSpace = i;
}
lastWasBackSlash = !lastWasBackSlash && qc == QLatin1Char('\\');
if (input == InputAlpha)
buffer += qc;
lastWasBackSlash = !lastWasBackSlash && c == '\\';
state = table[state][input];
switch (state) {
case StateStandard: {
setFormat(i, 1, Token::Empty);
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
if (!buffer.isEmpty() && input != InputAlpha ) {
if (! lookAtBinding)
highlightKeyword(i, buffer);
buffer.clear();
if (input == InputAlpha ) {
insertIdentifier(i);
} else if (input == InputSep || input == InputAsterix) {
insertCharToken(i, c);
}
break;
@@ -243,103 +171,82 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
case StateCommentStart1:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = true;
buffer.resize(0);
break;
case StateCCommentStart2:
setFormat(i - 1, 2, Token::Comment);
makeLastStandard = false;
buffer.resize(0);
insertComment(i - 1, 2);
break;
case StateCppCommentStart2:
setFormat(i - 1, 2, Token::Comment);
insertComment(i - 1, 2);
makeLastStandard = false;
buffer.resize(0);
break;
case StateCComment:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Comment);
buffer.resize(0);
insertComment(i, 1);
break;
case StateCppComment:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Comment);
buffer.resize(0);
insertComment(i, 1);
break;
case StateCCommentEnd1:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Comment);
buffer.resize(0);
insertComment(i, 1);
break;
case StateCCommentEnd2:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Comment);
buffer.resize(0);
insertComment(i, 1);
break;
case StateStringStart:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Empty);
buffer.resize(0);
insertString(i);
break;
case StateString:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::String);
buffer.resize(0);
insertString(i);
break;
case StateStringEnd:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Empty);
buffer.resize(0);
insertString(i);
break;
case StateString2Start:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Empty);
buffer.resize(0);
insertString(i);
break;
case StateString2:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::String);
buffer.resize(0);
insertString(i);
break;
case StateString2End:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat(i, 1, Token::Empty);
buffer.resize(0);
insertString(i);
break;
case StateNumber:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
insertCharToken(i - 1, text.at(i - 1).toAscii());
makeLastStandard = false;
setFormat( i, 1, Token::Number);
buffer.resize(0);
break;
case StatePreProcessor:
if (makeLastStandard)
setFormat(i - 1, 1, Token::Empty);
makeLastStandard = false;
setFormat(i, 1, Token::PreProcessor);
buffer.resize(0);
insertNumber(i);
break;
}
@@ -349,7 +256,7 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
break;
}
highlightKeyword(text.length(), buffer);
scanForKeywords(text);
if (state == StateCComment
|| state == StateCCommentEnd1
@@ -363,63 +270,63 @@ void QScriptIncrementalScanner::operator()(int startState, const QString &text)
blockEnd(state, firstNonSpace);
}
void QScriptIncrementalScanner::highlightKeyword(int currentPos, const QString &buffer)
void QScriptIncrementalScanner::insertToken(int start, int length, Token::Kind kind, bool forceNewToken)
{
if (buffer.isEmpty())
return;
if ((m_duiEnabled && buffer.at(0).isUpper()) || (! m_duiEnabled && buffer.at(0) == QLatin1Char('Q'))) {
setFormat(currentPos - buffer.length(), buffer.length(), Token::Type);
if (m_tokens.isEmpty() || forceNewToken) {
m_tokens.append(Token(start, length, kind));
} else {
if (m_keywords.contains(buffer))
setFormat(currentPos - buffer.length(), buffer.length(), Token::Keyword);
Token &lastToken(m_tokens.last());
if (lastToken.kind == kind && lastToken.end() == start) {
lastToken.length += 1;
} else {
m_tokens.append(Token(start, length, kind));
}
}
}
void QScriptIncrementalScanner::openingParenthesis(char c, int i)
void QScriptIncrementalScanner::insertCharToken(int start, const char c)
{
Token::Kind kind;
switch (c) {
case '(':
kind = Token::LeftParenthesis;
break;
case '!':
case '<':
case '>':
case '+':
case '-':
case '*':
case '/':
case '%': kind = Token::Operator; break;
case '[':
kind = Token::LeftBracket;
break;
case ';': kind = Token::Semicolon; break;
case ':': kind = Token::Colon; break;
case ',': kind = Token::Comma; break;
case '.': kind = Token::Dot; break;
case '{':
kind = Token::LeftBrace;
break;
case '(': kind = Token::LeftParenthesis; break;
case ')': kind = Token::RightParenthesis; break;
case '{': kind = Token::LeftBrace; break;
case '}': kind = Token::RightBrace; break;
case '[': kind = Token::LeftBracket; break;
case ']': kind = Token::RightBracket; break;
default:
return;
default: kind = Token::Identifier; break;
}
m_tokens.append(Token(i, 1, kind));
insertToken(start, 1, kind, true);
}
void QScriptIncrementalScanner::closingParenthesis(char c, int i)
void QScriptIncrementalScanner::scanForKeywords(const QString &text)
{
Token::Kind kind;
for (int i = 0; i < m_tokens.length(); ++i) {
Token &t(m_tokens[i]);
switch (c) {
case ')':
kind = Token::RightParenthesis;
break;
if (t.kind != Token::Identifier)
continue;
case ']':
kind = Token::RightBracket;
break;
case '}':
kind = Token::RightBrace;
break;
default:
return;
const QString id = text.mid(t.offset, t.length);
if (m_keywords.contains(id))
t.kind = Token::Keyword;
}
m_tokens.append(Token(i, 1, kind));
}

View File

@@ -15,10 +15,8 @@ public:
int offset;
int length;
enum Kind {
Empty,
Keyword,
Type,
Label,
Identifier,
String,
Comment,
Number,
@@ -28,18 +26,23 @@ public:
RightBrace,
LeftBracket,
RightBracket,
PreProcessor
Operator,
Semicolon,
Colon,
Comma,
Dot
} kind;
Token(int o, int l, Kind k): offset(o), length(l), kind(k) {}
inline Token(int o, int l, Kind k): offset(o), length(l), kind(k) {}
inline int end() const { return offset + length; }
};
public:
QScriptIncrementalScanner(bool duiEnabled = false);
QScriptIncrementalScanner();
virtual ~QScriptIncrementalScanner();
void setKeywords(const QSet<QString> &keywords)
{ m_keywords = keywords; }
void setKeywords(const QSet<QString> keywords)
{ m_keywords = keywords;; }
void reset();
@@ -57,15 +60,20 @@ public:
private:
void blockEnd(int state, int firstNonSpace)
{ m_endState = state; m_firstNonSpace = firstNonSpace; }
void setFormat(int start, int count, Token::Kind kind)
{ m_tokens.append(Token(start, count, kind)); }
void highlightKeyword(int currentPos, const QString &buffer);
void openingParenthesis(char c, int i);
void closingParenthesis(char c, int i);
void insertString(int start)
{ insertToken(start, 1, Token::String, false); }
void insertComment(int start, int length)
{ insertToken(start, length, Token::Comment, false); }
void insertCharToken(int start, const char c);
void insertIdentifier(int start)
{ insertToken(start, 1, Token::Identifier, false); }
void insertNumber(int start)
{ insertToken(start, 1, Token::Number, false); }
void insertToken(int start, int length, Token::Kind kind, bool forceNewToken);
void scanForKeywords(const QString &text);
private:
QSet<QString> m_keywords;
bool m_duiEnabled;
int m_endState;
int m_firstNonSpace;
QList<QScriptIncrementalScanner::Token> m_tokens;

View File

@@ -33,13 +33,22 @@
#include <QMainWindow>
#include <QApplication>
QString presetText = "import Qt 4.6\n"
"\n"
"Item {\n"
" id: Zoo\n"
" width: 1 + -1*3\n"
"}\n";
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow mw;
QTextEdit *textEdit = new QTextEdit;
new SharedTools::QScriptHighlighter(textEdit->document());
if (!presetText.isEmpty())
textEdit->setText(presetText);
new SharedTools::QScriptHighlighter(true, textEdit->document());
mw.setCentralWidget(textEdit);
mw.show();
return app.exec();