1#pragma once
2
3/****************************************************************************************
4 ** GitQlient is an application to manage and operate one or several Git repositories. With
5 ** GitQlient you will be able to add commits, branches and manage all the options Git provides.
6 ** Copyright (C) 2021 Francesc Martinez
7 **
8 ** LinkedIn: www.linkedin.com/in/cescmm/
9 ** Web: www.francescmm.com
10 **
11 ** This program is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU Lesser General Public
13 ** License as published by the Free Software Foundation; either
14 ** version 2 of the License, or (at your option) any later version.
15 **
16 ** This program is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** Lesser General Public License for more details.
20 **
21 ** You should have received a copy of the GNU Lesser General Public
22 ** License along with this library; if not, write to the Free Software
23 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 ***************************************************************************************/
25
26#include <DiffInfo.h>
27
28#include <QStringList>
29#include <QPair>
30#include <QVector>
31#include <QPlainTextEdit>
32#include <QMessageBox>
33
34namespace DiffHelper
35{
36
37struct DiffChange
38{
39 QString newFileName;
40 int newFileStartLine;
41 QString oldFileName;
42 int oldFileStartLine;
43 QString header;
44 QString content;
45 QPair<QStringList, QVector<ChunkDiffInfo::ChunkInfo>> oldData;
46 QPair<QStringList, QVector<ChunkDiffInfo::ChunkInfo>> newData;
47};
48
49inline void extractLinesFromHeader(QString header, int &startOldFile, int &startNewFile)
50{
51 header = header.split(" @@ ").first();
52 header.remove("@");
53 header = header.trimmed();
54 auto modifications = header.split(" ");
55 startOldFile = std::abs(modifications.first().split(",").first().toInt());
56 startNewFile = std::abs(modifications.last().split(",").first().toInt());
57}
58
59inline QVector<DiffChange> splitDiff(const QString &diff)
60{
61 QVector<DiffHelper::DiffChange> changes;
62
63#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
64 const auto flag = Qt::SkipEmptyParts;
65#else
66 const auto flag = QString::SkipEmptyParts;
67#endif
68
69 const auto chunks = diff.split("diff --git", flag);
70
71 for (const auto &chunk : chunks)
72 {
73 auto lines = chunk.split("\n");
74 DiffHelper::DiffChange change;
75
76 auto filesStr = lines.takeFirst();
77 auto files = filesStr.trimmed().split(" ");
78 change.newFileName = files.first().remove("a/");
79 change.oldFileName = files.last().remove("b/");
80
81 auto isA = lines.constFirst().startsWith("copy ") || lines.constFirst().startsWith("index ")
82 || lines.constFirst().startsWith("new ");
83 auto isB = lines.constFirst().startsWith("old ") || lines.constFirst().startsWith("rename ")
84 || lines.constFirst().startsWith("similarity ");
85 auto isC = lines.constFirst().startsWith("+++ ") || lines.constFirst().startsWith("--- ");
86
87 while (isA || isB || isC)
88 {
89 lines.takeFirst();
90
91 isA = lines.constFirst().startsWith("copy ") || lines.constFirst().startsWith("index ")
92 || lines.constFirst().startsWith("new ");
93 isB = lines.constFirst().startsWith("old ") || lines.constFirst().startsWith("rename ")
94 || lines.constFirst().startsWith("similarity ");
95 isC = lines.constFirst().startsWith("+++ ") || lines.constFirst().startsWith("--- ");
96 }
97
98 for (auto &line : lines)
99 {
100 if (line.startsWith("@@"))
101 {
102 if (!change.content.isEmpty())
103 {
104 changes.append(change);
105 change.content.clear();
106 }
107
108 change.header = line;
109 extractLinesFromHeader(change.header, change.oldFileStartLine, change.newFileStartLine);
110 }
111 else
112 change.content.append(line + "\n");
113 }
114
115 changes.append(change);
116 }
117 return changes;
118}
119
120inline DiffInfo processDiff(const QString &text, QPair<QStringList, QVector<ChunkDiffInfo::ChunkInfo>> &newFileData,
121 QPair<QStringList, QVector<ChunkDiffInfo::ChunkInfo>> &oldFileData)
122{
123 DiffInfo diffInfo;
124 ChunkDiffInfo diff;
125 int oldFileRow = 1;
126 int newFileRow = 1;
127
128 const auto lines = text.split("\n");
129 for (auto line : lines)
130 {
131 if (line.startsWith('-'))
132 {
133 line.remove(0, 1);
134
135 if (diff.oldFile.startLine == -1)
136 diff.oldFile.startLine = oldFileRow;
137
138 oldFileData.first.append(line);
139
140 ++oldFileRow;
141 }
142 else if (line.startsWith('+'))
143 {
144 line.remove(0, 1);
145
146 if (diff.newFile.startLine == -1)
147 {
148 diff.newFile.startLine = newFileRow;
149 diff.newFile.addition = true;
150 }
151
152 newFileData.first.append(line);
153
154 ++newFileRow;
155 }
156 else
157 {
158 line.remove(0, 1);
159
160 if (diff.oldFile.startLine != -1)
161 diff.oldFile.endLine = oldFileRow - 1;
162
163 if (diff.newFile.startLine != -1)
164 diff.newFile.endLine = newFileRow - 1;
165
166 if (diff.isValid())
167 {
168 if (diff.newFile.isValid())
169 newFileData.second.append(diff.newFile);
170
171 if (diff.oldFile.isValid())
172 oldFileData.second.append(diff.oldFile);
173
174 diffInfo.chunks.append(diff);
175 }
176
177 oldFileData.first.append(line);
178 newFileData.first.append(line);
179
180 diff = ChunkDiffInfo();
181
182 ++oldFileRow;
183 ++newFileRow;
184 }
185 }
186
187 diffInfo.fullDiff = lines;
188 diffInfo.newFileDiff = newFileData.first;
189 diffInfo.oldFileDiff = oldFileData.first;
190
191 return diffInfo;
192}
193
194inline void findString(const QString &s, QPlainTextEdit *textEdit, QWidget *managerWidget)
195{
196 if (!s.isEmpty())
197 {
198 QTextCursor cursor = textEdit->textCursor();
199 QTextCursor cursorSaved = cursor;
200
201 if (!textEdit->find(s))
202 {
203 cursor.movePosition(QTextCursor::Start);
204 textEdit->setTextCursor(cursor);
205
206 if (!textEdit->find(s))
207 {
208 textEdit->setTextCursor(cursorSaved);
209
210 QMessageBox::information(managerWidget, QObject::tr("Text not found"), QObject::tr("Text not found."));
211 }
212 }
213 }
214}
215
216}
217