Commit 9aa51d48 authored by David Schulz's avatar David Schulz

Editor: Fix whitespace cleaning.

Task-number: QTCREATORBUG-7994
Change-Id: I6c197ccc3a148555018e8f8184d116c88d7ea400
Reviewed-by: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
Reviewed-by: default avatarEike Ziller <eike.ziller@theqtcompany.com>
parent bc921b46
......@@ -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);
}
......@@ -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
......@@ -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
......@@ -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
......
......@@ -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)
......
......@@ -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;
......
......@@ -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
......@@ -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
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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);
}
......@@ -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
......
......@@ -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;
}
......@@ -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
......
......@@ -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);
}
......@@ -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
......
......@@ -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);