mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 11:45:11 +00:00

Currently the only error that can happen is an OOM. However, in the future there may be other errors that this function may throw, such as detecting an invalid patch.
135 lines
4.6 KiB
C++
135 lines
4.6 KiB
C++
/*
|
|
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "Hunks.h"
|
|
#include <AK/Debug.h>
|
|
|
|
namespace Diff {
|
|
|
|
ErrorOr<Vector<Hunk>> parse_hunks(DeprecatedString const& diff)
|
|
{
|
|
Vector<DeprecatedString> diff_lines = diff.split('\n');
|
|
if (diff_lines.is_empty())
|
|
return Vector<Hunk> {};
|
|
|
|
Vector<Hunk> hunks;
|
|
|
|
size_t line_index = 0;
|
|
HunkLocation current_location {};
|
|
|
|
// Skip to first hunk
|
|
while (diff_lines[line_index][0] != '@') {
|
|
++line_index;
|
|
}
|
|
|
|
while (line_index < diff_lines.size()) {
|
|
if (diff_lines[line_index][0] == '@') {
|
|
current_location = parse_hunk_location(diff_lines[line_index]);
|
|
++line_index;
|
|
continue;
|
|
}
|
|
if (diff_lines[line_index][0] == ' ') {
|
|
current_location.apply_offset(1, HunkLocation::LocationType::Both);
|
|
++line_index;
|
|
continue;
|
|
}
|
|
Hunk hunk {};
|
|
hunk.original_start_line = current_location.original_start_line;
|
|
hunk.target_start_line = current_location.target_start_line;
|
|
|
|
while (line_index < diff_lines.size() && diff_lines[line_index][0] == '-') {
|
|
TRY(hunk.removed_lines.try_append(diff_lines[line_index].substring(1, diff_lines[line_index].length() - 1)));
|
|
current_location.apply_offset(1, HunkLocation::LocationType::Original);
|
|
++line_index;
|
|
}
|
|
while (line_index < diff_lines.size() && diff_lines[line_index][0] == '+') {
|
|
TRY(hunk.added_lines.try_append(diff_lines[line_index].substring(1, diff_lines[line_index].length() - 1)));
|
|
current_location.apply_offset(1, HunkLocation::LocationType::Target);
|
|
++line_index;
|
|
}
|
|
|
|
while (line_index < diff_lines.size() && diff_lines[line_index][0] == ' ') {
|
|
current_location.apply_offset(1, HunkLocation::LocationType::Both);
|
|
++line_index;
|
|
}
|
|
TRY(hunks.try_append(hunk));
|
|
}
|
|
|
|
if constexpr (HUNKS_DEBUG) {
|
|
for (auto const& hunk : hunks) {
|
|
dbgln("Hunk location:");
|
|
dbgln(" orig: {}", hunk.original_start_line);
|
|
dbgln(" target: {}", hunk.target_start_line);
|
|
dbgln(" removed:");
|
|
for (auto const& line : hunk.removed_lines)
|
|
dbgln("- {}", line);
|
|
dbgln(" added:");
|
|
for (auto const& line : hunk.added_lines)
|
|
dbgln("+ {}", line);
|
|
}
|
|
}
|
|
|
|
return hunks;
|
|
}
|
|
|
|
HunkLocation parse_hunk_location(StringView location_line)
|
|
{
|
|
size_t char_index = 0;
|
|
struct StartAndLength {
|
|
size_t start { 0 };
|
|
size_t length { 0 };
|
|
};
|
|
auto parse_start_and_length_pair = [](StringView raw) {
|
|
auto index_of_separator = raw.find(',').value();
|
|
auto start = raw.substring_view(0, index_of_separator).to_uint().value();
|
|
auto length = raw.substring_view(index_of_separator + 1, raw.length() - index_of_separator - 1).to_uint().value();
|
|
|
|
if (start != 0)
|
|
start--;
|
|
|
|
if (length != 0)
|
|
length--;
|
|
|
|
return StartAndLength { start, length };
|
|
};
|
|
while (char_index < location_line.length() && location_line[char_index++] != '-') {
|
|
}
|
|
VERIFY(char_index < location_line.length());
|
|
|
|
size_t original_location_start_index = char_index;
|
|
|
|
while (char_index < location_line.length() && location_line[char_index++] != ' ') {
|
|
}
|
|
VERIFY(char_index < location_line.length() && location_line[char_index] == '+');
|
|
size_t original_location_end_index = char_index - 2;
|
|
|
|
size_t target_location_start_index = char_index + 1;
|
|
|
|
char_index += 1;
|
|
while (char_index < location_line.length() && location_line[char_index++] != ' ') {
|
|
}
|
|
VERIFY(char_index < location_line.length());
|
|
|
|
size_t target_location_end_index = char_index - 2;
|
|
|
|
auto original_pair = parse_start_and_length_pair(location_line.substring_view(original_location_start_index, original_location_end_index - original_location_start_index + 1));
|
|
auto target_pair = parse_start_and_length_pair(location_line.substring_view(target_location_start_index, target_location_end_index - target_location_start_index + 1));
|
|
return { original_pair.start, original_pair.length, target_pair.start, target_pair.length };
|
|
}
|
|
|
|
void HunkLocation::apply_offset(size_t offset, HunkLocation::LocationType type)
|
|
{
|
|
if (type == LocationType::Original || type == LocationType::Both) {
|
|
original_start_line += offset;
|
|
original_length -= offset;
|
|
}
|
|
if (type == LocationType::Target || type == LocationType::Both) {
|
|
target_start_line += offset;
|
|
target_length -= offset;
|
|
}
|
|
}
|
|
|
|
};
|