private function findInterestingSymbols()

in src/markup/syntax/highlighter/xhpast/PhutilXHPASTSyntaxHighlighterFuture.php [164:260]


  private function findInterestingSymbols(XHPASTNode $root) {
    // Class name symbols appear in:
    //    class X extends X implements X, X { ... }
    //    new X();
    //    $x instanceof X
    //    catch (X $x)
    //    function f(X $x)
    //    X::f();
    //    X::$m;
    //    X::CONST;

    // These are PHP builtin tokens which can appear in a classname context.
    // Don't link them since they don't go anywhere useful.
    static $builtin_class_tokens = array(
      'self'    => true,
      'parent'  => true,
      'static'  => true,
    );

    // Fortunately XHPAST puts all of these in a special node type so it's
    // easy to find them.
    $result_map = array();
    $class_names = $root->selectDescendantsOfType('n_CLASS_NAME');
    foreach ($class_names as $class_name) {
      foreach ($class_name->getTokens() as $key => $token) {
        if (isset($builtin_class_tokens[$token->getValue()])) {
          // This is something like "self::method()".
          continue;
        }
        $result_map[$key] = array(
          'nc', // "Name, Class"
          'symbol' => $class_name->getConcreteString(),
        );
      }
    }

    // Function name symbols appear in:
    //    f()

    $function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
    foreach ($function_calls as $call) {
      $call = $call->getChildByIndex(0);
      if ($call->getTypeName() == 'n_SYMBOL_NAME') {
        // This is a normal function call, not some $f() shenanigans.
        foreach ($call->getTokens() as $key => $token) {
          $result_map[$key] = array(
            'nf', // "Name, Function"
            'symbol' => $call->getConcreteString(),
          );
        }
      }
    }

    // Upon encountering $x->y, link y without context, since $x is unknown.

    $prop_access = $root->selectDescendantsOfType('n_OBJECT_PROPERTY_ACCESS');
    foreach ($prop_access as $access) {
      $right = $access->getChildByIndex(1);
      if ($right->getTypeName() == 'n_INDEX_ACCESS') {
        // otherwise $x->y[0] doesn't get highlighted
        $right = $right->getChildByIndex(0);
      }
      if ($right->getTypeName() == 'n_STRING') {
        foreach ($right->getTokens() as $key => $token) {
          $result_map[$key] = array(
            'na', // "Name, Attribute"
            'symbol' => $right->getConcreteString(),
          );
        }
      }
    }

    // Upon encountering x::y, try to link y with context x.

    $static_access = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS');
    foreach ($static_access as $access) {
      $class = $access->getChildByIndex(0);
      $right = $access->getChildByIndex(1);
      if ($class->getTypeName() == 'n_CLASS_NAME' &&
          ($right->getTypeName() == 'n_STRING' ||
           $right->getTypeName() == 'n_VARIABLE')) {
        $classname = head($class->getTokens())->getValue();
        $result = array(
          'na',
          'symbol' => ltrim($right->getConcreteString(), '$'),
        );
        if (!isset($builtin_class_tokens[$classname])) {
          $result['context'] = $classname;
        }
        foreach ($right->getTokens() as $key => $token) {
          $result_map[$key] = $result;
        }
      }
    }

    return $result_map;
  }