Commit 32b960db authored by Francois Ferrand's avatar Francois Ferrand Committed by hjk

C++: support smart splitting of strings.

If 'enter' is pressed while the cursor is in the middle of a string,
the string is ended at the current cursor position, and a new string
is started on the next line.  This makes it very easy to split a long
string onto multiple lines.

In addition, Shift+Enter insert an escape in the string, to continue the
string at the beginning of next line.

A setting can be used to enable or disable this option.

Change-Id: Ia5f3c6989fc00d40d06bc4fe1182fe8b1318f565
Reviewed-by: default avatarFrancois Ferrand <thetypz@gmail.com>
Reviewed-by: default avatarhjk <hjk121@nokiamail.com>
parent 82d9cf41
......@@ -85,6 +85,37 @@ static bool isInCommentHelper(const QTextCursor &cursor, Token *retToken = 0)
return tk.isComment();
}
static bool isInStringHelper(const QTextCursor &cursor, Token *retToken = 0)
{
LanguageFeatures features;
features.qtEnabled = false;
features.qtKeywordsEnabled = false;
features.qtMocRunEnabled = false;
features.cxx11Enabled = true;
features.c99Enabled = true;
SimpleLexer tokenize;
tokenize.setLanguageFeatures(features);
const int prevState = BackwardsScanner::previousBlockState(cursor.block()) & 0xFF;
const QList<Token> tokens = tokenize(cursor.block().text(), prevState);
const unsigned pos = cursor.selectionEnd() - cursor.block().position();
if (tokens.isEmpty() || pos < tokens.first().utf16charsBegin())
return prevState > 0;
if (pos >= tokens.last().utf16charsEnd()) {
const Token tk = tokens.last();
return tk.isStringLiteral() && prevState > 0;
}
Token tk = tokenAtPosition(tokens, pos);
if (retToken)
*retToken = tk;
return tk.isStringLiteral();
}
bool CppAutoCompleter::contextAllowsAutoParentheses(const QTextCursor &cursor,
const QString &textToInsert) const
{
......@@ -124,6 +155,11 @@ bool CppAutoCompleter::isInComment(const QTextCursor &cursor) const
return isInCommentHelper(cursor);
}
bool CppAutoCompleter::isInString(const QTextCursor &cursor) const
{
return isInStringHelper(cursor);
}
QString CppAutoCompleter::insertMatchingBrace(const QTextCursor &cursor,
const QString &text,
QChar la,
......
......@@ -45,6 +45,7 @@ public:
const QString &textToInsert = QString()) const;
virtual bool contextAllowsElectricCharacters(const QTextCursor &cursor) const;
virtual bool isInComment(const QTextCursor &cursor) const;
virtual bool isInString(const QTextCursor &cursor) const;
virtual QString insertMatchingBrace(const QTextCursor &cursor,
const QString &text,
QChar la,
......
......@@ -59,8 +59,10 @@
#include <cpptools/cppworkingcopy.h>
#include <cpptools/symbolfinder.h>
#include <texteditor/completionsettings.h>
#include <texteditor/textdocument.h>
#include <texteditor/textdocumentlayout.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/codeassist/assistproposalitem.h>
#include <texteditor/codeassist/genericproposalmodel.h>
#include <texteditor/codeassist/genericproposal.h>
......@@ -536,12 +538,47 @@ void CppEditorWidget::keyPressEvent(QKeyEvent *e)
if (d->m_localRenaming.handleKeyPressEvent(e))
return;
if (handleStringSplitting(e))
return;
if (d->m_cppDocumentationCommentHelper.handleKeyPressEvent(e))
return;
TextEditorWidget::keyPressEvent(e);
}
bool CppEditorWidget::handleStringSplitting(QKeyEvent *e) const
{
if (!TextEditorSettings::completionSettings().m_autoSplitStrings)
return false;
if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
QTextCursor cursor = textCursor();
if (autoCompleter()->isInString(cursor)) {
cursor.beginEditBlock();
if (cursor.positionInBlock() > 0
&& cursor.block().text().at(cursor.positionInBlock() - 1) == QLatin1Char('\\')) {
// Already escaped: simply go back to line, but do not indent.
cursor.insertText(QLatin1String("\n"));
} else if (e->modifiers() & Qt::ShiftModifier) {
// With 'shift' modifier, escape the end of line character
// and start at beginning of next line.
cursor.insertText(QLatin1String("\\\n"));
} else {
// End the current string, and start a new one on the line, properly indented.
cursor.insertText(QLatin1String("\"\n\""));
textDocument()->autoIndent(cursor);
}
cursor.endEditBlock();
e->accept();
return true;
}
}
return false;
}
void CppEditorWidget::applyFontSettings()
{
// This also makes the document apply font settings
......
......@@ -101,6 +101,7 @@ protected:
bool event(QEvent *e) Q_DECL_OVERRIDE;
void contextMenuEvent(QContextMenuEvent *) Q_DECL_OVERRIDE;
void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE;
bool handleStringSplitting(QKeyEvent *e) const;
void applyFontSettings() Q_DECL_OVERRIDE;
......
......@@ -99,6 +99,7 @@ QWidget *CompletionSettingsPage::widget()
m_page->surroundSelectedText->setChecked(settings.m_surroundingAutoBrackets);
m_page->partiallyComplete->setChecked(settings.m_partiallyComplete);
m_page->spaceAfterFunctionName->setChecked(settings.m_spaceAfterFunctionName);
m_page->autoSplitStrings->setChecked(settings.m_autoSplitStrings);
m_page->enableDoxygenCheckBox->setChecked(m_commentsSettings.m_enableDoxygen);
m_page->generateBriefCheckBox->setChecked(m_commentsSettings.m_generateBrief);
m_page->leadingAsterisksCheckBox->setChecked(m_commentsSettings.m_leadingAsterisks);
......@@ -118,6 +119,7 @@ void CompletionSettingsPage::apply()
settings.m_surroundingAutoBrackets = m_page->surroundSelectedText->isChecked();
settings.m_partiallyComplete = m_page->partiallyComplete->isChecked();
settings.m_spaceAfterFunctionName = m_page->spaceAfterFunctionName->isChecked();
settings.m_autoSplitStrings = m_page->autoSplitStrings->isChecked();
TextEditor::TextEditorSettings::setCompletionSettings(settings);
......
......@@ -17,6 +17,72 @@
<string>Behavior</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="surroundSelectedText">
<property name="toolTip">
<string>When typing a matching character and there is a text selection, instead of removing the selection, surrounds it with the corresponding characters.</string>
</property>
<property name="text">
<string>Surround &amp;text selections</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="2" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>70</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="completionTriggerLabel">
<property name="text">
<string>Activate completion:</string>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="caseSensitivityLabel">
<property name="text">
......@@ -52,23 +118,10 @@
</item>
</widget>
</item>
<item row="0" column="2" colspan="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>70</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="completionTriggerLabel">
<item row="6" column="0">
<widget class="QCheckBox" name="autoSplitStrings">
<property name="text">
<string>Activate completion:</string>
<string>Automatically split strings</string>
</property>
</widget>
</item>
......@@ -91,19 +144,6 @@
</item>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>24</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="partiallyComplete">
<property name="toolTip">
......@@ -130,40 +170,7 @@
</property>
</widget>
</item>
<item row="4" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>30</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="surroundSelectedText">
<property name="toolTip">
<string>When typing a matching character and there is a text selection, instead of removing the selection, surrounds it with the corresponding characters.</string>
</property>
<property name="text">
<string>Surround &amp;text selections</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_4">
......
......@@ -332,6 +332,12 @@ bool AutoCompleter::isInComment(const QTextCursor &cursor) const
return false;
}
bool AutoCompleter::isInString(const QTextCursor &cursor) const
{
Q_UNUSED(cursor);
return false;
}
QString AutoCompleter::insertMatchingBrace(const QTextCursor &cursor,
const QString &text,
QChar la,
......
......@@ -72,6 +72,9 @@ public:
// Returns true if the cursor is inside a comment.
virtual bool isInComment(const QTextCursor &cursor) const;
// Returns true if the cursor is inside a string.
virtual bool isInString(const QTextCursor &cursor) const;
virtual QString insertMatchingBrace(const QTextCursor &cursor, const
QString &text,
QChar la,
......
......@@ -39,6 +39,7 @@ static const char autoInsertBracesKey[] = "AutoInsertBraces";
static const char surroundingAutoBracketsKey[] = "SurroundingAutoBrackets";
static const char partiallyCompleteKey[] = "PartiallyComplete";
static const char spaceAfterFunctionNameKey[] = "SpaceAfterFunctionName";
static const char autoSplitStringsKey[] = "AutoSplitStrings";
using namespace TextEditor;
......@@ -49,6 +50,7 @@ CompletionSettings::CompletionSettings()
, m_surroundingAutoBrackets(true)
, m_partiallyComplete(true)
, m_spaceAfterFunctionName(false)
, m_autoSplitStrings(true)
{
}
......@@ -65,6 +67,7 @@ void CompletionSettings::toSettings(const QString &category, QSettings *s) const
s->setValue(QLatin1String(surroundingAutoBracketsKey), m_surroundingAutoBrackets);
s->setValue(QLatin1String(partiallyCompleteKey), m_partiallyComplete);
s->setValue(QLatin1String(spaceAfterFunctionNameKey), m_spaceAfterFunctionName);
s->setValue(QLatin1String(autoSplitStringsKey), m_autoSplitStrings);
s->endGroup();
}
......@@ -83,6 +86,7 @@ void CompletionSettings::fromSettings(const QString &category, const QSettings *
m_surroundingAutoBrackets = s->value(group + QLatin1String(surroundingAutoBracketsKey), m_surroundingAutoBrackets).toBool();
m_partiallyComplete = s->value(group + QLatin1String(partiallyCompleteKey), m_partiallyComplete).toBool();
m_spaceAfterFunctionName = s->value(group + QLatin1String(spaceAfterFunctionNameKey), m_spaceAfterFunctionName).toBool();
m_autoSplitStrings = s->value(group + QLatin1String(autoSplitStringsKey), m_autoSplitStrings).toBool();
}
bool CompletionSettings::equals(const CompletionSettings &cs) const
......@@ -93,5 +97,6 @@ bool CompletionSettings::equals(const CompletionSettings &cs) const
&& m_surroundingAutoBrackets == cs.m_surroundingAutoBrackets
&& m_partiallyComplete == cs.m_partiallyComplete
&& m_spaceAfterFunctionName == cs.m_spaceAfterFunctionName
&& m_autoSplitStrings == cs.m_autoSplitStrings
;
}
......@@ -70,6 +70,7 @@ public:
bool m_surroundingAutoBrackets;
bool m_partiallyComplete;
bool m_spaceAfterFunctionName;
bool m_autoSplitStrings;
};
inline bool operator==(const CompletionSettings &t1, const CompletionSettings &t2) { return t1.equals(t2); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment