diff options
author | David Schulz <david.schulz@theqtcompany.com> | 2016-01-13 14:32:23 +0100 |
---|---|---|
committer | David Schulz <david.schulz@theqtcompany.com> | 2016-02-24 12:03:12 +0000 |
commit | 9aa51d4857702c22d359014356d06c9d92677904 (patch) | |
tree | 79d73774ac0541e65fbe03e44592a43fc95ba152 /src | |
parent | bc921b46a2946294b467e1d2bcafb147ee77361a (diff) |
Editor: Fix whitespace cleaning.
Task-number: QTCREATORBUG-7994
Change-Id: I6c197ccc3a148555018e8f8184d116c88d7ea400
Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
Diffstat (limited to 'src')
22 files changed, 349 insertions, 203 deletions
diff --git a/src/plugins/android/javaindenter.cpp b/src/plugins/android/javaindenter.cpp index 5b867928fa2..21c99609add 100644 --- a/src/plugins/android/javaindenter.cpp +++ b/src/plugins/android/javaindenter.cpp @@ -48,40 +48,36 @@ bool JavaIndenter::isElectricCharacter(const QChar &ch) const } void JavaIndenter::indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings) + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) { - // At beginning: Leave as is. - if (block == doc->begin()) - return; - - const int tabsize = tabSettings.m_indentSize; + Q_UNUSED(doc); + int indent = indentFor(block, tabSettings); + if (typedChar == QLatin1Char('}')) + indent -= tabSettings.m_indentSize; + tabSettings.indentLine(block, qMax(0, indent)); +} +int JavaIndenter::indentFor(const QTextBlock &block, + const TextEditor::TabSettings &tabSettings) +{ QTextBlock previous = block.previous(); + if (!previous.isValid()) + return 0; + QString previousText = previous.text(); while (previousText.trimmed().isEmpty()) { previous = previous.previous(); - if (previous == doc->begin()) - return; + if (!previous.isValid()) + return 0; previousText = previous.text(); } - int adjust = 0; - if (previousText.contains(QLatin1Char('{'))) - adjust = tabsize; + int indent = tabSettings.indentationColumn(previousText); - if (block.text().contains(QLatin1Char('}')) || typedChar == QLatin1Char('}')) - adjust += -tabsize; + int adjust = previousText.count(QLatin1Char('{')) - previousText.count(QLatin1Char('}')); + adjust *= tabSettings.m_indentSize; - // Count the indentation of the previous line. - int i = 0; - while (i < previousText.size()) { - if (!previousText.at(i).isSpace()) { - tabSettings.indentLine(block, tabSettings.columnAt(previousText, i) - + adjust); - break; - } - ++i; - } + return qMax(0, indent + adjust); } diff --git a/src/plugins/android/javaindenter.h b/src/plugins/android/javaindenter.h index 46b5ccdddb1..9be0b47a954 100644 --- a/src/plugins/android/javaindenter.h +++ b/src/plugins/android/javaindenter.h @@ -34,16 +34,18 @@ class JavaIndenter : public TextEditor::Indenter { public: JavaIndenter(); - virtual ~JavaIndenter(); + ~JavaIndenter() override; - bool isElectricCharacter(const QChar &ch) const; + bool isElectricCharacter(const QChar &ch) const override; - virtual void indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings); + void indentBlock(QTextDocument *doc, + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) override; + + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; }; -} -} +} // namespace Internal +} // namespace Android #endif // JAVAINDENTER_H diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp index 9c582313466..882f0277a67 100644 --- a/src/plugins/cmakeprojectmanager/cmakeindenter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.cpp @@ -98,34 +98,29 @@ static int paranthesesLevel(const QString &line) return -1; } -void CMakeIndenter::indentBlock(QTextDocument *doc, const QTextBlock &block, const QChar &typedChar, const TextEditor::TabSettings &tabSettings) +int CMakeIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) { - Q_UNUSED(doc) - Q_UNUSED(typedChar) - QTextBlock previousBlock = block.previous(); // find the next previous block that is non-empty (contains non-whitespace characters) while (previousBlock.isValid() && lineIsEmpty(previousBlock.text())) previousBlock = previousBlock.previous(); - if (previousBlock.isValid()) { - const QString previousLine = previousBlock.text(); - const QString currentLine = block.text(); - int indentation = tabSettings.indentationColumn(previousLine); + if (!previousBlock.isValid()) + return 0; - if (lineStartsBlock(previousLine)) - indentation += tabSettings.m_indentSize; - if (lineEndsBlock(currentLine)) - indentation = qMax(0, indentation - tabSettings.m_indentSize); + const QString previousLine = previousBlock.text(); + const QString currentLine = block.text(); + int indentation = tabSettings.indentationColumn(previousLine); - // increase/decrease/keep the indentation level depending on if we have more opening or closing parantheses - indentation = qMax(0, indentation + tabSettings.m_indentSize * paranthesesLevel(previousLine)); + if (lineStartsBlock(previousLine)) + indentation += tabSettings.m_indentSize; + if (lineEndsBlock(currentLine)) + indentation = qMax(0, indentation - tabSettings.m_indentSize); - tabSettings.indentLine(block, indentation); - } else { - // First line in whole document - tabSettings.indentLine(block, 0); - } + // increase/decrease/keep the indentation level depending on if we have more opening or closing parantheses + return qMax(0, indentation + tabSettings.m_indentSize * paranthesesLevel(previousLine)); } } // namespace Internal } // namespace CMakeProjectManager + + diff --git a/src/plugins/cmakeprojectmanager/cmakeindenter.h b/src/plugins/cmakeprojectmanager/cmakeindenter.h index 3b2b2a27b4a..a47bdcced5f 100644 --- a/src/plugins/cmakeprojectmanager/cmakeindenter.h +++ b/src/plugins/cmakeprojectmanager/cmakeindenter.h @@ -36,7 +36,8 @@ class CMAKE_EXPORT CMakeIndenter : public TextEditor::Indenter { public: bool isElectricCharacter(const QChar &ch) const override; - void indentBlock(QTextDocument *doc, const QTextBlock &block, const QChar &typedChar, const TextEditor::TabSettings &tabSettings) override; + + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; }; } // namespace Internal diff --git a/src/plugins/cpptools/cppqtstyleindenter.cpp b/src/plugins/cpptools/cppqtstyleindenter.cpp index ea46e242ff6..e1596c60c69 100644 --- a/src/plugins/cpptools/cppqtstyleindenter.cpp +++ b/src/plugins/cpptools/cppqtstyleindenter.cpp @@ -168,6 +168,18 @@ void CppQtStyleIndenter::invalidateCache(QTextDocument *doc) formatter.invalidateCache(doc); } +int CppQtStyleIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) +{ + QtStyleCodeFormatter codeFormatter(tabSettings, codeStyleSettings()); + + codeFormatter.updateStateUntil(block); + int indent; + int padding; + codeFormatter.indentFor(block, &indent, &padding); + + return indent; +} + CppCodeStyleSettings CppQtStyleIndenter::codeStyleSettings() const { if (m_cppCodeStylePreferences) diff --git a/src/plugins/cpptools/cppqtstyleindenter.h b/src/plugins/cpptools/cppqtstyleindenter.h index 7c77affb4d1..eb3196043ab 100644 --- a/src/plugins/cpptools/cppqtstyleindenter.h +++ b/src/plugins/cpptools/cppqtstyleindenter.h @@ -43,21 +43,22 @@ class CPPTOOLS_EXPORT CppQtStyleIndenter : public TextEditor::Indenter { public: CppQtStyleIndenter(); - virtual ~CppQtStyleIndenter(); + ~CppQtStyleIndenter() override; - virtual bool isElectricCharacter(const QChar &ch) const; - virtual void indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings); + bool isElectricCharacter(const QChar &ch) const override; + void indentBlock(QTextDocument *doc, + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) override; - virtual void indent(QTextDocument *doc, - const QTextCursor &cursor, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings); + void indent(QTextDocument *doc, + const QTextCursor &cursor, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) override; - virtual void setCodeStylePreferences(TextEditor::ICodeStylePreferences *preferences); - virtual void invalidateCache(QTextDocument *doc); + void setCodeStylePreferences(TextEditor::ICodeStylePreferences *preferences) override; + void invalidateCache(QTextDocument *doc) override; + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; private: CppCodeStyleSettings codeStyleSettings() const; CppCodeStylePreferences *m_cppCodeStylePreferences; diff --git a/src/plugins/glsleditor/glslindenter.cpp b/src/plugins/glsleditor/glslindenter.cpp index fd4698ac6c3..33aa8470649 100644 --- a/src/plugins/glsleditor/glslindenter.cpp +++ b/src/plugins/glsleditor/glslindenter.cpp @@ -111,5 +111,18 @@ void GlslIndenter::indent(QTextDocument *doc, } } +int GlslIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) +{ + CppTools::QtStyleCodeFormatter codeFormatter(tabSettings, + CppTools::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings()); + + codeFormatter.updateStateUntil(block); + int indent; + int padding; + codeFormatter.indentFor(block, &indent, &padding); + + return indent; +} + } // namespace Internal } // namespace GlslEditor diff --git a/src/plugins/glsleditor/glslindenter.h b/src/plugins/glsleditor/glslindenter.h index 98f93feebfd..98ba0002cac 100644 --- a/src/plugins/glsleditor/glslindenter.h +++ b/src/plugins/glsleditor/glslindenter.h @@ -47,6 +47,8 @@ public: const QTextCursor &cursor, const QChar &typedChar, const TextEditor::TabSettings &tabSettings); + + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; }; } // namespace Internal diff --git a/src/plugins/pythoneditor/tools/pythonindenter.cpp b/src/plugins/pythoneditor/tools/pythonindenter.cpp index 1d09f9f9805..819772c74cf 100644 --- a/src/plugins/pythoneditor/tools/pythonindenter.cpp +++ b/src/plugins/pythoneditor/tools/pythonindenter.cpp @@ -32,9 +32,6 @@ namespace PythonEditor { -// Tab size hardcoded as PEP8 style guide requires, but can be moved to settings -static const int TAB_SIZE = 4; - PythonIndenter::PythonIndenter() { m_jumpKeywords << QLatin1String("return") @@ -59,39 +56,21 @@ bool PythonIndenter::isElectricCharacter(const QChar &ch) const return (ch == QLatin1Char(':')); } -/** - * @brief Indents one block (i.e. one line) of code - * @param doc Unused - * @param block Block that represents line - * @param typedChar Unused - * @param tabSettings An IDE tabulation settings - * - * Usually this function called once when you begin new line of code by pressing - * Enter. If Indenter reimplements indent() function, than indentBlock() may be - * called in other cases. - */ -void PythonIndenter::indentBlock(QTextDocument *document, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &settings) +int PythonIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) { - Q_UNUSED(document); - Q_UNUSED(typedChar); QTextBlock previousBlock = block.previous(); - if (previousBlock.isValid()) { - QString previousLine = previousBlock.text(); - int indentation = settings.indentationColumn(previousLine); - - if (isElectricLine(previousLine)) - indentation += TAB_SIZE; - else - indentation = qMax<int>(0, indentation + getIndentDiff(previousLine)); - - settings.indentLine(block, indentation); - } else { - // First line in whole document - settings.indentLine(block, 0); - } + if (!previousBlock.isValid()) + return 0; + + QString previousLine = previousBlock.text(); + int indentation = tabSettings.indentationColumn(previousLine); + + if (isElectricLine(previousLine)) + indentation += tabSettings.m_indentSize; + else + indentation = qMax<int>(0, indentation + getIndentDiff(previousLine, tabSettings)); + + return indentation; } /// @return True if electric character is last non-space character at given string @@ -109,13 +88,14 @@ bool PythonIndenter::isElectricLine(const QString &line) const } /// @return negative indent diff if previous line breaks control flow branch -int PythonIndenter::getIndentDiff(const QString &previousLine) const +int PythonIndenter::getIndentDiff(const QString &previousLine, + const TextEditor::TabSettings &tabSettings) const { Internal::Scanner sc(previousLine.constData(), previousLine.length()); forever { Internal::FormatToken tk = sc.read(); if ((tk.format() == Internal::Format_Keyword) && m_jumpKeywords.contains(sc.value(tk))) - return -TAB_SIZE; + return -tabSettings.m_indentSize; if (tk.format() != Internal::Format_Whitespace) break; } diff --git a/src/plugins/pythoneditor/tools/pythonindenter.h b/src/plugins/pythoneditor/tools/pythonindenter.h index faf73f4ac51..6846aa1ba81 100644 --- a/src/plugins/pythoneditor/tools/pythonindenter.h +++ b/src/plugins/pythoneditor/tools/pythonindenter.h @@ -35,17 +35,16 @@ class PythonIndenter : public TextEditor::Indenter { public: PythonIndenter(); - virtual ~PythonIndenter(); + virtual ~PythonIndenter() override; - bool isElectricCharacter(const QChar &ch) const; - void indentBlock(QTextDocument *document, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &settings); + bool isElectricCharacter(const QChar &ch) const override; + + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; protected: bool isElectricLine(const QString &line) const; - int getIndentDiff(const QString &previousLine) const; + int getIndentDiff(const QString &previousLine, + const TextEditor::TabSettings &tabSettings) const; private: QStringList m_jumpKeywords; diff --git a/src/plugins/qmljstools/qmljsindenter.cpp b/src/plugins/qmljstools/qmljsindenter.cpp index e9b7ec5972d..71abf832b96 100644 --- a/src/plugins/qmljstools/qmljsindenter.cpp +++ b/src/plugins/qmljstools/qmljsindenter.cpp @@ -58,13 +58,13 @@ void Indenter::indentBlock(QTextDocument *doc, { Q_UNUSED(doc) - QmlJSTools::CreatorCodeFormatter codeFormatter(tabSettings); - - codeFormatter.updateStateUntil(block); - const int depth = codeFormatter.indentFor(block); + const int depth = indentFor(block, tabSettings); if (depth == -1) return; + QmlJSTools::CreatorCodeFormatter codeFormatter(tabSettings); + codeFormatter.updateStateUntil(block); + if (isElectricCharacter(typedChar)) { // only reindent the current line when typing electric characters if the // indent is the same it would be if the line were empty @@ -81,3 +81,12 @@ void Indenter::invalidateCache(QTextDocument *doc) QmlJSTools::CreatorCodeFormatter codeFormatter; codeFormatter.invalidateCache(doc); } + + +int Indenter::indentFor(const QTextBlock &block, + const TextEditor::TabSettings &tabSettings) +{ + QmlJSTools::CreatorCodeFormatter codeFormatter(tabSettings); + codeFormatter.updateStateUntil(block); + return codeFormatter.indentFor(block); +} diff --git a/src/plugins/qmljstools/qmljsindenter.h b/src/plugins/qmljstools/qmljsindenter.h index 9ded32e53cf..3a7ced9fd0a 100644 --- a/src/plugins/qmljstools/qmljsindenter.h +++ b/src/plugins/qmljstools/qmljsindenter.h @@ -37,14 +37,16 @@ class QMLJSTOOLS_EXPORT Indenter : public TextEditor::Indenter { public: Indenter(); - virtual ~Indenter(); - - virtual bool isElectricCharacter(const QChar &ch) const; - virtual void indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings); - virtual void invalidateCache(QTextDocument *doc); + ~Indenter() override; + + bool isElectricCharacter(const QChar &ch) const override; + void indentBlock(QTextDocument *doc, + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &tabSettings) override; + void invalidateCache(QTextDocument *doc) override; + + int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings) override; }; } // Internal diff --git a/src/plugins/texteditor/indenter.cpp b/src/plugins/texteditor/indenter.cpp index 57573270aba..29e9f8e2a2c 100644 --- a/src/plugins/texteditor/indenter.cpp +++ b/src/plugins/texteditor/indenter.cpp @@ -25,6 +25,7 @@ #include "indenter.h" #include "tabsettings.h" +#include "textdocumentlayout.h" #include <QTextDocument> #include <QTextCursor> @@ -48,9 +49,11 @@ void Indenter::indentBlock(QTextDocument *doc, const TabSettings &tabSettings) { Q_UNUSED(doc); - Q_UNUSED(block); Q_UNUSED(typedChar); - Q_UNUSED(tabSettings); + const int indent = indentFor(block, tabSettings); + if (indent < 0) + return; + tabSettings.indentLine(block, indent); } void Indenter::indent(QTextDocument *doc, @@ -108,3 +111,10 @@ void Indenter::setCodeStylePreferences(ICodeStylePreferences *) void Indenter::invalidateCache(QTextDocument *) { } + +int Indenter::indentFor(const QTextBlock &block, const TabSettings &tabSettings) +{ + Q_UNUSED(block) + Q_UNUSED(tabSettings) + return -1; +} diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index d41bb020964..8f5093c9dd2 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -68,6 +68,8 @@ public: virtual void setCodeStylePreferences(ICodeStylePreferences *preferences); virtual void invalidateCache(QTextDocument *doc); + + virtual int indentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings); }; } // namespace TextEditor diff --git a/src/plugins/texteditor/normalindenter.cpp b/src/plugins/texteditor/normalindenter.cpp index cc824be0303..d1956d83d89 100644 --- a/src/plugins/texteditor/normalindenter.cpp +++ b/src/plugins/texteditor/normalindenter.cpp @@ -28,14 +28,6 @@ #include <QTextDocument> -using namespace TextEditor; - -NormalIndenter::NormalIndenter() -{} - -NormalIndenter::~NormalIndenter() -{} - // Indent a text block based on previous line. // Simple text paragraph layout: // aaaa aaaa @@ -55,31 +47,21 @@ NormalIndenter::~NormalIndenter() // for additional block being inserted. It might be possible // to do in 2 steps (indenting/wrapping)} // -void NormalIndenter::indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TabSettings &tabSettings) + +using namespace TextEditor; + +int NormalIndenter::indentFor(const QTextBlock &block, const TabSettings &tabSettings) { - Q_UNUSED(typedChar) + Q_UNUSED(tabSettings); - // At beginning: Leave as is. - if (block == doc->begin()) - return; + QTextBlock previous = block.previous(); + if (!previous.isValid()) + return 0; - const QTextBlock previous = block.previous(); const QString previousText = previous.text(); // Empty line indicates a start of a new paragraph. Leave as is. if (previousText.isEmpty() || previousText.trimmed().isEmpty()) - return; + return 0; - // Just use previous line. - // Skip blank characters when determining the indentation - int i = 0; - while (i < previousText.size()) { - if (!previousText.at(i).isSpace()) { - tabSettings.indentLine(block, tabSettings.columnAt(previousText, i)); - break; - } - ++i; - } + return tabSettings.indentationColumn(previousText); } diff --git a/src/plugins/texteditor/normalindenter.h b/src/plugins/texteditor/normalindenter.h index af5d0c19c59..3cbbb6fd98b 100644 --- a/src/plugins/texteditor/normalindenter.h +++ b/src/plugins/texteditor/normalindenter.h @@ -33,13 +33,10 @@ namespace TextEditor { class TEXTEDITOR_EXPORT NormalIndenter : public Indenter { public: - NormalIndenter(); - virtual ~NormalIndenter(); + NormalIndenter() {} + ~NormalIndenter() override {} - virtual void indentBlock(QTextDocument *doc, - const QTextBlock &block, - const QChar &typedChar, - const TextEditor::TabSettings &tabSettings); + int indentFor(const QTextBlock &block, const TabSettings &tabSettings) override; }; } // namespace TextEditor diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp index c396cf66799..0bf4bc29952 100644 --- a/src/plugins/texteditor/tabsettings.cpp +++ b/src/plugins/texteditor/tabsettings.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "tabsettings.h" +#include "texteditorplugin.h" #include <utils/settingsutils.h> @@ -42,12 +43,16 @@ static const char paddingModeKey[] = "PaddingMode"; namespace TextEditor { -TabSettings::TabSettings() : - m_tabPolicy(SpacesOnlyTabPolicy), - m_tabSize(8), - m_indentSize(4), - m_continuationAlignBehavior(ContinuationAlignWithSpaces) +TabSettings::TabSettings(TabSettings::TabPolicy tabPolicy, + int tabSize, + int indentSize, + TabSettings::ContinuationAlignBehavior continuationAlignBehavior) + : m_tabPolicy(tabPolicy) + , m_tabSize(tabSize) + , m_indentSize(indentSize) + , m_continuationAlignBehavior(continuationAlignBehavior) { + } void TabSettings::toSettings(const QString &category, QSettings *s) const @@ -157,7 +162,7 @@ void TabSettings::removeTrailingWhitespace(QTextCursor cursor, QTextBlock &block } } -bool TabSettings::isIndentationClean(const QTextBlock &block) const +bool TabSettings::isIndentationClean(const QTextBlock &block, const int indent) const { int i = 0; int spaceCount = 0; @@ -170,10 +175,16 @@ bool TabSettings::isIndentationClean(const QTextBlock &block) const if (c == QLatin1Char(' ')) { ++spaceCount; - if (!spacesForTabs && spaceCount == m_tabSize) + if (spaceCount == m_tabSize) + if (!spacesForTabs) + if ((m_continuationAlignBehavior != ContinuationAlignWithSpaces) || (i < indent)) + return false; + if (spaceCount > indent && m_continuationAlignBehavior == NoContinuationAlign) return false; } else if (c == QLatin1Char('\t')) { - if (spacesForTabs || spaceCount != 0) + if (spacesForTabs || (spaceCount != 0)) + return false; + if ((m_continuationAlignBehavior != ContinuationAlignWithIndent) && ((i + 1) * m_tabSize > indent)) return false; } ++i; @@ -275,23 +286,29 @@ bool TabSettings::guessSpacesForTabs(const QTextBlock &_block) const return m_tabPolicy != TabsOnlyTabPolicy; } -QString TabSettings::indentationString(int startColumn, int targetColumn, const QTextBlock &block) const +QString TabSettings::indentationString(int startColumn, int targetColumn, int padding, + const QTextBlock &block) const { targetColumn = qMax(startColumn, targetColumn); if (guessSpacesForTabs(block)) return QString(targetColumn - startColumn, QLatin1Char(' ')); QString s; - int alignedStart = startColumn - (startColumn % m_tabSize) + m_tabSize; + int alignedStart = startColumn == 0 ? 0 : startColumn - (startColumn % m_tabSize) + m_tabSize; if (alignedStart > startColumn && alignedStart <= targetColumn) { s += QLatin1Char('\t'); startColumn = alignedStart; } - if (int columns = targetColumn - startColumn) { - int tabs = columns / m_tabSize; - s += QString(tabs, QLatin1Char('\t')); - s += QString(columns - tabs * m_tabSize, QLatin1Char(' ')); + if (m_continuationAlignBehavior == NoContinuationAlign) { + targetColumn -= padding; + padding = 0; + } else if (m_continuationAlignBehavior == ContinuationAlignWithIndent) { + padding = 0; } + const int columns = targetColumn - padding - startColumn; + const int tabs = columns / m_tabSize; + s += QString(tabs, QLatin1Char('\t')); + s += QString(targetColumn - startColumn - tabs * m_tabSize, QLatin1Char(' ')); return s; } @@ -313,15 +330,7 @@ void TabSettings::indentLine(QTextBlock block, int newIndent, int padding) const // if (indentationColumn(text) == newIndent) // return; - QString indentString; - - if (m_tabPolicy == TabsOnlyTabPolicy) { - // user likes tabs for spaces and uses tabs for indentation, preserve padding - indentString = indentationString(0, newIndent - padding, block); - indentString += QString(padding, QLatin1Char(' ')); - } else { - indentString = indentationString(0, newIndent, block); - } + const QString indentString = indentationString(0, newIndent, padding, block); if (oldBlockLength == indentString.length() && text == indentString) return; @@ -346,15 +355,11 @@ void TabSettings::reindentLine(QTextBlock block, int delta) const if (oldIndent == newIndent) return; - QString indentString; - if (m_tabPolicy == TabsOnlyTabPolicy && m_tabSize == m_indentSize) { - // user likes tabs for spaces and uses tabs for indentation, preserve padding - int padding = qMin(maximumPadding(text), newIndent); - indentString = indentationString(0, newIndent - padding, block); - indentString += QString(padding, QLatin1Char(' ')); - } else { - indentString = indentationString(0, newIndent, block); - } + int padding = 0; + // user likes tabs for spaces and uses tabs for indentation, preserve padding + if (m_tabPolicy == TabsOnlyTabPolicy && m_tabSize == m_indentSize) + padding = qMin(maximumPadding(text), newIndent); + const QString indentString = indentationString(0, newIndent, padding, block); if (oldBlockLength == indentString.length() && text == indentString) return; diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h index a13a2ff87ba..f0b60584b73 100644 --- a/src/plugins/texteditor/tabsettings.h +++ b/src/plugins/texteditor/tabsettings.h @@ -55,7 +55,9 @@ public: ContinuationAlignWithIndent = 2 }; - TabSettings(); + TabSettings() = default; + TabSettings(TabPolicy tabPolicy, int tabSize, + int indentSize, ContinuationAlignBehavior continuationAlignBehavior); void toSettings(const QString &category, QSettings *s) const; void fromSettings(const QString &category, const QSettings *s); @@ -68,7 +70,7 @@ public: int positionAtColumn(const QString &text, int column, int *offset = 0, bool allowOverstep = false) const; int columnCountForText(const QString &text, int startColumn = 0) const; int indentedColumn(int column, bool doIndent = true) const; - QString indentationString(int startColumn, int targetColumn, const QTextBlock ¤tBlock = QTextBlock()) const; + QString indentationString(int startColumn, int targetColumn, int padding, const QTextBlock ¤tBlock = QTextBlock()) const; QString indentationString(const QString &text) const; int indentationColumn(const QString &text) const; static int maximumPadding(const QString &text); @@ -76,7 +78,7 @@ public: void indentLine(QTextBlock block, int newIndent, int padding = 0) const; void reindentLine(QTextBlock block, int delta) const; - bool isIndentationClean(const QTextBlock &block) const; + bool isIndentationClean(const QTextBlock &block, const int indent) const; bool guessSpacesForTabs(const QTextBlock &block) const; static int firstNonSpace(const QString &text); @@ -86,10 +88,10 @@ public: static int trailingWhitespaces(const QString &text); static void removeTrailingWhitespace(QTextCursor cursor, QTextBlock &block); - TabPolicy m_tabPolicy; - int m_tabSize; - int m_indentSize; - ContinuationAlignBehavior m_continuationAlignBehavior; + TabPolicy m_tabPolicy = SpacesOnlyTabPolicy; + int m_tabSize = 8; + int m_indentSize = 4; + ContinuationAlignBehavior m_continuationAlignBehavior = ContinuationAlignWithSpaces; bool equals(const TabSettings &ts) const; }; @@ -100,5 +102,7 @@ inline bool operator!=(const TabSettings &t1, const TabSettings &t2) { return !t } // namespace TextEditor Q_DECLARE_METATYPE(TextEditor::TabSettings) +Q_DECLARE_METATYPE(TextEditor::TabSettings::TabPolicy) +Q_DECLARE_METATYPE(TextEditor::TabSettings::ContinuationAlignBehavior) #endif // TABSETTINGS_H diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 97a0209e1b7..6df2b7e35c0 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -142,7 +142,7 @@ QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, indentPosition = ts.firstNonSpace(text); int targetColumn = ts.indentedColumn(ts.columnAt(text, indentPosition), doIndent); cursor.setPosition(block.position() + indentPosition); - cursor.insertText(ts.indentationString(0, targetColumn, block)); + cursor.insertText(ts.indentationString(0, targetColumn, 0, block)); cursor.setPosition(block.position()); cursor.setPosition(block.position() + indentPosition, QTextCursor::KeepAnchor); cursor.removeSelectedText(); @@ -159,7 +159,7 @@ QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, int blockColumn = ts.columnAt(text, text.size()); if (blockColumn < column) { cursor.setPosition(block.position() + text.size()); - cursor.insertText(ts.indentationString(blockColumn, column, block)); + cursor.insertText(ts.indentationString(blockColumn, column, 0, block)); text = block.text(); } @@ -170,7 +170,7 @@ QTextCursor TextDocumentPrivate::indentOrUnindent(const QTextCursor &textCursor, cursor.setPosition(block.position() + indentPosition); cursor.setPosition(block.position() + indentPosition - spaces, QTextCursor::KeepAnchor); cursor.removeSelectedText(); - cursor.insertText(ts.indentationString(startColumn, targetColumn, block)); + cursor.insertText(ts.indentationString(startColumn, targetColumn, 0, block)); } // Preserve initial anchor of block selection if (blockSelection) { @@ -750,7 +750,8 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b QString blockText = block.text(); d->m_tabSettings.removeTrailingWhitespace(cursor, block); - if (cleanIndentation && !d->m_tabSettings.isIndentationClean(block)) { + const int indent = d->m_indenter->indentFor(block, d->m_tabSettings); + if (cleanIndentation && !d->m_tabSettings.isIndentationClean(block, indent)) { cursor.setPosition(block.position()); int firstNonSpace = d->m_tabSettings.firstNonSpace(blockText); if (firstNonSpace == blockText.length()) { @@ -759,7 +760,7 @@ void TextDocument::cleanWhitespace(QTextCursor &cursor, bool cleanIndentation, b } else { int column = d->m_tabSettings.columnAt(blockText, firstNonSpace); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, firstNonSpace); - QString indentationString = d->m_tabSettings.indentationString(0, column, block); + QString indentationString = d->m_tabSettings.indentationString(0, column, column - indent, block); cursor.insertText(indentationString); } } diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index f0653d4b767..a065d191ad9 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -3337,11 +3337,11 @@ void TextEditorWidgetPrivate::setCursorToColumn(QTextCursor &cursor, int column, cursor.setPosition(cursor.block().position() + pos - 1, QTextCursor::KeepAnchor); cursor.insertText(ts.indentationString( ts.columnAt(cursor.block().text(), pos - 1), - ts.columnAt(cursor.block().text(), pos), cursor.block())); + ts.columnAt(cursor.block().text(), pos), 0, cursor.block())); } else { // column is behind the last position cursor.insertText(ts.indentationString(ts.columnAt(cursor.block().text(), pos), - column, cursor.block())); + column, 0, cursor.block())); } if (moveMode == QTextCursor::KeepAnchor) cursor.setPosition(cursorPosition); @@ -6360,7 +6360,7 @@ void TextEditorWidget::rewrapParagraph() if (commonPrefix.isEmpty()) { spacing = d->m_document->tabSettings().indentationString( - 0, indentLevel, textCursor().block()); + 0, indentLevel, 0, textCursor().block()); } else { spacing = commonPrefix; indentLevel = commonPrefix.length(); diff --git a/src/plugins/texteditor/texteditor_test.cpp b/src/plugins/texteditor/texteditor_test.cpp index 3d645116ce3..786235d3608 100644 --- a/src/plugins/texteditor/texteditor_test.cpp +++ b/src/plugins/texteditor/texteditor_test.cpp @@ -36,6 +36,7 @@ #include "texteditor.h" #include "texteditorplugin.h" #include "textdocument.h" +#include "tabsettings.h" using namespace TextEditor; @@ -497,4 +498,133 @@ void Internal::TextEditorPlugin::testBlockSelectionCopy() Core::EditorManager::closeDocument(editor->document(), false); } +QString tabPolicyToString(TabSettings::TabPolicy policy) +{ + switch (policy) { + case TabSettings::SpacesOnlyTabPolicy: + return QLatin1String("spacesOnlyPolicy"); + case TabSettings::TabsOnlyTabPolicy: + return QLatin1String("tabsOnlyPolicy"); + case TabSettings::MixedTabPolicy: + return QLatin1String("mixedIndentPolicy"); + } + return QString(); +} + +QString continuationAlignBehaviorToString(TabSettings::ContinuationAlignBehavior behavior) +{ + switch (behavior) { + case TabSettings::NoContinuationAlign: + return QLatin1String("noContinuation"); + case TabSettings::ContinuationAlignWithSpaces: + return QLatin1String("spacesContinuation"); + case TabSettings::ContinuationAlignWithIndent: + return QLatin1String("indentContinuation"); + } + return QString(); +} + +struct TabSettingsFlags{ + TabSettings::TabPolicy policy; + TabSettings::ContinuationAlignBehavior behavior; +}; + +typedef std::function<bool(TabSettingsFlags)> IsClean; +void generateTestRows(QLatin1String name, QString text, IsClean isClean) +{ + QList<TabSettings::TabPolicy> allPolicys; + allPolicys << TabSettings::SpacesOnlyTabPolicy + << TabSettings::TabsOnlyTabPolicy + << TabSettings::MixedTabPolicy; + QList<TabSettings::ContinuationAlignBehavior> allbehavior; + allbehavior << TabSettings::NoContinuationAlign + << TabSettings::ContinuationAlignWithSpaces + << TabSettings::ContinuationAlignWithIndent; + + const QLatin1Char splitter('_'); + const int indentSize = 3; + + foreach (TabSettings::TabPolicy policy, allPolicys) { + foreach (TabSettings::ContinuationAlignBehavior behavior, allbehavior) { + const QString tag = tabPolicyToString(policy) + splitter + + continuationAlignBehaviorToString(behavior) + splitter + + name; + QTest::newRow(tag.toLatin1().data()) + << policy + << behavior + << text + << indentSize + << isClean({policy, behavior}); + } + } +} + +void Internal::TextEditorPlugin::testIndentationClean_data() +{ + QTest::addColumn<TabSettings::TabPolicy>("policy"); + QTest::addColumn<TabSettings::ContinuationAlignBehavior>("behavior"); + QTest::addColumn<QString>("text"); + QTest::addColumn<int>("indentSize"); + QTest::addColumn<bool>("clean"); + + generateTestRows(QLatin1String("emptyString"), QString::fromLatin1(""), + [](TabSettingsFlags) -> bool { + return true; + }); + + generateTestRows(QLatin1String("spaceIndentation"), QString::fromLatin1(" f"), + [](TabSettingsFlags flags) -> bool { + return flags.policy != TabSettings::TabsOnlyTabPolicy; + }); + + generateTestRows(QLatin1String("spaceIndentationGuessTabs"), QString::fromLatin1(" f\n\tf"), + [](TabSettingsFlags flags) -> bool { + return flags.policy == TabSettings::SpacesOnlyTabPolicy; + }); + + generateTestRows(QLatin1String("tabIndentation"), QString::fromLatin1("\tf"), + [](TabSettingsFlags flags) -> bool { + return flags.policy == TabSettings::TabsOnlyTabPolicy; + }); + + generateTestRows(QLatin1String("tabIndentationGuessTabs"), QString::fromLatin1("\tf\n\tf"), + [](TabSettingsFlags flags) -> bool { + return flags.policy != TabSettings::SpacesOnlyTabPolicy; + }); + + generateTestRows(QLatin1String("doubleSpaceIndentation"), QString::fromLatin1(" f"), + [](TabSettingsFlags flags) -> bool { + return flags.policy != TabSettings::TabsOnlyTabPolicy + && flags.behavior != TabSettings::NoContinuationAlign; + }); + + generateTestRows(QLatin1String("doubleTabIndentation"), QString::fromLatin1("\t\tf"), + [](TabSettingsFlags flags) -> bool { + return flags.policy == TabSettings::TabsOnlyTabPolicy + && flags.behavior == TabSettings::ContinuationAlignWithIndent; + }); + + generateTestRows(QLatin1String("tabSpaceIndentation"), QString::fromLatin1("\t f"), + [](TabSettingsFlags flags) -> bool { + return flags.policy == TabSettings::TabsOnlyTabPolicy + && flags.behavior == TabSettings::ContinuationAlignWithSpaces; + }); +} + +void Internal::TextEditorPlugin::testIndentationClean() +{ + // fetch test data + QFETCH(TabSettings::TabPolicy, policy); + QFETCH(TabSettings::ContinuationAlignBehavior, behavior); + QFETCH(QString, text); + QFETCH(int, indentSize); + QFETCH(bool, clean); + + const TabSettings settings(policy, indentSize, indentSize, behavior); + const QTextDocument doc(text); + const QTextBlock block = doc.firstBlock(); + + QCOMPARE(settings.isIndentationClean(block, indentSize), clean); +} + #endif // ifdef WITH_TESTS diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h index b66feb99db7..f8486caa67b 100644 --- a/src/plugins/texteditor/texteditorplugin.h +++ b/src/plugins/texteditor/texteditorplugin.h @@ -79,6 +79,9 @@ private slots: void testBlockSelectionRemove(); void testBlockSelectionCopy_data(); void testBlockSelectionCopy(); + + void testIndentationClean_data(); + void testIndentationClean(); #endif }; |