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)