Automagically insert matching characters.

This commit is contained in:
Roberto Raggi
2009-09-17 17:57:17 +02:00
parent 245dfe51c6
commit 82b80b9e39
7 changed files with 145 additions and 10 deletions

View File

@@ -32,7 +32,7 @@
using namespace CPlusPlus; using namespace CPlusPlus;
BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, int maxBlockCount) BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, const QString &suffix, int maxBlockCount)
: _offset(0) : _offset(0)
, _blocksTokenized(0) , _blocksTokenized(0)
, _block(cursor.block()) , _block(cursor.block())
@@ -40,6 +40,10 @@ BackwardsScanner::BackwardsScanner(const QTextCursor &cursor, int maxBlockCount)
{ {
_tokenize.setSkipComments(true); _tokenize.setSkipComments(true);
_text = _block.text().left(cursor.position() - cursor.block().position()); _text = _block.text().left(cursor.position() - cursor.block().position());
if (! suffix.isEmpty())
_text += suffix;
_tokens.append(_tokenize(_text, previousBlockState(_block))); _tokens.append(_tokenize(_text, previousBlockState(_block)));
} }

View File

@@ -41,7 +41,9 @@ class CPLUSPLUS_EXPORT BackwardsScanner
enum { MAX_BLOCK_COUNT = 10 }; enum { MAX_BLOCK_COUNT = 10 };
public: public:
BackwardsScanner(const QTextCursor &cursor, int maxBlockCount = MAX_BLOCK_COUNT); BackwardsScanner(const QTextCursor &cursor,
const QString &suffix = QString(),
int maxBlockCount = MAX_BLOCK_COUNT);
int state() const; int state() const;
int startToken() const; int startToken() const;

View File

