Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
12256 views
1
<?php
2
3
final class DifferentialChangesetTwoUpRenderer
4
extends DifferentialChangesetHTMLRenderer {
5
6
private $newOffsetMap;
7
8
public function isOneUpRenderer() {
9
return false;
10
}
11
12
protected function getRendererTableClass() {
13
return 'diff-2up';
14
}
15
16
public function getRendererKey() {
17
return '2up';
18
}
19
20
protected function renderColgroup() {
21
return phutil_tag('colgroup', array(), array(
22
phutil_tag('col', array('class' => 'num')),
23
phutil_tag('col', array('class' => 'left')),
24
phutil_tag('col', array('class' => 'num')),
25
phutil_tag('col', array('class' => 'copy')),
26
phutil_tag('col', array('class' => 'right')),
27
phutil_tag('col', array('class' => 'cov')),
28
));
29
}
30
31
public function renderTextChange(
32
$range_start,
33
$range_len,
34
$rows) {
35
36
$hunk_starts = $this->getHunkStartLines();
37
38
$context_not_available = null;
39
if ($hunk_starts) {
40
$context_not_available = javelin_tag(
41
'tr',
42
array(
43
'sigil' => 'context-target',
44
),
45
phutil_tag(
46
'td',
47
array(
48
'colspan' => 6,
49
'class' => 'show-more',
50
),
51
pht('Context not available.')));
52
}
53
54
$html = array();
55
56
$old_lines = $this->getOldLines();
57
$new_lines = $this->getNewLines();
58
$gaps = $this->getGaps();
59
$reference = $this->getRenderingReference();
60
61
list($left_prefix, $right_prefix) = $this->getLineIDPrefixes();
62
63
$changeset = $this->getChangeset();
64
$copy_lines = idx($changeset->getMetadata(), 'copy:lines', array());
65
$highlight_old = $this->getHighlightOld();
66
$highlight_new = $this->getHighlightNew();
67
$old_render = $this->getOldRender();
68
$new_render = $this->getNewRender();
69
$original_left = $this->getOriginalOld();
70
$original_right = $this->getOriginalNew();
71
$mask = $this->getMask();
72
73
$scope_engine = $this->getScopeEngine();
74
$offset_map = null;
75
$depth_only = $this->getDepthOnlyLines();
76
77
for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
78
if (empty($mask[$ii])) {
79
// If we aren't going to show this line, we've just entered a gap.
80
// Pop information about the next gap off the $gaps stack and render
81
// an appropriate "Show more context" element. This branch eventually
82
// increments $ii by the entire size of the gap and then continues
83
// the loop.
84
$gap = array_pop($gaps);
85
$top = $gap[0];
86
$len = $gap[1];
87
88
$contents = $this->renderShowContextLinks($top, $len, $rows);
89
90
$is_last_block = false;
91
if ($ii + $len >= $rows) {
92
$is_last_block = true;
93
}
94
95
$context_text = null;
96
$context_line = null;
97
if (!$is_last_block && $scope_engine) {
98
$target_line = $new_lines[$ii + $len]['line'];
99
$context_line = $scope_engine->getScopeStart($target_line);
100
if ($context_line !== null) {
101
// The scope engine returns a line number in the file. We need
102
// to map that back to a display offset in the diff.
103
if (!$offset_map) {
104
$offset_map = $this->getNewLineToOffsetMap();
105
}
106
$offset = $offset_map[$context_line];
107
$context_text = $new_render[$offset];
108
}
109
}
110
111
$container = javelin_tag(
112
'tr',
113
array(
114
'sigil' => 'context-target',
115
),
116
array(
117
phutil_tag(
118
'td',
119
array(
120
'class' => 'show-context-line n left-context',
121
)),
122
phutil_tag(
123
'td',
124
array(
125
'class' => 'show-more',
126
),
127
$contents),
128
phutil_tag(
129
'td',
130
array(
131
'class' => 'show-context-line n',
132
'data-n' => $context_line,
133
)),
134
phutil_tag(
135
'td',
136
array(
137
'colspan' => 3,
138
'class' => 'show-context',
139
),
140
// TODO: [HTML] Escaping model here isn't ideal.
141
phutil_safe_html($context_text)),
142
));
143
144
$html[] = $container;
145
146
$ii += ($len - 1);
147
continue;
148
}
149
150
$o_num = null;
151
$o_classes = '';
152
$o_text = null;
153
if (isset($old_lines[$ii])) {
154
$o_num = $old_lines[$ii]['line'];
155
$o_text = isset($old_render[$ii]) ? $old_render[$ii] : null;
156
if ($old_lines[$ii]['type']) {
157
if ($old_lines[$ii]['type'] == '\\') {
158
$o_text = $old_lines[$ii]['text'];
159
$o_class = 'comment';
160
} else if ($original_left && !isset($highlight_old[$o_num])) {
161
$o_class = 'old-rebase';
162
} else if (empty($new_lines[$ii])) {
163
$o_class = 'old old-full';
164
} else {
165
if (isset($depth_only[$ii])) {
166
if ($depth_only[$ii] == '>') {
167
// When a line has depth-only change, we only highlight the
168
// left side of the diff if the depth is decreasing. When the
169
// depth is increasing, the ">>" marker on the right hand side
170
// of the diff generally provides enough visibility on its own.
171
172
$o_class = '';
173
} else {
174
$o_class = 'old';
175
}
176
} else {
177
$o_class = 'old';
178
}
179
}
180
$o_classes = $o_class;
181
}
182
}
183
184
$n_copy = hsprintf('<td class="copy" />');
185
$n_cov = null;
186
$n_colspan = 2;
187
$n_classes = '';
188
$n_num = null;
189
$n_text = null;
190
191
if (isset($new_lines[$ii])) {
192
$n_num = $new_lines[$ii]['line'];
193
$n_text = isset($new_render[$ii]) ? $new_render[$ii] : null;
194
$coverage = $this->getCodeCoverage();
195
196
if ($coverage !== null) {
197
if (empty($coverage[$n_num - 1])) {
198
$cov_class = 'N';
199
} else {
200
$cov_class = $coverage[$n_num - 1];
201
}
202
$cov_class = 'cov-'.$cov_class;
203
$n_cov = phutil_tag('td', array('class' => "cov {$cov_class}"));
204
$n_colspan--;
205
}
206
207
if ($new_lines[$ii]['type']) {
208
if ($new_lines[$ii]['type'] == '\\') {
209
$n_text = $new_lines[$ii]['text'];
210
$n_class = 'comment';
211
} else if ($original_right && !isset($highlight_new[$n_num])) {
212
$n_class = 'new-rebase';
213
} else if (empty($old_lines[$ii])) {
214
$n_class = 'new new-full';
215
} else {
216
// When a line has a depth-only change, never highlight it on
217
// the right side. The ">>" marker generally provides enough
218
// visibility on its own for indent depth increases, and the left
219
// side is still highlighted for indent depth decreases.
220
221
if (isset($depth_only[$ii])) {
222
$n_class = '';
223
} else {
224
$n_class = 'new';
225
}
226
}
227
$n_classes = $n_class;
228
229
$not_copied =
230
// If this line only changed depth, copy markers are pointless.
231
(!isset($copy_lines[$n_num])) ||
232
(isset($depth_only[$ii])) ||
233
($new_lines[$ii]['type'] == '\\');
234
235
if ($not_copied) {
236
$n_copy = phutil_tag('td', array('class' => 'copy'));
237
} else {
238
list($orig_file, $orig_line, $orig_type) = $copy_lines[$n_num];
239
$title = ($orig_type == '-' ? 'Moved' : 'Copied').' from ';
240
if ($orig_file == '') {
241
$title .= "line {$orig_line}";
242
} else {
243
$title .=
244
basename($orig_file).
245
":{$orig_line} in dir ".
246
dirname('/'.$orig_file);
247
}
248
$class = ($orig_type == '-' ? 'new-move' : 'new-copy');
249
$n_copy = javelin_tag(
250
'td',
251
array(
252
'meta' => array(
253
'msg' => $title,
254
),
255
'class' => 'copy '.$class,
256
));
257
}
258
}
259
}
260
261
if (isset($hunk_starts[$o_num])) {
262
$html[] = $context_not_available;
263
}
264
265
if ($o_num && $left_prefix) {
266
$o_id = $left_prefix.$o_num;
267
} else {
268
$o_id = null;
269
}
270
271
if ($n_num && $right_prefix) {
272
$n_id = $right_prefix.$n_num;
273
} else {
274
$n_id = null;
275
}
276
277
$old_comments = $this->getOldComments();
278
$new_comments = $this->getNewComments();
279
$scaffolds = array();
280
281
if ($o_num && isset($old_comments[$o_num])) {
282
foreach ($old_comments[$o_num] as $comment) {
283
$inline = $this->buildInlineComment(
284
$comment,
285
$on_right = false);
286
$scaffold = $this->getRowScaffoldForInline($inline);
287
288
if ($n_num && isset($new_comments[$n_num])) {
289
foreach ($new_comments[$n_num] as $key => $new_comment) {
290
if ($comment->isCompatible($new_comment)) {
291
$companion = $this->buildInlineComment(
292
$new_comment,
293
$on_right = true);
294
295
$scaffold->addInlineView($companion);
296
unset($new_comments[$n_num][$key]);
297
break;
298
}
299
}
300
}
301
302
303
$scaffolds[] = $scaffold;
304
}
305
}
306
307
if ($n_num && isset($new_comments[$n_num])) {
308
foreach ($new_comments[$n_num] as $comment) {
309
$inline = $this->buildInlineComment(
310
$comment,
311
$on_right = true);
312
313
$scaffolds[] = $this->getRowScaffoldForInline($inline);
314
}
315
}
316
317
$old_number = phutil_tag(
318
'td',
319
array(
320
'id' => $o_id,
321
'class' => $o_classes.' n',
322
'data-n' => $o_num,
323
));
324
325
$new_number = phutil_tag(
326
'td',
327
array(
328
'id' => $n_id,
329
'class' => $n_classes.' n',
330
'data-n' => $n_num,
331
));
332
333
$html[] = phutil_tag('tr', array(), array(
334
$old_number,
335
phutil_tag(
336
'td',
337
array(
338
'class' => $o_classes,
339
'data-copy-mode' => 'copy-l',
340
),
341
$o_text),
342
$new_number,
343
$n_copy,
344
phutil_tag(
345
'td',
346
array(
347
'class' => $n_classes,
348
'colspan' => $n_colspan,
349
'data-copy-mode' => 'copy-r',
350
),
351
$n_text),
352
$n_cov,
353
));
354
355
if ($context_not_available && ($ii == $rows - 1)) {
356
$html[] = $context_not_available;
357
}
358
359
foreach ($scaffolds as $scaffold) {
360
$html[] = $scaffold;
361
}
362
}
363
364
return $this->wrapChangeInTable(phutil_implode_html('', $html));
365
}
366
367
public function renderDocumentEngineBlocks(
368
PhabricatorDocumentEngineBlocks $block_list,
369
$old_changeset_key,
370
$new_changeset_key) {
371
372
$engine = $this->getDocumentEngine();
373
374
$old_ref = null;
375
$new_ref = null;
376
$refs = $block_list->getDocumentRefs();
377
if ($refs) {
378
list($old_ref, $new_ref) = $refs;
379
}
380
381
$old_comments = $this->getOldComments();
382
$new_comments = $this->getNewComments();
383
384
$rows = array();
385
$gap = array();
386
$in_gap = false;
387
388
// NOTE: The generated layout is affected by range constraints, and may
389
// represent only a slice of the document.
390
391
$layout = $block_list->newTwoUpLayout();
392
$available_count = $block_list->getLayoutAvailableRowCount();
393
394
foreach ($layout as $idx => $row) {
395
list($old, $new) = $row;
396
397
if ($old) {
398
$old_key = $old->getBlockKey();
399
$is_visible = $old->getIsVisible();
400
} else {
401
$old_key = null;
402
}
403
404
if ($new) {
405
$new_key = $new->getBlockKey();
406
$is_visible = $new->getIsVisible();
407
} else {
408
$new_key = null;
409
}
410
411
if (!$is_visible) {
412
if (!$in_gap) {
413
$in_gap = true;
414
}
415
$gap[$idx] = $row;
416
continue;
417
}
418
419
if ($in_gap) {
420
$in_gap = false;
421
$rows[] = $this->renderDocumentEngineGap(
422
$gap,
423
$available_count);
424
$gap = array();
425
}
426
427
if ($old) {
428
$is_rem = ($old->getDifferenceType() === '-');
429
} else {
430
$is_rem = false;
431
}
432
433
if ($new) {
434
$is_add = ($new->getDifferenceType() === '+');
435
} else {
436
$is_add = false;
437
}
438
439
if ($is_rem && $is_add) {
440
$block_diff = $engine->newBlockDiffViews(
441
$old_ref,
442
$old,
443
$new_ref,
444
$new);
445
446
$old_content = $block_diff->getOldContent();
447
$new_content = $block_diff->getNewContent();
448
449
$old_classes = $block_diff->getOldClasses();
450
$new_classes = $block_diff->getNewClasses();
451
} else {
452
$old_classes = array();
453
$new_classes = array();
454
455
if ($old) {
456
$old_content = $engine->newBlockContentView(
457
$old_ref,
458
$old);
459
460
if ($is_rem) {
461
$old_classes[] = 'old';
462
$old_classes[] = 'old-full';
463
}
464
} else {
465
$old_content = null;
466
}
467
468
if ($new) {
469
$new_content = $engine->newBlockContentView(
470
$new_ref,
471
$new);
472
473
if ($is_add) {
474
$new_classes[] = 'new';
475
$new_classes[] = 'new-full';
476
}
477
} else {
478
$new_content = null;
479
}
480
}
481
482
$old_classes[] = 'diff-flush';
483
$old_classes = implode(' ', $old_classes);
484
485
$new_classes[] = 'diff-flush';
486
$new_classes = implode(' ', $new_classes);
487
488
$old_inline_rows = array();
489
if ($old_key !== null) {
490
$old_inlines = idx($old_comments, $old_key, array());
491
foreach ($old_inlines as $inline) {
492
$inline = $this->buildInlineComment(
493
$inline,
494
$on_right = false);
495
$old_inline_rows[] = $this->getRowScaffoldForInline($inline);
496
}
497
}
498
499
$new_inline_rows = array();
500
if ($new_key !== null) {
501
$new_inlines = idx($new_comments, $new_key, array());
502
foreach ($new_inlines as $inline) {
503
$inline = $this->buildInlineComment(
504
$inline,
505
$on_right = true);
506
$new_inline_rows[] = $this->getRowScaffoldForInline($inline);
507
}
508
}
509
510
if ($old_content === null) {
511
$old_id = null;
512
} else {
513
$old_id = "C{$old_changeset_key}OL{$old_key}";
514
}
515
516
$old_line_cell = phutil_tag(
517
'td',
518
array(
519
'id' => $old_id,
520
'data-n' => $old_key,
521
'class' => 'n',
522
));
523
524
$old_content_cell = phutil_tag(
525
'td',
526
array(
527
'class' => $old_classes,
528
'data-copy-mode' => 'copy-l',
529
),
530
$old_content);
531
532
if ($new_content === null) {
533
$new_id = null;
534
} else {
535
$new_id = "C{$new_changeset_key}NL{$new_key}";
536
}
537
538
$new_line_cell = phutil_tag(
539
'td',
540
array(
541
'id' => $new_id,
542
'data-n' => $new_key,
543
'class' => 'n',
544
));
545
546
$copy_gutter = phutil_tag(
547
'td',
548
array(
549
'class' => 'copy',
550
));
551
552
$new_content_cell = phutil_tag(
553
'td',
554
array(
555
'class' => $new_classes,
556
'colspan' => '2',
557
'data-copy-mode' => 'copy-r',
558
),
559
$new_content);
560
561
$row_view = phutil_tag(
562
'tr',
563
array(),
564
array(
565
$old_line_cell,
566
$old_content_cell,
567
$new_line_cell,
568
$copy_gutter,
569
$new_content_cell,
570
));
571
572
$rows[] = array(
573
$row_view,
574
$old_inline_rows,
575
$new_inline_rows,
576
);
577
}
578
579
if ($in_gap) {
580
$rows[] = $this->renderDocumentEngineGap(
581
$gap,
582
$available_count);
583
}
584
585
$output = $this->wrapChangeInTable($rows);
586
587
return $this->renderChangesetTable($output);
588
}
589
590
public function getRowScaffoldForInline(PHUIDiffInlineCommentView $view) {
591
return id(new PHUIDiffTwoUpInlineCommentRowScaffold())
592
->addInlineView($view);
593
}
594
595
private function getNewLineToOffsetMap() {
596
if ($this->newOffsetMap === null) {
597
$new = $this->getNewLines();
598
599
$map = array();
600
foreach ($new as $offset => $new_line) {
601
if ($new_line === null) {
602
continue;
603
}
604
605
if ($new_line['line'] === null) {
606
continue;
607
}
608
609
$map[$new_line['line']] = $offset;
610
}
611
612
$this->newOffsetMap = $map;
613
}
614
615
return $this->newOffsetMap;
616
}
617
618
protected function getTableSigils() {
619
return array(
620
'intercept-copy',
621
);
622
}
623
624
private function renderDocumentEngineGap(array $gap, $available_count) {
625
$content = $this->renderShowContextLinks(
626
head_key($gap),
627
count($gap),
628
$available_count,
629
$is_blocks = true);
630
631
return javelin_tag(
632
'tr',
633
array(
634
'sigil' => 'context-target',
635
),
636
phutil_tag(
637
'td',
638
array(
639
'colspan' => 6,
640
'class' => 'show-more',
641
),
642
$content));
643
}
644
645
}
646
647