<commit_before><?php
namespace PhpKit\WebConsole\Loggers;

use PhpKit\WebConsole\DebugConsole\DebugConsole;
use PhpKit\WebConsole\ErrorConsole\ErrorConsole;
use PhpKit\WebConsole\Lib\Debug;
use Psr\Log\AbstractLogger;

class ConsoleLogger extends AbstractLogger
{
  /**
   * The name of the global logging function, defined on DebugConsole.php
   */
  const GLOBAL_LOG_FN = 'inspect';
  /**
   * Logging levels from syslog protocol defined in RFC 5424
   *
   * @var array $LEVELS Logging levels
   */
  protected static $LEVELS = [
    100 => 'DEBUG',
    200 => 'INFO',
    250 => 'NOTICE',
    300 => 'WARNING',
    400 => 'ERROR',
    500 => 'CRITICAL',
    550 => 'ALERT',
    600 => 'EMERGENCY',
  ];

  public $hasPanel = false;
  public $icon;
  public $title;
  /**
   * The caption to be used on the next table.
   * @var string
   */
  protected $caption;
  /**
   * @var string The panel's HTML content.
   */
  protected $content = '';
  /**
   * Filter callback for tables.
   *
   * <p>When set, for each object/array being debugged, the callbak receives the key name, the value and the target
   * object/array.
   * <p>It should return `true` if the value should be displayed.
   * @var callable
   */
  protected $filter;

  function __construct ($title = 'Logger', $icon = '')
  {
    $this->title = $title;
    $this->icon  = $icon;
  }


  function getContent ()
  {
    $c             = $this->content;
    $this->content = '';
    return $c;
  }

  function getRenderedInspection ($val, $alt = null)
  {
    return $this->format ($this->getInspection1 ($val, $alt));
  }

  /**
   * Displays detailed information about each specified value.
   *
   * @param mixed $...args Each value to be inspected and displayed on an horizontal sequence.
   * @return $this
   */
  public function inspect ()
  {
    foreach (func_get_args () as $arg)
      $this->write ($this->getInspection1 ($arg));
    return $this;
  }

  /**
   * Displays detailed information about the specified value.
   * @param mixed $val
   * @param mixed $alt
   * @return $this
   */
  function inspectValue ($val, $alt = null)
  {
    return $this->write ($this->getRenderedInspection ($val, $alt));
  }

  /**
   * PSR-3-compliant logging method.
   *
   * @param mixed  $level
   * @param string $message
   * @param array  $context
   *
   * @return null
   */
  public function log ($level, $message, array $context = [])
  {
    $levelName = isset(self::$LEVELS[$level]) ? self::$LEVELS[$level] : $level;
    $message   = Debug::interpolate ($message, $context);
    $this->write ("<#log><#i><span class=__alert>$levelName</span> $message</#i></#log>");
  }

  /**
   * Returns an object's unique identifier (a short version), useful for debugging.
   * @param object $o
   * @return $this
   */
  function objectId ($o)
  {
    return $this->write (Debug::objectId ($o));
  }

  public function render ()
  {
    return $this->format ($this->content);
  }

  public function showCallLocation ()
  {
    $namespace = Debug::libraryNamespace ();
    $base      = __DIR__;
    $stack     = debug_backtrace (0);
    // Discard frames of all functions that belong to this library.
    while (!empty($stack) && (
        (isset($stack[0]['file']) && stripos ($stack[0]['file'], $base) === 0) ||
        (isset($stack[0]['class']) && stripos ($stack[0]['class'], $namespace) === 0) ||
        (isset($stack[0]['function']) && $stack[0]['function'] != self::GLOBAL_LOG_FN)
      )
    ) array_shift ($stack);
    $trace     = $stack[0];
    $path      = isset($trace['file']) ? $trace['file'] : '';
    $line      = isset($trace['line']) ? $trace['line'] : '';
    $shortPath = ErrorConsole::shortFileName ($path);
    $location  = empty($line) ? $shortPath : ErrorConsole::errorLink ($path, $line, 1, "$shortPath($line)");
    if ($path != '')
      $path = <<<HTML
<div class="__debug-location"><b>At</b> $location</div>
HTML;
    $this->write ($path);

    return $this;
  }

