Skip to content

Commit d20d6e7

Browse files
author
epriestley
committed
Fix an issue with highlighting PHP snippets with leading whitespace using XHPAST
Summary: Ref T10576. In the test case there, XHPAST has incorrectly emitted an extra newline at the top of the output. This cascades downward and throws every line off by one, highlighting the wrong lines in the output. Fix this issue by stripping the prefix more carefully, so the input source is preserved accurately. Test Plan: - Added tests; ran tests. - Verified that the snippet now highlights correctly. {F1173155} Reviewers: chad Reviewed By: chad Maniphest Tasks: T10576 Differential Revision: https://secure.phabricator.com/D15469
1 parent f5f44f3 commit d20d6e7

13 files changed

+87
-6
lines changed

.arclint

+13-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,19 @@
4242
"type": "spelling"
4343
},
4444
"text": {
45-
"type": "text"
45+
"type": "text",
46+
"exclude": [
47+
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))"
48+
]
49+
},
50+
"text-without-length": {
51+
"type": "text",
52+
"include": [
53+
"(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))"
54+
],
55+
"severity": {
56+
"3": "disabled"
57+
}
4658
},
4759
"xhpast": {
4860
"type": "xhpast",

src/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ final class PhutilXHPASTSyntaxHighlighter extends Phobject {
55
public function getHighlightFuture($source) {
66
$scrub = false;
77
if (strpos($source, '<?') === false) {
8-
$source = "<?php\n".$source."\n";
8+
$source = "<?php\n".$source;
99
$scrub = true;
1010
}
1111

src/markup/syntax/highlighter/__tests__/PhutilXHPASTSyntaxHighlighterTestCase.php

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ public function testBuiltinClassnames() {
2626
$this->read('multiline-token.expect'),
2727
(string)$this->highlight($this->read('multiline-token.source')),
2828
pht('Multi-line tokens should be split across lines.'));
29+
$this->assertEqual(
30+
$this->read('leading-whitespace.expect'),
31+
(string)$this->highlight($this->read('leading-whitespace.source')),
32+
pht('Snippets with leading whitespace should be preserved.'));
33+
$this->assertEqual(
34+
$this->read('no-leading-whitespace.expect'),
35+
(string)$this->highlight($this->read('no-leading-whitespace.source')),
36+
pht('Snippets with no leading whitespace should be preserved.'));
2937
}
3038

3139
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span class="k">foreach</span> <span class="o">(</span><span class="nv">$x</span> <span class="k">as</span> <span class="nv">$y</span><span class="o">)</span> <span class="o">{</span>
2+
<span class="nf" data-symbol-name="z">z</span><span class="o">();</span>
3+
<span class="o">}</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foreach ($x as $y) {
2+
z();
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span class="k">foreach</span> <span class="o">(</span><span class="nv">$x</span> <span class="k">as</span> <span class="nv">$y</span><span class="o">)</span> <span class="o">{</span>
2+
<span class="nf" data-symbol-name="z">z</span><span class="o">();</span>
3+
<span class="o">}</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foreach ($x as $y) {
2+
z();
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span class="k">foreach</span> <span class="k">(</span><span class="nv">$x</span> <span class="k">as</span> <span class="nv">$y</span><span class="k">)</span> <span class="k">{</span>
2+
<span data-symbol-name="z" class="nf">z</span><span class="k">(</span><span class="k">)</span><span class="k">;</span>
3+
<span class="k">}</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foreach ($x as $y) {
2+
z();
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<span class="k">foreach</span> <span class="k">(</span><span class="nv">$x</span> <span class="k">as</span> <span class="nv">$y</span><span class="k">)</span> <span class="k">{</span>
2+
<span data-symbol-name="z" class="nf">z</span><span class="k">(</span><span class="k">)</span><span class="k">;</span>
3+
<span class="k">}</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foreach ($x as $y) {
2+
z();
3+
}

src/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php

+36-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,42 @@ private function applyXHPHighlight($result) {
4545
$tokens = $root->getTokens();
4646
$interesting_symbols = $this->findInterestingSymbols($root);
4747

48+
49+
if ($this->scrub) {
50+
// If we're scrubbing, we prepended "<?php\n" to the text to force the
51+
// highlighter to treat it as PHP source. Now, we need to remove that.
52+
53+
$ok = false;
54+
if (count($tokens) >= 2) {
55+
if ($tokens[0]->getTypeName() === 'T_OPEN_TAG') {
56+
if ($tokens[1]->getTypeName() === 'T_WHITESPACE') {
57+
$ok = true;
58+
}
59+
}
60+
}
61+
62+
if (!$ok) {
63+
throw new Exception(
64+
pht(
65+
'Expected T_OPEN_TAG, T_WHITESPACE tokens at head of results '.
66+
'for highlighting parse of PHP snippet.'));
67+
}
68+
69+
// Remove the "<?php".
70+
unset($tokens[0]);
71+
72+
$value = $tokens[1]->getValue();
73+
if ((strlen($value) < 1) || ($value[0] != "\n")) {
74+
throw new Exception(
75+
pht(
76+
'Expected "\\n" at beginning of T_WHITESPACE token at head of '.
77+
'tokens for highlighting parse of PHP snippet.'));
78+
}
79+
80+
$value = substr($value, 1);
81+
$tokens[1]->overwriteValue($value);
82+
}
83+
4884
$out = array();
4985
foreach ($tokens as $key => $token) {
5086
$value = $token->getValue();
@@ -122,10 +158,6 @@ private function applyXHPHighlight($result) {
122158
}
123159
}
124160

125-
if ($this->scrub) {
126-
array_shift($out);
127-
}
128-
129161
return phutil_implode_html('', $out);
130162
}
131163

src/parser/aast/api/AASTToken.php

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ final public function getValue() {
3636
return $this->value;
3737
}
3838

39+
final public function overwriteValue($value) {
40+
$this->value = $value;
41+
return $this;
42+
}
43+
3944
final public function getOffset() {
4045
return $this->offset;
4146
}

0 commit comments

Comments
 (0)