Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F9583963
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/lint/renderer/ArcanistLintJSONRenderer.php b/src/lint/renderer/ArcanistLintJSONRenderer.php
new file mode 100644
index 00000000..66073291
--- /dev/null
+++ b/src/lint/renderer/ArcanistLintJSONRenderer.php
@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shows lint messages to the user.
+ *
+ * @group lint
+ */
+class ArcanistLintJSONRenderer {
+ const LINES_OF_CONTEXT = 3;
+
+ public function renderLintResult(ArcanistLintResult $result) {
+ $messages = $result->getMessages();
+ $path = $result->getPath();
+ $data = explode("\n", $result->getData());
+ array_unshift($data, ''); // make the line numbers work as array indices
+
+ $output = array($path => array());
+
+ foreach ($messages as $message) {
+ $output[$path][] = array(
+ 'code' => $message->getCode(),
+ 'name' => $message->getName(),
+ 'severity' => $message->getSeverity(),
+ 'line' => $message->getLine(),
+ 'char' => $message->getChar(),
+ 'context' => implode("\n", array_slice(
+ $data,
+ $message->getLine() - self::LINES_OF_CONTEXT,
+ self::LINES_OF_CONTEXT * 2 + 1
+ )),
+ );
+ }
+
+ return json_encode($output)."\n";
+ }
+}
diff --git a/src/lint/renderer/ArcanistLintRenderer.php b/src/lint/renderer/ArcanistLintRenderer.php
index 0fca7044..dea93260 100644
--- a/src/lint/renderer/ArcanistLintRenderer.php
+++ b/src/lint/renderer/ArcanistLintRenderer.php
@@ -1,195 +1,159 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Shows lint messages to the user.
*
* @group lint
*/
class ArcanistLintRenderer {
-
- private $summaryMode;
-
- public function setSummaryMode($mode) {
- $this->summaryMode = $mode;
- }
-
public function renderLintResult(ArcanistLintResult $result) {
- if ($this->summaryMode) {
- return $this->renderResultSummary($result);
- } else {
- return $this->renderResultFull($result);
- }
- }
-
- protected function renderResultFull(ArcanistLintResult $result) {
$messages = $result->getMessages();
$path = $result->getPath();
$lines = explode("\n", $result->getData());
$text = array();
$text[] = phutil_console_format('**>>>** Lint for __%s__:', $path);
$text[] = null;
foreach ($messages as $message) {
if ($message->isError()) {
$color = 'red';
} else {
$color = 'yellow';
}
$severity = ArcanistLintSeverity::getStringForSeverity(
$message->getSeverity());
$code = $message->getCode();
$name = $message->getName();
$description = phutil_console_wrap($message->getDescription(), 4);
$text[] = phutil_console_format(
" **<bg:{$color}> %s </bg>** (%s) __%s__\n".
" %s\n",
$severity,
$code,
$name,
$description);
if ($message->hasFileContext()) {
$text[] = $this->renderContext($message, $lines);
}
}
$text[] = null;
$text[] = null;
return implode("\n", $text);
}
- protected function renderResultSummary(ArcanistLintResult $result) {
- $messages = $result->getMessages();
- $path = $result->getPath();
-
- $text = array();
- $text[] = $path.":";
- foreach ($messages as $message) {
- $name = $message->getName();
- $severity = ArcanistLintSeverity::getStringForSeverity(
- $message->getSeverity());
- $line = $message->getLine();
-
- $text[] = " {$severity} on line {$line}: {$name}";
- }
- $text[] = null;
-
- return implode("\n", $text);
- }
-
-
protected function renderContext(
ArcanistLintMessage $message,
array $line_data) {
$lines_of_context = 3;
$out = array();
$line_num = min($message->getLine(), count($line_data));
$line_num = max(1, $line_num);
// Print out preceding context before the impacted region.
$cursor = max(1, $line_num - $lines_of_context);
for (; $cursor < $line_num; $cursor++) {
$out[] = $this->renderLine($cursor, $line_data[$cursor - 1]);
}
// Print out the impacted region itself.
$diff = $message->isPatchable() ? '-' : null;
$text = $message->getOriginalText();
$text_lines = explode("\n", $text);
$text_length = count($text_lines);
for (; $cursor < $line_num + $text_length; $cursor++) {
$chevron = ($cursor == $line_num);
// We may not have any data if, e.g., the old file does not exist.
$data = idx($line_data, $cursor - 1, null);
// Highlight the problem substring.
$text_line = $text_lines[$cursor - $line_num];
if (strlen($text_line)) {
$data = substr_replace(
$data,
phutil_console_format('##%s##', $text_line),
($cursor == $line_num)
? $message->getChar() - 1
: 0,
strlen($text_line));
}
$out[] = $this->renderLine($cursor, $data, $chevron, $diff);
}
if ($message->isPatchable()) {
$patch = $message->getReplacementText();
$patch_lines = explode("\n", $patch);
$offset = 0;
foreach ($patch_lines as $patch_line) {
if (isset($line_data[$line_num - 1 + $offset])) {
$base = $line_data[$line_num - 1 + $offset];
} else {
$base = '';
}
if ($offset == 0) {
$start = $message->getChar() - 1;
} else {
$start = 0;
}
if (isset($text_lines[$offset])) {
$len = strlen($text_lines[$offset]);
} else {
$len = 0;
}
$patched = substr_replace(
$base,
phutil_console_format('##%s##', $patch_line),
$start,
$len);
$out[] = $this->renderLine(null, $patched, false, '+');
$offset++;
}
}
$lines_count = count($line_data);
$end = min($lines_count, $cursor + $lines_of_context);
for (; $cursor < $end; $cursor++) {
$out[] = $this->renderLine($cursor, $line_data[$cursor - 1]);
}
$out[] = null;
return implode("\n", $out);
}
protected function renderLine($line, $data, $chevron = false, $diff = null) {
$chevron = $chevron ? '>>>' : '';
return sprintf(
" %3s %1s %6s %s",
$chevron,
$diff,
$line,
$data);
}
-
}
diff --git a/src/lint/renderer/ArcanistLintSummaryRenderer.php b/src/lint/renderer/ArcanistLintSummaryRenderer.php
new file mode 100644
index 00000000..b580e38d
--- /dev/null
+++ b/src/lint/renderer/ArcanistLintSummaryRenderer.php
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * Copyright 2011 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shows lint messages to the user.
+ *
+ * @group lint
+ */
+class ArcanistLintSummaryRenderer {
+ public function renderLintResult(ArcanistLintResult $result) {
+ $messages = $result->getMessages();
+ $path = $result->getPath();
+
+ $text = array();
+ $text[] = $path.":";
+ foreach ($messages as $message) {
+ $name = $message->getName();
+ $severity = ArcanistLintSeverity::getStringForSeverity(
+ $message->getSeverity());
+ $line = $message->getLine();
+
+ $text[] = " {$severity} on line {$line}: {$name}";
+ }
+ $text[] = null;
+
+ return implode("\n", $text);
+ }
+}
diff --git a/src/lint/renderer/__init__.php b/src/lint/renderer/__init__.php
index e8872d7c..23f3b960 100644
--- a/src/lint/renderer/__init__.php
+++ b/src/lint/renderer/__init__.php
@@ -1,15 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('arcanist', 'lint/severity');
phutil_require_module('phutil', 'console');
phutil_require_module('phutil', 'utils');
phutil_require_source('ArcanistLintRenderer.php');
+phutil_require_source('ArcanistLintSummaryRenderer.php');
+phutil_require_source('ArcanistLintJSONRenderer.php');
diff --git a/src/workflow/lint/ArcanistLintWorkflow.php b/src/workflow/lint/ArcanistLintWorkflow.php
index a030a950..17a6fe3b 100644
--- a/src/workflow/lint/ArcanistLintWorkflow.php
+++ b/src/workflow/lint/ArcanistLintWorkflow.php
@@ -1,285 +1,295 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Runs lint rules on changes.
*
* @group workflow
*/
class ArcanistLintWorkflow extends ArcanistBaseWorkflow {
const RESULT_OKAY = 0;
const RESULT_WARNINGS = 1;
const RESULT_ERRORS = 2;
const RESULT_SKIP = 3;
private $unresolvedMessages;
private $shouldAmendChanges = false;
public function setShouldAmendChanges($should_amend) {
$this->shouldAmendChanges = $should_amend;
return $this;
}
public function getCommandHelp() {
return phutil_console_format(<<<EOTEXT
**lint** [__options__] [__paths__] (svn)
**lint** [__options__] [__commit_range__] (git)
Supports: git, svn
Run static analysis on changes to check for mistakes. If no files
are specified, lint will be run on all files which have been modified.
EOTEXT
);
}
public function getArguments() {
return array(
'lintall' => array(
'help' =>
"Show all lint warnings, not just those on changed lines."
),
- 'summary' => array(
+ 'output' => array(
+ 'param' => 'format',
'help' =>
- "Show lint warnings in a more compact format."
+ "With 'summary', show lint warnings in a more compact format. ".
+ "With 'json', show lint warnings in machine-readable JSON format."
),
'advice' => array(
'help' =>
"Show lint advice, not just warnings and errors."
),
'engine' => array(
'param' => 'classname',
'help' =>
"Override configured lint engine for this project."
),
'apply-patches' => array(
'help' =>
'Apply patches suggested by lint to the working copy without '.
'prompting.',
'conflicts' => array(
'never-apply-patches' => true,
),
),
'never-apply-patches' => array(
'help' => 'Never apply patches suggested by lint.',
'conflicts' => array(
'apply-patches' => true,
),
),
'*' => 'paths',
);
}
public function requiresWorkingCopy() {
return true;
}
public function run() {
$working_copy = $this->getWorkingCopy();
$engine = $this->getArgument('engine');
if (!$engine) {
$engine = $working_copy->getConfig('lint_engine');
}
$should_lint_all = $this->getArgument('lintall');
$repository_api = null;
if (!$should_lint_all) {
try {
$repository_api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity(
$working_copy);
$this->setRepositoryAPI($repository_api);
} catch (ArcanistUsageException $ex) {
throw new ArcanistUsageException(
$ex->getMessage()."\n\n".
"Use '--lintall' to ignore working copy changes when running lint.");
}
if ($repository_api instanceof ArcanistSubversionAPI) {
$paths = $repository_api->getWorkingCopyStatus();
$list = new FileList($this->getArgument('paths'));
foreach ($paths as $path => $flags) {
if (!$list->contains($path)) {
unset($paths[$path]);
}
}
} else {
$this->parseGitRelativeCommit(
$repository_api,
$this->getArgument('paths'));
$paths = $repository_api->getWorkingCopyStatus();
}
foreach ($paths as $path => $flags) {
if ($flags & ArcanistRepositoryAPI::FLAG_UNTRACKED) {
unset($paths[$path]);
}
}
$paths = array_keys($paths);
} else {
$paths = $this->getArgument('paths');
if (empty($paths)) {
throw new ArcanistUsageException(
"You must specify one or more files to lint when using '--lintall'.");
}
foreach ($paths as $key => $path) {
$full_path = Filesystem::resolvePath($path);
if (!Filesystem::pathExists($full_path)) {
throw new ArcanistUsageException("Path '{$path}' does not exist!");
}
$relative_path = Filesystem::readablePath(
$full_path,
$working_copy->getProjectRoot());
$paths[$key] = $relative_path;
}
}
if (!$engine) {
throw new ArcanistNoEngineException(
"No lint engine configured for this project. Edit .arcconfig to ".
"specify a lint engine.");
}
PhutilSymbolLoader::loadClass($engine);
$engine = newv($engine, array());
$engine->setWorkingCopy($working_copy);
if ($this->getArgument('advice')) {
$engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
} else {
$engine->setMinimumSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
}
$engine->setPaths($paths);
if (!$should_lint_all) {
foreach ($paths as $path) {
$engine->setPathChangedLines(
$path,
$this->getChangedLines($path, 'new'));
}
}
$results = $engine->run();
if ($this->getArgument('never-apply-patches')) {
$apply_patches = false;
} else {
$apply_patches = true;
}
if ($this->getArgument('apply-patches')) {
$prompt_patches = false;
} else {
$prompt_patches = true;
}
$wrote_to_disk = false;
- $renderer = new ArcanistLintRenderer();
- if ($this->getArgument('summary')) {
- $renderer->setSummaryMode(true);
+ switch ($this->getArgument('output')) {
+ case 'json':
+ $renderer = new ArcanistLintJSONRenderer();
+ break;
+ case 'summary':
+ $renderer = new ArcanistLintSummaryRenderer();
+ break;
+ default:
+ $renderer = new ArcanistLintRenderer();
+ break;
}
+
foreach ($results as $result) {
if (!$result->getMessages()) {
continue;
}
echo $renderer->renderLintResult($result);
if ($apply_patches && $result->isPatchable()) {
$patcher = ArcanistLintPatcher::newFromArcanistLintResult($result);
$old = $patcher->getUnmodifiedFileContent();
$new = $patcher->getModifiedFileContent();
if ($prompt_patches) {
$old_file = $result->getFilePathOnDisk();
if (!Filesystem::pathExists($old_file)) {
$old_file = '/dev/null';
}
$new_file = new TempFile();
Filesystem::writeFile($new_file, $new);
// TODO: Improve the behavior here, make it more like
// difference_render().
passthru(csprintf("diff -u %s %s", $old_file, $new_file));
$prompt = phutil_console_format(
"Apply this patch to __%s__?",
$result->getPath());
if (!phutil_console_confirm($prompt, $default_no = false)) {
continue;
}
}
$patcher->writePatchToDisk();
$wrote_to_disk = true;
}
}
if ($wrote_to_disk &&
($repository_api instanceof ArcanistGitAPI) &&
$this->shouldAmendChanges) {
$amend = phutil_console_confirm("Amend HEAD with lint patches?");
if ($amend) {
execx(
'(cd %s; git commit -a --amend -C HEAD)',
$repository_api->getPath());
} else {
throw new ArcanistUsageException(
"Sort out the lint changes that were applied to the working ".
"copy and relint.");
}
}
$unresolved = array();
$result_code = self::RESULT_OKAY;
foreach ($results as $result) {
foreach ($result->getMessages() as $message) {
if (!$message->isPatchApplied()) {
if ($message->isError()) {
$result_code = self::RESULT_ERRORS;
break;
} else if ($message->isWarning()) {
if ($result_code != self::RESULT_ERRORS) {
$result_code = self::RESULT_WARNINGS;
}
$unresolved[] = $message;
}
}
}
}
$this->unresolvedMessages = $unresolved;
if (!$this->getParentWorkflow()) {
if ($result_code == self::RESULT_OKAY) {
echo phutil_console_format(
"<bg:green>** OKAY **</bg> No lint warnings.\n");
}
}
return $result_code;
}
public function getUnresolvedMessages() {
return $this->unresolvedMessages;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 11, 11:14 (7 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
984283
Default Alt Text
(18 KB)
Attached To
Mode
R118 Arcanist - fork
Attached
Detach File
Event Timeline
Log In to Comment