From 29b073e92e61972846b743e52787b56a0a4d58d0 Mon Sep 17 00:00:00 2001
From: mae <qt-info@nokia.com>
Date: Thu, 5 Aug 2010 15:01:20 +0200
Subject: [PATCH] Refactor block selection

Block selection was "broken" when using tabs, or rather
incomplete: It treated tabs as normal characters, which
has shown to be unexpected by people using tabs in code.

The new implementation has a vastly improved find scope
as well. In addition, creating a blog selection with
mouse or keyboard feels a lot more solid now, as the
actual selection is detached from possible valid cursor
positions.

Task-number: QTCREATORBUG-1541
---
 src/plugins/find/basetextfind.cpp            |  54 +-
 src/plugins/find/basetextfind.h              |   7 +-
 src/plugins/projectexplorer/outputwindow.cpp |   6 +-
 src/plugins/texteditor/basetexteditor.cpp    | 559 ++++++++++++++-----
 src/plugins/texteditor/basetexteditor.h      |  38 +-
 src/plugins/texteditor/basetexteditor_p.h    |   8 +-
 src/plugins/texteditor/tabsettings.cpp       |  16 +
 src/plugins/texteditor/tabsettings.h         |   1 +
 src/plugins/texteditor/texteditoroverlay.cpp |  34 +-
 src/plugins/texteditor/texteditoroverlay.h   |  14 +-
 10 files changed, 511 insertions(+), 226 deletions(-)

diff --git a/src/plugins/find/basetextfind.cpp b/src/plugins/find/basetextfind.cpp
index 211066938b2..063afe8d221 100644
--- a/src/plugins/find/basetextfind.cpp
+++ b/src/plugins/find/basetextfind.cpp
@@ -39,14 +39,16 @@ using namespace Find;
 
 BaseTextFind::BaseTextFind(QTextEdit *editor)
     : m_editor(editor)
-    , m_findScopeVerticalBlockSelection(0)
+    , m_findScopeVerticalBlockSelectionFirstColumn(-1)
+    , m_findScopeVerticalBlockSelectionLastColumn(-1)
     , m_incrementalStartPos(-1)
 {
 }
 
 BaseTextFind::BaseTextFind(QPlainTextEdit *editor)
     : m_plaineditor(editor)
-    , m_findScopeVerticalBlockSelection(0)
+    , m_findScopeVerticalBlockSelectionFirstColumn(-1)
+    , m_findScopeVerticalBlockSelectionLastColumn(-1)
     , m_incrementalStartPos(-1)
 {
 }
