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;
}