OSDN Git Service

Snippets: Synchronize editing of equivalent variables inside snippets.
authorLeandro Melo <leandro.melo@nokia.com>
Tue, 2 Nov 2010 14:35:42 +0000 (15:35 +0100)
committerLeandro Melo <leandro.melo@nokia.com>
Wed, 8 Dec 2010 16:22:08 +0000 (17:22 +0100)
When editing one variable from a code snippet, all other variables
with the same name are updated accordingly.

src/plugins/texteditor/basetexteditor.cpp
src/plugins/texteditor/basetexteditor_p.h
src/plugins/texteditor/texteditoroverlay.cpp
src/plugins/texteditor/texteditoroverlay.h

index d429277..bf76217 100644 (file)
@@ -1758,16 +1758,26 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
         return;
     }
 
-    if (d->m_snippetOverlay->isVisible()
-        && (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) {
-        d->snippetCheckCursor(textCursor());
-    }
-
     if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
         if (cursorMoveKeyEvent(e))
             ;
-        else
+        else {
+            QTextCursor cursor = textCursor();
+            bool cursorWithinSnippet = false;
+            if (d->m_snippetOverlay->isVisible()
+                && (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) {
+                cursorWithinSnippet = d->snippetCheckCursor(cursor);
+            }
+            if (cursorWithinSnippet)
+                cursor.beginEditBlock();
+
             QPlainTextEdit::keyPressEvent(e);
+
+            if (cursorWithinSnippet) {
+                cursor.endEditBlock();
+                d->m_snippetOverlay->updateEquivalentSelections(textCursor());
+            }
+        }
     } else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
         QTextCursor cursor = textCursor();
         QString text = e->text();
@@ -1783,10 +1793,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
             }
         }
 
+        bool cursorWithinSnippet = false;
         if (d->m_snippetOverlay->isVisible())
-            d->snippetCheckCursor(cursor);
+            cursorWithinSnippet = d->snippetCheckCursor(cursor);
 
-        bool doEditBlock = !(electricChar.isNull() && autoText.isEmpty());
+        bool doEditBlock = !electricChar.isNull() || !autoText.isEmpty() || cursorWithinSnippet;
         if (doEditBlock)
             cursor.beginEditBlock();
 
@@ -1806,8 +1817,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
             cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
         }
 
-        if (doEditBlock)
+        if (doEditBlock) {
             cursor.endEditBlock();
+            if (cursorWithinSnippet)
+                d->m_snippetOverlay->updateEquivalentSelections(textCursor());
+        }
 
         setTextCursor(cursor);
     }
@@ -2431,10 +2445,10 @@ void BaseTextEditorPrivate::setupDocumentSignals(BaseTextDocument *document)
 }
 
 
-void BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
+bool BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
 {
     if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
-        return;
+        return false;
 
     QTextCursor start = cursor;
     start.setPosition(cursor.selectionStart());
@@ -2444,7 +2458,9 @@ void BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
         || !m_snippetOverlay->hasCursorInSelection(end)) {
         m_snippetOverlay->setVisible(false);
         m_snippetOverlay->clear();
+        return false;
     }
+    return true;
 }
 
 void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
