private function applyXHPHighlight()

in src/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php [31:162]


  private function applyXHPHighlight($result) {

    // We perform two passes here: one using the AST to find symbols we care
    // about -- particularly, class names and function names. These are used
    // in the crossreference stuff to link into Diffusion. After we've done our
    // AST pass, we do a followup pass on the token stream to catch all the
    // simple stuff like strings and comments.

    $tree = XHPASTTree::newFromDataAndResolvedExecFuture(
      $this->source,
      $result);

    $root = $tree->getRootNode();

    $tokens = $root->getTokens();
    $interesting_symbols = $this->findInterestingSymbols($root);


    if ($this->scrub) {
      // If we're scrubbing, we prepended "<?php\n" to the text to force the
      // highlighter to treat it as PHP source. Now, we need to remove that.

      $ok = false;
      if (count($tokens) >= 2) {
        if ($tokens[0]->getTypeName() === 'T_OPEN_TAG') {
          if ($tokens[1]->getTypeName() === 'T_WHITESPACE') {
            $ok = true;
          }
        }
      }

      if (!$ok) {
        throw new Exception(
          pht(
            'Expected T_OPEN_TAG, T_WHITESPACE tokens at head of results '.
            'for highlighting parse of PHP snippet.'));
      }

      // Remove the "<?php".
      unset($tokens[0]);

      $value = $tokens[1]->getValue();
      if ((strlen($value) < 1) || ($value[0] != "\n")) {
        throw new Exception(
          pht(
            'Expected "\\n" at beginning of T_WHITESPACE token at head of '.
            'tokens for highlighting parse of PHP snippet.'));
      }

      $value = substr($value, 1);
      $tokens[1]->overwriteValue($value);
    }

    $out = array();
    foreach ($tokens as $key => $token) {
      $value = $token->getValue();
      $class = null;
      $multi = false;
      $attrs = array();
      if (isset($interesting_symbols[$key])) {
        $sym = $interesting_symbols[$key];
        $class = $sym[0];
        $attrs['data-symbol-context'] = idx($sym, 'context');
        $attrs['data-symbol-name'] = idx($sym, 'symbol');
      } else {
        switch ($token->getTypeName()) {
          case 'T_WHITESPACE':
            break;
          case 'T_DOC_COMMENT':
            $class = 'dc';
            $multi = true;
            break;
          case 'T_COMMENT':
            $class = 'c';
            $multi = true;
            break;
          case 'T_CONSTANT_ENCAPSED_STRING':
          case 'T_ENCAPSED_AND_WHITESPACE':
          case 'T_INLINE_HTML':
            $class = 's';
            $multi = true;
            break;
          case 'T_VARIABLE':
            $class = 'nv';
            break;
          case 'T_OPEN_TAG':
          case 'T_OPEN_TAG_WITH_ECHO':
          case 'T_CLOSE_TAG':
            $class = 'o';
            break;
          case 'T_LNUMBER':
          case 'T_DNUMBER':
            $class = 'm';
            break;
          case 'T_STRING':
            static $magic = array(
              'true' => true,
              'false' => true,
              'null' => true,
            );
            if (isset($magic[strtolower($value)])) {
              $class = 'k';
              break;
            }
            $class = 'nx';
            break;
          default:
            $class = 'k';
            break;
        }
      }

      if ($class) {
        $attrs['class'] = $class;
        if ($multi) {
          // If the token may have multiple lines in it, make sure each
          // <span> crosses no more than one line so the lines can be put
          // in a table, etc., later.
          $value = phutil_split_lines($value, $retain_endings = true);
        } else {
          $value = array($value);
        }
        foreach ($value as $val) {
          $out[] = phutil_tag('span', $attrs, $val);
        }
      } else {
        $out[] = $value;
      }
    }

    return phutil_implode_html('', $out);
  }