KompareDiff2

parserbase.cpp
1/*
2 SPDX-FileCopyrightText: 2002-2004,2009 Otto Bruggeman <bruggie@gmail.com>
3 SPDX-FileCopyrightText: 2007,2010 Kevin Kofler <kevin.kofler@chello.at>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "parserbase.h"
9
10// lib
11#include "difference.h"
12#include "diffhunk.h"
13#include "diffmodel.h"
14#include "diffmodellist.h"
15#include "modellist.h"
16#include <komparediff2_logging.h>
17// Qt
18#include <QObject>
19
20using namespace KompareDiff2;
21
22// static
23QString ParserBase::unescapePath(QString path)
24{
25 // If path contains spaces, it is enclosed in quotes
27 path = path.mid(1, path.size() - 2);
28
29 // Unescape quotes
31
32#ifndef Q_OS_WIN
33 // Unescape backquotes
35#endif
36
37 return path;
38}
39
40// static
41QString ParserBase::escapePath(QString path)
42{
43#ifndef Q_OS_WIN
44 // Escape backquotes
46#endif
47
48 // Escape quotes
50
51 // Enclose in quotes if path contains space
52 if (path.contains(QLatin1Char(' ')))
53 path = QLatin1Char('"') + path + QLatin1Char('"');
54
55 return path;
56}
57
58ParserBase::ParserBase(const ModelList *list, const QStringList &diff)
59 : m_diffLines(diff)
60 , m_diffIterator(m_diffLines.begin())
61 , m_list(list)
62{
63// qCDebug(KOMPAREDIFF2_LOG) << diff;
64// qCDebug(KOMPAREDIFF2_LOG) << m_diffLines;
65 m_models = new DiffModelList();
66
67 // used in contexthunkheader
68 m_contextHunkHeader1.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\*{15} ?(.*)\\n"))); // capture is for function name
69 m_contextHunkHeader2.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\*\\*\\* ([0-9]+),([0-9]+) \\*\\*\\*\\*.*\\n")));
70 // used in contexthunkbody
71 m_contextHunkHeader3.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("--- ([0-9]+),([0-9]+) ----\\n")));
72
73 m_contextHunkBodyRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("- (.*)\\n")));
74 m_contextHunkBodyAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\+ (.*)\\n")));
75 m_contextHunkBodyChanged.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("! (.*)\\n")));
76 m_contextHunkBodyContext.setPattern(QRegularExpression::anchoredPattern(QStringLiteral(" (.*)\\n")));
77 m_contextHunkBodyLine.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[-\\+! ] (.*)\\n")));
78
79 // This regexp sucks... i'll see what happens
80 m_normalDiffHeader.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("diff (?:(?:-|--)[a-zA-Z0-9=\\\"]+ )*(?:|-- +)(.*) +(.*)\\n")));
81
82 m_normalHunkHeaderAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)a([0-9]+)(|,[0-9]+)(.*)\\n")));
83 m_normalHunkHeaderRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)(|,[0-9]+)d([0-9]+)(.*)\\n")));
84 m_normalHunkHeaderChanged.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("([0-9]+)(|,[0-9]+)c([0-9]+)(|,[0-9]+)(.*)\\n")));
85
86 m_normalHunkBodyRemoved.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("< (.*)\\n")));
87 m_normalHunkBodyAdded.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("> (.*)\\n")));
88 m_normalHunkBodyDivider.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("---\\n")));
89
90 m_unifiedDiffHeader1.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("--- ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n")));
91 m_unifiedDiffHeader2.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("\\+\\+\\+ ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n")));
92 m_unifiedHunkHeader.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("@@ -([0-9]+)(|,([0-9]+)) \\+([0-9]+)(|,([0-9]+)) @@(?: ?)(.*)\\n")));
93}
94
95ParserBase::~ParserBase()
96{
97 if (m_models)
98 m_models = nullptr; // do not delete this, i pass it around...
99}
100
101Format ParserBase::determineFormat()
102{
103 // Write your own format detection routine damn it :)
104 return UnknownFormat;
105}
106
107DiffModelList *ParserBase::parse(bool *malformed)
108{
109 DiffModelList *result;
110 switch (determineFormat()) {
111 case Context:
112 result = parseContext();
113 break;
114 case Ed:
115 result = parseEd();
116 break;
117 case Normal:
118 result = parseNormal();
119 break;
120 case RCS:
121 result = parseRCS();
122 break;
123 case Unified:
124 result = parseUnified();
125 break;
126 default: // Unknown and SideBySide for now
127 result = nullptr;
128 break;
129 }
130
131 // *malformed is set to true if some hunks or parts of hunks were
132 // probably missed due to a malformed diff
133 if (malformed)
134 *malformed = m_malformed;
135
136 return result;
137}
138
139bool ParserBase::parseContextDiffHeader()
140{
141// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseContextDiffHeader()";
142 bool result = false;
143
144 while (m_diffIterator != m_diffLines.end()) {
145 const auto contextDiffHeader1Match = m_contextDiffHeader1.match(*(m_diffIterator)++);
146 if (!contextDiffHeader1Match.hasMatch()) {
147 continue;
148 }
149// qCDebug(KOMPAREDIFF2_LOG) << "Matched length Header1 = " << contextDiffHeader1Match.capturedLength();
150// qCDebug(KOMPAREDIFF2_LOG) << "Matched string Header1 = " << contextDiffHeader1Match.captured( 0 );
151 const auto contextDiffHeader2Match = m_contextDiffHeader2.match(*m_diffIterator);
152 if (m_diffIterator != m_diffLines.end() && contextDiffHeader2Match.hasMatch()) {
153// qCDebug(KOMPAREDIFF2_LOG) << "Matched length Header2 = " << contextDiffHeader2Match.capturedLength();
154// qCDebug(KOMPAREDIFF2_LOG) << "Matched string Header2 = " << contextDiffHeader2Match.captured( 0 );
155
156 m_currentModel = new DiffModel(unescapePath(contextDiffHeader1Match.captured(1)), unescapePath(contextDiffHeader2Match.captured(1)));
157 m_currentModel->setSourceTimestamp(contextDiffHeader1Match.captured(3));
158 m_currentModel->setSourceRevision(contextDiffHeader1Match.captured(5));
159 m_currentModel->setDestinationTimestamp(contextDiffHeader2Match.captured(3));
160 m_currentModel->setDestinationRevision(contextDiffHeader2Match.captured(5));
161
162 ++m_diffIterator;
163 result = true;
164
165 break;
166 } else {
167 // We're screwed, second line does not match or is not there...
168 break;
169 }
170 // Do not inc the Iterator because the second line might be the first line of
171 // the context header and the first hit was a fluke (impossible imo)
172 // maybe we should return false here because the diff is broken ?
173 }
174
175 return result;
176}
177
178bool ParserBase::parseEdDiffHeader()
179{
180 return false;
181}
182
183bool ParserBase::parseNormalDiffHeader()
184{
185// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseNormalDiffHeader()";
186 bool result = false;
187
188 while (m_diffIterator != m_diffLines.end()) {
189 const auto normalDiffHeaderMatch = m_normalDiffHeader.match(*m_diffIterator);
190 if (normalDiffHeaderMatch.hasMatch()) {
191// qCDebug(KOMPAREDIFF2_LOG) << "Matched length Header = " << normalDiffHeaderMatch.capturedLength();
192// qCDebug(KOMPAREDIFF2_LOG) << "Matched string Header = " << normalDiffHeaderMatch.captured( 0 );
193
194 m_currentModel = new DiffModel();
195 m_currentModel->setSourceFile(unescapePath(normalDiffHeaderMatch.captured(1)));
196 m_currentModel->setDestinationFile(unescapePath(normalDiffHeaderMatch.captured(2)));
197
198 result = true;
199
200 ++m_diffIterator;
201 break;
202 } else {
203 qCDebug(KOMPAREDIFF2_LOG) << "No match for: " << (*m_diffIterator);
204 }
205 ++m_diffIterator;
206 }
207
208 if (result == false) {
209 // Set this to the first line again and hope it is a single file diff
210 m_diffIterator = m_diffLines.begin();
211 m_currentModel = new DiffModel();
212 m_singleFileDiff = true;
213 }
214
215 return result;
216}
217
218bool ParserBase::parseRCSDiffHeader()
219{
220 return false;
221}
222
223bool ParserBase::parseUnifiedDiffHeader()
224{
225// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseUnifiedDiffHeader()";
226 bool result = false;
227
228 while (m_diffIterator != m_diffLines.end()) // do not assume we start with the diffheader1 line
229 {
230 const auto unifiedDiffHeader1Match = m_unifiedDiffHeader1.match(*m_diffIterator);
231 if (!unifiedDiffHeader1Match.hasMatch()) {
232 ++m_diffIterator;
233 continue;
234 }
235// qCDebug(KOMPAREDIFF2_LOG) << "Matched length Header1 = " << unifiedDiffHeader1Match.capturedLength();
236// qCDebug(KOMPAREDIFF2_LOG) << "Matched string Header1 = " << unifiedDiffHeader1Match.captured( 0 );
237 ++m_diffIterator;
238 const auto unifiedDiffHeader2Match = m_unifiedDiffHeader2.match(*m_diffIterator);
239 if (m_diffIterator != m_diffLines.end() && unifiedDiffHeader2Match.hasMatch()) {
240 m_currentModel = new DiffModel(unescapePath(unifiedDiffHeader1Match.captured(1)), unescapePath(unifiedDiffHeader2Match.captured(1)));
241 m_currentModel->setSourceTimestamp(unifiedDiffHeader1Match.captured(2));
242 m_currentModel->setSourceRevision(unifiedDiffHeader1Match.captured(4));
243 m_currentModel->setDestinationTimestamp(unifiedDiffHeader2Match.captured(2));
244 m_currentModel->setDestinationRevision(unifiedDiffHeader2Match.captured(4));
245
246 ++m_diffIterator;
247 result = true;
248
249 break;
250 } else {
251 // We're screwed, second line does not match or is not there...
252 break;
253 }
254 }
255
256 return result;
257}
258
259bool ParserBase::parseContextHunkHeader()
260{
261// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseContextHunkHeader()";
262
263 if (m_diffIterator == m_diffLines.end())
264 return false;
265
266 m_contextHunkHeader1Match = m_contextHunkHeader1.match(*(m_diffIterator));
267 if (!m_contextHunkHeader1Match.hasMatch())
268 return false; // big fat trouble, aborting...
269
270 ++m_diffIterator;
271
272 if (m_diffIterator == m_diffLines.end())
273 return false;
274
275 m_contextHunkHeader2Match = m_contextHunkHeader2.match(*(m_diffIterator));
276 if (!m_contextHunkHeader2Match.hasMatch())
277 return false; // big fat trouble, aborting...
278
279 ++m_diffIterator;
280
281 return true;
282}
283
284bool ParserBase::parseEdHunkHeader()
285{
286 return false;
287}
288
289bool ParserBase::parseNormalHunkHeader()
290{
291// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseNormalHunkHeader()";
292 if (m_diffIterator != m_diffLines.end()) {
293// qCDebug(KOMPAREDIFF2_LOG) << "Header = " << *m_diffIterator;
294 if (m_normalHunkHeaderAddedMatch = m_normalHunkHeaderAdded.match(*m_diffIterator); m_normalHunkHeaderAddedMatch.hasMatch()) {
295 m_normalDiffType = Difference::Insert;
296 } else if (m_normalHunkHeaderRemovedMatch = m_normalHunkHeaderRemoved.match(*m_diffIterator); m_normalHunkHeaderRemovedMatch.hasMatch()) {
297 m_normalDiffType = Difference::Delete;
298 } else if (m_normalHunkHeaderChangedMatch = m_normalHunkHeaderChanged.match(*m_diffIterator); m_normalHunkHeaderChangedMatch.hasMatch()) {
299 m_normalDiffType = Difference::Change;
300 } else
301 return false;
302
303 ++m_diffIterator;
304 return true;
305 }
306
307 return false;
308}
309
310bool ParserBase::parseRCSHunkHeader()
311{
312 return false;
313}
314
315bool ParserBase::parseUnifiedHunkHeader()
316{
317// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseUnifiedHunkHeader()";
318
319 if (m_diffIterator != m_diffLines.end()) {
320 m_unifiedHunkHeaderMatch = m_unifiedHunkHeader.match(*m_diffIterator);
321 if (m_unifiedHunkHeaderMatch.hasMatch()) {
322 ++m_diffIterator;
323 return true;
324 }
325 }
326// qCDebug(KOMPAREDIFF2_LOG) << "This is not a unified hunk header : " << (*m_diffIterator);
327 return false;
328}
329
330bool ParserBase::parseContextHunkBody()
331{
332// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseContextHunkBody()";
333
334 // Storing the src part of the hunk for later use
335 QStringList oldLines;
336 for (; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.match(*m_diffIterator).hasMatch(); ++m_diffIterator) {
337// qCDebug(KOMPAREDIFF2_LOG) << "Added old line: " << *m_diffIterator;
338 oldLines.append(*m_diffIterator);
339 }
340
341 const auto contextHunkHeader3Match = m_contextHunkHeader3.match(*m_diffIterator);
342 if (!contextHunkHeader3Match.hasMatch())
343 return false;
344
345 ++m_diffIterator;
346
347 // Storing the dest part of the hunk for later use
348 QStringList newLines;
349 for (; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.match(*m_diffIterator).hasMatch(); ++m_diffIterator) {
350// qCDebug(KOMPAREDIFF2_LOG) << "Added new line: " << *m_diffIterator;
351 newLines.append(*m_diffIterator);
352 }
353
354 QString function = m_contextHunkHeader1Match.captured(1);
355// qCDebug(KOMPAREDIFF2_LOG) << "Captured function: " << function;
356 int linenoA = m_contextHunkHeader2Match.captured(1).toInt();
357// qCDebug(KOMPAREDIFF2_LOG) << "Source line number: " << linenoA;
358 int linenoB = contextHunkHeader3Match.captured(1).toInt();
359// qCDebug(KOMPAREDIFF2_LOG) << "Dest line number: " << linenoB;
360
361 DiffHunk *hunk = new DiffHunk(linenoA, linenoB, function);
362
363 m_currentModel->addHunk(hunk);
364
365 QStringList::Iterator oldIt = oldLines.begin();
366 QStringList::Iterator newIt = newLines.begin();
367
368 Difference *diff;
369 while (oldIt != oldLines.end() || newIt != newLines.end()) {
370 if (oldIt != oldLines.end() && m_contextHunkBodyRemoved.match(*oldIt).hasMatch()) {
371// qCDebug(KOMPAREDIFF2_LOG) << "Delete: ";
372 diff = new Difference(linenoA, linenoB);
373 diff->setType(Difference::Delete);
374 m_currentModel->addDiff(diff);
375// qCDebug(KOMPAREDIFF2_LOG) << "Difference added";
376 hunk->add(diff);
377 for (; oldIt != oldLines.end(); ++oldIt) {
378 const auto contextHunkBodyRemovedMatch = m_contextHunkBodyRemoved.match(*oldIt);
379 if (!contextHunkBodyRemovedMatch.hasMatch()) {
380 break;
381 }
382// qCDebug(KOMPAREDIFF2_LOG) << " " << contextHunkBodyRemovedMatch.captured( 1 );
383 diff->addSourceLine(contextHunkBodyRemovedMatch.captured(1));
384 ++linenoA;
385 }
386 } else if (newIt != newLines.end() && m_contextHunkBodyAdded.match(*newIt).hasMatch()) {
387// qCDebug(KOMPAREDIFF2_LOG) << "Insert: ";
388 diff = new Difference(linenoA, linenoB);
389 diff->setType(Difference::Insert);
390 m_currentModel->addDiff(diff);
391// qCDebug(KOMPAREDIFF2_LOG) << "Difference added";
392 hunk->add(diff);
393 for (; newIt != newLines.end(); ++newIt) {
394 const auto contextHunkBodyAddedMatch = m_contextHunkBodyAdded.match(*newIt);
395 if (!contextHunkBodyAddedMatch.hasMatch()) {
396 break;
397 }
398// qCDebug(KOMPAREDIFF2_LOG) << " " << contextHunkBodyAddedMatch.captured( 1 );
399 diff->addDestinationLine(contextHunkBodyAddedMatch.captured(1));
400 ++linenoB;
401 }
402 } else if ((oldIt == oldLines.end() || m_contextHunkBodyContext.match(*oldIt).hasMatch())
403 && (newIt == newLines.end() || m_contextHunkBodyContext.match(*newIt).hasMatch())) {
404// qCDebug(KOMPAREDIFF2_LOG) << "Unchanged: ";
405 diff = new Difference(linenoA, linenoB);
406 // Do not add this diff with addDiff to the model... no unchanged differences allowed in there...
407 diff->setType(Difference::Unchanged);
408 hunk->add(diff);
409 while ((oldIt == oldLines.end() || m_contextHunkBodyContext.match(*oldIt).hasMatch())
410 && (newIt == newLines.end() || m_contextHunkBodyContext.match(*newIt).hasMatch()) && (oldIt != oldLines.end() || newIt != newLines.end())) {
411 QString l;
412 if (oldIt != oldLines.end()) {
413 l = m_contextHunkBodyContext.match(*oldIt).captured(1);
414// qCDebug(KOMPAREDIFF2_LOG) << "old: " << l;
415 ++oldIt;
416 }
417 if (newIt != newLines.end()) {
418 l = m_contextHunkBodyContext.match(*newIt).captured(1);
419// qCDebug(KOMPAREDIFF2_LOG) << "new: " << l;
420 ++newIt;
421 }
422 diff->addSourceLine(l);
423 diff->addDestinationLine(l);
424 ++linenoA;
425 ++linenoB;
426 }
427 } else if ((oldIt != oldLines.end() && m_contextHunkBodyChanged.match(*oldIt).hasMatch())
428 || (newIt != newLines.end() && m_contextHunkBodyChanged.match(*newIt).hasMatch())) {
429// qCDebug(KOMPAREDIFF2_LOG) << "Changed: ";
430 diff = new Difference(linenoA, linenoB);
431 diff->setType(Difference::Change);
432 m_currentModel->addDiff(diff);
433// qCDebug(KOMPAREDIFF2_LOG) << "Difference added";
434 hunk->add(diff);
435 while (oldIt != oldLines.end()) {
436 const auto contextHunkBodyChangedMatch = m_contextHunkBodyChanged.match(*oldIt);
437 if (!contextHunkBodyChangedMatch.hasMatch()) {
438 break;
439 }
440// qCDebug(KOMPAREDIFF2_LOG) << " " << contextHunkBodyChangedMatch.captured( 1 );
441 diff->addSourceLine(contextHunkBodyChangedMatch.captured(1));
442 ++linenoA;
443 ++oldIt;
444 }
445 while (newIt != newLines.end()) {
446 const auto contextHunkBodyChangedMatch = m_contextHunkBodyChanged.match(*newIt);
447 if (!contextHunkBodyChangedMatch.hasMatch()) {
448 break;
449 }
450// qCDebug(KOMPAREDIFF2_LOG) << " " << contextHunkBodyChangedMatch.captured( 1 );
451 diff->addDestinationLine(contextHunkBodyChangedMatch.captured(1));
452 ++linenoB;
453 ++newIt;
454 }
455 } else
456 return false;
458 }
459
460 return true;
461}
462
463bool ParserBase::parseEdHunkBody()
464{
465 return false;
466}
467
468bool ParserBase::parseNormalHunkBody()
469{
470// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseNormalHunkBody";
471
473
474 int linenoA = 0, linenoB = 0;
475
476 if (m_normalDiffType == Difference::Insert) {
477 linenoA = m_normalHunkHeaderAddedMatch.captured(1).toInt();
478 linenoB = m_normalHunkHeaderAddedMatch.captured(2).toInt();
479 } else if (m_normalDiffType == Difference::Delete) {
480 linenoA = m_normalHunkHeaderRemovedMatch.captured(1).toInt();
481 linenoB = m_normalHunkHeaderRemovedMatch.captured(3).toInt();
482 } else if (m_normalDiffType == Difference::Change) {
483 linenoA = m_normalHunkHeaderChangedMatch.captured(1).toInt();
484 linenoB = m_normalHunkHeaderChangedMatch.captured(3).toInt();
485 }
486
487 DiffHunk *hunk = new DiffHunk(linenoA, linenoB);
488 m_currentModel->addHunk(hunk);
489 Difference *diff = new Difference(linenoA, linenoB);
490 hunk->add(diff);
491 m_currentModel->addDiff(diff);
492
493 diff->setType(m_normalDiffType);
494
495 if (m_normalDiffType == Difference::Change || m_normalDiffType == Difference::Delete)
496 for (; m_diffIterator != m_diffLines.end(); ++m_diffIterator) {
497 const auto normalHunkBodyRemovedMatch = m_normalHunkBodyRemoved.match(*m_diffIterator);
498 if (!normalHunkBodyRemovedMatch.hasMatch()) {
499 break;
500 }
501// qCDebug(KOMPAREDIFF2_LOG) << "Line = " << *m_diffIterator;
502 diff->addSourceLine(normalHunkBodyRemovedMatch.captured(1));
503 }
504
505 if (m_normalDiffType == Difference::Change) {
506 if (m_diffIterator != m_diffLines.end() && m_normalHunkBodyDivider.match(*m_diffIterator).hasMatch()) {
507// qCDebug(KOMPAREDIFF2_LOG) << "Line = " << *m_diffIterator;
508 ++m_diffIterator;
509 } else
510 return false;
511 }
512
513 if (m_normalDiffType == Difference::Insert || m_normalDiffType == Difference::Change)
514 for (; m_diffIterator != m_diffLines.end(); ++m_diffIterator) {
515 const auto normalHunkBodyAddedMatch = m_normalHunkBodyAdded.match(*m_diffIterator);
516 if (!normalHunkBodyAddedMatch.hasMatch()) {
517 break;
518 }
519// qCDebug(KOMPAREDIFF2_LOG) << "Line = " << *m_diffIterator;
520 diff->addDestinationLine(normalHunkBodyAddedMatch.captured(1));
521 }
522
523 return true;
524}
525
526bool ParserBase::parseRCSHunkBody()
527{
528 return false;
529}
530
531bool ParserBase::matchesUnifiedHunkLine(const QString &line) const
532{
533 static const QChar context = QLatin1Char(' ');
534 static const QChar added = QLatin1Char('+');
535 static const QChar removed = QLatin1Char('-');
536
537 QChar first = line[0];
538
539 return (first == context || first == added || first == removed);
540}
541
542bool ParserBase::parseUnifiedHunkBody()
543{
544// qCDebug(KOMPAREDIFF2_LOG) << "ParserBase::parseUnifiedHunkBody";
545
546 int linenoA = 0, linenoB = 0;
547 bool wasNum;
548
549 // Fetching the stuff we need from the hunkheader regexp that was parsed in parseUnifiedHunkHeader();
550 linenoA = m_unifiedHunkHeaderMatch.captured(1).toInt();
551 int lineCountA = 1, lineCountB = 1; // an omitted line count in the header implies a line count of 1
552 if (!m_unifiedHunkHeaderMatch.captured(3).isEmpty()) {
553 lineCountA = m_unifiedHunkHeaderMatch.captured(3).toInt(&wasNum);
554 if (!wasNum)
555 return false;
556
557 // If a hunk is an insertion or deletion with no context, the line number given
558 // is the one before the hunk. this isn't what we want, so increment it to fix this.
559 if (lineCountA == 0)
560 ++linenoA;
561 }
562 linenoB = m_unifiedHunkHeaderMatch.captured(4).toInt();
563 if (!m_unifiedHunkHeaderMatch.captured(6).isEmpty()) {
564 lineCountB = m_unifiedHunkHeaderMatch.captured(6).toInt(&wasNum);
565 if (!wasNum)
566 return false;
567
568 if (lineCountB == 0) // see above
569 ++linenoB;
570 }
571 QString function = m_unifiedHunkHeaderMatch.captured(7);
572
573 DiffHunk *hunk = new DiffHunk(linenoA, linenoB, function);
574 m_currentModel->addHunk(hunk);
575
576 const QStringList::ConstIterator m_diffLinesEnd = m_diffLines.end();
577
578 const QString context = QStringLiteral(" ");
579 const QString added = QStringLiteral("+");
580 const QString removed = QStringLiteral("-");
581
582 while (m_diffIterator != m_diffLinesEnd && matchesUnifiedHunkLine(*m_diffIterator) && (lineCountA || lineCountB)) {
583 Difference *diff = new Difference(linenoA, linenoB);
584 hunk->add(diff);
585
586 if ((*m_diffIterator).startsWith(context)) { // context
587 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(context) && (lineCountA || lineCountB); ++m_diffIterator) {
588 diff->addSourceLine(QString(*m_diffIterator).remove(0, 1));
589 diff->addDestinationLine(QString(*m_diffIterator).remove(0, 1));
590 ++linenoA;
591 ++linenoB;
592 --lineCountA;
593 --lineCountB;
594 }
595 } else { // This is a real difference, not context
596 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(removed) && (lineCountA || lineCountB); ++m_diffIterator) {
597 diff->addSourceLine(QString(*m_diffIterator).remove(0, 1));
598 ++linenoA;
599 --lineCountA;
600 }
601 for (; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith(added) && (lineCountA || lineCountB); ++m_diffIterator) {
602 diff->addDestinationLine(QString(*m_diffIterator).remove(0, 1));
603 ++linenoB;
604 --lineCountB;
605 }
606 if (diff->sourceLineCount() == 0) {
607 diff->setType(Difference::Insert);
608// qCDebug(KOMPAREDIFF2_LOG) << "Insert difference";
609 } else if (diff->destinationLineCount() == 0) {
610 diff->setType(Difference::Delete);
611// qCDebug(KOMPAREDIFF2_LOG) << "Delete difference";
612 } else {
613 diff->setType(Difference::Change);
614// qCDebug(KOMPAREDIFF2_LOG) << "Change difference";
615 }
617 m_currentModel->addDiff(diff);
618 }
619 }
620
621 return true;
622}
623
624void ParserBase::checkHeader(const QRegularExpression &header)
625{
626 // clang-format off
627 if (m_diffIterator != m_diffLines.end() &&
628 !header.match(*m_diffIterator).hasMatch() &&
629 !m_diffIterator->startsWith(QLatin1String("Index: ")) && /* SVN diff */
630 !m_diffIterator->startsWith(QLatin1String("diff ")) && /* concatenated diff */
631 !m_diffIterator->startsWith(QLatin1String("-- ")) /* git format-patch */)
632 // clang-format on
633 m_malformed = true;
634}
635
636DiffModelList *ParserBase::parseContext()
637{
638 while (parseContextDiffHeader()) {
639 while (parseContextHunkHeader())
640 parseContextHunkBody();
641 if (m_currentModel->differenceCount() > 0)
642 m_models->append(m_currentModel);
643 checkHeader(m_contextDiffHeader1);
644 }
645
646 m_models->sort();
647
648 if (m_models->count() > 0) {
649 return m_models;
650 } else {
651 delete m_models;
652 return nullptr;
653 }
654}
655
656DiffModelList *ParserBase::parseEd()
657{
658 while (parseEdDiffHeader()) {
659 while (parseEdHunkHeader())
660 parseEdHunkBody();
661 if (m_currentModel->differenceCount() > 0)
662 m_models->append(m_currentModel);
663 }
664
665 m_models->sort();
666
667 if (m_models->count() > 0) {
668 return m_models;
669 } else {
670 delete m_models;
671 return nullptr;
672 }
673}
674
675DiffModelList *ParserBase::parseNormal()
676{
677 while (parseNormalDiffHeader()) {
678 while (parseNormalHunkHeader())
679 parseNormalHunkBody();
680 if (m_currentModel->differenceCount() > 0)
681 m_models->append(m_currentModel);
682 checkHeader(m_normalDiffHeader);
683 }
684
685 if (m_singleFileDiff) {
686 while (parseNormalHunkHeader())
687 parseNormalHunkBody();
688 if (m_currentModel->differenceCount() > 0)
689 m_models->append(m_currentModel);
690 if (m_diffIterator != m_diffLines.end())
691 m_malformed = true;
692 }
693
694 m_models->sort();
695
696 if (m_models->count() > 0) {
697 return m_models;
698 } else {
699 delete m_models;
700 return nullptr;
701 }
702}
703
704DiffModelList *ParserBase::parseRCS()
705{
706 while (parseRCSDiffHeader()) {
707 while (parseRCSHunkHeader())
708 parseRCSHunkBody();
709 if (m_currentModel->differenceCount() > 0)
710 m_models->append(m_currentModel);
711 }
712
713 m_models->sort();
714
715 if (m_models->count() > 0) {
716 return m_models;
717 } else {
718 delete m_models;
719 return nullptr;
720 }
721}
722
723DiffModelList *ParserBase::parseUnified()
724{
725 while (parseUnifiedDiffHeader()) {
726 while (parseUnifiedHunkHeader())
727 parseUnifiedHunkBody();
728// qCDebug(KOMPAREDIFF2_LOG) << "New model ready to be analyzed...";
729// qCDebug(KOMPAREDIFF2_LOG) << " differenceCount() == " << m_currentModel->differenceCount();
730 if (m_currentModel->differenceCount() > 0)
731 m_models->append(m_currentModel);
732 checkHeader(m_unifiedDiffHeader1);
733 }
734
735 m_models->sort();
736
737 if (m_models->count() > 0) {
738 return m_models;
739 } else {
740 delete m_models;
741 return nullptr;
742 }
743}
A list of DiffModel.
A model describing the differences between two files.
Definition diffmodel.h:31
void determineInlineDifferences()
This method will calculate the differences between the individual strings and store them as Markers.
Type type(const QSqlDatabase &db)
QString path(const QString &relativePath)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
const QList< QKeySequence > & begin()
KompareDiff2 namespace.
Format
Patch format enum.
Definition global.h:16
void append(QList< T > &&value)
iterator begin()
qsizetype count() const const
iterator end()
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString anchoredPattern(QStringView expression)
QString captured(QStringView name) const const
bool hasMatch() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
int toInt(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:03:30 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.