@@ -268,17 +270,16 @@ QTextCursor BaseTextFind::findOne(const QRegExp &expr, const QTextCursor &from,
     if (candidate.isNull())
         return candidate;
 
-    if (!m_findScopeVerticalBlockSelection)
+    if (m_findScopeVerticalBlockSelectionFirstColumn < 0)
         return candidate;
     forever {
         if (!inScope(candidate.selectionStart(), candidate.selectionEnd()))
             return candidate;
-        QTextCursor b = candidate;
-        b.setPosition(candidate.selectionStart());
-        QTextCursor e = candidate;
-        e.setPosition(candidate.selectionEnd());
-        if (b.positionInBlock() >= m_findScopeStart.positionInBlock() + 1
-            && e.positionInBlock() <= m_findScopeStart.positionInBlock() + 1 + m_findScopeVerticalBlockSelection)
+        bool inVerticalFindScope = false;
+        QMetaObject::invokeMethod(m_plaineditor, "inFindScope", Qt::DirectConnection,
+                                  Q_RETURN_ARG(bool, inVerticalFindScope),
+                                  Q_ARG(QTextCursor, candidate));
+        if (inVerticalFindScope)
             return candidate;
         candidate = document()->find(expr, candidate, options);
     }
@@ -299,23 +300,19 @@ void BaseTextFind::defineFindScope()
     if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
         m_findScopeStart = QTextCursor(document()->docHandle(), qMax(0, cursor.selectionStart()-1));
         m_findScopeEnd = QTextCursor(document()->docHandle(), cursor.selectionEnd());
-        m_findScopeVerticalBlockSelection = 0;
-
-        int verticalBlockSelection = 0;
-        if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelection") >= 0)
-            verticalBlockSelection = m_plaineditor->property("verticalBlockSelection").toInt();
-
-        if (verticalBlockSelection) {
-            QTextCursor findScopeVisualStart(document()->docHandle(), cursor.selectionStart());
-            int findScopeFromColumn = qMin(findScopeVisualStart.positionInBlock(),
-                                         m_findScopeEnd.positionInBlock());
-            int findScopeToColumn = findScopeFromColumn + verticalBlockSelection;
-            m_findScopeStart.setPosition(findScopeVisualStart.block().position() + findScopeFromColumn - 1);
-            m_findScopeEnd.setPosition(m_findScopeEnd.block().position()
-                                       + qMin(m_findScopeEnd.block().length()-1, findScopeToColumn));
-            m_findScopeVerticalBlockSelection = verticalBlockSelection;
+        m_findScopeVerticalBlockSelectionFirstColumn = -1;
+        m_findScopeVerticalBlockSelectionLastColumn = -1;
+
+        if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelectionFirstColumn") >= 0) {
+            m_findScopeVerticalBlockSelectionFirstColumn
+                    = m_plaineditor->property("verticalBlockSelectionFirstColumn").toInt();
+            m_findScopeVerticalBlockSelectionLastColumn
+                    = m_plaineditor->property("verticalBlockSelectionLastColumn").toInt();
         }
-        emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
+
+        emit findScopeChanged(m_findScopeStart, m_findScopeEnd,
+                              m_findScopeVerticalBlockSelectionFirstColumn,
+                              m_findScopeVerticalBlockSelectionLastColumn);
         cursor.setPosition(m_findScopeStart.position()+1);
         setTextCursor(cursor);
     } else {
@@ -327,6 +324,9 @@ void BaseTextFind::clearFindScope()
 {
     m_findScopeStart = QTextCursor();
     m_findScopeEnd = QTextCursor();
-    m_findScopeVerticalBlockSelection = 0;
-    emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
+    m_findScopeVerticalBlockSelectionFirstColumn = -1;
+    m_findScopeVerticalBlockSelectionLastColumn = -1;
+    emit findScopeChanged(m_findScopeStart, m_findScopeEnd,
+                          m_findScopeVerticalBlockSelectionFirstColumn,
+                          m_findScopeVerticalBlockSelectionLastColumn);
 }
diff --git a/src/plugins/find/basetextfind.h b/src/plugins/find/basetextfind.h
index 2bce44a4db6..d632e730e80 100644
--- a/src/plugins/find/basetextfind.h
+++ b/src/plugins/find/basetextfind.h
@@ -72,7 +72,9 @@ public:
 
 signals:
     void highlightAll(const QString &txt, Find::FindFlags findFlags);
-    void findScopeChanged(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection);
+    void findScopeChanged(const QTextCursor &start, const QTextCursor &end,
+                          int verticalBlockSelectionFirstColumn,
+                          int verticalBlockSelectionLastColumn);
 
 private:
     bool find(const QString &txt,
@@ -89,7 +91,8 @@ private:
     QPointer<QPlainTextEdit> m_plaineditor;
     QTextCursor m_findScopeStart;
     QTextCursor m_findScopeEnd;
-    int m_findScopeVerticalBlockSelection;
+    int m_findScopeVerticalBlockSelectionFirstColumn;
+    int m_findScopeVerticalBlockSelectionLastColumn;
     bool inScope(int startPosition, int endPosition) const;
     QTextCursor findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const;
     int m_incrementalStartPos;
diff --git a/src/plugins/projectexplorer/outputwindow.cpp b/src/plugins/projectexplorer/outputwindow.cpp
index bcee5d1c771..ad111bfc2a9 100644
--- a/src/plugins/projectexplorer/outputwindow.cpp
+++ b/src/plugins/projectexplorer/outputwindow.cpp
@@ -591,7 +591,11 @@ void OutputWindow::appendText(const QString &textIn, const QTextCharFormat &form
 
 bool OutputWindow::isScrollbarAtBottom() const
 {
-    return verticalScrollBar()->value() == verticalScrollBar()->maximum();
+    return isVisible()
+            && (blockBoundingRect(document()->lastBlock()).bottom()
+                + contentOffset().y() <= viewport()->rect().bottom());
+
+    // return verticalScrollBar()->value() == verticalScrollBar()->maximum();
 }
 
 void OutputWindow::scrollToBottom()
diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp
index a842ce94638..d32c566d65a 100644
--- a/src/plugins/texteditor/basetexteditor.cpp
+++ b/src/plugins/texteditor/basetexteditor.cpp
@@ -710,12 +710,12 @@ void BaseTextEditor::editorContentsChange(int position, int charsRemoved, int ch
 
 void BaseTextEditor::slotSelectionChanged()
 {
-    bool changed = (d->m_inBlockSelectionMode != d->m_lastEventWasBlockSelectionEvent);
-    d->m_inBlockSelectionMode = d->m_lastEventWasBlockSelectionEvent;
-    if (changed || d->m_inBlockSelectionMode)
+    if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) {
+        d->m_inBlockSelectionMode = false;
+        d->m_blockSelection.clear();
         viewport()->update();
-    if (!d->m_inBlockSelectionMode)
-        d->m_blockSelectionExtraX = 0;
+    }
+
     if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection())
         d->m_selectBlockAnchor = QTextCursor();
 
@@ -1092,9 +1092,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
     d->m_moveLineUndoHack = false;
     d->clearVisibleFoldedBlock();
 
-    QKeyEvent *original_e = e;
-    d->m_lastEventWasBlockSelectionEvent = false;
-
     if (e->key() == Qt::Key_Escape) {
         if (d->m_snippetOverlay->isVisible()) {
             e->accept();
@@ -1182,16 +1179,20 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
     } else if (!ro
                && (e == QKeySequence::MoveToStartOfBlock
                    || e == QKeySequence::SelectStartOfBlock)){
-        if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
-            d->m_lastEventWasBlockSelectionEvent = true;
+        if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
+            e->accept();
+            return;
+        }
         handleHomeKey(e == QKeySequence::SelectStartOfBlock);
         e->accept();
         return;
     } else if (!ro
                && (e == QKeySequence::MoveToStartOfLine
                    || e == QKeySequence::SelectStartOfLine)){
-        if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
-            d->m_lastEventWasBlockSelectionEvent = true;
+        if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
+                e->accept();
+                return;
+        }
         QTextCursor cursor = textCursor();
         if (QTextLayout *layout = cursor.block().layout()) {
             if (layout->lineForTextPosition(cursor.position() - cursor.block().position()).lineNumber() == 0) {
@@ -1273,36 +1274,23 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
             return;
         }
         // fall through
-    case Qt::Key_End:
     case Qt::Key_Right:
     case Qt::Key_Left:
 #ifndef Q_WS_MAC
         if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
-
-            d->m_lastEventWasBlockSelectionEvent = true;
-
-            if (d->m_inBlockSelectionMode) {
-                if (e->key() == Qt::Key_Right && textCursor().atBlockEnd()) {
-                    d->m_blockSelectionExtraX++;
-                    viewport()->update();
-                    e->accept();
-                    return;
-                } else if (e->key() == Qt::Key_Left && d->m_blockSelectionExtraX > 0) {
-                    d->m_blockSelectionExtraX--;
-                    e->accept();
-                    viewport()->update();
-                    return;
-                }
-            }
-
-            e = new QKeyEvent(
-                e->type(),
-                e->key(),
-                e->modifiers() & ~Qt::AltModifier,
-                e->text(),
-                e->isAutoRepeat(),
-                e->count()
-                );
+            int diff_row = 0;
+            int diff_col = 0;
+            if (e->key() == Qt::Key_Up)
+                diff_row = -1;
+            else if (e->key() == Qt::Key_Down)
+                diff_row = 1;
+            else if (e->key() == Qt::Key_Left)
+                diff_col = -1;
+            else if (e->key() == Qt::Key_Right)
+                diff_col = 1;
+            handleBlockSelection(diff_row, diff_col);
+            e->accept();
+            return;
         }
 #endif
         break;
@@ -1395,8 +1383,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
         maybeRequestAutoCompletion(e->text().at(0));
     }
 
-    if (e != original_e)
-        delete e;
 }
 
 void BaseTextEditor::maybeRequestAutoCompletion(const QChar &ch)
@@ -1934,10 +1920,9 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
     m_editable(0),
     m_actionHack(0),
     m_inBlockSelectionMode(false),
-    m_lastEventWasBlockSelectionEvent(false),
-    m_blockSelectionExtraX(-1),
     m_moveLineUndoHack(false),
-    m_findScopeVerticalBlockSelection(0),
+    m_findScopeVerticalBlockSelectionFirstColumn(-1),
+    m_findScopeVerticalBlockSelectionLastColumn(-1),
     m_highlightBlocksTimer(0),
     m_requestAutoCompletionRevision(0),
     m_requestAutoCompletionPosition(0),
@@ -2158,12 +2143,6 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
     int idx = -1;
     int l = 1;
 
-    int m_findScopeFirstColumn = 0;
-    if (!m_findScopeStart.isNull() && m_findScopeVerticalBlockSelection)  {
-        m_findScopeFirstColumn = m_findScopeStart.positionInBlock() + 1;
-    }
-
-
     while (idx < text.length()) {
         idx = m_searchExpr.indexIn(text, idx + l);
         if (idx < 0)
@@ -2176,17 +2155,8 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
                 || (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
             continue;
 
-        if (!m_findScopeStart.isNull()) {
-            if (blockPosition + idx < m_findScopeStart.position()
-                || blockPosition + idx + l > m_findScopeEnd.position())
-                continue;
-            if (m_findScopeVerticalBlockSelection) {
-                if (idx < m_findScopeFirstColumn
-                    || idx + l > m_findScopeFirstColumn + m_findScopeVerticalBlockSelection)
-                    continue;
-            }
-        }
-
+        if (!q->inFindScope(blockPosition + idx, blockPosition + idx + l))
+            continue;
 
         overlay->addOverlaySelection(blockPosition + idx,
                                      blockPosition + idx + l,
@@ -2216,6 +2186,7 @@ void BaseTextEditorPrivate::clearBlockSelection()
 {
     if (m_inBlockSelectionMode) {
         m_inBlockSelectionMode = false;
+        m_blockSelection.clear();
         QTextCursor cursor = q->textCursor();
         cursor.clearSelection();
         q->setTextCursor(cursor);
@@ -2224,68 +2195,77 @@ void BaseTextEditorPrivate::clearBlockSelection()
 
 QString BaseTextEditorPrivate::copyBlockSelection()
 {
-    QString text;
-
+    QString selection;
     QTextCursor cursor = q->textCursor();
-    if (!cursor.hasSelection())
-        return text;
-
-    QTextDocument *doc = q->document();
-    int start = cursor.selectionStart();
-    int end = cursor.selectionEnd();
-    QTextBlock startBlock = doc->findBlock(start);
-    int columnA = start - startBlock.position();
-    QTextBlock endBlock = doc->findBlock(end);
-    int columnB = end - endBlock.position();
-    int firstColumn = qMin(columnA, columnB);
-    int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
-
-    QTextBlock block = startBlock;
+    if (!m_inBlockSelectionMode)
+        return selection;
+    const TabSettings &ts = q->tabSettings();
+    QTextBlock block = m_blockSelection.firstBlock.block();
+    QTextBlock lastBlock = m_blockSelection.lastBlock.block();
     for (;;) {
+        QString text = block.text();
+        int startOffset = 0;
+        int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
+        int endOffset = 0;
+        int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
 
-        cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
-        cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
-        text += cursor.selectedText();
-        if (block == endBlock)
+        if (startPos == endPos) {
+            selection += QString(endOffset - startOffset, QLatin1Char(' '));
+        } else {
+            if (startOffset < 0)
+                selection += QString(-startOffset, QLatin1Char(' '));
+            if (endOffset < 0)
+                --endPos;
+            selection += text.mid(startPos, endPos - startPos);
+            if (endOffset < 0) {
+                selection += QString(ts.m_tabSize + endOffset, QLatin1Char(' '));
+            } else if (endOffset > 0) {
+                selection += QString(endOffset, QLatin1Char(' '));
+            }
+        }
+        if (block == lastBlock)
             break;
-        text += QLatin1Char('\n');
+        selection += QLatin1Char('\n');
         block = block.next();
     }
-
-    return text;
+    return selection;
 }
 
 void BaseTextEditorPrivate::removeBlockSelection(const QString &text)
 {
     QTextCursor cursor = q->textCursor();
-    if (!cursor.hasSelection())
+    if (!cursor.hasSelection() || !m_inBlockSelectionMode)
         return;
 
-    QTextDocument *doc = q->document();
-    int start = cursor.selectionStart();
-    int end = cursor.selectionEnd();
-    QTextBlock startBlock = doc->findBlock(start);
-    int columnA = start - startBlock.position();
-    QTextBlock endBlock = doc->findBlock(end);
-    int columnB = end - endBlock.position();
-    int firstColumn = qMin(columnA, columnB);
-    int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
-
+    int cursorPosition = cursor.selectionStart();
     cursor.clearSelection();
     cursor.beginEditBlock();
 
-    QTextBlock block = startBlock;
+    const TabSettings &ts = q->tabSettings();
+    QTextBlock block = m_blockSelection.firstBlock.block();
+    QTextBlock lastBlock = m_blockSelection.lastBlock.block();
     for (;;) {
+        QString text = block.text();
+        int startOffset = 0;
+        int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
+        int endOffset = 0;
+        int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
 
-        cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
-        cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
+        cursor.setPosition(block.position() + startPos);
+        cursor.setPosition(block.position() + endPos, QTextCursor::KeepAnchor);
         cursor.removeSelectedText();
-        if (block == endBlock)
+
+        if (startOffset < 0)
+            cursor.insertText(QString(ts.m_tabSize + startOffset, QLatin1Char(' ')));
+        if (endOffset < 0)
+            cursor.insertText(QString(-endOffset, QLatin1Char(' ')));
+
+        if (block == lastBlock)
             break;
         block = block.next();
     }
 
-    cursor.setPosition(start);
+    cursor.setPosition(cursorPosition);
     if (!text.isEmpty())
         cursor.insertText(text);
     cursor.endEditBlock();
@@ -2448,37 +2428,25 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
         }
     }
 
-
-
-    if (!d->m_findScopeStart.isNull()) {
+    if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn < 0) {
 
         TextEditorOverlay *overlay = new TextEditorOverlay(this);
         overlay->addOverlaySelection(d->m_findScopeStart.position(),
                                      d->m_findScopeEnd.position(),
                                      d->m_searchScopeFormat.background().color().darker(120),
                                      d->m_searchScopeFormat.background().color(),
-                                     TextEditorOverlay::ExpandBegin,
-                                     d->m_findScopeVerticalBlockSelection);
+                                     TextEditorOverlay::ExpandBegin);
         overlay->setAlpha(false);
         overlay->paint(&painter, e->rect());
         delete overlay;
     }
 
-    BlockSelectionData *blockSelection = 0;
+    int blockSelectionIndex = -1;
 
     if (d->m_inBlockSelectionMode
         && context.selections.count() && context.selections.last().cursor == textCursor()) {
-        blockSelection = new BlockSelectionData;
-        blockSelection->selectionIndex = context.selections.size()-1;
-        const QAbstractTextDocumentLayout::Selection &selection = context.selections[blockSelection->selectionIndex];
-        int start = blockSelection->selectionStart = selection.cursor.selectionStart();
-        int end = blockSelection->selectionEnd = selection.cursor.selectionEnd();
-        QTextBlock block = doc->findBlock(start);
-        int columnA = start - block.position();
-        block = doc->findBlock(end);
-        int columnB = end - block.position();
-        blockSelection->firstColumn = qMin(columnA, columnB);
-        blockSelection->lastColumn = qMax(columnA, columnB) + d->m_blockSelectionExtraX;
+        blockSelectionIndex = context.selections.size()-1;
+        context.selections[blockSelectionIndex].format.clearBackground();
     }
 
     QTextBlock visibleCollapsedBlock;
@@ -2517,6 +2485,74 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
     } // end first pass
 
 
+    // possible extra pass for the block selection find scope
+    if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn >= 0) {
+        QTextBlock blockFS = block;
+        QPointF offsetFS = offset;
+
+        while (blockFS.isValid()) {
+
+            QRectF r = blockBoundingRect(blockFS).translated(offsetFS);
+
+            if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
+
+                if (blockFS.position() >= d->m_findScopeStart.block().position()
+                        && blockFS.position() <= d->m_findScopeEnd.block().position()) {
+                    QTextLayout *layout = blockFS.layout();
+                    QString text = blockFS.text();
+                    const TabSettings &ts = tabSettings();
+                    int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
+
+                    int offset = 0;
+                    int relativePos  =  ts.positionAtColumn(text,
+                                                            d->m_findScopeVerticalBlockSelectionFirstColumn,
+                                                            &offset);
+                    QTextLine line = layout->lineForTextPosition(relativePos);
+                    qreal x = line.cursorToX(relativePos) + offset * spacew;
+
+                    int eoffset = 0;
+                    int erelativePos  =  ts.positionAtColumn(text,
+                                                             d->m_findScopeVerticalBlockSelectionLastColumn,
+                                                             &eoffset);
+                    QTextLine eline = layout->lineForTextPosition(erelativePos);
+                    qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
+
+                    QRectF rr = line.naturalTextRect();
+                    rr.moveTop(rr.top() + r.top());
+                    rr.setLeft(r.left() + x);
+                    if (line.lineNumber() == eline.lineNumber())  {
+                        rr.setRight(r.left() + ex);
+                    }
+                    painter.fillRect(rr, d->m_searchScopeFormat.background());
+
+                    QColor lineCol = d->m_searchScopeFormat.background().color().darker(120);
+                    QPen pen = painter.pen();
+                    painter.setPen(lineCol);
+                    if (blockFS == d->m_findScopeStart.block())
+                        painter.drawLine(rr.topLeft(), rr.topRight());
+                    if (blockFS == d->m_findScopeEnd.block())
+                        painter.drawLine(rr.bottomLeft(), rr.bottomRight());
+                    painter.drawLine(rr.topLeft(), rr.bottomLeft());
+                    painter.drawLine(rr.topRight(), rr.bottomRight());
+                    painter.setPen(pen);
+                }
+            }
+            offsetFS.ry() += r.height();
+
+            if (offsetFS.y() > viewportRect.height())
+                break;
+
+            blockFS = blockFS.next();
+            if (!blockFS.isVisible()) {
+                // invisible blocks do have zero line count
+                blockFS = doc->findBlockByLineNumber(blockFS.firstLineNumber());
+            }
+
+        }
+    }
+
+
+
     d->m_searchResultOverlay->fill(&painter,
                                    d->m_searchResultFormat.background().color(),
                                    e->rect());
@@ -2565,9 +2601,11 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
                     o.start = selStart;
                     o.length = selEnd - selStart;
                     o.format = range.format;
-                    if (blockSelection && blockSelection->selectionIndex == i) {
-                        o.start = qMin(blockSelection->firstColumn, bllen-1);
-                        o.length = qMin(blockSelection->lastColumn, bllen-1) - o.start;
+                    if (i == blockSelectionIndex) {
+                        QString text = block.text();
+                        const TabSettings &ts = tabSettings();
+                        o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn);
+                        o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn) - o.start;
                     }
                     if ((hasMainSelection && i == context.selections.size()-1)
                         || (o.format.foreground().style() == Qt::NoBrush
@@ -2609,6 +2647,62 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
                 painter.fillRect(rr, color);
             }
 
+
+            QRectF blockSelectionCursorRect;
+            if (d->m_inBlockSelectionMode
+                    && block.position() >= d->m_blockSelection.firstBlock.block().position()
+                    && block.position() <= d->m_blockSelection.lastBlock.block().position()) {
+                QString text = block.text();
+                const TabSettings &ts = tabSettings();
+                int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
+
+                int offset = 0;
+                int relativePos  =  ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn, &offset);
+                QTextLine line = layout->lineForTextPosition(relativePos);
+                qreal x = line.cursorToX(relativePos) + offset * spacew;
+
+                int eoffset = 0;
+                int erelativePos  =  ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn, &eoffset);
+                QTextLine eline = layout->lineForTextPosition(erelativePos);
+                qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
+
+                QRectF rr = line.naturalTextRect();
+                rr.moveTop(rr.top() + r.top());
+                rr.setLeft(r.left() + x);
+                if (line.lineNumber() == eline.lineNumber())  {
+                    rr.setRight(r.left() + ex);
+                }
+                painter.fillRect(rr, palette().highlight());
+                if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopLeft
+                        && block == d->m_blockSelection.firstBlock.block())
+                        || (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomLeft
+                            && block == d->m_blockSelection.lastBlock.block())
+                        ) {
+                    rr.setRight(rr.left()+2);
+                    blockSelectionCursorRect = rr;
+                }
+                for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) {
+                    rr = layout->lineAt(i).naturalTextRect();
+                    rr.moveTop(rr.top() + r.top());
+                    rr.setLeft(r.left() + x);
+                    painter.fillRect(rr, palette().highlight());
+                }
+
+                rr = eline.naturalTextRect();
+                rr.moveTop(rr.top() + r.top());
+                rr.setRight(r.left() + ex);
+                if (line.lineNumber() != eline.lineNumber())
+                    painter.fillRect(rr, palette().highlight());
+                if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopRight
+                     && block == d->m_blockSelection.firstBlock.block())
+                        || (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomRight
+                            && block == d->m_blockSelection.lastBlock.block())) {
+                    rr.setLeft(rr.right()-2);
+                    blockSelectionCursorRect = rr;
+                }
+            }
+
+
             bool drawCursor = ((editable || true) // we want the cursor in read-only mode
                                && context.cursorPosition >= blpos
                                && context.cursorPosition < blpos + bllen);
