Path: blob/master/src/applications/diviner/markup/DivinerSymbolRemarkupRule.php
12256 views
<?php12final class DivinerSymbolRemarkupRule extends PhutilRemarkupRule {34const KEY_RULE_ATOM_REF = 'rule.diviner.atomref';56public function getPriority() {7return 200.0;8}910public function apply($text) {11// Grammar here is:12//13// rule = '@{' maybe_type name maybe_title '}'14// maybe_type = null | type ':' | type '@' book ':'15// name = name | name '@' context16// maybe_title = null | '|' title17//18// So these are all valid:19//20// @{name}21// @{type : name}22// @{name | title}23// @{type @ book : name @ context | title}2425return preg_replace_callback(26'/(?:^|\B)@{'.27'(?:(?P<type>[^:]+?):)?'.28'(?P<name>[^}|]+?)'.29'(?:[|](?P<title>[^}]+))?'.30'}/',31array($this, 'markupSymbol'),32$text);33}3435public function markupSymbol(array $matches) {36if (!$this->isFlatText($matches[0])) {37return $matches[0];38}3940$type = (string)idx($matches, 'type');41$name = (string)$matches['name'];42$title = (string)idx($matches, 'title');4344// Collapse sequences of whitespace into a single space.45$type = preg_replace('/\s+/', ' ', trim($type));46$name = preg_replace('/\s+/', ' ', trim($name));47$title = preg_replace('/\s+/', ' ', trim($title));4849$ref = array();5051if (strpos($type, '@') !== false) {52list($type, $book) = explode('@', $type, 2);53$ref['type'] = trim($type);54$ref['book'] = trim($book);55} else {56$ref['type'] = $type;57}5859if (strpos($name, '@') !== false) {60list($name, $context) = explode('@', $name, 2);61$ref['name'] = trim($name);62$ref['context'] = trim($context);63} else {64$ref['name'] = $name;65}6667$ref['title'] = nonempty($title, $name);6869foreach ($ref as $key => $value) {70if ($value === '') {71unset($ref[$key]);72}73}7475$engine = $this->getEngine();76$token = $engine->storeText('');7778$key = self::KEY_RULE_ATOM_REF;79$data = $engine->getTextMetadata($key, array());80$data[$token] = $ref;81$engine->setTextMetadata($key, $data);8283return $token;84}8586public function didMarkupText() {87$engine = $this->getEngine();8889$key = self::KEY_RULE_ATOM_REF;90$data = $engine->getTextMetadata($key, array());9192$renderer = $engine->getConfig('diviner.renderer');9394foreach ($data as $token => $ref_dict) {95$ref = DivinerAtomRef::newFromDictionary($ref_dict);96$title = $ref->getTitle();9798$href = null;99if ($renderer) {100// Here, we're generating documentation. If possible, we want to find101// the real atom ref so we can render the correct default title and102// render invalid links in an alternate style.103104$ref = $renderer->normalizeAtomRef($ref);105if ($ref) {106$title = nonempty($ref->getTitle(), $ref->getName());107$href = $renderer->getHrefForAtomRef($ref);108}109} else {110// Here, we're generating comment text or something like that. Just111// link to Diviner and let it sort things out.112113$params = array(114'book' => $ref->getBook(),115'name' => $ref->getName(),116'type' => $ref->getType(),117'context' => $ref->getContext(),118'jump' => true,119);120121$href = new PhutilURI('/diviner/find/', $params);122}123124// TODO: This probably is not the best place to do this. Move it somewhere125// better when it becomes more clear where it should actually go.126if ($ref) {127switch ($ref->getType()) {128case 'function':129case 'method':130$title = $title.'()';131break;132}133}134135if ($this->getEngine()->isTextMode()) {136if ($href) {137$link = $title.' <'.PhabricatorEnv::getProductionURI($href).'>';138} else {139$link = $title;140}141} else if ($href) {142if ($this->getEngine()->isHTMLMailMode()) {143$href = PhabricatorEnv::getProductionURI($href);144}145146$link = $this->newTag(147'a',148array(149'class' => 'atom-ref',150'href' => $href,151),152$title);153} else {154$link = $this->newTag(155'span',156array(157'class' => 'atom-ref-invalid',158),159$title);160}161162$engine->overwriteStoredText($token, $link);163}164}165166}167168169