Commit 7eecf394 authored by Leandro Melo's avatar Leandro Melo
Browse files

Snippets: Synchronize editing of equivalent variables inside snippets.

When editing one variable from a code snippet, all other variables
with the same name are updated accordingly.
parent 7528c6d6
...@@ -1758,16 +1758,26 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) ...@@ -1758,16 +1758,26 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
return; 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 (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) {
if (cursorMoveKeyEvent(e)) 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); QPlainTextEdit::keyPressEvent(e);
if (cursorWithinSnippet) {
cursor.endEditBlock();
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
}
}
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){ } else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
QString text = e->text(); QString text = e->text();
...@@ -1783,10 +1793,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) ...@@ -1783,10 +1793,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
} }
} }
bool cursorWithinSnippet = false;
if (d->m_snippetOverlay->isVisible()) 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) if (doEditBlock)
cursor.beginEditBlock(); cursor.beginEditBlock();
...@@ -1806,8 +1817,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) ...@@ -1806,8 +1817,11 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor()); cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
} }
if (doEditBlock) if (doEditBlock) {
cursor.endEditBlock(); cursor.endEditBlock();
if (cursorWithinSnippet)
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
}
setTextCursor(cursor); setTextCursor(cursor);
} }
...@@ -2431,10 +2445,10 @@ void BaseTextEditorPrivate::setupDocumentSignals(BaseTextDocument *document) ...@@ -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()) if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
return; return false;
QTextCursor start = cursor; QTextCursor start = cursor;
start.setPosition(cursor.selectionStart()); start.setPosition(cursor.selectionStart());
...@@ -2444,7 +2458,9 @@ void BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor) ...@@ -2444,7 +2458,9 @@ void BaseTextEditorPrivate::snippetCheckCursor(const QTextCursor &cursor)
|| !m_snippetOverlay->hasCursorInSelection(end)) { || !m_snippetOverlay->hasCursorInSelection(end)) {
m_snippetOverlay->setVisible(false); m_snippetOverlay->setVisible(false);
m_snippetOverlay->clear(); m_snippetOverlay->clear();
return false;
} }
return true;
} }
void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
...@@ -2454,8 +2470,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) ...@@ -2454,8 +2470,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
QTextCursor cursor = q->textCursor(); QTextCursor cursor = q->textCursor();
OverlaySelection final; OverlaySelection final;
if (forward) { if (forward) {
for (int i = 0; i < m_snippetOverlay->m_selections.count(); ++i){ for (int i = 0; i < m_snippetOverlay->selections().count(); ++i){
const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i); const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
if (selection.m_cursor_begin.position() >= cursor.position() if (selection.m_cursor_begin.position() >= cursor.position()
&& selection.m_cursor_end.position() > cursor.position()) { && selection.m_cursor_end.position() > cursor.position()) {
final = selection; final = selection;
...@@ -2463,8 +2479,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) ...@@ -2463,8 +2479,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
} }
} }
} else { } else {
for (int i = m_snippetOverlay->m_selections.count()-1; i >= 0; --i){ for (int i = m_snippetOverlay->selections().count()-1; i >= 0; --i){
const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i); const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
if (selection.m_cursor_end.position() < cursor.position()) { if (selection.m_cursor_end.position() < cursor.position()) {
final = selection; final = selection;
break; break;
...@@ -2473,7 +2489,7 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) ...@@ -2473,7 +2489,7 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
} }
if (final.m_cursor_begin.isNull()) 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 if (final.m_cursor_begin.position() == final.m_cursor_end.position()) { // empty tab stop
cursor.setPosition(final.m_cursor_end.position()); cursor.setPosition(final.m_cursor_end.position());
...@@ -2484,7 +2500,6 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) ...@@ -2484,7 +2500,6 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward)
q->setTextCursor(cursor); q->setTextCursor(cursor);
} }
bool BaseTextEditor::viewportEvent(QEvent *event) bool BaseTextEditor::viewportEvent(QEvent *event)
{ {
d->m_contentsChanged = false; d->m_contentsChanged = false;
...@@ -4382,18 +4397,17 @@ void BaseTextEditor::handleHomeKey(bool anchor) ...@@ -4382,18 +4397,17 @@ void BaseTextEditor::handleHomeKey(bool anchor)
setTextCursor(cursor); setTextCursor(cursor);
} }
#define SET_AND_RETURN(cursor) setTextCursor(cursor); return // make cursor visible and reset vertical x movement
void BaseTextEditor::handleBackspaceKey() void BaseTextEditor::handleBackspaceKey()
{ {
QTextCursor cursor = textCursor(); QTextCursor cursor = textCursor();
int pos = cursor.position(); int pos = cursor.position();
QTC_ASSERT(!cursor.hasSelection(), return); QTC_ASSERT(!cursor.hasSelection(), return);
bool cursorWithinSnippet = false;
if (d->m_snippetOverlay->isVisible()) { if (d->m_snippetOverlay->isVisible()) {
QTextCursor snippetCursor = cursor; QTextCursor snippetCursor = cursor;
snippetCursor.movePosition(QTextCursor::Left); snippetCursor.movePosition(QTextCursor::Left);
d->snippetCheckCursor(snippetCursor); cursorWithinSnippet = d->snippetCheckCursor(snippetCursor);
} }
const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings(); const TextEditor::TabSettings &tabSettings = d->m_document->tabSettings();
...@@ -4401,40 +4415,58 @@ void BaseTextEditor::handleBackspaceKey() ...@@ -4401,40 +4415,58 @@ void BaseTextEditor::handleBackspaceKey()
if (tabSettings.m_autoIndent && d->m_autoCompleter->autoBackspace(cursor)) if (tabSettings.m_autoIndent && d->m_autoCompleter->autoBackspace(cursor))
return; return;
bool handled = false;
if (!tabSettings.m_smartBackspace) { if (!tabSettings.m_smartBackspace) {
if (cursorWithinSnippet)
cursor.beginEditBlock();
cursor.deletePreviousChar(); 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(); if (!handled) {
int positionInBlock = pos - currentBlock.position(); if (cursorWithinSnippet)
const QString blockText = currentBlock.text(); cursor.beginEditBlock();
if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
cursor.deletePreviousChar(); cursor.deletePreviousChar();
SET_AND_RETURN(cursor);
} }
int previousIndent = 0; if (cursorWithinSnippet) {
const int indent = tabSettings.columnAt(blockText, positionInBlock); cursor.endEditBlock();
d->m_snippetOverlay->updateEquivalentSelections(cursor);
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);
}
} }
cursor.deletePreviousChar();
SET_AND_RETURN(cursor); setTextCursor(cursor);
} }
void BaseTextEditor::wheelEvent(QWheelEvent *e) void BaseTextEditor::wheelEvent(QWheelEvent *e)
...@@ -4997,6 +5029,7 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe ...@@ -4997,6 +5029,7 @@ void BaseTextEditor::setExtraSelections(ExtraSelectionKind kind, const QList<QTe
selection.format.background().color(), selection.format.background().color(),
TextEditorOverlay::ExpandBegin); TextEditorOverlay::ExpandBegin);
} }
d->m_snippetOverlay->mapEquivalentSelections();
d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty()); d->m_snippetOverlay->setVisible(!d->m_snippetOverlay->isEmpty());
} else { } else {
QList<QTextEdit::ExtraSelection> all; QList<QTextEdit::ExtraSelection> all;
......
...@@ -215,7 +215,7 @@ public: ...@@ -215,7 +215,7 @@ public:
TextEditorOverlay *m_overlay; TextEditorOverlay *m_overlay;
TextEditorOverlay *m_snippetOverlay; TextEditorOverlay *m_snippetOverlay;
TextEditorOverlay *m_searchResultOverlay; TextEditorOverlay *m_searchResultOverlay;
void snippetCheckCursor(const QTextCursor &cursor); bool snippetCheckCursor(const QTextCursor &cursor);
void snippetTabOrBacktab(bool forward); void snippetTabOrBacktab(bool forward);
QTextCharFormat m_occurrencesFormat; QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrenceRenameFormat; QTextCharFormat m_occurrenceRenameFormat;
......
...@@ -30,20 +30,22 @@ ...@@ -30,20 +30,22 @@
#include "texteditoroverlay.h" #include "texteditoroverlay.h"
#include "basetexteditor.h" #include "basetexteditor.h"
#include <QtCore/QMap>
#include <QtGui/QPainter> #include <QtGui/QPainter>
#include <QtGui/QTextBlock> #include <QtGui/QTextBlock>
using namespace TextEditor; using namespace TextEditor;
using namespace TextEditor::Internal; using namespace TextEditor::Internal;
TextEditorOverlay::TextEditorOverlay(BaseTextEditor *editor) TextEditorOverlay::TextEditorOverlay(BaseTextEditor *editor) :
:QObject(editor) { QObject(editor),
m_visible = false; m_visible(false),
m_borderWidth = 1; m_borderWidth(1),
m_dropShadowWidth = 2; m_dropShadowWidth(2),
m_editor = editor; m_alpha(true),
m_alpha = true; m_editor(editor),
m_viewport = editor->viewport(); m_viewport(editor->viewport())
{
} }
void TextEditorOverlay::update() void TextEditorOverlay::update()
...@@ -447,12 +449,79 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect ...@@ -447,12 +449,79 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect
start or end of a selection is counted as contained. start or end of a selection is counted as contained.
*/ */
bool TextEditorOverlay::hasCursorInSelection(const QTextCursor &cursor) const 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) { for (int i = 0; i < m_selections.size(); ++i) {
const OverlaySelection &selection = m_selections.at(i); const OverlaySelection &selection = m_selections.at(i);
if (cursor.position() >= selection.m_cursor_begin.position() if (cursor.position() >= selection.m_cursor_begin.position()
&& cursor.position() <= selection.m_cursor_end.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;
} }
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
#define TEXTEDITOROVERLAY_H #define TEXTEDITOROVERLAY_H
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QVector>
#include <QtGui/QTextCursor> #include <QtGui/QTextCursor>
#include <QtGui/QColor> #include <QtGui/QColor>
...@@ -45,6 +46,7 @@ namespace Internal { ...@@ -45,6 +46,7 @@ namespace Internal {
struct OverlaySelection struct OverlaySelection
{ {
OverlaySelection():m_fixedLength(-1), m_dropShadow(false){} OverlaySelection():m_fixedLength(-1), m_dropShadow(false){}
QTextCursor m_cursor_begin; QTextCursor m_cursor_begin;
QTextCursor m_cursor_end; QTextCursor m_cursor_end;
QColor m_fg; QColor m_fg;
...@@ -56,18 +58,6 @@ struct OverlaySelection ...@@ -56,18 +58,6 @@ struct OverlaySelection
class TextEditorOverlay : public QObject class TextEditorOverlay : public QObject
{ {
Q_OBJECT 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: public:
TextEditorOverlay(BaseTextEditor *editor); TextEditorOverlay(BaseTextEditor *editor);
...@@ -100,16 +90,33 @@ public: ...@@ -100,16 +90,33 @@ public:
void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg, void addOverlaySelection(int begin, int end, const QColor &fg, const QColor &bg,
uint overlaySelectionFlags = 0); uint overlaySelectionFlags = 0);
const QList<OverlaySelection> &selections() const { return m_selections; }
inline bool isEmpty() const { return m_selections.isEmpty(); } inline bool isEmpty() const { return m_selections.isEmpty(); }
inline int dropShadowWidth() const { return m_dropShadowWidth; } inline int dropShadowWidth() const { return m_dropShadowWidth; }
bool hasCursorInSelection(const QTextCursor &cursor) const; bool hasCursorInSelection(const QTextCursor &cursor) const;
void mapEquivalentSelections();
void updateEquivalentSelections(const QTextCursor &cursor);
private: private:
QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip); QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip);
void paintSelection(QPainter *painter, const OverlaySelection &selection); void paintSelection(QPainter *painter, const OverlaySelection &selection);
void fillSelection(QPainter *painter, const OverlaySelection &selection, const QColor &color); 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 } // namespace Internal
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment