Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/files/diff/PhabricatorDocumentEngineBlocks.php
12242 views
1
<?php
2
3
final class PhabricatorDocumentEngineBlocks
4
extends Phobject {
5
6
private $lists = array();
7
private $messages = array();
8
private $rangeMin;
9
private $rangeMax;
10
private $revealedIndexes;
11
private $layoutAvailableRowCount;
12
13
public function setRange($min, $max) {
14
$this->rangeMin = $min;
15
$this->rangeMax = $max;
16
return $this;
17
}
18
19
public function setRevealedIndexes(array $indexes) {
20
$this->revealedIndexes = $indexes;
21
return $this;
22
}
23
24
public function getLayoutAvailableRowCount() {
25
if ($this->layoutAvailableRowCount === null) {
26
throw new PhutilInvalidStateException('new...Layout');
27
}
28
29
return $this->layoutAvailableRowCount;
30
}
31
32
public function addMessage($message) {
33
$this->messages[] = $message;
34
return $this;
35
}
36
37
public function getMessages() {
38
return $this->messages;
39
}
40
41
public function addBlockList(
42
PhabricatorDocumentRef $ref = null,
43
array $blocks = array()) {
44
45
assert_instances_of($blocks, 'PhabricatorDocumentEngineBlock');
46
47
$this->lists[] = array(
48
'ref' => $ref,
49
'blocks' => array_values($blocks),
50
);
51
52
return $this;
53
}
54
55
public function getDocumentRefs() {
56
return ipull($this->lists, 'ref');
57
}
58
59
public function newTwoUpLayout() {
60
$rows = array();
61
$lists = $this->lists;
62
63
if (count($lists) != 2) {
64
$this->layoutAvailableRowCount = 0;
65
return array();
66
}
67
68
$specs = array();
69
foreach ($this->lists as $list) {
70
$specs[] = $this->newDiffSpec($list['blocks']);
71
}
72
73
$old_map = $specs[0]['map'];
74
$new_map = $specs[1]['map'];
75
76
$old_list = $specs[0]['list'];
77
$new_list = $specs[1]['list'];
78
79
$changeset = id(new PhabricatorDifferenceEngine())
80
->generateChangesetFromFileContent($old_list, $new_list);
81
82
$hunk_parser = id(new DifferentialHunkParser())
83
->parseHunksForLineData($changeset->getHunks())
84
->reparseHunksForSpecialAttributes();
85
86
$hunk_parser->generateVisibleBlocksMask(2);
87
$mask = $hunk_parser->getVisibleLinesMask();
88
89
$old_lines = $hunk_parser->getOldLines();
90
$new_lines = $hunk_parser->getNewLines();
91
92
$rows = array();
93
94
$count = count($old_lines);
95
for ($ii = 0; $ii < $count; $ii++) {
96
$old_line = idx($old_lines, $ii);
97
$new_line = idx($new_lines, $ii);
98
99
$is_visible = !empty($mask[$ii]);
100
101
if ($old_line) {
102
$old_hash = rtrim($old_line['text'], "\n");
103
if (!strlen($old_hash)) {
104
// This can happen when one of the sources has no blocks.
105
$old_block = null;
106
} else {
107
$old_block = array_shift($old_map[$old_hash]);
108
$old_block
109
->setDifferenceType($old_line['type'])
110
->setIsVisible($is_visible);
111
}
112
} else {
113
$old_block = null;
114
}
115
116
if ($new_line) {
117
$new_hash = rtrim($new_line['text'], "\n");
118
if (!strlen($new_hash)) {
119
$new_block = null;
120
} else {
121
$new_block = array_shift($new_map[$new_hash]);
122
$new_block
123
->setDifferenceType($new_line['type'])
124
->setIsVisible($is_visible);
125
}
126
} else {
127
$new_block = null;
128
}
129
130
// If both lists are empty, we may generate a row which has two empty
131
// blocks.
132
if (!$old_block && !$new_block) {
133
continue;
134
}
135
136
$rows[] = array(
137
$old_block,
138
$new_block,
139
);
140
}
141
142
$this->layoutAvailableRowCount = count($rows);
143
144
$rows = $this->revealIndexes($rows, true);
145
$rows = $this->sliceRows($rows);
146
147
return $rows;
148
}
149
150
public function newOneUpLayout() {
151
$rows = array();
152
$lists = $this->lists;
153
154
$idx = 0;
155
while (true) {
156
$found_any = false;
157
158
$row = array();
159
foreach ($lists as $list) {
160
$blocks = $list['blocks'];
161
$cell = idx($blocks, $idx);
162
163
if ($cell !== null) {
164
$found_any = true;
165
}
166
167
if ($cell) {
168
$rows[] = $cell;
169
}
170
}
171
172
if (!$found_any) {
173
break;
174
}
175
176
$idx++;
177
}
178
179
$this->layoutAvailableRowCount = count($rows);
180
181
$rows = $this->revealIndexes($rows, false);
182
$rows = $this->sliceRows($rows);
183
184
return $rows;
185
}
186
187
188
private function newDiffSpec(array $blocks) {
189
$map = array();
190
$list = array();
191
192
foreach ($blocks as $block) {
193
$hash = $block->getDifferenceHash();
194
195
if (!isset($map[$hash])) {
196
$map[$hash] = array();
197
}
198
$map[$hash][] = $block;
199
200
$list[] = $hash;
201
}
202
203
return array(
204
'map' => $map,
205
'list' => implode("\n", $list)."\n",
206
);
207
}
208
209
private function sliceRows(array $rows) {
210
$min = $this->rangeMin;
211
$max = $this->rangeMax;
212
213
if ($min === null && $max === null) {
214
return $rows;
215
}
216
217
if ($max === null) {
218
return array_slice($rows, $min, null, true);
219
}
220
221
if ($min === null) {
222
$min = 0;
223
}
224
225
return array_slice($rows, $min, $max - $min, true);
226
}
227
228
private function revealIndexes(array $rows, $is_vector) {
229
if ($this->revealedIndexes === null) {
230
return $rows;
231
}
232
233
foreach ($this->revealedIndexes as $index) {
234
if (!isset($rows[$index])) {
235
continue;
236
}
237
238
if ($is_vector) {
239
foreach ($rows[$index] as $block) {
240
if ($block !== null) {
241
$block->setIsVisible(true);
242
}
243
}
244
} else {
245
$rows[$index]->setIsVisible(true);
246
}
247
}
248
249
return $rows;
250
}
251
252
}
253
254