@@ -2454,8 +2470,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
     QTextCursor cursor = q->textCursor();
     OverlaySelection final;
     if (forward) {
-        for (int i = 0; i < m_snippetOverlay->m_selections.count(); ++i){
-            const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i);
+        for (int i = 0; i < m_snippetOverlay->selections().count(); ++i){
+            const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
             if (selection.m_cursor_begin.position() >= cursor.position()
                 && selection.m_cursor_end.position() > cursor.position()) {
                 final = selection;
@@ -2463,8 +2479,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
             }
         }
     } else {
-        for (int i = m_snippetOverlay->m_selections.count()-1; i >= 0; --i){
-            const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i);
+        for (int i = m_snippetOverlay->selections().count()-1; i >= 0; --i){
+            const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
             if (selection.m_cursor_end.position() < cursor.position()) {
                 final = selection;
                 break;
@@ -2473,7 +2489,7 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
 
     }
     if (final.m_cursor_begin.isNull())
-        final = forward ? m_snippetOverlay->m_selections.first() : m_snippetOverlay->m_selections.last();
+        final = forward ? m_snippetOverlay->selections().first() : m_snippetOverlay->selections().last();
 
     if (final.m_cursor_begin.position() == final.m_cursor_end.position()) { // empty tab stop
         cursor.setPosition(final.m_cursor_end.position());
@@ -2484,7 +2500,6 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
     q->setTextCursor(cursor);
 }
 
-
 bool BaseTextEditor::viewportEvent(QEvent *event)
 {
     d->m_contentsChanged = false;
@@ -4382,18 +4397,17 @@ void BaseTextEditor::handleHomeKey(bool anchor)
     setTextCursor(cursor);
 }
 
-
-#define SET_AND_RETURN(cursor) setTextCursor(cursor); return  // make cursor visible and reset vertical x movement
 void BaseTextEditor::handleBackspaceKey()
 {
     QTextCursor cursor = textCursor();
     int pos = cursor.position();
     QTC_ASSERT(!cursor.hasSelection(), return);
 
+    bool cursorWithinSnippet = false;
     if (d->m_snippetOverlay->isVisible()) {
         QTextCursor snippetCursor = cursor;
         snippetCursor.movePosition(QTextCursor::Left);
-        d->snippetCheckCursor(snippetCursor);
+        cursorWithinSnippet = d->snippetCheckCursor(snippetCursor);
     }
 
     const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
@@ -4401,40 +4415,58 @@ void BaseTextEditor::handleBackspaceKey()
     if (tabSettings.m_autoIndent && d->m_autoCompleter->autoBackspace(cursor))
         return;
 
+    bool handled = false;
     if (!tabSettings.m_smartBackspace) {
+        if (cursorWithinSnippet)
+            cursor.beginEditBlock();
         cursor.deletePreviousChar();
-        SET_AND_RETURN(cursor);
+        handled = true;
+    } else {
+        QTextBlock currentBlock = cursor.block();
+        int positionInBlock = pos - currentBlock.position();
+        const QString blockText = currentBlock.text();
+        if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
+            if (cursorWithinSnippet)
+                cursor.beginEditBlock();
+            cursor.deletePreviousChar();
+            handled = true;
+        } else {
+            int previousIndent = 0;
+            const int indent = tabSettings.columnAt(blockText, positionInBlock);
+
+            for (QTextBlock previousNonEmptyBlock = currentBlock.previous();
+                 previousNonEmptyBlock.isValid();
+                 previousNonEmptyBlock = previousNonEmptyBlock.previous()) {
+                QString previousNonEmptyBlockText = previousNonEmptyBlock.text();
+                if (previousNonEmptyBlockText.trimmed().isEmpty())
+                    continue;
+                previousIndent =
+                    tabSettings.columnAt(previousNonEmptyBlockText,
+                                         tabSettings.firstNonSpace(previousNonEmptyBlockText));
+                if (previousIndent < indent) {
+                    cursor.beginEditBlock();
+                    cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
+                    cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
+                    cursor.endEditBlock();
+                    handled = true;
+                    break;
+                }
+            }
+        }
     }
 
-    QTextBlock currentBlock = cursor.block();
-    int positionInBlock = pos - currentBlock.position();
-    const QString blockText = currentBlock.text();
-    if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
+    if (!handled) {
+        if (cursorWithinSnippet)
+            cursor.beginEditBlock();
         cursor.deletePreviousChar();
-        SET_AND_RETURN(cursor);
     }
 
-    int previousIndent = 0;
-    const int indent = tabSettings.columnAt(blockText, positionInBlock);
-
-    for (QTextBlock previousNonEmptyBlock = currentBlock.previous();
-         previousNonEmptyBlock.isValid();
-         previousNonEmptyBlock = previousNonEmptyBlock.previous()) {
-        QString previousNonEmptyBlockText = previousNonEmptyBlock.text();
-        if (previousNonEmptyBlockText.trimmed().isEmpty())
-            continue;
-        previousIndent = tabSettings.columnAt(previousNonEmptyBlockText,
-                                              tabSettings.firstNonSpace(previousNonEmptyBlockText));
-        if (previousIndent < indent) {
-            cursor.beginEditBlock();
-            cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
-            cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
-            cursor.endEditBlock();
-            SET_AND_RETURN(cursor);
-        }
+    if (cursorWithinSnippet) {
+        cursor.endEditBlock();
+        d->m_snippetOverlay->updateEquivalentSelections(cursor);
     }
-    cursor.deletePreviousChar();
-    SET_AND_RETURN(cursor);
+
+    setTextCursor(cursor);
 }
 
 void BaseTextEditor::wheelEvent(QWheelEvent *e)
@@ -4997,6 +5029,7 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe
                                               selection.format.background().color(),
                                               TextEditorOverlay::ExpandBegin);
         }
