Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/infrastructure/diff/PhabricatorDiffScopeEngine.php
12241 views
1
<?php
2
3
final class PhabricatorDiffScopeEngine
4
extends Phobject {
5
6
private $lineTextMap;
7
private $lineDepthMap;
8
9
public function setLineTextMap(array $map) {
10
if (array_key_exists(0, $map)) {
11
throw new Exception(
12
pht('ScopeEngine text map must be a 1-based map of lines.'));
13
}
14
15
$expect = 1;
16
foreach ($map as $key => $value) {
17
if ($key === $expect) {
18
$expect++;
19
continue;
20
}
21
22
throw new Exception(
23
pht(
24
'ScopeEngine text map must be a contiguous map of '.
25
'lines, but is not: found key "%s" where key "%s" was expected.',
26
$key,
27
$expect));
28
}
29
30
$this->lineTextMap = $map;
31
32
return $this;
33
}
34
35
public function getLineTextMap() {
36
if ($this->lineTextMap === null) {
37
throw new PhutilInvalidStateException('setLineTextMap');
38
}
39
return $this->lineTextMap;
40
}
41
42
public function getScopeStart($line) {
43
$text_map = $this->getLineTextMap();
44
$depth_map = $this->getLineDepthMap();
45
$length = count($text_map);
46
47
// Figure out the effective depth of the line we're getting scope for.
48
// If the line is just whitespace, it may have no depth on its own. In
49
// this case, we look for the next line.
50
$line_depth = null;
51
for ($ii = $line; $ii <= $length; $ii++) {
52
if ($depth_map[$ii] !== null) {
53
$line_depth = $depth_map[$ii];
54
break;
55
}
56
}
57
58
// If we can't find a line depth for the target line, just bail.
59
if ($line_depth === null) {
60
return null;
61
}
62
63
// Limit the maximum number of lines we'll examine. If a user has a
64
// million-line diff of nonsense, scanning the whole thing is a waste
65
// of time.
66
$search_range = 1000;
67
$search_until = max(0, $ii - $search_range);
68
69
for ($ii = $line - 1; $ii > $search_until; $ii--) {
70
$line_text = $text_map[$ii];
71
72
// This line is in missing context: the diff was diffed with partial
73
// context, and we ran out of context before finding a good scope line.
74
// Bail out, we don't want to jump across missing context blocks.
75
if ($line_text === null) {
76
return null;
77
}
78
79
$depth = $depth_map[$ii];
80
81
// This line is all whitespace. This isn't a possible match.
82
if ($depth === null) {
83
continue;
84
}
85
86
// Don't match context lines which are too deeply indented, since they
87
// are very unlikely to be symbol definitions.
88
if ($depth > 2) {
89
continue;
90
}
91
92
// The depth is the same as (or greater than) the depth we started with,
93
// so this isn't a possible match.
94
if ($depth >= $line_depth) {
95
continue;
96
}
97
98
// Reject lines which begin with "}" or "{". These lines are probably
99
// never good matches.
100
if (preg_match('/^\s*[{}]/i', $line_text)) {
101
continue;
102
}
103
104
return $ii;
105
}
106
107
return null;
108
}
109
110
private function getLineDepthMap() {
111
if (!$this->lineDepthMap) {
112
$this->lineDepthMap = $this->newLineDepthMap();
113
}
114
115
return $this->lineDepthMap;
116
}
117
118
private function getTabWidth() {
119
// TODO: This should be configurable once we handle tab widths better.
120
return 2;
121
}
122
123
private function newLineDepthMap() {
124
$text_map = $this->getLineTextMap();
125
$tab_width = $this->getTabWidth();
126
127
$depth_map = array();
128
foreach ($text_map as $line_number => $line_text) {
129
if ($line_text === null) {
130
$depth_map[$line_number] = null;
131
continue;
132
}
133
134
$len = strlen($line_text);
135
136
// If the line has no actual text, don't assign it a depth.
137
if (!$len || !strlen(trim($line_text))) {
138
$depth_map[$line_number] = null;
139
continue;
140
}
141
142
$count = 0;
143
for ($ii = 0; $ii < $len; $ii++) {
144
$c = $line_text[$ii];
145
if ($c == ' ') {
146
$count++;
147
} else if ($c == "\t") {
148
$count += $tab_width;
149
} else {
150
break;
151
}
152
}
153
154
// Round down to cheat our way through the " *" parts of docblock
155
// comments.
156
$depth = (int)floor($count / $tab_width);
157
158
$depth_map[$line_number] = $depth;
159
}
160
161
return $depth_map;
162
}
163
164
}
165
166