  function simpleTable ($data, $title = '', $columnHeaders = false)
  {
    $this->write ($this->table ($data, $title, 0, false, $columnHeaders));
    return $this;
  }

  function typeName ($v)
  {
    return $this->write ('<span class="__type">' . Debug::getType ($v) . '</span>');
  }

  function withCaption ($caption)
  {
    $args          = array_slice (func_get_args (), 1);
    $this->caption = $caption;
    call_user_func_array ([$this, 'inspect'], $args);

    return $this;
  }

  /**
   * Logs detailed information about the specified values or variables to the PHP console.
   *
   * > The filter function may remove some keys from the tabular output of objects or arrays.
   *
   * Extra params: list of one or more values to be displayed.
   * @param callable $fn Filter callback. Receives the key name, the value and the target object/array.<br>
   *                     Returns <kbd>true</kbd> if the value should be displayed, <kbd>false</kbd> if the whole
   *                     key + value should be hidden, or <kbd>'...'</kbd> if the key should appear, but the value
   *                     be omitted.
   * @return $this
   */
  public function withFilter (callable $fn)
  {
    $args         = array_slice (func_get_args (), 1);
    $this->filter = $fn;
    call_user_func_array ([$this, 'inspect'], $args);
    $this->filter = null;

    return $this;
  }

  public function write ($msg)
  {
    $this->content .= $msg;

    return $this;
  }