@@ -2665,6 +2759,12 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
                 cursor_cpos = cpos;
                 cursor_pen = painter.pen();
             }
+
+#ifndef Q_WS_MAC // no visible cursor on mac
+            if (blockSelectionCursorRect.isValid())
+                painter.fillRect(blockSelectionCursorRect, palette().text());
+#endif
+
         }
 
         offset.ry() += r.height();
@@ -2693,8 +2793,6 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
 
     //end QPlainTextEdit::paintEvent()
 
-    delete blockSelection;
-
     offset = contentOffset();
     block = firstVisibleBlock();
 
@@ -2822,22 +2920,27 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
         d->m_animator->draw(&painter, cursorRect(cursor).topLeft());
     }
 
-    if (d->m_overlay->isVisible())
-        d->m_overlay->paint(&painter, e->rect());
+    // draw the overlays, but only if we do not have a find scope, otherwise the
+    // view becomes too noisy.
+    if (d->m_findScopeStart.isNull()) {
+        if (d->m_overlay->isVisible())
+            d->m_overlay->paint(&painter, e->rect());
 
-    if (d->m_snippetOverlay->isVisible())
-        d->m_snippetOverlay->paint(&painter, e->rect());
+        if (d->m_snippetOverlay->isVisible())
+            d->m_snippetOverlay->paint(&painter, e->rect());
+
+        if (!d->m_refactorOverlay->isEmpty())
+            d->m_refactorOverlay->paint(&painter, e->rect());
+    }
 
     if (!d->m_searchResultOverlay->isEmpty()) {
         d->m_searchResultOverlay->paint(&painter, e->rect());
         d->m_searchResultOverlay->clear();
     }
 
