KompareDiff2

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

KDE's Doxygen guidelines are available online.