Editor: improve visual indent depth for empty blocks

Use the indenter based visual indent only for the cpp qt style indenter
since it gives accurate results. In all other casess Look for the next
and previous not empty lines and use the minimum indent depth of those
lines as the visual indent depth for empty lines.

Task-number: QTCREATORBUG-28179
Change-Id: Ic456fba5adfb2a12f20e2bd4fc663010c752b65c
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
David Schulz
2022-10-05 13:48:16 +02:00
parent f513820be0
commit bf5a8835ee
6 changed files with 85 additions and 12 deletions

View File

@@ -157,6 +157,12 @@ int CppQtStyleIndenter::indentFor(const QTextBlock &block,
return indent;
}
int CppQtStyleIndenter::visualIndentFor(const QTextBlock &block,
const TextEditor::TabSettings &tabSettings)
{
return indentFor(block, tabSettings);
}
CppCodeStyleSettings CppQtStyleIndenter::codeStyleSettings() const
{
if (m_cppCodeStylePreferences)

View File

@@ -34,6 +34,8 @@ public:
int indentFor(const QTextBlock &block,
const TextEditor::TabSettings &tabSettings,
int cursorPositionInEditor = -1) override;
int visualIndentFor(const QTextBlock &block,
const TextEditor::TabSettings &tabSettings) override;
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
const TextEditor::TabSettings &tabSettings,
int cursorPositionInEditor = -1) override;

View File

@@ -67,6 +67,11 @@ int Indenter::indentFor(const QTextBlock &block,
return codeFormatter.indentFor(block);
}
int Indenter::visualIndentFor(const QTextBlock &block, const TextEditor::TabSettings &tabSettings)
{
return indentFor(block, tabSettings);
}
TextEditor::IndentationForBlock Indenter::indentationForBlocks(
const QVector<QTextBlock> &blocks,
const TextEditor::TabSettings &tabSettings,

View File

@@ -26,6 +26,8 @@ public:
int indentFor(const QTextBlock &block,
const TextEditor::TabSettings &tabSettings,
int cursorPositionInEditor = -1) override;
int visualIndentFor(const QTextBlock &block,
const TextEditor::TabSettings &tabSettings) override;
TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks,
const TextEditor::TabSettings &tabSettings,
int cursorPositionInEditor = -1) override;

View File

@@ -57,6 +57,12 @@ public:
return -1;
}
virtual int visualIndentFor(const QTextBlock & /*block*/,
const TabSettings & /*tabSettings*/)
{
return -1;
}
virtual void autoIndent(const QTextCursor &cursor,
const TabSettings &tabSettings,
int cursorPositionInEditor = -1)

View File

