4
* List available linters.
6
final class ArcanistLintersWorkflow extends ArcanistWorkflow {
8
public function getWorkflowName() {
12
public function getCommandSynopses() {
13
return phutil_console_format(<<<EOTEXT
14
**linters** [__options__]
19
public function getCommandHelp() {
20
return phutil_console_format(pht(<<<EOTEXT
22
List the available and configured linters, with information about
23
what they do and which versions are installed.
28
public function getArguments() {
31
'help' => pht('Show detailed information, including options.'),
36
public function run() {
37
$console = PhutilConsole::getConsole();
39
$linters = id(new PhutilSymbolLoader())
40
->setAncestorClass('ArcanistLinter')
44
$built = $this->newLintEngine()->buildLinters();
45
} catch (ArcanistNoEngineException $ex) {
49
// Note that an engine can emit multiple linters of the same class to run
50
// different rulesets on different groups of files, so these linters do not
51
// necessarily have unique classes or types.
53
foreach ($built as $linter) {
54
$groups[get_class($linter)][] = $linter;
57
$linter_info = array();
58
foreach ($linters as $key => $linter) {
59
$installed = idx($groups, $key, array());
63
$status = 'configured';
65
$version = head($installed)->getVersion();
66
} catch (Exception $ex) {
71
$status = 'available';
75
$linter_info[$key] = array(
76
'short' => $linter->getLinterConfigurationName(),
77
'class' => get_class($linter),
79
'version' => $version,
80
'name' => $linter->getInfoName(),
81
'uri' => $linter->getInfoURI(),
82
'description' => $linter->getInfoDescription(),
83
'exception' => $exception,
84
'options' => $linter->getLinterConfigurationOptions(),
88
$linter_info = isort($linter_info, 'short');
90
$status_map = $this->getStatusMap();
94
'configured' => 'green',
95
'available' => 'yellow',
99
foreach ($linter_info as $key => $linter) {
100
$status = $linter['status'];
101
$color = $color_map[$status];
102
$text = $status_map[$status];
106
"<bg:".$color.">** %s **</bg> **%s** (%s)\n",
108
nonempty($linter['short'], '-'),
111
if ($linter['exception']) {
115
get_class($linter['exception']),
117
$linter['exception']->getMessage(),
122
$version = $linter['version'];
123
$uri = $linter['uri'];
124
if ($version || $uri) {
125
$console->writeOut("\n");
130
$console->writeOut("%s%s **%s**\n", $pad, pht('Version'), $version);
134
$console->writeOut("%s__%s__\n", $pad, $linter['uri']);
137
$description = $linter['description'];
141
phutil_console_wrap($linter['description'], strlen($pad)));
145
$options = $linter['options'];
146
if ($options && $this->getArgument('verbose')) {
150
pht('Configuration Options'));
152
$last_option = last_key($options);
153
foreach ($options as $option => $option_spec) {
158
$option_spec['type']);
163
$option_spec['help'],
166
if ($option != $last_option) {
167
$console->writeOut("\n");
174
$console->writeOut("\n");
178
if (!$this->getArgument('verbose')) {
181
pht('(Run `arc linters --verbose` for more details.)'));
187
* Get human-readable linter statuses, padded to fixed width.
189
* @return map<string, string> Human-readable linter status names.
191
private function getStatusMap() {
193
'configured' => pht('CONFIGURED'),
194
'available' => pht('AVAILABLE'),
195
'error' => pht('ERROR'),
199
foreach ($text_map as $key => $string) {
200
$sizes[$key] = phutil_utf8_console_strlen($string);
203
$longest = max($sizes);
204
foreach ($text_map as $key => $string) {
205
if ($sizes[$key] < $longest) {
206
$text_map[$key] .= str_repeat(' ', $longest - $sizes[$key]);
210
$text_map['padding'] = str_repeat(' ', $longest);