+        d->m_snippetOverlay->mapEquivalentSelections();
         d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty());
     } else {
         QList<QTextEdit::ExtraSelection> all;
index d658899..f4d688c 100644 (file)
@@ -215,7 +215,7 @@ public:
     TextEditorOverlay *m_overlay;
     TextEditorOverlay *m_snippetOverlay;
     TextEditorOverlay *m_searchResultOverlay;
-    void snippetCheckCursor(const QTextCursor &cursor);
+    bool snippetCheckCursor(const QTextCursor &cursor);
     void snippetTabOrBacktab(bool forward);
     QTextCharFormat m_occurrencesFormat;
     QTextCharFormat m_occurrenceRenameFormat;
index 8cfb018..5059b20 100644 (file)
 #include "texteditoroverlay.h"
 #include "basetexteditor.h"
 
+#include <QtCore/QMap>
 #include <QtGui/QPainter>
 #include <QtGui/QTextBlock>
 
 using namespace TextEditor;
 using namespace TextEditor::Internal;
 
-TextEditorOverlay::TextEditorOverlay(BaseTextEditor *editor)
-    :QObject(editor) {
-    m_visible = false;
-    m_borderWidth = 1;
-    m_dropShadowWidth = 2;
-    m_editor = editor;
-    m_alpha = true;
-    m_viewport = editor->viewport();
+TextEditorOverlay::TextEditorOverlay(BaseTextEditor *editor) :
+    QObject(editor),
+    m_visible(false),
+    m_borderWidth(1),
+    m_dropShadowWidth(2),
+    m_alpha(true),
+    m_editor(editor),
+    m_viewport(editor->viewport())
+{
 }
 
 void TextEditorOverlay::update()
@@ -448,11 +450,78 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect
 */
 bool TextEditorOverlay::hasCursorInSelection(const QTextCursor &cursor) const
 {
+    if (selectionIndexForCursor(cursor) != -1)
+        return true;
+    return false;
+}
+
+int TextEditorOverlay::selectionIndexForCursor(const QTextCursor &cursor) const
+{
     for (int i = 0; i < m_selections.size(); ++i) {
         const OverlaySelection &selection = m_selections.at(i);
         if (cursor.position() >= selection.m_cursor_begin.position()
             && cursor.position() <= selection.m_cursor_end.position())
-            return true;
+            return i;
+    }
+    return -1;
+}
+
+QString TextEditorOverlay::selectionText(int selectionIndex) const
+{
+    return assembleCursorForSelection(selectionIndex).selectedText();
+}
+
+QTextCursor TextEditorOverlay::assembleCursorForSelection(int selectionIndex) const
+{
+    const OverlaySelection &selection = m_selections.at(selectionIndex);
+    QTextCursor cursor(m_editor->document());
+    cursor.setPosition(selection.m_cursor_begin.position());
+    cursor.setPosition(selection.m_cursor_end.position(), QTextCursor::KeepAnchor);
+    return cursor;
+}
+
+void TextEditorOverlay::mapEquivalentSelections()
+{
+    m_equivalentSelections.clear();
+    m_equivalentSelections.resize(m_selections.size());
+
+    QMap<QString, int> all;
+    for (int i = 0; i < m_selections.size(); ++i)
+        all.insertMulti(selectionText(i), i);
+
+    const QList<QString> &uniqueKeys = all.uniqueKeys();
+    foreach (const QString &key, uniqueKeys) {
+        QList<int> indexes;
+        QMap<QString, int>::const_iterator lbit = all.lowerBound(key);
+        QMap<QString, int>::const_iterator ubit = all.upperBound(key);
+        while (lbit != ubit) {
+            indexes.append(lbit.value());
+            ++lbit;
+        }
+
+        foreach (int index, indexes)
+            m_equivalentSelections[index] = indexes;
+    }
+}
+
+void TextEditorOverlay::updateEquivalentSelections(const QTextCursor &cursor)
+{
+    int selectionIndex = selectionIndexForCursor(cursor);
+    if (selectionIndex == -1)
+        return;
+
+    const QString &currentText = selectionText(selectionIndex);
+    const QList<int> &equivalents = m_equivalentSelections.at(selectionIndex);
+    foreach (int i, equivalents) {
+        if (i == selectionIndex)
+            continue;
+        const QString &equivalentText = selectionText(i);
+        if (currentText != equivalentText) {
+            QTextCursor selectionCursor = assembleCursorForSelection(i);
+            selectionCursor.joinPreviousEditBlock();
+            selectionCursor.removeSelectedText();
+            selectionCursor.insertText(currentText);
+            selectionCursor.endEditBlock();
+        }
     }
-    return false;
 }
index f64bb08..48a61ad 100644 (file)
@@ -31,7 +31,8 @@
 #define TEXTEDITOROVERLAY_H
 
 #include <QtCore/QObject>
-
+#include <QtCore/QList>
+#include <QtCore/QVector>
 #include <QtGui/QTextCursor>
 #include <QtGui/QColor>
 
@@ -45,6 +46,7 @@ namespace Internal {
 struct OverlaySelection
 {
     OverlaySelection():m_fixedLength(-1), m_dropShadow(false){}
+
     QTextCursor m_cursor_begin;
     QTextCursor m_cursor_end;
     QColor m_fg;
@@ -56,18 +58,6 @@ struct OverlaySelection
 class TextEditorOverlay : public QObject
 {
     Q_OBJECT
-    BaseTextEditor *m_editor;
-    QWidget *m_viewport;
-
-public:
-    QList<OverlaySelection> m_selections;
-
-private:
-    bool m_visible;
-    int m_borderWidth;
-    int m_dropShadowWidth;
-    bool m_alpha;
-
 public:
     TextEditorOverlay(BaseTextEditor *editor);
 
@@ -100,16 +90,33 @@ public:
     void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg,
                              uint overlaySelectionFlags = 0);
 
+    const QList<OverlaySelection> &selections() const { return m_selections; }
+
     inline bool isEmpty() const { return m_selections.isEmpty(); }
 
     inline int dropShadowWidth() const { return m_dropShadowWidth; }
 
     bool hasCursorInSelection(const QTextCursor &cursor) const;
 
+    void mapEquivalentSelections();
+    void updateEquivalentSelections(const QTextCursor &cursor);
+
 private:
     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);
+    int selectionIndexForCursor(const QTextCursor &cursor) const;
+    QString selectionText(int selectionIndex) const;
+    QTextCursor assembleCursorForSelection(int selectionIndex) const;
+
+    bool m_visible;
+    int m_borderWidth;
+    int m_dropShadowWidth;
+    bool m_alpha;
+    BaseTextEditor *m_editor;
+    QWidget *m_viewport;
+    QList<OverlaySelection> m_selections;
+    QVector<QList<int> > m_equivalentSelections;
 };
 
 } // namespace Internal