Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2890106
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
14 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/lint/renderer/ArcanistConsoleLintRenderer.php b/src/lint/renderer/ArcanistConsoleLintRenderer.php
index 4acbb644..8880fbe8 100644
--- a/src/lint/renderer/ArcanistConsoleLintRenderer.php
+++ b/src/lint/renderer/ArcanistConsoleLintRenderer.php
@@ -1,266 +1,314 @@
<?php
/**
* Shows lint messages to the user.
*/
final class ArcanistConsoleLintRenderer extends ArcanistLintRenderer {
private $showAutofixPatches = false;
+ private $testableMode;
public function setShowAutofixPatches($show_autofix_patches) {
$this->showAutofixPatches = $show_autofix_patches;
return $this;
}
+ public function setTestableMode($testable_mode) {
+ $this->testableMode = $testable_mode;
+ return $this;
+ }
+
+ public function getTestableMode() {
+ return $this->testableMode;
+ }
+
public function renderLintResult(ArcanistLintResult $result) {
$messages = $result->getMessages();
$path = $result->getPath();
$data = $result->getData();
$line_map = $this->newOffsetMap($data);
$text = array();
foreach ($messages as $message) {
if (!$this->showAutofixPatches && $message->isAutofix()) {
continue;
}
if ($message->isError()) {
$color = 'red';
} else {
$color = 'yellow';
}
$severity = ArcanistLintSeverity::getStringForSeverity(
$message->getSeverity());
$code = $message->getCode();
$name = $message->getName();
$description = $message->getDescription();
if ($message->getOtherLocations()) {
$locations = array();
foreach ($message->getOtherLocations() as $location) {
$locations[] =
idx($location, 'path', $path).
(!empty($location['line']) ? ":{$location['line']}" : '');
}
$description .= "\n".pht(
'Other locations: %s',
implode(', ', $locations));
}
$text[] = phutil_console_format(
" **<bg:{$color}> %s </bg>** (%s) __%s__\n%s\n",
$severity,
$code,
$name,
phutil_console_wrap($description, 4));
if ($message->hasFileContext()) {
$text[] = $this->renderContext($message, $data, $line_map);
}
}
if ($text) {
$prefix = phutil_console_format(
"**>>>** %s\n\n\n",
pht(
'Lint for %s:',
phutil_console_format('__%s__', $path)));
return $prefix.implode("\n", $text);
} else {
return null;
}
}
protected function renderContext(
ArcanistLintMessage $message,
$data,
array $line_map) {
$context = 3;
$message = $message->newTrimmedMessage();
$original = $message->getOriginalText();
$replacement = $message->getReplacementText();
$line = $message->getLine();
$char = $message->getChar();
$old = $data;
$old_lines = phutil_split_lines($old);
+ $old_impact = substr_count($original, "\n") + 1;
$start = $line;
if ($message->isPatchable()) {
$patch_offset = $line_map[$line] + ($char - 1);
$new = substr_replace(
$old,
$replacement,
$patch_offset,
strlen($original));
$new_lines = phutil_split_lines($new);
// Figure out how many "-" and "+" lines we have by counting the newlines
// for the relevant patches. This may overestimate things if we are adding
// or removing entire lines, but we'll adjust things below.
- $old_impact = substr_count($original, "\n") + 1;
$new_impact = substr_count($replacement, "\n") + 1;
+
+ // If this is a change on a single line, we'll try to highlight the
+ // changed character range to make it easier to pick out.
+ if ($old_impact === 1 && $new_impact === 1) {
+ $old_lines[$start - 1] = substr_replace(
+ $old_lines[$start - 1],
+ $this->highlightText($original),
+ $char - 1,
+ strlen($original));
+
+ $new_lines[$start - 1] = substr_replace(
+ $new_lines[$start - 1],
+ $this->highlightText($replacement),
+ $char - 1,
+ strlen($replacement));
+ }
+
+
// If lines at the beginning of the changed line range are actually the
// same, shrink the range. This happens when a patch just adds a line.
do {
if ($old_lines[$start - 1] != $new_lines[$start - 1]) {
break;
}
$start++;
$old_impact--;
$new_impact--;
if ($old_impact < 0 || $new_impact < 0) {
throw new Exception(
pht(
'Modified prefix line range has become negative '.
'(old = %d, new = %d).',
$old_impact,
$new_impact));
}
} while (true);
// If the lines at the end of the changed line range are actually the
// same, shrink the range. This happens when a patch just removes a
// line.
do {
$old_suffix = $old_lines[$start + $old_impact - 2];
$new_suffix = $new_lines[$start + $new_impact - 2];
if ($old_suffix != $new_suffix) {
break;
}
$old_impact--;
$new_impact--;
if ($old_impact < 0 || $new_impact < 0) {
throw new Exception(
pht(
'Modified suffix line range has become negative '.
'(old = %d, new = %d).',
$old_impact,
$new_impact));
}
} while (true);
} else {
+
+ // If we have "original" text and it is contained on a single line,
+ // highlight the affected area. If we don't have any text, we'll mark
+ // the character with a caret (below, in rendering) instead.
+ if ($old_impact == 1 && strlen($original)) {
+ $old_lines[$start - 1] = substr_replace(
+ $old_lines[$start - 1],
+ $this->highlightText($original),
+ $char - 1,
+ strlen($original));
+ }
+
$old_impact = 0;
$new_impact = 0;
}
$out = array();
$head = max(1, $start - $context);
for ($ii = $head; $ii < $start; $ii++) {
$out[] = array(
'text' => $old_lines[$ii - 1],
'number' => $ii,
);
}
for ($ii = $start; $ii < $start + $old_impact; $ii++) {
$out[] = array(
'text' => $old_lines[$ii - 1],
'number' => $ii,
'type' => '-',
'chevron' => ($ii == $start),
);
}
for ($ii = $start; $ii < $start + $new_impact; $ii++) {
$out[] = array(
'text' => $new_lines[$ii - 1],
'type' => '+',
'chevron' => ($ii == $start),
);
}
$cursor = $start + $old_impact;
$foot = min(count($old_lines), $cursor + $context);
for ($ii = $cursor; $ii <= $foot; $ii++) {
$out[] = array(
'text' => $old_lines[$ii - 1],
'number' => $ii,
'chevron' => ($ii == $cursor),
);
}
$result = array();
$seen_chevron = false;
foreach ($out as $spec) {
if ($seen_chevron) {
$chevron = false;
} else {
$chevron = !empty($spec['chevron']);
if ($chevron) {
$seen_chevron = true;
}
}
$result[] = $this->renderLine(
idx($spec, 'number'),
$spec['text'],
$chevron,
idx($spec, 'type'));
// If this is just a message and does not have a patch, put a little
// caret underneath the line to point out where the issue is.
if ($chevron) {
if (!$message->isPatchable() && !strlen($original)) {
$result[] = $this->renderCaret($char)."\n";
}
}
}
return implode('', $result);
}
private function renderCaret($pos) {
return str_repeat(' ', 16 + $pos).'^';
}
protected function renderLine($line, $data, $chevron = false, $diff = null) {
$chevron = $chevron ? '>>>' : '';
return sprintf(
' %3s %1s %6s %s',
$chevron,
$diff,
$line,
$data);
}
public function renderOkayResult() {
return phutil_console_format(
"<bg:green>** %s **</bg> %s\n",
pht('OKAY'),
pht('No lint warnings.'));
}
private function newOffsetMap($data) {
$lines = phutil_split_lines($data);
$line_map = array();
$number = 1;
$offset = 0;
foreach ($lines as $line) {
$line_map[$number] = $offset;
$number++;
$offset += strlen($line);
}
return $line_map;
}
+ private function highlightText($text) {
+ if ($this->getTestableMode()) {
+ return '>'.$text.'<';
+ } else {
+ return (string)tsprintf('##%s##', $text);
+ }
+ }
+
}
diff --git a/src/lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php b/src/lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php
index 693b1788..68a35420 100644
--- a/src/lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php
+++ b/src/lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php
@@ -1,138 +1,145 @@
<?php
final class ArcanistConsoleLintRendererTestCase
extends PhutilTestCase {
public function testRendering() {
$map = array(
'simple' => array(
'line' => 1,
'char' => 1,
'original' => 'a',
'replacement' => 'z',
),
'inline' => array(
'line' => 1,
'char' => 7,
'original' => 'cat',
'replacement' => 'dog',
),
// In this test, the original and replacement texts have a large
// amount of overlap.
'overlap' => array(
'line' => 1,
'char' => 1,
'original' => 'tantawount',
'replacement' => 'tantamount',
),
'newline' => array(
'line' => 6,
'char' => 1,
'original' => "\n",
'replacement' => '',
),
'addline' => array(
'line' => 3,
'char' => 1,
'original' => '',
'replacement' => "cherry\n",
),
'addlinesuffix' => array(
'line' => 2,
'char' => 7,
'original' => '',
'replacement' => "\ncherry",
),
'xml' => array(
'line' => 3,
'char' => 6,
'original' => '',
'replacement' => "\n",
),
'caret' => array(
'line' => 2,
'char' => 13,
'name' => 'Fruit Misinformation',
'description' => 'Arguably untrue.',
),
+
+ 'original' => array(
+ 'line' => 1,
+ 'char' => 4,
+ 'original' => 'should of',
+ ),
);
$defaults = array(
'severity' => ArcanistLintSeverity::SEVERITY_WARNING,
'name' => 'Lint Warning',
'path' => 'path/to/example.c',
'description' => 'Consider this.',
'code' => 'WARN123',
);
foreach ($map as $key => $test_case) {
$data = $this->readTestData("{$key}.txt");
$expect = $this->readTestData("{$key}.expect");
$test_case = $test_case + $defaults;
$path = $test_case['path'];
$severity = $test_case['severity'];
$name = $test_case['name'];
$description = $test_case['description'];
$code = $test_case['code'];
$line = $test_case['line'];
$char = $test_case['char'];
$original = idx($test_case, 'original');
$replacement = idx($test_case, 'replacement');
$message = id(new ArcanistLintMessage())
->setPath($path)
->setSeverity($severity)
->setName($name)
->setDescription($description)
->setCode($code)
->setLine($line)
->setChar($char)
->setOriginalText($original)
->setReplacementText($replacement);
$result = id(new ArcanistLintResult())
->setPath($path)
->setData($data)
->addMessage($message);
- $renderer = new ArcanistConsoleLintRenderer();
+ $renderer = id(new ArcanistConsoleLintRenderer())
+ ->setTestableMode(true);
try {
PhutilConsoleFormatter::disableANSI(true);
$actual = $renderer->renderLintResult($result);
PhutilConsoleFormatter::disableANSI(false);
} catch (Exception $ex) {
PhutilConsoleFormatter::disableANSI(false);
throw $ex;
}
// Trim "~" off the ends of lines. This allows the "expect" file to test
// for trailing whitespace without actually containing trailing
// whitespace.
$expect = preg_replace('/~+$/m', '', $expect);
$this->assertEqual(
$expect,
$actual,
pht(
'Lint rendering for "%s".',
$key));
}
}
private function readTestData($filename) {
$path = dirname(__FILE__).'/data/'.$filename;
return Filesystem::readFile($path);
}
}
diff --git a/src/lint/renderer/__tests__/data/inline.expect b/src/lint/renderer/__tests__/data/inline.expect
index eba4522b..d1d63fda 100644
--- a/src/lint/renderer/__tests__/data/inline.expect
+++ b/src/lint/renderer/__tests__/data/inline.expect
@@ -1,8 +1,8 @@
>>> Lint for path/to/example.c:
Warning (WARN123) Lint Warning
Consider this.
- >>> - 1 adjudicated
- + adjudidoged
+ >>> - 1 adjudi>cat<ed
+ + adjudi>dog<ed
diff --git a/src/lint/renderer/__tests__/data/overlap.expect b/src/lint/renderer/__tests__/data/original.expect
similarity index 61%
copy from src/lint/renderer/__tests__/data/overlap.expect
copy to src/lint/renderer/__tests__/data/original.expect
index 19de1c46..f3bec302 100644
--- a/src/lint/renderer/__tests__/data/overlap.expect
+++ b/src/lint/renderer/__tests__/data/original.expect
@@ -1,8 +1,7 @@
>>> Lint for path/to/example.c:
Warning (WARN123) Lint Warning
Consider this.
- >>> - 1 tantawount
- + tantamount
+ >>> 1 He >should of< known.
diff --git a/src/lint/renderer/__tests__/data/original.txt b/src/lint/renderer/__tests__/data/original.txt
new file mode 100644
index 00000000..090fe16d
--- /dev/null
+++ b/src/lint/renderer/__tests__/data/original.txt
@@ -0,0 +1 @@
+He should of known.
diff --git a/src/lint/renderer/__tests__/data/overlap.expect b/src/lint/renderer/__tests__/data/overlap.expect
index 19de1c46..d7756f3d 100644
--- a/src/lint/renderer/__tests__/data/overlap.expect
+++ b/src/lint/renderer/__tests__/data/overlap.expect
@@ -1,8 +1,8 @@
>>> Lint for path/to/example.c:
Warning (WARN123) Lint Warning
Consider this.
- >>> - 1 tantawount
- + tantamount
+ >>> - 1 tanta>w<ount
+ + tanta>m<ount
diff --git a/src/lint/renderer/__tests__/data/simple.expect b/src/lint/renderer/__tests__/data/simple.expect
index 43b9910f..c363ba07 100644
--- a/src/lint/renderer/__tests__/data/simple.expect
+++ b/src/lint/renderer/__tests__/data/simple.expect
@@ -1,10 +1,10 @@
>>> Lint for path/to/example.c:
Warning (WARN123) Lint Warning
Consider this.
- >>> - 1 a
- + z
+ >>> - 1 >a<
+ + >z<
2 b
3 c
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 13:04 (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124884
Default Alt Text
(14 KB)
Attached To
Mode
rARC Arcanist
Attached
Detach File
Event Timeline
Log In to Comment