-    if (!d->m_refactorOverlay->isEmpty())
-        d->m_refactorOverlay->paint(&painter, e->rect());
 
     // draw the cursor last, on top of everything
-    if (cursor_layout) {
+    if (cursor_layout && !d->m_inBlockSelectionMode) {
         painter.setPen(cursor_pen);
         cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
     }
@@ -2897,6 +3000,7 @@ void BaseTextEditor::drawCollapsedBlockPopup(QPainter &painter,
         layout->draw(&painter, offset, selections, clip);
 
         b.setVisible(false); // restore previous state
+        b.setLineCount(0); // restore 0 line count for invisible block
         offset.ry() += r.height();
         b = b.next();
     }
@@ -3421,8 +3525,6 @@ void BaseTextEditorPrivate::clearVisibleFoldedBlock()
 
 void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
 {
-    d->m_lastEventWasBlockSelectionEvent = (e->modifiers() & Qt::AltModifier);
-
     updateLink(e);
 
     if (e->buttons() == Qt::NoButton) {
@@ -3447,12 +3549,23 @@ void BaseTextEditor::mouseMoveEvent(QMouseEvent *e)
         }
     } else {
         QPlainTextEdit::mouseMoveEvent(e);
-    }
-    if (d->m_lastEventWasBlockSelectionEvent && d->m_inBlockSelectionMode) {
-        if (textCursor().atBlockEnd()) {
-            d->m_blockSelectionExtraX = qMax(0, e->pos().x() - cursorRect().center().x()) / fontMetrics().averageCharWidth();
-        } else {
-            d->m_blockSelectionExtraX = 0;
+
+        if (e->modifiers() & Qt::AltModifier) {
+            if (!d->m_inBlockSelectionMode) {
+                d->m_blockSelection.fromSelection(tabSettings(), textCursor());
+                d->m_inBlockSelectionMode = true;
+            } else {
+                QTextCursor cursor = textCursor();
+
+                // get visual column
+                int column = tabSettings().columnAt(cursor.block().text(), cursor.positionInBlock());
+                if (cursor.positionInBlock() == cursor.block().length()-1) {
+                    column += (e->pos().x() - cursorRect().center().x())/fontMetrics().width(QChar(QLatin1Char(' ')));
+                }
+                d->m_blockSelection.moveAnchor(cursor.blockNumber(), column);
+                setTextCursor(d->m_blockSelection.selection(tabSettings()));
+                viewport()->update();
+            }
         }
     }
     if (viewport()->cursor().shape() == Qt::BlankCursor)
@@ -4306,16 +4419,18 @@ void BaseTextEditor::highlightSearchResults(const QString &txt, Find::FindFlags
     d->m_delayedUpdateTimer->start(10);
 }
 
-int BaseTextEditor::verticalBlockSelection() const
+int BaseTextEditor::verticalBlockSelectionFirstColumn() const
 {
-    if (!d->m_inBlockSelectionMode)
-        return 0;
-    QTextCursor b = textCursor();
-    QTextCursor e = b;
-    b.setPosition(b.selectionStart());
-    e.setPosition(e.selectionEnd());
+    if (d->m_inBlockSelectionMode)
+        return d->m_blockSelection.firstVisualColumn;
+    return -1;
+}
 
-    return qAbs(b.positionInBlock() - e.positionInBlock()) + d->m_blockSelectionExtraX;
+int BaseTextEditor::verticalBlockSelectionLastColumn() const
+{
+    if (d->m_inBlockSelectionMode)
+        return d->m_blockSelection.lastVisualColumn;
+    return -1;
 }
 
 QRegion BaseTextEditor::translatedLineRegion(int lineStart, int lineEnd) const
@@ -4337,12 +4452,18 @@ QRegion BaseTextEditor::translatedLineRegion(int lineStart, int lineEnd) const
     return region;
 }
 
-void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection)
+void BaseTextEditor::setFindScope(const QTextCursor &start, const QTextCursor &end,
+                                  int verticalBlockSelectionFirstColumn,
+                                  int verticalBlockSelectionLastColumn)
 {
-    if (start != d->m_findScopeStart || end != d->m_findScopeEnd) {
+    if (start != d->m_findScopeStart
+            || end != d->m_findScopeEnd
+            || verticalBlockSelectionFirstColumn != d->m_findScopeVerticalBlockSelectionFirstColumn
+            || verticalBlockSelectionLastColumn != d->m_findScopeVerticalBlockSelectionLastColumn) {
         d->m_findScopeStart = start;
         d->m_findScopeEnd = end;
-        d->m_findScopeVerticalBlockSelection = verticalBlockSelection;
+        d->m_findScopeVerticalBlockSelectionFirstColumn = verticalBlockSelectionFirstColumn;
+        d->m_findScopeVerticalBlockSelectionLastColumn = verticalBlockSelectionLastColumn;
         viewport()->update();
     }
 }
@@ -5247,21 +5368,27 @@ void BaseTextEditor::insertFromMimeData(const QMimeData *source)
         QStringList lines = text.split(QLatin1Char('\n'));
         QTextCursor cursor = textCursor();
         cursor.beginEditBlock();
+        const TabSettings &ts = d->m_document->tabSettings();
         int initialCursorPosition = cursor.position();
-        int column = cursor.position() - cursor.block().position();
+        int column = ts.columnAt(cursor.block().text(), cursor.positionInBlock());
         cursor.insertText(lines.first());
         for (int i = 1; i < lines.count(); ++i) {
             QTextBlock next = cursor.block().next();
             if (next.isValid()) {
-                cursor.setPosition(next.position() + qMin(column, next.length()-1));
+                cursor.setPosition(next.position());
             } else {
                 cursor.movePosition(QTextCursor::EndOfBlock);
                 cursor.insertBlock();
             }
-
-            int actualColumn = cursor.position() - cursor.block().position();
-            if (actualColumn < column)
-                cursor.insertText(QString(column - actualColumn, QLatin1Char(' ')));
+            int offset = 0;
+            int position = ts.positionAtColumn(cursor.block().text(), column, &offset);
+            cursor.setPosition(cursor.block().position() + position);
+            if (offset < 0) {
+                cursor.deleteChar();
+                cursor.insertText(QString(-offset, QLatin1Char(' ')));
+            } else {
+                cursor.insertText(QString(offset, QLatin1Char(' ')));
+            }
             cursor.insertText(lines.at(i));
         }
         cursor.setPosition(initialCursorPosition);
@@ -5359,8 +5486,8 @@ BaseTextEditorEditable::BaseTextEditorEditable(BaseTextEditor *editor)
     BaseTextFind *baseTextFind = new BaseTextFind(editor);
     connect(baseTextFind, SIGNAL(highlightAll(QString, Find::FindFlags)),
             editor, SLOT(highlightSearchResults(QString, Find::FindFlags)));
-    connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, QTextCursor, int)),
-            editor, SLOT(setFindScope(QTextCursor, QTextCursor, int)));
+    connect(baseTextFind, SIGNAL(findScopeChanged(QTextCursor, QTextCursor, int, int)),
+            editor, SLOT(setFindScope(QTextCursor, QTextCursor, int, int)));
     aggregate->add(baseTextFind);
     aggregate->add(editor);
 
