QmlDesigner: Adjust ComponentTextModifier offsets for whitespace change

Whitespace change can also occur inside the subcomponent code when
new nodes are added into subcomponent and the document is saved.
Changed the text change handling to adjust for whitespace changes both
before and inside the subcomponent code.

The new handling is simpler and doesn't require subcomponent to have
id to work. The downside is that non-whitespace changes before
subcomponent code are no longer adjusted for, but that was just a
hacky workaround for a small part of a much larger issue that needs
to be tackled properly anyway (i.e. supporting arbitrary
undo/redo/manual edits), so losing it is not a big deal.

Fixes: QDS-5362
Change-Id: I1ca1084baf78709727b1b4726b4c034558063401
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2021-11-08 16:33:57 +02:00
parent dcf09bbf2c
commit 9ba2d2758a

View File

@@ -111,7 +111,8 @@ void ComponentTextModifier::commitGroup()
{ {
m_originalModifier->commitGroup(); m_originalModifier->commitGroup();
int textLength = m_originalModifier->text().length(); m_originalText = m_originalModifier->text();
int textLength = m_originalText.length();
m_componentEndOffset += (textLength - m_startLength); m_componentEndOffset += (textLength - m_startLength);
m_startLength = textLength; m_startLength = textLength;
} }
@@ -157,40 +158,42 @@ void ComponentTextModifier::handleOriginalTextChanged()
{ {
// Update offsets when original text changes, if necessary // Update offsets when original text changes, if necessary
// Detect and adjust for removal/addition of unrelated text before the subcomponent code,
// as that can happen even without user editing the text (e.g. whitespace removal at save time)
const QString currentText = m_originalModifier->text(); const QString currentText = m_originalModifier->text();
if (m_originalText.left(m_componentStartOffset) != currentText.left(m_componentStartOffset)) { // Adjust for removal/addition of whitespace in the document
// Subcomponent item id is the only reliable indicator for adjustment // Check that non-whitespace portion of the text is the same and count the whitespace diff
const int idIndex = m_originalText.indexOf("id:", m_componentStartOffset); const int oldLen = m_originalText.size();
if (idIndex != -1 && idIndex < m_componentEndOffset) { const int newLen = currentText.size();
int newLineIndex = m_originalText.indexOf('\n', idIndex); int newSpace = 0;
if (newLineIndex != -1) { int oldSpace = 0;
const QString checkLine = m_originalText.mid(idIndex, newLineIndex - idIndex); int newIdx = 0;
int lineIndex = currentText.indexOf(checkLine); for (int oldIdx = 0; oldIdx < oldLen; ++oldIdx) {
if (lineIndex != -1) { const QChar oldChar = m_originalText[oldIdx];
// Paranoia check - This shouldn't happen except when modifying text manually, if (oldIdx == m_componentStartOffset)
// but it's possible something was inserted between id and start m_componentStartOffset += newSpace - oldSpace;
// of the component, which would throw off the calculation, so check that if (oldIdx == m_componentEndOffset) {
// the first line is still correct even with new offset. m_componentEndOffset += newSpace - oldSpace;
const int diff = idIndex - lineIndex; break;
newLineIndex = m_originalText.indexOf('\n', m_componentStartOffset); }
if (newLineIndex != -1) {
const QString firstLine = m_originalText.mid(m_componentStartOffset, while (newIdx < newLen && currentText[newIdx].isSpace()) {
newLineIndex - m_componentStartOffset); ++newSpace;
const int newStart = m_componentStartOffset - diff; ++newIdx;
if (firstLine == currentText.mid(newStart, firstLine.size())) { }
m_componentEndOffset -= diff;
m_componentStartOffset = newStart; if (oldChar.isSpace()) {
m_originalText = currentText; ++oldSpace;
} continue;
} }
}
} if (currentText[newIdx] != oldChar) {
// Non-whitespace difference, we can't determine a valid offset in this case
// TODO: Needs proper handling to deal with undo/redo/arbitrary edits somehow (QDS-5392)
break;
} else {
++newIdx;
} }
} }
m_originalText = currentText;
emit textChanged(); emit textChanged();
} }