Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 70 additions & 12 deletions classes/local/cli/commands/format.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace local_devkit\local\cli\commands;

use core\di;
use InvalidArgumentException;
use local_devkit\local\format\base;
use local_devkit\local\format\biome;
use local_devkit\local\format\eslint;
Expand All @@ -29,11 +30,14 @@
use Symfony\Component\Console\Helper\ProgressIndicator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Finder;

use function count;

/**
* Format command.
*
Expand Down Expand Up @@ -69,6 +73,7 @@ class format extends Command {
*/
protected function configure(): void {
$this->addArgument('paths', InputArgument::IS_ARRAY);
$this->addOption('batch-size', null, InputOption::VALUE_REQUIRED, 'Number of files per formatter batch', 50);
}

/**
Expand All @@ -80,12 +85,18 @@ public function __invoke(
OutputInterface $output,
): int {
$paths = $input->getArgument('paths');
$rawbatchsize = $input->getOption('batch-size');
if (!is_numeric($rawbatchsize) || (int) $rawbatchsize <= 0) {
$io->error('The batch-size option must be a positive integer.');
return Command::FAILURE;
}
$batchsize = (int) $rawbatchsize;
$progress = $output instanceof ConsoleOutputInterface
? new ProgressIndicator($output->getErrorOutput())
: null;

$progress?->start('Starting...');
$this->format_run($paths, $progress);
$this->format_run($paths, $batchsize, $progress);
$progress?->finish('All done.');

return Command::SUCCESS;
Expand All @@ -95,7 +106,33 @@ public function __invoke(
* Format files in the given paths.
* @param string[] $paths
*/
private function format_run(array $paths, ?ProgressIndicator $progress): void {
private function format_run(array $paths, int $batchsize, ?ProgressIndicator $progress): void {
if ($batchsize < 1) {
throw new InvalidArgumentException('Argument $batchsize must be a positive integer.');
}

$allfiles = $this->collect_files($paths);

$formattermap = $this->build_formatter_map($allfiles);

foreach ($formattermap as $formatterclass => $files) {
$name = $formatterclass::get_name();
$batches = array_chunk($files, $batchsize);
foreach ($batches as $batch) {
$progress?->setMessage("Running $name on " . count($batch) . " files...");
$formatterclass::format_batch($batch);
}
}
}

/**
* Collect all files from the given paths.
* @param string[] $paths
* @return string[]
*/
private function collect_files(array $paths): array {
$files = [];

foreach ($paths as $path) {
if (!file_exists($path)) {
continue;
Expand Down Expand Up @@ -125,26 +162,47 @@ private function format_run(array $paths, ?ProgressIndicator $progress): void {
foreach ($finder as $file) {
$realpath = $file->getRealPath();
if ($realpath !== false) {
$this->format_file($realpath, $progress);
$files[] = $realpath;
}
}
} else {
$this->format_file($path, $progress);
$files[] = $path;
}
}

return $files;
}

/**
* Run formatters on a single file.
* Build a map of formatter class to file paths, preserving pick_formatters order.
*
* Formatters are emitted in the order they first appear across all files,
* which respects the per-file ordering defined by pick_formatters.
*
* @param string[] $files
* @return array<class-string<base>, string[]>
*/
private function format_file(string $path, ?ProgressIndicator $progress): void {
$progress?->setMessage("Formatting $path...");
$formatters = $this->pick_formatters($path);
foreach ($formatters as $formatter) {
$name = $formatter::get_name();
$progress?->setMessage("Formatting $path with $name");
$formatter::format($path);
private function build_formatter_map(array $files): array {
$formatterorder = [];
$formattermap = [];

foreach ($files as $file) {
foreach ($this->pick_formatters($file) as $formatter) {
$class = $formatter::class;
if (!isset($formattermap[$class])) {
$formatterorder[] = $class;
$formattermap[$class] = [];
}
$formattermap[$class][] = $file;
}
}

$ordered = [];
foreach ($formatterorder as $class) {
$ordered[$class] = $formattermap[$class];
}

return $ordered;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions classes/local/format/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,17 @@ abstract public static function get_name(): string;
* Apply formatting to a given file.
*/
abstract public static function format(string $file): ?int;

/**
* Apply formatting to a batch of files.
*
* Subclasses may override this to pass multiple files in a single process invocation.
*
* @param string[] $files
*/
public static function format_batch(array $files): void {
foreach ($files as $file) {
static::format($file);
}
}
}
19 changes: 19 additions & 0 deletions classes/local/format/biome.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ public static function format(string $path): ?int {
return $process->getExitCode();
}

#[\Override]
public static function format_batch(array $files): void {
$config = self::get_config_path();
if ($config === null) {
return;
}

$process = new Process(array_merge([
'bunx',
'--bun',
'@biomejs/biome',
'format',
'--config-path',
$config,
'--write',
], $files));
$process->run();
}

/**
* Get the biome.jsonc file.
*/
Expand Down
6 changes: 6 additions & 0 deletions classes/local/format/eslint.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public static function format(string $path): ?int {

return $process->getExitCode();
}

#[\Override]
public static function format_batch(array $files): void {
$process = new Process(array_merge(['bunx', 'eslint', '--fix'], $files));
$process->run();
}
}
9 changes: 9 additions & 0 deletions classes/local/format/phpcbf.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,13 @@ public static function format(string $path): ?int {

return $process->getExitCode();
}

#[\Override]
public static function format_batch(array $files): void {
$process = new Process(array_merge([
'phpcbf',
'-q',
], $files), timeout: MINSECS);
$process->run();
}
}
21 changes: 21 additions & 0 deletions classes/local/format/pint.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,25 @@ public static function format(string $path): ?int {

return $process->getExitCode();
}

#[\Override]
public static function format_batch(array $files): void {
global $CFG;
$bin = realpath("$CFG->dirroot/local/devkit/vendor/bin/pint");
$config = realpath("$CFG->dirroot/local/devkit/pint.json");

if ($bin === false || $config === false) {
return;
}

$process = new Process(array_merge([
'php',
$bin,
'--no-interaction',
'--quiet',
'--config',
$config,
], $files), timeout: MINSECS);
$process->run();
}
}
6 changes: 6 additions & 0 deletions classes/local/format/stylelint.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public static function format(string $path): ?int {

return $process->getExitCode();
}

#[\Override]
public static function format_batch(array $files): void {
$process = new Process(array_merge(['bunx', 'stylelint', '--fix'], $files));
$process->run();
}
}