diff --git a/src/libs/cplusplus/BackwardsScanner.cpp b/src/libs/cplusplus/BackwardsScanner.cpp index b0b71ffecdf98e7234c93be90e2e245bd387ac9e..eb0e5c280335c9af1637b3733976819cda027b25 100644 --- a/src/libs/cplusplus/BackwardsScanner.cpp +++ b/src/libs/cplusplus/BackwardsScanner.cpp @@ -38,6 +38,7 @@ BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, const QString &suf , _block(cursor.block()) , _maxBlockCount(maxBlockCount) { + _tokenize.setQtMocRunEnabled(true); _tokenize.setSkipComments(true); _text = _block.text().left(cursor.position() - cursor.block().position()); @@ -52,13 +53,10 @@ BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, const QString &suf int BackwardsScanner::state() const { return _tokenize.state(); } -const QList<SimpleToken> &BackwardsScanner::tokens() const -{ return _tokens; } - -const SimpleToken &BackwardsScanner::LA(int index) const +SimpleToken BackwardsScanner::LA(int index) const { return const_cast<BackwardsScanner *>(this)->fetchToken(_startToken - index); } -const SimpleToken &BackwardsScanner::operator[](int index) const +SimpleToken BackwardsScanner::operator[](int index) const { return const_cast<BackwardsScanner *>(this)->fetchToken(index); } const SimpleToken &BackwardsScanner::fetchToken(int i) @@ -72,16 +70,27 @@ const SimpleToken &BackwardsScanner::fetchToken(int i) } else { ++_blocksTokenized; - QString blockText = _block.text(); + const QString blockText = _block.text(); _text.prepend(blockText); + QList<SimpleToken> adaptedTokens; for (int i = 0; i < _tokens.size(); ++i) { - const SimpleToken &t = _tokens.at(i); - const int position = t.position() + blockText.length(); - adaptedTokens.append(SimpleToken(t.kind(), - position, - t.length(), - _text.midRef(position, t.length()))); + SimpleToken t = _tokens.at(i); + if (i == 0) { + Q_ASSERT(t.followsNewline()); + } + t.setPosition(t.position() + blockText.length()); + t.setText(_text.midRef(t.position(), t.length())); + + if (i == 0) { + Q_ASSERT(t.followsNewline()); + } + + adaptedTokens.append(t); + + if (i == 0) { + Q_ASSERT(adaptedTokens.last().followsNewline()); + } } _tokens = _tokenize(blockText, previousBlockState(_block)); @@ -156,6 +165,17 @@ int BackwardsScanner::startOfMatchingBrace(int index) const --count; --i; } while (count != 0 && tk[i].isNot(T_EOF_SYMBOL)); + } else if (tk[index - 1].is(T_RBRACE)) { + int i = index - 1; + int count = 0; + do { + if (tk[i].is(T_LBRACE)) { + if (! ++count) + return i; + } else if (tk[i].is(T_RBRACE)) + --count; + --i; + } while (count != 0 && tk[i].isNot(T_EOF_SYMBOL)); } else if (tk[index - 1].is(T_GREATER)) { int i = index - 1; int count = 0; @@ -167,7 +187,71 @@ int BackwardsScanner::startOfMatchingBrace(int index) const --count; --i; } while (count != 0 && tk[i].isNot(T_EOF_SYMBOL)); + } else { + Q_ASSERT(0); + } + + return index; +} + +int BackwardsScanner::startOfLine(int index) const +{ + const BackwardsScanner tk(*this); + + forever { + const SimpleToken &tok = tk[index - 1]; + + if (tok.is(T_EOF_SYMBOL)) + break; + else if (tok.followsNewline()) + return index - 1; + + --index; } return index; } + +int BackwardsScanner::startOfBlock(int index) const +{ + const BackwardsScanner tk(*this); + + const int start = index; + + forever { + SimpleToken token = tk[index - 1]; + + if (token.is(T_EOF_SYMBOL)) { + break; + + } else if (token.is(T_GREATER)) { + const int matchingBrace = startOfMatchingBrace(index); + + if (matchingBrace != index && tk[matchingBrace - 1].is(T_TEMPLATE)) + index = matchingBrace; + + } else if (token.is(T_RPAREN) || token.is(T_RBRACKET) || token.is(T_RBRACE)) { + const int matchingBrace = startOfMatchingBrace(index); + + if (matchingBrace != index) + index = matchingBrace; + + } else if (token.is(T_LPAREN) || token.is(T_LBRACKET)) { + break; // unmatched brace + + } else if (token.is(T_LBRACE)) { + return index - 1; + + } + + --index; + } + + return start; +} + +int BackwardsScanner::indentation(int index) const +{ + SimpleToken newline = operator[](startOfLine(index + 1)); + return newline.position(); +} diff --git a/src/libs/cplusplus/BackwardsScanner.h b/src/libs/cplusplus/BackwardsScanner.h index 68d1f047878ff5440b0421d964e45665f613555b..1ce6d3f85c20866c08ba6d965a55a5bf5e1d5738 100644 --- a/src/libs/cplusplus/BackwardsScanner.h +++ b/src/libs/cplusplus/BackwardsScanner.h @@ -55,17 +55,21 @@ public: QStringRef textRef(int begin, int end) const; // 1-based - const SimpleToken &LA(int index) const; + SimpleToken LA(int index) const; // n-la token is [startToken - n] - const SimpleToken &operator[](int index) const; // ### deprecate + SimpleToken operator[](int index) const; // ### deprecate + int indentation(int index) const; + + int startOfLine(int index) const; int startOfMatchingBrace(int index) const; + int startOfBlock(int index) const; + int previousBlockState(const QTextBlock &block) const; private: const SimpleToken &fetchToken(int i); - const QList<SimpleToken> &tokens() const; private: QList<SimpleToken> _tokens; diff --git a/src/libs/cplusplus/SimpleLexer.cpp b/src/libs/cplusplus/SimpleLexer.cpp index 7e60f76115bc7e8a27adc30612dac06fa91fb0f3..8c023d860366f35df73bd41d6fb40a192cc4b16f 100644 --- a/src/libs/cplusplus/SimpleLexer.cpp +++ b/src/libs/cplusplus/SimpleLexer.cpp @@ -35,6 +35,17 @@ using namespace CPlusPlus; +SimpleToken::SimpleToken(const Token &token, const QStringRef &text) + : _kind(token.f.kind) + , _flags(0) + , _position(token.begin()) + , _length(token.f.length) + , _text(text) +{ + f._whitespace = token.f.whitespace; + f._newline = token.f.newline; +} + bool SimpleToken::isLiteral() const { return _kind >= T_FIRST_LITERAL && _kind <= T_LAST_LITERAL; @@ -60,6 +71,11 @@ bool SimpleToken::isObjCAtKeyword() const return _kind >= T_FIRST_OBJC_AT_KEYWORD && _kind <= T_LAST_OBJC_AT_KEYWORD; } +const char *SimpleToken::name() const +{ + return Token::name(_kind); +} + SimpleLexer::SimpleLexer() : _lastState(0), _skipComments(false), @@ -113,6 +129,7 @@ QList<SimpleToken> SimpleLexer::operator()(const QString &text, int state) Lexer lex(firstChar, lastChar); lex.setQtMocRunEnabled(_qtMocRunEnabled); lex.setObjCEnabled(_objCEnabled); + lex.setStartWithNewline(true); if (! _skipComments) lex.setScanCommentTokens(true); @@ -122,17 +139,26 @@ QList<SimpleToken> SimpleLexer::operator()(const QString &text, int state) bool inPreproc = false; + bool first = true; + for (;;) { Token tk; lex(&tk); if (tk.is(T_EOF_SYMBOL)) break; - SimpleToken simpleTk; - simpleTk._kind = int(tk.f.kind); - simpleTk._position = int(lex.tokenOffset()); - simpleTk._length = int(lex.tokenLength()); - simpleTk._text = text.midRef(simpleTk._position, simpleTk._length); + Q_ASSERT(lex.tokenOffset() == tk.begin()); + Q_ASSERT(lex.tokenLength() == tk.f.length); + + QStringRef spell = text.midRef(lex.tokenOffset(), lex.tokenLength()); + SimpleToken simpleTk(tk, spell); + + if (first) { + first = false; + + Q_ASSERT(tk.f.newline); + Q_ASSERT(simpleTk.followsNewline()); + } lex.setScanAngleStringLiteralTokens(false); diff --git a/src/libs/cplusplus/SimpleLexer.h b/src/libs/cplusplus/SimpleLexer.h index 3f15193ee036e0b76050a22eb64734766687d979..0775eccb1c9162bc7c31ec2c29757aafecc948ba 100644 --- a/src/libs/cplusplus/SimpleLexer.h +++ b/src/libs/cplusplus/SimpleLexer.h @@ -37,21 +37,18 @@ namespace CPlusPlus { class SimpleLexer; +class Token; class CPLUSPLUS_EXPORT SimpleToken { public: - SimpleToken(int kind, int position, int length, const QStringRef &text) - : _kind(kind) - , _position(position) - , _length(length) - , _text(text) - { } + SimpleToken(const Token &token, const QStringRef &text); SimpleToken() - : _kind(0), - _position(0), - _length(0) + : _kind(0) + , _flags(0) + , _position(0) + , _length(0) { } inline int kind() const @@ -72,6 +69,12 @@ public: inline QStringRef text() const { return _text; } + inline bool followsNewline() const + { return f._newline; } + + inline bool followsWhitespace() const + { return f._whitespace; } + inline bool is(int k) const { return _kind == k; } inline bool isNot(int k) const { return _kind != k; } @@ -81,8 +84,27 @@ public: bool isComment() const; bool isObjCAtKeyword() const; + const char *name() const; + + // internal + inline void setPosition(int position) + { _position = position; } + + // internal + inline void setText(const QStringRef &text) + { _text = text; } + public: - int _kind; + short _kind; + union { + short _flags; + + struct { + short _newline: 1; + short _whitespace: 1; + } f; + }; + int _position; int _length; QStringRef _text; diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp index 2a681e858c514331a8ba32488909df83f2acea07..4a4c8dff1abe69d6432d8f25f39cedf7f7e8c7c0 100644 --- a/src/plugins/cppeditor/cppeditor.cpp +++ b/src/plugins/cppeditor/cppeditor.cpp @@ -53,6 +53,7 @@ #include <cplusplus/TokenUnderCursor.h> #include <cplusplus/TypeOfExpression.h> #include <cplusplus/MatchingText.h> +#include <cplusplus/BackwardsScanner.h> #include <cpptools/cppmodelmanagerinterface.h> #include <coreplugin/icore.h> @@ -1263,6 +1264,7 @@ bool CPPEditor::isElectricCharacter(const QChar &ch) const { if (ch == QLatin1Char('{') || ch == QLatin1Char('}') || + ch == QLatin1Char(':') || ch == QLatin1Char('#')) { return true; } @@ -1473,6 +1475,40 @@ void CPPEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedCha const TextEditor::TextBlockIterator begin(doc->begin()); const TextEditor::TextBlockIterator end(block.next()); + QTextCursor tc(block); + tc.movePosition(QTextCursor::EndOfBlock); + + BackwardsScanner tk(tc, QString(), 400); + const int tokenCount = tk.startToken(); + const int indentSize = tabSettings().m_indentSize; + + if (tokenCount != 0) { + const SimpleToken firstToken = tk[0]; + + if (firstToken.is(T_COLON)) { + const int indent = tk.indentation(-1) + // indentation of the previous newline + indentSize; + tabSettings().indentLine(block, indent); + return; + } else if ((firstToken.is(T_PUBLIC) || firstToken.is(T_PROTECTED) || firstToken.is(T_PRIVATE) || + firstToken.is(T_Q_SIGNALS) || firstToken.is(T_Q_SLOTS)) && tk[1].is(T_COLON)) { + const int startOfBlock = tk.startOfBlock(0); + if (startOfBlock != 0) { + const int indent = tk.indentation(startOfBlock); + tabSettings().indentLine(block, indent); + return; + } + } else if (firstToken.is(T_CASE) || firstToken.is(T_DEFAULT)) { + const int startOfBlock = tk.startOfBlock(0); + if (startOfBlock != 0) { + const int indent = tk.indentation(startOfBlock); + tabSettings().indentLine(block, indent); + return; + } + return; + } + } + indentCPPBlock(tabSettings(), block, begin, end, typedChar); }