9#include "diffmodel_p.h"
12#include "difference.h"
13#include "levenshteintable.h"
14#include "parserbase.h"
15#include "stringlistpair.h"
16#include <komparediff2_logging.h>
23DiffModel::DiffModel(
const QString &source,
const QString &destination)
24 : d_ptr(new DiffModelPrivate(source, destination))
28 d->splitSourceInPathAndFileName();
29 d->splitDestinationInPathAndFileName();
33 : d_ptr(new DiffModelPrivate())
37DiffModel::~DiffModel() =
default;
51bool DiffModel::operator<(
const DiffModel &model)
const
53 if (localeAwareCompareSource(model) < 0)
58int DiffModel::hunkCount()
const
62 return d->hunks.count();
65int DiffModel::differenceCount()
const
69 return d->differences.count();
72int DiffModel::appliedCount()
const
76 return d->appliedCount;
83 return (d->hunks.at(i));
86const Difference *DiffModel::differenceAt(
int i)
const
90 return (d->differences.at(i));
97 return (d->differences.at(i));
118 return &d->differences;
125 return &d->differences;
128int DiffModel::findDifference(
Difference *diff)
const
132 return d->differences.indexOf(diff);
135int DiffModel::localeAwareCompareSource(
const DiffModel &model)
const
139 qCDebug(KOMPAREDIFF2_LOG) <<
"Path: " << model.d_ptr->sourcePath;
140 qCDebug(KOMPAREDIFF2_LOG) <<
"File: " << model.d_ptr->sourceFile;
142 int result = d->sourcePath.localeAwareCompare(model.d_ptr->sourcePath);
145 return d->sourceFile.localeAwareCompare(model.d_ptr->sourceFile);
150QString DiffModel::recreateDiff()
const
160 diff += QStringLiteral(
"--- %1\t%2").
arg(ParserBase::escapePath(d->source), d->sourceTimestamp);
161 if (!d->sourceRevision.isEmpty())
162 diff += tab + d->sourceRevision;
164 diff += QStringLiteral(
"+++ %1\t%2").
arg(ParserBase::escapePath(d->destination), d->destinationTimestamp);
165 if (!d->destinationRevision.isEmpty())
166 diff += tab + d->destinationRevision;
170 for (
const DiffHunk *hunk : d->hunks) {
171 if (hunk->type() != DiffHunk::AddedByBlend) {
172 diff += hunk->recreateHunk();
183 qCDebug(KOMPAREDIFF2_LOG) <<
"DiffModel::firstDifference()";
185 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
187 d->selectedDifference = d->differences[d->diffIndex];
189 return d->selectedDifference;
196 qCDebug(KOMPAREDIFF2_LOG) <<
"DiffModel::lastDifference()";
197 d->diffIndex = d->differences.count() - 1;
198 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
200 d->selectedDifference = d->differences[d->diffIndex];
202 return d->selectedDifference;
209 qCDebug(KOMPAREDIFF2_LOG) <<
"DiffModel::prevDifference()";
210 if (d->diffIndex > 0 && --d->diffIndex < d->differences.count()) {
211 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
212 d->selectedDifference = d->differences[d->diffIndex];
214 d->selectedDifference =
nullptr;
216 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
219 return d->selectedDifference;
226 qCDebug(KOMPAREDIFF2_LOG) <<
"DiffModel::nextDifference()";
227 if (++d->diffIndex < d->differences.count()) {
228 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
229 d->selectedDifference = d->differences[d->diffIndex];
231 d->selectedDifference =
nullptr;
233 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
236 return d->selectedDifference;
239QString DiffModel::source()
const
246QString DiffModel::destination()
const
250 return d->destination;
253QString DiffModel::sourceFile()
const
257 return d->sourceFile;
260QString DiffModel::destinationFile()
const
264 return d->destinationFile;
267QString DiffModel::sourcePath()
const
271 return d->sourcePath;
274QString DiffModel::destinationPath()
const
278 return d->destinationPath;
281QString DiffModel::sourceTimestamp()
const
285 return d->sourceTimestamp;
288QString DiffModel::destinationTimestamp()
const
292 return d->destinationTimestamp;
295QString DiffModel::sourceRevision()
const
299 return d->sourceRevision;
302QString DiffModel::destinationRevision()
const
306 return d->destinationRevision;
309void DiffModel::setSourceFile(
const QString &path)
314 d->splitSourceInPathAndFileName();
317void DiffModel::setDestinationFile(
const QString &path)
321 d->destination =
path;
322 d->splitDestinationInPathAndFileName();
325void DiffModel::setSourceTimestamp(
const QString ×tamp)
329 d->sourceTimestamp = timestamp;
332void DiffModel::setDestinationTimestamp(
const QString ×tamp)
336 d->destinationTimestamp = timestamp;
339void DiffModel::setSourceRevision(
const QString &revision)
343 d->sourceRevision = revision;
346void DiffModel::setDestinationRevision(
const QString &revision)
350 d->destinationRevision = revision;
353bool DiffModel::isBlended()
const
360void DiffModel::setBlended(
bool blended)
364 d->blended = blended;
367void DiffModel::addHunk(
DiffHunk *hunk)
371 d->hunks.append(hunk);
378 d->differences.append(diff);
379 connect(diff, &Difference::differenceApplied,
this, &DiffModel::slotDifferenceApplied);
382int DiffModel::diffIndex()
const
389void DiffModel::setDiffIndex(
int diffIndex)
393 d->diffIndex = diffIndex;
396bool DiffModel::hasUnsavedChanges()
const
400 return std::any_of(d->differences.constBegin(), d->differences.constEnd(), [] (
const Difference* diff) {
401 return diff->isUnsaved();
405void DiffModel::applyDifference(
bool apply)
409 bool appliedState = d->selectedDifference->applied();
410 if (appliedState == apply) {
413 if (apply && !d->selectedDifference->applied())
415 else if (!apply && d->selectedDifference->applied())
418 d->selectedDifference->apply(apply);
421static int GetDifferenceDelta(
Difference *diff)
423 int delta = diff->destinationLineCount() - diff->sourceLineCount();
424 if (!diff->applied()) {
430void DiffModel::slotDifferenceApplied(
Difference *diff)
434 int delta = GetDifferenceDelta(diff);
435 for (
Difference *current : std::as_const(d->differences)) {
436 if (current->destinationLineNumber() > diff->destinationLineNumber()) {
437 current->setTrackingDestinationLineNumber(current->trackingDestinationLineNumber() + delta);
442void DiffModel::applyAllDifferences(
bool apply)
447 d->appliedCount = d->differences.count();
454 for (
Difference *diff : std::as_const(d->differences)) {
456 const bool appliedState = diff->applied();
457 if (appliedState == apply) {
461 const int currentDelta = GetDifferenceDelta(diff);
462 totalDelta += currentDelta;
466bool DiffModel::setSelectedDifference(
Difference *diff)
470 qCDebug(KOMPAREDIFF2_LOG) <<
"diff = " << diff;
471 qCDebug(KOMPAREDIFF2_LOG) <<
"d->selectedDifference = " << d->selectedDifference;
473 if (diff != d->selectedDifference) {
474 if ((d->differences.indexOf(diff)) == -1)
477 d->diffIndex = d->differences.indexOf(diff);
478 qCDebug(KOMPAREDIFF2_LOG) <<
"d->diffIndex = " << d->diffIndex;
479 d->selectedDifference = diff;
492 if (oldLines.
size() == 0 && newLines.
size() == 0) {
495 int editLineEnd = editLineNumber + oldLines.
size();
500 for (iterBegin = d->differences.begin(); iterBegin != d->differences.end(); ++iterBegin) {
503 int lineAfterLast = (*iterBegin)->trackingDestinationLineEnd();
504 if (lineAfterLast > editLineNumber
505 || (lineAfterLast == editLineNumber && ((*iterBegin)->applied() || (*iterBegin)->trackingDestinationLineNumber() == editLineNumber))) {
510 for (iterEnd = iterBegin; iterEnd != d->differences.end(); ++iterEnd) {
512 int firstLine = (*iterEnd)->trackingDestinationLineNumber();
513 if (firstLine > editLineEnd || (!(*iterEnd)->applied() && firstLine == editLineEnd)) {
516 if ((*iterEnd)->applied()) {
522 int sourceLineNumber;
523 int destinationLineNumber;
524 if (iterBegin == d->differences.end()) {
525 destinationLineNumber = editLineNumber;
526 if (!d->differences.isEmpty()) {
527 sourceLineNumber = d->differences.last()->sourceLineEnd() - (d->differences.last()->trackingDestinationLineEnd() - editLineNumber);
529 sourceLineNumber = destinationLineNumber;
531 }
else if (!(*iterBegin)->applied() || (*iterBegin)->trackingDestinationLineNumber() >= editLineNumber) {
532 destinationLineNumber = editLineNumber;
533 sourceLineNumber = (*iterBegin)->sourceLineNumber() - ((*iterBegin)->trackingDestinationLineNumber() - editLineNumber);
535 sourceLineNumber = (*iterBegin)->sourceLineNumber();
536 destinationLineNumber = (*iterBegin)->trackingDestinationLineNumber();
547 if (appliedBegin == appliedEnd) {
548 destinationLines = newLines;
549 sourceLines = oldLines;
552 int firstDestinationLineNumber = (*appliedBegin)->trackingDestinationLineNumber();
553 for (
int lineNumber = firstDestinationLineNumber; lineNumber < editLineNumber; ++lineNumber) {
554 destinationLines.
append((*appliedBegin)->destinationLineAt(lineNumber - firstDestinationLineNumber)->string());
556 for (
const QString &line : newLines) {
557 destinationLines.
append(line);
561 int lastDestinationLineNumber = (*appliedLast)->trackingDestinationLineNumber();
562 for (
int lineNumber = editLineEnd; lineNumber < (*appliedLast)->trackingDestinationLineEnd(); ++lineNumber) {
563 destinationLines.
append((*appliedLast)->destinationLineAt(lineNumber - lastDestinationLineNumber)->string());
567 if ((*appliedBegin)->trackingDestinationLineNumber() >= editLineNumber) {
568 for (
int i = editLineNumber; i < (*appliedBegin)->trackingDestinationLineNumber(); ++i) {
569 sourceLines.
append(oldLines.
at(i - editLineNumber));
574 int startPos = (*iter)->trackingDestinationLineNumber();
575 if ((*iter)->applied()) {
576 for (
int i = 0; i < (*iter)->sourceLineCount(); ++i) {
577 sourceLines.
append((*iter)->sourceLineAt(i)->string());
579 startPos = (*iter)->trackingDestinationLineEnd();
580 }
else if (startPos < editLineNumber) {
581 startPos = editLineNumber;
584 int endPos = (iter == appliedEnd) ? editLineEnd : (*iter)->trackingDestinationLineNumber();
585 for (
int i = startPos; i < endPos; ++i) {
586 sourceLines.
append(oldLines.
at(i - editLineNumber));
594 insertPosition = d->differences.erase(iterBegin, iterEnd);
597 StringListPair *pair =
new StringListPair(sourceLines, destinationLines);
600 table.createListsOfMarkers();
601 MarkerList sourceMarkers = pair->markerListFirst();
602 MarkerList destinationMarkers = pair->markerListSecond();
604 int currentSourceListLine = 0;
605 int currentDestinationListLine = 0;
608 const int terminatorLineNumber = sourceLines.
size() + destinationLines.
size() + 1;
612 while (sourceMarkerIter != sourceMarkers.
constEnd() || destinationMarkerIter != destinationMarkers.
constEnd()) {
613 int nextSourceListLine = sourceMarkerIter != sourceMarkers.
constEnd() ? (*sourceMarkerIter)->offset() : terminatorLineNumber;
614 int nextDestinationListLine = destinationMarkerIter != destinationMarkers.
constEnd() ? (*destinationMarkerIter)->offset() : terminatorLineNumber;
617 int linesToSkip = qMin(nextDestinationListLine - currentDestinationListLine, nextSourceListLine - currentSourceListLine);
618 currentSourceListLine += linesToSkip;
619 currentDestinationListLine += linesToSkip;
620 Difference *diff =
new Difference(sourceLineNumber + currentSourceListLine, destinationLineNumber + currentDestinationListLine);
621 if (nextSourceListLine == currentSourceListLine) {
622 DiffModelPrivate::processStartMarker(diff, sourceLines, sourceMarkerIter, currentSourceListLine,
true);
624 if (nextDestinationListLine == currentDestinationListLine) {
625 DiffModelPrivate::processStartMarker(diff, destinationLines, destinationMarkerIter, currentDestinationListLine,
false);
627 DiffModelPrivate::computeDiffStats(diff);
628 Q_ASSERT(diff->type() != Difference::Unchanged);
630 diff->setTrackingDestinationLineNumber(diff->destinationLineNumber());
631 insertPosition = d->differences.insert(insertPosition, diff);
636 for (; insertPosition != d->differences.end(); ++insertPosition) {
637 (*insertPosition)->setTrackingDestinationLineNumber((*insertPosition)->trackingDestinationLineNumber() + (newLines.
size() - oldLines.
size()));
639 return qMakePair(inserted, removed);
642#include "moc_diffmodel.cpp"
A model describing the differences between two files.
QPair< QList< Difference * >, QList< Difference * > > linesChanged(const QStringList &oldLines, const QStringList &newLines, int editLineNumber)
oldlines - lines that were removed.
void applyQuietly(bool apply)
Apply without emitting any signals.
int trackingDestinationLineNumber() const
Destination line number that tracks applying/unapplying of other differences Essentially a line numbe...
Computes the Levenshtein distance between two sequences.
unsigned int createTable(SequencePair *sequences)
This calculates the levenshtein distance of 2 sequences.
QString path(const QString &relativePath)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const