From bf5a8835eefd6494caa5330da5f597c46796fa27 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 5 Oct 2022 13:48:16 +0200 Subject: [PATCH] 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 --- src/plugins/cppeditor/cppqtstyleindenter.cpp | 6 ++ src/plugins/cppeditor/cppqtstyleindenter.h | 2 + src/plugins/qmljstools/qmljsindenter.cpp | 5 ++ src/plugins/qmljstools/qmljsindenter.h | 2 + src/plugins/texteditor/indenter.h | 6 ++ src/plugins/texteditor/texteditor.cpp | 76 ++++++++++++++++---- 6 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/plugins/cppeditor/cppqtstyleindenter.cpp b/src/plugins/cppeditor/cppqtstyleindenter.cpp index e19632ab2ca..c12df09941b 100644 --- a/src/plugins/cppeditor/cppqtstyleindenter.cpp +++ b/src/plugins/cppeditor/cppqtstyleindenter.cpp @@ -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) diff --git a/src/plugins/cppeditor/cppqtstyleindenter.h b/src/plugins/cppeditor/cppqtstyleindenter.h index 646ab909b93..a5ad6dff38b 100644 --- a/src/plugins/cppeditor/cppqtstyleindenter.h +++ b/src/plugins/cppeditor/cppqtstyleindenter.h @@ -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 &blocks, const TextEditor::TabSettings &tabSettings, int cursorPositionInEditor = -1) override; diff --git a/src/plugins/qmljstools/qmljsindenter.cpp b/src/plugins/qmljstools/qmljsindenter.cpp index f051855b4f6..0418728eca5 100644 --- a/src/plugins/qmljstools/qmljsindenter.cpp +++ b/src/plugins/qmljstools/qmljsindenter.cpp @@ -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 &blocks, const TextEditor::TabSettings &tabSettings, diff --git a/src/plugins/qmljstools/qmljsindenter.h b/src/plugins/qmljstools/qmljsindenter.h index 36430fbccc5..603c868c36c 100644 --- a/src/plugins/qmljstools/qmljsindenter.h +++ b/src/plugins/qmljstools/qmljsindenter.h @@ -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 &blocks, const TextEditor::TabSettings &tabSettings, int cursorPositionInEditor = -1) override; diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index ccaae3e2e8f..91c11f043a5 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -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) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 7c4a47870c0..688d231ad79 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -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; QStack m_undoCursorStack; + QList 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)