1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2025-01-11 07:11:03 +01:00

Introduce arc lint --only-new 1

Test Plan:
  $ arc lint src/workflow/ArcanistLintWorkflow.php
  $ arc lint --only-new 1 src/workflow/ArcanistLintWorkflow.php

Reviewers: epriestley

Reviewed By: epriestley

CC: aran, Korvin

Maniphest Tasks: T2038

Differential Revision: https://secure.phabricator.com/D3934
This commit is contained in:
vrana 2012-12-03 16:52:58 -08:00
parent 886c1721dd
commit 5025c06f3d
5 changed files with 149 additions and 2 deletions

View file

@ -82,7 +82,7 @@ final class ArcanistLintResult {
return true;
}
private function sortAndFilterMessages() {
public function sortAndFilterMessages() {
$messages = $this->messages;
foreach ($messages as $key => $message) {

View file

@ -190,6 +190,42 @@ final class ArcanistDiffChange {
return $this->hunks;
}
/**
* @return array $old => array($new, )
*/
public function buildLineMap() {
$line_map = array();
$old = 1;
$new = 1;
foreach ($this->getHunks() as $hunk) {
for ($n = $old; $n < $hunk->getOldOffset(); $n++) {
$line_map[$n] = array($n + $new - $old);
}
$old = $hunk->getOldOffset();
$new = $hunk->getNewOffset();
$olds = array();
$news = array();
$lines = explode("\n", $hunk->getCorpus());
foreach ($lines as $line) {
$type = substr($line, 0, 1);
if ($type == '-' || $type == ' ') {
$olds[] = $old;
$old++;
}
if ($type == '+' || $type == ' ') {
$news[] = $new;
$new++;
}
if ($type == ' ' || $type == '') {
$line_map += array_fill_keys($olds, $news);
$olds = array();
$news = array();
}
}
}
return $line_map;
}
public function convertToBinaryChange() {
$this->hunks = array();
$this->setFileType(ArcanistDiffChangeType::FILE_BINARY);

View file

@ -985,7 +985,7 @@ abstract class ArcanistBaseWorkflow {
return array_keys($lines);
}
private function getChange($path) {
protected function getChange($path) {
$repository_api = $this->getRepositoryAPI();
if ($repository_api instanceof ArcanistSubversionAPI) {

View file

@ -240,6 +240,14 @@ EOTEXT
"Require excuse for lint advice in addition to lint warnings and ".
"errors.",
),
'only-new' => array(
'param' => 'bool',
'help' =>
'Display only lint messages not present in the original code.',
'passthru' => array(
'lint' => true,
),
),
'apply-patches' => array(
'help' =>
'Apply patches suggested by lint to the working copy without '.

View file

@ -83,6 +83,11 @@ EOTEXT
"With 'json', show lint warnings in machine-readable JSON format. ".
"With 'compiler', show lint warnings in suitable for your editor."
),
'only-new' => array(
'param' => 'bool',
'supports' => array('git', 'hg'), // TODO: svn
'help' => 'Display only messages not present in the original code.',
),
'engine' => array(
'param' => 'classname',
'help' =>
@ -129,6 +134,10 @@ EOTEXT
);
}
public function requiresAuthentication() {
return (bool)$this->getArgument('only-new');
}
public function requiresWorkingCopy() {
return true;
}
@ -228,6 +237,41 @@ EOTEXT
$engine->setEnableAsyncLint(false);
}
if ($this->getArgument('only-new')) {
$conduit = $this->getConduit();
$api = $this->getRepositoryAPI();
$relative_commit = ($rev ? $rev : $api->resolveBaseCommit());
$api->setRelativeCommit($relative_commit);
$svn_root = id(new PhutilURI($api->getSourceControlPath()))->getPath();
$all_paths = array();
foreach ($paths as $path) {
$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
$full_paths = array($path);
$change = $this->getChange($path);
$type = $change->getType();
if (ArcanistDiffChangeType::isOldLocationChangeType($type)) {
$full_paths = $change->getAwayPaths();
} else if (ArcanistDiffChangeType::isNewLocationChangeType($type)) {
continue;
} else if (ArcanistDiffChangeType::isDeleteChangeType($type)) {
continue;
}
foreach ($full_paths as $full_path) {
$all_paths[$svn_root.'/'.$full_path] = $path;
}
}
$lint_future = $conduit->callMethod('diffusion.getlintmessages', array(
'arcanistProject' => $this->getWorkingCopy()->getProjectID(),
'branch' => '', // TODO: Tracking branch.
'commit' => $api->getRelativeCommit(),
'files' => array_keys($all_paths),
));
}
$failed = null;
try {
$engine->run();
@ -237,6 +281,65 @@ EOTEXT
$results = $engine->getResults();
if ($this->getArgument('only-new')) {
$total = 0;
foreach ($results as $result) {
$total += count($result->getMessages());
}
// Don't wait for response with default value of --only-new.
$timeout = null;
if ($this->getArgument('only-new') === null || !$total) {
$timeout = 0;
}
$raw_messages = $lint_future->resolve($timeout);
if ($raw_messages && $total) {
$old_messages = array();
$line_maps = array();
foreach ($raw_messages as $message) {
$path = $all_paths[$message['path']];
$line = $message['line'];
$code = $message['code'];
if (!isset($line_maps[$path])) {
$line_maps[$path] = $this->getChange($path)->buildLineMap();
}
$new_lines = idx($line_maps[$path], $line);
if (!$new_lines) { // Unmodified lines after last hunk.
$last_old = ($line_maps[$path] ? last_key($line_maps[$path]) : 0);
$news = array_filter($line_maps[$path]);
$last_new = ($news ? last(end($news)) : 0);
$new_lines = array($line + $last_new - $last_old);
}
$error = array($code => array(true));
foreach ($new_lines as $new) {
if (isset($old_messages[$path][$new])) {
$old_messages[$path][$new][$code][] = true;
break;
}
$old_messages[$path][$new] = &$error;
}
unset($error);
}
foreach ($results as $result) {
foreach ($result->getMessages() as $message) {
$path = str_replace(DIRECTORY_SEPARATOR, '/', $message->getPath());
$line = $message->getLine();
$code = $message->getCode();
if (!empty($old_messages[$path][$line][$code])) {
$message->setObsolete(true);
array_pop($old_messages[$path][$line][$code]);
}
}
$result->sortAndFilterMessages();
}
}
}
// It'd be nice to just return a single result from the run method above
// which contains both the lint messages and the postponed linters.
// However, to maintain compatibility with existing lint subclasses, use