<?php

abstract class ArcanistFutureLinter extends ArcanistLinter {

  private $futures;

  abstract protected function buildFutures(array $paths);
  abstract protected function resolveFuture($path, Future $future);

  final protected function getFuturesLimit() {
    return 8;
  }

  final public function willLintPaths(array $paths) {
    $limit = $this->getFuturesLimit();
    $this->futures = id(new FutureIterator(array()))->limit($limit);
    foreach ($this->buildFutures($paths) as $path => $future) {
      $this->futures->addFuture($future, $path);
    }
  }

  final public function lintPath($path) {
    return;
  }

  final public function didLintPaths(array $paths) {
    if (!$this->futures) {
      return;
    }

    $map = array();
    foreach ($this->futures as $path => $future) {
      $this->setActivePath($path);
      $this->resolveFuture($path, $future);
      $map[$path] = $future;
    }
    $this->futures = array();

    $this->didResolveLinterFutures($map);
  }


  /**
   * Hook for cleaning up resources.
   *
   * This is invoked after a block of futures resolve, and allows linters to
   * discard or clean up any shared resources they no longer need.
   *
   * @param map<string, Future> Map of paths to resolved futures.
   * @return void
   */
  protected function didResolveLinterFutures(array $futures) {
    return;
  }

}

<?php

abstract class ArcanistFutureLinter extends ArcanistLinter {

  private $futures;

  abstract protected function buildFutures(array $paths);
  abstract protected function resolveFuture($path, Future $future);

  final protected function getFuturesLimit() {
    return 8;
  }

  public function willLintPaths(array $paths) {
    $limit = $this->getFuturesLimit();
    $this->futures = id(new FutureIterator(array()))->limit($limit);
    foreach ($this->buildFutures($paths) as $path => $future) {
      $this->futures->addFuture($future, $path);
    }
  }

  final public function lintPath($path) {
    return;
  }

  public function didLintPaths(array $paths) {
    if (!$this->futures) {
      return;
    }

    $map = array();
    foreach ($this->futures as $path => $future) {
      $this->setActivePath($path);
      $this->resolveFuture($path, $future);
      $map[$path] = $future;
    }
    $this->futures = array();

    $this->didResolveLinterFutures($map);
  }


  /**
   * Hook for cleaning up resources.
   *
   * This is invoked after a block of futures resolve, and allows linters to
   * discard or clean up any shared resources they no longer need.
   *
   * @param map<string, Future> Map of paths to resolved futures.
   * @return void
   */
  protected function didResolveLinterFutures(array $futures) {
    return;
  }

}

Allow `willLintPaths` and `didLintPaths` to be overridden
Allow `willLintPaths` and `didLintPaths` to be overridden

Summary: I'm not sure if the upstream will be interested in this change, but we are writing a linter which works by running an external command on the entire repository. This can't be done with `ArcanistExternalLinter` at the moment, which meant that we ended up copy-pasting most of `ArcanistFutureLinter`. This would be a lot easier if we could override `willLintPaths` and `didLintPaths`, but these methods are currently marked as `final`. An alternative solution would be some sort of `ArcanistLinter::transformPath` method.

Test Plan: N/A

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: faulconbridge, Korvin

Differential Revision: https://secure.phabricator.com/D19630