@@ -153,10 +153,8 @@ QString ExpressionUnderCursor::operator()(const QTextCursor &cursor)
return scanner.text(i, initialSize); return scanner.text(i, initialSize);
} }
int ExpressionUnderCursor::startOfFunctionCall(const QTextCursor &cursor) int ExpressionUnderCursor::startOfFunctionCall(const QTextCursor &cursor) const
{ {
QString text;
BackwardsScanner scanner(cursor); BackwardsScanner scanner(cursor);
int index = scanner.startToken(); int index = scanner.startToken();

View File

@@ -51,7 +51,7 @@ public:
~ExpressionUnderCursor(); ~ExpressionUnderCursor();
QString operator()(const QTextCursor &cursor); QString operator()(const QTextCursor &cursor);
int startOfFunctionCall(const QTextCursor &cursor); int startOfFunctionCall(const QTextCursor &cursor) const;
private: private:
int startOfExpression(BackwardsScanner &tk, int index); int startOfExpression(BackwardsScanner &tk, int index);

View File

@@ -34,17 +34,124 @@
using namespace CPlusPlus; using namespace CPlusPlus;
enum { MAX_NUM_LINES = 400 };
static bool maybeOverrideChar(const QChar &ch)
{
if (ch == QLatin1Char(')')) return true;
else if (ch == QLatin1Char(']')) return true;
else if (ch == QLatin1Char('"')) return true;
else if (ch == QLatin1Char('\'')) return true;
else return false;
}
static bool isCompleteStringLiteral(const BackwardsScanner &tk, int index, int startToken)
{
const QStringRef text = tk.textRef(index, startToken);
if (text.length() < 2)
return false;
else if (text.at(text.length() - 1) == QLatin1Char('"'))
return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly.
return false;
}
static bool isCompleteCharLiteral(const BackwardsScanner &tk, int index, int startToken)
{
const QStringRef text = tk.textRef(index, startToken);
if (text.length() < 2)
return false;
else if (text.at(text.length() - 1) == QLatin1Char('\''))
return text.at(text.length() - 2) != QLatin1Char('\\'); // ### not exactly.
return false;
}
MatchingText::MatchingText() MatchingText::MatchingText()
{ } { }
QString MatchingText::insertMatchingBrace(const QTextCursor &cursor, const QString &textToProcess, int *skippedChars) const
{
*skippedChars = 0;
QTextCursor tc = cursor;
QString text = textToProcess;
const QString blockText = tc.block().text().mid(tc.columnNumber());
const int length = qMin(blockText.length(), textToProcess.length());
for (int i = 0; i < length; ++i) {
const QChar ch1 = blockText.at(i);
const QChar ch2 = textToProcess.at(i);
if (ch1 != ch2)
break;
else if (! maybeOverrideChar(ch1))
break;
++*skippedChars;
}
if (*skippedChars != 0) {
tc.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, *skippedChars);
text = textToProcess.mid(*skippedChars);
}
if (text.isEmpty())
return QString();
BackwardsScanner tk(tc, textToProcess.left(*skippedChars), MAX_NUM_LINES);
const int startToken = tk.startToken();
int index = startToken;
const SimpleToken &token = tk[index - 1];
if (text.at(0) == QLatin1Char('"') && (token.is(T_STRING_LITERAL) || token.is(T_WIDE_STRING_LITERAL))) {
if (text.length() != 1)
qWarning() << Q_FUNC_INFO << "handle event compression";
if (isCompleteStringLiteral(tk, index - 1, startToken))
return QLatin1String("\"");
return QString();
} else if (text.at(0) == QLatin1Char('\'') && (token.is(T_CHAR_LITERAL) || token.is(T_WIDE_CHAR_LITERAL))) {
if (text.length() != 1)
qWarning() << Q_FUNC_INFO << "handle event compression";
if (isCompleteCharLiteral(tk, index - 1, startToken))
return QLatin1String("'");
return QString();
}
QString result;
foreach (const QChar &ch, text) {
if (ch == QLatin1Char('(')) result += ')';
else if (ch == QLatin1Char('[')) result += ']';
else if (ch == QLatin1Char('"')) result += '"';
else if (ch == QLatin1Char('\'')) result += '\'';
}
return result;
}
QString MatchingText::insertParagraphSeparator(const QTextCursor &tc) const QString MatchingText::insertParagraphSeparator(const QTextCursor &tc) const
{ {
BackwardsScanner tk(tc, 400); BackwardsScanner tk(tc, QString(), MAX_NUM_LINES);
int index = tk.startToken(); int index = tk.startToken();
if (tk[index - 1].isNot(T_LBRACE)) if (tk[index - 1].isNot(T_LBRACE))
return QString(); // nothing to do. return QString(); // nothing to do.
const QString textBlock = tc.block().text().mid(tc.columnNumber()).trimmed();
if (! textBlock.isEmpty())
return QString();
--index; // consume the `{' --index; // consume the `{'
const SimpleToken &token = tk[index - 1]; const SimpleToken &token = tk[index - 1];

View File

@@ -41,6 +41,7 @@ class CPLUSPLUS_EXPORT MatchingText
public: public:
MatchingText(); MatchingText();
QString insertMatchingBrace(const QTextCursor &tc, const QString &text, int *skippedChars) const;
QString insertParagraphSeparator(const QTextCursor &tc) const; QString insertParagraphSeparator(const QTextCursor &tc) const;
}; };

View File

@@ -1269,6 +1269,27 @@ bool CPPEditor::isElectricCharacter(const QChar &ch) const
return false; return false;
} }
#if 1
QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &text) const
{
if (!contextAllowsAutoParentheses(cursor))
return QString();
QString autoText;
int skippedChars = 0;
MatchingText matchingText;
autoText = matchingText.insertMatchingBrace(cursor, text, &skippedChars);
if (skippedChars) {
const int pos = cursor.position();
cursor.setPosition(pos + skippedChars);
cursor.setPosition(pos, QTextCursor::KeepAnchor);
}
return autoText;
}
#else
QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &text) const QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &text) const
{ {
bool checkBlockEnd = m_allowSkippingOfBlockEnd; bool checkBlockEnd = m_allowSkippingOfBlockEnd;
@@ -1328,6 +1349,7 @@ QString CPPEditor::autoComplete(QTextCursor &cursor, const QString &text) const
return autoText; return autoText;
} }
#endif
bool CPPEditor::autoBackspace(QTextCursor &cursor) bool CPPEditor::autoBackspace(QTextCursor &cursor)
{ {
@@ -1401,9 +1423,10 @@ bool CPPEditor::contextAllowsAutoParentheses(const QTextCursor &cursor) const
{ {
CPlusPlus::TokenUnderCursor tokenUnderCursor; CPlusPlus::TokenUnderCursor tokenUnderCursor;
const SimpleToken tk = tokenUnderCursor(cursor); const SimpleToken tk = tokenUnderCursor(cursor);
if (tk.isComment() || tk.isLiteral())
if (tk.end() > cursor.position() - cursor.block().position()) if (tk.isComment())
return false; return false;
return true; return true;
} }