/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "unifieddiffeditorwidget.h" #include "diffeditorguicontroller.h" #include "diffutils.h" #include "diffeditorconstants.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //static const int FILE_LEVEL = 1; //static const int CHUNK_LEVEL = 2; using namespace Core; using namespace TextEditor; namespace DiffEditor { UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent) : SelectableTextEditorWidget(parent) , m_guiController(0) , m_controller(0) , m_ignoreCurrentIndexChange(false) , m_contextMenuFileIndex(-1) , m_contextMenuChunkIndex(-1) , m_leftLineNumberDigits(1) , m_rightLineNumberDigits(1) { DisplaySettings settings = displaySettings(); settings.m_textWrapping = false; settings.m_displayLineNumbers = true; settings.m_highlightCurrentLine = false; settings.m_displayFoldingMarkers = true; settings.m_markTextChanges = false; settings.m_highlightBlocks = false; SelectableTextEditorWidget::setDisplaySettings(settings); setReadOnly(true); connect(TextEditorSettings::instance(), SIGNAL(displaySettingsChanged(TextEditor::DisplaySettings)), this, SLOT(setDisplaySettings(TextEditor::DisplaySettings))); setDisplaySettings(TextEditorSettings::displaySettings()); setCodeStyle(TextEditorSettings::codeStyle()); connect(TextEditorSettings::instance(), SIGNAL(fontSettingsChanged(TextEditor::FontSettings)), this, SLOT(setFontSettings(TextEditor::FontSettings))); setFontSettings(TextEditorSettings::fontSettings()); clear(tr("No controller")); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChangedInEditor())); } UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget() { } void UnifiedDiffEditorWidget::setDiffEditorGuiController( DiffEditorGuiController *controller) { if (m_guiController == controller) return; if (m_guiController) { disconnect(m_controller, SIGNAL(cleared(QString)), this, SLOT(clearAll(QString))); disconnect(m_controller, SIGNAL(diffFilesChanged(QList,QString)), this, SLOT(setDiff(QList,QString))); disconnect(m_controller, SIGNAL(saveStateRequested()), this, SLOT(saveStateRequested())); disconnect(m_controller, SIGNAL(restoreStateRequested()), this, SLOT(restoreStateRequested())); disconnect(m_guiController, SIGNAL(currentDiffFileIndexChanged(int)), this, SLOT(setCurrentDiffFileIndex(int))); clear(tr("No controller")); } m_guiController = controller; m_controller = 0; if (m_guiController) { m_controller = m_guiController->controller(); connect(m_controller, SIGNAL(cleared(QString)), this, SLOT(clearAll(QString))); connect(m_controller, SIGNAL(diffFilesChanged(QList,QString)), this, SLOT(setDiff(QList,QString))); connect(m_controller, SIGNAL(saveStateRequested()), this, SLOT(saveStateRequested())); connect(m_controller, SIGNAL(restoreStateRequested()), this, SLOT(restoreStateRequested())); connect(m_guiController, SIGNAL(currentDiffFileIndexChanged(int)), this, SLOT(setCurrentDiffFileIndex(int))); setDiff(m_controller->diffFiles(), m_controller->workingDirectory()); setCurrentDiffFileIndex(m_guiController->currentDiffFileIndex()); } } void UnifiedDiffEditorWidget::saveStateRequested() { if (!m_state.isNull()) return; m_state = saveState(); } void UnifiedDiffEditorWidget::restoreStateRequested() { if (m_state.isNull()) return; restoreState(m_state); m_state.clear(); } DiffEditorGuiController *UnifiedDiffEditorWidget::diffEditorGuiController() const { return m_guiController; } void UnifiedDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds) { DisplaySettings settings = displaySettings(); settings.m_visualizeWhitespace = ds.m_visualizeWhitespace; SelectableTextEditorWidget::setDisplaySettings(settings); } void UnifiedDiffEditorWidget::setFontSettings(const FontSettings &fontSettings) { textDocument()->setFontSettings(fontSettings); m_fileLineFormat = fontSettings.toTextCharFormat(C_DIFF_FILE_LINE); m_chunkLineFormat = fontSettings.toTextCharFormat(C_DIFF_CONTEXT_LINE); m_leftLineFormat = fontSettings.toTextCharFormat(C_DIFF_SOURCE_LINE); m_leftCharFormat = fontSettings.toTextCharFormat(C_DIFF_SOURCE_CHAR); m_rightLineFormat = fontSettings.toTextCharFormat(C_DIFF_DEST_LINE); m_rightCharFormat = fontSettings.toTextCharFormat(C_DIFF_DEST_CHAR); update(); } void UnifiedDiffEditorWidget::slotCursorPositionChangedInEditor() { if (!m_guiController) return; if (m_ignoreCurrentIndexChange) return; const bool oldIgnore = m_ignoreCurrentIndexChange; m_ignoreCurrentIndexChange = true; m_guiController->setCurrentDiffFileIndex(fileIndexForBlockNumber( textCursor().blockNumber())); m_ignoreCurrentIndexChange = oldIgnore; } void UnifiedDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton && !(e->modifiers() & Qt::ShiftModifier)) { QTextCursor cursor = cursorForPosition(e->pos()); jumpToOriginalFile(cursor); e->accept(); return; } SelectableTextEditorWidget::mouseDoubleClickEvent(e); } void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e) { QPointer menu = createStandardContextMenu(); QTextCursor cursor = cursorForPosition(e->pos()); const int blockNumber = cursor.blockNumber(); addContextMenuActions(menu, fileIndexForBlockNumber(blockNumber), chunkIndexForBlockNumber(blockNumber)); connect(this, SIGNAL(destroyed()), menu, SLOT(deleteLater())); menu->exec(e->globalPos()); delete menu; } void UnifiedDiffEditorWidget::addContextMenuActions(QMenu *menu, int diffFileIndex, int chunkIndex) { if (!m_controller) return; menu->addSeparator(); menu->addSeparator(); QAction *sendChunkToCodePasterAction = menu->addAction(tr("Send Chunk to CodePaster...")); connect(sendChunkToCodePasterAction, SIGNAL(triggered()), this, SLOT(slotSendChunkToCodePaster())); QAction *applyAction = menu->addAction(tr("Apply Chunk...")); connect(applyAction, SIGNAL(triggered()), this, SLOT(slotApplyChunk())); QAction *revertAction = menu->addAction(tr("Revert Chunk...")); connect(revertAction, SIGNAL(triggered()), this, SLOT(slotRevertChunk())); m_contextMenuFileIndex = diffFileIndex; m_contextMenuChunkIndex = chunkIndex; applyAction->setEnabled(false); revertAction->setEnabled(false); if (m_contextMenuFileIndex < 0 || m_contextMenuChunkIndex < 0) return; if (m_contextMenuFileIndex >= m_contextFileData.count()) return; const FileData fileData = m_contextFileData.at(m_contextMenuFileIndex); if (m_contextMenuChunkIndex >= fileData.chunks.count()) return; m_controller->requestChunkActions(menu, diffFileIndex, chunkIndex); revertAction->setEnabled(true); if (fileData.leftFileInfo.fileName == fileData.rightFileInfo.fileName) return; applyAction->setEnabled(true); } void UnifiedDiffEditorWidget::slotSendChunkToCodePaster() { if (!m_controller) return; if (m_contextMenuFileIndex < 0 || m_contextMenuChunkIndex < 0) return; if (m_contextMenuFileIndex >= m_contextFileData.count()) return; const FileData fileData = m_contextFileData.at(m_contextMenuFileIndex); if (m_contextMenuChunkIndex >= fileData.chunks.count()) return; const QString patch = m_controller->makePatch(m_contextMenuFileIndex, m_contextMenuChunkIndex, false); if (patch.isEmpty()) return; // Retrieve service by soft dependency. QObject *pasteService = ExtensionSystem::PluginManager::getObjectByClassName( QLatin1String("CodePaster::CodePasterService")); if (pasteService) { QMetaObject::invokeMethod(pasteService, "postText", Q_ARG(QString, patch), Q_ARG(QString, QLatin1String(DiffEditor::Constants::DIFF_EDITOR_MIMETYPE))); } else { QMessageBox::information(this, tr("Unable to Paste"), tr("Code pasting services are not available.")); } } void UnifiedDiffEditorWidget::slotApplyChunk() { patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, false); } void UnifiedDiffEditorWidget::slotRevertChunk() { patch(m_contextMenuFileIndex, m_contextMenuChunkIndex, true); } void UnifiedDiffEditorWidget::patch(int diffFileIndex, int chunkIndex, bool revert) { if (!m_controller) return; if (diffFileIndex < 0 || chunkIndex < 0) return; if (diffFileIndex >= m_contextFileData.count()) return; const FileData fileData = m_contextFileData.at(diffFileIndex); if (chunkIndex >= fileData.chunks.count()) return; const QString title = revert ? tr("Revert Chunk") : tr("Apply Chunk"); const QString question = revert ? tr("Would you like to revert the chunk?") : tr("Would you like to apply the chunk?"); if (QMessageBox::No == QMessageBox::question(this, title, question, QMessageBox::Yes | QMessageBox::No)) { return; } const int strip = m_controller->workingDirectory().isEmpty() ? -1 : 0; const QString fileName = revert ? fileData.rightFileInfo.fileName : fileData.leftFileInfo.fileName; const QString workingDirectory = m_controller->workingDirectory().isEmpty() ? QFileInfo(fileName).absolutePath() : m_controller->workingDirectory(); const QString patch = m_controller->makePatch(diffFileIndex, chunkIndex, revert); if (patch.isEmpty()) return; if (PatchTool::runPatch( Core::EditorManager::defaultTextCodec()->fromUnicode(patch), workingDirectory, strip, revert)) m_controller->requestReload(); } TextEditor::BaseTextEditor *UnifiedDiffEditorWidget::createEditor() { return new BaseTextEditor; } void UnifiedDiffEditorWidget::clear(const QString &message) { m_leftLineNumberDigits = 1; m_rightLineNumberDigits = 1; m_leftLineNumbers.clear(); m_rightLineNumbers.clear(); m_fileInfo.clear(); m_chunkInfo.clear(); setSelections(QMap >()); const bool oldIgnore = m_ignoreCurrentIndexChange; m_ignoreCurrentIndexChange = true; SelectableTextEditorWidget::clear(); setPlainText(message); m_ignoreCurrentIndexChange = oldIgnore; } void UnifiedDiffEditorWidget::clearAll(const QString &message) { setDiff(QList(), QString()); clear(message); } QString UnifiedDiffEditorWidget::lineNumber(int blockNumber) const { QString lineNumberString; const bool leftLineExists = m_leftLineNumbers.contains(blockNumber); const bool rightLineExists = m_rightLineNumbers.contains(blockNumber); if (leftLineExists || rightLineExists) { const QString leftLine = leftLineExists ? QString::number(m_leftLineNumbers.value(blockNumber)) : QString(); lineNumberString += QString(m_leftLineNumberDigits - leftLine.count(), QLatin1Char(' ')) + leftLine; lineNumberString += QLatin1Char('|'); const QString rightLine = rightLineExists ? QString::number(m_rightLineNumbers.value(blockNumber)) : QString(); lineNumberString += QString(m_rightLineNumberDigits - rightLine.count(), QLatin1Char(' ')) + rightLine; } return lineNumberString; } int UnifiedDiffEditorWidget::lineNumberDigits() const { return m_leftLineNumberDigits + m_rightLineNumberDigits + 1; } void UnifiedDiffEditorWidget::setLeftLineNumber(int blockNumber, int lineNumber) { const QString lineNumberString = QString::number(lineNumber); m_leftLineNumbers.insert(blockNumber, lineNumber); m_leftLineNumberDigits = qMax(m_leftLineNumberDigits, lineNumberString.count()); } void UnifiedDiffEditorWidget::setRightLineNumber(int blockNumber, int lineNumber) { const QString lineNumberString = QString::number(lineNumber); m_rightLineNumbers.insert(blockNumber, lineNumber); m_rightLineNumberDigits = qMax(m_rightLineNumberDigits, lineNumberString.count()); } void UnifiedDiffEditorWidget::setFileInfo(int blockNumber, const DiffFileInfo &leftFileInfo, const DiffFileInfo &rightFileInfo) { m_fileInfo[blockNumber] = qMakePair(leftFileInfo, rightFileInfo); } void UnifiedDiffEditorWidget::setChunkIndex(int startBlockNumber, int blockCount, int chunkIndex) { m_chunkInfo.insert(startBlockNumber, qMakePair(blockCount, chunkIndex)); } void UnifiedDiffEditorWidget::setDiff(const QList &diffFileList, const QString &workingDirectory) { Q_UNUSED(workingDirectory) m_contextFileData = diffFileList; showDiff(); } QString UnifiedDiffEditorWidget::showChunk(const ChunkData &chunkData, bool lastChunk, int *blockNumber, int *charNumber, QMap > *selections) { if (chunkData.contextChunk) return QString(); QString diffText; int leftLineCount = 0; int rightLineCount = 0; int blockCount = 0; int charCount = 0; QList leftBuffer, rightBuffer; (*selections)[*blockNumber].append(DiffSelection(&m_chunkLineFormat)); int lastEqualRow = -1; if (lastChunk) { for (int i = chunkData.rows.count(); i > 0; i--) { if (chunkData.rows.at(i - 1).equal) { if (i != chunkData.rows.count()) lastEqualRow = i - 1; break; } } } for (int i = 0; i <= chunkData.rows.count(); i++) { const RowData &rowData = i < chunkData.rows.count() ? chunkData.rows.at(i) : RowData(TextLineData(TextLineData::Separator)); // dummy, // ensure we process buffers to the end. // rowData will be equal if (rowData.equal && i != lastEqualRow) { if (leftBuffer.count()) { for (int j = 0; j < leftBuffer.count(); j++) { const TextLineData &lineData = leftBuffer.at(j); const QString line = DiffUtils::makePatchLine( QLatin1Char('-'), lineData.text, lastChunk, i == chunkData.rows.count() && j == leftBuffer.count() - 1); const int blockDelta = line.count(QLatin1Char('\n')); // no new line // could have been added for (int k = 0; k < blockDelta; k++) (*selections)[*blockNumber + blockCount + 1 + k].append(&m_leftLineFormat); QMapIterator itPos(lineData.changedPositions); while (itPos.hasNext()) { itPos.next(); const int startPos = itPos.key() < 0 ? 1 : itPos.key() + 1; const int endPos = itPos.value() < 0 ? itPos.value() : itPos.value() + 1; (*selections)[*blockNumber + blockCount + 1].append( DiffSelection(startPos, endPos, &m_leftCharFormat)); } if (!line.isEmpty()) { setLeftLineNumber(*blockNumber + blockCount + 1, chunkData.leftStartingLineNumber + leftLineCount + 1); blockCount += blockDelta; ++leftLineCount; } diffText += line; charCount += line.count(); } leftBuffer.clear(); } if (rightBuffer.count()) { for (int j = 0; j < rightBuffer.count(); j++) { const TextLineData &lineData = rightBuffer.at(j); const QString line = DiffUtils::makePatchLine( QLatin1Char('+'), lineData.text, lastChunk, i == chunkData.rows.count() && j == rightBuffer.count() - 1); const int blockDelta = line.count(QLatin1Char('\n')); // no new line // could have been added for (int k = 0; k < blockDelta; k++) (*selections)[*blockNumber + blockCount + 1 + k].append(&m_rightLineFormat); QMapIterator itPos(lineData.changedPositions); while (itPos.hasNext()) { itPos.next(); const int startPos = itPos.key() < 0 ? 1 : itPos.key() + 1; const int endPos = itPos.value() < 0 ? itPos.value() : itPos.value() + 1; (*selections)[*blockNumber + blockCount + 1].append (DiffSelection(startPos, endPos, &m_rightCharFormat)); } if (!line.isEmpty()) { setRightLineNumber(*blockNumber + blockCount + 1, chunkData.rightStartingLineNumber + rightLineCount + 1); blockCount += blockDelta; ++rightLineCount; } diffText += line; charCount += line.count(); } rightBuffer.clear(); } if (i < chunkData.rows.count()) { const QString line = DiffUtils::makePatchLine(QLatin1Char(' '), rowData.rightLine.text, lastChunk, i == chunkData.rows.count() - 1); if (!line.isEmpty()) { setLeftLineNumber(*blockNumber + blockCount + 1, chunkData.leftStartingLineNumber + leftLineCount + 1); setRightLineNumber(*blockNumber + blockCount + 1, chunkData.rightStartingLineNumber + rightLineCount + 1); blockCount += line.count(QLatin1Char('\n')); ++leftLineCount; ++rightLineCount; } diffText += line; charCount += line.count(); } } else { if (rowData.leftLine.textLineType == TextLineData::TextLine) leftBuffer.append(rowData.leftLine); if (rowData.rightLine.textLineType == TextLineData::TextLine) rightBuffer.append(rowData.rightLine); } } const QString chunkLine = QLatin1String("@@ -") + QString::number(chunkData.leftStartingLineNumber + 1) + QLatin1Char(',') + QString::number(leftLineCount) + QLatin1String(" +") + QString::number(chunkData.rightStartingLineNumber+ 1) + QLatin1Char(',') + QString::number(rightLineCount) + QLatin1String(" @@") + chunkData.contextInfo + QLatin1Char('\n'); diffText.prepend(chunkLine); *blockNumber += blockCount + 1; // +1 for chunk line *charNumber += charCount + chunkLine.count(); return diffText; } void UnifiedDiffEditorWidget::showDiff() { clear(tr("No difference")); QString diffText; int blockNumber = 0; int charNumber = 0; QMap > selections; for (int i = 0; i < m_contextFileData.count(); i++) { const FileData &fileData = m_contextFileData.at(i); const QString leftFileInfo = QLatin1String("--- ") + fileData.leftFileInfo.fileName + QLatin1Char('\n'); const QString rightFileInfo = QLatin1String("+++ ") + fileData.rightFileInfo.fileName + QLatin1Char('\n'); setFileInfo(blockNumber, fileData.leftFileInfo, fileData.rightFileInfo); selections[blockNumber].append(DiffSelection(&m_fileLineFormat)); blockNumber++; selections[blockNumber].append(DiffSelection(&m_fileLineFormat)); blockNumber++; diffText += leftFileInfo; diffText += rightFileInfo; charNumber += leftFileInfo.count() + rightFileInfo.count(); if (fileData.binaryFiles) { selections[blockNumber].append(DiffSelection(&m_chunkLineFormat)); blockNumber++; const QString binaryLine = QLatin1String("Binary files ") + fileData.leftFileInfo.fileName + QLatin1String(" and ") + fileData.rightFileInfo.fileName + QLatin1String(" differ\n"); diffText += binaryLine; charNumber += binaryLine.count(); } else { for (int j = 0; j < fileData.chunks.count(); j++) { const int oldBlockNumber = blockNumber; diffText += showChunk(fileData.chunks.at(j), (j == fileData.chunks.count() - 1) && fileData.lastChunkAtTheEndOfFile, &blockNumber, &charNumber, &selections); if (!fileData.chunks.at(j).contextChunk) setChunkIndex(oldBlockNumber, blockNumber - oldBlockNumber, j); } } } if (diffText.isEmpty()) return; diffText.replace(QLatin1Char('\r'), QLatin1Char(' ')); const bool oldIgnore = m_ignoreCurrentIndexChange; m_ignoreCurrentIndexChange = true; setPlainText(diffText); m_ignoreCurrentIndexChange = oldIgnore; setSelections(selections); } int UnifiedDiffEditorWidget::blockNumberForFileIndex(int fileIndex) const { if (fileIndex < 0 || fileIndex >= m_fileInfo.count()) return -1; QMap >::const_iterator it = m_fileInfo.constBegin(); for (int i = 0; i < fileIndex; i++) ++it; return it.key(); } int UnifiedDiffEditorWidget::fileIndexForBlockNumber(int blockNumber) const { QMap >::const_iterator it = m_fileInfo.constBegin(); QMap >::const_iterator itEnd = m_fileInfo.constEnd(); int i = -1; while (it != itEnd) { if (it.key() > blockNumber) break; ++it; ++i; } return i; } int UnifiedDiffEditorWidget::chunkIndexForBlockNumber(int blockNumber) const { if (m_chunkInfo.isEmpty()) return -1; QMap >::const_iterator it = m_chunkInfo.upperBound(blockNumber); if (it == m_chunkInfo.constBegin()) return -1; --it; if (blockNumber < it.key() + it.value().first) return it.value().second; return -1; } void UnifiedDiffEditorWidget::jumpToOriginalFile(const QTextCursor &cursor) { if (m_fileInfo.isEmpty()) return; const int blockNumber = cursor.blockNumber(); const int fileIndex = fileIndexForBlockNumber(blockNumber); if (fileIndex < 0) return; const FileData fileData = m_contextFileData.at(fileIndex); const QString leftFileName = fileData.leftFileInfo.fileName; const QString rightFileName = fileData.rightFileInfo.fileName; const int columnNumber = cursor.positionInBlock() - 1; // -1 for the first character in line const int rightLineNumber = m_rightLineNumbers.value(blockNumber, -1); if (rightLineNumber >= 0) { jumpToOriginalFile(rightFileName, rightLineNumber, columnNumber); return; } const int leftLineNumber = m_leftLineNumbers.value(blockNumber, -1); if (leftLineNumber >= 0) { if (leftFileName == rightFileName) { for (int i = 0; i < fileData.chunks.count(); i++) { const ChunkData chunkData = fileData.chunks.at(i); int newLeftLineNumber = chunkData.leftStartingLineNumber; int newRightLineNumber = chunkData.rightStartingLineNumber; for (int j = 0; j < chunkData.rows.count(); j++) { const RowData rowData = chunkData.rows.at(j); if (rowData.leftLine.textLineType == TextLineData::TextLine) newLeftLineNumber++; if (rowData.rightLine.textLineType == TextLineData::TextLine) newRightLineNumber++; if (newLeftLineNumber == leftLineNumber) { jumpToOriginalFile(leftFileName, newRightLineNumber, 0); return; } } } } else { jumpToOriginalFile(leftFileName, leftLineNumber, columnNumber); } return; } } void UnifiedDiffEditorWidget::jumpToOriginalFile(const QString &fileName, int lineNumber, int columnNumber) { if (!m_controller) return; const QDir dir(m_controller->workingDirectory()); const QString absoluteFileName = dir.absoluteFilePath(fileName); QFileInfo fi(absoluteFileName); if (fi.exists() && !fi.isDir()) Core::EditorManager::openEditorAt(absoluteFileName, lineNumber, columnNumber); } void UnifiedDiffEditorWidget::setCurrentDiffFileIndex(int diffFileIndex) { if (m_ignoreCurrentIndexChange) return; const bool oldIgnore = m_ignoreCurrentIndexChange; m_ignoreCurrentIndexChange = true; const int blockNumber = blockNumberForFileIndex(diffFileIndex); QTextBlock block = document()->findBlockByNumber(blockNumber); QTextCursor cursor = textCursor(); cursor.setPosition(block.position()); setTextCursor(cursor); centerCursor(); m_ignoreCurrentIndexChange = oldIgnore; } } // namespace DiffEditor