  /**
   * Renders log markup to HTML.
   *
   * ##### Syntax:
   * ```
   *    <#tag>text</#tag>
   *    <#tag|arg1|...argN>text</#tag>
   * ```
   * ##### Supported tags:
   * - `<#t>text</#t>` - non-evaluated raw HTML text.
   * - `<#i>text</#i>` - Log item. It adds spacing.
   * - `<#section|title>text</#section>` - A section box with an optional title.
   * - `<#log>content</#log>` - Output content wrapped by a log stripe.
   * - `<#data>data</#data>` - Format a data structure's textual representation.
   * - `<#header>text</#header>` - A subsection title.
   * - `<#footer>text</#footer>` - Extra information, right aligned.
   * - `<#alert>text</#alert>` - Warning message.
   * - `<#type>text</#type>` - A colored short type name with more details on a tooltip.
   * - `<#indent>text</#indent>` - An indented block (nested blocks are not supported).
   *
   * @param $msg
   * @return mixed
   */
  protected function format ($msg)
  {
    if (is_string ($msg)) {
      do {
        $msg = preg_replace_callback ('~<#(.+?)(?:\|([^>]+))?>(.*?)</#\1>~s', function ($m) {
          list ($all, $tag, $args, $str) = $m;
          $args = $args ? explode ('|', $args) : [];
          switch ($tag) {
            case 'i':
              return "<div class='__log-item " . get ($args, 0) . "'>$str</div>";
            case 'section':
              return "<div class='__log-section'>" . ($args ? "<div class='__log-title'>$args[0]</div>" : '') .
                     "$str</div>";
            case 'log':
              return "<div class='__log-stripe'>$str</div>";
            case 'data':
              return "<div class='__log-data'>$str</div>";
            case 'header':
              return "<div class='__header'>$str</div>";
            case 'footer':
              return "<div class='__footer'>$str</div><div></div>";
            case 'alert':
              return "<div class='__alert'>$str</div>";
            case 'type':<commit_msg>Added support for recursive (same tag nested) Log Markup tags<commit_after><?php
namespace PhpKit\WebConsole\Loggers;

use PhpKit\WebConsole\DebugConsole\DebugConsole;
use PhpKit\WebConsole\ErrorConsole\ErrorConsole;
use PhpKit\WebConsole\Lib\Debug;
use Psr\Log\AbstractLogger;

class ConsoleLogger extends AbstractLogger
{
  /**
   * The name of the global logging function, defined on DebugConsole.php
   */
  const GLOBAL_LOG_FN = 'inspect';
  /**
   * Logging levels from syslog protocol defined in RFC 5424
   *
   * @var array $LEVELS Logging levels
   */
  protected static $LEVELS = [
    100 => 'DEBUG',
    200 => 'INFO',
    250 => 'NOTICE',
    300 => 'WARNING',
    400 => 'ERROR',
    500 => 'CRITICAL',
    550 => 'ALERT',
    600 => 'EMERGENCY',
  ];

  public $hasPanel = false;
  public $icon;
  public $title;
  /**
   * The caption to be used on the next table.
   * @var string
   */
  protected $caption;
  /**
   * @var string The panel's HTML content.
   */
  protected $content = '';
  /**
   * Filter callback for tables.
   *
   * <p>When set, for each object/array being debugged, the callbak receives the key name, the value and the target
   * object/array.
   * <p>It should return `true` if the value should be displayed.
   * @var callable
   */
  protected $filter;

  function __construct ($title = 'Logger', $icon = '')
  {
    $this->title = $title;
    $this->icon  = $icon;
  }


  function getContent ()
  {
    $c             = $this->content;
    $this->content = '';
    return $c;
  }

  function getRenderedInspection ($val, $alt = null)
  {
    return $this->format ($this->getInspection1 ($val, $alt));
  }

  /**
   * Displays detailed information about each specified value.
   *
   * @param mixed $...args Each value to be inspected and displayed on an horizontal sequence.
   * @return $this
   */
  public function inspect ()
  {
    foreach (func_get_args () as $arg)
      $this->write ($this->getInspection1 ($arg));
    return $this;
  }

  /**
   * Displays detailed information about the specified value.
   * @param mixed $val
   * @param mixed $alt
   * @return $this
   */
  function inspectValue ($val, $alt = null)
  {
    return $this->write ($this->getRenderedInspection ($val, $alt));
  }

  /**
   * PSR-3-compliant logging method.
   *
   * @param mixed  $level
   * @param string $message
   * @param array  $context
   *
   * @return null
   */
  public function log ($level, $message, array $context = [])
  {
    $levelName = isset(self::$LEVELS[$level]) ? self::$LEVELS[$level] : $level;
    $message   = Debug::interpolate ($message, $context);
    $this->write ("<#log><#i><span class=__alert>$levelName</span> $message</#i></#log>");
  }

  /**
   * Returns an object's unique identifier (a short version), useful for debugging.
   * @param object $o
   * @return $this
   */
  function objectId ($o)
  {
    return $this->write (Debug::objectId ($o));
  }

  public function render ()
  {
    return $this->format ($this->content);
  }

  public function showCallLocation ()
  {
    $namespace = Debug::libraryNamespace ();
    $base      = __DIR__;
    $stack     = debug_backtrace (0);
    // Discard frames of all functions that belong to this library.
    while (!empty($stack) && (
        (isset($stack[0]['file']) && stripos ($stack[0]['file'], $base) === 0) ||
        (isset($stack[0]['class']) && stripos ($stack[0]['class'], $namespace) === 0) ||
        (isset($stack[0]['function']) && $stack[0]['function'] != self::GLOBAL_LOG_FN)
      )
    ) array_shift ($stack);
    $trace     = $stack[0];
    $path      = isset($trace['file']) ? $trace['file'] : '';
    $line      = isset($trace['line']) ? $trace['line'] : '';
    $shortPath = ErrorConsole::shortFileName ($path);
    $location  = empty($line) ? $shortPath : ErrorConsole::errorLink ($path, $line, 1, "$shortPath($line)");
    if ($path != '')
      $path = <<<HTML
<div class="__debug-location"><b>At</b> $location</div>
HTML;
    $this->write ($path);

    return $this;
  }

  function simpleTable ($data, $title = '', $columnHeaders = false)
  {
    $this->write ($this->table ($data, $title, 0, false, $columnHeaders));
    return $this;
  }

  function typeName ($v)
  {
    return $this->write ('<span class="__type">' . Debug::getType ($v) . '</span>');
  }

  function withCaption ($caption)
  {
    $args          = array_slice (func_get_args (), 1);
    $this->caption = $caption;
    call_user_func_array ([$this, 'inspect'], $args);

    return $this;
  }

  /**
   * Logs detailed information about the specified values or variables to the PHP console.
   *
   * > The filter function may remove some keys from the tabular output of objects or arrays.
   *
   * Extra params: list of one or more values to be displayed.
   * @param callable $fn Filter callback. Receives the key name, the value and the target object/array.<br>
   *                     Returns <kbd>true</kbd> if the value should be displayed, <kbd>false</kbd> if the whole
   *                     key + value should be hidden, or <kbd>'...'</kbd> if the key should appear, but the value
   *                     be omitted.
   * @return $this
   */
  public function withFilter (callable $fn)
  {
    $args         = array_slice (func_get_args (), 1);
    $this->filter = $fn;
    call_user_func_array ([$this, 'inspect'], $args);
    $this->filter = null;

    return $this;
  }

  public function write ($msg)
  {
    $this->content .= $msg;

    return $this;
  }

  /**
   * Renders log markup to HTML.
   *
   * ##### Syntax:
   * ```
   *    <#tag>text</#tag>
   *    <#tag|arg1|...argN>text</#tag>
   * ```
   * ##### Supported tags:
   * - `<#t>text</#t>` - non-evaluated raw HTML text.
   * - `<#i>text</#i>` - Log item. It adds spacing.
   * - `<#section|title>text</#section>` - A section box with an optional title.
   * - `<#log>content</#log>` - Output content wrapped by a log stripe.
   * - `<#data>data</#data>` - Format a data structure's textual representation.
   * - `<#header>text</#header>` - A subsection title.
   * - `<#footer>text</#footer>` - Extra information, right aligned.
   * - `<#alert>text</#alert>` - Warning message.
   * - `<#type>text</#type>` - A colored short type name with more details on a tooltip.
   * - `<#indent>text</#indent>` - An indented block.
   *
   * @param $msg
   * @return mixed
   */
  protected function format ($msg)
  {
    if (is_string ($msg)) {
      do {
        $msg = preg_replace_callback ('~
<\#(\w+) \s* (?: \| ([^>]+) )? >
  (                               # capture the tag\'s content
    (?:                           # loop begin
      (?= <\#\1[\s\|>] )          # either the same tag is opened again
      (?R)                        # and we must recurse
    |                             # or
      (?! </\#\1> ).              # consume one char until the closing tag is reached
    )*                            # repeat
  )
</\#\1>
~sx', function ($m) {
          list ($all, $tag, $args, $str) = $m;
          $args = $args ? explode ('|', $args) : [];
          switch ($tag) {
            case 'i':
              return "<div class='__log-item " . get ($args, 0) . "'>$str</div>";
            case 'section':
              return "<div class='__log-section'>" . ($args ? "<div class='__log-title'>$args[0]</div>" : '') .
                     "$str</div>";
            case 'log':
              return "<div class='__log-stripe'>$str</div>";
            case 'data':
              return "<div class='__log-data'>$str</div>";
            case 'header':
              return "<div class='__header'>$str</div>";
            case 'footer':
              return "<div class='__footer'>$str</div><div></div>";
            case 'alert':
              return "<div class='__alert'>$str</div>";
            case 'type':
{'fraction_non_alphanumeric': 0.1733447188649501, 'fraction_numerical': 0.006568575932737782, 'mean_word_length': 2.48079561042524, 'pattern_counts': {'":': 0, '<': 143, '<?xml version=': 0, '>': 199, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6587143', 'n_tokens_mistral': 4992, 'n_tokens_neox': 4845, 'n_words': 1327}