Serenity Operating System
1/*
2 * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "Hunks.h"
8#include <AK/Debug.h>
9
10namespace Diff {
11Vector<Hunk> parse_hunks(DeprecatedString const& diff)
12{
13 Vector<DeprecatedString> diff_lines = diff.split('\n');
14 if (diff_lines.is_empty())
15 return {};
16
17 Vector<Hunk> hunks;
18
19 size_t line_index = 0;
20 HunkLocation current_location {};
21
22 // Skip to first hunk
23 while (diff_lines[line_index][0] != '@') {
24 ++line_index;
25 }
26
27 while (line_index < diff_lines.size()) {
28 if (diff_lines[line_index][0] == '@') {
29 current_location = parse_hunk_location(diff_lines[line_index]);
30 ++line_index;
31 continue;
32 }
33 if (diff_lines[line_index][0] == ' ') {
34 current_location.apply_offset(1, HunkLocation::LocationType::Both);
35 ++line_index;
36 continue;
37 }
38 Hunk hunk {};
39 hunk.original_start_line = current_location.original_start_line;
40 hunk.target_start_line = current_location.target_start_line;
41
42 while (line_index < diff_lines.size() && diff_lines[line_index][0] == '-') {
43 hunk.removed_lines.append(diff_lines[line_index].substring(1, diff_lines[line_index].length() - 1));
44 current_location.apply_offset(1, HunkLocation::LocationType::Original);
45 ++line_index;
46 }
47 while (line_index < diff_lines.size() && diff_lines[line_index][0] == '+') {
48 hunk.added_lines.append(diff_lines[line_index].substring(1, diff_lines[line_index].length() - 1));
49 current_location.apply_offset(1, HunkLocation::LocationType::Target);
50 ++line_index;
51 }
52
53 while (line_index < diff_lines.size() && diff_lines[line_index][0] == ' ') {
54 current_location.apply_offset(1, HunkLocation::LocationType::Both);
55 ++line_index;
56 }
57 hunks.append(hunk);
58 }
59
60 if constexpr (HUNKS_DEBUG) {
61 for (auto const& hunk : hunks) {
62 dbgln("Hunk location:");
63 dbgln(" orig: {}", hunk.original_start_line);
64 dbgln(" target: {}", hunk.target_start_line);
65 dbgln(" removed:");
66 for (auto const& line : hunk.removed_lines)
67 dbgln("- {}", line);
68 dbgln(" added:");
69 for (auto const& line : hunk.added_lines)
70 dbgln("+ {}", line);
71 }
72 }
73
74 return hunks;
75}
76
77HunkLocation parse_hunk_location(DeprecatedString const& location_line)
78{
79 size_t char_index = 0;
80 struct StartAndLength {
81 size_t start { 0 };
82 size_t length { 0 };
83 };
84 auto parse_start_and_length_pair = [](DeprecatedString const& raw) {
85 auto index_of_separator = raw.find(',').value();
86 auto start = raw.substring(0, index_of_separator).to_uint().value();
87 auto length = raw.substring(index_of_separator + 1, raw.length() - index_of_separator - 1).to_uint().value();
88
89 if (start != 0)
90 start--;
91
92 if (length != 0)
93 length--;
94
95 return StartAndLength { start, length };
96 };
97 while (char_index < location_line.length() && location_line[char_index++] != '-') {
98 }
99 VERIFY(char_index < location_line.length());
100
101 size_t original_location_start_index = char_index;
102
103 while (char_index < location_line.length() && location_line[char_index++] != ' ') {
104 }
105 VERIFY(char_index < location_line.length() && location_line[char_index] == '+');
106 size_t original_location_end_index = char_index - 2;
107
108 size_t target_location_start_index = char_index + 1;
109
110 char_index += 1;
111 while (char_index < location_line.length() && location_line[char_index++] != ' ') {
112 }
113 VERIFY(char_index < location_line.length());
114
115 size_t target_location_end_index = char_index - 2;
116
117 auto original_pair = parse_start_and_length_pair(location_line.substring(original_location_start_index, original_location_end_index - original_location_start_index + 1));
118 auto target_pair = parse_start_and_length_pair(location_line.substring(target_location_start_index, target_location_end_index - target_location_start_index + 1));
119 return { original_pair.start, original_pair.length, target_pair.start, target_pair.length };
120}
121
122void HunkLocation::apply_offset(size_t offset, HunkLocation::LocationType type)
123{
124 if (type == LocationType::Original || type == LocationType::Both) {
125 original_start_line += offset;
126 original_length -= offset;
127 }
128 if (type == LocationType::Target || type == LocationType::Both) {
129 target_start_line += offset;
130 target_length -= offset;
131 }
132}
133
134};