aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/cplusplus/MatchingText.cpp
diff options
context:
space:
mode:
authorNikolai Kosjar <nikolai.kosjar@qt.io>2018-02-05 10:32:04 +0100
committerNikolai Kosjar <nikolai.kosjar@qt.io>2018-02-07 14:22:38 +0000
commite48d8afedd46e631ca96e7f02457ee9591566053 (patch)
tree29e23c35b1ed46dd7bdf7f2b11ab11ff1582199f /src/libs/cplusplus/MatchingText.cpp
parentc020f2448b9e9dde6f0f38912dd9ac0d30d27a27 (diff)
C++: Check previous lines for auto insertion of closing brace
If '{' is typed, we check whether '}' should be auto-inserted. Previously, we did this by only looking at the tokens of the current line. By using the BackwardsScanner we can easily look also at the previous lines, which fixes e.g. namespace N <CURSOR> // type '{' - no '}' should be inserted. Change-Id: Ib2c2989c33f87464431d45facf986bcbb2eff2f8 Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
Diffstat (limited to 'src/libs/cplusplus/MatchingText.cpp')
-rw-r--r--src/libs/cplusplus/MatchingText.cpp129
1 files changed, 64 insertions, 65 deletions
diff --git a/src/libs/cplusplus/MatchingText.cpp b/src/libs/cplusplus/MatchingText.cpp
index 447c91ea591..e6973ee3942 100644
--- a/src/libs/cplusplus/MatchingText.cpp
+++ b/src/libs/cplusplus/MatchingText.cpp
@@ -35,6 +35,7 @@
#include <QDebug>
#include <utils/algorithm.h>
+#include <utils/optional.h>
using namespace CPlusPlus;
@@ -137,50 +138,73 @@ static const Token tokenAtPosition(const Tokens &tokens, const unsigned pos)
return Token();
}
-static int tokenIndexBeforePosition(const Tokens &tokens, unsigned pos)
+static LanguageFeatures languageFeatures()
{
- for (int i = tokens.size() - 1; i >= 0; --i) {
- if (tokens[i].utf16charsBegin() < pos)
- return i;
- }
- return -1;
+ LanguageFeatures features;
+ features.qtEnabled = false;
+ features.qtKeywordsEnabled = false;
+ features.qtMocRunEnabled = false;
+ features.cxx11Enabled = true;
+ features.cxxEnabled = true;
+ features.c99Enabled = true;
+ features.objCEnabled = true;
+
+ return features;
}
-static bool isCursorAtEndOfLineButMaybeBeforeComment(const Tokens &tokens, int pos)
+static Tokens getTokens(const QTextCursor &cursor, int &prevState)
{
- int index = tokenIndexBeforePosition(tokens, uint(pos));
- if (index == -1 || index >= tokens.size())
- return false;
+ SimpleLexer tokenize;
+ tokenize.setLanguageFeatures(languageFeatures());
- do {
- ++index;
- } while (index < tokens.size() && tokens[index].isComment());
+ prevState = BackwardsScanner::previousBlockState(cursor.block()) & 0xFF;
+ return tokenize(cursor.block().text(), prevState);
+}
- return index >= tokens.size();
+static int isEmptyOrWhitespace(const QString &text)
+{
+ return Utils::allOf(text, [](const QChar &c) {return c.isSpace(); });
}
+static bool isCursorAtEndOfLineButMaybeBeforeComment(const QTextCursor &cursor)
+{
+ const QString textAfterCursor = cursor.block().text().mid(cursor.positionInBlock());
+ if (isEmptyOrWhitespace(textAfterCursor))
+ return true;
+
+ SimpleLexer tokenize;
+ tokenize.setLanguageFeatures(languageFeatures());
+ const Tokens tokens = tokenize(textAfterCursor);
+ return Utils::allOf(tokens, [](const Token &token) { return token.isComment(); });
+}
+
+using TokenIndexResult = Utils::optional<int>;
+
// 10.6.1 Attribute syntax and semantics
// This does not handle alignas() since it is not needed for the namespace case.
-static int skipAttributeSpecifierSequence(const Tokens &tokens, int index)
+static TokenIndexResult skipAttributeSpecifierSequence(const BackwardsScanner &tokens, int index)
{
+ if (tokens[index].is(T_EOF_SYMBOL))
+ return {};
+
// [[ attribute-using-prefixopt attribute-list ]]
- if (index >= 1 && tokens[index].is(T_RBRACKET) && tokens[index - 1].is(T_RBRACKET)) {
+ if (tokens[index].is(T_RBRACKET) && tokens[index - 1].is(T_RBRACKET)) {
// Skip everything within [[ ]]
- for (int i = index - 2; i >= 0; --i) {
- if (i >= 1 && tokens[i].is(T_LBRACKET) && tokens[i - 1].is(T_LBRACKET))
+ for (int i = index - 2; !tokens[i].is(T_EOF_SYMBOL); --i) {
+ if (tokens[i].is(T_LBRACKET) && tokens[i - 1].is(T_LBRACKET))
return i - 2;
}
- return -1;
+ return {};
}
return index;
}
-static int skipNamespaceName(const Tokens &tokens, int index)
+static TokenIndexResult skipNamespaceName(const BackwardsScanner &tokens, int index)
{
- if (index >= tokens.size())
- return -1;
+ if (tokens[index].is(T_EOF_SYMBOL))
+ return {};
if (!tokens[index].is(T_IDENTIFIER))
return index;
@@ -189,12 +213,12 @@ static int skipNamespaceName(const Tokens &tokens, int index)
// SomeName
// Some::Nested::Name
bool expectIdentifier = false;
- for (int i = index - 1; i >= 0; --i) {
+ for (int i = index - 1; !tokens[i].is(T_EOF_SYMBOL); --i) {
if (expectIdentifier) {
if (tokens[i].is(T_IDENTIFIER))
expectIdentifier = false;
else
- return -1;
+ return {};
} else if (tokens[i].is(T_COLON_COLON)) {
expectIdentifier = true;
} else {
@@ -206,28 +230,22 @@ static int skipNamespaceName(const Tokens &tokens, int index)
}
// 10.3.1 Namespace definition
-static bool isAfterNamespaceDefinition(const Tokens &tokens, int position)
+static bool isAfterNamespaceDefinition(const BackwardsScanner &tokens, int index)
{
- int index = tokenIndexBeforePosition(tokens, uint(position));
- if (index == -1)
+ if (tokens[index].is(T_EOF_SYMBOL))
return false;
// Handle optional name
- index = skipNamespaceName(tokens, index);
- if (index == -1)
+ TokenIndexResult newIndex = skipNamespaceName(tokens, index);
+ if (!newIndex)
return false;
// Handle optional attribute specifier sequence
- index = skipAttributeSpecifierSequence(tokens, index);
- if (index == -1)
+ newIndex = skipAttributeSpecifierSequence(tokens, newIndex.value());
+ if (!newIndex)
return false;
- return index >= 0 && tokens[index].is(T_NAMESPACE);
-}
-
-static int isEmptyOrWhitespace(const QString &text)
-{
- return Utils::allOf(text, [](const QChar &c) {return c.isSpace(); });
+ return tokens[newIndex.value()].is(T_NAMESPACE);
}
static QTextBlock previousNonEmptyBlock(const QTextBlock &currentBlock)
@@ -271,24 +289,6 @@ static bool allowAutoClosingBraceAtEmptyLine(
&& !trimmedText.endsWith('}');
}
-static Tokens getTokens(const QTextCursor &cursor, int &prevState)
-{
- LanguageFeatures features;
- features.qtEnabled = false;
- features.qtKeywordsEnabled = false;
- features.qtMocRunEnabled = false;
- features.cxx11Enabled = true;
- features.cxxEnabled = true;
- features.c99Enabled = true;
- features.objCEnabled = true;
-
- SimpleLexer tokenize;
- tokenize.setLanguageFeatures(features);
-
- prevState = BackwardsScanner::previousBlockState(cursor.block()) & 0xFF;
- return tokenize(cursor.block().text(), prevState);
-}
-
static QChar firstNonSpace(const QTextCursor &cursor)
{
int position = cursor.position();
@@ -320,21 +320,20 @@ static bool allowAutoClosingBrace(const QTextCursor &cursor,
if (MatchingText::isInCommentHelper(cursor))
return false;
- const QTextBlock block = cursor.block();
- if (isEmptyOrWhitespace(block.text()))
- return allowAutoClosingBraceAtEmptyLine(cursor.block(), isNextIndented);
-
- int prevState;
- const Tokens tokens = getTokens(cursor, prevState);
+ BackwardsScanner tokens(cursor, languageFeatures(), /*maxBlockCount=*/ 5);
+ const int index = tokens.startToken() - 1;
- const Token token = tokenAtPosition(tokens, cursor.positionInBlock());
- if (token.isStringLiteral())
+ if (tokens[index].isStringLiteral())
return false;
- if (isAfterNamespaceDefinition(tokens, cursor.positionInBlock()))
+ if (isAfterNamespaceDefinition(tokens, index))
return false;
- if (isCursorAtEndOfLineButMaybeBeforeComment(tokens, cursor.positionInBlock()))
+ const QTextBlock block = cursor.block();
+ if (isEmptyOrWhitespace(block.text()))
+ return allowAutoClosingBraceAtEmptyLine(cursor.block(), isNextIndented);
+
+ if (isCursorAtEndOfLineButMaybeBeforeComment(cursor))
return !(isNextIndented && isNextIndented(block));
return allowAutoClosingBraceByLookahead(cursor);