Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/diviner/markup/DivinerRemarkupRuleSymbol.php b/src/applications/diviner/markup/DivinerRemarkupRuleSymbol.php
index b851555f07..48ff6655b8 100644
--- a/src/applications/diviner/markup/DivinerRemarkupRuleSymbol.php
+++ b/src/applications/diviner/markup/DivinerRemarkupRuleSymbol.php
@@ -1,159 +1,163 @@
<?php
final class DivinerRemarkupRuleSymbol extends PhutilRemarkupRule {
const KEY_RULE_ATOM_REF = 'rule.diviner.atomref';
public function getPriority() {
- return 40.0;
+ return 200.0;
}
public function apply($text) {
// Grammar here is:
//
// rule = '@{' maybe_type name maybe_title '}'
// maybe_type = null | type ':' | type '@' book ':'
// name = name | name '@' context
// maybe_title = null | '|' title
//
// So these are all valid:
//
// @{name}
// @{type : name}
// @{name | title}
// @{type @ book : name @ context | title}
return preg_replace_callback(
'/(?:^|\B)@{'.
'(?:(?P<type>[^:]+?):)?'.
'(?P<name>[^}|]+?)'.
'(?:[|](?P<title>[^}]+))?'.
'}/',
array($this, 'markupSymbol'),
$text);
}
public function markupSymbol($matches) {
+ if ($this->isTextFlat($matches[0])) {
+ return $matches[0];
+ }
+
$type = (string)idx($matches, 'type');
$name = (string)$matches['name'];
$title = (string)idx($matches, 'title');
// Collapse sequences of whitespace into a single space.
$type = preg_replace('/\s+/', ' ', trim($type));
$name = preg_replace('/\s+/', ' ', trim($name));
$title = preg_replace('/\s+/', ' ', trim($title));
$ref = array();
if (strpos($type, '@') !== false) {
list($type, $book) = explode('@', $type, 2);
$ref['type'] = trim($type);
$ref['book'] = trim($book);
} else {
$ref['type'] = $type;
}
if (strpos($name, '@') !== false) {
list($name, $context) = explode('@', $name, 2);
$ref['name'] = trim($name);
$ref['context'] = trim($context);
} else {
$ref['name'] = $name;
}
$ref['title'] = nonempty($title, $name);
foreach ($ref as $key => $value) {
if ($value === '') {
unset($ref[$key]);
}
}
$engine = $this->getEngine();
$token = $engine->storeText('');
$key = self::KEY_RULE_ATOM_REF;
$data = $engine->getTextMetadata($key, array());
$data[$token] = $ref;
$engine->setTextMetadata($key, $data);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$key = self::KEY_RULE_ATOM_REF;
$data = $engine->getTextMetadata($key, array());
$renderer = $engine->getConfig('diviner.renderer');
foreach ($data as $token => $ref_dict) {
$ref = DivinerAtomRef::newFromDictionary($ref_dict);
$title = $ref->getTitle();
$href = null;
if ($renderer) {
// Here, we're generating documentation. If possible, we want to find
// the real atom ref so we can render the correct default title and
// render invalid links in an alternate style.
$ref = $renderer->normalizeAtomRef($ref);
if ($ref) {
$title = nonempty($ref->getTitle(), $ref->getName());
$href = $renderer->getHrefForAtomRef($ref);
}
} else {
// Here, we're generating comment text or something like that. Just
// link to Diviner and let it sort things out.
$href = id(new PhutilURI('/diviner/find/'))
->setQueryParams(
array(
'book' => $ref->getBook(),
'name' => $ref->getName(),
'type' => $ref->getType(),
'context' => $ref->getContext(),
'jump' => true,
));
}
// TODO: This probably is not the best place to do this. Move it somewhere
// better when it becomes more clear where it should actually go.
if ($ref) {
switch ($ref->getType()) {
case 'function':
case 'method':
$title = $title.'()';
break;
}
}
if ($this->getEngine()->isTextMode()) {
if ($href) {
$link = $title.' <'.PhabricatorEnv::getProductionURI($href).'>';
} else {
$link = $title;
}
} else if ($href) {
$link = $this->newTag(
'a',
array(
'class' => 'atom-ref',
'href' => $href,
),
$title);
} else {
$link = $this->newTag(
'span',
array(
'class' => 'atom-ref-invalid',
),
$title);
}
$engine->overwriteStoredText($token, $link);
}
}
}
diff --git a/src/applications/macro/remarkup/PhabricatorRemarkupRuleIcon.php b/src/applications/macro/remarkup/PhabricatorRemarkupRuleIcon.php
index 0e59f94e64..a301578030 100644
--- a/src/applications/macro/remarkup/PhabricatorRemarkupRuleIcon.php
+++ b/src/applications/macro/remarkup/PhabricatorRemarkupRuleIcon.php
@@ -1,72 +1,76 @@
<?php
final class PhabricatorRemarkupRuleIcon
extends PhutilRemarkupRule {
public function getPriority() {
- return 50.0;
+ return 200.0;
}
public function apply($text) {
return preg_replace_callback(
'@{icon\b((?:[^}\\\\]+|\\\\.)*)}@m',
array($this, 'markupIcon'),
$text);
}
public function markupIcon($matches) {
+ if (!$this->isFlatText($matches[0])) {
+ return $matches[0];
+ }
+
$extra = idx($matches, 1);
// We allow various forms, like these:
//
// {icon}
// {icon camera}
// {icon,camera}
// {icon camera color=red}
// {icon, camera, color=red}
$extra = ltrim($extra, ", \n");
$extra = preg_split('/[\s,]+/', $extra, 2);
// Choose some arbitrary default icon so that previews render in a mostly
// reasonable way as you're typing the syntax.
$icon = idx($extra, 0, 'paw');
$defaults = array(
'color' => null,
);
$options = idx($extra, 1, '');
$parser = new PhutilSimpleOptions();
$options = $parser->parse($options) + $defaults;
// NOTE: We're validating icon and color names to prevent users from
// adding arbitrary CSS classes to the document. Although this probably
// isn't dangerous, it's safer to validate.
static $icon_names;
if (!$icon_names) {
$icon_names = array_fuse(PHUIIconView::getFontIcons());
}
static $color_names;
if (!$color_names) {
$color_names = array_fuse(PHUIIconView::getFontIconColors());
}
if (empty($icon_names['fa-'.$icon])) {
$icon = 'paw';
}
$color = $options['color'];
if (empty($color_names[$color])) {
$color = null;
}
$icon_view = id(new PHUIIconView())
->setIconFont('fa-'.$icon, $color);
return $this->getEngine()->storeText($icon_view);
}
}
diff --git a/src/applications/macro/remarkup/PhabricatorRemarkupRuleMeme.php b/src/applications/macro/remarkup/PhabricatorRemarkupRuleMeme.php
index 242f2a8db1..7deed4a6c8 100644
--- a/src/applications/macro/remarkup/PhabricatorRemarkupRuleMeme.php
+++ b/src/applications/macro/remarkup/PhabricatorRemarkupRuleMeme.php
@@ -1,60 +1,64 @@
<?php
/**
* @group markup
*/
final class PhabricatorRemarkupRuleMeme
extends PhutilRemarkupRule {
private $images;
public function getPriority() {
- return 50.0;
+ return 200.0;
}
public function apply($text) {
return preg_replace_callback(
'@{meme,((?:[^}\\\\]+|\\\\.)+)}$@m',
array($this, 'markupMeme'),
$text);
}
public function markupMeme($matches) {
+ if (!$this->isFlatText($matches[0])) {
+ return $matches[0];
+ }
+
$options = array(
'src' => null,
'above' => null,
'below' => null,
);
$parser = new PhutilSimpleOptions();
$options = $parser->parse($matches[1]) + $options;
$uri = id(new PhutilURI('/macro/meme/'))
->alter('macro', $options['src'])
->alter('uppertext', $options['above'])
->alter('lowertext', $options['below']);
if ($this->getEngine()->isTextMode()) {
$img =
($options['above'] != '' ? "\"{$options['above']}\"\n" : '').
$options['src'].' <'.PhabricatorEnv::getProductionURI($uri).'>'.
($options['below'] != '' ? "\n\"{$options['below']}\"" : '');
} else {
$alt_text = pht(
'Macro %s: %s %s',
$options['src'],
$options['above'],
$options['below']);
$img = $this->newTag(
'img',
array(
'src' => $uri,
'alt' => $alt_text,
));
}
return $this->getEngine()->storeText($img);
}
}
diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
index ee5fd3a2b4..c22f4285c4 100644
--- a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
+++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleObject.php
@@ -1,230 +1,238 @@
<?php
/**
* @group markup
*/
abstract class PhabricatorRemarkupRuleObject
extends PhutilRemarkupRule {
const KEY_RULE_OBJECT = 'rule.object';
const KEY_MENTIONED_OBJECTS = 'rule.object.mentioned';
abstract protected function getObjectNamePrefix();
abstract protected function loadObjects(array $ids);
public function getPriority() {
- return 50.0;
+ return 200.0;
}
protected function getObjectNamePrefixBeginsWithWordCharacter() {
$prefix = $this->getObjectNamePrefix();
return preg_match('/^\w/', $prefix);
}
protected function getObjectIDPattern() {
return '[1-9]\d*';
}
protected function shouldMarkupObject(array $params) {
return true;
}
protected function loadHandles(array $objects) {
$phids = mpull($objects, 'getPHID');
$handles = id(new PhabricatorHandleQuery($phids))
->withPHIDs($phids)
->setViewer($this->getEngine()->getConfig('viewer'))
->execute();
$result = array();
foreach ($objects as $id => $object) {
$result[$id] = $handles[$object->getPHID()];
}
return $result;
}
protected function getObjectHref($object, $handle, $id) {
return $handle->getURI();
}
protected function renderObjectRef($object, $handle, $anchor, $id) {
$href = $this->getObjectHref($object, $handle, $id);
$text = $this->getObjectNamePrefix().$id;
if ($anchor) {
$href = $href.'#'.$anchor;
$text = $text.'#'.$anchor;
}
if ($this->getEngine()->isTextMode()) {
return PhabricatorEnv::getProductionURI($href);
}
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
$attr = array(
'phid' => $handle->getPHID(),
'closed' => ($handle->getStatus() == $status_closed),
);
return $this->renderHovertag($text, $href, $attr);
}
protected function renderObjectEmbed($object, $handle, $options) {
$name = $handle->getFullName();
$href = $handle->getURI();
$status_closed = PhabricatorObjectHandleStatus::STATUS_CLOSED;
if ($this->getEngine()->isTextMode()) {
return $name.' <'.PhabricatorEnv::getProductionURI($href).'>';
}
$attr = array(
'phid' => $handle->getPHID(),
'closed' => ($handle->getStatus() == $status_closed),
);
return $this->renderHovertag($name, $href, $attr);
}
protected function renderHovertag($name, $href, array $attr = array()) {
return id(new PHUITagView())
->setName($name)
->setHref($href)
->setType(PHUITagView::TYPE_OBJECT)
->setPHID(idx($attr, 'phid'))
->setClosed(idx($attr, 'closed'))
->render();
}
public function apply($text) {
$prefix = $this->getObjectNamePrefix();
$prefix = preg_quote($prefix, '@');
$id = $this->getObjectIDPattern();
$text = preg_replace_callback(
'@\B{'.$prefix.'('.$id.')((?:[^}\\\\]|\\\\.)*)}\B@',
array($this, 'markupObjectEmbed'),
$text);
// If the prefix starts with a word character (like "D"), we want to
// require a word boundary so that we don't match "XD1" as "D1". If the
// prefix does not start with a word character, we want to require no word
// boundary for the same reasons. Test if the prefix starts with a word
// character.
if ($this->getObjectNamePrefixBeginsWithWordCharacter()) {
$boundary = '\\b';
} else {
$boundary = '\\B';
}
// NOTE: The "(?<!#)" prevents us from linking "#abcdef" or similar. The
// "(?<!/)" prevents us from linking things in URLs. The "\b" allows us to
// link "(abcdef)" or similar without linking things in the middle of
// words.
$text = preg_replace_callback(
'((?<!#)(?<!/)'.$boundary.$prefix.'('.$id.')(?:#([-\w\d]+))?\b)',
array($this, 'markupObjectReference'),
$text);
return $text;
}
public function markupObjectEmbed($matches) {
+ if (!$this->isFlatText($matches[0])) {
+ return $matches[0];
+ }
+
return $this->markupObject(array(
'type' => 'embed',
'id' => $matches[1],
'options' => idx($matches, 2),
'original' => $matches[0],
));
}
public function markupObjectReference($matches) {
+ if (!$this->isFlatText($matches[0])) {
+ return $matches[0];
+ }
+
return $this->markupObject(array(
'type' => 'ref',
'id' => $matches[1],
'anchor' => idx($matches, 2),
'original' => $matches[0],
));
}
private function markupObject(array $params) {
if (!$this->shouldMarkupObject($params)) {
return $params['original'];
}
$engine = $this->getEngine();
$token = $engine->storeText('x');
$metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix();
$metadata = $engine->getTextMetadata($metadata_key, array());
$metadata[] = array(
'token' => $token,
) + $params;
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
}
public function didMarkupText() {
$engine = $this->getEngine();
$metadata_key = self::KEY_RULE_OBJECT.'.'.$this->getObjectNamePrefix();
$metadata = $engine->getTextMetadata($metadata_key, array());
if (!$metadata) {
return;
}
$ids = ipull($metadata, 'id');
$objects = $this->loadObjects($ids);
// For objects that are invalid or which the user can't see, just render
// the original text.
// TODO: We should probably distinguish between these cases and render a
// "you can't see this" state for nonvisible objects.
foreach ($metadata as $key => $spec) {
if (empty($objects[$spec['id']])) {
$engine->overwriteStoredText(
$spec['token'],
$spec['original']);
unset($metadata[$key]);
}
}
$phids = $engine->getTextMetadata(self::KEY_MENTIONED_OBJECTS, array());
foreach ($objects as $object) {
$phids[$object->getPHID()] = $object->getPHID();
}
$engine->setTextMetadata(self::KEY_MENTIONED_OBJECTS, $phids);
$handles = $this->loadHandles($objects);
foreach ($metadata as $key => $spec) {
$handle = $handles[$spec['id']];
$object = $objects[$spec['id']];
switch ($spec['type']) {
case 'ref':
$view = $this->renderObjectRef(
$object,
$handle,
$spec['anchor'],
$spec['id']);
break;
case 'embed':
$spec['options'] = $this->assertFlatText($spec['options']);
$view = $this->renderObjectEmbed($object, $handle, $spec['options']);
break;
}
$engine->overwriteStoredText($spec['token'], $view);
}
$engine->setTextMetadata($metadata_key, array());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 4:14 AM (2 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
187960
Default Alt Text
(15 KB)

Event Timeline