diff --git a/src/plugins/texteditor/behaviorsettingswidget.cpp b/src/plugins/texteditor/behaviorsettingswidget.cpp
index f5dc751eb6a..fe7e983fade 100644
--- a/src/plugins/texteditor/behaviorsettingswidget.cpp
+++ b/src/plugins/texteditor/behaviorsettingswidget.cpp
@@ -101,6 +101,8 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent)
this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
connect(d->m_ui.cleanIndentation, &QAbstractButton::clicked,
this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
+ connect(d->m_ui.skipTrailingWhitespace, &QAbstractButton::clicked,
+ this, &BehaviorSettingsWidget::slotStorageSettingsChanged);
connect(d->m_ui.mouseHiding, &QAbstractButton::clicked,
this, &BehaviorSettingsWidget::slotBehaviorSettingsChanged);
connect(d->m_ui.mouseNavigation, &QAbstractButton::clicked,
@@ -190,6 +192,9 @@ void BehaviorSettingsWidget::setAssignedStorageSettings(const StorageSettings &s
d->m_ui.inEntireDocument->setChecked(storageSettings.m_inEntireDocument);
d->m_ui.cleanIndentation->setChecked(storageSettings.m_cleanIndentation);
d->m_ui.addFinalNewLine->setChecked(storageSettings.m_addFinalNewLine);
+ d->m_ui.skipTrailingWhitespace->setChecked(storageSettings.m_skipTrailingWhitespace);
+ d->m_ui.ignoreFileTypes->setText(storageSettings.m_ignoreFileTypes);
+ d->m_ui.ignoreFileTypes->setEnabled(d->m_ui.skipTrailingWhitespace->isChecked());
}
void BehaviorSettingsWidget::assignedStorageSettings(StorageSettings *storageSettings) const
@@ -198,6 +203,8 @@ void BehaviorSettingsWidget::assignedStorageSettings(StorageSettings *storageSet
storageSettings->m_inEntireDocument = d->m_ui.inEntireDocument->isChecked();
storageSettings->m_cleanIndentation = d->m_ui.cleanIndentation->isChecked();
storageSettings->m_addFinalNewLine = d->m_ui.addFinalNewLine->isChecked();
+ storageSettings->m_skipTrailingWhitespace = d->m_ui.skipTrailingWhitespace->isChecked();
+ storageSettings->m_ignoreFileTypes = d->m_ui.ignoreFileTypes->text();
}
void BehaviorSettingsWidget::updateConstrainTooltipsBoxTooltip() const
@@ -273,6 +280,10 @@ void BehaviorSettingsWidget::slotStorageSettingsChanged()
{
StorageSettings settings;
assignedStorageSettings(&settings);
+
+ bool ignoreFileTypesEnabled = d->m_ui.cleanWhitespace->isChecked() && d->m_ui.skipTrailingWhitespace->isChecked();
+ d->m_ui.ignoreFileTypes->setEnabled(ignoreFileTypesEnabled);
+
emit storageSettingsChanged(settings);
}
diff --git a/src/plugins/texteditor/behaviorsettingswidget.ui b/src/plugins/texteditor/behaviorsettingswidget.ui
index c36bb9ae47b..d6d9fed5c60 100644
--- a/src/plugins/texteditor/behaviorsettingswidget.ui
+++ b/src/plugins/texteditor/behaviorsettingswidget.ui
@@ -7,7 +7,7 @@
0
0
801
- 547
+ 693
@@ -169,13 +169,72 @@ Specifies how backspace interacts with indentation.
Cleanups Upon Saving
- -
-
+
-
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ false
+
+
+ For the file patterns listed, do not trim trailing whitespace.
+
+
+ Skip clean whitespace for file types:
+
+
+ false
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+ List of wildcard-aware file patterns, separated by commas or semicolons.
+
+
+
+
+
+
+
+
+
+ -
+
+
+ true
+
- Removes trailing whitespace upon saving.
+ Always writes a newline character at the end of the file.
- &Clean whitespace
+ &Ensure newline at end of file
@@ -195,6 +254,29 @@ Specifies how backspace interacts with indentation.
+ -
+
+
+ Removes trailing whitespace upon saving.
+
+
+ &Clean whitespace
+
+
+
+ -
+
+
+ false
+
+
+ Corrects leading whitespace according to tab settings.
+
+
+ Clean indentation
+
+
+
-
@@ -214,29 +296,6 @@ Specifies how backspace interacts with indentation.
- -
-
-
- false
-
-
- Corrects leading whitespace according to tab settings.
-
-
- Clean indentation
-
-
-
- -
-
-
- Always writes a newline character at the end of the file.
-
-
- &Ensure newline at end of file
-
-
-
@@ -521,5 +580,37 @@ Specifies how backspace interacts with indentation.
+
+ cleanWhitespace
+ toggled(bool)
+ skipTrailingWhitespace
+ setEnabled(bool)
+
+
+ 530
+ 48
+
+
+ 548
+ 144
+
+
+
+
+ cleanWhitespace
+ toggled(bool)
+ ignoreFileTypes
+ setEnabled(bool)
+
+
+ 530
+ 48
+
+
+ 556
+ 177
+
+
+
diff --git a/src/plugins/texteditor/storagesettings.cpp b/src/plugins/texteditor/storagesettings.cpp
index a1c4645b0cf..2053ffa440c 100644
--- a/src/plugins/texteditor/storagesettings.cpp
+++ b/src/plugins/texteditor/storagesettings.cpp
@@ -27,6 +27,7 @@
#include
+#include
#include
#include
@@ -36,13 +37,18 @@ static const char cleanWhitespaceKey[] = "cleanWhitespace";
static const char inEntireDocumentKey[] = "inEntireDocument";
static const char addFinalNewLineKey[] = "addFinalNewLine";
static const char cleanIndentationKey[] = "cleanIndentation";
+static const char skipTrailingWhitespaceKey[] = "skipTrailingWhitespace";
+static const char ignoreFileTypesKey[] = "ignoreFileTypes";
static const char groupPostfix[] = "StorageSettings";
+static const char defaultTrailingWhitespaceBlacklist[] = "*.md, *.MD, Makefile";
StorageSettings::StorageSettings()
- : m_cleanWhitespace(true),
+ : m_ignoreFileTypes(defaultTrailingWhitespaceBlacklist),
+ m_cleanWhitespace(true),
m_inEntireDocument(false),
m_addFinalNewLine(true),
- m_cleanIndentation(true)
+ m_cleanIndentation(true),
+ m_skipTrailingWhitespace(true)
{
}
@@ -63,6 +69,8 @@ void StorageSettings::toMap(const QString &prefix, QVariantMap *map) const
map->insert(prefix + QLatin1String(inEntireDocumentKey), m_inEntireDocument);
map->insert(prefix + QLatin1String(addFinalNewLineKey), m_addFinalNewLine);
map->insert(prefix + QLatin1String(cleanIndentationKey), m_cleanIndentation);
+ map->insert(prefix + QLatin1String(skipTrailingWhitespaceKey), m_skipTrailingWhitespace);
+ map->insert(prefix + QLatin1String(ignoreFileTypesKey), m_ignoreFileTypes.toLatin1().data());
}
void StorageSettings::fromMap(const QString &prefix, const QVariantMap &map)
@@ -75,6 +83,42 @@ void StorageSettings::fromMap(const QString &prefix, const QVariantMap &map)
map.value(prefix + QLatin1String(addFinalNewLineKey), m_addFinalNewLine).toBool();
m_cleanIndentation =
map.value(prefix + QLatin1String(cleanIndentationKey), m_cleanIndentation).toBool();
+ m_skipTrailingWhitespace =
+ map.value(prefix + QLatin1String(skipTrailingWhitespaceKey), m_skipTrailingWhitespace).toBool();
+ m_ignoreFileTypes =
+ map.value(prefix + QLatin1String(ignoreFileTypesKey), m_ignoreFileTypes).toString();
+}
+
+bool StorageSettings::removeTrailingWhitespace(const QString &fileName) const
+{
+ // if the user has elected not to trim trailing whitespace altogether, then
+ // early out here
+ if (!m_skipTrailingWhitespace) {
+ return true;
+ }
+
+ const QString ignoreFileTypesRegExp(R"(\s*((?>\*\.)?[\w\d\.\*]+)[,;]?\s*)");
+
+ // use the ignore-files regex to extract the specified file patterns
+ QRegularExpression re(ignoreFileTypesRegExp);
+ QRegularExpressionMatchIterator iter = re.globalMatch(m_ignoreFileTypes);
+
+ while (iter.hasNext()) {
+ QRegularExpressionMatch match = iter.next();
+ QString pattern = match.captured(1);
+
+ QString wildcardRegExp = QRegularExpression::wildcardToRegularExpression(pattern);
+ QRegularExpression patternRegExp(wildcardRegExp);
+ QRegularExpressionMatch patternMatch = patternRegExp.match(fileName);
+ if (patternMatch.hasMatch()) {
+ // if the filename has a pattern we want to ignore, then we need to return
+ // false ("don't remove trailing whitespace")
+ return false;
+ }
+ }
+
+ // the supplied pattern does not match, so we want to remove trailing whitespace
+ return true;
}
bool StorageSettings::equals(const StorageSettings &ts) const
@@ -82,7 +126,9 @@ bool StorageSettings::equals(const StorageSettings &ts) const
return m_addFinalNewLine == ts.m_addFinalNewLine
&& m_cleanWhitespace == ts.m_cleanWhitespace
&& m_inEntireDocument == ts.m_inEntireDocument
- && m_cleanIndentation == ts.m_cleanIndentation;
+ && m_cleanIndentation == ts.m_cleanIndentation
+ && m_skipTrailingWhitespace == ts.m_skipTrailingWhitespace
+ && m_ignoreFileTypes == ts.m_ignoreFileTypes;
}
} // namespace TextEditor
diff --git a/src/plugins/texteditor/storagesettings.h b/src/plugins/texteditor/storagesettings.h
index ffe7823f0fd..cd9c176b597 100644
--- a/src/plugins/texteditor/storagesettings.h
+++ b/src/plugins/texteditor/storagesettings.h
@@ -46,12 +46,17 @@ public:
void toMap(const QString &prefix, QVariantMap *map) const;
void fromMap(const QString &prefix, const QVariantMap &map);
+ // calculated based on boolean setting plus file type blacklist examination
+ bool removeTrailingWhitespace(const QString &filePattern) const;
+
bool equals(const StorageSettings &ts) const;
+ QString m_ignoreFileTypes;
bool m_cleanWhitespace;
bool m_inEntireDocument;
bool m_addFinalNewLine;
bool m_cleanIndentation;
+ bool m_skipTrailingWhitespace;
};
inline bool operator==(const StorageSettings &t1, const StorageSettings &t2) { return t1.equals(t2); }
diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp
index 319f77a3978..0f3ef7a05f5 100644
--- a/src/plugins/texteditor/textdocument.cpp
+++ b/src/plugins/texteditor/textdocument.cpp
@@ -622,7 +622,7 @@ bool TextDocument::save(QString *errorString, const QString &saveFileName, bool
cursor.movePosition(QTextCursor::Start);
if (d->m_storageSettings.m_cleanWhitespace)
- cleanWhitespace(cursor, d->m_storageSettings.m_cleanIndentation, d->m_storageSettings.m_inEntireDocument);
+ cleanWhitespace(cursor, d->m_storageSettings);
if (d->m_storageSettings.m_addFinalNewLine)
ensureFinalNewLine(cursor);
cursor.endEditBlock();
@@ -883,14 +883,22 @@ void TextDocument::cleanWhitespace(const QTextCursor &cursor)
QTextCursor copyCursor = cursor;
copyCursor.setVisualNavigation(false);
copyCursor.beginEditBlock();
- cleanWhitespace(copyCursor, true, true);
+
+ cleanWhitespace(copyCursor, d->m_storageSettings);
+
if (!hasSelection)
ensureFinalNewLine(copyCursor);
+
copyCursor.endEditBlock();
}
-void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument)
+void TextDocument::cleanWhitespace(QTextCursor &cursor, const StorageSettings &storageSettings)
{
+ if (!d->m_storageSettings.m_cleanWhitespace)
+ return;
+
+ const QString fileName(filePath().fileName());
+
auto documentLayout = qobject_cast(d->m_document.documentLayout());
Q_ASSERT(cursor.visualNavigation() == false);
@@ -901,7 +909,7 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b
QVector blocks;
while (block.isValid() && block != end) {
- if (inEntireDocument || block.revision() != documentLayout->lastSaveRevision)
+ if (storageSettings.m_inEntireDocument || block.revision() != documentLayout->lastSaveRevision)
blocks.append(block);
block = block.next();
}
@@ -914,9 +922,12 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b
foreach (block, blocks) {
QString blockText = block.text();
- currentTabSettings.removeTrailingWhitespace(cursor, block);
+
+ if (storageSettings.removeTrailingWhitespace(fileName))
+ currentTabSettings.removeTrailingWhitespace(cursor, block);
+
const int indent = indentations[block.blockNumber()];
- if (cleanIndentation && !currentTabSettings.isIndentationClean(block, indent)) {
+ if (storageSettings.m_cleanIndentation && !currentTabSettings.isIndentationClean(block, indent)) {
cursor.setPosition(block.position());
int firstNonSpace = currentTabSettings.firstNonSpace(blockText);
if (firstNonSpace == blockText.length()) {
@@ -934,6 +945,9 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b
void TextDocument::ensureFinalNewLine(QTextCursor& cursor)
{
+ if (!d->m_storageSettings.m_addFinalNewLine)
+ return;
+
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
bool emptyFile = !cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h
index 447edd0e508..35e6b334eff 100644
--- a/src/plugins/texteditor/textdocument.h
+++ b/src/plugins/texteditor/textdocument.h
@@ -169,7 +169,7 @@ protected:
private:
OpenResult openImpl(QString *errorString, const QString &fileName, const QString &realFileName,
bool reload);
- void cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, bool inEntireDocument);
+ void cleanWhitespace(QTextCursor &cursor, const StorageSettings &storageSettings);
void ensureFinalNewLine(QTextCursor &cursor);
void modificationChanged(bool modified);
void updateLayout() const;