Finish up the matcher for basic .gitignore matching
[vng.git] / hunks / Hunk.cpp
blobb5f5812ef268838d59ef7259e05f011b9becfb52
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Hunk.h"
21 #include <QDebug>
22 #include <QRegExp>
24 class SubHunk {
25 public:
26 SubHunk() : start(-1), length(0), accepted(Vng::Undecided), added(0), removed(0) { }
27 int start, length;
28 Vng::Acceptance accepted;
29 int added, removed;
30 int lineNumber;
33 class Hunk::Private {
34 public:
35 Private() : lineNumber(-1), ref(1), linesBefore(0), linesAfter(0)
37 SubHunk *sh = new SubHunk();
38 subhunks.append(sh);
41 ~Private() {
42 qDeleteAll(subhunks);
44 QByteArray header;
45 int lineNumber;
46 QByteArray diff;
47 int ref;
48 int linesBefore, linesAfter;
50 QList<SubHunk *> subhunks;
52 QByteArray patchForAccepted(bool inverse);
55 Hunk::Hunk()
56 : d(new Private())
60 Hunk::Hunk(const Hunk &other)
61 : d(other.d)
63 d->ref++;
66 Hunk::~Hunk()
68 if (--d->ref == 0)
69 delete d;
72 void Hunk::addLine(const QByteArray &line)
74 if (line.size() == 0)
75 return;
76 if(d->header.isEmpty()) {
77 d->header = line;
78 QString header(line);
79 QRegExp regexp("^@@ -[\\d,]+ \\+(\\d+)\\b");
80 if (regexp.indexIn(header) != -1)
81 d->lineNumber = regexp.cap(1).toInt();
82 return;
84 if (line[0] == '\\') { // "\ No newline at end of file"
85 if (d->diff.size())
86 d->diff.chop(1); // TODO should that be 2 on Windows?
87 return;
89 else if (line[0] != ' ') {
90 SubHunk *last = d->subhunks[d->subhunks.count()-1];
91 if (last->start < 0) {
92 last->start = d->diff.size();
93 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
95 else if (last->start + last->length < d->diff.size()) {
96 last = new SubHunk();
97 d->subhunks.append(last);
98 last->start = d->diff.size();
99 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
101 last->length+=line.length();
102 if (line[0] == '-') {
103 last->removed++;
104 d->linesBefore++;
106 else {
107 last->added++;
108 d->linesAfter++;
111 else {
112 d->linesBefore++;
113 d->linesAfter++;
115 d->diff.append(line);
118 Hunk & Hunk::operator=(const Hunk &other)
120 other.d->ref++;
121 if (--d->ref == 0)
122 delete d;
123 d = other.d;
124 return *this;
127 int Hunk::lineNumber() const
129 return d->lineNumber;
132 int Hunk::lineNumber(int subHunk) const
134 Q_ASSERT(subHunk < d->subhunks.count());
135 SubHunk *sh = d->subhunks[subHunk];
136 return sh->lineNumber;
139 QByteArray Hunk::patch() const
141 return d->diff;
144 bool Hunk::isEmpty() const
146 return d->diff.isEmpty();
149 Vng::Acceptance Hunk::acceptance() const
151 Vng::Acceptance a = Vng::Undecided;
152 for (int i = 0; i < d->subhunks.count(); i++) {
153 SubHunk *sh = d->subhunks[i];
154 if (a != sh->accepted) {
155 if (a == Vng::Undecided)
156 a = sh->accepted;
157 else
158 return Vng::MixedAcceptance;
161 return a;
164 void Hunk::setAcceptance(Vng::Acceptance acceptance)
166 foreach(SubHunk *sh, d->subhunks)
167 sh->accepted = acceptance;
170 Vng::Acceptance Hunk::acceptance(int subHunk) const
172 Q_ASSERT(subHunk < d->subhunks.count());
173 SubHunk *sh = d->subhunks[subHunk];
174 return sh->accepted;
177 void Hunk::setAcceptance(int subhunk, Vng::Acceptance accepted)
179 Q_ASSERT(subhunk < d->subhunks.count());
180 SubHunk *sh = d->subhunks[subhunk];
181 sh->accepted = accepted;
184 QByteArray Hunk::header() const
186 return d->header;
189 QByteArray Hunk::subHunk(int index) const
191 if (d->subhunks.count() == 0)
192 return QByteArray();
193 Q_ASSERT(index >= 0);
194 Q_ASSERT(index < d->subhunks.count());
195 SubHunk *sh = d->subhunks[index];
196 return d->diff.mid(sh->start, sh->length);
199 int Hunk::subHunkCount() const
201 return d->subhunks.count();
204 QByteArray Hunk::rejectedPatch() const
206 return d->patchForAccepted(true);
209 QByteArray Hunk::acceptedPatch() const
211 return d->patchForAccepted(false);
214 QByteArray Hunk::Private::patchForAccepted(bool inverse)
216 QByteArray patch;
217 QDataStream out (&patch, QIODevice::WriteOnly);
218 const char *myDiff = diff.data();
219 const char *space = " ";
220 int offsetInDiff = 0; // position in diff
221 int before = linesBefore; // for the header
222 foreach(SubHunk *sh, subhunks) {
223 out.writeRawData(myDiff + offsetInDiff, sh->start - offsetInDiff); // write until start
224 if (!inverse && sh->accepted == Vng::Accepted || inverse && sh->accepted != Vng::Accepted) {
225 out.writeRawData(myDiff + sh->start, sh->length);
227 else {
228 const int end = sh->start + sh->length;
229 bool startOfLine = true;
230 bool skipLine;
231 int lastLineStart = 0;
232 for (int x = sh->start; x < end; x++) {
233 if (startOfLine) {
234 startOfLine = false;
235 skipLine = myDiff[x] == '-';
236 if (! skipLine) {
237 out.writeRawData(space, 1);
238 lastLineStart = x + 1;
240 continue;
242 if (myDiff[x] == '\n') {
243 startOfLine = true;
244 if (! skipLine)
245 out.writeRawData(myDiff + lastLineStart, x - lastLineStart + 1);
248 before += sh->added - sh->removed;
250 offsetInDiff = sh->start + sh->length;
252 out.writeRawData(myDiff + offsetInDiff, diff.size() - offsetInDiff);
254 // create header
255 QString header = "@@ -"+ QString::number(lineNumber) +","+ QString::number(before)
256 +" +"+ QString::number(lineNumber) +","+ QString::number(linesAfter) + " @@\n";
258 QByteArray answer;
259 answer.append(header);
260 answer.append(patch);
261 return answer;
264 int Hunk::linesAdded() const
266 int total = 0;
267 foreach(SubHunk *sh, d->subhunks)
268 total += sh->added;
269 return total;
272 int Hunk::linesRemoved() const
274 int total = 0;
275 foreach(SubHunk *sh, d->subhunks)
276 total += sh->removed;
277 return total;