@@ -5524,3 +5651,125 @@ void BaseTextEditor::doFoo() {
     setRefactorMarkers(markers);
 #endif
 }
+
+void Internal::BaseTextBlockSelection::moveAnchor(int blockNumber, int visualColumn)
+{
+    if (visualColumn >= 0) {
+        if (anchor % 2) {
+            lastVisualColumn = visualColumn;
+            if (lastVisualColumn < firstVisualColumn) {
+                qSwap(firstVisualColumn, lastVisualColumn);
+                anchor = (Anchor) (anchor - 1);
+            }
+        } else {
+            firstVisualColumn = visualColumn;
+            if (firstVisualColumn > lastVisualColumn) {
+                qSwap(firstVisualColumn, lastVisualColumn);
+                anchor = (Anchor) (anchor + 1);
+            }
+        }
+    }
+
+    if (blockNumber >= 0 && blockNumber < firstBlock.document()->blockCount()) {
+        if (anchor <= TopRight) {
+            firstBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
+            if (firstBlock.blockNumber() > lastBlock.blockNumber()) {
+                qSwap(firstBlock, lastBlock);
+                anchor = (Anchor) (anchor + 2);
+            }
+        } else {
+            lastBlock.setPosition(firstBlock.document()->findBlockByNumber(blockNumber).position());
+            if (lastBlock.blockNumber() < firstBlock.blockNumber()) {
+                qSwap(firstBlock, lastBlock);
+                anchor = (Anchor) (anchor - 2);
+            }
+        }
+    }
+    firstBlock.movePosition(QTextCursor::StartOfBlock);
+    lastBlock.movePosition(QTextCursor::EndOfBlock);
+}
+
+QTextCursor Internal::BaseTextBlockSelection::selection(const TabSettings &ts) const
+{
+    QTextCursor cursor = firstBlock;
+    if (anchor <= TopRight) {
+        cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn));
+        cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn),
+                           QTextCursor::KeepAnchor);
+    } else {
+        cursor.setPosition(firstBlock.block().position() + ts.positionAtColumn(firstBlock.block().text(), firstVisualColumn));
+        cursor.setPosition(lastBlock.block().position() + ts.positionAtColumn(lastBlock.block().text(), lastVisualColumn),
+                           QTextCursor::KeepAnchor);
+    }
+    return cursor;
+}
+
+void Internal::BaseTextBlockSelection::fromSelection(const TabSettings &ts, const QTextCursor &selection)
+{
+    firstBlock = selection;
+    firstBlock.setPosition(selection.selectionStart());
+    firstVisualColumn = ts.columnAt(firstBlock.block().text(), firstBlock.positionInBlock());
+    lastBlock = selection;
+    lastBlock.setPosition(selection.selectionEnd());
+    lastVisualColumn = ts.columnAt(lastBlock.block().text(), lastBlock.positionInBlock());
+    if (selection.anchor() > selection.position())
+        anchor = TopLeft;
+    else
+        anchor = BottomRight;
+
+    firstBlock.movePosition(QTextCursor::StartOfBlock);
+    lastBlock.movePosition(QTextCursor::EndOfBlock);
+}
+bool BaseTextEditor::inFindScope(const QTextCursor &cursor)
+{
+    if (cursor.isNull())
+        return false;
+    return inFindScope(cursor.selectionStart(), cursor.selectionEnd());
+}
+
+bool BaseTextEditor::inFindScope(int selectionStart, int selectionEnd)
+{
+    if (d->m_findScopeStart.isNull())
+        return true; // no scope, everything is included
+    if (selectionStart < d->m_findScopeStart.position())
+        return false;
+    if (selectionEnd > d->m_findScopeEnd.position())
+        return false;
+    if (d->m_findScopeVerticalBlockSelectionFirstColumn < 0)
+        return true;
+    QTextBlock block = document()->findBlock(selectionStart);
+    if (block != document()->findBlock(selectionEnd))
+        return false;
+    QString text = block.text();
+    const TabSettings &ts = tabSettings();
+    int startPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionFirstColumn);
+    int endPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionLastColumn);
+    if (selectionStart - block.position() < startPosition)
+        return false;
+    if (selectionEnd - block.position() > endPosition)
+        return false;
+    return true;
+}
+
+void BaseTextEditor::handleBlockSelection(int diff_row, int diff_col)
+{
+
+    if (!d->m_inBlockSelectionMode) {
+        d->m_blockSelection.fromSelection(tabSettings(), textCursor());
+        d->m_inBlockSelectionMode = true;
+    }
+
+    d->m_blockSelection.moveAnchor(d->m_blockSelection.anchorBlockNumber() + diff_row,
+                                   d->m_blockSelection.anchorColumnNumber() + diff_col);
+    setTextCursor(d->m_blockSelection.selection(tabSettings()));
+
+    viewport()->update();
+
+    // ### TODO ensure horizontal visibility
+//    const bool rtl = q->isRightToLeft();
+//    if (cr.left() < visible.left() || cr.right() > visible.right()) {
+//        int x = cr.center().x() + horizontalOffset() - visible.width()/2;
+//        hbar->setValue(rtl ? hbar->maximum() - x : x);
+//    }
+
+}
diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h
index ecd0494fce0..2aa8f39f31e 100644
--- a/src/plugins/texteditor/basetexteditor.h
+++ b/src/plugins/texteditor/basetexteditor.h
@@ -47,6 +47,7 @@ namespace Utils {
 }
 
 namespace TextEditor {
+struct TabSettings;
 
 namespace Internal {
     class BaseTextEditorPrivate;
@@ -55,6 +56,28 @@ namespace Internal {
     struct RefactorMarker;
     typedef QList<RefactorMarker> RefactorMarkers;
 
+    class TEXTEDITOR_EXPORT BaseTextBlockSelection
+    {
+    public:
+
+        bool isValid() const{ return !firstBlock.isNull() && !lastBlock.isNull(); }
+        void clear() { firstBlock = lastBlock = QTextCursor(); }
+
+        QTextCursor firstBlock; // defines the first block
+        QTextCursor lastBlock; // defines the last block
+        int firstVisualColumn; // defines the first visual column of the selection
+        int lastVisualColumn; // defines the last visual column of the selection
+        enum Anchor {TopLeft = 0, TopRight, BottomLeft, BottomRight} anchor;
+        BaseTextBlockSelection():firstVisualColumn(0), lastVisualColumn(0), anchor(BottomRight){}
+        void moveAnchor(int blockNumber, int visualColumn);
+        inline int anchorColumnNumber() const { return (anchor % 2) ? lastVisualColumn : firstVisualColumn; }
+        inline int anchorBlockNumber() const {
+            return (anchor <= TopRight ? firstBlock.blockNumber() : lastBlock.blockNumber()); }
+        QTextCursor selection(const TabSettings &ts) const;
+        void fromSelection(const TabSettings &ts, const QTextCursor &selection);
+    };
+
+
 }
 
 class ITextMarkable;
@@ -66,8 +89,6 @@ struct BehaviorSettings;
 struct CompletionSettings;
 struct DisplaySettings;
 struct StorageSettings;
-struct TabSettings;
-
 
 class TEXTEDITOR_EXPORT BaseTextEditorAnimator : public QObject
 {
@@ -113,8 +134,8 @@ private:
 class TEXTEDITOR_EXPORT BaseTextEditor : public QPlainTextEdit
 {
     Q_OBJECT
-    Q_PROPERTY(int verticalBlockSelection READ verticalBlockSelection)
-
+    Q_PROPERTY(int verticalBlockSelectionFirstColumn READ verticalBlockSelectionFirstColumn)
+    Q_PROPERTY(int verticalBlockSelectionLastColumn READ verticalBlockSelectionLastColumn)
 public:
     BaseTextEditor(QWidget *parent);
     ~BaseTextEditor();
@@ -214,7 +235,8 @@ public:
     void insertCodeSnippet(const QTextCursor &cursor, const QString &snippet);
 
 
-    int verticalBlockSelection() const;
+    int verticalBlockSelectionFirstColumn() const;
+    int verticalBlockSelectionLastColumn() const;
 
     QRegion translatedLineRegion(int lineStart, int lineEnd) const;
 
@@ -315,7 +337,9 @@ private slots:
     void documentAboutToBeReloaded();
     void documentReloaded();
     void highlightSearchResults(const QString &txt, Find::FindFlags findFlags);
-    void setFindScope(const QTextCursor &start, const QTextCursor &end, int);
+    void setFindScope(const QTextCursor &start, const QTextCursor &end, int, int);
+    bool inFindScope(const QTextCursor &cursor);
+    bool inFindScope(int selectionStart, int selectionEnd);
     void currentEditorChanged(Core::IEditor *editor);
     void maybeEmitContentsChangedBecauseOfUndo();
 
@@ -532,6 +556,8 @@ private slots:
     // auto completion
     void _q_requestAutoCompletion();
 
+    void handleBlockSelection(int diff_row, int diff_col);
+
     // parentheses matcher
     void _q_matchParentheses();
     void _q_highlightBlocks();
diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h
index a344349be54..c16803a1bf8 100644
--- a/src/plugins/texteditor/basetexteditor_p.h
+++ b/src/plugins/texteditor/basetexteditor_p.h
@@ -245,8 +245,6 @@ public:
 
     // block selection mode
     bool m_inBlockSelectionMode;
-    bool m_lastEventWasBlockSelectionEvent;
-    int m_blockSelectionExtraX;
     void clearBlockSelection();
     QString copyBlockSelection();
     void removeBlockSelection(const QString &text = QString());
@@ -254,9 +252,13 @@ public:
 
     QTextCursor m_findScopeStart;
     QTextCursor m_findScopeEnd;
-    int m_findScopeVerticalBlockSelection;
+    int m_findScopeVerticalBlockSelectionFirstColumn;
+    int m_findScopeVerticalBlockSelectionLastColumn;
+
     QTextCursor m_selectBlockAnchor;
 
+    Internal::BaseTextBlockSelection m_blockSelection;
+
     void moveCursorVisible(bool ensureVisible = true);
 
     int visualIndent(const QTextBlock &block) const;
diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp
index e02d13d74fa..b9772bb37a2 100644
--- a/src/plugins/texteditor/tabsettings.cpp
+++ b/src/plugins/texteditor/tabsettings.cpp
@@ -226,6 +226,22 @@ int TabSettings::columnAt(const QString &text, int position) const
     return column;
 }
 
+int TabSettings::positionAtColumn(const QString &text, int column, int *offset) const
+{
+    int col = 0;
+    int i = 0;
+    while (i < text.size() && col < column) {
+        if (text.at(i) == QLatin1Char('\t'))
+            col = col - (col % m_tabSize) + m_tabSize;
+        else
+            ++col;
+        ++i;
+    }
+    if (offset)
+        *offset = column - col;
+    return i;
+}
+
 int TabSettings::spacesLeftFromPosition(const QString &text, int position) const
 {
     int i = position;
diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h
index ae63e116a81..edccbbe1986 100644
--- a/src/plugins/texteditor/tabsettings.h
+++ b/src/plugins/texteditor/tabsettings.h
@@ -61,6 +61,7 @@ struct TEXTEDITOR_EXPORT TabSettings
     int firstNonSpace(const QString &text) const;
     inline bool onlySpace(const QString &text) const { return firstNonSpace(text) == text.length(); }
     int columnAt(const QString &text, int position) const;
+    int positionAtColumn(const QString &text, int column, int *offset = 0) const;
     int spacesLeftFromPosition(const QString &text, int position) const;
     int indentedColumn(int column, bool doIndent = true) const;
     QString indentationString(int startColumn, int targetColumn, const QTextBlock &currentBlock = QTextBlock()) const;
diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp
index 20d38fae38a..a9bc6d76347 100644
--- a/src/plugins/texteditor/texteditoroverlay.cpp
+++ b/src/plugins/texteditor/texteditoroverlay.cpp
@@ -72,8 +72,7 @@ void TextEditorOverlay::clear()
 
 void TextEditorOverlay::addOverlaySelection(int begin, int end,
                                             const QColor &fg, const QColor &bg,
-                                            uint overlaySelectionFlags,
-                                            int verticalBlockSelection)
+                                            uint overlaySelectionFlags)
 {
     if (end < begin)
         return;
@@ -84,14 +83,15 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end,
     selection.m_fg = fg;
     selection.m_bg = bg;
 
+    selection.m_cursor_begin = QTextCursor(document->docHandle(), begin);
+    selection.m_cursor_end = QTextCursor(document->docHandle(), end);
+
     if (overlaySelectionFlags & ExpandBegin) {
         if (begin > 0 && begin < end) { // not empty
-            selection.m_expandBegin = true;
+            selection.m_cursor_begin.setKeepPositionOnInsert(true);
         }
     }
 
-    selection.m_cursor_begin = QTextCursor(document->docHandle(), begin);
-    selection.m_cursor_end = QTextCursor(document->docHandle(), end);
 
     if (overlaySelectionFlags & LockSize)
         selection.m_fixedLength = (end - begin);
@@ -99,8 +99,6 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end,
 
     selection.m_dropShadow = (overlaySelectionFlags & DropShadow);
 
-    selection.m_verticalBlockSelection = verticalBlockSelection;
-
     m_selections.append(selection);
     update();
 }
@@ -108,11 +106,9 @@ void TextEditorOverlay::addOverlaySelection(int begin, int end,
 
 void TextEditorOverlay::addOverlaySelection(const QTextCursor &cursor,
                                             const QColor &fg, const QColor &bg,
-                                            uint overlaySelectionFlags,
-                                            int verticalBlockSelection)
+                                            uint overlaySelectionFlags)
 {
-    addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, overlaySelectionFlags,
-                        verticalBlockSelection);
+    addOverlaySelection(cursor.selectionStart(), cursor.selectionEnd(), fg, bg, overlaySelectionFlags);
 }
 
 QRect TextEditorOverlay::rect() const
@@ -121,7 +117,7 @@ QRect TextEditorOverlay::rect() const
 }
 
 QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, const QTextCursor &end,
-                                                    const QRect &clip, int verticalBlockSelection)
+                                                    const QRect &clip)
 {
     if (begin.isNull() || end.isNull() || begin.position() > end.position())
         return QPainterPath();
@@ -173,8 +169,6 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
                 line = blockLayout->lineForTextPosition(beginChar);
                 inSelection = true;
                 firstOrLastBlock = true;
-            } else if (verticalBlockSelection) {
-                beginChar = qMin(block.length()-1, begin.positionInBlock());
             } else {
                 while (beginChar < block.length() && document->characterAt(block.position() + beginChar).isSpace())
                     ++beginChar;
@@ -189,8 +183,6 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
                 lastLine = blockLayout->lineForTextPosition(endChar).lineNumber();
                 inSelection = false;
                 firstOrLastBlock = true;
-            } else if (verticalBlockSelection) {
-                endChar = qMin(block.length()-1, begin.positionInBlock() + verticalBlockSelection);
             } else {
                 endChar = block.length();
                 while (endChar > beginChar && document->characterAt(block.position() + endChar - 1).isSpace())
@@ -211,7 +203,7 @@ QPainterPath TextEditorOverlay::createSelectionPath(const QTextCursor &begin, co
                         lineRect.setRight(line.cursorToX(endChar));
                     selection += lineRect.translated(blockGeometry.topLeft());
                 }
-            } else if (!verticalBlockSelection){ // empty lines
+            } else { // empty lines
                 const int emptyLineSelectionSize = 16;
                 if (!firstOrLastBlock && !selection.isEmpty()) { // middle
                     lineRect.setLeft(selection.last().left());
@@ -315,8 +307,6 @@ void TextEditorOverlay::paintSelection(QPainter *painter,
 {
 
     QTextCursor begin = selection.m_cursor_begin;
-    if (selection.m_expandBegin)
-        begin.setPosition(begin.position() + 1);
 
     const QTextCursor &end= selection.m_cursor_end;
     const QColor &fg = selection.m_fg;
@@ -328,8 +318,7 @@ void TextEditorOverlay::paintSelection(QPainter *painter,
         || begin.position() > end.position())
         return;
 
-    QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect(),
-                                            selection.m_verticalBlockSelection);
+    QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect());
 
     painter->save();
     QColor penColor = fg;
@@ -389,8 +378,7 @@ void TextEditorOverlay::fillSelection(QPainter *painter,
     if (begin.isNull() || end.isNull() || begin.position() > end.position())
         return;
 
-    QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect(),
-                                            selection.m_verticalBlockSelection);
+    QPainterPath path = createSelectionPath(begin, end, m_editor->viewport()->rect());
 
     painter->save();
     painter->translate(-.5, -.5);
diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h
index 7cc9593c6d1..59fa6ed7cf2 100644
--- a/src/plugins/texteditor/texteditoroverlay.h
+++ b/src/plugins/texteditor/texteditoroverlay.h
@@ -37,17 +37,14 @@ namespace TextEditor {
 namespace Internal {
 
 struct TEXTEDITOR_EXPORT OverlaySelection {
-    OverlaySelection():m_fixedLength(-1), m_dropShadow(false),
-    m_expandBegin(false), m_verticalBlockSelection(0){}
+    OverlaySelection():m_fixedLength(-1), m_dropShadow(false){}
     QTextCursor m_cursor_begin;
     QTextCursor m_cursor_end;
     QColor m_fg;
     QColor m_bg;
     int m_fixedLength;
     bool m_dropShadow;
-    bool m_expandBegin;
-    int m_verticalBlockSelection;
-};
+    };
 
 class TEXTEDITOR_EXPORT TextEditorOverlay : public QObject
 {
@@ -92,9 +89,9 @@ public:
     };
 
     void addOverlaySelection(const QTextCursor &cursor, const QColor &fg, const QColor &bg,
-                             uint overlaySelectionFlags = 0, int verticalBlockSelection = 0);
+                             uint overlaySelectionFlags = 0);
     void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg,
-                             uint overlaySelectionFlags = 0, int verticalBlockSelection = 0);
+                             uint overlaySelectionFlags = 0);
 
     inline bool isEmpty() const { return m_selections.isEmpty(); }
 
@@ -103,8 +100,7 @@ public:
     bool hasCursorInSelection(const QTextCursor &cursor) const;
 
 private:
-    QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip,
-                                     int verticalBlockSelection);
+    QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip);
     void paintSelection(QPainter *painter, const OverlaySelection &selection);
     void fillSelection(QPainter *painter, const OverlaySelection &selection, const QColor &color);
 
-- 
GitLab