diff --git a/src/plugins/texteditor/snippets/snippet.cpp b/src/plugins/texteditor/snippets/snippet.cpp
index ea4671596cf..fbe00be39d7 100644
--- a/src/plugins/texteditor/snippets/snippet.cpp
+++ b/src/plugins/texteditor/snippets/snippet.cpp
@@ -170,34 +170,10 @@ bool Snippet::isModified() const
return m_isModified;
}
-struct SnippetReplacement
-{
- QString text;
- int posDelta = 0;
-};
-
-static SnippetReplacement replacementAt(int pos, ParsedSnippet &parsedSnippet)
+static QString tipPart(const ParsedSnippet::Part &part)
{
static const char kOpenBold[] = "";
static const char kCloseBold[] = "";
-
- auto mangledText = [](const QString &text, const ParsedSnippet::Range &range) {
- if (range.length == 0)
- return QString("...");
- if (NameMangler *mangler = range.mangler)
- return mangler->mangle(text.mid(range.start, range.length));
- return text.mid(range.start, range.length);
- };
-
- if (!parsedSnippet.ranges.isEmpty() && parsedSnippet.ranges.first().start == pos) {
- ParsedSnippet::Range range = parsedSnippet.ranges.takeFirst();
- return {kOpenBold + mangledText(parsedSnippet.text, range) + kCloseBold, range.length};
- }
- return {};
-}
-
-QString Snippet::generateTip() const
-{
static const QHash replacements = {{'\n', "
"},
{' ', " "},
{'"', """},
@@ -205,34 +181,30 @@ QString Snippet::generateTip() const
{'<', "<"},
{'>', ">"}};
+ QString text;
+ text.reserve(part.text.size());
+
+ for (const QChar &c : part.text)
+ text.append(replacements.value(c, c));
+
+ if (part.variableIndex >= 0)
+ text = kOpenBold + (text.isEmpty() ? QString("...") : part.text) + kCloseBold;
+
+ return text;
+}
+
+QString Snippet::generateTip() const
+{
SnippetParseResult result = Snippet::parse(m_content);
if (Utils::holds_alternative(result))
return Utils::get(result).htmlMessage();
QTC_ASSERT(Utils::holds_alternative(result), return {});
- auto parsedSnippet = Utils::get(result);
+ const ParsedSnippet parsedSnippet = Utils::get(result);
QString tip("");
- int pos = 0;
- for (int end = parsedSnippet.text.count(); pos < end;) {
- const SnippetReplacement &replacement = replacementAt(pos, parsedSnippet);
- if (!replacement.text.isEmpty()) {
- tip += replacement.text;
- pos += replacement.posDelta;
- } else {
- const QChar ¤tChar = parsedSnippet.text.at(pos);
- tip += replacements.value(currentChar, currentChar);
- ++pos;
- }
- }
- SnippetReplacement replacement = replacementAt(pos, parsedSnippet);
- while (!replacement.text.isEmpty()) {
- tip += replacement.text;
- pos += replacement.posDelta;
- replacement = replacementAt(pos, parsedSnippet);
- }
-
- QTC_CHECK(parsedSnippet.ranges.isEmpty());
+ for (const ParsedSnippet::Part &part : parsedSnippet.parts)
+ tip.append(tipPart(part));
return tip;
}
@@ -253,25 +225,35 @@ SnippetParseResult Snippet::parse(const QString &snippet)
return {SnippetParseError{errorMessage, {}, -1}};
const int count = preprocessedSnippet.count();
- int start = -1;
NameMangler *mangler = nullptr;
- result.text.reserve(count);
+ QMap variableIndexes;
+ bool inVar = false;
+
+ ParsedSnippet::Part currentPart;
for (int i = 0; i < count; ++i) {
QChar current = preprocessedSnippet.at(i);
- QChar next = (i + 1) < count ? preprocessedSnippet.at(i + 1) : QChar();
if (current == Snippet::kVariableDelimiter) {
- if (start < 0) {
- // start delimiter:
- start = result.text.count();
- } else {
- int length = result.text.count() - start;
- result.ranges << ParsedSnippet::Range(start, length, mangler);
+ if (inVar) {
+ const QString variable = currentPart.text;
+ const int index = variableIndexes.value(currentPart.text, result.variables.size());
+ if (index == result.variables.size()) {
+ variableIndexes[variable] = index;
+ result.variables.append(QList());
+ }
+ currentPart.variableIndex = index;
+ currentPart.mangler = mangler;
mangler = nullptr;
- start = -1;
+ result.variables[index] << result.parts.size() - 1;
+ } else if (currentPart.text.isEmpty()) {
+ inVar = !inVar;
+ continue;
}
+ result.parts << currentPart;
+ currentPart = ParsedSnippet::Part();
+ inVar = !inVar;
continue;
}
@@ -281,12 +263,13 @@ SnippetParseResult Snippet::parse(const QString &snippet)
i}};
}
- if (current == QLatin1Char(':') && start >= 0) {
- if (next == QLatin1Char('l')) {
+ if (current == ':' && inVar) {
+ QChar next = (i + 1) < count ? preprocessedSnippet.at(i + 1) : QChar();
+ if (next == 'l') {
mangler = &lcMangler;
- } else if (next == QLatin1Char('u')) {
+ } else if (next == 'u') {
mangler = &ucMangler;
- } else if (next == QLatin1Char('c')) {
+ } else if (next == 'c') {
mangler = &tcMangler;
} else {
return SnippetParseResult{
@@ -299,20 +282,26 @@ SnippetParseResult Snippet::parse(const QString &snippet)
continue;
}
- if (current == kEscapeChar && (next == kEscapeChar || next == kVariableDelimiter)) {
- result.text.append(next);
- ++i;
- continue;
+ if (current == kEscapeChar){
+ QChar next = (i + 1) < count ? preprocessedSnippet.at(i + 1) : QChar();
+ if (next == kEscapeChar || next == kVariableDelimiter) {
+ currentPart.text.append(next);
+ ++i;
+ continue;
+ }
}
- result.text.append(current);
+ currentPart.text.append(current);
}
- if (start >= 0) {
+ if (inVar) {
return SnippetParseResult{
- SnippetParseError{tr("Missing closing variable delimiter"), result.text, start}};
+ SnippetParseError{tr("Missing closing variable delimiter for:"), currentPart.text, 0}};
}
+ if (!currentPart.text.isEmpty())
+ result.parts << currentPart;
+
return SnippetParseResult(result);
}
@@ -323,131 +312,130 @@ SnippetParseResult Snippet::parse(const QString &snippet)
const char NOMANGLER_ID[] = "TextEditor::NoMangler";
+struct SnippetPart
+{
+ SnippetPart() = default;
+ explicit SnippetPart(const QString &text,
+ int index = -1,
+ const Utils::Id &manglerId = NOMANGLER_ID)
+ : text(text)
+ , variableIndex(index)
+ , manglerId(manglerId)
+ {}
+ QString text;
+ int variableIndex = -1; // if variable index is >= 0 the text is interpreted as a variable
+ Utils::Id manglerId;
+};
+Q_DECLARE_METATYPE(SnippetPart);
+
+using Parts = QList;
+
void Internal::TextEditorPlugin::testSnippetParsing_data()
{
QTest::addColumn("input");
- QTest::addColumn("text");
QTest::addColumn("success");
- QTest::addColumn >("ranges_start");
- QTest::addColumn >("ranges_length");
- QTest::addColumn >("ranges_mangler");
+ QTest::addColumn("parts");
- QTest::newRow("no input")
- << QString() << QString() << true
- << (QList()) << (QList()) << (QList());
- QTest::newRow("empty input")
- << QString::fromLatin1("") << QString::fromLatin1("") << true
- << (QList()) << (QList()) << (QList());
- QTest::newRow("newline only")
- << QString::fromLatin1("\n") << QString::fromLatin1("\n") << true
- << (QList()) << (QList()) << (QList());
+ QTest::newRow("no input") << QString() << true << Parts();
+ QTest::newRow("empty input") << QString("") << true << Parts();
+ QTest::newRow("newline only") << QString("\n") << true << Parts{SnippetPart("\n")};
QTest::newRow("simple identifier")
- << QString::fromLatin1("$tESt$") << QString::fromLatin1("tESt") << true
- << (QList() << 0) << (QList() << 4)
- << (QList() << NOMANGLER_ID);
+ << QString("$tESt$") << true << Parts{SnippetPart("tESt", 0)};
QTest::newRow("simple identifier with lc")
- << QString::fromLatin1("$tESt:l$") << QString::fromLatin1("tESt") << true
- << (QList() << 0) << (QList() << 4)
- << (QList() << LCMANGLER_ID);
+ << QString("$tESt:l$") << true << Parts{SnippetPart("tESt", 0, LCMANGLER_ID)};
QTest::newRow("simple identifier with uc")
- << QString::fromLatin1("$tESt:u$") << QString::fromLatin1("tESt") << true
- << (QList() << 0) << (QList() << 4)
- << (QList() << UCMANGLER_ID);
+ << QString("$tESt:u$") << true << Parts{SnippetPart("tESt", 0, UCMANGLER_ID)};
QTest::newRow("simple identifier with tc")
- << QString::fromLatin1("$tESt:c$") << QString::fromLatin1("tESt") << true
- << (QList() << 0) << (QList() << 4)
- << (QList() << TCMANGLER_ID);
+ << QString("$tESt:c$") << true << Parts{SnippetPart("tESt", 0, TCMANGLER_ID)};
QTest::newRow("escaped string")
- << QString::fromLatin1("\\\\$test\\\\$") << QString::fromLatin1("$test$") << true
- << (QList()) << (QList())
- << (QList());
- QTest::newRow("escaped escape")
- << QString::fromLatin1("\\\\\\\\$test$\\\\\\\\") << QString::fromLatin1("\\test\\") << true
- << (QList() << 1) << (QList() << 4)
- << (QList() << NOMANGLER_ID);
+ << QString("\\\\$test\\\\$") << true << Parts{SnippetPart("$test$")};
+ QTest::newRow("escaped escape") << QString("\\\\\\\\$test$\\\\\\\\") << true
+ << Parts{
+ SnippetPart("\\"),
+ SnippetPart("test", 0),
+ SnippetPart("\\"),
+ };
QTest::newRow("broken escape")
- << QString::fromLatin1("\\\\$test\\\\\\\\$\\\\") << QString::fromLatin1("\\$test\\\\$\\") << false
- << (QList()) << (QList())
- << (QList());
+ << QString::fromLatin1("\\\\$test\\\\\\\\$\\\\") << false << Parts();
- QTest::newRow("Q_PROPERTY")
- << QString::fromLatin1("Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed)")
- << QString::fromLatin1("Q_PROPERTY(type name READ name WRITE setname NOTIFY nameChanged)") << true
- << (QList() << 11 << 16 << 26 << 40 << 52)
- << (QList() << 4 << 4 << 4 << 4 << 4)
- << (QList() << NOMANGLER_ID << NOMANGLER_ID << NOMANGLER_ID << TCMANGLER_ID << NOMANGLER_ID);
+ QTest::newRow("Q_PROPERTY") << QString(
+ "Q_PROPERTY($type$ $name$ READ $name$ WRITE set$name:c$ NOTIFY $name$Changed)")
+ << true
+ << Parts{SnippetPart("Q_PROPERTY("),
+ SnippetPart("type", 0),
+ SnippetPart(" "),
+ SnippetPart("name", 1),
+ SnippetPart(" READ "),
+ SnippetPart("name", 1),
+ SnippetPart(" WRITE set"),
+ SnippetPart("name", 1, TCMANGLER_ID),
+ SnippetPart(" NOTIFY "),
+ SnippetPart("name", 1),
+ SnippetPart("Changed)")};
- QTest::newRow("open identifier")
- << QString::fromLatin1("$test") << QString::fromLatin1("$test") << false
- << (QList()) << (QList())
- << (QList());
- QTest::newRow("wrong mangler")
- << QString::fromLatin1("$test:X$") << QString::fromLatin1("$test:X$") << false
- << (QList()) << (QList())
- << (QList());
+ QTest::newRow("open identifier") << QString("$test") << false << Parts();
+ QTest::newRow("wrong mangler") << QString("$test:X$") << false << Parts();
- QTest::newRow("multiline with :")
- << QString::fromLatin1("class $name$\n"
- "{\n"
- "public:\n"
- " $name$() {}\n"
- "};")
- << QString::fromLatin1("class name\n"
- "{\n"
- "public:\n"
- " name() {}\n"
- "};")
- << true
- << (QList() << 6 << 25)
- << (QList() << 4 << 4)
- << (QList() << NOMANGLER_ID << NOMANGLER_ID);
-
- QTest::newRow("escape sequences")
- << QString::fromLatin1("class $name$\\n"
- "{\\n"
- "public\\\\:\\n"
- "\\t$name$() {}\\n"
- "};")
- << QString::fromLatin1("class name\n"
- "{\n"
- "public\\:\n"
- "\tname() {}\n"
- "};")
- << true
- << (QList() << 6 << 23)
- << (QList() << 4 << 4)
- << (QList() << NOMANGLER_ID << NOMANGLER_ID);
+ QTest::newRow("multiline with :") << QString("class $name$\n"
+ "{\n"
+ "public:\n"
+ " $name$() {}\n"
+ "};")
+ << true
+ << Parts{
+ SnippetPart("class "),
+ SnippetPart("name", 0),
+ SnippetPart("\n"
+ "{\n"
+ "public:\n"
+ " "),
+ SnippetPart("name", 0),
+ SnippetPart("() {}\n"
+ "};"),
+ };
+ QTest::newRow("escape sequences") << QString("class $name$\\n"
+ "{\\n"
+ "public\\\\:\\n"
+ "\\t$name$() {}\\n"
+ "};")
+ << true
+ << Parts{
+ SnippetPart("class "),
+ SnippetPart("name", 0),
+ SnippetPart("\n"
+ "{\n"
+ "public\\:\n"
+ "\t"),
+ SnippetPart("name", 0),
+ SnippetPart("() {}\n"
+ "};"),
+ };
}
void Internal::TextEditorPlugin::testSnippetParsing()
{
QFETCH(QString, input);
- QFETCH(QString, text);
QFETCH(bool, success);
- QFETCH(QList, ranges_start);
- QFETCH(QList, ranges_length);
- QFETCH(QList, ranges_mangler);
- Q_ASSERT(ranges_start.count() == ranges_length.count()); // sanity check for the test data
- Q_ASSERT(ranges_start.count() == ranges_mangler.count()); // sanity check for the test data
+ QFETCH(Parts, parts);
SnippetParseResult result = Snippet::parse(input);
QCOMPARE(Utils::holds_alternative(result), success);
+ if (!success)
+ return;
ParsedSnippet snippet = Utils::get(result);
- QCOMPARE(snippet.text, text);
- QCOMPARE(snippet.ranges.count(), ranges_start.count());
- for (int i = 0; i < ranges_start.count(); ++i) {
- QCOMPARE(snippet.ranges.at(i).start, ranges_start.at(i));
- QCOMPARE(snippet.ranges.at(i).length, ranges_length.at(i));
- Utils::Id id = NOMANGLER_ID;
- if (snippet.ranges.at(i).mangler)
- id = snippet.ranges.at(i).mangler->id();
- QCOMPARE(id, ranges_mangler.at(i));
- }
+ auto rangesCompare = [&](const ParsedSnippet::Part &actual, const SnippetPart &expected) {
+ QCOMPARE(actual.text, expected.text);
+ QCOMPARE(actual.variableIndex, expected.variableIndex);
+ auto manglerId = actual.mangler ? actual.mangler->id() : NOMANGLER_ID;
+ QCOMPARE(manglerId, expected.manglerId);
+ };
+
+ for (int i = 0; i < parts.count(); ++i)
+ rangesCompare(snippet.parts.at(i), parts.at(i));
}
#endif
-
diff --git a/src/plugins/texteditor/snippets/snippet.h b/src/plugins/texteditor/snippets/snippet.h
index 6fe3f8ad0ce..8334a66a492 100644
--- a/src/plugins/texteditor/snippets/snippet.h
+++ b/src/plugins/texteditor/snippets/snippet.h
@@ -49,14 +49,16 @@ public:
class TEXTEDITOR_EXPORT ParsedSnippet
{
public:
- QString text;
- struct Range {
- Range(int s, int l, NameMangler *m) : start(s), length(l), mangler(m) { }
- int start;
- int length;
- NameMangler *mangler;
+ class Part {
+ public:
+ Part() = default;
+ explicit Part(const QString &text) : text(text) {}
+ QString text;
+ int variableIndex = -1; // if variable index is >= 0 the text is interpreted as a variable
+ NameMangler *mangler = nullptr;
};
- QList ranges;
+ QList parts;
+ QList> variables;
};
class TEXTEDITOR_EXPORT SnippetParseError
diff --git a/src/plugins/texteditor/snippets/snippetoverlay.cpp b/src/plugins/texteditor/snippets/snippetoverlay.cpp
index e80cf8cb948..f35c8b6aa91 100644
--- a/src/plugins/texteditor/snippets/snippetoverlay.cpp
+++ b/src/plugins/texteditor/snippets/snippetoverlay.cpp
@@ -25,88 +25,140 @@
#include "snippetoverlay.h"
-#include "snippet.h"
+#include
+#include
namespace TextEditor {
namespace Internal {
-
void SnippetOverlay::clear()
{
TextEditorOverlay::clear();
- m_equivalentSelections.clear();
- m_manglers.clear();
+ m_selections.clear();
+ m_variables.clear();
}
-void SnippetOverlay::mapEquivalentSelections()
+void SnippetOverlay::addSnippetSelection(const QTextCursor &cursor,
+ const QColor &color,
+ NameMangler *mangler,
+ int variableIndex)
{
- m_equivalentSelections.clear();
- m_equivalentSelections.resize(selections().size());
-
- QMultiMap all;
- for (int i = 0; i < selections().size(); ++i)
- all.insert(selectionText(i).toLower(), i);
-
- const QList &uniqueKeys = all.uniqueKeys();
- foreach (const QString &key, uniqueKeys) {
- QList indexes;
- const auto cAll = all;
- QMultiMap::const_iterator lbit = cAll.lowerBound(key);
- QMultiMap::const_iterator ubit = cAll.upperBound(key);
- while (lbit != ubit) {
- indexes.append(lbit.value());
- ++lbit;
- }
-
- foreach (int index, indexes)
- m_equivalentSelections[index] = indexes;
- }
+ m_variables[variableIndex] << selections().size();
+ SnippetSelection selection;
+ selection.variableIndex = variableIndex;
+ selection.mangler = mangler;
+ m_selections << selection;
+ addOverlaySelection(cursor, color, color, TextEditorOverlay::ExpandBegin);
}
void SnippetOverlay::updateEquivalentSelections(const QTextCursor &cursor)
{
- int selectionIndex = selectionIndexForCursor(cursor);
- if (selectionIndex == -1)
+ const int ¤tIndex = indexForCursor(cursor);
+ if (currentIndex < 0)
return;
-
- const QString ¤tText = selectionText(selectionIndex);
- const QList &equivalents = m_equivalentSelections.at(selectionIndex);
- foreach (int i, equivalents) {
- if (i == selectionIndex)
+ const QString ¤tText = cursorForIndex(currentIndex).selectedText();
+ const QList &equivalents = m_variables.value(m_selections[currentIndex].variableIndex);
+ for (int i : equivalents) {
+ if (i == currentIndex)
continue;
- const QString &equivalentText = selectionText(i);
+ QTextCursor cursor = cursorForIndex(i);
+ const QString &equivalentText = cursor.selectedText();
if (currentText != equivalentText) {
- QTextCursor selectionCursor = assembleCursorForSelection(i);
- selectionCursor.joinPreviousEditBlock();
- selectionCursor.removeSelectedText();
- selectionCursor.insertText(currentText);
- selectionCursor.endEditBlock();
+ cursor.joinPreviousEditBlock();
+ cursor.insertText(currentText);
+ cursor.endEditBlock();
}
}
}
-void SnippetOverlay::setNameMangler(const QList &manglers)
-{
- m_manglers = manglers;
-}
-
void SnippetOverlay::mangle()
{
- for (int i = 0; i < m_manglers.count(); ++i) {
- if (!m_manglers.at(i))
- continue;
-
- const QString current = selectionText(i);
- const QString result = m_manglers.at(i)->mangle(current);
- if (result != current) {
- QTextCursor selectionCursor = assembleCursorForSelection(i);
- selectionCursor.joinPreviousEditBlock();
- selectionCursor.removeSelectedText();
- selectionCursor.insertText(result);
- selectionCursor.endEditBlock();
+ for (int i = 0; i < m_selections.size(); ++i) {
+ if (NameMangler *mangler = m_selections[i].mangler) {
+ QTextCursor cursor = cursorForIndex(i);
+ const QString current = cursor.selectedText();
+ const QString result = mangler->mangle(current);
+ if (result != current) {
+ cursor.joinPreviousEditBlock();
+ cursor.insertText(result);
+ cursor.endEditBlock();
+ }
}
}
}
+bool SnippetOverlay::hasCursorInSelection(const QTextCursor &cursor) const
+{
+ return indexForCursor(cursor) >= 0;
+}
+
+QTextCursor SnippetOverlay::nextSelectionCursor(const QTextCursor &cursor) const
+{
+ const QList selections = TextEditorOverlay::selections();
+ if (selections.isEmpty())
+ return {};
+ const SnippetSelection ¤tSelection = selectionForCursor(cursor);
+ if (currentSelection.variableIndex >= 0) {
+ int nextVariableIndex = currentSelection.variableIndex + 1;
+ if (nextVariableIndex >= m_variables.size())
+ nextVariableIndex = 0;
+
+ for (int selectionIndex : m_variables[nextVariableIndex]) {
+ if (selections[selectionIndex].m_cursor_begin.position() > cursor.position())
+ return cursorForIndex(selectionIndex);
+ }
+ return cursorForIndex(m_variables[nextVariableIndex].first());
+ }
+ // currently not over a variable simply select the next available one
+ for (const OverlaySelection &candidate : selections) {
+ if (candidate.m_cursor_begin.position() > cursor.position())
+ return cursorForSelection(candidate);
+ }
+ return cursorForSelection(selections.first());
+}
+
+QTextCursor SnippetOverlay::previousSelectionCursor(const QTextCursor &cursor) const
+{
+ const QList selections = TextEditorOverlay::selections();
+ if (selections.isEmpty())
+ return {};
+ const SnippetSelection ¤tSelection = selectionForCursor(cursor);
+ if (currentSelection.variableIndex >= 0) {
+ int previousVariableIndex = currentSelection.variableIndex - 1;
+ if (previousVariableIndex < 0)
+ previousVariableIndex = m_variables.size();
+
+ auto equivalents = m_variables[previousVariableIndex].toStdList();
+ equivalents.reverse();
+ for (int selectionIndex : equivalents) {
+ if (selections[selectionIndex].m_cursor_end.position() < cursor.position())
+ return cursorForIndex(selectionIndex);
+ }
+ return cursorForIndex(m_variables[previousVariableIndex].last());
+ }
+ // currently not over a variable simply select the previous available one
+ auto reverse = selections.toStdList();
+ reverse.reverse();
+ for (const OverlaySelection &candidate : reverse) {
+ if (candidate.m_cursor_end.position() < cursor.position())
+ return cursorForSelection(candidate);
+ }
+ return cursorForSelection(selections.last());
+}
+
+int SnippetOverlay::indexForCursor(const QTextCursor &cursor) const
+{
+ return Utils::indexOf(selections(),
+ [pos = cursor.position()](const OverlaySelection &selection) {
+ return selection.m_cursor_begin.position() <= pos
+ && selection.m_cursor_end.position() >= pos;
+ });
+}
+
+SnippetOverlay::SnippetSelection SnippetOverlay::selectionForCursor(const QTextCursor &cursor) const
+{
+ return m_selections.value(indexForCursor(cursor));
+}
+
} // namespace Internal
} // namespace TextEditor
diff --git a/src/plugins/texteditor/snippets/snippetoverlay.h b/src/plugins/texteditor/snippets/snippetoverlay.h
index 0edf0a8e0d9..a6ca9f64069 100644
--- a/src/plugins/texteditor/snippets/snippetoverlay.h
+++ b/src/plugins/texteditor/snippets/snippetoverlay.h
@@ -25,8 +25,11 @@
#pragma once
+#include "snippet.h"
#include "texteditor/texteditoroverlay.h"
+#include
+
namespace TextEditor {
class NameMangler;
@@ -39,14 +42,30 @@ public:
void clear() override;
- void mapEquivalentSelections();
+ void addSnippetSelection(const QTextCursor &cursor,
+ const QColor &color,
+ NameMangler *mangler,
+ int variableGoup);
void updateEquivalentSelections(const QTextCursor &cursor);
- void setNameMangler(const QList &manglers);
void mangle();
+ bool hasCursorInSelection(const QTextCursor &cursor) const;
+
+ QTextCursor nextSelectionCursor(const QTextCursor &cursor) const;
+ QTextCursor previousSelectionCursor(const QTextCursor &cursor) const;
+
private:
- QVector > m_equivalentSelections;
- QList m_manglers;
+ struct SnippetSelection
+ {
+ int variableIndex = -1;
+ NameMangler *mangler;
+ };
+
+ int indexForCursor(const QTextCursor &cursor) const;
+ SnippetSelection selectionForCursor(const QTextCursor &cursor) const;
+
+ QList m_selections;
+ QMap> m_variables;
};
} // namespace Internal
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 921b3610105..8eaac179e35 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -2699,12 +2699,33 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e)
d->m_codeAssistant.process();
}
+class PositionedPart : public ParsedSnippet::Part
+{
+public:
+ explicit PositionedPart(const ParsedSnippet::Part &part) : ParsedSnippet::Part(part) {}
+ int start;
+ int end;
+};
+
+class CursorPart : public ParsedSnippet::Part
+{
+public:
+ CursorPart(const PositionedPart &part, QTextDocument *doc)
+ : ParsedSnippet::Part(part)
+ , cursor(doc)
+ {
+ cursor.setPosition(part.start);
+ cursor.setPosition(part.end, QTextCursor::KeepAnchor);
+ }
+ QTextCursor cursor;
+};
+
void TextEditorWidget::insertCodeSnippet(const QTextCursor &cursor_arg, const QString &snippet)
{
SnippetParseResult result = Snippet::parse(snippet);
if (Utils::holds_alternative(result)) {
const auto &error = Utils::get(result);
- QMessageBox::warning(this, QLatin1String("Snippet Parse Error"), error.htmlMessage());
+ QMessageBox::warning(this, tr("Snippet Parse Error"), error.htmlMessage());
return;
}
QTC_ASSERT(Utils::holds_alternative(result), return);
@@ -2715,44 +2736,47 @@ void TextEditorWidget::insertCodeSnippet(const QTextCursor &cursor_arg, const QS
cursor.removeSelectedText();
const int startCursorPosition = cursor.position();
- cursor.insertText(data.text);
- QList selections;
+ d->m_snippetOverlay->mangle();
+ d->m_snippetOverlay->clear();
- QList manglers;
- for (int i = 0; i < data.ranges.count(); ++i) {
- int position = data.ranges.at(i).start + startCursorPosition;
- int length = data.ranges.at(i).length;
-
- QTextCursor tc(document());
- tc.setPosition(position);
- tc.setPosition(position + length, QTextCursor::KeepAnchor);
- QTextEdit::ExtraSelection selection;
- selection.cursor = tc;
- selection.format = (length
- ? textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES)
- : textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES_RENAME));
- selections.append(selection);
- manglers << data.ranges.at(i).mangler;
+ QList positionedParts;
+ for (const ParsedSnippet::Part &part : qAsConst(data.parts)) {
+ if (part.variableIndex >= 0) {
+ PositionedPart posPart(part);
+ posPart.start = cursor.position();
+ cursor.insertText(part.text);
+ posPart.end = cursor.position();
+ positionedParts << posPart;
+ } else {
+ cursor.insertText(part.text);
+ }
}
+ QList cursorParts = Utils::transform(positionedParts,
+ [doc = document()](const PositionedPart &part) {
+ return CursorPart(part, doc);
+ });
+
cursor.setPosition(startCursorPosition, QTextCursor::KeepAnchor);
d->m_document->autoIndent(cursor);
cursor.endEditBlock();
- setExtraSelections(TextEditorWidget::SnippetPlaceholderSelection, selections);
- d->m_snippetOverlay->setNameMangler(manglers);
+ const QColor &occurrencesColor
+ = textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES).background().color();
+ const QColor &renameColor
+ = textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES_RENAME).background().color();
- if (!selections.isEmpty()) {
- const QTextEdit::ExtraSelection &selection = selections.first();
+ for (const CursorPart &part : cursorParts) {
+ const QColor &color = part.cursor.hasSelection() ? occurrencesColor : renameColor;
+ d->m_snippetOverlay->addSnippetSelection(part.cursor,
+ color,
+ part.mangler,
+ part.variableIndex);
+ }
- cursor = textCursor();
- if (selection.cursor.hasSelection()) {
- cursor.setPosition(selection.cursor.selectionStart());
- cursor.setPosition(selection.cursor.selectionEnd(), QTextCursor::KeepAnchor);
- } else {
- cursor.setPosition(selection.cursor.position());
- }
- setTextCursor(cursor);
+ if (!cursorParts.isEmpty()) {
+ setTextCursor(cursorParts.first().cursor);
+ d->m_snippetOverlay->setVisible(true);
}
}
@@ -3461,36 +3485,8 @@ void TextEditorWidgetPrivate::snippetTabOrBacktab(bool forward)
if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
return;
QTextCursor cursor = q->textCursor();
- OverlaySelection final;
- if (forward) {
- 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;
- break;
- }
- }
- } else {
- 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;
- }
- }
-
- }
- if (final.m_cursor_begin.isNull())
- 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());
- } else {
- cursor.setPosition(final.m_cursor_begin.position());
- cursor.setPosition(final.m_cursor_end.position(), QTextCursor::KeepAnchor);
- }
- q->setTextCursor(cursor);
+ q->setTextCursor(forward ? m_snippetOverlay->nextSelectionCursor(cursor)
+ : m_snippetOverlay->previousSelectionCursor(cursor));
}
// Calculate global position for a tooltip considering the left extra area.
@@ -7082,17 +7078,6 @@ void TextEditorWidgetPrivate::setExtraSelections(Id kind, const QListsetVisible(!m_overlay->isEmpty());
- } else if (kind == TextEditorWidget::SnippetPlaceholderSelection) {
- m_snippetOverlay->mangle();
- m_snippetOverlay->clear();
- foreach (const QTextEdit::ExtraSelection &selection, m_extraSelections[kind]) {
- m_snippetOverlay->addOverlaySelection(selection.cursor,
- selection.format.background().color(),
- selection.format.background().color(),
- TextEditorOverlay::ExpandBegin);
- }
- m_snippetOverlay->mapEquivalentSelections();
- m_snippetOverlay->setVisible(!m_snippetOverlay->isEmpty());
} else {
QList all;
for (auto i = m_extraSelections.constBegin(); i != m_extraSelections.constEnd(); ++i) {
diff --git a/src/plugins/texteditor/texteditoroverlay.cpp b/src/plugins/texteditor/texteditoroverlay.cpp
index 41107e39d92..4fb7de00a25 100644
--- a/src/plugins/texteditor/texteditoroverlay.cpp
+++ b/src/plugins/texteditor/texteditoroverlay.cpp
@@ -33,6 +33,7 @@
#include
#include
+#include
using namespace TextEditor;
using namespace TextEditor::Internal;
@@ -416,6 +417,21 @@ void TextEditorOverlay::paint(QPainter *painter, const QRect &clip)
}
}
+QTextCursor TextEditorOverlay::cursorForSelection(const OverlaySelection &selection) const
+{
+ QTextCursor cursor = selection.m_cursor_begin;
+ cursor.setPosition(selection.m_cursor_begin.position());
+ cursor.setKeepPositionOnInsert(false);
+ if (!cursor.isNull())
+ cursor.setPosition(selection.m_cursor_end.position(), QTextCursor::KeepAnchor);
+ return cursor;
+}
+
+QTextCursor TextEditorOverlay::cursorForIndex(int selectionIndex) const
+{
+ return cursorForSelection(m_selections.value(selectionIndex));
+}
+
void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect &clip)
{
Q_UNUSED(clip)
@@ -443,41 +459,6 @@ void TextEditorOverlay::fill(QPainter *painter, const QColor &color, const QRect
}
}
-/*! \returns true if any selection contains \a cursor, where a cursor on the
- start or end of a selection is counted as contained.
-*/
-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 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;
-}
-
bool TextEditorOverlay::hasFirstSelectionBeginMoved() const
{
if (m_firstSelectionOriginalBegin == -1 || m_selections.isEmpty())
diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h
index e995f007c5d..d87a0fe7aab 100644
--- a/src/plugins/texteditor/texteditoroverlay.h
+++ b/src/plugins/texteditor/texteditoroverlay.h
@@ -92,14 +92,11 @@ public:
inline int dropShadowWidth() const { return m_dropShadowWidth; }
- bool hasCursorInSelection(const QTextCursor &cursor) const;
-
bool hasFirstSelectionBeginMoved() const;
protected:
- int selectionIndexForCursor(const QTextCursor &cursor) const;
- QString selectionText(int selectionIndex) const;
- QTextCursor assembleCursorForSelection(int selectionIndex) const;
+ QTextCursor cursorForSelection(const OverlaySelection &selection) const;
+ QTextCursor cursorForIndex(int selectionIndex) const;
private:
QPainterPath createSelectionPath(const QTextCursor &begin, const QTextCursor &end, const QRect& clip);