diff --git a/src/libs/qmljs/qmljsscanner.cpp b/src/libs/qmljs/qmljsscanner.cpp index b7f82dd4205bcaf5a5fce91d55b5d151be8cd05c..58604df715d003fa9fa470cac24c1042719e1b10 100644 --- a/src/libs/qmljs/qmljsscanner.cpp +++ b/src/libs/qmljs/qmljsscanner.cpp @@ -123,6 +123,67 @@ static bool isNumberChar(QChar ch) } } +static int findRegExpEnd(const QString &text, int start) +{ + if (start >= text.size() || text.at(start) != QLatin1Char('/')) + return start; + + // find the second / + int index = start + 1; + for (; index < text.length(); ++index) { + const QChar ch = text.at(index); + + if (ch == QLatin1Char('\\')) { + ++index; + } else if (ch == QLatin1Char('[')) { + // find closing ] + for (; index < text.length(); ++index) { + const QChar ch2 = text.at(index); + if (ch2 == QLatin1Char('\\')) { + ++index; + } else if (ch2 == QLatin1Char(']')) + break; + } + if (index >= text.size()) + return text.size(); + } else if (ch == QLatin1Char('/')) + break; + } + if (index >= text.size()) + return text.size(); + ++index; + + // find end of reg exp flags + for (; index < text.size(); ++index) { + const QChar ch = text.at(index); + if (!isIdentifierChar(ch)) + break; + } + + return index; +} + + +static inline int multiLineState(int state) +{ + return state & 0b11; +} + +static inline void setMultiLineState(int *state, int s) +{ + *state = s | (*state & ~0b11); +} + +static inline bool regexpMayFollow(int state) +{ + return state & 0b100; +} + +static inline void setRegexpMayFollow(int *state, bool on) +{ + *state = (on << 2) | (*state & 0b11); +} + QList<Token> Scanner::operator()(const QString &text, int startState) { _state = startState; @@ -132,7 +193,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) int index = 0; - if (_state == MultiLineComment) { + if (multiLineState(_state) == MultiLineComment) { int start = -1; while (index < text.length()) { const QChar ch = text.at(index); @@ -145,7 +206,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) la = text.at(index + 1); if (ch == QLatin1Char('*') && la == QLatin1Char('/')) { - _state = Normal; + setMultiLineState(&_state, Normal); index += 2; break; } else { @@ -155,7 +216,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) if (_scanComments && start != -1) tokens.append(Token(start, index - start, Token::Comment)); - } else if (_state == MultiLineStringDQuote || _state == MultiLineStringSQuote) { + } else if (multiLineState(_state) == MultiLineStringDQuote || multiLineState(_state) == MultiLineStringSQuote) { const QChar quote = (_state == MultiLineStringDQuote ? QLatin1Char('"') : QLatin1Char('\'')); const int start = index; while (index < text.length()) { @@ -170,10 +231,11 @@ QList<Token> Scanner::operator()(const QString &text, int startState) } if (index < text.length()) { ++index; - _state = Normal; + setMultiLineState(&_state, Normal); } if (start < index) tokens.append(Token(start, index - start, Token::String)); + setRegexpMayFollow(&_state, false); } while (index < text.length()) { @@ -192,7 +254,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) } else if (la == QLatin1Char('*')) { const int start = index; index += 2; - _state = MultiLineComment; + setMultiLineState(&_state, MultiLineComment); while (index < text.length()) { const QChar ch = text.at(index); QChar la; @@ -200,7 +262,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) la = text.at(index + 1); if (ch == QLatin1Char('*') && la == QLatin1Char('/')) { - _state = Normal; + setMultiLineState(&_state, Normal); index += 2; break; } else { @@ -209,8 +271,14 @@ QList<Token> Scanner::operator()(const QString &text, int startState) } if (_scanComments) tokens.append(Token(start, index - start, Token::Comment)); + } else if (regexpMayFollow(_state)) { + const int end = findRegExpEnd(text, index); + tokens.append(Token(index, end - index, Token::RegExp)); + index = end; + setRegexpMayFollow(&_state, false); } else { tokens.append(Token(index++, 1, Token::Delimiter)); + setRegexpMayFollow(&_state, true); } break; @@ -235,12 +303,13 @@ QList<Token> Scanner::operator()(const QString &text, int startState) // good one } else { if (quote.unicode() == '"') - _state = MultiLineStringDQuote; + setMultiLineState(&_state, MultiLineStringDQuote); else - _state = MultiLineStringSQuote; + setMultiLineState(&_state, MultiLineStringSQuote); } tokens.append(Token(start, index - start, Token::String)); + setRegexpMayFollow(&_state, false); } break; case '.': @@ -253,42 +322,52 @@ QList<Token> Scanner::operator()(const QString &text, int startState) break; } tokens.append(Token(index++, 1, Token::Dot)); + setRegexpMayFollow(&_state, false); break; case '(': tokens.append(Token(index++, 1, Token::LeftParenthesis)); + setRegexpMayFollow(&_state, true); break; case ')': tokens.append(Token(index++, 1, Token::RightParenthesis)); + setRegexpMayFollow(&_state, false); break; case '[': tokens.append(Token(index++, 1, Token::LeftBracket)); + setRegexpMayFollow(&_state, true); break; case ']': tokens.append(Token(index++, 1, Token::RightBracket)); + setRegexpMayFollow(&_state, false); break; case '{': tokens.append(Token(index++, 1, Token::LeftBrace)); + setRegexpMayFollow(&_state, true); break; case '}': tokens.append(Token(index++, 1, Token::RightBrace)); + setRegexpMayFollow(&_state, false); break; case ';': tokens.append(Token(index++, 1, Token::Semicolon)); + setRegexpMayFollow(&_state, true); break; case ':': tokens.append(Token(index++, 1, Token::Colon)); + setRegexpMayFollow(&_state, true); break; case ',': tokens.append(Token(index++, 1, Token::Comma)); + setRegexpMayFollow(&_state, true); break; case '+': @@ -299,6 +378,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) } else { tokens.append(Token(index++, 1, Token::Delimiter)); } + setRegexpMayFollow(&_state, true); break; default: @@ -312,6 +392,7 @@ QList<Token> Scanner::operator()(const QString &text, int startState) ++index; } while (index < text.length() && isNumberChar(text.at(index))); tokens.append(Token(start, index - start, Token::Number)); + setRegexpMayFollow(&_state, false); } else if (ch.isLetter() || ch == QLatin1Char('_') || ch == QLatin1Char('$')) { const int start = index; do { @@ -322,8 +403,10 @@ QList<Token> Scanner::operator()(const QString &text, int startState) tokens.append(Token(start, index - start, Token::Keyword)); // ### fixme else tokens.append(Token(start, index - start, Token::Identifier)); + setRegexpMayFollow(&_state, false); } else { tokens.append(Token(index++, 1, Token::Delimiter)); + setRegexpMayFollow(&_state, true); } } // end of switch } diff --git a/src/libs/qmljs/qmljsscanner.h b/src/libs/qmljs/qmljsscanner.h index 9c9b43bd18063e40cb6c3c0f21b326a454c75343..777fd1ba0522c063185d68f36e53257284049086 100644 --- a/src/libs/qmljs/qmljsscanner.h +++ b/src/libs/qmljs/qmljsscanner.h @@ -59,7 +59,8 @@ public: Colon, Comma, Dot, - Delimiter + Delimiter, + RegExp }; inline Token(): offset(0), length(0), kind(EndOfFile) {} @@ -82,7 +83,8 @@ public: Normal = 0, MultiLineComment = 1, MultiLineStringDQuote = 2, - MultiLineStringSQuote = 3 + MultiLineStringSQuote = 3, + RegexpMayFollow = 4 // flag that may be combined with the above }; Scanner(); diff --git a/src/plugins/qmljseditor/qmljscompletionassist.cpp b/src/plugins/qmljseditor/qmljscompletionassist.cpp index 6de4e825493a6b44766544aa7c72f30c8633f0a4..3bda6afd8463bfe36c53c2e8078f7471f71dd4bd 100644 --- a/src/plugins/qmljseditor/qmljscompletionassist.cpp +++ b/src/plugins/qmljseditor/qmljscompletionassist.cpp @@ -681,7 +681,7 @@ bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const if (column >= tk.begin() && column <= tk.end()) { if (charBeforeCursor == QLatin1Char('/') && tk.is(Token::String)) return true; // path completion inside string literals - if (tk.is(Token::Comment) || tk.is(Token::String)) + if (tk.is(Token::Comment) || tk.is(Token::String) || tk.is(Token::RegExp)) return false; break; } diff --git a/src/plugins/qmljseditor/qmljshighlighter.cpp b/src/plugins/qmljseditor/qmljshighlighter.cpp index ac3f408aa46398630942d53f0d8f3082cdd84c9d..55b917db357f092ece0975155ff16815ff1bd6e2 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.cpp +++ b/src/plugins/qmljseditor/qmljshighlighter.cpp @@ -116,6 +116,10 @@ void Highlighter::highlightBlock(const QString &text) setFormat(token.offset, token.length, m_formats[CommentFormat]); break; + case Token::RegExp: + setFormat(token.offset, token.length, m_formats[StringFormat]); + break; + case Token::LeftParenthesis: onOpeningParenthesis('(', token.offset, index == 0); break; @@ -229,7 +233,8 @@ void Highlighter::highlightBlock(const QString &text) switch (token.kind) { case Token::Comment: - case Token::String: { + case Token::String: + case Token::RegExp: { int i = token.begin(), e = token.end(); while (i < e) { const QChar ch = text.at(i);