2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
#include "diffeditorwidgetcontroller.h"
|
|
|
|
|
#include "diffeditorconstants.h"
|
2018-02-14 13:38:56 +01:00
|
|
|
#include "diffeditorcontroller.h"
|
2016-07-13 15:55:18 +02:00
|
|
|
#include "diffeditordocument.h"
|
|
|
|
|
|
2016-07-15 00:59:19 +03:00
|
|
|
#include <coreplugin/documentmanager.h>
|
2016-07-13 15:55:18 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2016-07-15 00:59:19 +03:00
|
|
|
#include <coreplugin/patchtool.h>
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
#include <texteditor/fontsettings.h>
|
2016-10-19 16:44:25 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
|
|
|
|
|
|
#include <cpaster/codepasterservice.h>
|
|
|
|
|
|
2020-06-17 12:23:44 +02:00
|
|
|
#include <utils/infobar.h>
|
2016-11-22 13:32:29 +01:00
|
|
|
#include <utils/progressindicator.h>
|
2016-07-13 15:55:18 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2017-01-19 16:44:22 +01:00
|
|
|
#include <utils/temporaryfile.h>
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
#include <QDir>
|
|
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QTextCodec>
|
|
|
|
|
|
|
|
|
|
using namespace Core;
|
|
|
|
|
using namespace TextEditor;
|
2020-06-26 13:59:38 +02:00
|
|
|
using namespace Utils;
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
namespace DiffEditor {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
DiffEditorWidgetController::DiffEditorWidgetController(QWidget *diffEditorWidget)
|
|
|
|
|
: QObject(diffEditorWidget)
|
|
|
|
|
, m_diffEditorWidget(diffEditorWidget)
|
|
|
|
|
{
|
2016-11-22 13:32:29 +01:00
|
|
|
m_timer.setSingleShot(true);
|
|
|
|
|
m_timer.setInterval(100);
|
|
|
|
|
connect(&m_timer, &QTimer::timeout, this, &DiffEditorWidgetController::showProgress);
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-22 13:48:54 +02:00
|
|
|
bool DiffEditorWidgetController::isInProgress() const
|
|
|
|
|
{
|
|
|
|
|
return m_isBusyShowing || (m_document && m_document->state() == DiffEditorDocument::Reloading);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidgetController::toggleProgress(bool wasInProgress)
|
|
|
|
|
{
|
|
|
|
|
const bool inProgress = isInProgress();
|
|
|
|
|
if (wasInProgress == inProgress)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (inProgress)
|
|
|
|
|
scheduleShowProgress();
|
|
|
|
|
else
|
|
|
|
|
hideProgress();
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
void DiffEditorWidgetController::setDocument(DiffEditorDocument *document)
|
|
|
|
|
{
|
2016-11-22 13:32:29 +01:00
|
|
|
if (!m_progressIndicator) {
|
2022-06-03 15:19:02 +02:00
|
|
|
m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Large);
|
2016-11-22 13:32:29 +01:00
|
|
|
m_progressIndicator->attachToWidget(m_diffEditorWidget);
|
|
|
|
|
m_progressIndicator->hide();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_document == document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (m_document) {
|
|
|
|
|
disconnect(m_document, &IDocument::aboutToReload, this, &DiffEditorWidgetController::scheduleShowProgress);
|
2020-04-14 11:10:52 +02:00
|
|
|
disconnect(m_document, &IDocument::reloadFinished, this, &DiffEditorWidgetController::onDocumentReloadFinished);
|
2016-11-22 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-22 13:48:54 +02:00
|
|
|
const bool wasInProgress = isInProgress();
|
2016-11-22 13:32:29 +01:00
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
m_document = document;
|
2016-11-22 13:32:29 +01:00
|
|
|
if (m_document) {
|
|
|
|
|
connect(m_document, &IDocument::aboutToReload, this, &DiffEditorWidgetController::scheduleShowProgress);
|
2020-04-14 11:10:52 +02:00
|
|
|
connect(m_document, &IDocument::reloadFinished, this, &DiffEditorWidgetController::onDocumentReloadFinished);
|
|
|
|
|
updateCannotDecodeInfo();
|
2016-11-22 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-22 13:48:54 +02:00
|
|
|
toggleProgress(wasInProgress);
|
2016-11-22 13:32:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-11-23 11:20:12 +01:00
|
|
|
DiffEditorDocument *DiffEditorWidgetController::document() const
|
|
|
|
|
{
|
|
|
|
|
return m_document;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-22 13:48:54 +02:00
|
|
|
void DiffEditorWidgetController::setBusyShowing(bool busy)
|
|
|
|
|
{
|
|
|
|
|
if (m_isBusyShowing == busy)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const bool wasInProgress = isInProgress();
|
|
|
|
|
m_isBusyShowing = busy;
|
|
|
|
|
toggleProgress(wasInProgress);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-22 13:32:29 +01:00
|
|
|
void DiffEditorWidgetController::scheduleShowProgress()
|
|
|
|
|
{
|
|
|
|
|
m_timer.start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidgetController::showProgress()
|
|
|
|
|
{
|
|
|
|
|
m_timer.stop();
|
|
|
|
|
if (m_progressIndicator)
|
|
|
|
|
m_progressIndicator->show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidgetController::hideProgress()
|
|
|
|
|
{
|
|
|
|
|
m_timer.stop();
|
|
|
|
|
if (m_progressIndicator)
|
|
|
|
|
m_progressIndicator->hide();
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-14 11:10:52 +02:00
|
|
|
void DiffEditorWidgetController::onDocumentReloadFinished()
|
|
|
|
|
{
|
|
|
|
|
updateCannotDecodeInfo();
|
2022-09-22 13:48:54 +02:00
|
|
|
if (!isInProgress())
|
|
|
|
|
hideProgress();
|
2020-04-14 11:10:52 +02:00
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
void DiffEditorWidgetController::patch(bool revert, int fileIndex, int chunkIndex)
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
if (!chunkExists(fileIndex, chunkIndex))
|
|
|
|
|
return;
|
|
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
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(m_diffEditorWidget, title,
|
|
|
|
|
question,
|
|
|
|
|
QMessageBox::Yes
|
|
|
|
|
| QMessageBox::No)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
const FileData fileData = m_contextFileData.at(fileIndex);
|
2016-07-13 15:55:18 +02:00
|
|
|
const QString fileName = revert
|
|
|
|
|
? fileData.rightFileInfo.fileName
|
|
|
|
|
: fileData.leftFileInfo.fileName;
|
2016-10-19 16:44:25 +02:00
|
|
|
const DiffFileInfo::PatchBehaviour patchBehaviour = revert
|
|
|
|
|
? fileData.rightFileInfo.patchBehaviour
|
|
|
|
|
: fileData.leftFileInfo.patchBehaviour;
|
2016-07-13 15:55:18 +02:00
|
|
|
|
2021-08-17 12:26:10 +02:00
|
|
|
const FilePath workingDirectory = m_document->baseDirectory().isEmpty()
|
|
|
|
|
? FilePath::fromString(fileName).absolutePath()
|
2016-07-13 15:55:18 +02:00
|
|
|
: m_document->baseDirectory();
|
2021-08-17 12:26:10 +02:00
|
|
|
const FilePath absFilePath = workingDirectory.resolvePath(fileName).absoluteFilePath();
|
2016-07-13 15:55:18 +02:00
|
|
|
|
2016-10-19 16:44:25 +02:00
|
|
|
if (patchBehaviour == DiffFileInfo::PatchFile) {
|
|
|
|
|
const int strip = m_document->baseDirectory().isEmpty() ? -1 : 0;
|
2016-07-13 15:55:18 +02:00
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
const QString patch = m_document->makePatch(fileIndex, chunkIndex, ChunkSelection(), revert);
|
2016-10-19 16:44:25 +02:00
|
|
|
|
|
|
|
|
if (patch.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2021-06-09 09:59:37 +02:00
|
|
|
FileChangeBlocker fileChangeBlocker(absFilePath);
|
2016-10-19 16:44:25 +02:00
|
|
|
if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch),
|
2021-08-17 12:26:10 +02:00
|
|
|
workingDirectory, strip, revert))
|
2016-10-19 16:44:25 +02:00
|
|
|
m_document->reload();
|
|
|
|
|
} else { // PatchEditor
|
2018-11-07 23:22:59 +01:00
|
|
|
auto textDocument = qobject_cast<TextEditor::TextDocument *>(
|
2021-06-09 09:59:37 +02:00
|
|
|
DocumentModel::documentForFilePath(absFilePath));
|
2016-10-19 16:44:25 +02:00
|
|
|
if (!textDocument)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-06-03 15:19:02 +02:00
|
|
|
TemporaryFile contentsCopy("diff");
|
2016-10-19 16:44:25 +02:00
|
|
|
if (!contentsCopy.open())
|
|
|
|
|
return;
|
2016-07-13 15:55:18 +02:00
|
|
|
|
2016-10-19 16:44:25 +02:00
|
|
|
contentsCopy.write(textDocument->contents());
|
|
|
|
|
contentsCopy.close();
|
|
|
|
|
|
|
|
|
|
const QString contentsCopyFileName = contentsCopy.fileName();
|
|
|
|
|
const QString contentsCopyDir = QFileInfo(contentsCopyFileName).absolutePath();
|
|
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
const QString patch = m_document->makePatch(fileIndex, chunkIndex,
|
|
|
|
|
ChunkSelection(), revert, false,
|
|
|
|
|
QFileInfo(contentsCopyFileName).fileName());
|
2016-10-19 16:44:25 +02:00
|
|
|
|
|
|
|
|
if (patch.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (PatchTool::runPatch(EditorManager::defaultTextCodec()->fromUnicode(patch),
|
2021-08-17 13:37:20 +02:00
|
|
|
FilePath::fromString(contentsCopyDir), 0, revert)) {
|
2016-10-19 16:44:25 +02:00
|
|
|
QString errorString;
|
2021-05-18 13:55:23 +02:00
|
|
|
if (textDocument->reload(&errorString, FilePath::fromString(contentsCopyFileName)))
|
2016-10-19 16:44:25 +02:00
|
|
|
m_document->reload();
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidgetController::jumpToOriginalFile(const QString &fileName,
|
|
|
|
|
int lineNumber,
|
|
|
|
|
int columnNumber)
|
|
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-08-17 12:26:10 +02:00
|
|
|
const FilePath filePath = m_document->baseDirectory().resolvePath(fileName);
|
|
|
|
|
if (filePath.exists() && !filePath.isDir())
|
2021-11-01 17:02:02 +01:00
|
|
|
EditorManager::openEditorAt({filePath, lineNumber, columnNumber});
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DiffEditorWidgetController::setFontSettings(const FontSettings &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);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
void DiffEditorWidgetController::addCodePasterAction(QMenu *menu, int fileIndex, int chunkIndex)
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
if (ExtensionSystem::PluginManager::getObject<CodePaster::Service>()) {
|
|
|
|
|
// optional code pasting service
|
|
|
|
|
QAction *sendChunkToCodePasterAction = menu->addAction(tr("Send Chunk to CodePaster..."));
|
2022-09-22 11:00:38 +02:00
|
|
|
connect(sendChunkToCodePasterAction, &QAction::triggered, this, [this, fileIndex, chunkIndex] {
|
2018-02-15 10:29:14 +01:00
|
|
|
sendChunkToCodePaster(fileIndex, chunkIndex);
|
|
|
|
|
});
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
bool DiffEditorWidgetController::chunkExists(int fileIndex, int chunkIndex) const
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return false;
|
|
|
|
|
|
2018-02-14 13:38:56 +01:00
|
|
|
if (DiffEditorController *controller = m_document->controller())
|
2018-02-15 10:29:14 +01:00
|
|
|
return controller->chunkExists(fileIndex, chunkIndex);
|
2016-07-13 15:55:18 +02:00
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
return false;
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
ChunkData DiffEditorWidgetController::chunkData(int fileIndex, int chunkIndex) const
|
|
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return ChunkData();
|
|
|
|
|
|
|
|
|
|
if (fileIndex < 0 || chunkIndex < 0)
|
|
|
|
|
return ChunkData();
|
|
|
|
|
|
|
|
|
|
if (fileIndex >= m_contextFileData.count())
|
|
|
|
|
return ChunkData();
|
|
|
|
|
|
|
|
|
|
const FileData fileData = m_contextFileData.at(fileIndex);
|
|
|
|
|
if (chunkIndex >= fileData.chunks.count())
|
|
|
|
|
return ChunkData();
|
|
|
|
|
|
|
|
|
|
return fileData.chunks.at(chunkIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
bool DiffEditorWidgetController::fileNamesAreDifferent(int fileIndex) const
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
2018-02-15 10:29:14 +01:00
|
|
|
const FileData fileData = m_contextFileData.at(fileIndex);
|
2016-07-13 15:55:18 +02:00
|
|
|
return fileData.leftFileInfo.fileName != fileData.rightFileInfo.fileName;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
void DiffEditorWidgetController::addApplyAction(QMenu *menu, int fileIndex, int chunkIndex)
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
QAction *applyAction = menu->addAction(tr("Apply Chunk..."));
|
2022-09-22 11:00:38 +02:00
|
|
|
connect(applyAction, &QAction::triggered, this, [this, fileIndex, chunkIndex] {
|
2018-02-15 10:29:14 +01:00
|
|
|
patch(false, fileIndex, chunkIndex);
|
|
|
|
|
});
|
|
|
|
|
applyAction->setEnabled(chunkExists(fileIndex, chunkIndex) && fileNamesAreDifferent(fileIndex));
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
void DiffEditorWidgetController::addRevertAction(QMenu *menu, int fileIndex, int chunkIndex)
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
|
2022-09-22 11:00:38 +02:00
|
|
|
connect(revertAction, &QAction::triggered, this, [this, fileIndex, chunkIndex] {
|
2018-02-15 10:29:14 +01:00
|
|
|
patch(true, fileIndex, chunkIndex);
|
|
|
|
|
});
|
|
|
|
|
revertAction->setEnabled(chunkExists(fileIndex, chunkIndex));
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
void DiffEditorWidgetController::addExtraActions(QMenu *menu, int fileIndex, int chunkIndex,
|
|
|
|
|
const ChunkSelection &selection)
|
2018-02-15 10:29:14 +01:00
|
|
|
{
|
|
|
|
|
if (DiffEditorController *controller = m_document->controller())
|
2017-11-29 21:36:30 +01:00
|
|
|
controller->requestChunkActions(menu, fileIndex, chunkIndex, selection);
|
2018-02-15 10:29:14 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-14 11:10:52 +02:00
|
|
|
void DiffEditorWidgetController::updateCannotDecodeInfo()
|
|
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-06-03 15:19:02 +02:00
|
|
|
InfoBar *infoBar = m_document->infoBar();
|
2020-04-14 11:10:52 +02:00
|
|
|
Id selectEncodingId(Constants::SELECT_ENCODING);
|
|
|
|
|
if (m_document->hasDecodingError()) {
|
|
|
|
|
if (!infoBar->canInfoBeAdded(selectEncodingId))
|
|
|
|
|
return;
|
2022-06-03 15:19:02 +02:00
|
|
|
InfoBarEntry info(selectEncodingId,
|
2020-06-17 12:23:44 +02:00
|
|
|
tr("<b>Error:</b> Could not decode \"%1\" with \"%2\"-encoding.")
|
|
|
|
|
.arg(m_document->displayName(),
|
|
|
|
|
QString::fromLatin1(m_document->codec()->name())));
|
2022-09-22 11:00:38 +02:00
|
|
|
info.addCustomButton(tr("Select Encoding"), [this] { m_document->selectEncoding(); });
|
2020-04-14 11:10:52 +02:00
|
|
|
infoBar->addInfo(info);
|
|
|
|
|
} else {
|
|
|
|
|
infoBar->removeInfo(selectEncodingId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-15 10:29:14 +01:00
|
|
|
void DiffEditorWidgetController::sendChunkToCodePaster(int fileIndex, int chunkIndex)
|
2016-07-13 15:55:18 +02:00
|
|
|
{
|
|
|
|
|
if (!m_document)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Retrieve service by soft dependency.
|
|
|
|
|
auto pasteService = ExtensionSystem::PluginManager::getObject<CodePaster::Service>();
|
|
|
|
|
QTC_ASSERT(pasteService, return);
|
|
|
|
|
|
2017-11-29 21:36:30 +01:00
|
|
|
const QString patch = m_document->makePatch(fileIndex, chunkIndex,
|
|
|
|
|
ChunkSelection(), false);
|
2016-07-13 15:55:18 +02:00
|
|
|
|
|
|
|
|
if (patch.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2017-12-06 21:30:57 +01:00
|
|
|
pasteService->postText(patch, Constants::DIFF_EDITOR_MIMETYPE);
|
2016-07-13 15:55:18 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-22 11:00:38 +02:00
|
|
|
DiffEditorInput::DiffEditorInput(DiffEditorWidgetController *controller)
|
|
|
|
|
: m_contextFileData(controller->m_contextFileData)
|
|
|
|
|
, m_fileLineFormat(&controller->m_fileLineFormat)
|
|
|
|
|
, m_chunkLineFormat(&controller->m_chunkLineFormat)
|
|
|
|
|
, m_leftLineFormat(&controller->m_leftLineFormat)
|
|
|
|
|
, m_rightLineFormat(&controller->m_rightLineFormat)
|
|
|
|
|
, m_leftCharFormat(&controller->m_leftCharFormat)
|
|
|
|
|
, m_rightCharFormat(&controller->m_rightCharFormat)
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
2016-07-13 15:55:18 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace DiffEditor
|