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
26 if (path.startsWith(QLatin1Char('"')) && path.endsWith(QLatin1Char('"')))
27 path = path.mid(1, path.size() - 2);
28
29 // Unescape quotes
30 path.replace(QLatin1String("\\\""), QLatin1String("\""));
31
32#ifndef Q_OS_WIN
33 // Unescape backquotes
34 path.replace(QLatin1String("\\\\"), QLatin1String("\\"));
35#endif
36
37 return path;
38}
39
40// static
41QString ParserBase::escapePath(QString path)
42{
43#ifndef Q_OS_WIN
44 // Escape backquotes
45 path.replace(QLatin1String("\\"), QLatin1String("\\\\"));
46#endif
47
48 // Escape quotes
49 path.replace(QLatin1String("\""), QLatin1String("\\\""));
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
472 QString type;
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.
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
typedef ConstIterator
typedef Iterator
void append(QList< T > &&value)
iterator begin()
iterator end()
QRegularExpressionMatch match(QStringView subjectView, qsizetype offset, MatchType matchType, MatchOptions matchOptions) const const
QString anchoredPattern(QStringView expression)
bool hasMatch() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString mid(qsizetype position, qsizetype n) const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
qsizetype size() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 31 2025 12:02:35 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.