Skip to content

Instantly share code, notes, and snippets.

@ambv
Created September 26, 2018 19:36
Show Gist options
  • Save ambv/af475051961126765e35d31bd054e61c to your computer and use it in GitHub Desktop.
Save ambv/af475051961126765e35d31bd054e61c to your computer and use it in GitHub Desktop.
How to integrate Black with Arcanist
<?php
final class PythonFormatLinter extends ArcanistFutureLinter {
public function getLinterName() {
return 'BLACK';
}
protected function getFuturesLimit() {
return 8;
}
protected function buildFutures(array $paths) {
$vcs_root = $this->getVCSRoot();
$futures = array();
foreach ($paths as $path) {
$fullPath = $this->getEngine()->getFilePathOnDisk($path);
$dirPath = dirname($fullPath);
$futures[$path] = new ExecFuture(
'/usr/local/bin/black --diff %s',
$this->getEngine()->getFilePathOnDisk($path));
$futures[$path]->setCWD($this->getProjectRoot());
}
return $futures;
}
protected function resolveFuture($path, Future $future) {
list($err, $stdout, $stderr) = $future->resolve();
if ($err) {
$this->addLintMessage(
$this->parseLinterError($path, $stdout, $stderr, $err));
} else {
$messages = $this->parseLinterOutput($path, $stdout, $stderr);
if ($messages !== false) {
foreach ($messages as $message) {
$this->addLintMessage($message);
}
}
}
}
protected function parseLinterOutput($path, $stdout, $stderr) {
if (empty($stdout) || substr($stdout, 0, 3) != '---') {
return array();
}
$messages = array();
$parser = new ArcanistDiffParser();
$changes = $parser->parseDiff($stdout);
foreach ($changes as $change) {
foreach ($change->getHunks() as $hunk) {
$repl = array();
$orig = array();
$lines = phutil_split_lines($hunk->getCorpus(), false);
foreach ($lines as $line) {
if (empty($line)) {
continue;
}
$char = $line[0];
$rest = substr($line, 1);
switch ($char) {
case '-':
$orig[] = $rest;
break;
case '+':
$repl[] = $rest;
break;
case '~':
break;
case ' ':
$orig[] = $rest;
$repl[] = $rest;
break;
}
}
$messages[] = id(new ArcanistLintMessage())
->setPath($path)
->setLine($hunk->getOldOffset())
->setChar(1)
->setCode($this->getLinterName())
->setSeverity(ArcanistLintSeverity::SEVERITY_AUTOFIX)
->setName('format')
->setOriginalText(implode("\n", $orig))
->setReplacementText(implode("\n", $repl))
->setBypassChangedLineFiltering(true);
}
}
return $messages;
}
protected function parseLinterError($path, $stdout, $stderr, $err) {
$matches = null;
preg_match(
'/error: cannot format -: Cannot parse: (?P<line>\d+):(?P<column>\d+): (?P<message>.*)/',
$stderr,
$matches);
if ($matches) {
$line = $matches['line'];
$col = $matches['column'] + 1;
$message = $matches['message'];
$name = 'Cannot parse';
$severity = ArcanistLintSeverity::SEVERITY_ERROR;
} else {
$line = 1;
$col = 1;
$message = $stderr;
$name = sprintf('Command execution failed (%d)', $err);
$severity = ArcanistLintSeverity::SEVERITY_ADVICE;
}
return id(new ArcanistLintMessage())
->setPath($path)
->setLine($line)
->setChar($col)
->setCode($this->getLinterName())
->setSeverity($severity)
->setName($name)
->setDescription($message)
->setBypassChangedLineFiltering(true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment