<commit_before><?php
namespace Kahlan\Reporter;

use Kahlan\Reporter\Coverage\Collector;

class Coverage extends Terminal
{
    /**
     * Class dependencies.
     *
     * @var array
     */
    protected static $_classes = [
        'interceptor' => 'Kahlan\Jit\Interceptor'
    ];

    /**
     * Collect time.
     *
     * @var float
     */
    protected $_time = 0;

    /**
     * The coverage verbosity.
     *
     * @param integer
     */
    protected $_verbosity = 0;

    /**
     * Reference to the coverage collector driver.
     *
     * @param object
     */
    protected $_collector = '';

    /**
     * Status of the reporter.
     *
     * @var array
     */
    protected $_enabled = true;

    /**
     * Store prefix by level for tree rendering.
     *
     * @var array
     */
    protected $_prefixes = [];

    /**
     * The Constructor.
     *
     * @param array $config The config for the reporter, the options are:
     *                      - `'verbosity`' _integer|string_: The verbosity level:
     *                        - 1      : overall coverage value for the whole code.
     *                        - 2      : overall coverage by namespaces.
     *                        - 3      : overall coverage by classes.
     *                        - 4      : overall coverage by methods and functions.
     *                        - string : coverage for a fully namespaced (class/method/namespace) string.
     */
    public function __construct($config = [])
    {
        parent::__construct($config);
        $defaults = ['verbosity' => 1];
        $config += $defaults;

        $verbosity = $config['verbosity'];
        $this->_verbosity  = is_numeric($verbosity) ? (integer) $verbosity : (string) $verbosity;

        if (is_string($this->_verbosity)) {
            $class = preg_replace('/(::)?\w+\(\)$/', '', $this->_verbosity);
            $interceptor = static::$_classes['interceptor'];
            $loader = $interceptor::instance();

            if ($loader && $path = $loader->findPath($class)) {
                $config['path'] = $path;
            }
        }

        $this->_collector = new Collector($config);
    }

    /**
     * Callback called before any specs processing.
     *
     * @param array $args The suite arguments.
     */
    public function start($args)
    {
    }

    /**
     * Callback called on a spec start.
     *
     * @param object $spec The spec object of the whole spec.
     */
    public function specStart($spec = null)
    {
        parent::specStart($spec);
        if (!$this->enabled()) {
            return;
        }
        $this->_collector->start();
    }

    /**
     * Callback called after a spec execution.
     *
     * @param object $log The log object of the whole spec.
     */
    public function specEnd($log = null)
    {
        parent::specEnd($log);
        if (!$this->enabled()) {
            return;
        }
        $this->_collector->stop();
    }

    /**
     * Gets the collector.
     *
     * @return object
     */
    public function collector()
    {
        return $this->_collector;
    }

    /**
     * Delegates the call to the collector instance.
     *
     * @param  string  $name The function name.
     * @param  array   $args The arguments to pass to the function.
     * @return mixed
     */
    public function __call($name, $args)
    {
        return call_user_func_array([$this->collector(), $name], $args);
    }

    /**
     * Gets the metrics about the coverage result.
     */
    public function metrics()
    {
        $this->_start = microtime(true);
        $result = $this->_collector->metrics();
        $this->_time  = microtime(true) - $this->_start;
        return $result;
    }

    /**
     * Outputs some metrics info where the metric is not the total coverage.
     *
     * @param Metrics $metrics A metrics instance.
     * @param array   $options The options for the reporter, the options are:
     *                         - `'verbosity`' _integer|string_: The verbosity level:
     *                           - 1      : overall coverage value for the whole code.
     *                           - 2      : overall coverage by namespaces.
     *                           - 3      : overall coverage by classes.
     *                           - 4      : overall coverage by methods and functions.
     *                           - string : coverage for a fully namespaced (class/method/namespace) string.
     */
    protected function _renderMetrics($metrics, $verbosity)
    {
        $maxLabelWidth = null;
        if ($verbosity === 1) {
            return;
        }
        $metricsReport = $this->_getMetricsReport($metrics->children(), $verbosity, 0, 3, $maxLabelWidth);
        $name = $metrics->name() ?: '\\';
        $maxLabelWidth = max(strlen($name) + 1, $maxLabelWidth);
        $maxLabelWidth += 4;
        $stats = $metrics->data();
        $percent = number_format($stats['percent'], 2);
        $style = $this->_style($percent);
        $maxLineWidth = strlen($stats['lloc']);

        $this->write(str_repeat(' ', $maxLabelWidth));
        $this->write('  ');
        $this->write(str_pad('Lines', $maxLineWidth * 2 + 3, ' ', STR_PAD_BOTH));
        $this->write(str_pad('%', 12, ' ', STR_PAD_LEFT));
        $this->write("\n\n");
        $this->write(str_pad(' ' . $name, $maxLabelWidth));
        $this->write('  ');
        $this->write(str_pad("{$stats['cloc']}", $maxLineWidth, ' ', STR_PAD_LEFT));
        $this->write(' / ');
        $this->write(str_pad("{$stats['lloc']}", $maxLineWidth, ' ', STR_PAD_LEFT));
        $this->write('     ');
        $this->write(str_pad("{$percent}%", 7, ' ', STR_PAD_LEFT), $style);
        $this->write("\n");
        $this->_renderMetricsReport($metricsReport, $maxLabelWidth, $maxLineWidth, 0);
    }

    /**
     * Outputs some metrics reports built using `::_getMetricsReport()`.
     *
     * @param array $metricsReport An array of nested metrics reports extracted according some verbosity.
     * @param array $labelWidth    The width column of the label column used for padding.
     * @param array $lineWidth     The width column of the covered lines data used for padding.
     * @param array $depth         The actual depth in the reporting to build tree prefix.
     */
    protected function _renderMetricsReport($metricsReport, $labelWidth, $lineWidth, $depth)
    {
        $nbChilden = count($metricsReport);
        $index = 0;
        foreach ($metricsReport as $name => $data) {
            $isLast = $index === $nbChilden - 1;
            if ($isLast) {
                $this->_prefixes[$depth] = '└──';
            } else {
                $this->_prefixes[$depth] = '├──';
            }

            $metrics = $data['metrics'];
            $stats = $metrics->data();
            $percent = number_format($stats['percent'], 2);
            $style = $this->_style($percent);

            $prefix = join('', $this->_prefixes) . ' ';
            $diff = strlen($prefix) - strlen(utf8_decode($prefix));

            $type = $metrics->type();
            $color = $type === 'function' || $type === 'method' ? 'd' : '';
            $this->write($prefix);
            $this->write(str_pad($name, $labelWidth + $diff - strlen($prefix)), $color);
            $this->write('  ');
            $this->write(str_pad("{$stats['cloc']}", $lineWidth, ' ', STR_PAD_LEFT));
            $this->write(' / ');
            $this->write(str_pad("{$stats['lloc']}", $lineWidth, ' ', STR_PAD_LEFT));
            $this->write('     ');
            $this->write(str_pad("{$percent}%", 7, ' ', STR_PAD_LEFT), $style);
            $this->write("\n");

            if ($isLast) {
                $this->_prefixes[$depth] = '   ';
            } else {
                $this->_prefixes[$depth] = '│  ';
            }
            $this->_renderMetricsReport($data['children'], $labelWidth, $lineWidth, $depth + 1);
            $index++;
        }
        $this->_prefixes[$depth] = '';
    }