@@ -513,6 +513,7 @@ public:
void updateHighlights();
void updateCurrentLineInScrollbar();
void updateCurrentLineHighlight();
int indentDepthForBlock(const QTextBlock &block);
void drawFoldingMarker(QPainter *painter, const QPalette &pal,
const QRect &rect,
@@ -533,7 +534,7 @@ public:
void paintCursorAsBlock(const PaintEventData &data, QPainter &painter,
PaintEventBlockData &blockData, int cursorPosition) const;
void paintAdditionalVisualWhitespaces(PaintEventData &data, QPainter &painter, qreal top) const;
void paintIndentDepth(PaintEventData &data, QPainter &painter, const PaintEventBlockData &blockData) const;
void paintIndentDepth(PaintEventData &data, QPainter &painter, const PaintEventBlockData &blockData);
void paintReplacement(PaintEventData &data, QPainter &painter, qreal top) const;
void paintWidgetBackground(const PaintEventData &data, QPainter &painter) const;
void paintOverlays(const PaintEventData &data, QPainter &painter) const;
@@ -808,6 +809,7 @@ public:
};
using UndoMultiCursor = QList<UndoCursor>;
QStack<UndoMultiCursor> m_undoCursorStack;
QList<int> m_visualIndentCache;
};
class TextEditorWidgetFind : public BaseTextFind
@@ -1852,6 +1854,7 @@ void TextEditorWidgetPrivate::editorContentsChange(int position, int charsRemove
}
m_blockCount = newBlockCount;
m_scrollBarUpdateTimer.start(500);
m_visualIndentCache.clear();
}
void TextEditorWidgetPrivate::slotSelectionChanged()
@@ -4379,24 +4382,72 @@ void TextEditorWidgetPrivate::paintAdditionalVisualWhitespaces(PaintEventData &d
}
}
int TextEditorWidgetPrivate::indentDepthForBlock(const QTextBlock &block)
{
const TabSettings &tabSettings = m_document->tabSettings();
const auto blockDepth = [&](const QTextBlock &block) {
int depth = m_visualIndentCache.value(block.blockNumber(), -1);
if (depth < 0) {
const QString text = block.text();
depth = text.simplified().isEmpty() ? -1 : tabSettings.indentationColumn(text);
}
return depth;
};
const auto ensureCacheSize = [&](const int size) {
if (m_visualIndentCache.size() < size)
m_visualIndentCache.resize(size, -1);
};
int depth = blockDepth(block);
if (depth < 0) // the block was empty and uncached ask the indenter for a visual indentation
depth = m_document->indenter()->visualIndentFor(block, tabSettings);
if (depth >= 0) {
ensureCacheSize(block.blockNumber() + 1);
m_visualIndentCache[block.blockNumber()] = depth;
} else {
// find previous non empty block and get the indent depth of this block
QTextBlock it = block.previous();
int prevDepth = -1;
while (it.isValid()) {
prevDepth = blockDepth(it);
if (prevDepth >= 0)
break;
it = it.previous();
}
const int startBlockNumber = it.isValid() ? it.blockNumber() + 1 : 0;
// find next non empty block and get the indent depth of this block
it = block.next();
int nextDepth = -1;
while (it.isValid()) {
nextDepth = blockDepth(it);
if (nextDepth >= 0)
break;
it = it.next();
}
const int endBlockNumber = it.isValid() ? it.blockNumber() : m_blockCount;
// get the depth for the whole range of empty blocks and fill the cache so we do not need to
// redo this for every paint event
depth = prevDepth > 0 && nextDepth > 0 ? qMin(prevDepth, nextDepth) : 0;
ensureCacheSize(endBlockNumber);
for (int i = startBlockNumber; i < endBlockNumber; ++i)
m_visualIndentCache[i] = depth;
}
return depth;
}
void TextEditorWidgetPrivate::paintIndentDepth(PaintEventData &data,
QPainter &painter,
const PaintEventBlockData &blockData) const
const PaintEventBlockData &blockData)
{
if (!m_displaySettings.m_visualizeIndent)
return;
const QString text = data.block.text();
const TabSettings &tabSettings = m_document->tabSettings();
int currentDepth = -1;
if (text.simplified().isEmpty())
currentDepth = m_document->indenter()->indentFor(data.block, tabSettings);
if (currentDepth < 0)
currentDepth = tabSettings.indentationColumn(text);
if (currentDepth == 0 || blockData.layout->lineCount() < 1)
const int depth = indentDepthForBlock(data.block);
if (depth <= 0 || blockData.layout->lineCount() < 1)
return;
const TabSettings &tabSettings = m_document->tabSettings();
const qreal horizontalAdvance = QFontMetricsF(q->font()).horizontalAdvance(
QString(tabSettings.m_indentSize, QChar(' ')));
@@ -4408,7 +4459,8 @@ void TextEditorWidgetPrivate::paintIndentDepth(PaintEventData &data,
qreal x = textLine.cursorToX(0) + data.offset.x() + qMax(0, q->cursorWidth() - 1);
int paintColumn = 0;
while (paintColumn < currentDepth) {
const QString text = data.block.text();
while (paintColumn < depth) {
if (x >= 0) {
int paintPosition = tabSettings.positionAtColumn(text, paintColumn);
if (blockData.layout->lineForTextPosition(paintPosition).lineNumber() != 0)