    /**
     * Extract some metrics reports to display according to a verbosity parameter.
     *
     * @param Metrics[] $children A array of metrics.
     * @param array     $options  The options for the reporter, the options are:
     *                            - `'verbosity`' _integer|string_: The verbosity level:
     *                              - 1      : overall coverage value for the whole code.
     *                              - 2      : overall coverage by namespaces.
     *                              - 3      : overall coverage by classes.
     *                              - 4      : overall coverage by methods and functions.
     *                              - string : coverage for a fully namespaced (class/method/namespace) string.
     * @param array     $depth     The actual depth in the reporting.
     * @param array     $tab       The size of the tab used for lablels.
     * @param array     $maxWidth  Will contain the maximum width obtained for labels.
     */
    protected function _getMetricsReport($children, $verbosity, $depth = 0, $tab = 3, &$maxWidth = null)
    {
        $list = [];
        foreach ($children as $child) {
            $type = $child->type();

            if ($verbosity === 2 && $type !== 'namespace') {
                continue;
            }
            if ($verbosity === 3 && ($type === 'function' || $type === 'method')) {
                continue;
            }

            $name = $child->name();
            $pos = strrpos($name, '\\', $type === 'namespace' ? - 2 : 0);
            $basename = substr($name, $pos !== false ? $pos + 1 : 0);
<commit_msg>Make coverage reporter support global namespace.<commit_after><?php
namespace Kahlan\Reporter;

use Kahlan\Reporter\Coverage\Collector;

class Coverage extends Terminal
{
    /**
     * Class dependencies.
     *
     * @var array
     */
    protected static $_classes = [
        'interceptor' => 'Kahlan\Jit\Interceptor'
    ];

    /**
     * Collect time.
     *
     * @var float
     */
    protected $_time = 0;

    /**
     * The coverage verbosity.
     *
     * @param integer
     */
    protected $_verbosity = 0;

    /**
     * Reference to the coverage collector driver.
     *
     * @param object
     */
    protected $_collector = '';

    /**
     * Status of the reporter.
     *
     * @var array
     */
    protected $_enabled = true;

    /**
     * Store prefix by level for tree rendering.
     *
     * @var array
     */
    protected $_prefixes = [];

    /**
     * The Constructor.
     *
     * @param array $config The config for the reporter, the options are:
     *                      - `'verbosity`' _integer|string_: The verbosity level:
     *                        - 1      : overall coverage value for the whole code.
     *                        - 2      : overall coverage by namespaces.
     *                        - 3      : overall coverage by classes.
     *                        - 4      : overall coverage by methods and functions.
     *                        - string : coverage for a fully namespaced (class/method/namespace) string.
     */
    public function __construct($config = [])
    {
        parent::__construct($config);
        $defaults = ['verbosity' => 1];
        $config += $defaults;

        $verbosity = $config['verbosity'];
        $this->_verbosity  = is_numeric($verbosity) ? (integer) $verbosity : (string) $verbosity;

        if (is_string($this->_verbosity)) {
            $class = preg_replace('/(::)?\w+\(\)$/', '', $this->_verbosity);
            $interceptor = static::$_classes['interceptor'];
            $loader = $interceptor::instance();

            if ($loader && $path = $loader->findPath($class)) {
                $config['path'] = $path;
            }
        }

        $this->_collector = new Collector($config);
    }

    /**
     * Callback called before any specs processing.
     *
     * @param array $args The suite arguments.
     */
    public function start($args)
    {
    }

    /**
     * Callback called on a spec start.
     *
     * @param object $spec The spec object of the whole spec.
     */
    public function specStart($spec = null)
    {
        parent::specStart($spec);
        if (!$this->enabled()) {
            return;
        }
        $this->_collector->start();
    }

    /**
     * Callback called after a spec execution.
     *
     * @param object $log The log object of the whole spec.
     */
    public function specEnd($log = null)
    {
        parent::specEnd($log);
        if (!$this->enabled()) {
            return;
        }
        $this->_collector->stop();
    }

    /**
     * Gets the collector.
     *
     * @return object
     */
    public function collector()
    {
        return $this->_collector;
    }

    /**
     * Delegates the call to the collector instance.
     *
     * @param  string  $name The function name.
     * @param  array   $args The arguments to pass to the function.
     * @return mixed
     */
    public function __call($name, $args)
    {
        return call_user_func_array([$this->collector(), $name], $args);
    }

    /**
     * Gets the metrics about the coverage result.
     */
    public function metrics()
    {
        $this->_start = microtime(true);
        $result = $this->_collector->metrics();
        $this->_time  = microtime(true) - $this->_start;
        return $result;
    }

    /**
     * Outputs some metrics info where the metric is not the total coverage.
     *
     * @param Metrics $metrics A metrics instance.
     * @param array   $options The options for the reporter, the options are:
     *                         - `'verbosity`' _integer|string_: The verbosity level:
     *                           - 1      : overall coverage value for the whole code.
     *                           - 2      : overall coverage by namespaces.
     *                           - 3      : overall coverage by classes.
     *                           - 4      : overall coverage by methods and functions.
     *                           - string : coverage for a fully namespaced (class/method/namespace) string.
     */
    protected function _renderMetrics($metrics, $verbosity)
    {
        $maxLabelWidth = null;
        if ($verbosity === 1) {
            return;
        }
        $metricsReport = $this->_getMetricsReport($metrics->children(), $verbosity, 0, 3, $maxLabelWidth);
        $name = $metrics->name() ?: '\\';
        $maxLabelWidth = max(strlen($name) + 1, $maxLabelWidth);
        $maxLabelWidth += 4;
        $stats = $metrics->data();
        $percent = number_format($stats['percent'], 2);
        $style = $this->_style($percent);
        $maxLineWidth = strlen($stats['lloc']);

        $this->write(str_repeat(' ', $maxLabelWidth));
        $this->write('  ');
        $this->write(str_pad('Lines', $maxLineWidth * 2 + 3, ' ', STR_PAD_BOTH));
        $this->write(str_pad('%', 12, ' ', STR_PAD_LEFT));
        $this->write("\n\n");
        $this->write(str_pad(' ' . $name, $maxLabelWidth));
        $this->write('  ');
        $this->write(str_pad("{$stats['cloc']}", $maxLineWidth, ' ', STR_PAD_LEFT));
        $this->write(' / ');
        $this->write(str_pad("{$stats['lloc']}", $maxLineWidth, ' ', STR_PAD_LEFT));
        $this->write('     ');
        $this->write(str_pad("{$percent}%", 7, ' ', STR_PAD_LEFT), $style);
        $this->write("\n");
        $this->_renderMetricsReport($metricsReport, $maxLabelWidth, $maxLineWidth, 0);
    }

    /**
     * Outputs some metrics reports built using `::_getMetricsReport()`.
     *
     * @param array $metricsReport An array of nested metrics reports extracted according some verbosity.
     * @param array $labelWidth    The width column of the label column used for padding.
     * @param array $lineWidth     The width column of the covered lines data used for padding.
     * @param array $depth         The actual depth in the reporting to build tree prefix.
     */
    protected function _renderMetricsReport($metricsReport, $labelWidth, $lineWidth, $depth)
    {
        $nbChilden = count($metricsReport);
        $index = 0;
        foreach ($metricsReport as $name => $data) {
            $isLast = $index === $nbChilden - 1;
            if ($isLast) {
                $this->_prefixes[$depth] = '└──';
            } else {
                $this->_prefixes[$depth] = '├──';
            }

            $metrics = $data['metrics'];
            $stats = $metrics->data();
            $percent = number_format($stats['percent'], 2);
            $style = $this->_style($percent);

            $prefix = join('', $this->_prefixes) . ' ';
            $diff = strlen($prefix) - strlen(utf8_decode($prefix));

            $type = $metrics->type();
            $color = $type === 'function' || $type === 'method' ? 'd' : '';
            $this->write($prefix);
            $this->write(str_pad($name, $labelWidth + $diff - strlen($prefix)), $color);
            $this->write('  ');
            $this->write(str_pad("{$stats['cloc']}", $lineWidth, ' ', STR_PAD_LEFT));
            $this->write(' / ');
            $this->write(str_pad("{$stats['lloc']}", $lineWidth, ' ', STR_PAD_LEFT));
            $this->write('     ');
            $this->write(str_pad("{$percent}%", 7, ' ', STR_PAD_LEFT), $style);
            $this->write("\n");

            if ($isLast) {
                $this->_prefixes[$depth] = '   ';
            } else {
                $this->_prefixes[$depth] = '│  ';
            }
            $this->_renderMetricsReport($data['children'], $labelWidth, $lineWidth, $depth + 1);
            $index++;
        }
        $this->_prefixes[$depth] = '';
    }

    /**
     * Extract some metrics reports to display according to a verbosity parameter.
     *
     * @param Metrics[] $children A array of metrics.
     * @param array     $options  The options for the reporter, the options are:
     *                            - `'verbosity`' _integer|string_: The verbosity level:
     *                              - 1      : overall coverage value for the whole code.
     *                              - 2      : overall coverage by namespaces.
     *                              - 3      : overall coverage by classes.
     *                              - 4      : overall coverage by methods and functions.
     *                              - string : coverage for a fully namespaced (class/method/namespace) string.
     * @param array     $depth     The actual depth in the reporting.
     * @param array     $tab       The size of the tab used for lablels.
     * @param array     $maxWidth  Will contain the maximum width obtained for labels.
     */
    protected function _getMetricsReport($children, $verbosity, $depth = 0, $tab = 3, &$maxWidth = null)
    {
        $list = [];
        foreach ($children as $child) {
            $type = $child->type();

            if ($verbosity === 2 && $type !== 'namespace') {
                continue;
            }
            if ($verbosity === 3 && ($type === 'function' || $type === 'method')) {
                continue;
            }

            $name = $child->name();

            if ($name !== '\\') {
                $pos = strrpos($name, '\\', $type === 'namespace' ? - 2 : 0);
                $basename = substr($name, $pos !== false ? $pos + 1 : 0);
            } else {
                $basename = '\\';
            }

{'fraction_non_alphanumeric': 0.14515383430068313, 'fraction_numerical': 0.004211823925214443, 'mean_word_length': 1.7215543751747275, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 48, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1429848', 'n_tokens_mistral': 5535, 'n_tokens_neox': 5320, 'n_words': 1399}